Repository: pajlads/DinkPlugin
Pull request #80 (2022-12-03 01:02:00)

refactor: hard code achievement diary enum
Hardcoding to better comply with plugin hub restrictions


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 81e85a4..d453510 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,7 @@
 
 - Minor: Use notifier-specific screenshot filenames. (#79)
 - Minor: Support notifier-specific webhook URLs. (#78)
+- Dev: Explicitly define achievement diaries. (#80)
 - Dev: Add mockito test suite for notifiers. (#74, #75)
 - Dev: Utilize more dependency injection. (#73)
 
diff --git a/src/main/java/dinkplugin/DinkPluginConfig.java b/src/main/java/dinkplugin/DinkPluginConfig.java
index 6f6165f..dbcc9a5 100644
--- a/src/main/java/dinkplugin/DinkPluginConfig.java
+++ b/src/main/java/dinkplugin/DinkPluginConfig.java
@@ -1,6 +1,6 @@
 package dinkplugin;
 
-import dinkplugin.domain.AchievementDiaries;
+import dinkplugin.domain.AchievementDiary;
 import dinkplugin.domain.CombatAchievementTier;
 import net.runelite.client.config.Config;
 import net.runelite.client.config.ConfigGroup;
@@ -781,8 +781,8 @@ public interface DinkPluginConfig extends Config {
         position = 112,
         section = diarySection
     )
-    default AchievementDiaries.Difficulty minDiaryDifficulty() {
-        return AchievementDiaries.Difficulty.EASY;
+    default AchievementDiary.Difficulty minDiaryDifficulty() {
+        return AchievementDiary.Difficulty.EASY;
     }
 
     @ConfigItem(
diff --git a/src/main/java/dinkplugin/domain/AchievementDiaries.java b/src/main/java/dinkplugin/domain/AchievementDiaries.java
deleted file mode 100644
index 150fced..0000000
--- a/src/main/java/dinkplugin/domain/AchievementDiaries.java
+++ /dev/null
@@ -1,84 +0,0 @@
-package dinkplugin.domain;
-
-import lombok.Getter;
-import lombok.Value;
-import lombok.extern.slf4j.Slf4j;
-import net.runelite.api.Varbits;
-import net.runelite.api.annotations.Varbit;
-import org.apache.commons.lang3.StringUtils;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import java.lang.reflect.Field;
-import java.lang.reflect.Modifier;
-import java.util.Arrays;
-import java.util.Map;
-import java.util.Objects;
-import java.util.function.Function;
-import java.util.stream.Collectors;
-
-@Slf4j
-public enum AchievementDiaries {
-
-    INSTANCE;
-
-    @Getter
-    private final Map<Integer, Diary> diaries;
-
-    AchievementDiaries() {
-        this.diaries = Arrays.stream(Varbits.class.getDeclaredFields())
-            .filter(field -> Modifier.isPublic(field.getModifiers()))
-            .filter(field -> Modifier.isStatic(field.getModifiers()))
-            .filter(field -> Modifier.isFinal(field.getModifiers()))
-            .filter(field -> field.getType() == int.class)
-            .filter(field -> field.getName().startsWith("DIARY_"))
-            .map(Diary::from)
-            .filter(Objects::nonNull)
-            .collect(Collectors.toMap(Diary::getId, Function.identity()));
-    }
-
-    public enum Difficulty {
-        EASY,
-        MEDIUM,
-        HARD,
-        ELITE;
-
-        private final String displayName = this.name().charAt(0) + this.name().substring(1).toLowerCase();
-
-        @Override
-        public String toString() {
-            return this.displayName;
-        }
-    }
-
-    @Value
-    public static class Diary {
-        @Varbit
-        int id;
-        String area;
-        Difficulty difficulty;
-
-        @Nullable
-        static Diary from(@NotNull Field varbit) {
-            try {
-                int id = varbit.getInt(null);
-
-                String fieldName = varbit.getName();
-                String[] parts = StringUtils.split(fieldName, '_');
-                if (parts.length != 3) return null;
-
-                Difficulty difficulty = Difficulty.valueOf(parts[2]);
-
-                String area = parts[1];
-                area = area.charAt(0) + area.substring(1).toLowerCase();
-                if ("Western".equals(area)) area = "Western Provinces";
-
-                return new Diary(id, area, difficulty);
-            } catch (Exception e) {
-                log.debug("Failed to parse achievement diary: " + varbit.getName(), e);
-                return null;
-            }
-        }
-    }
-
-}
diff --git a/src/main/java/dinkplugin/domain/AchievementDiary.java b/src/main/java/dinkplugin/domain/AchievementDiary.java
new file mode 100644
index 0000000..7cf3b8e
--- /dev/null
+++ b/src/main/java/dinkplugin/domain/AchievementDiary.java
@@ -0,0 +1,89 @@
+package dinkplugin.domain;
+
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+import net.runelite.api.Varbits;
+import net.runelite.api.annotations.Varbit;
+import org.apache.commons.lang3.tuple.Pair;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+@Getter
+@RequiredArgsConstructor
+public enum AchievementDiary {
+    ARDOUGNE_EASY(Varbits.DIARY_ARDOUGNE_EASY, "Ardougne", Difficulty.EASY),
+    ARDOUGNE_MEDIUM(Varbits.DIARY_ARDOUGNE_MEDIUM, "Ardougne", Difficulty.MEDIUM),
+    ARDOUGNE_HARD(Varbits.DIARY_ARDOUGNE_HARD, "Ardougne", Difficulty.HARD),
+    ARDOUGNE_ELITE(Varbits.DIARY_ARDOUGNE_ELITE, "Ardougne", Difficulty.ELITE),
+    DESERT_EASY(Varbits.DIARY_DESERT_EASY, "Desert", Difficulty.EASY),
+    DESERT_MEDIUM(Varbits.DIARY_DESERT_MEDIUM, "Desert", Difficulty.MEDIUM),
+    DESERT_HARD(Varbits.DIARY_DESERT_HARD, "Desert", Difficulty.HARD),
+    DESERT_ELITE(Varbits.DIARY_DESERT_ELITE, "Desert", Difficulty.ELITE),
+    FALADOR_EASY(Varbits.DIARY_FALADOR_EASY, "Falador", Difficulty.EASY),
+    FALADOR_MEDIUM(Varbits.DIARY_FALADOR_MEDIUM, "Falador", Difficulty.MEDIUM),
+    FALADOR_HARD(Varbits.DIARY_FALADOR_HARD, "Falador", Difficulty.HARD),
+    FALADOR_ELITE(Varbits.DIARY_FALADOR_ELITE, "Falador", Difficulty.ELITE),
+    FREMENNIK_EASY(Varbits.DIARY_FREMENNIK_EASY, "Fremennik", Difficulty.EASY),
+    FREMENNIK_MEDIUM(Varbits.DIARY_FREMENNIK_MEDIUM, "Fremennik", Difficulty.MEDIUM),
+    FREMENNIK_HARD(Varbits.DIARY_FREMENNIK_HARD, "Fremennik", Difficulty.HARD),
+    FREMENNIK_ELITE(Varbits.DIARY_FREMENNIK_ELITE, "Fremennik", Difficulty.ELITE),
+    KANDARIN_EASY(Varbits.DIARY_KANDARIN_EASY, "Kandarin", Difficulty.EASY),
+    KANDARIN_MEDIUM(Varbits.DIARY_KANDARIN_MEDIUM, "Kandarin", Difficulty.MEDIUM),
+    KANDARIN_HARD(Varbits.DIARY_KANDARIN_HARD, "Kandarin", Difficulty.HARD),
+    KANDARIN_ELITE(Varbits.DIARY_KANDARIN_ELITE, "Kandarin", Difficulty.ELITE),
+    KARAMJA_EASY(Varbits.DIARY_KARAMJA_EASY, "Karamja", Difficulty.EASY),
+    KARAMJA_MEDIUM(Varbits.DIARY_KARAMJA_MEDIUM, "Karamja", Difficulty.MEDIUM),
+    KARAMJA_HARD(Varbits.DIARY_KARAMJA_HARD, "Karamja", Difficulty.HARD),
+    KARAMJA_ELITE(Varbits.DIARY_KARAMJA_ELITE, "Karamja", Difficulty.ELITE),
+    KOUREND_EASY(Varbits.DIARY_KOUREND_EASY, "Kourend & Kebos", Difficulty.EASY),
+    KOUREND_MEDIUM(Varbits.DIARY_KOUREND_MEDIUM, "Kourend & Kebos", Difficulty.MEDIUM),
+    KOUREND_HARD(Varbits.DIARY_KOUREND_HARD, "Kourend & Kebos", Difficulty.HARD),
+    KOUREND_ELITE(Varbits.DIARY_KOUREND_ELITE, "Kourend & Kebos", Difficulty.ELITE),
+    LUMBRIDGE_EASY(Varbits.DIARY_LUMBRIDGE_EASY, "Lumbridge & Draynor", Difficulty.EASY),
+    LUMBRIDGE_MEDIUM(Varbits.DIARY_LUMBRIDGE_MEDIUM, "Lumbridge & Draynor", Difficulty.MEDIUM),
+    LUMBRIDGE_HARD(Varbits.DIARY_LUMBRIDGE_HARD, "Lumbridge & Draynor", Difficulty.HARD),
+    LUMBRIDGE_ELITE(Varbits.DIARY_LUMBRIDGE_ELITE, "Lumbridge & Draynor", Difficulty.ELITE),
+    MORYTANIA_EASY(Varbits.DIARY_MORYTANIA_EASY, "Morytania", Difficulty.EASY),
+    MORYTANIA_MEDIUM(Varbits.DIARY_MORYTANIA_MEDIUM, "Morytania", Difficulty.MEDIUM),
+    MORYTANIA_HARD(Varbits.DIARY_MORYTANIA_HARD, "Morytania", Difficulty.HARD),
+    MORYTANIA_ELITE(Varbits.DIARY_MORYTANIA_ELITE, "Morytania", Difficulty.ELITE),
+    VARROCK_EASY(Varbits.DIARY_VARROCK_EASY, "Varrock", Difficulty.EASY),
+    VARROCK_MEDIUM(Varbits.DIARY_VARROCK_MEDIUM, "Varrock", Difficulty.MEDIUM),
+    VARROCK_HARD(Varbits.DIARY_VARROCK_HARD, "Varrock", Difficulty.HARD),
+    VARROCK_ELITE(Varbits.DIARY_VARROCK_ELITE, "Varrock", Difficulty.ELITE),
+    WESTERN_EASY(Varbits.DIARY_WESTERN_EASY, "Western Provinces", Difficulty.EASY),
+    WESTERN_MEDIUM(Varbits.DIARY_WESTERN_MEDIUM, "Western Provinces", Difficulty.MEDIUM),
+    WESTERN_HARD(Varbits.DIARY_WESTERN_HARD, "Western Provinces", Difficulty.HARD),
+    WESTERN_ELITE(Varbits.DIARY_WESTERN_ELITE, "Western Provinces", Difficulty.ELITE),
+    WILDERNESS_EASY(Varbits.DIARY_WILDERNESS_EASY, "Wilderness", Difficulty.EASY),
+    WILDERNESS_MEDIUM(Varbits.DIARY_WILDERNESS_MEDIUM, "Wilderness", Difficulty.MEDIUM),
+    WILDERNESS_HARD(Varbits.DIARY_WILDERNESS_HARD, "Wilderness", Difficulty.HARD),
+    WILDERNESS_ELITE(Varbits.DIARY_WILDERNESS_ELITE, "Wilderness", Difficulty.ELITE);
+
+    public static final Map<Integer, Pair<String, Difficulty>> DIARIES = Collections.unmodifiableMap(
+        Arrays.stream(values())
+            .collect(Collectors.toMap(AchievementDiary::getId, diary -> Pair.of(diary.getArea(), diary.getDifficulty())))
+    );
+
+    @Varbit
+    private final int id;
+    private final String area;
+    private final Difficulty difficulty;
+
+    public enum Difficulty {
+        EASY,
+        MEDIUM,
+        HARD,
+        ELITE;
+
+        private final String displayName = this.name().charAt(0) + this.name().substring(1).toLowerCase();
+
+        @Override
+        public String toString() {
+            return this.displayName;
+        }
+    }
+}
diff --git a/src/main/java/dinkplugin/notifiers/DiaryNotifier.java b/src/main/java/dinkplugin/notifiers/DiaryNotifier.java
index 7a38313..891f476 100644
--- a/src/main/java/dinkplugin/notifiers/DiaryNotifier.java
+++ b/src/main/java/dinkplugin/notifiers/DiaryNotifier.java
@@ -2,7 +2,7 @@ package dinkplugin.notifiers;
 
 import dinkplugin.DinkPluginConfig;
 import dinkplugin.Utils;
-import dinkplugin.domain.AchievementDiaries;
+import dinkplugin.domain.AchievementDiary;
 import dinkplugin.message.NotificationBody;
 import dinkplugin.message.NotificationType;
 import dinkplugin.notifiers.data.DiaryNotificationData;
@@ -10,14 +10,16 @@ import net.runelite.api.GameState;
 import net.runelite.api.events.GameStateChanged;
 import net.runelite.api.events.VarbitChanged;
 import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.tuple.Pair;
 
 import javax.inject.Singleton;
 import java.util.HashMap;
 import java.util.Map;
 
+import static dinkplugin.domain.AchievementDiary.DIARIES;
+
 @Singleton
 public class DiaryNotifier extends BaseNotifier {
-    private static final Map<Integer, AchievementDiaries.Diary> DIARIES = AchievementDiaries.INSTANCE.getDiaries();
     private final Map<Integer, Integer> diaryCompletionById = new HashMap<>();
     private int initDelayTicks = 0;
 
@@ -59,7 +61,7 @@ public class DiaryNotifier extends BaseNotifier {
     public void onVarbitChanged(VarbitChanged event) {
         int id = event.getVarbitId();
         if (id < 0) return;
-        AchievementDiaries.Diary diary = DIARIES.get(id);
+        Pair<String, AchievementDiary.Difficulty> diary = DIARIES.get(id);
         if (diary == null) return;
         if (diaryCompletionById.isEmpty()) return;
         if (!super.isEnabled()) return;
@@ -76,31 +78,31 @@ public class DiaryNotifier extends BaseNotifier {
                 return;
             }
 
-            if (checkDifficulty(diary))
-                handle(diary);
+            if (checkDifficulty(diary.getRight()))
+                handle(diary.getLeft(), diary.getRight());
         }
     }
 
-    private void handle(AchievementDiaries.Diary diary) {
+    private void handle(String area, AchievementDiary.Difficulty difficulty) {
         int total = getTotalCompleted();
         String player = Utils.getPlayerName(client);
         String message = StringUtils.replaceEach(
             config.diaryNotifyMessage(),
             new String[] { "%USERNAME%", "%DIFFICULTY%", "%AREA%", "%TOTAL%" },
-            new String[] { player, diary.getDifficulty().toString(), diary.getArea(), String.valueOf(total) }
+            new String[] { player, difficulty.toString(), area, String.valueOf(total) }
         );
 
         createMessage(DinkPluginConfig::diarySendImage, NotificationBody.builder()
             .type(NotificationType.ACHIEVEMENT_DIARY)
             .content(message)
-            .extra(new DiaryNotificationData(diary.getArea(), diary.getDifficulty(), total))
+            .extra(new DiaryNotificationData(area, difficulty, total))
             .playerName(player)
             .screenshotFile("achievementDiaryImage.png")
             .build());
     }
 
-    private boolean checkDifficulty(AchievementDiaries.Diary diary) {
-        return config.notifyAchievementDiary() && diary.getDifficulty().ordinal() >= config.minDiaryDifficulty().ordinal();
+    private boolean checkDifficulty(AchievementDiary.Difficulty difficulty) {
+        return config.notifyAchievementDiary() && difficulty.ordinal() >= config.minDiaryDifficulty().ordinal();
     }
 
     private int getTotalCompleted() {
diff --git a/src/main/java/dinkplugin/notifiers/data/DiaryNotificationData.java b/src/main/java/dinkplugin/notifiers/data/DiaryNotificationData.java
index 414fe1a..87a80bc 100644
--- a/src/main/java/dinkplugin/notifiers/data/DiaryNotificationData.java
+++ b/src/main/java/dinkplugin/notifiers/data/DiaryNotificationData.java
@@ -1,11 +1,11 @@
 package dinkplugin.notifiers.data;
 
-import dinkplugin.domain.AchievementDiaries;
+import dinkplugin.domain.AchievementDiary;
 import lombok.Value;
 
 @Value
 public class DiaryNotificationData {
     String area;
-    AchievementDiaries.Difficulty difficulty;
+    AchievementDiary.Difficulty difficulty;
     int total;
 }


Test patch:
diff --git a/src/test/java/dinkplugin/domain/AchievementDiariesTest.java b/src/test/java/dinkplugin/domain/AchievementDiariesTest.java
deleted file mode 100644
index faeac09..0000000
--- a/src/test/java/dinkplugin/domain/AchievementDiariesTest.java
+++ /dev/null
@@ -1,30 +0,0 @@
-package dinkplugin.domain;
-
-import org.junit.jupiter.api.DisplayName;
-import org.junit.jupiter.api.Test;
-
-import java.util.Map;
-
-import static net.runelite.api.Varbits.*;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertNull;
-
-class AchievementDiariesTest {
-
-    @Test
-    @DisplayName("Test that all Achievement Diaries have been parsed")
-    void testParsingSuccessful() {
-        Map<Integer, AchievementDiaries.Diary> diaries = AchievementDiaries.INSTANCE.getDiaries();
-
-        assertEquals(48, diaries.size());
-        assertEquals(new AchievementDiaries.Diary(DIARY_VARROCK_EASY, "Varrock", AchievementDiaries.Difficulty.EASY), diaries.get(DIARY_VARROCK_EASY));
-        assertEquals(new AchievementDiaries.Diary(DIARY_FALADOR_MEDIUM, "Falador", AchievementDiaries.Difficulty.MEDIUM), diaries.get(DIARY_FALADOR_MEDIUM));
-        assertEquals(new AchievementDiaries.Diary(DIARY_DESERT_HARD, "Desert", AchievementDiaries.Difficulty.HARD), diaries.get(DIARY_DESERT_HARD));
-        assertEquals(new AchievementDiaries.Diary(DIARY_KARAMJA_ELITE, "Karamja", AchievementDiaries.Difficulty.ELITE), diaries.get(DIARY_KARAMJA_ELITE));
-        assertEquals(new AchievementDiaries.Diary(DIARY_KARAMJA_HARD, "Karamja", AchievementDiaries.Difficulty.HARD), diaries.get(DIARY_KARAMJA_HARD));
-        assertNull(diaries.get(3610));
-        assertNull(diaries.get(PRAYER_AUGURY));
-        assertNull(diaries.get(KOUREND_FAVOR_ARCEUUS));
-    }
-
-}
diff --git a/src/test/java/dinkplugin/notifiers/CombatTaskNotifierTest.java b/src/test/java/dinkplugin/notifiers/CombatTaskNotifierTest.java
index 5297b5f..8d98806 100644
--- a/src/test/java/dinkplugin/notifiers/CombatTaskNotifierTest.java
+++ b/src/test/java/dinkplugin/notifiers/CombatTaskNotifierTest.java
@@ -1,6 +1,6 @@
 package dinkplugin.notifiers;
 
-import dinkplugin.domain.AchievementDiaries;
+import dinkplugin.domain.AchievementDiary;
 import dinkplugin.domain.CombatAchievementTier;
 import dinkplugin.message.NotificationBody;
 import dinkplugin.message.NotificationType;
@@ -42,7 +42,7 @@ class CombatTaskNotifierTest extends MockedNotifierTest {
             PRIMARY_WEBHOOK_URL,
             false,
             NotificationBody.builder()
-                .content(String.format("%s has completed %s combat task: %s", PLAYER_NAME, AchievementDiaries.Difficulty.HARD, "Whack-a-Mole"))
+                .content(String.format("%s has completed %s combat task: %s", PLAYER_NAME, "Hard", "Whack-a-Mole"))
                 .extra(new CombatAchievementData(CombatAchievementTier.HARD, "Whack-a-Mole"))
                 .playerName(PLAYER_NAME)
                 .type(NotificationType.COMBAT_ACHIEVEMENT)
diff --git a/src/test/java/dinkplugin/notifiers/DiaryNotifierTest.java b/src/test/java/dinkplugin/notifiers/DiaryNotifierTest.java
index bc2b58f..920ff67 100644
--- a/src/test/java/dinkplugin/notifiers/DiaryNotifierTest.java
+++ b/src/test/java/dinkplugin/notifiers/DiaryNotifierTest.java
@@ -1,6 +1,6 @@
 package dinkplugin.notifiers;
 
-import dinkplugin.domain.AchievementDiaries;
+import dinkplugin.domain.AchievementDiary;
 import dinkplugin.message.NotificationBody;
 import dinkplugin.message.NotificationType;
 import dinkplugin.notifiers.data.DiaryNotificationData;
@@ -34,7 +34,7 @@ class DiaryNotifierTest extends MockedNotifierTest {
         // init config mocks
         when(config.notifyAchievementDiary()).thenReturn(true);
         when(config.diarySendImage()).thenReturn(false);
-        when(config.minDiaryDifficulty()).thenReturn(AchievementDiaries.Difficulty.MEDIUM);
+        when(config.minDiaryDifficulty()).thenReturn(AchievementDiary.Difficulty.MEDIUM);
         when(config.diaryNotifyMessage()).thenReturn("%USERNAME% has completed the %DIFFICULTY% %AREA% Diary, for a total of %TOTAL%");
 
         // init client mocks
@@ -58,8 +58,8 @@ class DiaryNotifierTest extends MockedNotifierTest {
             PRIMARY_WEBHOOK_URL,
             false,
             NotificationBody.builder()
-                .content(String.format("%s has completed the %s %s Diary, for a total of %d", PLAYER_NAME, AchievementDiaries.Difficulty.ELITE, "Desert", 1))
-                .extra(new DiaryNotificationData("Desert", AchievementDiaries.Difficulty.ELITE, 1))
+                .content(String.format("%s has completed the %s %s Diary, for a total of %d", PLAYER_NAME, AchievementDiary.Difficulty.ELITE, "Desert", 1))
+                .extra(new DiaryNotificationData("Desert", AchievementDiary.Difficulty.ELITE, 1))
                 .type(NotificationType.ACHIEVEMENT_DIARY)
                 .playerName(PLAYER_NAME)
                 .build()
@@ -70,7 +70,7 @@ class DiaryNotifierTest extends MockedNotifierTest {
     void testNotifyKaramja() {
         // initially many diary completions
         when(client.getVarbitValue(anyInt())).thenReturn(1);
-        int total = AchievementDiaries.INSTANCE.getDiaries().size() - 3;
+        int total = AchievementDiary.DIARIES.size() - 3;
 
         // perform enough ticks to trigger diary initialization
         IntStream.range(0, 16).forEach(i -> notifier.onTick());
@@ -84,8 +84,8 @@ class DiaryNotifierTest extends MockedNotifierTest {
             PRIMARY_WEBHOOK_URL,
             false,
             NotificationBody.builder()
-                .content(String.format("%s has completed the %s %s Diary, for a total of %d", PLAYER_NAME, AchievementDiaries.Difficulty.HARD, "Karamja", total + 1))
-                .extra(new DiaryNotificationData("Karamja", AchievementDiaries.Difficulty.HARD, total + 1))
+                .content(String.format("%s has completed the %s %s Diary, for a total of %d", PLAYER_NAME, AchievementDiary.Difficulty.HARD, "Karamja", total + 1))
+                .extra(new DiaryNotificationData("Karamja", AchievementDiary.Difficulty.HARD, total + 1))
                 .type(NotificationType.ACHIEVEMENT_DIARY)
                 .playerName(PLAYER_NAME)
                 .build()