From 21a7bf49064a2d6abdd4a1fe74dea0f5b4e1336c Mon Sep 17 00:00:00 2001 From: Adam Date: Fri, 16 Jul 2021 16:43:17 -0400 Subject: [PATCH] chat commands: add pets command Co-authored-by: Illya Myshakov --- .../runelite/http/api/chat/ChatClient.java | 53 ++++ .../http/service/chat/ChatController.java | 18 ++ .../http/service/chat/ChatService.java | 33 ++- .../net/runelite/api/widgets/WidgetID.java | 10 + .../net/runelite/api/widgets/WidgetInfo.java | 7 + .../chatcommands/ChatCommandsConfig.java | 14 +- .../chatcommands/ChatCommandsPlugin.java | 246 ++++++++++++++++++ .../client/plugins/chatcommands/Pet.java | 98 +++++++ .../chatcommands/ChatCommandsPluginTest.java | 134 ++++++++++ 9 files changed, 611 insertions(+), 2 deletions(-) create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/chatcommands/Pet.java diff --git a/http-api/src/main/java/net/runelite/http/api/chat/ChatClient.java b/http-api/src/main/java/net/runelite/http/api/chat/ChatClient.java index e234fdfb5f..06ca9bb623 100644 --- a/http-api/src/main/java/net/runelite/http/api/chat/ChatClient.java +++ b/http-api/src/main/java/net/runelite/http/api/chat/ChatClient.java @@ -25,10 +25,13 @@ package net.runelite.http.api.chat; import com.google.gson.JsonParseException; +import com.google.gson.reflect.TypeToken; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; +import java.util.Collection; +import java.util.Set; import lombok.AllArgsConstructor; import net.runelite.http.api.RuneLiteAPI; import okhttp3.HttpUrl; @@ -362,4 +365,54 @@ public class ChatClient throw new IOException(ex); } } + + public boolean submitPetList(String username, Collection petList) throws IOException + { + HttpUrl url = RuneLiteAPI.getApiBase().newBuilder() + .addPathSegment("chat") + .addPathSegment("pets") + .addQueryParameter("name", username) + .build(); + + Request request = new Request.Builder() + .post(RequestBody.create(RuneLiteAPI.JSON, RuneLiteAPI.GSON.toJson(petList))) + .url(url) + .build(); + + try (Response response = client.newCall(request).execute()) + { + return response.isSuccessful(); + } + } + + public Set getPetList(String username) throws IOException + { + HttpUrl url = RuneLiteAPI.getApiBase().newBuilder() + .addPathSegment("chat") + .addPathSegment("pets") + .addQueryParameter("name", username) + .build(); + + Request request = new Request.Builder() + .url(url) + .build(); + + try (Response response = client.newCall(request).execute()) + { + if (!response.isSuccessful()) + { + throw new IOException("Unable to look up pet list!"); + } + + InputStream in = response.body().byteStream(); + // CHECKSTYLE:OFF + return RuneLiteAPI.GSON.fromJson(new InputStreamReader(in, StandardCharsets.UTF_8), + new TypeToken>(){}.getType()); + // CHECKSTYLE:ON + } + catch (JsonParseException ex) + { + throw new IOException(ex); + } + } } diff --git a/http-service/src/main/java/net/runelite/http/service/chat/ChatController.java b/http-service/src/main/java/net/runelite/http/service/chat/ChatController.java index e796e738bb..65f4acfa1a 100644 --- a/http-service/src/main/java/net/runelite/http/service/chat/ChatController.java +++ b/http-service/src/main/java/net/runelite/http/service/chat/ChatController.java @@ -247,4 +247,22 @@ public class ChatController return layout; } + + @PostMapping("/pets") + public void submitPetList(@RequestParam String name, @RequestBody int[] petList) + { + chatService.setPetList(name, petList); + } + + @GetMapping("/pets") + public int[] getPetList(@RequestParam String name) + { + int[] petList = chatService.getPetList(name); + if (petList == null) + { + throw new NotFoundException(); + } + + return petList; + } } diff --git a/http-service/src/main/java/net/runelite/http/service/chat/ChatService.java b/http-service/src/main/java/net/runelite/http/service/chat/ChatService.java index fb2d8f7517..7748728aab 100644 --- a/http-service/src/main/java/net/runelite/http/service/chat/ChatService.java +++ b/http-service/src/main/java/net/runelite/http/service/chat/ChatService.java @@ -28,11 +28,13 @@ import com.google.common.base.Joiner; import com.google.common.base.Splitter; import com.google.common.collect.ImmutableMap; import java.time.Duration; +import java.util.Arrays; import java.util.List; import java.util.Map; +import java.util.Set; +import net.runelite.http.api.chat.Duels; import net.runelite.http.api.chat.LayoutRoom; import net.runelite.http.api.chat.Task; -import net.runelite.http.api.chat.Duels; import net.runelite.http.service.util.redis.RedisPool; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -229,4 +231,33 @@ public class ChatService jedis.setex("layout." + name, (int) EXPIRE.getSeconds(), Joiner.on(' ').join(rooms)); } } + + public int[] getPetList(String name) + { + Set pets; + try (Jedis jedis = jedisPool.getResource()) + { + pets = jedis.smembers("pets." + name); + } + + if (pets.isEmpty()) + { + return null; + } + + return pets.stream() + .mapToInt(Integer::parseInt) + .toArray(); + } + + public void setPetList(String name, int[] petList) + { + String[] pets = Arrays.stream(petList).mapToObj(Integer::toString).toArray(String[]::new); + String key = "pets." + name; + try (Jedis jedis = jedisPool.getResource()) + { + jedis.sadd(key, pets); + jedis.expire(key, (int) EXPIRE.getSeconds()); + } + } } diff --git a/runelite-api/src/main/java/net/runelite/api/widgets/WidgetID.java b/runelite-api/src/main/java/net/runelite/api/widgets/WidgetID.java index a388106687..aade61297b 100644 --- a/runelite-api/src/main/java/net/runelite/api/widgets/WidgetID.java +++ b/runelite-api/src/main/java/net/runelite/api/widgets/WidgetID.java @@ -153,6 +153,7 @@ public class WidgetID public static final int LMS_GROUP_ID = 333; public static final int LMS_INGAME_GROUP_ID = 328; public static final int ADVENTURE_LOG_ID = 187; + public static final int COLLECTION_LOG_ID = 621; public static final int GENERIC_SCROLL_GROUP_ID = 625; public static final int GAUNTLET_TIMER_GROUP_ID = 637; public static final int HALLOWED_SEPULCHRE_TIMER_GROUP_ID = 668; @@ -878,6 +879,15 @@ public class WidgetID static final int CONTAINER = 0; } + static class CollectionLog + { + static final int CONTAINER = 0; + static final int TABS = 3; + static final int ENTRY = 17; + static final int ENTRY_HEADER = 19; + static final int ENTRY_ITEMS = 35; + } + static class GenericScroll { static final int TEXT = 7; diff --git a/runelite-api/src/main/java/net/runelite/api/widgets/WidgetInfo.java b/runelite-api/src/main/java/net/runelite/api/widgets/WidgetInfo.java index 5bacba5c47..98f66a8b83 100644 --- a/runelite-api/src/main/java/net/runelite/api/widgets/WidgetInfo.java +++ b/runelite-api/src/main/java/net/runelite/api/widgets/WidgetInfo.java @@ -480,6 +480,13 @@ public enum WidgetInfo ADVENTURE_LOG(WidgetID.ADVENTURE_LOG_ID, WidgetID.AdventureLog.CONTAINER), + COLLECTION_LOG(WidgetID.COLLECTION_LOG_ID, WidgetID.CollectionLog.CONTAINER), + + COLLECTION_LOG_TABS(WidgetID.COLLECTION_LOG_ID, WidgetID.CollectionLog.TABS), + COLLECTION_LOG_ENTRY(WidgetID.COLLECTION_LOG_ID, WidgetID.CollectionLog.ENTRY), + COLLECTION_LOG_ENTRY_HEADER(WidgetID.COLLECTION_LOG_ID, WidgetID.CollectionLog.ENTRY_HEADER), + COLLECTION_LOG_ENTRY_ITEMS(WidgetID.COLLECTION_LOG_ID, WidgetID.CollectionLog.ENTRY_ITEMS), + GENERIC_SCROLL_TEXT(WidgetID.GENERIC_SCROLL_GROUP_ID, WidgetID.GenericScroll.TEXT), WORLD_SWITCHER_LIST(WidgetID.WORLD_SWITCHER_GROUP_ID, WidgetID.WorldSwitcher.WORLD_LIST), diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/chatcommands/ChatCommandsConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/chatcommands/ChatCommandsConfig.java index a7213a33f1..859aa9f9d1 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/chatcommands/ChatCommandsConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/chatcommands/ChatCommandsConfig.java @@ -179,6 +179,18 @@ public interface ChatCommandsConfig extends Config @ConfigItem( position = 13, + keyName = "pets", + name = "Pets Command", + description = "Configures whether the player pet list command is enabled
!pets
" + + " Note: Update your pet list by looking at the All Pets tab in the Collection Log" + ) + default boolean pets() + { + return true; + } + + @ConfigItem( + position = 20, keyName = "clearSingleWord", name = "Clear Single Word", description = "Enable hot key to clear single word at a time" @@ -189,7 +201,7 @@ public interface ChatCommandsConfig extends Config } @ConfigItem( - position = 14, + position = 21, keyName = "clearEntireChatBox", name = "Clear Chat Box", description = "Enable hotkey to clear entire chat box" diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/chatcommands/ChatCommandsPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/chatcommands/ChatCommandsPlugin.java index 9a998e8aca..fe8b03cd26 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/chatcommands/ChatCommandsPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/chatcommands/ChatCommandsPlugin.java @@ -28,14 +28,23 @@ package net.runelite.client.plugins.chatcommands; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.MoreObjects; import com.google.common.collect.ImmutableMap; +import com.google.gson.Gson; +import com.google.gson.JsonSyntaxException; +import com.google.gson.reflect.TypeToken; import com.google.inject.Provides; +import java.awt.image.BufferedImage; import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; import java.util.EnumSet; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.concurrent.ScheduledExecutorService; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.stream.Collectors; import javax.inject.Inject; import lombok.Value; import lombok.extern.slf4j.Slf4j; @@ -43,6 +52,7 @@ import net.runelite.api.ChatMessageType; import net.runelite.api.Client; import net.runelite.api.Experience; import net.runelite.api.IconID; +import net.runelite.api.IndexedSprite; import net.runelite.api.ItemComposition; import net.runelite.api.MessageNode; import net.runelite.api.Player; @@ -57,9 +67,11 @@ import net.runelite.api.events.WidgetLoaded; import net.runelite.api.vars.AccountType; import net.runelite.api.widgets.Widget; import static net.runelite.api.widgets.WidgetID.ADVENTURE_LOG_ID; +import static net.runelite.api.widgets.WidgetID.COLLECTION_LOG_ID; import static net.runelite.api.widgets.WidgetID.GENERIC_SCROLL_GROUP_ID; import static net.runelite.api.widgets.WidgetID.KILL_LOGS_GROUP_ID; import net.runelite.api.widgets.WidgetInfo; +import net.runelite.client.callback.ClientThread; import net.runelite.client.chat.ChatColorType; import net.runelite.client.chat.ChatCommandManager; import net.runelite.client.chat.ChatMessageBuilder; @@ -72,6 +84,7 @@ import net.runelite.client.game.ItemManager; import net.runelite.client.input.KeyManager; import net.runelite.client.plugins.Plugin; import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.util.ImageUtil; import net.runelite.client.util.QuantityFormatter; import net.runelite.client.util.Text; import net.runelite.http.api.chat.ChatClient; @@ -112,6 +125,7 @@ public class ChatCommandsPlugin extends Plugin "(?:
Overall time: (?[0-9:]+(?:\\.[0-9]+)?)(?: \\(new personal best\\)|. Personal best: (?[0-9:]+(?:\\.[0-9]+)?)))?"); private static final Pattern HS_KC_FLOOR_PATTERN = Pattern.compile("You have completed Floor (\\d) of the Hallowed Sepulchre! Total completions: ([0-9,]+)\\."); private static final Pattern HS_KC_GHC_PATTERN = Pattern.compile("You have opened the Grand Hallowed Coffin ([0-9,]+) times?!"); + private static final Pattern COLLECTION_LOG_ITEM_PATTERN = Pattern.compile("New item added to your collection log: (.*)"); private static final String TOTAL_LEVEL_COMMAND_STRING = "!total"; private static final String PRICE_COMMAND_STRING = "!price"; @@ -128,9 +142,11 @@ public class ChatCommandsPlugin extends Plugin private static final String DUEL_ARENA_COMMAND = "!duels"; private static final String LEAGUE_POINTS_COMMAND = "!lp"; private static final String SOUL_WARS_ZEAL_COMMAND = "!sw"; + private static final String PET_LIST_COMMAND = "!pets"; @VisibleForTesting static final int ADV_LOG_EXPLOITS_TEXT_INDEX = 1; + static final int COL_LOG_ENTRY_HEADER_TITLE_INDEX = 0; private static final Map KILLCOUNT_RENAMES = ImmutableMap.of( "Barrows chest", "Barrows Chests" @@ -139,15 +155,20 @@ public class ChatCommandsPlugin extends Plugin private boolean bossLogLoaded; private boolean advLogLoaded; private boolean scrollInterfaceLoaded; + private boolean collectionLogLoaded; private String pohOwner; private HiscoreEndpoint hiscoreEndpoint; // hiscore endpoint for current player private String lastBossKill; private int lastBossTime = -1; private double lastPb = -1; + private int modIconIdx = -1; @Inject private Client client; + @Inject + private ClientThread clientThread; + @Inject private ChatCommandsConfig config; @@ -181,6 +202,9 @@ public class ChatCommandsPlugin extends Plugin @Inject private RuneLiteConfig runeLiteConfig; + @Inject + private Gson gson; + @Override public void startUp() { @@ -201,6 +225,9 @@ public class ChatCommandsPlugin extends Plugin chatCommandManager.registerCommandAsync(GC_COMMAND_STRING, this::gambleCountLookup, this::gambleCountSubmit); chatCommandManager.registerCommandAsync(DUEL_ARENA_COMMAND, this::duelArenaLookup, this::duelArenaSubmit); chatCommandManager.registerCommandAsync(SOUL_WARS_ZEAL_COMMAND, this::soulWarsZealLookup); + chatCommandManager.registerCommandAsync(PET_LIST_COMMAND, this::petListLookup, this::petListSubmit); + + clientThread.invoke(this::loadPetIcons); } @Override @@ -226,6 +253,7 @@ public class ChatCommandsPlugin extends Plugin chatCommandManager.unregisterCommand(GC_COMMAND_STRING); chatCommandManager.unregisterCommand(DUEL_ARENA_COMMAND); chatCommandManager.unregisterCommand(SOUL_WARS_ZEAL_COMMAND); + chatCommandManager.unregisterCommand(PET_LIST_COMMAND); } @Provides @@ -272,6 +300,69 @@ public class ChatCommandsPlugin extends Plugin return personalBest == null ? 0 : personalBest; } + private void loadPetIcons() + { + final IndexedSprite[] modIcons = client.getModIcons(); + if (modIconIdx != -1 || modIcons == null) + { + return; + } + + final Pet[] pets = Pet.values(); + final IndexedSprite[] newModIcons = Arrays.copyOf(modIcons, modIcons.length + pets.length); + modIconIdx = modIcons.length; + + for (int i = 0; i < pets.length; i++) + { + final Pet pet = pets[i]; + + final BufferedImage image = ImageUtil.resizeImage(itemManager.getImage(pet.getIconID()), 18, 16); + final IndexedSprite sprite = ImageUtil.getImageIndexedSprite(image, client); + newModIcons[modIconIdx + i] = sprite; + } + + client.setModIcons(newModIcons); + } + + /** + * Sets the list of owned pets for the local player + * + * @param petList The total list of owned pets for the local player + */ + private void setPetList(List petList) + { + if (petList == null) + { + return; + } + + configManager.setRSProfileConfiguration("chatcommands", "pets", + gson.toJson(petList)); + } + + /** + * Looks up the list of owned pets for the local player + */ + private List getPetList() + { + String petListJson = configManager.getRSProfileConfiguration("chatcommands", "pets", + String.class); + + List petList; + try + { + // CHECKSTYLE:OFF + petList = gson.fromJson(petListJson, new TypeToken>(){}.getType()); + // CHECKSTYLE:ON + } + catch (JsonSyntaxException ex) + { + return Collections.emptyList(); + } + + return petList != null ? petList : Collections.emptyList(); + } + @Subscribe public void onChatMessage(ChatMessage chatMessage) { @@ -431,6 +522,24 @@ public class ChatCommandsPlugin extends Plugin lastBossKill = null; lastBossTime = -1; } + + matcher = COLLECTION_LOG_ITEM_PATTERN.matcher(message); + if (matcher.find()) + { + String item = matcher.group(1); + Pet pet = Pet.findPet(item); + + if (pet != null) + { + List petList = new ArrayList<>(getPetList()); + if (!petList.contains(pet)) + { + log.debug("New pet added: {}", pet); + petList.add(pet); + setPetList(petList); + } + } + } } @VisibleForTesting @@ -490,6 +599,39 @@ public class ChatCommandsPlugin extends Plugin } } + if (collectionLogLoaded && (pohOwner == null || pohOwner.equals(client.getLocalPlayer().getName()))) + { + collectionLogLoaded = false; + + Widget collectionLogEntryHeader = client.getWidget(WidgetInfo.COLLECTION_LOG_ENTRY_HEADER); + if (collectionLogEntryHeader != null && collectionLogEntryHeader.getChildren() != null) + { + Widget entryTitle = collectionLogEntryHeader.getChild(COL_LOG_ENTRY_HEADER_TITLE_INDEX); + // Make sure that the player is looking in the All Pets tab of the collection log + if (entryTitle.getText().equals("All Pets")) + { + Widget collectionLogEntryItems = client.getWidget(WidgetInfo.COLLECTION_LOG_ENTRY_ITEMS); + if (collectionLogEntryItems != null && collectionLogEntryItems.getChildren() != null) + { + List petList = new ArrayList<>(); + for (Widget child : collectionLogEntryItems.getChildren()) + { + if (child.getOpacity() == 0) + { + Pet pet = Pet.findPet(Text.removeTags(child.getName())); + if (pet != null) + { + petList.add(pet); + } + } + } + + setPetList(petList); + } + } + } + } + if (bossLogLoaded && (pohOwner == null || pohOwner.equals(client.getLocalPlayer().getName()))) { bossLogLoaded = false; @@ -566,6 +708,9 @@ public class ChatCommandsPlugin extends Plugin case ADVENTURE_LOG_ID: advLogLoaded = true; break; + case COLLECTION_LOG_ID: + collectionLogLoaded = true; + break; case KILL_LOGS_GROUP_ID: bossLogLoaded = true; break; @@ -583,6 +728,10 @@ public class ChatCommandsPlugin extends Plugin case LOADING: case HOPPING: pohOwner = null; + break; + case LOGGED_IN: + loadPetIcons(); + break; } } @@ -999,6 +1148,103 @@ public class ChatCommandsPlugin extends Plugin return true; } + /** + * Looks up the pet list for the player who triggered !pets + * + * @param chatMessage The chat message containing the command. + * @param message The chat message in string format + *

+ */ + private void petListLookup(ChatMessage chatMessage, String message) + { + if (!config.pets()) + { + return; + } + + ChatMessageType type = chatMessage.getType(); + + final String player; + if (type.equals(ChatMessageType.PRIVATECHATOUT)) + { + player = client.getLocalPlayer().getName(); + } + else + { + player = Text.sanitize(chatMessage.getName()); + } + + Set playerPetList; + try + { + playerPetList = chatClient.getPetList(player); + } + catch (IOException ex) + { + log.debug("unable to lookup pet list", ex); + + if (player.equals(client.getLocalPlayer().getName())) + { + String response = "Open the 'All Pets' tab in the Collection Log to update your pet list"; + log.debug("Setting response {}", response); + final MessageNode messageNode = chatMessage.getMessageNode(); + messageNode.setValue(response); + client.refreshChat(); + } + return; + } + + ChatMessageBuilder responseBuilder = new ChatMessageBuilder().append("Pets: ") + .append("(" + playerPetList.size() + ")"); + + // Append pets that the player owns + Pet[] pets = Pet.values(); + for (Pet pet : pets) + { + if (playerPetList.contains(pet.getIconID())) + { + responseBuilder.append(" ").img(modIconIdx + pet.ordinal()); + } + } + + String response = responseBuilder.build(); + + log.debug("Setting response {}", response); + final MessageNode messageNode = chatMessage.getMessageNode(); + messageNode.setValue(response); + client.refreshChat(); + } + + /** + * Submits the pet list for the local player + * + * @param chatInput The chat message containing the command. + * @param value The chat message + */ + private boolean petListSubmit(ChatInput chatInput, String value) + { + final String playerName = client.getLocalPlayer().getName(); + + executor.execute(() -> + { + try + { + List petList = getPetList().stream().map(Pet::getIconID).collect(Collectors.toList()); + chatClient.submitPetList(playerName, petList); + } + catch (Exception ex) + { + log.warn("unable to submit pet list", ex); + } + finally + { + chatInput.resume(); + } + }); + + return true; + } + /** * Looks up the item price and changes the original message to the * response. diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/chatcommands/Pet.java b/runelite-client/src/main/java/net/runelite/client/plugins/chatcommands/Pet.java new file mode 100644 index 0000000000..b48a810ec5 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/chatcommands/Pet.java @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2021, Illya Myshakov + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.chatcommands; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import net.runelite.api.ItemID; + +@AllArgsConstructor +@Getter +enum Pet +{ + ABYSSAL_ORPHAN("Abyssal orphan", ItemID.ABYSSAL_ORPHAN), + IKKLE_HYDRA("Ikkle hydra", ItemID.IKKLE_HYDRA), + CALLISTO_CUB("Callisto cub", ItemID.CALLISTO_CUB), + HELLPUPPY("Hellpuppy", ItemID.HELLPUPPY), + PET_CHAOS_ELEMENTAL("Pet chaos elemental", ItemID.PET_CHAOS_ELEMENTAL), + PET_ZILYANA("Pet zilyana", ItemID.PET_ZILYANA), + PET_DARK_CORE("Pet dark core", ItemID.PET_DARK_CORE), + PET_DAGANNOTH_PRIME("Pet dagannoth prime", ItemID.PET_DAGANNOTH_PRIME), + PET_DAGANNOTH_SUPREME("Pet dagannoth supreme", ItemID.PET_DAGANNOTH_SUPREME), + PET_DAGANNOTH_REX("Pet dagannoth rex", ItemID.PET_DAGANNOTH_REX), + TZREKJAD("Tzrek-jad", ItemID.TZREKJAD), + PET_GENERAL_GRAARDOR("Pet general graardor", ItemID.PET_GENERAL_GRAARDOR), + BABY_MOLE("Baby mole", ItemID.BABY_MOLE), + NOON("Noon", ItemID.NOON), + JALNIBREK("Jal-nib-rek", ItemID.JALNIBREK), + KALPHITE_PRINCESS("Kalphite princess", ItemID.KALPHITE_PRINCESS), + PRINCE_BLACK_DRAGON("Prince black dragon", ItemID.PRINCE_BLACK_DRAGON), + PET_KRAKEN("Pet kraken", ItemID.PET_KRAKEN), + PET_KREEARRA("Pet kree'arra", ItemID.PET_KREEARRA), + PET_KRIL_TSUTSAROTH("Pet k'ril tsutsaroth", ItemID.PET_KRIL_TSUTSAROTH), + SCORPIAS_OFFSPRING("Scorpia's offspring", ItemID.SCORPIAS_OFFSPRING), + SKOTOS("Skotos", ItemID.SKOTOS), + PET_SMOKE_DEVIL("Pet smoke devil", ItemID.PET_SMOKE_DEVIL), + VENENATIS_SPIDERLING("Venenatis spiderling", ItemID.VENENATIS_SPIDERLING), + VETION_JR("Vet'ion jr.", ItemID.VETION_JR), + VORKI("Vorki", ItemID.VORKI), + PHOENIX("Phoenix", ItemID.PHOENIX), + PET_SNAKELING("Pet snakeling", ItemID.PET_SNAKELING), + OLMLET("Olmlet", ItemID.OLMLET), + LIL_ZIK("Lil' zik", ItemID.LIL_ZIK), + BLOODHOUND("Bloodhound", ItemID.BLOODHOUND), + PET_PENANCE_QUEEN("Pet penance queen", ItemID.PET_PENANCE_QUEEN), + HERON("Heron", ItemID.HERON), + ROCK_GOLEM("Rock golem", ItemID.ROCK_GOLEM), + BEAVER("Beaver", ItemID.BEAVER), + BABY_CHINCHOMPA("Baby chinchompa", ItemID.BABY_CHINCHOMPA), + GIANT_SQUIRREL("Giant squirrel", ItemID.GIANT_SQUIRREL), + TANGLEROOT("Tangleroot", ItemID.TANGLEROOT), + ROCKY("Rocky", ItemID.ROCKY), + RIFT_GUARDIAN("Rift guardian", ItemID.RIFT_GUARDIAN), + HERBI("Herbi", ItemID.HERBI), + CHOMPY_CHICK("Chompy chick", ItemID.CHOMPY_CHICK), + SRARACHA("Sraracha", ItemID.SRARACHA), + SMOLCANO("Smolcano", ItemID.SMOLCANO), + YOUNGLLEF("Youngllef", ItemID.YOUNGLLEF), + LITTLE_NIGHTMARE("Little nightmare", ItemID.LITTLE_NIGHTMARE), + LIL_CREATOR("Lil' creator", ItemID.LIL_CREATOR), + TINY_TEMPOR("Tiny tempor", ItemID.TINY_TEMPOR); + + private final String name; + private final Integer iconID; + + static Pet findPet(String petName) + { + for (Pet pet : values()) + { + if (pet.name.equals(petName)) + { + return pet; + } + } + return null; + } +} diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/chatcommands/ChatCommandsPluginTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/chatcommands/ChatCommandsPluginTest.java index e7106953e2..0f2b4da035 100644 --- a/runelite-client/src/test/java/net/runelite/client/plugins/chatcommands/ChatCommandsPluginTest.java +++ b/runelite-client/src/test/java/net/runelite/client/plugins/chatcommands/ChatCommandsPluginTest.java @@ -25,6 +25,7 @@ package net.runelite.client.plugins.chatcommands; import com.google.common.collect.Sets; +import com.google.gson.Gson; import com.google.inject.Guice; import com.google.inject.testing.fieldbinder.Bind; import com.google.inject.testing.fieldbinder.BoundFieldModule; @@ -43,6 +44,7 @@ import net.runelite.api.events.GameTick; import net.runelite.api.events.WidgetLoaded; import net.runelite.api.widgets.Widget; import static net.runelite.api.widgets.WidgetID.ADVENTURE_LOG_ID; +import static net.runelite.api.widgets.WidgetID.COLLECTION_LOG_ID; import static net.runelite.api.widgets.WidgetID.GENERIC_SCROLL_GROUP_ID; import net.runelite.api.widgets.WidgetInfo; import net.runelite.client.chat.ChatCommandManager; @@ -50,6 +52,7 @@ import net.runelite.client.chat.ChatMessageManager; import net.runelite.client.config.ChatColorConfig; import net.runelite.client.config.ConfigManager; import net.runelite.client.config.RuneLiteConfig; +import net.runelite.http.api.RuneLiteAPI; import net.runelite.http.api.chat.ChatClient; import net.runelite.http.api.hiscore.HiscoreClient; import net.runelite.http.api.hiscore.HiscoreSkill; @@ -118,6 +121,8 @@ public class ChatCommandsPluginTest @Inject ChatCommandsPlugin chatCommandsPlugin; + final Gson gson = RuneLiteAPI.GSON; + @Before public void before() { @@ -952,6 +957,135 @@ public class ChatCommandsPluginTest verify(configManager).setRSProfileConfiguration("personalbest", "tempoross", 5 * 60 + 42.6); } + @Test + public void testPlayerPetList() + { + Widget logEntryHeaderWidget = mock(Widget.class); + when(client.getWidget(WidgetInfo.COLLECTION_LOG_ENTRY_HEADER)).thenReturn(logEntryHeaderWidget); + + Widget[] logEntryHeaderItemsWidget = new Widget[1]; + when(logEntryHeaderWidget.getChildren()).thenReturn(logEntryHeaderItemsWidget); + + Widget logEntryHeaderTitleWidget = mock(Widget.class); + when(logEntryHeaderWidget.getChild(ChatCommandsPlugin.COL_LOG_ENTRY_HEADER_TITLE_INDEX)) + .thenReturn(logEntryHeaderTitleWidget); + when(logEntryHeaderTitleWidget.getText()).thenReturn("All Pets"); + + Widget logEntryItemsWidget = mock(Widget.class); + when(client.getWidget(WidgetInfo.COLLECTION_LOG_ENTRY_ITEMS)).thenReturn(logEntryItemsWidget); + + Widget[] logPetEntriesWidget = new Widget[3]; + for (int i = 0; i < 3; i++) + { + logPetEntriesWidget[i] = mock(Widget.class); + when(logPetEntriesWidget[i].getOpacity()).thenReturn(175); + } + + when(logPetEntriesWidget[1].getName()).thenReturn("Ikkle hydra"); + when(logPetEntriesWidget[1].getOpacity()).thenReturn(0); + + when(logEntryItemsWidget.getChildren()).thenReturn(logPetEntriesWidget); + + WidgetLoaded logEvent = new WidgetLoaded(); + logEvent.setGroupId(COLLECTION_LOG_ID); + chatCommandsPlugin.onWidgetLoaded(logEvent); + + chatCommandsPlugin.onGameTick(new GameTick()); + + Pet[] playerPetList = new Pet[1]; + playerPetList[0] = Pet.IKKLE_HYDRA; + + verify(configManager).setRSProfileConfiguration("chatcommands", "pets", gson.toJson(playerPetList)); + } + + @Test + public void testEmptyPlayerPetList() + { + Widget logEntryHeaderWidget = mock(Widget.class); + when(client.getWidget(WidgetInfo.COLLECTION_LOG_ENTRY_HEADER)).thenReturn(logEntryHeaderWidget); + + Widget[] logEntryHeaderItemsWidget = new Widget[1]; + when(logEntryHeaderWidget.getChildren()).thenReturn(logEntryHeaderItemsWidget); + + Widget logEntryHeaderTitleWidget = mock(Widget.class); + when(logEntryHeaderWidget.getChild(ChatCommandsPlugin.COL_LOG_ENTRY_HEADER_TITLE_INDEX)) + .thenReturn(logEntryHeaderTitleWidget); + when(logEntryHeaderTitleWidget.getText()).thenReturn("All Pets"); + + Widget logEntryItemsWidget = mock(Widget.class); + when(client.getWidget(WidgetInfo.COLLECTION_LOG_ENTRY_ITEMS)).thenReturn(logEntryItemsWidget); + + Widget[] logPetEntriesWidget = new Widget[3]; + for (int i = 0; i < 3; i++) + { + logPetEntriesWidget[i] = mock(Widget.class); + when(logPetEntriesWidget[i].getOpacity()).thenReturn(175); + } + + when(logEntryItemsWidget.getChildren()).thenReturn(logPetEntriesWidget); + + WidgetLoaded logEvent = new WidgetLoaded(); + logEvent.setGroupId(COLLECTION_LOG_ID); + chatCommandsPlugin.onWidgetLoaded(logEvent); + + chatCommandsPlugin.onGameTick(new GameTick()); + + verify(configManager).setRSProfileConfiguration("chatcommands", "pets", gson.toJson(new Pet[0])); + } + + @Test + public void testUpdatePlayerPetList() + { + Widget logEntryHeaderWidget = mock(Widget.class); + when(client.getWidget(WidgetInfo.COLLECTION_LOG_ENTRY_HEADER)).thenReturn(logEntryHeaderWidget); + + Widget[] logEntryHeaderItemsWidget = new Widget[1]; + when(logEntryHeaderWidget.getChildren()).thenReturn(logEntryHeaderItemsWidget); + + Widget logEntryHeaderTitleWidget = mock(Widget.class); + when(logEntryHeaderWidget.getChild(ChatCommandsPlugin.COL_LOG_ENTRY_HEADER_TITLE_INDEX)) + .thenReturn(logEntryHeaderTitleWidget); + when(logEntryHeaderTitleWidget.getText()).thenReturn("All Pets"); + + Widget logEntryItemsWidget = mock(Widget.class); + when(client.getWidget(WidgetInfo.COLLECTION_LOG_ENTRY_ITEMS)).thenReturn(logEntryItemsWidget); + + Widget[] logPetEntriesWidget = new Widget[3]; + for (int i = 0; i < 3; i++) + { + logPetEntriesWidget[i] = mock(Widget.class); + when(logPetEntriesWidget[i].getOpacity()).thenReturn(175); + } + + when(logPetEntriesWidget[1].getName()).thenReturn("Ikkle hydra"); + when(logPetEntriesWidget[1].getOpacity()).thenReturn(0); + + when(logEntryItemsWidget.getChildren()).thenReturn(logPetEntriesWidget); + + WidgetLoaded logEvent = new WidgetLoaded(); + logEvent.setGroupId(COLLECTION_LOG_ID); + chatCommandsPlugin.onWidgetLoaded(logEvent); + + chatCommandsPlugin.onGameTick(new GameTick()); + + Pet[] playerPetList = new Pet[1]; + playerPetList[0] = Pet.IKKLE_HYDRA; + + verify(configManager).setRSProfileConfiguration("chatcommands", "pets", gson.toJson(playerPetList)); + + ChatMessage chatMessage = new ChatMessage(); + chatMessage.setMessage("New item added to your collection log: Chompy chick"); + chatMessage.setType(GAMEMESSAGE); + when(configManager.getRSProfileConfiguration("chatcommands", "pets", + String.class)).thenReturn(gson.toJson(playerPetList)); + chatCommandsPlugin.onChatMessage(chatMessage); + + playerPetList = new Pet[2]; + playerPetList[0] = Pet.IKKLE_HYDRA; + playerPetList[1] = Pet.CHOMPY_CHICK; + verify(configManager).setRSProfileConfiguration("chatcommands", "pets", gson.toJson(playerPetList)); + } + @Test public void testTimeStringToSeconds() {