package com.leohabrom.discordrpc; import com.leohabrom.discord.DiscordActivity; import com.leohabrom.discord.DiscordBridge; import com.leohabrom.discord.DiscordBridgeAdapter; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReference; public class Bridge implements DiscordBridgeAdapter { private final DiscordBridge bridge; private final BlockingQueue feedbackQueue = new ArrayBlockingQueue<>(1); // Scheduling tools private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(); private final AtomicLong lastSendTime = new AtomicLong(0); private final AtomicReference pendingActivity = new AtomicReference<>(); private final AtomicReference> scheduledTask = new AtomicReference<>(); private DiscordActivity lastSentActivity = null; public Bridge(long appId) { this.bridge = new DiscordBridge(); this.bridge.init(appId); this.bridge.setAdapter(this); this.bridge.start(); } /** * Updates the activity. If called too fast, it buffers the latest * request and sends it as soon as the rate limit allows. */ public void update(DiscordActivity activity) { pendingActivity.set(activity); long now = System.currentTimeMillis(); long timeSinceLastSend = now - lastSendTime.get(); long delayNeeded = 1000 - timeSinceLastSend; if (delayNeeded <= 0) { sendNow(); } else { // If a task is already scheduled, don't create another one. // The scheduled task will naturally pick up the "latest" pendingActivity. if (scheduledTask.get() == null || scheduledTask.get().isDone()) { ScheduledFuture task = scheduler.schedule(this::sendNow, delayNeeded, TimeUnit.MILLISECONDS); scheduledTask.set(task); } } } private void sendNow() { DiscordActivity activity = pendingActivity.getAndSet(null); if (activity != null) { if (activity.equals(lastSentActivity)) { return; } lastSentActivity = activity; lastSendTime.set(System.currentTimeMillis()); bridge.update(activity); } } public boolean updateWithFeedback(DiscordActivity activity) { feedbackQueue.clear(); update(activity); try { // We wait a bit longer because the update might be delayed by the scheduler Boolean result = feedbackQueue.poll(3, TimeUnit.SECONDS); return result != null && result; } catch (InterruptedException e) { Thread.currentThread().interrupt(); return false; } } @Override public void onMessage(String message) { if (message == null) return; String msg = message.toLowerCase(); if (msg.contains("sent") || msg.contains("success")) { feedbackQueue.offer(true); } else if (msg.contains("fail") || msg.contains("error")) { feedbackQueue.offer(false); } } }