From 8ee3e483f4112fbb51b5e9ad63256fd8dc502d9b Mon Sep 17 00:00:00 2001 From: Robert Alexander Date: Fri, 24 May 2019 21:45:27 +0200 Subject: [PATCH] chat commands: add duel arena chat command --- .../runelite/http/api/chat/ChatClient.java | 51 +++++++ .../net/runelite/http/api/chat/Duels.java | 36 +++++ .../http/service/chat/ChatController.java | 31 ++++ .../http/service/chat/ChatService.java | 41 ++++++ .../chatcommands/ChatCommandsConfig.java | 11 ++ .../chatcommands/ChatCommandsPlugin.java | 138 +++++++++++++++++- .../chatcommands/ChatCommandsPluginTest.java | 37 ++++- 7 files changed, 343 insertions(+), 2 deletions(-) create mode 100644 http-api/src/main/java/net/runelite/http/api/chat/Duels.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 2ab1d0c8ed..79c4a889ed 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 @@ -258,4 +258,55 @@ public class ChatClient return Integer.parseInt(response.body().string()); } } + + public boolean submitDuels(String username, int wins, int losses, int winningStreak, int losingStreak) throws IOException + { + HttpUrl url = RuneLiteAPI.getApiBase().newBuilder() + .addPathSegment("chat") + .addPathSegment("duels") + .addQueryParameter("name", username) + .addQueryParameter("wins", Integer.toString(wins)) + .addQueryParameter("losses", Integer.toString(losses)) + .addQueryParameter("winningStreak", Integer.toString(winningStreak)) + .addQueryParameter("losingStreak", Integer.toString(losingStreak)) + .build(); + + Request request = new Request.Builder() + .post(RequestBody.create(null, new byte[0])) + .url(url) + .build(); + + try (Response response = RuneLiteAPI.CLIENT.newCall(request).execute()) + { + return response.isSuccessful(); + } + } + + public Duels getDuels(String username) throws IOException + { + HttpUrl url = RuneLiteAPI.getApiBase().newBuilder() + .addPathSegment("chat") + .addPathSegment("duels") + .addQueryParameter("name", username) + .build(); + + Request request = new Request.Builder() + .url(url) + .build(); + + try (Response response = RuneLiteAPI.CLIENT.newCall(request).execute()) + { + if (!response.isSuccessful()) + { + throw new IOException("Unable to look up duels!"); + } + + InputStream in = response.body().byteStream(); + return RuneLiteAPI.GSON.fromJson(new InputStreamReader(in), Duels.class); + } + catch (JsonParseException ex) + { + throw new IOException(ex); + } + } } diff --git a/http-api/src/main/java/net/runelite/http/api/chat/Duels.java b/http-api/src/main/java/net/runelite/http/api/chat/Duels.java new file mode 100644 index 0000000000..ba117a526a --- /dev/null +++ b/http-api/src/main/java/net/runelite/http/api/chat/Duels.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2019, 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.http.api.chat; + +import lombok.Data; + +@Data +public class Duels +{ + private int wins; + private int losses; + private int winningStreak; + private int losingStreak; +} 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 f0a9fba213..7b5132e47a 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 @@ -29,6 +29,7 @@ import com.google.common.cache.CacheBuilder; import java.util.concurrent.TimeUnit; import java.util.regex.Matcher; import java.util.regex.Pattern; +import net.runelite.http.api.chat.Duels; import net.runelite.http.api.chat.Task; import net.runelite.http.service.util.exception.NotFoundException; import org.springframework.beans.factory.annotation.Autowired; @@ -177,4 +178,34 @@ public class ChatController } return pb; } + + @PostMapping("/duels") + public void submitDuels(@RequestParam String name, @RequestParam int wins, + @RequestParam int losses, + @RequestParam int winningStreak, @RequestParam int losingStreak) + { + if (wins < 0 || losses < 0 || winningStreak < 0 || losingStreak < 0) + { + return; + } + + Duels duels = new Duels(); + duels.setWins(wins); + duels.setLosses(losses); + duels.setWinningStreak(winningStreak); + duels.setLosingStreak(losingStreak); + + chatService.setDuels(name, duels); + } + + @GetMapping("/duels") + public Duels getDuels(@RequestParam String name) + { + Duels duels = chatService.getDuels(name); + if (duels == null) + { + throw new NotFoundException(); + } + return duels; + } } 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 70830889bc..0b0762d3ea 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,6 +28,7 @@ import com.google.common.collect.ImmutableMap; import java.time.Duration; import java.util.Map; 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; @@ -157,4 +158,44 @@ public class ChatService jedis.setex("gc." + name, (int) EXPIRE.getSeconds(), Integer.toString(gc)); } } + + public Duels getDuels(String name) + { + Map map; + + try (Jedis jedis = jedisPool.getResource()) + { + map = jedis.hgetAll("duels." + name); + } + + if (map.isEmpty()) + { + return null; + } + + Duels duels = new Duels(); + duels.setWins(Integer.parseInt(map.get("wins"))); + duels.setLosses(Integer.parseInt(map.get("losses"))); + duels.setWinningStreak(Integer.parseInt(map.get("winningStreak"))); + duels.setLosingStreak(Integer.parseInt(map.get("losingStreak"))); + return duels; + } + + public void setDuels(String name, Duels duels) + { + Map duelsMap = ImmutableMap.builderWithExpectedSize(4) + .put("wins", Integer.toString(duels.getWins())) + .put("losses", Integer.toString(duels.getLosses())) + .put("winningStreak", Integer.toString(duels.getWinningStreak())) + .put("losingStreak", Integer.toString(duels.getLosingStreak())) + .build(); + + String key = "duels." + name; + + try (Jedis jedis = jedisPool.getResource()) + { + jedis.hmset(key, duelsMap); + jedis.expire(key, (int) EXPIRE.getSeconds()); + } + } } 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 92987467d0..5a08937345 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 @@ -110,6 +110,17 @@ public interface ChatCommandsConfig extends Config @ConfigItem( position = 7, + keyName = "duels", + name = "Duels Command", + description = "Configures whether the duel arena command is enabled
!duels" + ) + default boolean duels() + { + return true; + } + + @ConfigItem( + position = 8, keyName = "clearShortcuts", name = "Clear shortcuts", description = "Enable shortcuts (ctrl+w and backspace) for clearing the chatbox" 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 ffc54bbad7..694542636f 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 @@ -65,6 +65,7 @@ import net.runelite.client.plugins.PluginDescriptor; import net.runelite.client.util.StackFormatter; import static net.runelite.client.util.Text.sanitize; import net.runelite.http.api.chat.ChatClient; +import net.runelite.http.api.chat.Duels; import net.runelite.http.api.hiscore.HiscoreClient; import net.runelite.http.api.hiscore.HiscoreEndpoint; import net.runelite.http.api.hiscore.HiscoreResult; @@ -88,6 +89,9 @@ public class ChatCommandsPlugin extends Plugin private static final Pattern BARROWS_PATTERN = Pattern.compile("Your Barrows chest count is: (\\d+)"); private static final Pattern KILL_DURATION_PATTERN = Pattern.compile("Fight duration: [0-9:]+. Personal best: ([0-9:]+)"); private static final Pattern NEW_PB_PATTERN = Pattern.compile("Fight duration: ([0-9:]+) \\(new personal best\\)"); + private static final Pattern DUEL_ARENA_WINS_PATTERN = Pattern.compile("You (were defeated|won)! You have(?: now)? won (\\d+) duels?"); + private static final Pattern DUEL_ARENA_LOSSES_PATTERN = Pattern.compile("You have(?: now)? lost (\\d+) duels?"); + private static final String TOTAL_LEVEL_COMMAND_STRING = "!total"; private static final String PRICE_COMMAND_STRING = "!price"; private static final String LEVEL_COMMAND_STRING = "!lvl"; @@ -97,6 +101,7 @@ public class ChatCommandsPlugin extends Plugin private static final String QP_COMMAND_STRING = "!qp"; private static final String PB_COMMAND = "!pb"; private static final String GC_COMMAND_STRING = "!gc"; + private static final String DUEL_ARENA_COMMAND = "!duels"; private final HiscoreClient hiscoreClient = new HiscoreClient(); private final ChatClient chatClient = new ChatClient(); @@ -146,6 +151,7 @@ public class ChatCommandsPlugin extends Plugin chatCommandManager.registerCommandAsync(QP_COMMAND_STRING, this::questPointsLookup, this::questPointsSubmit); chatCommandManager.registerCommandAsync(PB_COMMAND, this::personalBestLookup, this::personalBestSubmit); chatCommandManager.registerCommandAsync(GC_COMMAND_STRING, this::gambleCountLookup, this::gambleCountSubmit); + chatCommandManager.registerCommandAsync(DUEL_ARENA_COMMAND, this::duelArenaLookup, this::duelArenaSubmit); } @Override @@ -164,6 +170,7 @@ public class ChatCommandsPlugin extends Plugin chatCommandManager.unregisterCommand(QP_COMMAND_STRING); chatCommandManager.unregisterCommand(PB_COMMAND); chatCommandManager.unregisterCommand(GC_COMMAND_STRING); + chatCommandManager.unregisterCommand(DUEL_ARENA_COMMAND); } @Provides @@ -201,7 +208,9 @@ public class ChatCommandsPlugin extends Plugin @Subscribe public void onChatMessage(ChatMessage chatMessage) { - if (chatMessage.getType() != ChatMessageType.GAMEMESSAGE && chatMessage.getType() != ChatMessageType.SPAM) + if (chatMessage.getType() != ChatMessageType.TRADE + && chatMessage.getType() != ChatMessageType.GAMEMESSAGE + && chatMessage.getType() != ChatMessageType.SPAM) { return; } @@ -237,6 +246,43 @@ public class ChatCommandsPlugin extends Plugin return; } + matcher = DUEL_ARENA_WINS_PATTERN.matcher(message); + if (matcher.find()) + { + final int oldWins = getKc("Duel Arena Wins"); + final int wins = Integer.parseInt(matcher.group(2)); + final String result = matcher.group(1); + int winningStreak = getKc("Duel Arena Win Streak"); + int losingStreak = getKc("Duel Arena Lose Streak"); + + if (result.equals("won") && wins > oldWins) + { + losingStreak = 0; + winningStreak += 1; + } + else if (result.equals("were defeated")) + { + losingStreak += 1; + winningStreak = 0; + } + else + { + log.warn("unrecognized duel streak chat message: {}", message); + } + + setKc("Duel Arena Wins", wins); + setKc("Duel Arena Win Streak", winningStreak); + setKc("Duel Arena Lose Streak", losingStreak); + } + + matcher = DUEL_ARENA_LOSSES_PATTERN.matcher(message); + if (matcher.find()) + { + int losses = Integer.parseInt(matcher.group(1)); + + setKc("Duel Arena Losses", losses); + } + matcher = BARROWS_PATTERN.matcher(message); if (matcher.find()) { @@ -417,6 +463,96 @@ public class ChatCommandsPlugin extends Plugin client.refreshChat(); } + private boolean duelArenaSubmit(ChatInput chatInput, String value) + { + final int wins = getKc("Duel Arena Wins"); + final int losses = getKc("Duel Arena Losses"); + final int winningStreak = getKc("Duel Arena Win Streak"); + final int losingStreak = getKc("Duel Arena Lose Streak"); + + if (wins <= 0 && losses <= 0 && winningStreak <= 0 && losingStreak <= 0) + { + return false; + } + + final String playerName = client.getLocalPlayer().getName(); + + executor.execute(() -> + { + try + { + chatClient.submitDuels(playerName, wins, losses, winningStreak, losingStreak); + } + catch (Exception ex) + { + log.warn("unable to submit duels", ex); + } + finally + { + chatInput.resume(); + } + }); + + return true; + } + + private void duelArenaLookup(ChatMessage chatMessage, String message) + { + if (!config.duels()) + { + return; + } + + ChatMessageType type = chatMessage.getType(); + + final String player; + if (type == ChatMessageType.PRIVATECHATOUT) + { + player = client.getLocalPlayer().getName(); + } + else + { + player = sanitize(chatMessage.getName()); + } + + Duels duels; + try + { + duels = chatClient.getDuels(player); + } + catch (IOException ex) + { + log.debug("unable to lookup duels", ex); + return; + } + + final int wins = duels.getWins(); + final int losses = duels.getLosses(); + final int winningStreak = duels.getWinningStreak(); + final int losingStreak = duels.getLosingStreak(); + + String response = new ChatMessageBuilder() + .append(ChatColorType.NORMAL) + .append("Duel Arena wins: ") + .append(ChatColorType.HIGHLIGHT) + .append(Integer.toString(wins)) + .append(ChatColorType.NORMAL) + .append(" losses: ") + .append(ChatColorType.HIGHLIGHT) + .append(Integer.toString(losses)) + .append(ChatColorType.NORMAL) + .append(" streak: ") + .append(ChatColorType.HIGHLIGHT) + .append(Integer.toString((winningStreak != 0 ? winningStreak : -losingStreak))) + .build(); + + log.debug("Setting response {}", response); + final MessageNode messageNode = chatMessage.getMessageNode(); + messageNode.setRuneLiteFormatMessage(response); + chatMessageManager.update(messageNode); + client.refreshChat(); + } + private void questPointsLookup(ChatMessage chatMessage, String message) { if (!config.qp()) 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 922b8fed25..ea96a7caec 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 @@ -30,6 +30,7 @@ import com.google.inject.testing.fieldbinder.BoundFieldModule; import java.util.concurrent.ScheduledExecutorService; import javax.inject.Inject; import static net.runelite.api.ChatMessageType.GAMEMESSAGE; +import static net.runelite.api.ChatMessageType.TRADE; import net.runelite.api.Client; import net.runelite.api.events.ChatMessage; import net.runelite.client.config.ChatColorConfig; @@ -191,4 +192,38 @@ public class ChatCommandsPluginTest verify(configManager).setConfiguration(eq("personalbest.adam"), eq("kree'arra"), eq(181)); } -} \ No newline at end of file + + @Test + public void testDuelArenaWin() + { + when(client.getUsername()).thenReturn("Adam"); + + ChatMessage chatMessageEvent = new ChatMessage(null, TRADE, "", "You won! You have now won 27 duels.", null, 0); + chatCommandsPlugin.onChatMessage(chatMessageEvent); + + verify(configManager).setConfiguration("killcount.adam", "duel arena wins", 27); + verify(configManager).setConfiguration("killcount.adam", "duel arena win streak", 1); + } + + @Test + public void testDuelArenaWin2() + { + when(client.getUsername()).thenReturn("Adam"); + + ChatMessage chatMessageEvent = new ChatMessage(null, TRADE, "", "You were defeated! You have won 22 duels.", null, 0); + chatCommandsPlugin.onChatMessage(chatMessageEvent); + + verify(configManager).setConfiguration("killcount.adam", "duel arena wins", 22); + } + + @Test + public void testDuelArenaLose() + { + when(client.getUsername()).thenReturn("Adam"); + + ChatMessage chatMessageEvent = new ChatMessage(null, TRADE, "", "You have now lost 999 duels.", null, 0); + chatCommandsPlugin.onChatMessage(chatMessageEvent); + + verify(configManager).setConfiguration("killcount.adam", "duel arena losses", 999); + } +}