Repository: pajlads/DinkPlugin
Pull request #357 (2023-11-04 01:37:36)

feat: allow notifications on seasonal worlds to be ignored
Work towards #356 (adds `seasonalWorld` to notification metadata)


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0730515..95d12bf 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,6 @@
 ## Unreleased
 
+- Minor: Allow notifications on seasonal worlds to be ignored via advanced config. (#357)
 - Minor: Prefer historical name for first Recipe for Disaster quest. (#352)
 - Minor: Include owned pets in login notification metadata. (#347)
 - Minor: Include individual skill XP in login notification metadata. (#345)
diff --git a/src/main/java/dinkplugin/DinkPluginConfig.java b/src/main/java/dinkplugin/DinkPluginConfig.java
index b6c9c1b..272b205 100644
--- a/src/main/java/dinkplugin/DinkPluginConfig.java
+++ b/src/main/java/dinkplugin/DinkPluginConfig.java
@@ -348,6 +348,17 @@ public interface DinkPluginConfig extends Config {
         return "";
     }
 
+    @ConfigItem(
+        keyName = "ignoreSeasonalWorlds",
+        name = "Ignore Seasonal Worlds",
+        description = "Whether to suppress notifications that occur on seasonal worlds like Leagues",
+        position = 1015,
+        section = advancedSection
+    )
+    default boolean ignoreSeasonal() {
+        return false;
+    }
+
     @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 3d5c9f0..9028e26 100644
--- a/src/main/java/dinkplugin/message/DiscordMessageHandler.java
+++ b/src/main/java/dinkplugin/message/DiscordMessageHandler.java
@@ -12,6 +12,7 @@ import dinkplugin.util.Utils;
 import lombok.NonNull;
 import lombok.extern.slf4j.Slf4j;
 import net.runelite.api.Client;
+import net.runelite.api.WorldType;
 import net.runelite.api.clan.ClanChannel;
 import net.runelite.api.clan.ClanID;
 import net.runelite.api.widgets.Widget;
@@ -218,6 +219,10 @@ public class DiscordMessageHandler {
 
         NotificationBody.NotificationBodyBuilder<?> builder = mBody.toBuilder();
 
+        if (!config.ignoreSeasonal()) {
+            builder.seasonalWorld(client.getWorldType().contains(WorldType.SEASONAL));
+        }
+
         if (config.sendDiscordUser()) {
             builder.discordUser(DiscordProfile.of(discordService.getCurrentUser()));
         }
@@ -248,10 +253,11 @@ public class DiscordMessageHandler {
     private NotificationBody<?> injectThreadName(HttpUrl url, NotificationBody<?> mBody, boolean force) {
         Collection<String> queryParams = url.queryParameterNames();
         if (force || (queryParams.contains("forum") && !queryParams.contains("thread_id"))) {
+            String type = mBody.isSeasonalWorld() ? "Seasonal - " + mBody.getType().getTitle() : mBody.getType().getTitle();
             String threadName = Template.builder()
                 .template(config.threadNameTemplate())
                 .replacementBoundary("%")
-                .replacement("%TYPE%", Replacements.ofText(mBody.getType().getTitle()))
+                .replacement("%TYPE%", Replacements.ofText(type))
                 .replacement("%MESSAGE%", mBody.getText())
                 .replacement("%USERNAME%", Replacements.ofText(mBody.getPlayerName()))
                 .build()
@@ -340,7 +346,7 @@ public class DiscordMessageHandler {
             Embed.builder()
                 .author(author)
                 .color(Utils.PINK)
-                .title(type.getTitle())
+                .title(body.isSeasonalWorld() ? "[Seasonal] " + type.getTitle() : type.getTitle())
                 .description(Utils.truncate(body.getText().evaluate(config.discordRichEmbeds()), Embed.MAX_DESCRIPTION_LENGTH))
                 .image(screenshot ? new Embed.UrlEmbed("attachment://" + type.getScreenshot()) : null)
                 .thumbnail(new Embed.UrlEmbed(thumbnail))
diff --git a/src/main/java/dinkplugin/message/NotificationBody.java b/src/main/java/dinkplugin/message/NotificationBody.java
index 0674f7b..c061c4e 100644
--- a/src/main/java/dinkplugin/message/NotificationBody.java
+++ b/src/main/java/dinkplugin/message/NotificationBody.java
@@ -37,6 +37,7 @@ public class NotificationBody<T extends NotificationData> {
     String clanName;
     @Nullable
     String groupIronClanName;
+    boolean seasonalWorld;
     @Nullable
     T extra;
     @NotNull
diff --git a/src/main/java/dinkplugin/notifiers/BaseNotifier.java b/src/main/java/dinkplugin/notifiers/BaseNotifier.java
index 77a5cb9..a6299c1 100644
--- a/src/main/java/dinkplugin/notifiers/BaseNotifier.java
+++ b/src/main/java/dinkplugin/notifiers/BaseNotifier.java
@@ -6,9 +6,11 @@ import dinkplugin.message.DiscordMessageHandler;
 import dinkplugin.message.NotificationBody;
 import dinkplugin.util.WorldUtils;
 import net.runelite.api.Client;
+import net.runelite.api.WorldType;
 import org.apache.commons.lang3.StringUtils;
 
 import javax.inject.Inject;
+import java.util.Set;
 
 public abstract class BaseNotifier {
 
@@ -25,7 +27,14 @@ public abstract class BaseNotifier {
     private DiscordMessageHandler messageHandler;
 
     public boolean isEnabled() {
-        return !WorldUtils.isIgnoredWorld(client.getWorldType()) && settingsManager.isNamePermitted(client.getLocalPlayer().getName());
+        Set<WorldType> world = client.getWorldType();
+        if (config.ignoreSeasonal() && world.contains(WorldType.SEASONAL)) {
+            return false;
+        }
+        if (WorldUtils.isIgnoredWorld(world)) {
+            return false;
+        }
+        return settingsManager.isNamePermitted(client.getLocalPlayer().getName());
     }
 
     protected abstract String getWebhookUrl();


Test patch:
diff --git a/src/test/java/dinkplugin/notifiers/PetNotifierTest.java b/src/test/java/dinkplugin/notifiers/PetNotifierTest.java
index 081adba..a0d24a4 100644
--- a/src/test/java/dinkplugin/notifiers/PetNotifierTest.java
+++ b/src/test/java/dinkplugin/notifiers/PetNotifierTest.java
@@ -9,11 +9,13 @@ import dinkplugin.util.ItemSearcher;
 import dinkplugin.util.ItemUtils;
 import net.runelite.api.ItemID;
 import net.runelite.api.Varbits;
+import net.runelite.api.WorldType;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.mockito.InjectMocks;
 import org.mockito.Mock;
 
+import java.util.EnumSet;
 import java.util.stream.IntStream;
 
 import static dinkplugin.notifiers.PetNotifier.MAX_TICKS_WAIT;
@@ -339,6 +341,42 @@ class PetNotifierTest extends MockedNotifierTest {
         verify(messageHandler, never()).createMessage(any(), anyBoolean(), any());
     }
 
+    @Test
+    void testNotifySeasonal() {
+        // update mocks
+        when(config.ignoreSeasonal()).thenReturn(false);
+        when(client.getWorldType()).thenReturn(EnumSet.of(WorldType.SEASONAL));
+
+        // send fake message
+        notifier.onChatMessage("You feel something weird sneaking into your backpack.");
+        IntStream.rangeClosed(0, MAX_TICKS_WAIT).forEach(i -> notifier.onTick());
+
+        // verify handled
+        verify(messageHandler).createMessage(
+            PRIMARY_WEBHOOK_URL,
+            false,
+            NotificationBody.builder()
+                .extra(new PetNotificationData(null, null, false, null))
+                .text(buildTemplate(PLAYER_NAME + " got a pet"))
+                .type(NotificationType.PET)
+                .build()
+        );
+    }
+
+    @Test
+    void testIgnoreSeasonal() {
+        // update mocks
+        when(config.ignoreSeasonal()).thenReturn(true);
+        when(client.getWorldType()).thenReturn(EnumSet.of(WorldType.SEASONAL));
+
+        // send fake message
+        notifier.onChatMessage("You feel something weird sneaking into your backpack.");
+        IntStream.rangeClosed(0, MAX_TICKS_WAIT).forEach(i -> notifier.onTick());
+
+        // ensure no notification occurred
+        verify(messageHandler, never()).createMessage(any(), anyBoolean(), any());
+    }
+
     @Test
     void testNotifyIrrelevantNameIgnore() {
         // ignore notifications for an irrelevant player