Repository: pajlads/DinkPlugin
Pull request #226 (2023-05-03 02:20:59)

feat: include discord user in metadata
Includes information about the logged-in Discord user in notification metadata, which can be helpful for webhook consumers that forward to Discord (or involve a Discord community).

```json
{
  "discordUser": {
    "id": "1234567890",
    "name": "Gamer",
    "avatarHash": "abcdefabcdefabcdefabcdef"
  },
  "playerName": "",
  "extra": {},
  "embeds": []
}
```

This information is acquired via RPC (that is already executed by base RuneLite), and can be disabled via an advanced config option.



Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 83a4130..2b4e10c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,6 @@
 ## Unreleased
 
+- Minor: Include discord user profile in notification metadata. (#226)
 - Minor: Add setting to allow death notifications during safe activities. (#227)
 - Minor: Allow player name in Discord notifications to link to CollectionLog.net profile. (#224)
 
diff --git a/src/main/java/dinkplugin/DinkPluginConfig.java b/src/main/java/dinkplugin/DinkPluginConfig.java
index 1629ce3..6c5e419 100644
--- a/src/main/java/dinkplugin/DinkPluginConfig.java
+++ b/src/main/java/dinkplugin/DinkPluginConfig.java
@@ -258,6 +258,17 @@ public interface DinkPluginConfig extends Config {
         return false;
     }
 
+    @ConfigItem(
+        keyName = "sendDiscordUser",
+        name = "Send Discord Profile",
+        description = "Whether to send your discord user information to the webhook server via metadata",
+        position = 1010,
+        section = advancedSection
+    )
+    default boolean sendDiscordUser() {
+        return true;
+    }
+
     @ConfigItem(
         keyName = "discordWebhook", // do not rename; would break old configs
         name = "Primary Webhook URLs",
diff --git a/src/main/java/dinkplugin/message/DiscordMessageHandler.java b/src/main/java/dinkplugin/message/DiscordMessageHandler.java
index 392ccc3..bde7f7e 100644
--- a/src/main/java/dinkplugin/message/DiscordMessageHandler.java
+++ b/src/main/java/dinkplugin/message/DiscordMessageHandler.java
@@ -5,6 +5,7 @@ import dinkplugin.DinkPlugin;
 import dinkplugin.DinkPluginConfig;
 import dinkplugin.domain.PlayerLookupService;
 import dinkplugin.notifiers.data.NotificationData;
+import dinkplugin.util.DiscordProfile;
 import dinkplugin.util.Utils;
 import lombok.NonNull;
 import lombok.extern.slf4j.Slf4j;
@@ -12,6 +13,7 @@ import net.runelite.api.Client;
 import net.runelite.api.widgets.Widget;
 import net.runelite.api.widgets.WidgetInfo;
 import net.runelite.client.callback.ClientThread;
+import net.runelite.client.discord.DiscordService;
 import net.runelite.client.ui.DrawManager;
 import net.runelite.client.util.ImageUtil;
 import okhttp3.HttpUrl;
@@ -58,16 +60,18 @@ public class DiscordMessageHandler {
     private final DinkPluginConfig config;
     private final ScheduledExecutorService executor;
     private final ClientThread clientThread;
+    private final DiscordService discordService;
 
     @Inject
     @VisibleForTesting
-    public DiscordMessageHandler(Gson gson, Client client, DrawManager drawManager, OkHttpClient httpClient, DinkPluginConfig config, ScheduledExecutorService executor, ClientThread clientThread) {
+    public DiscordMessageHandler(Gson gson, Client client, DrawManager drawManager, OkHttpClient httpClient, DinkPluginConfig config, ScheduledExecutorService executor, ClientThread clientThread, DiscordService discordService) {
         this.gson = gson;
         this.client = client;
         this.drawManager = drawManager;
         this.config = config;
         this.executor = executor;
         this.clientThread = clientThread;
+        this.discordService = discordService;
         this.httpClient = httpClient.newBuilder()
             .addInterceptor(chain -> {
                 Request request = chain.request().newBuilder()
@@ -98,6 +102,9 @@ public class DiscordMessageHandler {
         if (mBody.getAccountType() == null)
             mBody = mBody.withAccountType(client.getAccountType());
 
+        if (config.sendDiscordUser())
+            mBody = mBody.withDiscordUser(DiscordProfile.of(discordService.getCurrentUser()));
+
         if (config.discordRichEmbeds()) {
             mBody = injectContent(mBody, sendImage, config);
         } else {
diff --git a/src/main/java/dinkplugin/message/NotificationBody.java b/src/main/java/dinkplugin/message/NotificationBody.java
index acd634c..51db290 100644
--- a/src/main/java/dinkplugin/message/NotificationBody.java
+++ b/src/main/java/dinkplugin/message/NotificationBody.java
@@ -1,7 +1,9 @@
 package dinkplugin.message;
 
 import com.google.gson.annotations.SerializedName;
+import dinkplugin.DinkPluginConfig;
 import dinkplugin.notifiers.data.NotificationData;
+import dinkplugin.util.DiscordProfile;
 import lombok.AllArgsConstructor;
 import lombok.Builder;
 import lombok.EqualsAndHashCode;
@@ -37,6 +39,15 @@ public class NotificationBody<T extends NotificationData> {
      * Discord fields
      */
 
+    /**
+     * Information about the current discord user, acquired via RPC (handled by base RuneLite).
+     * <p>
+     * This is only sent if {@link DinkPluginConfig#sendDiscordUser()} is enabled.
+     * While this field is not used by Discord, it can be useful for custom webhook handlers that forward to Discord.
+     */
+    @Nullable
+    DiscordProfile discordUser;
+
     /**
      * Filled in with the text of the notifier (e.g., {@link #getText()} is "Forsen has levelled Attack to 100")
      * <p>
diff --git a/src/main/java/dinkplugin/util/DiscordProfile.java b/src/main/java/dinkplugin/util/DiscordProfile.java
new file mode 100644
index 0000000..2b7e096
--- /dev/null
+++ b/src/main/java/dinkplugin/util/DiscordProfile.java
@@ -0,0 +1,17 @@
+package dinkplugin.util;
+
+import lombok.Value;
+import net.runelite.discord.DiscordUser;
+import org.jetbrains.annotations.Nullable;
+
+@Value
+public class DiscordProfile {
+    String id;
+    String name;
+    String avatarHash;
+
+    public static DiscordProfile of(@Nullable DiscordUser user) {
+        if (user == null || user.userId == null) return null;
+        return new DiscordProfile(user.userId, user.username, user.avatar);
+    }
+}


Test patch:
diff --git a/src/test/java/dinkplugin/notifiers/MockedNotifierTest.java b/src/test/java/dinkplugin/notifiers/MockedNotifierTest.java
index d8dfd91..6a1b893 100644
--- a/src/test/java/dinkplugin/notifiers/MockedNotifierTest.java
+++ b/src/test/java/dinkplugin/notifiers/MockedNotifierTest.java
@@ -20,6 +20,7 @@ import net.runelite.client.callback.ClientThread;
 import net.runelite.client.chat.ChatMessageManager;
 import net.runelite.client.config.ConfigDescriptor;
 import net.runelite.client.config.ConfigManager;
+import net.runelite.client.discord.DiscordService;
 import net.runelite.client.game.ItemManager;
 import net.runelite.client.game.NPCManager;
 import net.runelite.client.ui.DrawManager;
@@ -68,6 +69,9 @@ abstract class MockedNotifierTest extends MockedTestBase {
     @Bind
     protected ScheduledExecutorService executor = new BlockingExecutor();
 
+    @Bind
+    protected DiscordService discordService = Mockito.mock(DiscordService.class);
+
     @Bind
     protected ChatMessageManager chatManager = Mockito.mock(ChatMessageManager.class);
 
@@ -87,7 +91,7 @@ abstract class MockedNotifierTest extends MockedTestBase {
     protected SettingsManager settingsManager = Mockito.spy(new SettingsManager(gson, client, clientThread, plugin, config, configManager));
 
     @Bind
-    protected DiscordMessageHandler messageHandler = Mockito.spy(new DiscordMessageHandler(gson, client, drawManager, httpClient, config, executor, clientThread));
+    protected DiscordMessageHandler messageHandler = Mockito.spy(new DiscordMessageHandler(gson, client, drawManager, httpClient, config, executor, clientThread, discordService));
 
     @Override
     protected void setUp() {