package me.chayapak1.chomens_bot.plugins; 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.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 ListenerAdapter { private final Configuration.IRC ircConfig; private final Map servers; public final Map> messageQueue = new HashMap<>(); private PircBotX bot; public IRCPlugin (Configuration config) { this.ircConfig = config.irc; this.servers = ircConfig.servers; if (!ircConfig.enabled) return; 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); if (!ircConfig.password.isEmpty()) builder.addCapHandler(new SASLCapHandler(ircConfig.name, ircConfig.password, true)); 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() { @Override public void connected(ConnectedEvent event) { IRCPlugin.this.connected(bot); } @Override public void loadedPlugins() { bot.chat.addListener(new ChatPlugin.Listener() { @Override public boolean systemMessageReceived(Component component, String string, String ansi) { IRCPlugin.this.systemMessageReceived(bot, ansi); return true; } }); } }); bot.irc = this; } Main.executor.scheduleAtFixedRate(this::queueTick, 0, 500, TimeUnit.MILLISECONDS); } @Override public void onMessage(MessageEvent event) { Bot serverBot = null; String targetChannel = null; for (Map.Entry entry : servers.entrySet()) { if (entry.getValue().equals(event.getChannel().getName())) { serverBot = Main.bots.stream() .filter(eachBot -> entry.getKey().equals(eachBot.host + ":" + eachBot.port)) .findFirst() .orElse(null); targetChannel = entry.getValue(); break; } } if (serverBot == null) return; final String commandPrefix = ircConfig.prefix; final User user = event.getUser(); if (user == null) return; final String name = user.getRealName().isBlank() ? user.getNick() : 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); if (output != null) context.sendOutput(output); return; } final Component prefix = Component .text(targetChannel) .hoverEvent( HoverEvent.showText( Component .empty() .append(Component.text("on ")) .append(Component.text(ircConfig.host)) .append(Component.text(":")) .append(Component.text(ircConfig.port)) .color(NamedTextColor.GRAY) ) ) .color(NamedTextColor.BLUE); final Component username = Component .text(name) .hoverEvent(HoverEvent.showText(Component.text(event.getUser().getHostname()).color(NamedTextColor.RED))) .color(NamedTextColor.RED); final Component messageComponent = Component .text(message) .color(NamedTextColor.GRAY); final Component component = Component.translatable( "[%s] %s › %s", prefix, username, messageComponent ).color(NamedTextColor.DARK_GRAY); serverBot.chat.tellraw(component); } private void systemMessageReceived (Bot bot, String ansi) { sendMessage( bot, ansi ); } public void quit (String reason) { if (bot.isConnected()) bot.sendIRC().quitServer(reason); } private void connected (Bot bot) { sendMessage( bot, String.format( "Successfully connected to: %s:%s", bot.host, bot.port ) ); } private void queueTick () { if (!bot.isConnected() || messageQueue.isEmpty()) return; try { final Map> clonedMap = new HashMap<>(messageQueue); for (Map.Entry> entry : clonedMap.entrySet()) { final List logs = entry.getValue(); if (logs.isEmpty()) continue; final String firstLog = logs.get(0); logs.remove(0); final String withIRCColors = ColorUtilities.convertAnsiToIrc(firstLog); bot.sendIRC().message(entry.getKey(), withIRCColors); } } catch (Exception ignored) {} } private void addMessageToQueue (Bot bot, String message) { final String channel = servers.get(bot.host + ":" + bot.port); addMessageToQueue(channel, message); } private void addMessageToQueue (String channel, String message) { final List split = new ArrayList<>(Arrays.asList(message.split("\n"))); if (!messageQueue.containsKey(channel)) { messageQueue.put(channel, split); } else { if (messageQueue.get(channel).size() > 10) return; messageQueue.get(channel).addAll(split); } } public void sendMessage (Bot bot, String message) { final String hostAndPort = bot.host + ":" + bot.port; final String channel = servers.get(hostAndPort); if (channel == null) { bot.logger.error("Channel is not configured for " + hostAndPort); return; } addMessageToQueue(channel, message); } }