From 43f8c58d5a84816d5349e3b38849e6eef5f595b2 Mon Sep 17 00:00:00 2001 From: Adam Date: Sun, 16 Jul 2017 13:49:17 -0400 Subject: [PATCH] runelite-client: add !price command --- .../runelite/http/api/item/ItemClient.java | 33 +++++ .../main/java/net/runelite/api/Client.java | 5 + .../java/net/runelite/api/MessageNode.java | 55 +++++++ .../net/runelite/client/callback/Hooks.java | 17 +++ .../runelite/client/events/SetMessage.java | 68 +++++++++ .../client/plugins/PluginManager.java | 2 + .../plugins/pricecommands/PriceCommands.java | 134 ++++++++++++++++++ .../pricecommands/PriceCommandsConfig.java | 47 ++++++ .../main/java/net/runelite/rs/api/Client.java | 6 + .../java/net/runelite/rs/api/MessageNode.java | 7 +- 10 files changed, 373 insertions(+), 1 deletion(-) create mode 100644 runelite-api/src/main/java/net/runelite/api/MessageNode.java create mode 100644 runelite-client/src/main/java/net/runelite/client/events/SetMessage.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/pricecommands/PriceCommands.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/pricecommands/PriceCommandsConfig.java diff --git a/http-api/src/main/java/net/runelite/http/api/item/ItemClient.java b/http-api/src/main/java/net/runelite/http/api/item/ItemClient.java index 76bf6fd850..a601de2517 100644 --- a/http-api/src/main/java/net/runelite/http/api/item/ItemClient.java +++ b/http-api/src/main/java/net/runelite/http/api/item/ItemClient.java @@ -72,4 +72,37 @@ public class ItemClient throw new IOException(ex); } } + + public SearchResult search(String itemName) throws IOException + { + HttpUrl url = RuneliteAPI.getApiBase().newBuilder() + .addPathSegment("item") + .addPathSegment("search") + .addQueryParameter("query", itemName) + .build(); + + logger.debug("Built URI: {}", url); + + Request request = new Request.Builder() + .url(url) + .build(); + + Response response = RuneliteAPI.CLIENT.newCall(request).execute(); + + if (!response.isSuccessful()) + { + logger.debug("Error looking up item {}: {}", itemName, response.message()); + return null; + } + + try (ResponseBody body = response.body()) + { + InputStream in = body.byteStream(); + return RuneliteAPI.GSON.fromJson(new InputStreamReader(in), SearchResult.class); + } + catch (JsonParseException ex) + { + throw new IOException(ex); + } + } } diff --git a/runelite-api/src/main/java/net/runelite/api/Client.java b/runelite-api/src/main/java/net/runelite/api/Client.java index 0d6253886f..4adb206b2a 100644 --- a/runelite-api/src/main/java/net/runelite/api/Client.java +++ b/runelite-api/src/main/java/net/runelite/api/Client.java @@ -410,4 +410,9 @@ public class Client { client.setGameDrawingMode(gameDrawingMode); } + + public void refreshChat() + { + client.setChatCycle(client.getCycleCntr()); + } } diff --git a/runelite-api/src/main/java/net/runelite/api/MessageNode.java b/runelite-api/src/main/java/net/runelite/api/MessageNode.java new file mode 100644 index 0000000000..eb9e9f7061 --- /dev/null +++ b/runelite-api/src/main/java/net/runelite/api/MessageNode.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2017, Adam + * 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.api; + +public class MessageNode +{ + private final net.runelite.rs.api.MessageNode messageNode; + + public MessageNode(net.runelite.rs.api.MessageNode messageNode) + { + this.messageNode = messageNode; + } + + public ChatMessageType getType() + { + return ChatMessageType.of(messageNode.getType()); + } + + public String getSender() + { + return messageNode.getSender(); + } + + public String getValue() + { + return messageNode.getValue(); + } + + public void setValue(String value) + { + messageNode.setValue(value); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/callback/Hooks.java b/runelite-client/src/main/java/net/runelite/client/callback/Hooks.java index d5f688464e..af1e347a4c 100644 --- a/runelite-client/src/main/java/net/runelite/client/callback/Hooks.java +++ b/runelite-client/src/main/java/net/runelite/client/callback/Hooks.java @@ -35,6 +35,7 @@ import net.runelite.client.game.DeathChecker; import net.runelite.client.task.Scheduler; import net.runelite.client.ui.overlay.OverlayRenderer; import net.runelite.rs.api.MainBufferProvider; +import net.runelite.rs.api.MessageNode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -190,4 +191,20 @@ public class Hooks runelite.getEventBus().post(chatMessage); } + + public static void setMessage(Object object, int type, String name, String sender, String value) + { + MessageNode messageNode = (MessageNode) object; + + // Hook is fired prior to actually setting these on the MessageNode, so send them + // in the event too. + SetMessage setMessage = new SetMessage(); + setMessage.setMessageNode(new net.runelite.api.MessageNode(messageNode)); + setMessage.setType(ChatMessageType.of(type)); + setMessage.setName(name); + setMessage.setSender(sender); + setMessage.setValue(value); + + runelite.getEventBus().post(setMessage); + } } diff --git a/runelite-client/src/main/java/net/runelite/client/events/SetMessage.java b/runelite-client/src/main/java/net/runelite/client/events/SetMessage.java new file mode 100644 index 0000000000..00409dc3e3 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/events/SetMessage.java @@ -0,0 +1,68 @@ +package net.runelite.client.events; + +import net.runelite.api.ChatMessageType; +import net.runelite.api.MessageNode; + +/** + * called when a message node's message is set + * + * @author Adam + */ +public class SetMessage +{ + private MessageNode messageNode; + private ChatMessageType type; + private String name; + private String sender; + private String value; + + public MessageNode getMessageNode() + { + return messageNode; + } + + public void setMessageNode(MessageNode messageNode) + { + this.messageNode = messageNode; + } + + public ChatMessageType getType() + { + return type; + } + + public void setType(ChatMessageType type) + { + this.type = type; + } + + public String getName() + { + return name; + } + + public void setName(String name) + { + this.name = name; + } + + public String getSender() + { + return sender; + } + + public void setSender(String sender) + { + this.sender = sender; + } + + public String getValue() + { + return value; + } + + public void setValue(String value) + { + this.value = value; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/PluginManager.java b/runelite-client/src/main/java/net/runelite/client/plugins/PluginManager.java index fb80119e7b..5f4d8e8879 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/PluginManager.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/PluginManager.java @@ -24,6 +24,7 @@ */ package net.runelite.client.plugins; +import net.runelite.client.plugins.pricecommands.PriceCommands; import net.runelite.client.task.ScheduledMethod; import com.google.common.util.concurrent.MoreExecutors; import com.google.common.util.concurrent.Service; @@ -102,6 +103,7 @@ public class PluginManager plugins.add(new FishingPlugin()); plugins.add(new WoodcuttingPlugin()); plugins.add(new RememberUsername()); + plugins.add(new PriceCommands()); if (RuneLite.getOptions().has("developer-mode")) { diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/pricecommands/PriceCommands.java b/runelite-client/src/main/java/net/runelite/client/plugins/pricecommands/PriceCommands.java new file mode 100644 index 0000000000..a9e329e06c --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/pricecommands/PriceCommands.java @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2017. l2- + * Copyright (c) 2017, Adam + * 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.pricecommands; + +import com.google.common.eventbus.Subscribe; +import java.io.IOException; +import java.util.concurrent.ScheduledExecutorService; +import net.runelite.api.Client; +import net.runelite.api.GameState; +import net.runelite.api.MessageNode; +import net.runelite.client.game.ItemManager; +import net.runelite.client.RuneLite; +import net.runelite.client.events.SetMessage; +import net.runelite.client.plugins.Plugin; +import net.runelite.http.api.item.ItemClient; +import net.runelite.http.api.item.ItemPrice; +import net.runelite.http.api.item.SearchResult; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class PriceCommands extends Plugin +{ + private static final Logger logger = LoggerFactory.getLogger(PriceCommands.class); + + private final PriceCommandsConfig config = RuneLite.getRunelite().getConfigManager().getConfig(PriceCommandsConfig.class); + private final ItemManager itemManager = RuneLite.getRunelite().getItemManager(); + private final ItemClient itemClient = new ItemClient(); + private final RuneLite runelite = RuneLite.getRunelite(); + private final Client client = RuneLite.getClient(); + + @Override + protected void startUp() throws Exception + { + } + + @Override + protected void shutDown() throws Exception + { + } + + @Subscribe + public void onSetMessage(SetMessage setMessage) + { + if (client.getGameState() != GameState.LOGGED_IN || !config.enabled()) + { + return; + } + + switch (setMessage.getType()) + { + case PUBLIC: + case CLANCHAT: + case PRIVATE_MESSAGE_RECEIVED: + break; + default: + return; + } + + String message = setMessage.getValue(); + + if (message.toLowerCase().startsWith("!price") && message.length() > 7) + { + String search = message.substring(7); + + logger.debug("Running lookup for {}", search); + + ScheduledExecutorService executor = runelite.getExecutor(); + executor.submit(() -> lookup(setMessage.getMessageNode(), search)); + } + } + + private void lookup(MessageNode messageNode, String search) + { + SearchResult result; + + try + { + result = itemClient.search(search); + } + catch (IOException ex) + { + logger.warn("Unable to search for item {}", search, ex); + return; + } + + if (result != null && result.getItems().size() == 1) + { + int itemId = result.getItems().get(0).getId(); + ItemPrice itemPrice; + + try + { + itemPrice = itemManager.getItemPrice(itemId); + } + catch (IOException ex) + { + logger.warn("Unable to fetch item price for {}", itemId, ex); + return; + } + + int cost = itemPrice.getPrice(); + String response = "Price of " + result.getItems().get(0).getName() + ": GE average " + String.format("%,d", cost); + + logger.debug("Setting response {}", response); + + // XXX hopefully messageNode hasn't been reused yet? + messageNode.setValue(response); + client.refreshChat(); + } + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/pricecommands/PriceCommandsConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/pricecommands/PriceCommandsConfig.java new file mode 100644 index 0000000000..109badbc39 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/pricecommands/PriceCommandsConfig.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2017, Adam + * 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.pricecommands; + +import net.runelite.client.config.ConfigGroup; +import net.runelite.client.config.ConfigItem; + +@ConfigGroup( + keyName = "pricecommands", + name = "Chat commands", + description = "Configuration for chat commands" +) +public interface PriceCommandsConfig +{ + @ConfigItem( + keyName = "enabled", + name = "Enable", + description = "Configures whether chat commands are enabled" + ) + default boolean enabled() + { + return true; + } + +} diff --git a/runescape-api/src/main/java/net/runelite/rs/api/Client.java b/runescape-api/src/main/java/net/runelite/rs/api/Client.java index e0e2a5451b..fb65948b3f 100644 --- a/runescape-api/src/main/java/net/runelite/rs/api/Client.java +++ b/runescape-api/src/main/java/net/runelite/rs/api/Client.java @@ -234,4 +234,10 @@ public interface Client extends GameEngine setter = true ) void setGameDrawingMode(int gameDrawingMode); + + @Import("cycleCntr") + int getCycleCntr(); + + @Import(value = "chatCycle", setter = true) + void setChatCycle(int value); } diff --git a/runescape-api/src/main/java/net/runelite/rs/api/MessageNode.java b/runescape-api/src/main/java/net/runelite/rs/api/MessageNode.java index 79d0c52621..a903b6357f 100644 --- a/runescape-api/src/main/java/net/runelite/rs/api/MessageNode.java +++ b/runescape-api/src/main/java/net/runelite/rs/api/MessageNode.java @@ -22,7 +22,6 @@ * (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.rs.api; import net.runelite.mapping.Import; @@ -37,4 +36,10 @@ public interface MessageNode @Import("value") String getValue(); + + @Import( + value = "value", + setter = true + ) + void setValue(String value); }