diff --git a/CHANGELOG.md b/CHANGELOG.md index 3e7373c2..66432652 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ ## Unreleased +- Minor: Include owned pets in login notification metadata. (#347) - Minor: Include individual skill XP in login notification metadata. (#345) - Bugfix: Improve handling of queued notifications upon concurrent config changes. (#355) diff --git a/README.md b/README.md index 3a00c6f0..4eb88045 100644 --- a/README.md +++ b/README.md @@ -859,13 +859,25 @@ On login, Dink can submit a character summary containing data that spans multipl "slayer": { "points": 2204, "streak": 1074 - } + }, + "pets": [ + { + "itemId": 11995, + "name": "Pet chaos elemental" + }, + { + "itemId": 13071, + "name": "Chompy chick" + } + ] } } ``` Note: `clanName` requires `Advanced > Send Clan Name` to be enabled (default: on). The `groupIronClanName` and `discordUser` fields also have similar toggles in the Advanced config section. +Note: `extra.pets` requires the base Chat Commands plugin to be enabled. + ## Credits diff --git a/src/main/java/dinkplugin/notifiers/MetaNotifier.java b/src/main/java/dinkplugin/notifiers/MetaNotifier.java index 9b435dba..4269fb61 100644 --- a/src/main/java/dinkplugin/notifiers/MetaNotifier.java +++ b/src/main/java/dinkplugin/notifiers/MetaNotifier.java @@ -1,5 +1,7 @@ package dinkplugin.notifiers; +import com.google.gson.Gson; +import com.google.gson.JsonSyntaxException; import dinkplugin.domain.AchievementDiary; import dinkplugin.message.NotificationBody; import dinkplugin.message.NotificationType; @@ -7,7 +9,9 @@ import dinkplugin.message.templating.Template; import dinkplugin.notifiers.data.LoginNotificationData; import dinkplugin.notifiers.data.Progress; +import dinkplugin.util.SerializedPet; import dinkplugin.util.Utils; +import lombok.extern.slf4j.Slf4j; import net.runelite.api.Experience; import net.runelite.api.GameState; import net.runelite.api.Skill; @@ -15,18 +19,26 @@ import net.runelite.api.Varbits; import net.runelite.api.events.GameStateChanged; import net.runelite.client.callback.ClientThread; +import net.runelite.client.config.ConfigManager; +import net.runelite.client.config.RuneLiteConfig; +import net.runelite.client.plugins.chatcommands.ChatCommandsPlugin; import org.apache.commons.lang3.StringUtils; import org.jetbrains.annotations.VisibleForTesting; import javax.inject.Inject; import javax.inject.Singleton; +import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; +@Slf4j @Singleton public class MetaNotifier extends BaseNotifier { + static final @VisibleForTesting String RL_CHAT_CMD_PLUGIN_NAME = ChatCommandsPlugin.class.getSimpleName().toLowerCase(); static final @VisibleForTesting int INIT_TICKS = 10; // 6 seconds after login private final AtomicInteger loginTicks = new AtomicInteger(-1); @@ -35,6 +47,12 @@ public class MetaNotifier extends BaseNotifier { @Inject private ClientThread clientThread; + @Inject + private ConfigManager configManager; + + @Inject + private Gson gson; + @Override public boolean isEnabled() { return StringUtils.isNotBlank(config.metadataWebhook()) && super.isEnabled(); @@ -123,7 +141,8 @@ private void notifyLogin() { new LoginNotificationData.SkillData(experienceTotal, levelTotal, skillLevels, skillExperience), Progress.of(questsCompleted, questsTotal), Progress.of(questPoints, questPointsTotal), - new LoginNotificationData.SlayerData(slayerPoints, slayerStreak) + new LoginNotificationData.SlayerData(slayerPoints, slayerStreak), + getPets() ); createMessage(false, NotificationBody.builder() .type(NotificationType.LOGIN) @@ -134,4 +153,28 @@ private void notifyLogin() { ); } + @VisibleForTesting + List getPets() { + if ("false".equals(configManager.getConfiguration(RuneLiteConfig.GROUP_NAME, RL_CHAT_CMD_PLUGIN_NAME))) + return null; + + String json = configManager.getRSProfileConfiguration("chatcommands", "pets2"); + if (json == null || json.isEmpty()) + return null; + + int[] petItemIds; + try { + petItemIds = gson.fromJson(json, int[].class); + } catch (JsonSyntaxException e) { + log.info("Failed to deserialize owned pet IDs", e); + return null; + } + + List pets = new ArrayList<>(petItemIds.length); + for (int itemId : petItemIds) { + pets.add(new SerializedPet(itemId, client.getItemDefinition(itemId).getMembersName())); + } + return pets; + } + } diff --git a/src/main/java/dinkplugin/notifiers/data/LoginNotificationData.java b/src/main/java/dinkplugin/notifiers/data/LoginNotificationData.java index b0f859f4..9fcd417d 100644 --- a/src/main/java/dinkplugin/notifiers/data/LoginNotificationData.java +++ b/src/main/java/dinkplugin/notifiers/data/LoginNotificationData.java @@ -1,9 +1,11 @@ package dinkplugin.notifiers.data; +import dinkplugin.util.SerializedPet; import lombok.EqualsAndHashCode; import lombok.Value; import org.jetbrains.annotations.Nullable; +import java.util.List; import java.util.Map; @Value @@ -21,6 +23,8 @@ public class LoginNotificationData extends NotificationData { Progress questCount; Progress questPoints; SlayerData slayer; + @Nullable // requires Chat Commands plugin to be enabled + List pets; @Value public static class SkillData { diff --git a/src/main/java/dinkplugin/util/SerializedPet.java b/src/main/java/dinkplugin/util/SerializedPet.java new file mode 100644 index 00000000..2cac4bfe --- /dev/null +++ b/src/main/java/dinkplugin/util/SerializedPet.java @@ -0,0 +1,9 @@ +package dinkplugin.util; + +import lombok.Value; + +@Value +public class SerializedPet { + int itemId; + String name; +} diff --git a/src/test/java/dinkplugin/notifiers/MetaNotifierTest.java b/src/test/java/dinkplugin/notifiers/MetaNotifierTest.java index 5938d9da..23c186b5 100644 --- a/src/test/java/dinkplugin/notifiers/MetaNotifierTest.java +++ b/src/test/java/dinkplugin/notifiers/MetaNotifierTest.java @@ -5,18 +5,23 @@ import dinkplugin.message.NotificationType; import dinkplugin.notifiers.data.LoginNotificationData; import dinkplugin.notifiers.data.Progress; +import dinkplugin.util.SerializedPet; import net.runelite.api.Experience; import net.runelite.api.GameState; +import net.runelite.api.ItemID; import net.runelite.api.Skill; import net.runelite.api.VarPlayer; import net.runelite.api.Varbits; import net.runelite.api.events.GameStateChanged; +import net.runelite.client.config.RuneLiteConfig; import org.apache.commons.lang3.StringUtils; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; import java.util.Arrays; +import java.util.List; import java.util.Map; import java.util.stream.Collectors; import java.util.stream.IntStream; @@ -100,7 +105,8 @@ void testNotify() { new LoginNotificationData.BarbarianAssault(666), new LoginNotificationData.SkillData(xp * skillCount, level * skillCount, levels, exp), Progress.of(21, 158), Progress.of(43, 300), - new LoginNotificationData.SlayerData(2484, 300) + new LoginNotificationData.SlayerData(2484, 300), + null ); verify(messageHandler).createMessage( url, @@ -139,7 +145,8 @@ void testNotifyWithoutCollection() { new LoginNotificationData.BarbarianAssault(666), new LoginNotificationData.SkillData(xp * skillCount, level * skillCount, levels, exp), Progress.of(21, 158), Progress.of(43, 300), - new LoginNotificationData.SlayerData(2484, 300) + new LoginNotificationData.SlayerData(2484, 300), + null ); verify(messageHandler).createMessage( url, @@ -168,6 +175,24 @@ void testDisabled() { verify(messageHandler, never()).createMessage(any(), anyBoolean(), any()); } + @Test + void testPetDeserialization() { + when(configManager.getConfiguration(RuneLiteConfig.GROUP_NAME, MetaNotifier.RL_CHAT_CMD_PLUGIN_NAME)) + .thenReturn(Boolean.TRUE.toString()); + + when(configManager.getRSProfileConfiguration("chatcommands", "pets2")) + .thenReturn(String.format("[%d, %d]", ItemID.HERBI, ItemID.BABY_MOLE)); + + mockItem(ItemID.HERBI, 0, "Herbi"); + mockItem(ItemID.BABY_MOLE, 0, "Baby mole"); + + List expected = List.of( + new SerializedPet(ItemID.HERBI, "Herbi"), + new SerializedPet(ItemID.BABY_MOLE, "Baby mole") + ); + Assertions.assertEquals(expected, notifier.getPets()); + } + private static GameStateChanged event(GameState state) { GameStateChanged event = new GameStateChanged(); event.setGameState(state); diff --git a/src/test/java/dinkplugin/notifiers/MockedNotifierTest.java b/src/test/java/dinkplugin/notifiers/MockedNotifierTest.java index 46647534..fd99d9a6 100644 --- a/src/test/java/dinkplugin/notifiers/MockedNotifierTest.java +++ b/src/test/java/dinkplugin/notifiers/MockedNotifierTest.java @@ -138,6 +138,7 @@ protected void mockItem(int id, int price, String name) { ItemComposition item = mock(ItemComposition.class); when(item.getName()).thenReturn(name); when(item.getMembersName()).thenReturn(name); + when(client.getItemDefinition(id)).thenReturn(item); when(itemManager.getItemComposition(id)).thenReturn(item); when(itemManager.canonicalize(id)).thenReturn(id); }