Merge pull request #4067 from Adam-/kc

Add kc command
This commit is contained in:
Adam
2018-07-08 17:12:34 -04:00
committed by GitHub
10 changed files with 644 additions and 6 deletions

View File

@@ -0,0 +1,77 @@
/*
* Copyright (c) 2018, Adam <Adam@sigterm.info>
* 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.kc;
import java.io.IOException;
import net.runelite.http.api.RuneLiteAPI;
import okhttp3.HttpUrl;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
public class KillCountClient
{
public boolean submit(String username, String boss, int kc) throws IOException
{
HttpUrl url = RuneLiteAPI.getApiBase().newBuilder()
.addPathSegment("kc")
.addQueryParameter("name", username)
.addQueryParameter("boss", boss)
.addQueryParameter("kc", Integer.toString(kc))
.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 int get(String username, String boss) throws IOException
{
HttpUrl url = RuneLiteAPI.getApiBase().newBuilder()
.addPathSegment("kc")
.addQueryParameter("name", username)
.addQueryParameter("boss", boss)
.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 killcount!");
}
return Integer.parseInt(response.body().string());
}
}
}

View File

@@ -0,0 +1,81 @@
/*
* Copyright (c) 2018, Adam <Adam@sigterm.info>
* 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.service.kc;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import java.util.concurrent.TimeUnit;
import net.runelite.http.service.util.exception.NotFoundException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/kc")
public class KillCountController
{
private final Cache<KillCountKey, Integer> cache = CacheBuilder.newBuilder()
.expireAfterWrite(2, TimeUnit.MINUTES)
.maximumSize(128L)
.build();
@Autowired
private KillCountService killCountService;
@PostMapping
public void submit(@RequestParam String name, @RequestParam String boss, @RequestParam int kc)
{
if (kc <= 0)
{
return;
}
killCountService.setKc(name, boss, kc);
cache.put(new KillCountKey(name, boss), kc);
}
@GetMapping
public int get(@RequestParam String name, @RequestParam String boss)
{
Integer kc = cache.getIfPresent(new KillCountKey(name, boss));
if (kc == null)
{
kc = killCountService.getKc(name, boss);
if (kc != null)
{
cache.put(new KillCountKey(name, boss), kc);
}
}
if (kc == null)
{
throw new NotFoundException();
}
return kc;
}
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright (c) 2018, Adam <Adam@sigterm.info>
* 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.service.kc;
import lombok.Value;
@Value
class KillCountKey
{
private String username;
private String boss;
}

View File

@@ -0,0 +1,97 @@
/*
* Copyright (c) 2018, Adam <Adam@sigterm.info>
* 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.service.kc;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.sql2o.Connection;
import org.sql2o.Sql2o;
@Service
public class KillCountService
{
private static final String CREATE = "CREATE TABLE IF NOT EXISTS `kc` (\n" +
" `name` varchar(32) NOT NULL,\n" +
" `boss` varchar(32) NOT NULL,\n" +
" `kc` int(11) NOT NULL,\n" +
" `time` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),\n" +
" UNIQUE KEY `name` (`name`, `boss`),\n" +
" KEY `time` (`time`)\n" +
") ENGINE=InnoDB;";
private final Sql2o sql2o;
@Autowired
public KillCountService(@Qualifier("Runelite SQL2O") Sql2o sql2o)
{
this.sql2o = sql2o;
try (Connection con = sql2o.open())
{
con.createQuery(CREATE)
.executeUpdate();
}
}
public Integer getKc(String name, String boss)
{
try (Connection con = sql2o.open())
{
return con.createQuery("select kc from kc where name = :name and boss = :boss")
.addParameter("name", name)
.addParameter("boss", boss)
.executeScalar(Integer.class);
}
}
public void setKc(String name, String boss, int kc)
{
try (Connection con = sql2o.open())
{
con.createQuery("insert into kc (name, boss, kc) values (:name, :boss, :kc) on duplicate key update kc = VALUES(kc)")
.addParameter("name", name)
.addParameter("boss", boss)
.addParameter("kc", kc)
.executeUpdate();
}
}
public void purge()
{
try (Connection con = sql2o.open())
{
con.createQuery("delete from kc where time < (now() - interval 2 minute);")
.executeUpdate();
}
}
@Scheduled(fixedDelay = 60_000)
public void schedPurge()
{
purge();
}
}

View File

@@ -26,5 +26,6 @@ package net.runelite.api;
public final class ScriptID
{
public static final int CHATBOX_INPUT = 96;
public static final int RUNELITE_CHATBOX_INPUT_INIT = 10001;
}

View File

@@ -33,33 +33,54 @@ import javax.inject.Provider;
import javax.inject.Singleton;
import lombok.extern.slf4j.Slf4j;
import net.runelite.api.Client;
import net.runelite.api.ScriptID;
import net.runelite.api.VarClientStr;
import net.runelite.api.events.CommandExecuted;
import net.runelite.api.events.ScriptCallbackEvent;
import net.runelite.client.callback.ClientThread;
import net.runelite.client.events.ChatboxInput;
@Slf4j
@Singleton
public class CommandManager
{
private static final String CALLBACK_NAME = "runeliteCommand";
private static final String RUNELITE_COMMAND = "runeliteCommand";
private static final String CHATBOX_INPUT = "chatboxInput";
private final Provider<Client> clientProvider;
private final EventBus eventBus;
private final Provider<ClientThread> clientThreadProvider;
private boolean sending;
@Inject
public CommandManager(Provider<Client> clientProvider, EventBus eventBus)
public CommandManager(Provider<Client> clientProvider, EventBus eventBus, Provider<ClientThread> clientThreadProvider)
{
this.clientProvider = clientProvider;
this.eventBus = eventBus;
this.clientThreadProvider = clientThreadProvider;
}
@Subscribe
private void scriptEvent(ScriptCallbackEvent event)
{
if (!CALLBACK_NAME.equals(event.getEventName()))
if (sending)
{
return;
}
switch (event.getEventName())
{
case RUNELITE_COMMAND:
runCommand();
break;
case CHATBOX_INPUT:
handleInput(event);
break;
}
}
private void runCommand()
{
Client client = clientProvider.get();
String typedText = client.getVar(VarClientStr.CHATBOX_TYPED_TEXT).substring(2); // strip ::
@@ -79,4 +100,55 @@ public class CommandManager
CommandExecuted commandExecuted = new CommandExecuted(command, args);
eventBus.post(commandExecuted);
}
private void handleInput(ScriptCallbackEvent event)
{
Client client = clientProvider.get();
final String[] stringStack = client.getStringStack();
final int[] intStack = client.getIntStack();
int stringStackCount = client.getStringStackSize();
int intStackCount = client.getIntStackSize();
final String typedText = stringStack[stringStackCount - 1];
final int chatType = intStack[intStackCount - 1];
ChatboxInput chatboxInput = new ChatboxInput(typedText, chatType)
{
private boolean resumed;
@Override
public void resume()
{
if (resumed)
{
return;
}
resumed = true;
ClientThread clientThread = clientThreadProvider.get();
clientThread.invokeLater(() -> sendChatboxInput(chatType, typedText));
}
};
eventBus.post(chatboxInput);
if (chatboxInput.isStop())
{
// input was blocked.
stringStack[stringStackCount - 1] = ""; // prevent script from sending
}
}
private void sendChatboxInput(int chatType, String input)
{
Client client = clientProvider.get();
sending = true;
try
{
client.runScript(ScriptID.CHATBOX_INPUT, chatType, input);
}
finally
{
sending = false;
}
}
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright (c) 2018, Adam <Adam@sigterm.info>
* 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.events;
import lombok.Data;
@Data
public abstract class ChatboxInput
{
private final String value;
private final int chatType;
private boolean stop;
public abstract void resume();
}

View File

@@ -47,7 +47,9 @@ import net.runelite.api.vars.AccountType;
import net.runelite.client.chat.ChatColorType;
import net.runelite.client.chat.ChatMessageBuilder;
import net.runelite.client.chat.ChatMessageManager;
import net.runelite.client.chat.CommandManager;
import net.runelite.client.config.ConfigManager;
import net.runelite.client.events.ChatboxInput;
import net.runelite.client.game.ItemManager;
import net.runelite.client.input.KeyManager;
import net.runelite.client.plugins.Plugin;
@@ -62,6 +64,7 @@ import net.runelite.http.api.hiscore.Skill;
import net.runelite.http.api.item.Item;
import net.runelite.http.api.item.ItemPrice;
import net.runelite.http.api.item.SearchResult;
import net.runelite.http.api.kc.KillCountClient;
@PluginDescriptor(
name = "Chat Commands",
@@ -76,6 +79,7 @@ public class ChatCommandsPlugin extends Plugin
private static final Pattern WINTERTODT_PATERN = Pattern.compile("Your subdued Wintertodt count is: <col=ff0000>(\\d+)</col>.");
private final HiscoreClient hiscoreClient = new HiscoreClient();
private final KillCountClient killCountClient = new KillCountClient();
@Inject
private Client client;
@@ -101,6 +105,9 @@ public class ChatCommandsPlugin extends Plugin
@Inject
private ChatKeyboardListener chatKeyboardListener;
@Inject
private CommandManager commandManager;
@Override
public void startUp()
{
@@ -194,6 +201,13 @@ public class ChatCommandsPlugin extends Plugin
log.debug("Running clue lookup for {}", search);
executor.submit(() -> playerClueLookup(setMessage, search));
}
else if (message.toLowerCase().startsWith("!kc "))
{
String search = message.substring(4);
log.debug("Running killcount lookup for {}", search);
executor.submit(() -> killCountLookup(setMessage.getType(), setMessage, search));
}
}
@Subscribe
@@ -223,12 +237,90 @@ public class ChatCommandsPlugin extends Plugin
}
}
@Subscribe
public void onChatboxInput(ChatboxInput chatboxInput)
{
final String value = chatboxInput.getValue();
if (!value.startsWith("!kc "))
{
return;
}
final String boss = longBossName(value.substring(4));
final int kc = getKc(boss);
if (kc <= 0)
{
return;
}
chatboxInput.setStop(true);
final String playerName = client.getLocalPlayer().getName();
executor.execute(() ->
{
try
{
killCountClient.submit(playerName, boss, kc);
}
catch (Exception ex)
{
log.warn("unable to submit killcount", ex);
}
finally
{
chatboxInput.resume();
}
});
}
private void killCountLookup(ChatMessageType type, SetMessage setMessage, String search)
{
final String player;
if (type.equals(ChatMessageType.PRIVATE_MESSAGE_SENT))
{
player = client.getLocalPlayer().getName();
}
else
{
player = sanitize(setMessage.getName());
}
search = longBossName(search);
final int kc;
try
{
kc = killCountClient.get(player, search);
}
catch (IOException ex)
{
log.debug("unable to lookup killcount", ex);
return;
}
String response = new ChatMessageBuilder()
.append(ChatColorType.HIGHLIGHT)
.append(search)
.append(ChatColorType.NORMAL)
.append(" kill count: ")
.append(ChatColorType.HIGHLIGHT)
.append(Integer.toString(kc))
.build();
log.debug("Setting response {}", response);
final MessageNode messageNode = setMessage.getMessageNode();
messageNode.setRuneLiteFormatMessage(response);
chatMessageManager.update(messageNode);
client.refreshChat();
}
/**
* Looks up the item price and changes the original message to the
* response.
*
* @param messageNode The chat message containing the command.
* @param search The item given with the command.
* @param search The item given with the command.
*/
private void itemPriceLookup(MessageNode messageNode, String search)
{
@@ -291,7 +383,7 @@ public class ChatCommandsPlugin extends Plugin
* response.
*
* @param setMessage The chat message containing the command.
* @param search The item given with the command.
* @param search The item given with the command.
*/
private void playerSkillLookup(SetMessage setMessage, String search)
{
@@ -415,6 +507,7 @@ public class ChatCommandsPlugin extends Plugin
/**
* Gets correct lookup data for message
*
* @param setMessage chat message
* @return hiscore lookup data
*/
@@ -452,7 +545,7 @@ public class ChatCommandsPlugin extends Plugin
* Returns the item if its name is equal to the original input or null
* if it can't find the item.
*
* @param items List of items.
* @param items List of items.
* @param originalInput String with the original input.
* @return Item which has a name equal to the original input.
*/
@@ -483,6 +576,7 @@ public class ChatCommandsPlugin extends Plugin
/**
* Looks up the ironman status of the local player. Does NOT work on other players.
*
* @return hiscore endpoint
*/
private HiscoreEndpoint getHiscoreEndpointType()
@@ -492,6 +586,7 @@ public class ChatCommandsPlugin extends Plugin
/**
* Returns the ironman status based on the symbol in the name of the player.
*
* @param name player name
* @return hiscore endpoint
*/
@@ -517,6 +612,7 @@ public class ChatCommandsPlugin extends Plugin
/**
* Converts account type to hiscore endpoint
*
* @param accountType account type
* @return hiscore endpoint
*/
@@ -541,4 +637,95 @@ public class ChatCommandsPlugin extends Plugin
private final String name;
private final HiscoreEndpoint endpoint;
}
private static String longBossName(String boss)
{
switch (boss.toLowerCase())
{
case "corp":
return "Corporeal Beast";
case "jad":
return "TzTok-Jad";
case "kq":
return "Kalphite Queen";
case "chaos ele":
return "Chaos Elemental";
case "dusk":
case "dawn":
case "gargs":
return "Grotesque Guardians";
case "archaeologist":
return "Crazy Archaeologist";
case "mole":
return "Giant Mole";
case "vetion":
return "Vet'ion";
case "kbd":
return "King Black Dragon";
case "sire":
return "Abyssal Sire";
case "smoke devil":
case "thermy":
return "Thermonuclear Smoke Devil";
case "zuk":
case "inferno":
return "TzKal-Zuk";
// gwd
case "sara":
case "saradomin":
case "zilyana":
return "Commander Zilyana";
case "zammy":
case "zamorak":
case "kril":
case "kril trutsaroth":
return "K'ril Tsutsaroth";
case "arma":
case "kree":
case "kreearra":
case "armadyl":
return "Kree'arra";
case "bando":
case "bandos":
case "graardor":
return "General Graardor";
// dks
case "supreme":
return "Dagannoth Supreme";
case "rex":
return "Dagannoth Rex";
case "prime":
return "Dagannoth Prime";
case "wt":
return "Wintertodt";
case "barrows":
return "Barrows Chests";
case "xeric":
case "olm":
case "raids":
return "Chambers of Xeric";
case "verxik":
case "raids 2":
return "Theatre of Blood";
default:
return boss;
}
}
}

View File

@@ -0,0 +1 @@
7D172258726F2B866D2EFFB0F2E401D0551A377DCA7F72CCD8839AC88B3EA6A2

View File

@@ -0,0 +1,51 @@
.id 96
.int_stack_count 1
.string_stack_count 1
.int_var_count 1
.string_var_count 1
sload 0 ; load input
iload 0 ; load chat type
load_string "chatboxInput" ; event name
runelite_callback ; invoke callback
pop_int ; pop chat type
string_length ; get string length of chat message
iload 0 ; load 0
if_icmpne LABEL100 ; if length is not 0, continue
return
LABEL100:
get_varbit 4394
load_int 1
if_icmpeq LABEL4
jump LABEL24
LABEL4:
iload 0
load_int 1
if_icmpeq LABEL8
jump LABEL16
LABEL8:
get_localplayer_name
load_string ": "
load_string "<col=2020ef>"
sload 0
load_string "</col>"
string_append 5
send_game_message
jump LABEL23
LABEL16:
get_localplayer_name
load_string ": "
load_string "<col=0000ff>"
sload 0
load_string "</col>"
string_append 5
send_game_message
LABEL23:
jump LABEL27
LABEL24:
sload 0
iload 0
chatbox_input
LABEL27:
get_gamecycle
put_varc 61
return