From 5ac244e3d2eaf7090e8cae63aa25c5dd1624245b Mon Sep 17 00:00:00 2001
From: ChomeNS <95471003+ChomeNS@users.noreply.github.com>
Date: Fri, 18 Oct 2024 08:22:39 +0700
Subject: [PATCH] refactor IRC which is VERY broken currently theres
ConcurrentModificationException at IRCPlugin line 190 (for loop). i guess we
could fix this by cloning it?
---
.idea/jarRepositories.xml | 5 +
build.gradle | 6 +
.../chayapak1/chomens_bot/Configuration.java | 9 +-
.../chomens_bot/irc/IRCMessageLoop.java | 128 ------------------
.../chomens_bot/irc/MessageBuffer.java | 29 ----
.../chomens_bot/irc/MessageParser.java | 70 ----------
.../chomens_bot/plugins/IRCPlugin.java | 127 +++++++++--------
.../chomens_bot/util/ColorUtilities.java | 63 +++++++++
src/main/resources/default-config.yml | 9 +-
src/main/resources/logback.xml | 12 ++
10 files changed, 165 insertions(+), 293 deletions(-)
delete mode 100644 src/main/java/me/chayapak1/chomens_bot/irc/IRCMessageLoop.java
delete mode 100644 src/main/java/me/chayapak1/chomens_bot/irc/MessageBuffer.java
delete mode 100644 src/main/java/me/chayapak1/chomens_bot/irc/MessageParser.java
create mode 100644 src/main/resources/logback.xml
diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml
index 78b7d7a..9030eee 100644
--- a/.idea/jarRepositories.xml
+++ b/.idea/jarRepositories.xml
@@ -56,5 +56,10 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
index cdfc0ba..50e9e41 100644
--- a/build.gradle
+++ b/build.gradle
@@ -19,6 +19,10 @@ repositories {
mavenCentral()
+ maven {
+ url = uri('https://jitpack.io')
+ }
+
maven {
url = uri('https://repo.opencollab.dev/main/')
}
@@ -46,6 +50,8 @@ dependencies {
implementation 'com.google.code.gson:gson:2.10.1'
implementation 'com.google.guava:guava:31.1-jre'
implementation 'org.jline:jline:3.23.0'
+ implementation 'ch.qos.logback:logback-classic:1.5.11'
+ implementation 'com.github.pircbotx:pircbotx:2.3.1'
implementation 'com.github.ricksbrown:cowsay:1.1.0'
implementation 'org.yaml:snakeyaml:2.0'
implementation 'org.luaj:luaj-jse:3.0.1'
diff --git a/src/main/java/me/chayapak1/chomens_bot/Configuration.java b/src/main/java/me/chayapak1/chomens_bot/Configuration.java
index e58cdc7..6027377 100644
--- a/src/main/java/me/chayapak1/chomens_bot/Configuration.java
+++ b/src/main/java/me/chayapak1/chomens_bot/Configuration.java
@@ -97,7 +97,7 @@ public class Configuration {
public EmbedColors embedColors = new EmbedColors();
public String trustedRoleName = "Trusted";
public String adminRoleName = "Admin";
- public String statusMessage = "Gay Sex";
+ public String statusMessage = "Oh hi!";
public String inviteLink = "https://discord.gg/xdgCkUyaA4";
}
@@ -106,11 +106,8 @@ public class Configuration {
public String prefix = "!";
public String host;
public int port;
- public String nickname = "chomens-bot";
- public String username = "chomens-bot";
- public String realName = "chomens-bot";
- public String hostName = "null";
- public String serverName = "null";
+ public String name = "chomens-bot";
+ public String password = "";
public Map servers = new HashMap<>();
}
diff --git a/src/main/java/me/chayapak1/chomens_bot/irc/IRCMessageLoop.java b/src/main/java/me/chayapak1/chomens_bot/irc/IRCMessageLoop.java
deleted file mode 100644
index b2c579d..0000000
--- a/src/main/java/me/chayapak1/chomens_bot/irc/IRCMessageLoop.java
+++ /dev/null
@@ -1,128 +0,0 @@
-package me.chayapak1.chomens_bot.irc;
-
-import me.chayapak1.chomens_bot.data.IRCMessage;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.Socket;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-// https://gist.github.com/kaecy/286f8ad334aec3fcb588516feb727772#file-ircmessageloop-java
-public abstract class IRCMessageLoop implements Runnable {
- private Socket socket;
- private OutputStream out;
-
- public List channelList;
-
- protected boolean initialSetupStatus = false;
-
- public IRCMessageLoop (String host, int port) {
- channelList = new ArrayList<>();
- try {
- socket = new Socket(host, port);
- out = socket.getOutputStream();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
-
- public void send(String text) {
- byte[] bytes = (text + "\r\n").getBytes();
-
- try {
- out.write(bytes);
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
-
- public void nick(String nickname) {
- final String msg = "NICK " + nickname;
- send(msg);
- }
-
- public void user(String username, String hostname, String serverName, String realName) {
- final String msg = "USER " + username + " " + hostname + " " + serverName + " :" + realName;
- send(msg);
- }
-
- public void join(String channel) {
- if (!initialSetupStatus) {
- channelList.add(channel);
- return;
- }
-
- final String msg = "JOIN " + channel;
- send(msg);
- }
-
- public void part(String channel) {
- final String msg = "PART " + channel;
- send(msg);
- }
-
- public void channelMessage (String channel, String message) {
- final String msg = "PRIVMSG " + channel + " :" + message;
- send(msg);
- }
-
- public void pong (String server) {
- final String msg = "PONG " + server;
- send(msg);
- }
-
- public void quit (String reason) {
- final String msg = "QUIT :Quit: " + reason;
- send(msg);
- }
-
- protected abstract void onMessage (IRCMessage message);
-
- private void initialSetup() {
- initialSetupStatus = true;
-
- // now join the channels. you need to wait for message 001 before you join a channel.
- for (String channel: channelList) {
- join(channel);
- }
- }
-
- private void processMessage(String ircMessage) {
- final IRCMessage message = MessageParser.message(ircMessage);
-
- switch (message.command) {
- case "privmsg" -> onMessage(message);
- case "001" -> initialSetup();
- case "ping" -> pong(message.content);
- }
- }
-
- public void run() {
- final InputStream stream;
-
- try {
- stream = socket.getInputStream();
- final MessageBuffer messageBuffer = new MessageBuffer();
- final byte[] buffer = new byte[512];
-
- int count;
-
- while (true) {
- count = stream.read(buffer);
- if (count == -1) break;
- messageBuffer.append(Arrays.copyOfRange(buffer, 0, count));
- while (messageBuffer.hasCompleteMessage()) {
- String ircMessage = messageBuffer.getNextMessage();
-
- processMessage(ircMessage);
- }
- }
- } catch (IOException e) {
- quit("Internal Error: " + e);
- e.printStackTrace();
- }
- }
-}
diff --git a/src/main/java/me/chayapak1/chomens_bot/irc/MessageBuffer.java b/src/main/java/me/chayapak1/chomens_bot/irc/MessageBuffer.java
deleted file mode 100644
index 8c862e3..0000000
--- a/src/main/java/me/chayapak1/chomens_bot/irc/MessageBuffer.java
+++ /dev/null
@@ -1,29 +0,0 @@
-package me.chayapak1.chomens_bot.irc;
-
-public class MessageBuffer {
- private String buffer;
-
- public MessageBuffer() {
- buffer = "";
- }
-
- public void append (byte[] bytes) {
- buffer += new String(bytes);
- }
-
- public boolean hasCompleteMessage() {
- return buffer.contains("\r\n");
- }
-
- public String getNextMessage() {
- int index = buffer.indexOf("\r\n");
- String message = "";
-
- if (index > -1) {
- message = buffer.substring(0, index);
- buffer = buffer.substring(index + 2);
- }
-
- return message;
- }
-}
diff --git a/src/main/java/me/chayapak1/chomens_bot/irc/MessageParser.java b/src/main/java/me/chayapak1/chomens_bot/irc/MessageParser.java
deleted file mode 100644
index 21dda2d..0000000
--- a/src/main/java/me/chayapak1/chomens_bot/irc/MessageParser.java
+++ /dev/null
@@ -1,70 +0,0 @@
-package me.chayapak1.chomens_bot.irc;
-
-import me.chayapak1.chomens_bot.data.IRCMessage;
-
-// https://gist.github.com/kaecy/286f8ad334aec3fcb588516feb727772#file-messageparser-java
-public class MessageParser {
- public static IRCMessage message (String ircMessage) {
- final IRCMessage message = new IRCMessage();
-
- int spIndex;
-
- if (ircMessage.startsWith(":")) {
- spIndex = ircMessage.indexOf(' ');
- if (spIndex > -1) {
- message.origin = ircMessage.substring(1, spIndex);
- ircMessage = ircMessage.substring(spIndex + 1);
-
- int uIndex = message.origin.indexOf('!');
- if (uIndex > -1) {
- message.nickName = message.origin.substring(0, uIndex);
-
- message.origin = message.origin.substring(uIndex + 1);
- }
- }
- }
-
- spIndex = ircMessage.indexOf(' ');
- if (spIndex == -1) {
- message.command = "null";
- return message;
- }
-
- message.command = ircMessage.substring(0, spIndex).toLowerCase();
- ircMessage = ircMessage.substring(spIndex + 1);
-
- // parse privmsg params
- if (message.command.equals("privmsg")) {
- spIndex = ircMessage.indexOf(' ');
- message.channel = ircMessage.substring(0, spIndex);
- ircMessage = ircMessage.substring(spIndex + 1);
-
- if (ircMessage.startsWith(":")) {
- message.content = ircMessage.substring(1);
- } else {
- message.content = ircMessage;
- }
- }
-
- // parse quit/join
- if (message.command.equals("quit") || message.command.equals("join")) {
- if (ircMessage.startsWith(":")) {
- message.content = ircMessage.substring(1);
- } else {
- message.content = ircMessage;
- }
- }
-
- // parse ping params
- if (message.command.equals("ping")) {
- spIndex = ircMessage.indexOf(' ');
- if (spIndex > -1) {
- message.content = ircMessage.substring(0, spIndex);
- } else {
- message.content = ircMessage;
- }
- }
-
- return message;
- }
-}
diff --git a/src/main/java/me/chayapak1/chomens_bot/plugins/IRCPlugin.java b/src/main/java/me/chayapak1/chomens_bot/plugins/IRCPlugin.java
index 1f0ee36..70c3e2b 100644
--- a/src/main/java/me/chayapak1/chomens_bot/plugins/IRCPlugin.java
+++ b/src/main/java/me/chayapak1/chomens_bot/plugins/IRCPlugin.java
@@ -4,41 +4,60 @@ import me.chayapak1.chomens_bot.Bot;
import me.chayapak1.chomens_bot.Configuration;
import me.chayapak1.chomens_bot.Main;
import me.chayapak1.chomens_bot.command.IRCCommandContext;
-import me.chayapak1.chomens_bot.data.IRCMessage;
-import me.chayapak1.chomens_bot.data.chat.PlayerMessage;
-import me.chayapak1.chomens_bot.irc.IRCMessageLoop;
+import me.chayapak1.chomens_bot.util.ColorUtilities;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.event.HoverEvent;
import net.kyori.adventure.text.format.NamedTextColor;
import org.geysermc.mcprotocollib.network.event.session.ConnectedEvent;
+import org.pircbotx.PircBotX;
+import org.pircbotx.User;
+import org.pircbotx.cap.SASLCapHandler;
+import org.pircbotx.hooks.ListenerAdapter;
+import org.pircbotx.hooks.events.MessageEvent;
+import javax.net.ssl.SSLSocketFactory;
import java.util.*;
import java.util.concurrent.TimeUnit;
-public class IRCPlugin extends IRCMessageLoop {
+public class IRCPlugin extends ListenerAdapter {
private final Configuration.IRC ircConfig;
private final Map servers;
- private final Map> messageQueue = new HashMap<>();
+ public final Map> messageQueue = new HashMap<>();
-// private String lastMessage = "";
+ private PircBotX bot;
public IRCPlugin (Configuration config) {
- super(config.irc.host, config.irc.port);
-
this.ircConfig = config.irc;
this.servers = ircConfig.servers;
if (!ircConfig.enabled) return;
- nick(ircConfig.nickname);
- user(ircConfig.username, ircConfig.hostName, ircConfig.serverName, ircConfig.realName);
+ final org.pircbotx.Configuration.Builder builder = new org.pircbotx.Configuration.Builder()
+ .setName(ircConfig.name)
+ .setLogin("bot@chomens-bot")
+ .addServer(ircConfig.host, ircConfig.port)
+ .setSocketFactory(SSLSocketFactory.getDefault())
+ .setAutoReconnect(true)
+ .addListener(this);
- for (Map.Entry entry : ircConfig.servers.entrySet()) join(entry.getValue());
+ if (!ircConfig.password.isEmpty()) builder.addCapHandler(new SASLCapHandler(ircConfig.name, ircConfig.password, true));
- Main.executorService.submit(this);
+ for (Map.Entry entry : ircConfig.servers.entrySet()) builder.addAutoJoinChannel(entry.getValue());
+
+ final org.pircbotx.Configuration configuration = builder.buildConfiguration();
+
+ bot = new PircBotX(configuration);
+
+ new Thread(() -> {
+ try {
+ bot.startBot();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }).start();
for (Bot bot : Main.bots) {
bot.addListener(new Bot.Listener() {
@@ -51,8 +70,8 @@ public class IRCPlugin extends IRCMessageLoop {
public void loadedPlugins() {
bot.chat.addListener(new ChatPlugin.Listener() {
@Override
- public boolean playerMessageReceived(PlayerMessage message) {
- IRCPlugin.this.playerMessageReceived(bot, message);
+ public boolean systemMessageReceived(Component component, String string, String ansi) {
+ IRCPlugin.this.systemMessageReceived(bot, ansi);
return true;
}
@@ -67,18 +86,19 @@ public class IRCPlugin extends IRCMessageLoop {
}
@Override
- protected void onMessage (IRCMessage message) {
+ public void onMessage(MessageEvent event) {
Bot serverBot = null;
- String channel = null;
+ String targetChannel = null;
for (Map.Entry entry : servers.entrySet()) {
- if (entry.getValue().equals(message.channel)) {
+ if (entry.getValue().equals(event.getChannel().getName())) {
serverBot = Main.bots.stream()
.filter(eachBot -> entry.getKey().equals(eachBot.host + ":" + eachBot.port))
- .toArray(Bot[]::new)[0];
+ .findFirst()
+ .orElse(null);
- channel = entry.getValue();
+ targetChannel = entry.getValue();
break;
}
@@ -88,10 +108,17 @@ public class IRCPlugin extends IRCMessageLoop {
final String commandPrefix = ircConfig.prefix;
- if (message.content.startsWith(commandPrefix)) {
- final String noPrefix = message.content.substring(commandPrefix.length());
+ final User user = event.getUser();
- final IRCCommandContext context = new IRCCommandContext(serverBot, commandPrefix, message.nickName);
+ if (user == null) return;
+
+ final String name = user.getRealName();
+ final String message = event.getMessage();
+
+ if (message.startsWith(commandPrefix)) {
+ final String noPrefix = message.substring(commandPrefix.length());
+
+ final IRCCommandContext context = new IRCCommandContext(serverBot, commandPrefix, name);
final Component output = serverBot.commandHandler.executeCommand(noPrefix, context, null);
@@ -101,7 +128,7 @@ public class IRCPlugin extends IRCMessageLoop {
}
final Component prefix = Component
- .text(channel)
+ .text(targetChannel)
.hoverEvent(
HoverEvent.showText(
Component
@@ -116,12 +143,12 @@ public class IRCPlugin extends IRCMessageLoop {
.color(NamedTextColor.BLUE);
final Component username = Component
- .text(message.nickName)
- .hoverEvent(HoverEvent.showText(Component.text(message.origin).color(NamedTextColor.RED)))
+ .text(name)
+ .hoverEvent(HoverEvent.showText(Component.text(event.getUser().getHostname()).color(NamedTextColor.RED)))
.color(NamedTextColor.RED);
final Component messageComponent = Component
- .text(message.content)
+ .text(message)
.color(NamedTextColor.GRAY);
final Component component = Component.translatable(
@@ -134,31 +161,17 @@ public class IRCPlugin extends IRCMessageLoop {
serverBot.chat.tellraw(component);
}
- private void playerMessageReceived (Bot bot, PlayerMessage message) {
- // best fix in 2023
-
- /*
- final String stringifiedName = ComponentUtilities.stringify(message.displayName);
- final String stringifiedContents = ComponentUtilities.stringify(message.contents);
-
- final String toSend = String.format(
- "<%s> %s",
- stringifiedName,
- stringifiedContents
- );
-
- if (lastMessage.equals(toSend)) return;
-
- lastMessage = toSend;
-
- addMessageToQueue(
+ private void systemMessageReceived (Bot bot, String ansi) {
+ sendMessage(
bot,
- toSend
+ ansi
);
- */
}
- // i only have listened to connected because the ratelimit
+ public void quit (String reason) {
+ bot.sendIRC().quitServer(reason);
+ }
+
private void connected (Bot bot) {
sendMessage(
bot,
@@ -171,18 +184,24 @@ public class IRCPlugin extends IRCMessageLoop {
}
private void queueTick () {
- if (!initialSetupStatus || messageQueue.isEmpty()) return;
+ if (!bot.isConnected() || messageQueue.isEmpty()) return;
- for (Map.Entry> entry : messageQueue.entrySet()) {
- final List logs = entry.getValue();
+ try {
+ for (Map.Entry> entry : messageQueue.entrySet()) {
+ final List logs = entry.getValue();
- if (logs.isEmpty()) continue;
+ if (logs.isEmpty()) continue;
- final String firstLog = logs.get(0);
+ final String firstLog = logs.get(0);
- logs.remove(0);
+ logs.remove(0);
- channelMessage(entry.getKey(), firstLog);
+ final String withIRCColors = ColorUtilities.convertAnsiToIrc(firstLog);
+
+ bot.sendIRC().message(entry.getKey(), withIRCColors);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
}
}
diff --git a/src/main/java/me/chayapak1/chomens_bot/util/ColorUtilities.java b/src/main/java/me/chayapak1/chomens_bot/util/ColorUtilities.java
index 19b2227..fa47b0b 100644
--- a/src/main/java/me/chayapak1/chomens_bot/util/ColorUtilities.java
+++ b/src/main/java/me/chayapak1/chomens_bot/util/ColorUtilities.java
@@ -4,8 +4,12 @@ import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.format.TextColor;
import java.awt.*;
+import java.util.HashMap;
+import java.util.Map;
public class ColorUtilities {
+ private static final Map ansiToIrcMap = new HashMap<>();
+
public static TextColor getColorByString (String _color) {
final String color = _color.toLowerCase();
@@ -87,6 +91,65 @@ public class ColorUtilities {
return closest.colorName;
}
+ // Author: ChatGPT
+ static {
+ ansiToIrcMap.put(30, "01"); // Black -> IRC Black
+ ansiToIrcMap.put(31, "04"); // Red -> IRC Red
+ ansiToIrcMap.put(32, "09"); // Green -> IRC Green
+ ansiToIrcMap.put(33, "08"); // Yellow -> IRC Yellow
+ ansiToIrcMap.put(34, "02"); // Blue -> IRC Blue
+ ansiToIrcMap.put(35, "13"); // Magenta -> IRC Magenta (Purple)
+ ansiToIrcMap.put(36, "11"); // Cyan -> IRC Cyan
+ ansiToIrcMap.put(37, "00"); // White -> IRC White
+ ansiToIrcMap.put(39, "00"); // White -> IRC White
+
+ final Map clone = new HashMap<>(ansiToIrcMap);
+
+ for (Map.Entry entry : clone.entrySet()) {
+ ansiToIrcMap.put(entry.getKey() + 10, ansiToIrcMap.get(entry.getKey()));
+ }
+
+ ansiToIrcMap.put(49, "01");
+ }
+
+ public static String convertAnsiToIrc(String input) {
+ StringBuilder result = new StringBuilder();
+ boolean insideEscape = false;
+ StringBuilder ansiCode = new StringBuilder();
+
+ for (int i = 0; i < input.length(); i++) {
+ char c = input.charAt(i);
+ if (insideEscape) {
+ if (c == 'm') {
+ insideEscape = false;
+ // Parse the ANSI color code and map it to IRC color
+ String[] codes = ansiCode.toString().split(";");
+ for (String code : codes) {
+ try {
+ int ansiColorCode = Integer.parseInt(code);
+ if (ansiToIrcMap.containsKey(ansiColorCode)) {
+ result.append("\u0003").append(ansiToIrcMap.get(ansiColorCode));
+ }
+ } catch (NumberFormatException ignored) {
+ }
+ }
+ ansiCode.setLength(0); // Clear the buffer
+ } else {
+ ansiCode.append(c); // Collect the rest of the ANSI sequence
+ }
+ } else {
+ if (c == '\u001B' && i + 1 < input.length() && input.charAt(i + 1) == '[') {
+ insideEscape = true;
+ i++; // Skip the '[' character
+ } else {
+ result.append(c); // Append non-ANSI characters directly
+ }
+ }
+ }
+
+ return result.toString();
+ }
+
public static final class ChatColor {
private final String colorName;
diff --git a/src/main/resources/default-config.yml b/src/main/resources/default-config.yml
index 17a54dc..dd4778f 100644
--- a/src/main/resources/default-config.yml
+++ b/src/main/resources/default-config.yml
@@ -39,13 +39,10 @@ irc:
prefix: '!'
host: 'irc.libera.chat'
port: 6665
- nickname: 'chomens-bot'
- username: 'chomens-bot'
- realName: 'chomens-bot'
- hostName: 'null'
- serverName: 'null'
+ name: 'chomens_bot'
+ password: ''
servers:
- localhost:25565: '#chomens-localhost' # channel name
+ localhost:25565: '#chomens/localhost' # channel name
music:
urlRatelimit:
diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml
new file mode 100644
index 0000000..e06ec00
--- /dev/null
+++ b/src/main/resources/logback.xml
@@ -0,0 +1,12 @@
+
+
+
+
+ %d{HH:mm:ss.SSS} %boldCyan(%-34.-34thread) %red(%10.10X{jda.shard}) %boldGreen(%-15.-15logger{0}) %highlight(%-6level) %msg%n
+
+
+
+
+
+
+