Repository: pajlads/DinkPlugin (java)

Issue:
[Feature] Introduce extra substitute variable(s) for Collection Log notifier
### Checklist

- [X] I've searched the [issues and pull requests](https://github.com/pajlads/Dinkplugin/issues?q=) for similar looking suggestions.
- [X] I've checked the `Unreleased` section of the [changelog](https://github.com/pajlads/DinkPlugin/blob/master/CHANGELOG.md) for newly added features that sound like my suggestion.

### Describe your Suggestion

I'd wanna have an ability to add a substitute variable to Dink for amount of collection logs a player has gotten to the notifier. For example, say I'd be called `LOGS_AMOUNT` and so using `%LOGS_AMOUNT%` inside the notifier message would be simply substituted with `592` in this scenario below: 
![in-game interface showing amount of clogs gotten and total amount](https://cdn.zneix.eu/Ra4IhkP.png)

Also, introducing an another variable for the total amount is perhaps not needed, but if user were to hardcode the amount of collection logs (for example, to made their message look like what's available in the in-game inferface) available then every once in a while, when new items are added to the game & collection log, it'd be a silly thing to update, which I could also see it tedious to keep having users have to "upkeep" that.

Names for variables are up for discussion though - don't get hung up on my original suggestion with `LOGS_AMOUNT`; I think it's not good, especially with how ambiguous it could get, especially if there was the second one added too, as described in paragraph above.

### Reasoning

Friend of mine has reached out saying they were looking for a way to include the amount of collection logs, similar to how it's presented in the in-game interface, in the posted messaged, I do think it'd be nice, albeit niche, addition for whoever wants to personalize their messages.

Pull request description:
feat(collection): add completed and total as template vars
Closes #437


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6e5fe75..5268d54 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,7 @@
 ## Unreleased
 
 - Major: Add item rarity to monster loot and collection notifications. (#425, #427)
+- Minor: Add `%COMPLETED` and `%TOTAL_POSSIBLE%` variables for collection log template. (#439)
 - Minor: Add wiki link for loot source in rich embeds. (#431)
 - Minor: Add chat message upon noteworthy plugin updates. (#429, #436)
 - Minor: Allow loot notifications for rare items below the min value threshold. (#426)
diff --git a/README.md b/README.md
index 3684324..83e679b 100644
--- a/README.md
+++ b/README.md
@@ -148,6 +148,12 @@ Lastly, Dink makes exceptions for Inferno and TzHaar Fight Cave; deaths in these
 
 `%ITEM%` will be replaced with the item that was dropped for the collection log.
 
+`%COMPLETED%` will be replaced with the number of unique entries that have been completed.
+
+`%TOTAL_POSSIBLE%` will be replaced with the total number of unique entries that are tracked in the collection log.
+
+Note: `%COMPLETED%` may not be populated if the [Character Summary](https://oldschool.runescape.wiki/w/Character_Summary) tab was never selected since logging in.
+
 ### Level:
 
 `%SKILL%` will be replaced with the skill name and level that was achieved
diff --git a/src/main/java/dinkplugin/DinkPluginConfig.java b/src/main/java/dinkplugin/DinkPluginConfig.java
index a64331a..0fd4605 100644
--- a/src/main/java/dinkplugin/DinkPluginConfig.java
+++ b/src/main/java/dinkplugin/DinkPluginConfig.java
@@ -631,7 +631,9 @@ public interface DinkPluginConfig extends Config {
         name = "Notification Message",
         description = "The message to be sent through the webhook.<br/>" +
             "Use %USERNAME% to insert your username<br/>" +
-            "Use %ITEM% for the item",
+            "Use %ITEM% to insert the item name<br/>" +
+            "Use %COMPLETED% to insert the number of completed entries<br/>" +
+            "Use %TOTAL_POSSIBLE% to insert the number of possible entries",
         position = 3,
         section = collectionSection
     )
diff --git a/src/main/java/dinkplugin/notifiers/CollectionNotifier.java b/src/main/java/dinkplugin/notifiers/CollectionNotifier.java
index 55a332f..95351d8 100644
--- a/src/main/java/dinkplugin/notifiers/CollectionNotifier.java
+++ b/src/main/java/dinkplugin/notifiers/CollectionNotifier.java
@@ -24,6 +24,7 @@ import net.runelite.client.game.ItemManager;
 import net.runelite.client.game.ItemStack;
 import net.runelite.http.api.loottracker.LootRecordType;
 import org.jetbrains.annotations.Nullable;
+import org.jetbrains.annotations.VisibleForTesting;
 
 import javax.inject.Inject;
 import java.time.Duration;
@@ -45,6 +46,8 @@ public class CollectionNotifier extends BaseNotifier {
      */
     public static final @Varp int COMPLETED_VARP = 2943, TOTAL_VARP = 2944;
 
+    static final @VisibleForTesting int TOTAL_ENTRIES = 1_491; // fallback if TOTAL_VARP is not populated
+
     private static final Duration RECENT_DROP = Duration.ofSeconds(30L);
 
     /**
@@ -143,13 +146,6 @@ public class CollectionNotifier extends BaseNotifier {
     }
 
     private void handleNotify(String itemName) {
-        Template notifyMessage = Template.builder()
-            .template(config.collectionNotifyMessage())
-            .replacementBoundary("%")
-            .replacement("%USERNAME%", Replacements.ofText(Utils.getPlayerName(client)))
-            .replacement("%ITEM%", Replacements.ofWiki(itemName))
-            .build();
-
         // varp isn't updated for a few ticks, so we increment the count locally.
         // this approach also has the benefit of yielding incrementing values even when
         // multiple collection log entries are completed within a single tick.
@@ -161,6 +157,17 @@ public class CollectionNotifier extends BaseNotifier {
             log.debug("Collection log progress varps were invalid ({} / {})", completed, total);
         }
 
+        // build message
+        Template notifyMessage = Template.builder()
+            .template(config.collectionNotifyMessage())
+            .replacementBoundary("%")
+            .replacement("%USERNAME%", Replacements.ofText(Utils.getPlayerName(client)))
+            .replacement("%ITEM%", Replacements.ofWiki(itemName))
+            .replacement("%COMPLETED%", Replacements.ofText(completed > 0 ? String.valueOf(completed) : "?"))
+            .replacement("%TOTAL_POSSIBLE%", Replacements.ofText(String.valueOf(total > 0 ? total : TOTAL_ENTRIES)))
+            .build();
+
+        // populate metadata
         Integer itemId = itemSearcher.findItemId(itemName);
         Long price = itemId != null ? ItemUtils.getPrice(itemManager, itemId) : null;
         Drop loot = itemId != null ? getLootSource(itemId) : null;


Test patch:
diff --git a/src/test/java/dinkplugin/notifiers/CollectionNotifierTest.java b/src/test/java/dinkplugin/notifiers/CollectionNotifierTest.java
index e011f4b..4e89431 100644
--- a/src/test/java/dinkplugin/notifiers/CollectionNotifierTest.java
+++ b/src/test/java/dinkplugin/notifiers/CollectionNotifierTest.java
@@ -28,6 +28,7 @@ import org.mockito.Mockito;
 import java.util.List;
 import java.util.function.BiFunction;
 
+import static dinkplugin.notifiers.CollectionNotifier.TOTAL_ENTRIES;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.Mockito.mock;
@@ -37,8 +38,6 @@ import static org.mockito.Mockito.when;
 
 class CollectionNotifierTest extends MockedNotifierTest {
 
-    private static final int TOTAL_ENTRIES = 1443;
-
     @Bind
     @InjectMocks
     CollectionNotifier notifier;