diff --git a/pom.xml b/pom.xml index f283fc7..0ccfe46 100755 --- a/pom.xml +++ b/pom.xml @@ -106,6 +106,9 @@ UTF-8 + + master + @@ -159,11 +162,16 @@ provided - - com.discord4j - Discord4J - 2.10.1 - + + + com.github.SizableShrimp + Discord4J + httprequestchange-SNAPSHOT + org.slf4j @@ -173,7 +181,7 @@ com.github.TBMCPlugins.ButtonCore ButtonCore - master-SNAPSHOT + ${branch}-SNAPSHOT provided @@ -225,4 +233,19 @@ 4.0.0 + + + + ci + + + env.TRAVIS_BRANCH + + + + + ${env.TRAVIS_BRANCH} + + + diff --git a/src/main/java/buttondevteam/discordplugin/AsyncDiscordEvent.java b/src/main/java/buttondevteam/discordplugin/AsyncDiscordEvent.java new file mode 100644 index 0000000..c4479f3 --- /dev/null +++ b/src/main/java/buttondevteam/discordplugin/AsyncDiscordEvent.java @@ -0,0 +1,27 @@ +package buttondevteam.discordplugin; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.Setter; +import org.bukkit.event.Cancellable; +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; + +@RequiredArgsConstructor +public class AsyncDiscordEvent extends Event implements Cancellable { + private final @Getter T event; + @Getter + @Setter + private boolean cancelled; + + private static final HandlerList handlers = new HandlerList(); + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/src/main/java/buttondevteam/discordplugin/ChromaBot.java b/src/main/java/buttondevteam/discordplugin/ChromaBot.java index 79a0514..56a6fb9 100755 --- a/src/main/java/buttondevteam/discordplugin/ChromaBot.java +++ b/src/main/java/buttondevteam/discordplugin/ChromaBot.java @@ -1,8 +1,7 @@ package buttondevteam.discordplugin; -import buttondevteam.discordplugin.listeners.MCChatListener; +import buttondevteam.discordplugin.mcchat.MCChatUtils; import lombok.Getter; -import org.bukkit.Bukkit; import org.bukkit.entity.Player; import org.bukkit.scheduler.BukkitScheduler; import sx.blah.discord.api.internal.json.objects.EmbedObject; @@ -11,8 +10,6 @@ import sx.blah.discord.util.EmbedBuilder; import javax.annotation.Nullable; import java.awt.*; -import java.util.Arrays; -import java.util.stream.Collectors; public class ChromaBot { /** @@ -43,7 +40,7 @@ public class ChromaBot { * The message to send, duh */ public void sendMessage(String message) { - MCChatListener.forAllMCChat(ch -> DiscordPlugin.sendMessageToChannel(ch, message)); + MCChatUtils.forAllMCChat(ch -> DiscordPlugin.sendMessageToChannel(ch, message)); } /** @@ -55,7 +52,7 @@ public class ChromaBot { * Custom fancy stuff, use {@link EmbedBuilder} to create one */ public void sendMessage(String message, EmbedObject embed) { - MCChatListener.forAllMCChat(ch -> DiscordPlugin.sendMessageToChannel(ch, message, embed)); + MCChatUtils.forAllMCChat(ch -> DiscordPlugin.sendMessageToChannel(ch, message, embed)); } /** @@ -66,7 +63,7 @@ public class ChromaBot { * @param toggle The toggle type for channelcon */ public void sendMessageCustomAsWell(String message, EmbedObject embed, @Nullable ChannelconBroadcast toggle) { - MCChatListener.forCustomAndAllMCChat(ch -> DiscordPlugin.sendMessageToChannel(ch, message, embed), toggle, false); + MCChatUtils.forCustomAndAllMCChat(ch -> DiscordPlugin.sendMessageToChannel(ch, message, embed), toggle, false); } /** @@ -92,7 +89,7 @@ public class ChromaBot { * The color of the line before the text */ public void sendMessage(String message, Color color) { - MCChatListener.forAllMCChat(ch -> DiscordPlugin.sendMessageToChannel(ch, message, + MCChatUtils.forAllMCChat(ch -> DiscordPlugin.sendMessageToChannel(ch, message, new EmbedBuilder().withTitle(message).withColor(color).build())); } @@ -107,7 +104,7 @@ public class ChromaBot { * The name of the Minecraft player who is the author of this message */ public void sendMessage(String message, Color color, String mcauthor) { - MCChatListener.forAllMCChat(ch -> DiscordPlugin.sendMessageToChannel(ch, message, + MCChatUtils.forAllMCChat(ch -> DiscordPlugin.sendMessageToChannel(ch, message, DPUtils.embedWithHead(new EmbedBuilder().withTitle(message).withColor(color), mcauthor).build())); } @@ -124,7 +121,7 @@ public class ChromaBot { * The URL of the avatar image for this message's author */ public void sendMessage(String message, Color color, String authorname, String authorimg) { - MCChatListener.forAllMCChat(ch -> DiscordPlugin.sendMessageToChannel(ch, message, new EmbedBuilder() + MCChatUtils.forAllMCChat(ch -> DiscordPlugin.sendMessageToChannel(ch, message, new EmbedBuilder() .withTitle(message).withColor(color).withAuthorName(authorname).withAuthorIcon(authorimg).build())); } @@ -139,20 +136,11 @@ public class ChromaBot { * The player who sends this message */ public void sendMessage(String message, Color color, Player sender) { - MCChatListener.forAllMCChat(ch -> DiscordPlugin.sendMessageToChannel(ch, message, DPUtils + MCChatUtils.forAllMCChat(ch -> DiscordPlugin.sendMessageToChannel(ch, message, DPUtils .embedWithHead(new EmbedBuilder().withTitle(message).withColor(color), sender.getName()).build())); } public void updatePlayerList() { - DPUtils.performNoWait(() -> { - String[] s = DiscordPlugin.chatchannel.getTopic().split("\\n----\\n"); - if (s.length < 3) - return; - s[0] = Bukkit.getOnlinePlayers().size() + " player" + (Bukkit.getOnlinePlayers().size() != 1 ? "s" : "") - + " online"; - s[s.length - 1] = "Players: " + Bukkit.getOnlinePlayers().stream() - .map(p -> DPUtils.sanitizeString(p.getDisplayName())).collect(Collectors.joining(", ")); - DiscordPlugin.chatchannel.changeTopic(Arrays.stream(s).collect(Collectors.joining("\n----\n"))); - }); + MCChatUtils.updatePlayerList(); } } diff --git a/src/main/java/buttondevteam/discordplugin/DPUtils.java b/src/main/java/buttondevteam/discordplugin/DPUtils.java index ea4d14d..f68053a 100755 --- a/src/main/java/buttondevteam/discordplugin/DPUtils.java +++ b/src/main/java/buttondevteam/discordplugin/DPUtils.java @@ -9,6 +9,7 @@ import sx.blah.discord.util.RequestBuffer.IVoidRequest; import javax.annotation.Nullable; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import java.util.logging.Logger; import java.util.regex.Matcher; public final class DPUtils { @@ -96,4 +97,10 @@ public final class DPUtils { return message.replaceAll("([*_~])", Matcher.quoteReplacement("\\")+"$1"); } + public static Logger getLogger() { + if (DiscordPlugin.plugin == null || DiscordPlugin.plugin.getLogger() == null) + return Logger.getLogger("DiscordPlugin"); + return DiscordPlugin.plugin.getLogger(); + } + } diff --git a/src/main/java/buttondevteam/discordplugin/DiscordPlayer.java b/src/main/java/buttondevteam/discordplugin/DiscordPlayer.java index ec7863c..0055792 100755 --- a/src/main/java/buttondevteam/discordplugin/DiscordPlayer.java +++ b/src/main/java/buttondevteam/discordplugin/DiscordPlayer.java @@ -1,6 +1,6 @@ package buttondevteam.discordplugin; -import buttondevteam.discordplugin.listeners.MCChatListener; +import buttondevteam.discordplugin.mcchat.MCChatPrivate; import buttondevteam.lib.player.ChromaGamerBase; import buttondevteam.lib.player.UserClass; @@ -20,9 +20,9 @@ public class DiscordPlayer extends ChromaGamerBase { /** * Returns true if player has the private Minecraft chat enabled. For setting the value, see - * {@link MCChatListener#privateMCChat(sx.blah.discord.handle.obj.IChannel, boolean, sx.blah.discord.handle.obj.IUser, DiscordPlayer)} + * {@link MCChatPrivate#privateMCChat(sx.blah.discord.handle.obj.IChannel, boolean, sx.blah.discord.handle.obj.IUser, DiscordPlayer)} */ public boolean isMinecraftChatEnabled() { - return MCChatListener.isMinecraftChatEnabled(this); + return MCChatPrivate.isMinecraftChatEnabled(this); } } diff --git a/src/main/java/buttondevteam/discordplugin/DiscordPlugin.java b/src/main/java/buttondevteam/discordplugin/DiscordPlugin.java index be7e8a9..9870d99 100755 --- a/src/main/java/buttondevteam/discordplugin/DiscordPlugin.java +++ b/src/main/java/buttondevteam/discordplugin/DiscordPlugin.java @@ -1,13 +1,17 @@ package buttondevteam.discordplugin; +import buttondevteam.discordplugin.broadcaster.GeneralEventBroadcasterModule; import buttondevteam.discordplugin.commands.DiscordCommandBase; -import buttondevteam.discordplugin.listeners.CommandListener; -import buttondevteam.discordplugin.listeners.ExceptionListener; -import buttondevteam.discordplugin.listeners.MCChatListener; +import buttondevteam.discordplugin.exceptions.ExceptionListenerModule; +import buttondevteam.discordplugin.listeners.CommonListeners; import buttondevteam.discordplugin.listeners.MCListener; +import buttondevteam.discordplugin.mcchat.*; import buttondevteam.discordplugin.mccommands.DiscordMCCommandBase; import buttondevteam.discordplugin.mccommands.ResetMCCommand; import buttondevteam.lib.TBMCCoreAPI; +import buttondevteam.lib.architecture.ButtonPlugin; +import buttondevteam.lib.architecture.Component; +import buttondevteam.lib.architecture.ConfigData; import buttondevteam.lib.chat.Channel; import buttondevteam.lib.chat.TBMCChatAPI; import buttondevteam.lib.player.ChromaGamerBase; @@ -20,9 +24,7 @@ import lombok.val; import net.milkbowl.vault.permission.Permission; import org.bukkit.Bukkit; import org.bukkit.entity.Player; -import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.plugin.RegisteredServiceProvider; -import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.scheduler.BukkitTask; import sx.blah.discord.api.ClientBuilder; import sx.blah.discord.api.IDiscordClient; @@ -44,29 +46,28 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.stream.Collectors; -public class DiscordPlugin extends JavaPlugin implements IListener { +public class DiscordPlugin extends ButtonPlugin implements IListener { private static final String SubredditURL = "https://www.reddit.com/r/ChromaGamers"; private static boolean stop = false; public static IDiscordClient dc; public static DiscordPlugin plugin; public static boolean SafeMode = true; public static List GameRoles; - public static boolean hooked = false; - @SuppressWarnings("unchecked") + public ConfigData Prefix() { + return getData("prefix", '/'); + } + + public static char getPrefix() { + if (plugin == null) return '/'; + return plugin.Prefix().get(); + } + @Override - public void onEnable() { + public void pluginEnable() { stop = false; //If not the first time try { Bukkit.getLogger().info("Initializing DiscordPlugin..."); - try { - PlayerListWatcher.hookUp(); - hooked = true; - Bukkit.getLogger().info("Finished hooking into the player list"); - } catch (Throwable e) { - e.printStackTrace(); - Bukkit.getLogger().warning("Couldn't hook into the player list!"); - } plugin = this; lastannouncementtime = getConfig().getLong("lastannouncementtime"); lastseentime = getConfig().getLong("lastseentime"); @@ -156,7 +157,7 @@ public class DiscordPlugin extends JavaPlugin implements IListener { val toggles = chcon.getInt("toggles"); if (!mcch.isPresent() || ch == null || user == null || groupid == null) continue; - MCChatListener.addCustomChat(ch, groupid, mcch.get(), user, dcp, toggles); + MCChatCustom.addCustomChat(ch, groupid, mcch.get(), user, dcp, toggles); } } @@ -212,21 +213,16 @@ public class DiscordPlugin extends JavaPlugin implements IListener { }*/ } }, 0, 10); - for (IListener listener : CommandListener.getListeners()) + for (IListener listener : CommonListeners.getListeners()) dc.getDispatcher().registerListener(listener); - MCChatListener mcchat = new MCChatListener(); - dc.getDispatcher().registerListener(mcchat); - TBMCCoreAPI.RegisterEventsForExceptions(mcchat, this); - Bukkit.getPluginManager().registerEvents(new ExceptionListener(), this); + Component.registerComponent(this, new GeneralEventBroadcasterModule()); + Component.registerComponent(this, new MinecraftChatModule()); + Component.registerComponent(this, new ExceptionListenerModule()); TBMCCoreAPI.RegisterEventsForExceptions(new MCListener(), this); TBMCChatAPI.AddCommands(this, DiscordMCCommandBase.class); TBMCCoreAPI.RegisterUserClass(DiscordPlayer.class); - ChromaGamerBase.addConverter(sender -> { - //System.out.println("Discord converter queried: "+sender+" "+sender.getName()); - TODO: Remove - //System.out.println(((DiscordSenderBase) sender).getChromaUser().channel().get().ID); //TODO: TMP - return Optional.ofNullable(sender instanceof DiscordSenderBase - ? ((DiscordSenderBase) sender).getChromaUser() : null); - }); + ChromaGamerBase.addConverter(sender -> Optional.ofNullable(sender instanceof DiscordSenderBase + ? ((DiscordSenderBase) sender).getChromaUser() : null)); new Thread(this::AnnouncementGetterThreadMethod).start(); setupProviders(); } catch (Exception e) { @@ -248,17 +244,14 @@ public class DiscordPlugin extends JavaPlugin implements IListener { public static boolean Restart; @Override - public void onDisable() { + public void pluginDisable() { stop = true; - for (val entry : MCChatListener.ConnectedSenders.entrySet()) - for (val valueEntry : entry.getValue().entrySet()) - MCListener.callEventExcludingSome(new PlayerQuitEvent(valueEntry.getValue(), "")); - MCChatListener.ConnectedSenders.clear(); + MCChatPrivate.logoutAll(); getConfig().set("lastannouncementtime", lastannouncementtime); getConfig().set("lastseentime", lastseentime); getConfig().set("serverup", false); - val chcons = MCChatListener.getCustomChats(); + val chcons = MCChatCustom.getCustomChats(); val chconsc = getConfig().createSection("chcons"); for (val chcon : chcons) { val chconc = chconsc.createSection(chcon.channel.getStringID()); @@ -287,7 +280,7 @@ public class DiscordPlugin extends JavaPlugin implements IListener { + "kicked the hell out.") //TODO: Make configurable : "") //If 'restart' is disabled then this isn't shown even if joinleave is enabled .build(); - MCChatListener.forCustomAndAllMCChat(ch -> { + MCChatUtils.forCustomAndAllMCChat(ch -> { try { DiscordPlugin.sendMessageToChannelWait(ch, "", embed, 5, TimeUnit.SECONDS); @@ -299,16 +292,6 @@ public class DiscordPlugin extends JavaPlugin implements IListener { try { SafeMode = true; // Stop interacting with Discord MCChatListener.stop(true); - try { - if (PlayerListWatcher.hookDown()) - System.out.println("Finished unhooking the player list!"); - else - System.out.println("Didn't have the player list hooked."); - hooked = false; - } catch (Throwable e) { - e.printStackTrace(); - Bukkit.getLogger().warning("Couldn't unhook the player list!"); - } ChromaBot.delete(); dc.changePresence(StatusType.IDLE, ActivityType.PLAYING, "Chromacraft"); //No longer using the same account for testing dc.logout(); @@ -424,7 +407,7 @@ public class DiscordPlugin extends JavaPlugin implements IListener { .warning("Message was too long to send to discord and got truncated. In " + channel.getName()); } try { - MCChatListener.resetLastMessage(channel); // If this is a chat message, it'll be set again + MCChatUtils.resetLastMessage(channel); // If this is a chat message, it'll be set again final String content = message; RequestBuffer.IRequest r = () -> embed == null ? channel.sendMessage(content) : channel.sendMessage(content, embed, false); diff --git a/src/main/java/buttondevteam/discordplugin/DiscordSenderBase.java b/src/main/java/buttondevteam/discordplugin/DiscordSenderBase.java index 14c7fc4..b031bcb 100755 --- a/src/main/java/buttondevteam/discordplugin/DiscordSenderBase.java +++ b/src/main/java/buttondevteam/discordplugin/DiscordSenderBase.java @@ -1,13 +1,13 @@ package buttondevteam.discordplugin; import buttondevteam.lib.TBMCCoreAPI; -import buttondevteam.lib.chat.IDiscordSender; import org.bukkit.Bukkit; +import org.bukkit.command.CommandSender; import org.bukkit.scheduler.BukkitTask; import sx.blah.discord.handle.obj.IChannel; import sx.blah.discord.handle.obj.IUser; -public abstract class DiscordSenderBase implements IDiscordSender { +public abstract class DiscordSenderBase implements CommandSender { /** * May be null. */ @@ -51,6 +51,7 @@ public abstract class DiscordSenderBase implements IDiscordSender { public void sendMessage(String message) { try { final boolean broadcast = new Exception().getStackTrace()[2].getMethodName().contains("broadcast"); + //if (broadcast && DiscordPlugin.hooked) - TODO: What should happen if unhooked if (broadcast) return; final String sendmsg = DPUtils.sanitizeString(message); diff --git a/src/main/java/buttondevteam/discordplugin/broadcaster/GeneralEventBroadcasterModule.java b/src/main/java/buttondevteam/discordplugin/broadcaster/GeneralEventBroadcasterModule.java new file mode 100644 index 0000000..8830f37 --- /dev/null +++ b/src/main/java/buttondevteam/discordplugin/broadcaster/GeneralEventBroadcasterModule.java @@ -0,0 +1,35 @@ +package buttondevteam.discordplugin.broadcaster; + +import buttondevteam.discordplugin.DPUtils; +import buttondevteam.lib.TBMCCoreAPI; +import buttondevteam.lib.architecture.Component; +import lombok.Getter; +import org.bukkit.Bukkit; + +public class GeneralEventBroadcasterModule extends Component { + private static @Getter boolean hooked = false; + + @Override + protected void enable() { + try { + PlayerListWatcher.hookUp(); + Bukkit.getLogger().info("Finished hooking into the player list"); + hooked = true; + } catch (Exception e) { + TBMCCoreAPI.SendException("Error while hacking the player list!", e); + } + } + + @Override + protected void disable() { + try { + if (PlayerListWatcher.hookDown()) + DPUtils.getLogger().info("Finished unhooking the player list!"); + else + DPUtils.getLogger().info("Didn't have the player list hooked."); + hooked = false; + } catch (Exception e) { + TBMCCoreAPI.SendException("Error while hacking the player list!", e); + } + } +} diff --git a/src/main/java/buttondevteam/discordplugin/PlayerListWatcher.java b/src/main/java/buttondevteam/discordplugin/broadcaster/PlayerListWatcher.java similarity index 81% rename from src/main/java/buttondevteam/discordplugin/PlayerListWatcher.java rename to src/main/java/buttondevteam/discordplugin/broadcaster/PlayerListWatcher.java index 432b75c..eafe7f5 100755 --- a/src/main/java/buttondevteam/discordplugin/PlayerListWatcher.java +++ b/src/main/java/buttondevteam/discordplugin/broadcaster/PlayerListWatcher.java @@ -1,6 +1,6 @@ -package buttondevteam.discordplugin; +package buttondevteam.discordplugin.broadcaster; -import buttondevteam.discordplugin.listeners.MCChatListener; +import buttondevteam.discordplugin.mcchat.MCChatUtils; import buttondevteam.lib.TBMCCoreAPI; import com.mojang.authlib.GameProfile; import lombok.val; @@ -30,7 +30,7 @@ public class PlayerListWatcher extends DedicatedPlayerList { if (packet instanceof PacketPlayOutChat) { Field msgf = PacketPlayOutChat.class.getDeclaredField("a"); msgf.setAccessible(true); - MCChatListener.forAllMCChat(MCChatListener.send(((IChatBaseComponent) msgf.get(packet)).toPlainText())); + MCChatUtils.forAllMCChat(MCChatUtils.send(((IChatBaseComponent) msgf.get(packet)).toPlainText())); } } catch (Exception e) { TBMCCoreAPI.SendException("Failed to broadcast message sent to all players - hacking failed.", e); @@ -53,52 +53,43 @@ public class PlayerListWatcher extends DedicatedPlayerList { } @Override - public void sendMessage(IChatBaseComponent[] iChatBaseComponents) { // Needed so it calls the overriden method + public void sendMessage(IChatBaseComponent[] iChatBaseComponents) { // Needed so it calls the overridden method for (IChatBaseComponent component : iChatBaseComponents) { sendMessage(component, true); } } - public static void hookUp() { - try { - Field conf = CraftServer.class.getDeclaredField("console"); - conf.setAccessible(true); - val server = (MinecraftServer) conf.get(Bukkit.getServer()); - val plw = new ObjenesisStd().newInstance(PlayerListWatcher.class); // Cannot call super constructor - plw.plist = (DedicatedPlayerList) server.getPlayerList(); - plw.maxPlayers = plw.plist.getMaxPlayers(); - Field plf = plw.getClass().getField("players"); - plf.setAccessible(true); - Field modf = plf.getClass().getDeclaredField("modifiers"); - modf.setAccessible(true); - modf.set(plf, plf.getModifiers() & ~Modifier.FINAL); - plf.set(plw, plw.plist.players); - server.a(plw); - Field pllf = CraftServer.class.getDeclaredField("playerList"); - pllf.setAccessible(true); - pllf.set(Bukkit.getServer(), plw); - } catch (Exception e) { - TBMCCoreAPI.SendException("Error while hacking the player list!", e); - } + static void hookUp() throws Exception { + Field conf = CraftServer.class.getDeclaredField("console"); + conf.setAccessible(true); + val server = (MinecraftServer) conf.get(Bukkit.getServer()); + val plw = new ObjenesisStd().newInstance(PlayerListWatcher.class); // Cannot call super constructor + plw.plist = (DedicatedPlayerList) server.getPlayerList(); + plw.maxPlayers = plw.plist.getMaxPlayers(); + Field plf = plw.getClass().getField("players"); + plf.setAccessible(true); + Field modf = plf.getClass().getDeclaredField("modifiers"); + modf.setAccessible(true); + modf.set(plf, plf.getModifiers() & ~Modifier.FINAL); + plf.set(plw, plw.plist.players); + server.a(plw); + Field pllf = CraftServer.class.getDeclaredField("playerList"); + pllf.setAccessible(true); + pllf.set(Bukkit.getServer(), plw); } - public static boolean hookDown() { - try { - Field conf = CraftServer.class.getDeclaredField("console"); - conf.setAccessible(true); - val server = (MinecraftServer) conf.get(Bukkit.getServer()); - val plist = (DedicatedPlayerList) server.getPlayerList(); - if (!(plist instanceof PlayerListWatcher)) - return false; - server.a(((PlayerListWatcher) plist).plist); - Field pllf = CraftServer.class.getDeclaredField("playerList"); - pllf.setAccessible(true); - pllf.set(Bukkit.getServer(), ((PlayerListWatcher) plist).plist); - return true; - } catch (Exception e) { - TBMCCoreAPI.SendException("Error while hacking the player list!", e); - return true; - } + static boolean hookDown() throws Exception { + Field conf = CraftServer.class.getDeclaredField("console"); + conf.setAccessible(true); + val server = (MinecraftServer) conf.get(Bukkit.getServer()); + val plist = (DedicatedPlayerList) server.getPlayerList(); + if (!(plist instanceof PlayerListWatcher)) + return false; + server.a(((PlayerListWatcher) plist).plist); + Field pllf = CraftServer.class.getDeclaredField("playerList"); + pllf.setAccessible(true); + pllf.set(Bukkit.getServer(), ((PlayerListWatcher) plist).plist); + return true; } public void a(EntityHuman entityhuman, IChatBaseComponent ichatbasecomponent) { diff --git a/src/main/java/buttondevteam/discordplugin/commands/ChannelconCommand.java b/src/main/java/buttondevteam/discordplugin/commands/ChannelconCommand.java index 73eefd1..c89baab 100644 --- a/src/main/java/buttondevteam/discordplugin/commands/ChannelconCommand.java +++ b/src/main/java/buttondevteam/discordplugin/commands/ChannelconCommand.java @@ -3,7 +3,8 @@ package buttondevteam.discordplugin.commands; import buttondevteam.discordplugin.ChannelconBroadcast; import buttondevteam.discordplugin.DiscordConnectedPlayer; import buttondevteam.discordplugin.DiscordPlayer; -import buttondevteam.discordplugin.listeners.MCChatListener; +import buttondevteam.discordplugin.DiscordPlugin; +import buttondevteam.discordplugin.mcchat.MCChatCustom; import buttondevteam.lib.chat.Channel; import buttondevteam.lib.player.TBMCPlayer; import lombok.val; @@ -30,16 +31,16 @@ public class ChannelconCommand extends DiscordCommandBase { message.reply("you need to have manage permissions for this channel!"); return true; } - if (MCChatListener.hasCustomChat(message.getChannel())) { + if (MCChatCustom.hasCustomChat(message.getChannel())) { if (args.toLowerCase().startsWith("remove")) { - if (MCChatListener.removeCustomChat(message.getChannel())) + if (MCChatCustom.removeCustomChat(message.getChannel())) message.reply("channel connection removed."); else message.reply("wait what, couldn't remove channel connection."); return true; } if (args.toLowerCase().startsWith("toggle")) { - val cc = MCChatListener.getCustomChat(message.getChannel()); + val cc = MCChatCustom.getCustomChat(message.getChannel()); Supplier togglesString = () -> Arrays.stream(ChannelconBroadcast.values()).map(t -> t.toString().toLowerCase() + ": " + ((cc.toggles & t.flag) == 0 ? "disabled" : "enabled")).collect(Collectors.joining("\n")); String[] argsa = args.split(" "); if (argsa.length < 2) { @@ -88,7 +89,7 @@ public class ChannelconCommand extends DiscordCommandBase { message.reply("sorry, this MC chat is already connected to a different channel, multiple channels are not supported atm."); return true; }*/ //TODO: "Channel admins" that can connect channels? - MCChatListener.addCustomChat(message.getChannel(), groupid, chan.get(), message.getAuthor(), dcp, 0); + MCChatCustom.addCustomChat(message.getChannel(), groupid, chan.get(), message.getAuthor(), dcp, 0); message.reply("alright, connection made to group `" + groupid + "`!"); return true; } @@ -99,10 +100,12 @@ public class ChannelconCommand extends DiscordCommandBase { "---- Channel connect ---", // "This command allows you to connect a Minecraft channel to a Discord channel (just like how the global chat is connected to #minecraft-chat).", // "You need to have access to the MC channel and have manage permissions on the Discord channel.", // - "You also need to have your Minecraft account connected. In #bot use /connect .", // - "Call this command from the channel you want to use. Usage: @ChromaBot channelcon ", // + "You also need to have your Minecraft account connected. In #bot use " + DiscordPlugin.getPrefix() + "connect .", // + "Call this command from the channel you want to use.", // + "Usage: @" + DiscordPlugin.dc.getOurUser().getName() + " channelcon ", // + "Use the ID (command) of the channel, for example `g` for the global chat.", // "To remove a connection use @ChromaBot channelcon remove in the channel.", // - "Mentioning the bot is needed in this case because the / prefix only works in #bot.", // + "Mentioning the bot is needed in this case because the " + DiscordPlugin.getPrefix() + " prefix only works in #bot.", // "Invite link: " // }; } diff --git a/src/main/java/buttondevteam/discordplugin/commands/ConnectCommand.java b/src/main/java/buttondevteam/discordplugin/commands/ConnectCommand.java index a339c4d..6e16c09 100755 --- a/src/main/java/buttondevteam/discordplugin/commands/ConnectCommand.java +++ b/src/main/java/buttondevteam/discordplugin/commands/ConnectCommand.java @@ -30,7 +30,7 @@ public class ConnectCommand extends DiscordCommandBase { return false; if (args.contains(" ")) { DiscordPlugin.sendMessageToChannel(message.getChannel(), - "Too many arguments.\nUsage: connect "); + "Too many arguments.\nUsage: " + DiscordPlugin.getPrefix() + "connect "); return true; } if (WaitingToConnect.inverse().containsKey(message.getAuthor().getStringID())) { @@ -68,8 +68,8 @@ public class ConnectCommand extends DiscordCommandBase { public String[] getHelpText() { return new String[] { // "---- Connect command ----", // - "This commands let's you connect your acoount with a Minecraft account. This'd allow using the Minecraft chat and other things.", // - "Usage: connect " // + "This command lets you connect your account with a Minecraft account. This allows using the Minecraft chat and other things.", // + "Usage: /connect " // }; } diff --git a/src/main/java/buttondevteam/discordplugin/commands/DebugCommand.java b/src/main/java/buttondevteam/discordplugin/commands/DebugCommand.java index 1b6a605..b37e2b8 100644 --- a/src/main/java/buttondevteam/discordplugin/commands/DebugCommand.java +++ b/src/main/java/buttondevteam/discordplugin/commands/DebugCommand.java @@ -1,7 +1,7 @@ package buttondevteam.discordplugin.commands; import buttondevteam.discordplugin.DiscordPlugin; -import buttondevteam.discordplugin.listeners.CommandListener; +import buttondevteam.discordplugin.listeners.CommonListeners; import sx.blah.discord.handle.obj.IMessage; public class DebugCommand extends DiscordCommandBase { @@ -13,7 +13,7 @@ public class DebugCommand extends DiscordCommandBase { @Override public boolean run(IMessage message, String args) { if (message.getAuthor().hasRole(DiscordPlugin.mainServer.getRoleByID(126030201472811008L))) - message.reply("Debug " + (CommandListener.debug() ? "enabled" : "disabled")); + message.reply("Debug " + (CommonListeners.debug() ? "enabled" : "disabled")); else message.reply("You need to be a moderator to use this command."); return true; diff --git a/src/main/java/buttondevteam/discordplugin/commands/DiscordCommandBase.java b/src/main/java/buttondevteam/discordplugin/commands/DiscordCommandBase.java index 34790b6..45f175f 100755 --- a/src/main/java/buttondevteam/discordplugin/commands/DiscordCommandBase.java +++ b/src/main/java/buttondevteam/discordplugin/commands/DiscordCommandBase.java @@ -1,6 +1,7 @@ package buttondevteam.discordplugin.commands; import buttondevteam.discordplugin.DiscordPlugin; +import buttondevteam.discordplugin.mcchat.MCChatCommand; import buttondevteam.lib.TBMCCoreAPI; import sx.blah.discord.handle.obj.IMessage; @@ -8,7 +9,7 @@ import java.util.Arrays; import java.util.HashMap; import java.util.stream.Collectors; -import static buttondevteam.discordplugin.listeners.CommandListener.debug; +import static buttondevteam.discordplugin.listeners.CommonListeners.debug; public abstract class DiscordCommandBase { public abstract String getCommandName(); diff --git a/src/main/java/buttondevteam/discordplugin/commands/HelpCommand.java b/src/main/java/buttondevteam/discordplugin/commands/HelpCommand.java index 29aff28..bcef6ea 100755 --- a/src/main/java/buttondevteam/discordplugin/commands/HelpCommand.java +++ b/src/main/java/buttondevteam/discordplugin/commands/HelpCommand.java @@ -3,7 +3,6 @@ package buttondevteam.discordplugin.commands; import buttondevteam.discordplugin.DiscordPlugin; import sx.blah.discord.handle.obj.IMessage; -import java.util.Arrays; import java.util.stream.Collectors; public class HelpCommand extends DiscordCommandBase { @@ -19,11 +18,11 @@ public class HelpCommand extends DiscordCommandBase { if (args.length() == 0) DiscordPlugin.sendMessageToChannel(message.getChannel(), "Available commands:\n" + DiscordCommandBase.commands.values().stream() - .map(dc -> dc.getCommandName()).collect(Collectors.joining("\n"))); + .map(dc -> DiscordPlugin.getPrefix() + dc.getCommandName()).collect(Collectors.joining("\n"))); else DiscordPlugin.sendMessageToChannel(message.getChannel(), (argdc = DiscordCommandBase.commands.get(args)) == null ? "Command not found: " + args - : Arrays.stream(argdc.getHelpText()).collect(Collectors.joining("\n"))); + : String.join("\n", argdc.getHelpText())); return true; } @@ -32,7 +31,7 @@ public class HelpCommand extends DiscordCommandBase { return new String[] { // "---- Help command ----", // "Shows some info about a command or lists the available commands.", // - "Usage: help [command]"// + "Usage: " + DiscordPlugin.getPrefix() + "help [command]"// }; } diff --git a/src/main/java/buttondevteam/discordplugin/commands/RoleCommand.java b/src/main/java/buttondevteam/discordplugin/commands/RoleCommand.java index 3978c6a..a7f2bf5 100755 --- a/src/main/java/buttondevteam/discordplugin/commands/RoleCommand.java +++ b/src/main/java/buttondevteam/discordplugin/commands/RoleCommand.java @@ -86,7 +86,7 @@ public class RoleCommand extends DiscordCommandBase { public String[] getHelpText() { return new String[]{ // "Add or remove game roles from yourself.", // - "Usage: role add|remove or role list", // + "Usage: " + DiscordPlugin.getPrefix() + "role add|remove or role list", // }; } diff --git a/src/main/java/buttondevteam/discordplugin/commands/UserinfoCommand.java b/src/main/java/buttondevteam/discordplugin/commands/UserinfoCommand.java index b0608ed..b1dbf8b 100755 --- a/src/main/java/buttondevteam/discordplugin/commands/UserinfoCommand.java +++ b/src/main/java/buttondevteam/discordplugin/commands/UserinfoCommand.java @@ -91,7 +91,8 @@ public class UserinfoCommand extends DiscordCommandBase { "---- User information ----", // "Shows some information about users, from Discord, from Minecraft or from Reddit if they have these accounts connected.", // "If used without args, shows your info.", // - "Usage: userinfo [username/nickname[#tag]/ping]\nExamples:\nuserinfo ChromaBot\nuserinfo ChromaBot#6338\nuserinfo @ChromaBot#6338" // + "Usage: " + DiscordPlugin.getPrefix() + "userinfo [username/nickname[#tag]/ping]", // + "Examples:\n" + DiscordPlugin.getPrefix() + "userinfo ChromaBot\n" + DiscordPlugin.getPrefix() + "userinfo ChromaBot#6338\n" + DiscordPlugin.getPrefix() + "userinfo @ChromaBot#6338" // }; } diff --git a/src/main/java/buttondevteam/discordplugin/listeners/DebugMessageListener.java b/src/main/java/buttondevteam/discordplugin/exceptions/DebugMessageListener.java similarity index 91% rename from src/main/java/buttondevteam/discordplugin/listeners/DebugMessageListener.java rename to src/main/java/buttondevteam/discordplugin/exceptions/DebugMessageListener.java index 1e163c4..cca3e98 100755 --- a/src/main/java/buttondevteam/discordplugin/listeners/DebugMessageListener.java +++ b/src/main/java/buttondevteam/discordplugin/exceptions/DebugMessageListener.java @@ -1,4 +1,4 @@ -package buttondevteam.discordplugin.listeners; +package buttondevteam.discordplugin.exceptions; import buttondevteam.discordplugin.DiscordPlugin; import buttondevteam.lib.TBMCDebugMessageEvent; diff --git a/src/main/java/buttondevteam/discordplugin/listeners/ExceptionListener.java b/src/main/java/buttondevteam/discordplugin/exceptions/ExceptionListenerModule.java similarity index 79% rename from src/main/java/buttondevteam/discordplugin/listeners/ExceptionListener.java rename to src/main/java/buttondevteam/discordplugin/exceptions/ExceptionListenerModule.java index 549bb97..f491a24 100755 --- a/src/main/java/buttondevteam/discordplugin/listeners/ExceptionListener.java +++ b/src/main/java/buttondevteam/discordplugin/exceptions/ExceptionListenerModule.java @@ -1,9 +1,12 @@ -package buttondevteam.discordplugin.listeners; +package buttondevteam.discordplugin.exceptions; +import buttondevteam.core.ComponentManager; import buttondevteam.discordplugin.DiscordPlugin; import buttondevteam.lib.TBMCCoreAPI; import buttondevteam.lib.TBMCExceptionEvent; +import buttondevteam.lib.architecture.Component; import org.apache.commons.lang.exception.ExceptionUtils; +import org.bukkit.Bukkit; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import sx.blah.discord.handle.obj.IRole; @@ -13,13 +16,13 @@ import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; -public class ExceptionListener implements Listener { +public class ExceptionListenerModule extends Component implements Listener { private List lastthrown = new ArrayList<>(); private List lastsourcemsg = new ArrayList<>(); @EventHandler public void onException(TBMCExceptionEvent e) { - if (DiscordPlugin.SafeMode) + if (DiscordPlugin.SafeMode || !ComponentManager.isEnabled(getClass())) return; if (lastthrown.stream() .anyMatch(ex -> Arrays.equals(e.getException().getStackTrace(), ex.getStackTrace()) @@ -59,4 +62,15 @@ public class ExceptionListener implements Listener { ex.printStackTrace(); } } + + @Override + protected void enable() { + Bukkit.getPluginManager().registerEvents(new ExceptionListenerModule(), getPlugin()); + TBMCCoreAPI.RegisterEventsForExceptions(new DebugMessageListener(), getPlugin()); + } + + @Override + protected void disable() { + + } } diff --git a/src/main/java/buttondevteam/discordplugin/fun/FunModule.java b/src/main/java/buttondevteam/discordplugin/fun/FunModule.java new file mode 100644 index 0000000..5dea2d3 --- /dev/null +++ b/src/main/java/buttondevteam/discordplugin/fun/FunModule.java @@ -0,0 +1,86 @@ +package buttondevteam.discordplugin.fun; + +import buttondevteam.core.ComponentManager; +import buttondevteam.discordplugin.DiscordPlugin; +import buttondevteam.lib.TBMCCoreAPI; +import buttondevteam.lib.architecture.Component; +import buttondevteam.lib.architecture.ConfigData; +import sx.blah.discord.handle.obj.IMessage; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Random; + +public class FunModule extends Component { + private static FunModule mod; + + private static final String[] serverReadyStrings = new String[]{"In one week from now", // Ali + "Between now and the heat-death of the universe.", // Ghostise + "Soon™", "Ask again this time next month", // Ghostise + "In about 3 seconds", // Nicolai + "After we finish 8 plugins", // Ali + "Tomorrow.", // Ali + "After one tiiiny feature", // Ali + "Next commit", // Ali + "After we finish strangling Towny", // Ali + "When we kill every *fucking* bug", // Ali + "Once the server stops screaming.", // Ali + "After HL3 comes out", // Ali + "Next time you ask", // Ali + "When will *you* be open?" // Ali + }; + + private ConfigData serverReady() { + return getData("serverReady", true); + } + + private ConfigData> serverReadyAnswers() { + return getData("serverReadyAnswers", Arrays.asList(serverReadyStrings), + data -> (List) data, data -> data); //TODO: Test + } + + private static final String[] serverReadyQuestions = new String[]{"when will the server be open", + "when will the server be ready", "when will the server be done", "when will the server be complete", + "when will the server be finished", "when's the server ready", "when's the server open", + "Vhen vill ze server be open?"}; + + private static final Random serverReadyRandom = new Random(); + private static final ArrayList usableServerReadyStrings = new ArrayList(serverReadyStrings.length) { + private static final long serialVersionUID = 2213771460909848770L; + + { + createUsableServerReadyStrings(this); + } + }; + + private static void createUsableServerReadyStrings(ArrayList list) { + for (short i = 0; i < serverReadyStrings.length; i++) + list.add(i); + } + + @Override + protected void enable() { + mod = this; + } + + @Override + protected void disable() { + } + + public static boolean executeMemes(IMessage message) { + if (!ComponentManager.isEnabled(FunModule.class)) return false; + if (mod.serverReady().get()) { + if (!TBMCCoreAPI.IsTestServer() + && Arrays.stream(serverReadyQuestions).anyMatch(s -> message.getContent().toLowerCase().contains(s))) { + int next; + if (usableServerReadyStrings.size() == 0) + createUsableServerReadyStrings(usableServerReadyStrings); + next = usableServerReadyStrings.remove(serverReadyRandom.nextInt(usableServerReadyStrings.size())); + DiscordPlugin.sendMessageToChannel(message.getChannel(), serverReadyStrings[next]); + return true; + } + } + return false; + } +} diff --git a/src/main/java/buttondevteam/discordplugin/listeners/CommandListener.java b/src/main/java/buttondevteam/discordplugin/listeners/CommandListener.java old mode 100755 new mode 100644 index f46abff..73e827d --- a/src/main/java/buttondevteam/discordplugin/listeners/CommandListener.java +++ b/src/main/java/buttondevteam/discordplugin/listeners/CommandListener.java @@ -1,227 +1,72 @@ -package buttondevteam.discordplugin.listeners; - -import buttondevteam.discordplugin.DiscordPlugin; -import buttondevteam.discordplugin.commands.DiscordCommandBase; -import buttondevteam.lib.TBMCCoreAPI; -import lombok.val; -import org.bukkit.Bukkit; -import sx.blah.discord.api.events.IListener; -import sx.blah.discord.handle.impl.events.guild.channel.message.MentionEvent; -import sx.blah.discord.handle.impl.events.guild.channel.message.MessageReceivedEvent; -import sx.blah.discord.handle.impl.events.guild.role.RoleCreateEvent; -import sx.blah.discord.handle.impl.events.guild.role.RoleDeleteEvent; -import sx.blah.discord.handle.impl.events.guild.role.RoleUpdateEvent; -import sx.blah.discord.handle.impl.events.user.PresenceUpdateEvent; -import sx.blah.discord.handle.obj.IChannel; -import sx.blah.discord.handle.obj.IMessage; -import sx.blah.discord.handle.obj.StatusType; -import sx.blah.discord.util.EmbedBuilder; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Calendar; -import java.util.Random; -import java.util.concurrent.TimeUnit; - -public class CommandListener { - - private static final String[] serverReadyStrings = new String[]{"In one week from now", // Ali - "Between now and the heat-death of the universe.", // Ghostise - "Soon™", "Ask again this time next month", // Ghostise - "In about 3 seconds", // Nicolai - "After we finish 8 plugins", // Ali - "Tomorrow.", // Ali - "After one tiiiny feature", // Ali - "Next commit", // Ali - "After we finish strangling Towny", // Ali - "When we kill every *fucking* bug", // Ali - "Once the server stops screaming.", // Ali - "After HL3 comes out", // Ali - "Next time you ask", // Ali - "When will *you* be open?" // Ali - }; - - private static final String[] serverReadyQuestions = new String[]{"when will the server be open", - "when will the server be ready", "when will the server be done", "when will the server be complete", - "when will the server be finished", "when's the server ready", "when's the server open", - "Vhen vill ze server be open?"}; - - private static final Random serverReadyRandom = new Random(); - private static final ArrayList usableServerReadyStrings = new ArrayList(serverReadyStrings.length) { - private static final long serialVersionUID = 2213771460909848770L; - - { - createUsableServerReadyStrings(this); - } - }; - - private static void createUsableServerReadyStrings(ArrayList list) { - for (short i = 0; i < serverReadyStrings.length; i++) - list.add(i); - } - - private static long lasttime = 0; - - public static IListener[] getListeners() { - return new IListener[]{new IListener() { - @Override - public void handle(MentionEvent event) { - if (DiscordPlugin.SafeMode) - return; - if (event.getMessage().getAuthor().isBot()) - return; - final IChannel channel = event.getMessage().getChannel(); - if (!channel.getStringID().equals(DiscordPlugin.botchannel.getStringID()) - && (!event.getMessage().getContent().contains("channelcon") || MCChatListener.hasCustomChat(channel))) //Allow channelcon in other servers but avoid double handling when it's enabled - return; - if (channel.getStringID().equals(DiscordPlugin.chatchannel.getStringID())) - return; // The chat code already handles this - Right now while testing botchannel is the same as chatchannel - event.getMessage().getChannel().setTypingStatus(true); // Fun - runCommand(event.getMessage(), true); - } - }, new IListener() { - @Override - public void handle(MessageReceivedEvent event) { - if (DiscordPlugin.SafeMode) - return; - final String msglowercase = event.getMessage().getContent().toLowerCase(); - if (!TBMCCoreAPI.IsTestServer() - && Arrays.stream(serverReadyQuestions).anyMatch(s -> msglowercase.contains(s))) { - int next; - if (usableServerReadyStrings.size() == 0) - createUsableServerReadyStrings(usableServerReadyStrings); - next = usableServerReadyStrings.remove(serverReadyRandom.nextInt(usableServerReadyStrings.size())); - DiscordPlugin.sendMessageToChannel(event.getMessage().getChannel(), serverReadyStrings[next]); - return; - } - if (!event.getMessage().getChannel().isPrivate() - && !(event.getMessage().getContent().startsWith("/") - && event.getChannel().getStringID().equals(DiscordPlugin.botchannel.getStringID()))) // - return; - if (MCChatListener.isMinecraftChatEnabled(event.getAuthor().toString())) - if (!event.getMessage().getContent().equalsIgnoreCase("mcchat")) - return; - if (event.getMessage().getAuthor().isBot()) - return; - runCommand(event.getMessage(), false); - } - }, new IListener() { - @Override - public void handle(PresenceUpdateEvent event) { - if (DiscordPlugin.SafeMode) - return; - val devrole = DiscordPlugin.devServer.getRolesByName("Developer").get(0); - if (event.getOldPresence().getStatus().equals(StatusType.OFFLINE) - && !event.getNewPresence().getStatus().equals(StatusType.OFFLINE) - && event.getUser().getRolesForGuild(DiscordPlugin.devServer).stream() - .anyMatch(r -> r.getLongID() == devrole.getLongID()) - && DiscordPlugin.devServer.getUsersByRole(devrole).stream() - .noneMatch(u -> u.getPresence().getStatus().equals(StatusType.OFFLINE)) - && lasttime + 10 < TimeUnit.NANOSECONDS.toHours(System.nanoTime()) - && Calendar.getInstance().get(Calendar.DAY_OF_MONTH) % 5 == 0) { - DiscordPlugin.sendMessageToChannel(DiscordPlugin.devofficechannel, "Full house!", - new EmbedBuilder() - .withImage( - "https://cdn.discordapp.com/attachments/249295547263877121/249687682618359808/poker-hand-full-house-aces-kings-playing-cards-15553791.png") - .build()); - lasttime = TimeUnit.NANOSECONDS.toHours(System.nanoTime()); - } - } - }, (IListener) event -> { - Bukkit.getScheduler().runTaskLaterAsynchronously(DiscordPlugin.plugin, () -> { - if (event.getRole().isDeleted() || !DiscordPlugin.plugin.isGameRole(event.getRole())) - return; //Deleted or not a game role - DiscordPlugin.GameRoles.add(event.getRole().getName()); - DiscordPlugin.sendMessageToChannel(DiscordPlugin.modlogchannel, "Added " + event.getRole().getName() + " as game role. If you don't want this, change the role's color from the default."); - }, 100); - }, (IListener) event -> { - if (DiscordPlugin.GameRoles.remove(event.getRole().getName())) - DiscordPlugin.sendMessageToChannel(DiscordPlugin.modlogchannel, "Removed " + event.getRole().getName() + " as a game role."); - }, (IListener) event -> { //Role update event - if (!DiscordPlugin.plugin.isGameRole(event.getNewRole())) { - if (DiscordPlugin.GameRoles.remove(event.getOldRole().getName())) - DiscordPlugin.sendMessageToChannel(DiscordPlugin.modlogchannel, "Removed " + event.getOldRole().getName() + " as a game role because it's color changed."); - } else { - if (DiscordPlugin.GameRoles.contains(event.getOldRole().getName()) && event.getOldRole().getName().equals(event.getNewRole().getName())) - return; - boolean removed = DiscordPlugin.GameRoles.remove(event.getOldRole().getName()); //Regardless of whether it was a game role - DiscordPlugin.GameRoles.add(event.getNewRole().getName()); //Add it because it has no color - if (removed) - DiscordPlugin.sendMessageToChannel(DiscordPlugin.modlogchannel, "Changed game role from " + event.getOldRole().getName() + " to " + event.getNewRole().getName() + "."); - else - DiscordPlugin.sendMessageToChannel(DiscordPlugin.modlogchannel, "Added " + event.getNewRole().getName() + " as game role because it has the default color."); - } - }}; - } - - /** - * Runs a ChromaBot command. - * - * @param message The Discord message - * @param mentionedonly Only run the command if ChromaBot is mentioned at the start of the message - * @return Whether it ran the command (always true if mentionedonly is false) - */ - public static boolean runCommand(IMessage message, boolean mentionedonly) { - debug("A"); - if (DiscordPlugin.SafeMode) - return true; - debug("B"); - final StringBuilder cmdwithargs = new StringBuilder(message.getContent()); - final String mention = DiscordPlugin.dc.getOurUser().mention(false); - final String mentionNick = DiscordPlugin.dc.getOurUser().mention(true); - boolean gotmention = checkanddeletemention(cmdwithargs, mention, message); - gotmention = checkanddeletemention(cmdwithargs, mentionNick, message) || gotmention; - for (String mentionRole : (Iterable) message.getRoleMentions().stream().filter(r -> DiscordPlugin.dc.getOurUser().hasRole(r)).map(r -> r.mention())::iterator) - gotmention = checkanddeletemention(cmdwithargs, mentionRole, message) || gotmention; // Delete all mentions - debug("C"); - if (mentionedonly && !gotmention) { - message.getChannel().setTypingStatus(false); - return false; - } - debug("D"); - message.getChannel().setTypingStatus(true); - String cmdwithargsString = cmdwithargs.toString().trim(); //Remove spaces between mention and command - int index = cmdwithargsString.indexOf(" "); - String cmd; - String args; - if (index == -1) { - cmd = cmdwithargsString; - args = ""; - } else { - cmd = cmdwithargsString.substring(0, index); - args = cmdwithargsString.substring(index + 1).trim(); //In case there are multiple spaces - } - debug("E"); - DiscordCommandBase.runCommand(cmd.toLowerCase(), args, message); - message.getChannel().setTypingStatus(false); - return true; - } - - private static boolean debug = false; - - public static void debug(String debug) { - if (CommandListener.debug) //Debug - System.out.println(debug); - } - - public static boolean debug() { - return debug = !debug; - } - - private static boolean checkanddeletemention(StringBuilder cmdwithargs, String mention, IMessage message) { - if (message.getContent().startsWith(mention)) // TODO: Resolve mentions: Compound arguments, either a mention or text - if (cmdwithargs.length() > mention.length() + 1) - cmdwithargs.delete(0, - cmdwithargs.charAt(mention.length()) == ' ' ? mention.length() + 1 : mention.length()); - else - cmdwithargs.replace(0, cmdwithargs.length(), "help"); - else { - if (cmdwithargs.length() > 0 && cmdwithargs.charAt(0) == '/') - cmdwithargs.deleteCharAt(0); //Don't treat / as mention, mentions can be used in public mcchat - return false; - } - if (cmdwithargs.length() == 0) - cmdwithargs.replace(0, cmdwithargs.length(), "help"); - return true; - } -} +package buttondevteam.discordplugin.listeners; + +import buttondevteam.discordplugin.DiscordPlugin; +import buttondevteam.discordplugin.commands.DiscordCommandBase; +import sx.blah.discord.handle.obj.IChannel; +import sx.blah.discord.handle.obj.IMessage; +import sx.blah.discord.handle.obj.IRole; + +public class CommandListener { + /** + * Runs a ChromaBot command. If mentionedonly is false, it will only execute the command if it was in #bot with the correct prefix or in private. + * + * @param message The Discord message + * @param mentionedonly Only run the command if ChromaBot is mentioned at the start of the message + * @return Whether it ran the command + */ + public static boolean runCommand(IMessage message, boolean mentionedonly) { + if (message.getContent().length() == 0) + return false; //Pin messages and such, let the mcchat listener deal with it + final IChannel channel = message.getChannel(); + if (!mentionedonly) { //mentionedonly conditions are in CommonListeners + if (!message.getChannel().isPrivate() + && !(message.getContent().charAt(0) == DiscordPlugin.getPrefix() + && channel.getStringID().equals(DiscordPlugin.botchannel.getStringID()))) // + return false; + message.getChannel().setTypingStatus(true); // Fun + } + final StringBuilder cmdwithargs = new StringBuilder(message.getContent()); + final String mention = DiscordPlugin.dc.getOurUser().mention(false); + final String mentionNick = DiscordPlugin.dc.getOurUser().mention(true); + boolean gotmention = checkanddeletemention(cmdwithargs, mention, message); + gotmention = checkanddeletemention(cmdwithargs, mentionNick, message) || gotmention; + for (String mentionRole : (Iterable) message.getRoleMentions().stream().filter(r -> DiscordPlugin.dc.getOurUser().hasRole(r)).map(IRole::mention)::iterator) + gotmention = checkanddeletemention(cmdwithargs, mentionRole, message) || gotmention; // Delete all mentions + if (mentionedonly && !gotmention) { + message.getChannel().setTypingStatus(false); + return false; + } + message.getChannel().setTypingStatus(true); + String cmdwithargsString = cmdwithargs.toString().trim(); //Remove spaces between mention and command + int index = cmdwithargsString.indexOf(" "); + String cmd; + String args; + if (index == -1) { + cmd = cmdwithargsString; + args = ""; + } else { + cmd = cmdwithargsString.substring(0, index); + args = cmdwithargsString.substring(index + 1).trim(); //In case there are multiple spaces + } + DiscordCommandBase.runCommand(cmd.toLowerCase(), args, message); + message.getChannel().setTypingStatus(false); + return true; + } + + private static boolean checkanddeletemention(StringBuilder cmdwithargs, String mention, IMessage message) { + if (message.getContent().startsWith(mention)) // TODO: Resolve mentions: Compound arguments, either a mention or text + if (cmdwithargs.length() > mention.length() + 1) + cmdwithargs.delete(0, + cmdwithargs.charAt(mention.length()) == ' ' ? mention.length() + 1 : mention.length()); + else + cmdwithargs.replace(0, cmdwithargs.length(), "help"); + else { + if (cmdwithargs.length() > 0 && cmdwithargs.charAt(0) == '/') + cmdwithargs.deleteCharAt(0); //Don't treat / as mention, mentions can be used in public mcchat + return false; + } + if (cmdwithargs.length() == 0) + cmdwithargs.replace(0, cmdwithargs.length(), "help"); + return true; + } +} diff --git a/src/main/java/buttondevteam/discordplugin/listeners/CommonListeners.java b/src/main/java/buttondevteam/discordplugin/listeners/CommonListeners.java new file mode 100755 index 0000000..7cc3dcc --- /dev/null +++ b/src/main/java/buttondevteam/discordplugin/listeners/CommonListeners.java @@ -0,0 +1,128 @@ +package buttondevteam.discordplugin.listeners; + +import buttondevteam.discordplugin.DPUtils; +import buttondevteam.discordplugin.DiscordPlugin; +import buttondevteam.discordplugin.mcchat.MinecraftChatModule; +import buttondevteam.lib.architecture.Component; +import lombok.val; +import org.bukkit.Bukkit; +import sx.blah.discord.api.events.IListener; +import sx.blah.discord.handle.impl.events.guild.channel.message.MessageReceivedEvent; +import sx.blah.discord.handle.impl.events.guild.role.RoleCreateEvent; +import sx.blah.discord.handle.impl.events.guild.role.RoleDeleteEvent; +import sx.blah.discord.handle.impl.events.guild.role.RoleUpdateEvent; +import sx.blah.discord.handle.impl.events.user.PresenceUpdateEvent; +import sx.blah.discord.handle.obj.StatusType; +import sx.blah.discord.util.EmbedBuilder; + +import java.util.Calendar; +import java.util.concurrent.TimeUnit; + +public class CommonListeners { + + /*private static ArrayList dcListeners=new ArrayList<>(); + + public static void registerDiscordListener(DiscordListener listener) { + //Step 1: Get all events that are handled by us + //Step 2: Find methods that handle these + //...or just simply call the methods in the right order + } + + private static void callDiscordEvent(Event event) { + String name=event.getClass().getSimpleName(); + name=Character.toLowerCase(name.charAt(0))+name.substring(1); + for (Object listener : dcListeners) { + listener.getClass().getMethods(name, AsyncDiscordEvent.class); + } + }*/ + + private static long lasttime = 0; + + /* + MentionEvent: + - CommandListener (starts with mention, only 'channelcon' and not in #bot) + + MessageReceivedEvent: + - v CommandListener (starts with mention, in #bot or a connected chat) + - Minecraft chat (is enabled in the channel and message isn't [/]mcchat) + - CommandListener (with the correct prefix in #bot, or in private) + */ + public static IListener[] getListeners() { + return new IListener[]{new IListener() { + @Override + public void handle(MessageReceivedEvent event) { + if (DiscordPlugin.SafeMode) + return; + if (event.getMessage().getAuthor().isBot()) + return; + boolean handled = false; + if (event.getChannel().getLongID() == DiscordPlugin.botchannel.getLongID() //If mentioned, that's higher than chat + || event.getMessage().getContent().contains("channelcon")) //Only 'channelcon' is allowed in other channels + handled = CommandListener.runCommand(event.getMessage(), true); //#bot is handled here + if (handled) return; + val mcchat = Component.getComponents().get(MinecraftChatModule.class); + if (mcchat != null && mcchat.isEnabled()) //ComponentManager.isEnabled() searches the component again + handled = ((MinecraftChatModule) mcchat).getListener().handleDiscord(event); //Also runs Discord commands in chat channels + if (!handled) + handled = CommandListener.runCommand(event.getMessage(), false); + } + }, new IListener() { + @Override + public void handle(PresenceUpdateEvent event) { + if (DiscordPlugin.SafeMode) + return; + val devrole = DiscordPlugin.devServer.getRolesByName("Developer").get(0); + if (event.getOldPresence().getStatus().equals(StatusType.OFFLINE) + && !event.getNewPresence().getStatus().equals(StatusType.OFFLINE) + && event.getUser().getRolesForGuild(DiscordPlugin.devServer).stream() + .anyMatch(r -> r.getLongID() == devrole.getLongID()) + && DiscordPlugin.devServer.getUsersByRole(devrole).stream() + .noneMatch(u -> u.getPresence().getStatus().equals(StatusType.OFFLINE)) + && lasttime + 10 < TimeUnit.NANOSECONDS.toHours(System.nanoTime()) + && Calendar.getInstance().get(Calendar.DAY_OF_MONTH) % 5 == 0) { + DiscordPlugin.sendMessageToChannel(DiscordPlugin.devofficechannel, "Full house!", + new EmbedBuilder() + .withImage( + "https://cdn.discordapp.com/attachments/249295547263877121/249687682618359808/poker-hand-full-house-aces-kings-playing-cards-15553791.png") + .build()); + lasttime = TimeUnit.NANOSECONDS.toHours(System.nanoTime()); + } + } + }, (IListener) event -> { + Bukkit.getScheduler().runTaskLaterAsynchronously(DiscordPlugin.plugin, () -> { + if (event.getRole().isDeleted() || !DiscordPlugin.plugin.isGameRole(event.getRole())) + return; //Deleted or not a game role + DiscordPlugin.GameRoles.add(event.getRole().getName()); + DiscordPlugin.sendMessageToChannel(DiscordPlugin.modlogchannel, "Added " + event.getRole().getName() + " as game role. If you don't want this, change the role's color from the default."); + }, 100); + }, (IListener) event -> { + if (DiscordPlugin.GameRoles.remove(event.getRole().getName())) + DiscordPlugin.sendMessageToChannel(DiscordPlugin.modlogchannel, "Removed " + event.getRole().getName() + " as a game role."); + }, (IListener) event -> { //Role update event + if (!DiscordPlugin.plugin.isGameRole(event.getNewRole())) { + if (DiscordPlugin.GameRoles.remove(event.getOldRole().getName())) + DiscordPlugin.sendMessageToChannel(DiscordPlugin.modlogchannel, "Removed " + event.getOldRole().getName() + " as a game role because it's color changed."); + } else { + if (DiscordPlugin.GameRoles.contains(event.getOldRole().getName()) && event.getOldRole().getName().equals(event.getNewRole().getName())) + return; + boolean removed = DiscordPlugin.GameRoles.remove(event.getOldRole().getName()); //Regardless of whether it was a game role + DiscordPlugin.GameRoles.add(event.getNewRole().getName()); //Add it because it has no color + if (removed) + DiscordPlugin.sendMessageToChannel(DiscordPlugin.modlogchannel, "Changed game role from " + event.getOldRole().getName() + " to " + event.getNewRole().getName() + "."); + else + DiscordPlugin.sendMessageToChannel(DiscordPlugin.modlogchannel, "Added " + event.getNewRole().getName() + " as game role because it has the default color."); + } + }}; + } + + private static boolean debug = false; + + public static void debug(String debug) { + if (CommonListeners.debug) //Debug + DPUtils.getLogger().info(debug); + } + + public static boolean debug() { + return debug = !debug; + } +} diff --git a/src/main/java/buttondevteam/discordplugin/listeners/DiscordListener.java b/src/main/java/buttondevteam/discordplugin/listeners/DiscordListener.java new file mode 100644 index 0000000..292a1a1 --- /dev/null +++ b/src/main/java/buttondevteam/discordplugin/listeners/DiscordListener.java @@ -0,0 +1,4 @@ +package buttondevteam.discordplugin.listeners; + +public interface DiscordListener { +} diff --git a/src/main/java/buttondevteam/discordplugin/listeners/MCListener.java b/src/main/java/buttondevteam/discordplugin/listeners/MCListener.java index 6f9b3c9..a39d5fc 100755 --- a/src/main/java/buttondevteam/discordplugin/listeners/MCListener.java +++ b/src/main/java/buttondevteam/discordplugin/listeners/MCListener.java @@ -1,98 +1,24 @@ package buttondevteam.discordplugin.listeners; -import buttondevteam.discordplugin.*; -import buttondevteam.discordplugin.commands.ConnectCommand; -import buttondevteam.lib.TBMCCoreAPI; -import buttondevteam.lib.TBMCSystemChatEvent; -import buttondevteam.lib.player.*; -import com.earth2me.essentials.CommandSource; +import buttondevteam.discordplugin.DiscordPlayer; +import buttondevteam.discordplugin.DiscordPlugin; +import buttondevteam.lib.player.TBMCPlayerGetInfoEvent; import lombok.val; -import net.ess3.api.events.AfkStatusChangeEvent; -import net.ess3.api.events.MuteStatusChangeEvent; import org.bukkit.Bukkit; -import org.bukkit.entity.Player; -import org.bukkit.event.*; -import org.bukkit.event.entity.PlayerDeathEvent; -import org.bukkit.event.player.PlayerJoinEvent; -import org.bukkit.event.player.PlayerKickEvent; -import org.bukkit.event.player.PlayerLoginEvent; -import org.bukkit.event.player.PlayerLoginEvent.Result; -import org.bukkit.event.player.PlayerQuitEvent; -import org.bukkit.event.server.BroadcastMessageEvent; +import org.bukkit.event.Event; +import org.bukkit.event.EventHandler; +import org.bukkit.event.HandlerList; +import org.bukkit.event.Listener; import org.bukkit.event.server.ServerCommandEvent; import org.bukkit.plugin.AuthorNagException; import org.bukkit.plugin.Plugin; import org.bukkit.plugin.RegisteredListener; -import sx.blah.discord.handle.obj.IRole; import sx.blah.discord.handle.obj.IUser; -import sx.blah.discord.util.DiscordException; -import sx.blah.discord.util.MissingPermissionsException; import java.util.Arrays; import java.util.logging.Level; public class MCListener implements Listener { - @EventHandler(priority = EventPriority.HIGHEST) - public void onPlayerLogin(PlayerLoginEvent e) { - if (e.getResult() != Result.ALLOWED) - return; - MCChatListener.ConnectedSenders.values().stream().flatMap(v -> v.values().stream()) //Only private mcchat should be in ConnectedSenders - .filter(s -> s.getUniqueId().equals(e.getPlayer().getUniqueId())).findAny() - .ifPresent(dcp -> callEventExcludingSome(new PlayerQuitEvent(dcp, ""))); - } - - @EventHandler(priority = EventPriority.LOWEST) - public void onPlayerJoin(TBMCPlayerJoinEvent e) { - if (e.getPlayer() instanceof DiscordConnectedPlayer) - return; // Don't show the joined message for the fake player - Bukkit.getScheduler().runTaskAsynchronously(DiscordPlugin.plugin, () -> { - final Player p = e.getPlayer(); - DiscordPlayer dp = e.GetPlayer().getAs(DiscordPlayer.class); - if (dp != null) { - val user = DiscordPlugin.dc.getUserByID(Long.parseLong(dp.getDiscordID())); - MCChatListener.addSender(MCChatListener.OnlineSenders, dp.getDiscordID(), - new DiscordPlayerSender(user, user.getOrCreatePMChannel(), p)); - MCChatListener.addSender(MCChatListener.OnlineSenders, dp.getDiscordID(), - new DiscordPlayerSender(user, DiscordPlugin.chatchannel, p)); //Stored per-channel - } - if (ConnectCommand.WaitingToConnect.containsKey(e.GetPlayer().PlayerName().get())) { - IUser user = DiscordPlugin.dc - .getUserByID(Long.parseLong(ConnectCommand.WaitingToConnect.get(e.GetPlayer().PlayerName().get()))); - p.sendMessage("§bTo connect with the Discord account @" + user.getName() + "#" + user.getDiscriminator() - + " do /discord accept"); - p.sendMessage("§bIf it wasn't you, do /discord decline"); - } - final String message = e.GetPlayer().PlayerName().get() + " joined the game"; - MCChatListener.forAllowedCustomAndAllMCChat(MCChatListener.send(message), e.getPlayer(), ChannelconBroadcast.JOINLEAVE, true); - //System.out.println("Does this appear more than once?"); //No - MCChatListener.ListC = 0; - ChromaBot.getInstance().updatePlayerList(); - }); - } - - @EventHandler(priority = EventPriority.HIGHEST) - public void onPlayerLeave(TBMCPlayerQuitEvent e) { - if (e.getPlayer() instanceof DiscordConnectedPlayer) - return; // Only care about real users - MCChatListener.OnlineSenders.entrySet() - .removeIf(entry -> entry.getValue().entrySet().stream().anyMatch(p -> p.getValue().getUniqueId().equals(e.getPlayer().getUniqueId()))); - Bukkit.getScheduler().runTask(DiscordPlugin.plugin, - () -> MCChatListener.ConnectedSenders.values().stream().flatMap(v -> v.values().stream()) - .filter(s -> s.getUniqueId().equals(e.getPlayer().getUniqueId())).findAny() - .ifPresent(dcp -> callEventExcludingSome(new PlayerJoinEvent(dcp, "")))); - Bukkit.getScheduler().runTaskLaterAsynchronously(DiscordPlugin.plugin, - ChromaBot.getInstance()::updatePlayerList, 5); - final String message = e.GetPlayer().PlayerName().get() + " left the game"; - MCChatListener.forAllowedCustomAndAllMCChat(MCChatListener.send(message), e.getPlayer(), ChannelconBroadcast.JOINLEAVE, true); - } - - @EventHandler(priority = EventPriority.HIGHEST) - public void onPlayerKick(PlayerKickEvent e) { - /*if (!DiscordPlugin.hooked && !e.getReason().equals("The server is restarting") - && !e.getReason().equals("Server closed")) // The leave messages errored with the previous setup, I could make it wait since I moved it here, but instead I have a special - MCChatListener.forAllowedCustomAndAllMCChat(e.getPlayer().getName() + " left the game"); // message for this - Oh wait this doesn't even send normally because of the hook*/ - } - @EventHandler public void onGetInfo(TBMCPlayerGetInfoEvent e) { if (DiscordPlugin.SafeMode) @@ -107,125 +33,71 @@ public class MCListener implements Listener { e.addInfo(user.getPresence().getActivity().get() + ": " + user.getPresence().getText().get()); } - @EventHandler(priority = EventPriority.LOW) - public void onPlayerDeath(PlayerDeathEvent e) { - MCChatListener.forAllowedCustomAndAllMCChat(MCChatListener.send(e.getDeathMessage()), e.getEntity(), ChannelconBroadcast.DEATH, true); - } - - @EventHandler - public void onPlayerAFK(AfkStatusChangeEvent e) { - final Player base = e.getAffected().getBase(); - if (e.isCancelled() || !base.isOnline()) - return; - final String msg = base.getDisplayName() - + " is " + (e.getValue() ? "now" : "no longer") + " AFK."; - MCChatListener.forAllowedCustomAndAllMCChat(MCChatListener.send(msg), base, ChannelconBroadcast.AFK, false); - } - @EventHandler public void onServerCommand(ServerCommandEvent e) { DiscordPlugin.Restart = !e.getCommand().equalsIgnoreCase("stop"); // The variable is always true except if stopped } - @EventHandler - public void onPlayerMute(MuteStatusChangeEvent e) { - try { - DPUtils.performNoWait(() -> { - final IRole role = DiscordPlugin.dc.getRoleByID(164090010461667328L); - final CommandSource source = e.getAffected().getSource(); - if (!source.isPlayer()) - return; - final IUser user = DiscordPlugin.dc.getUserByID( - Long.parseLong(TBMCPlayerBase.getPlayer(source.getPlayer().getUniqueId(), TBMCPlayer.class) - .getAs(DiscordPlayer.class).getDiscordID())); - if (e.getValue()) - user.addRole(role); - else - user.removeRole(role); - }); - } catch (DiscordException | MissingPermissionsException ex) { - TBMCCoreAPI.SendException("Failed to give/take Muted role to player " + e.getAffected().getName() + "!", - ex); - } - } + private static final String[] EXCLUDED_PLUGINS = {"ProtocolLib", "LibsDisguises", "JourneyMapServer"}; //TODO: Make configurable - @EventHandler - public void onChatSystemMessage(TBMCSystemChatEvent event) { - MCChatListener.forAllowedMCChat(MCChatListener.send(event.getMessage()), event); - } + public static void callEventExcludingSome(Event event) { + callEventExcluding(event, false, EXCLUDED_PLUGINS); + } - @EventHandler - public void onBroadcastMessage(BroadcastMessageEvent event) { - MCChatListener.forCustomAndAllMCChat(MCChatListener.send(event.getMessage()), ChannelconBroadcast.BROADCAST, false); - } + /** + * Calls an event with the given details. + *

+ * This method only synchronizes when the event is not asynchronous. + * + * @param event Event details + * @param only Flips the operation and includes the listed plugins + * @param plugins The plugins to exclude. Not case sensitive. + */ + public static void callEventExcluding(Event event, boolean only, String... plugins) { // Copied from Spigot-API and modified a bit + if (event.isAsynchronous()) { + if (Thread.holdsLock(Bukkit.getPluginManager())) { + throw new IllegalStateException( + event.getEventName() + " cannot be triggered asynchronously from inside synchronized code."); + } + if (Bukkit.getServer().isPrimaryThread()) { + throw new IllegalStateException( + event.getEventName() + " cannot be triggered asynchronously from primary server thread."); + } + fireEventExcluding(event, only, plugins); + } else { + synchronized (Bukkit.getPluginManager()) { + fireEventExcluding(event, only, plugins); + } + } + } - @EventHandler - public void onYEEHAW(TBMCYEEHAWEvent event) { //TODO: Inherit from the chat event base to have channel support - String name = event.getSender() instanceof Player ? ((Player) event.getSender()).getDisplayName() - : event.getSender().getName(); - //Channel channel = ChromaGamerBase.getFromSender(event.getSender()).channel().get(); - TODO - MCChatListener.forAllMCChat(MCChatListener.send(name + " <:YEEHAW:" + DiscordPlugin.mainServer.getEmojiByName("YEEHAW").getStringID() + ">s")); - } + private static void fireEventExcluding(Event event, boolean only, String... plugins) { + HandlerList handlers = event.getHandlers(); // Code taken from SimplePluginManager in Spigot-API + RegisteredListener[] listeners = handlers.getRegisteredListeners(); + val server = Bukkit.getServer(); - private static final String[] EXCLUDED_PLUGINS = {"ProtocolLib", "LibsDisguises"}; + for (RegisteredListener registration : listeners) { + if (!registration.getPlugin().isEnabled() + || Arrays.stream(plugins).anyMatch(p -> only ^ p.equalsIgnoreCase(registration.getPlugin().getName()))) + continue; // Modified to exclude plugins - public static void callEventExcludingSome(Event event) { - callEventExcluding(event, EXCLUDED_PLUGINS); - } + try { + registration.callEvent(event); + } catch (AuthorNagException ex) { + Plugin plugin = registration.getPlugin(); - /** - * Calls an event with the given details. - *

- * This method only synchronizes when the event is not asynchronous. - * - * @param event Event details - * @param plugins The plugins to exclude. Not case sensitive. - */ - private static void callEventExcluding(Event event, String... plugins) { // Copied from Spigot-API and modified a bit - if (event.isAsynchronous()) { - if (Thread.holdsLock(Bukkit.getPluginManager())) { - throw new IllegalStateException( - event.getEventName() + " cannot be triggered asynchronously from inside synchronized code."); - } - if (Bukkit.getServer().isPrimaryThread()) { - throw new IllegalStateException( - event.getEventName() + " cannot be triggered asynchronously from primary server thread."); - } - fireEventExcluding(event, plugins); - } else { - synchronized (Bukkit.getPluginManager()) { - fireEventExcluding(event, plugins); - } - } - } + if (plugin.isNaggable()) { + plugin.setNaggable(false); - private static void fireEventExcluding(Event event, String... plugins) { - HandlerList handlers = event.getHandlers(); // Code taken from SimplePluginManager in Spigot-API - RegisteredListener[] listeners = handlers.getRegisteredListeners(); - val server = Bukkit.getServer(); - - for (RegisteredListener registration : listeners) { - if (!registration.getPlugin().isEnabled() - || Arrays.stream(plugins).anyMatch(p -> p.equalsIgnoreCase(registration.getPlugin().getName()))) - continue; // Modified to exclude plugins - - try { - registration.callEvent(event); - } catch (AuthorNagException ex) { - Plugin plugin = registration.getPlugin(); - - if (plugin.isNaggable()) { - plugin.setNaggable(false); - - server.getLogger().log(Level.SEVERE, - String.format("Nag author(s): '%s' of '%s' about the following: %s", - plugin.getDescription().getAuthors(), plugin.getDescription().getFullName(), - ex.getMessage())); - } - } catch (Throwable ex) { - server.getLogger().log(Level.SEVERE, "Could not pass event " + event.getEventName() + " to " - + registration.getPlugin().getDescription().getFullName(), ex); - } - } - } + server.getLogger().log(Level.SEVERE, + String.format("Nag author(s): '%s' of '%s' about the following: %s", + plugin.getDescription().getAuthors(), plugin.getDescription().getFullName(), + ex.getMessage())); + } + } catch (Throwable ex) { + server.getLogger().log(Level.SEVERE, "Could not pass event " + event.getEventName() + " to " + + registration.getPlugin().getDescription().getFullName(), ex); + } + } + } } diff --git a/src/main/java/buttondevteam/discordplugin/commands/MCChatCommand.java b/src/main/java/buttondevteam/discordplugin/mcchat/MCChatCommand.java similarity index 65% rename from src/main/java/buttondevteam/discordplugin/commands/MCChatCommand.java rename to src/main/java/buttondevteam/discordplugin/mcchat/MCChatCommand.java index e2ca2ca..3c05f02 100755 --- a/src/main/java/buttondevteam/discordplugin/commands/MCChatCommand.java +++ b/src/main/java/buttondevteam/discordplugin/mcchat/MCChatCommand.java @@ -1,8 +1,8 @@ -package buttondevteam.discordplugin.commands; +package buttondevteam.discordplugin.mcchat; import buttondevteam.discordplugin.DiscordPlayer; import buttondevteam.discordplugin.DiscordPlugin; -import buttondevteam.discordplugin.listeners.MCChatListener; +import buttondevteam.discordplugin.commands.DiscordCommandBase; import buttondevteam.lib.TBMCCoreAPI; import sx.blah.discord.handle.obj.IMessage; @@ -13,7 +13,7 @@ public class MCChatCommand extends DiscordCommandBase { return "mcchat"; } - @Override + @Override //TODO: Only register if module is enabled public boolean run(IMessage message, String args) { if (!message.getChannel().isPrivate()) { DiscordPlugin.sendMessageToChannel(message.getChannel(), @@ -22,10 +22,10 @@ public class MCChatCommand extends DiscordCommandBase { } try (final DiscordPlayer user = DiscordPlayer.getUser(message.getAuthor().getStringID(), DiscordPlayer.class)) { boolean mcchat = !user.isMinecraftChatEnabled(); - MCChatListener.privateMCChat(message.getChannel(), mcchat, message.getAuthor(), user); + MCChatPrivate.privateMCChat(message.getChannel(), mcchat, message.getAuthor(), user); DiscordPlugin.sendMessageToChannel(message.getChannel(), "Minecraft chat " + (mcchat // - ? "enabled. Use '/mcchat' again to turn it off." // + ? "enabled. Use '" + DiscordPlugin.getPrefix() + "mcchat' again to turn it off." // : "disabled.")); } catch (Exception e) { TBMCCoreAPI.SendException("Error while setting mcchat for user" + message.getAuthor().getName(), e); @@ -36,8 +36,9 @@ public class MCChatCommand extends DiscordCommandBase { @Override public String[] getHelpText() { return new String[] { // - "mcchat enables or disables the Minecraft chat in private messages.", // - "It can be useful if you don't want your messages to be visible, for example when talking a private channel." // + DiscordPlugin.getPrefix() + "mcchat enables or disables the Minecraft chat in private messages.", // + "It can be useful if you don't want your messages to be visible, for example when talking in a private channel.", // + "You can also run all of the ingame commands you have access to using this command, if you have your accounts connected." // }; // TODO: Pin channel switching to indicate the current channel } diff --git a/src/main/java/buttondevteam/discordplugin/mcchat/MCChatCustom.java b/src/main/java/buttondevteam/discordplugin/mcchat/MCChatCustom.java new file mode 100644 index 0000000..74979f8 --- /dev/null +++ b/src/main/java/buttondevteam/discordplugin/mcchat/MCChatCustom.java @@ -0,0 +1,59 @@ +package buttondevteam.discordplugin.mcchat; + +import buttondevteam.discordplugin.DiscordConnectedPlayer; +import buttondevteam.lib.chat.Channel; +import lombok.NonNull; +import lombok.val; +import sx.blah.discord.handle.obj.IChannel; +import sx.blah.discord.handle.obj.IUser; + +import javax.annotation.Nullable; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class MCChatCustom { + /** + * Used for town or nation chats or anything else + */ + static ArrayList lastmsgCustom = new ArrayList<>(); + + public static void addCustomChat(IChannel channel, String groupid, Channel mcchannel, IUser user, DiscordConnectedPlayer dcp, int toggles) { + val lmd = new CustomLMD(channel, user, groupid, mcchannel, dcp, toggles); + lastmsgCustom.add(lmd); + } + + public static boolean hasCustomChat(IChannel channel) { + return lastmsgCustom.stream().anyMatch(lmd -> lmd.channel.getLongID() == channel.getLongID()); + } + + @Nullable + public static CustomLMD getCustomChat(IChannel channel) { + return lastmsgCustom.stream().filter(lmd -> lmd.channel.getLongID() == channel.getLongID()).findAny().orElse(null); + } + + public static boolean removeCustomChat(IChannel channel) { + MCChatUtils.lastmsgfromd.remove(channel.getLongID()); + return lastmsgCustom.removeIf(lmd -> lmd.channel.getLongID() == channel.getLongID()); + } + + public static List getCustomChats() { + return Collections.unmodifiableList(lastmsgCustom); + } + + public static class CustomLMD extends MCChatUtils.LastMsgData { + public final String groupID; + public final Channel mcchannel; + public final DiscordConnectedPlayer dcp; + public int toggles; + + private CustomLMD(@NonNull IChannel channel, @NonNull IUser user, + @NonNull String groupid, @NonNull Channel mcchannel, @NonNull DiscordConnectedPlayer dcp, int toggles) { + super(channel, user); + groupID = groupid; + this.mcchannel = mcchannel; + this.dcp = dcp; + this.toggles = toggles; + } + } +} diff --git a/src/main/java/buttondevteam/discordplugin/listeners/MCChatListener.java b/src/main/java/buttondevteam/discordplugin/mcchat/MCChatListener.java similarity index 56% rename from src/main/java/buttondevteam/discordplugin/listeners/MCChatListener.java rename to src/main/java/buttondevteam/discordplugin/mcchat/MCChatListener.java index 4d73c83..c2ec714 100755 --- a/src/main/java/buttondevteam/discordplugin/listeners/MCChatListener.java +++ b/src/main/java/buttondevteam/discordplugin/mcchat/MCChatListener.java @@ -1,56 +1,48 @@ -package buttondevteam.discordplugin.listeners; +package buttondevteam.discordplugin.mcchat; -import buttondevteam.discordplugin.*; +import buttondevteam.core.ComponentManager; +import buttondevteam.discordplugin.DPUtils; +import buttondevteam.discordplugin.DiscordPlugin; +import buttondevteam.discordplugin.DiscordSender; +import buttondevteam.discordplugin.DiscordSenderBase; +import buttondevteam.discordplugin.listeners.CommandListener; import buttondevteam.discordplugin.playerfaker.VanillaCommandListener; import buttondevteam.lib.TBMCChatEvent; import buttondevteam.lib.TBMCChatPreprocessEvent; import buttondevteam.lib.TBMCCoreAPI; -import buttondevteam.lib.TBMCSystemChatEvent; import buttondevteam.lib.chat.Channel; import buttondevteam.lib.chat.ChatMessage; import buttondevteam.lib.chat.ChatRoom; import buttondevteam.lib.chat.TBMCChatAPI; import buttondevteam.lib.player.TBMCPlayer; import com.vdurmont.emoji.EmojiParser; -import io.netty.util.collection.LongObjectHashMap; -import lombok.NonNull; -import lombok.RequiredArgsConstructor; -import lombok.experimental.var; import lombok.val; import org.bukkit.Bukkit; -import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; -import org.bukkit.event.player.PlayerJoinEvent; -import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.scheduler.BukkitTask; -import sx.blah.discord.api.events.IListener; import sx.blah.discord.api.internal.json.objects.EmbedObject; import sx.blah.discord.handle.impl.events.guild.channel.message.MessageReceivedEvent; import sx.blah.discord.handle.obj.IChannel; import sx.blah.discord.handle.obj.IMessage; -import sx.blah.discord.handle.obj.IPrivateChannel; import sx.blah.discord.handle.obj.IUser; import sx.blah.discord.util.DiscordException; import sx.blah.discord.util.EmbedBuilder; import sx.blah.discord.util.MissingPermissionsException; -import javax.annotation.Nullable; import java.awt.*; import java.time.Instant; -import java.util.*; -import java.util.List; +import java.util.AbstractMap; +import java.util.Arrays; +import java.util.Optional; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeoutException; -import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; -import java.util.function.Supplier; import java.util.stream.Collectors; -import java.util.stream.Stream; -public class MCChatListener implements Listener, IListener { +public class MCChatListener implements Listener { private BukkitTask sendtask; private LinkedBlockingQueue> sendevents = new LinkedBlockingQueue<>(); private Runnable sendrunnable; @@ -58,7 +50,7 @@ public class MCChatListener implements Listener, IListener @EventHandler // Minecraft public void onMCChat(TBMCChatEvent ev) { - if (DiscordPlugin.SafeMode || ev.isCancelled()) //SafeMode: Needed so it doesn't restart after server shutdown + if (!ComponentManager.isEnabled(MinecraftChatModule.class) || ev.isCancelled()) //SafeMode: Needed so it doesn't restart after server shutdown return; sendevents.add(new AbstractMap.SimpleEntry<>(ev, Instant.now())); if (sendtask != null) @@ -81,7 +73,7 @@ public class MCChatListener implements Listener, IListener time = se.getValue(); final String authorPlayer = "[" + DPUtils.sanitizeStringNoEscape(e.getChannel().DisplayName) + "] " // - + (e.getSender() instanceof DiscordSenderBase ? "[D]" : "") // + + ("Minecraft".equals(e.getOrigin()) ? "" : "[" + e.getOrigin().substring(0, 1) + "]") // + (DPUtils.sanitizeStringNoEscape(e.getSender() instanceof Player // ? ((Player) e.getSender()).getDisplayName() // : e.getSender().getName())); @@ -102,7 +94,7 @@ public class MCChatListener implements Listener, IListener // embed.withFooterText(e.getChannel().DisplayName); embed.withTimestamp(time); final long nanoTime = System.nanoTime(); - InterruptibleConsumer doit = lastmsgdata -> { + InterruptibleConsumer doit = lastmsgdata -> { final EmbedObject embedObject = embed.build(); if (lastmsgdata.message == null || lastmsgdata.message.isDeleted() || !authorPlayer.equals(lastmsgdata.message.getEmbeds().get(0).getAuthor().getName()) @@ -117,7 +109,7 @@ public class MCChatListener implements Listener, IListener try { lastmsgdata.content = embedObject.description = lastmsgdata.content + "\n" + embedObject.description;// The message object doesn't get updated - final LastMsgData _lastmsgdata = lastmsgdata; + final MCChatUtils.LastMsgData _lastmsgdata = lastmsgdata; DPUtils.perform(() -> _lastmsgdata.message.edit("", embedObject)); } catch (MissingPermissionsException | DiscordException e1) { TBMCCoreAPI.SendException("An error occurred while editing chat message!", e1); @@ -129,21 +121,21 @@ public class MCChatListener implements Listener, IListener || ((DiscordSenderBase) e.getSender()).getChannel().getLongID() != ch.getLongID(); if (e.getChannel().isGlobal() - && (e.isFromcmd() || isdifferentchannel.test(DiscordPlugin.chatchannel))) - doit.accept(lastmsgdata == null - ? lastmsgdata = new LastMsgData(DiscordPlugin.chatchannel, null) - : lastmsgdata); + && (e.isFromCommand() || isdifferentchannel.test(DiscordPlugin.chatchannel))) + doit.accept(MCChatUtils.lastmsgdata == null + ? MCChatUtils.lastmsgdata = new MCChatUtils.LastMsgData(DiscordPlugin.chatchannel, null) + : MCChatUtils.lastmsgdata); - for (LastMsgData data : lastmsgPerUser) { - if ((e.isFromcmd() || isdifferentchannel.test(data.channel)) - && e.shouldSendTo(getSender(data.channel, data.user))) + for (MCChatUtils.LastMsgData data : MCChatPrivate.lastmsgPerUser) { + if ((e.isFromCommand() || isdifferentchannel.test(data.channel)) + && e.shouldSendTo(MCChatUtils.getSender(data.channel, data.user))) doit.accept(data); } - val iterator = lastmsgCustom.iterator(); + val iterator = MCChatCustom.lastmsgCustom.iterator(); while (iterator.hasNext()) { val lmd = iterator.next(); - if ((e.isFromcmd() || isdifferentchannel.test(lmd.channel)) //Test if msg is from Discord + if ((e.isFromCommand() || isdifferentchannel.test(lmd.channel)) //Test if msg is from Discord && e.getChannel().ID.equals(lmd.mcchannel.ID) //If it's from a command, the command msg has been deleted, so we need to send it && e.getGroupID().equals(lmd.groupID)) { //Check if this is the group we want to test - #58 if (e.shouldSendTo(lmd.dcp)) //Check original user's permissions @@ -162,32 +154,6 @@ public class MCChatListener implements Listener, IListener } } - @RequiredArgsConstructor - public static class LastMsgData { - public IMessage message; - public long time; - public String content; - public final IChannel channel; - public Channel mcchannel; - public final IUser user; - } - - public static class CustomLMD extends LastMsgData { - public final String groupID; - public final Channel mcchannel; - public final DiscordConnectedPlayer dcp; - public int toggles; - - private CustomLMD(@NonNull IChannel channel, @NonNull IUser user, - @NonNull String groupid, @NonNull Channel mcchannel, @NonNull DiscordConnectedPlayer dcp, int toggles) { - super(channel, user); - groupID = groupid; - this.mcchannel = mcchannel; - this.dcp = dcp; - this.toggles = toggles; - } - } - @EventHandler public void onChatPreprocess(TBMCChatPreprocessEvent event) { int start = -1; @@ -211,72 +177,8 @@ public class MCChatListener implements Listener, IListener private static final String[] UnconnectedCmds = new String[]{"list", "u", "shrug", "tableflip", "unflip", "mwiki", "yeehaw", "lenny", "rp", "plugins"}; - private static LastMsgData lastmsgdata; private static short lastlist = 0; private static short lastlistp = 0; - /** - * Used for messages in PMs (mcchat). - */ - private static ArrayList lastmsgPerUser = new ArrayList(); - /** - * Used for town or nation chats or anything else - */ - private static ArrayList lastmsgCustom = new ArrayList<>(); - private static LongObjectHashMap lastmsgfromd = new LongObjectHashMap<>(); // Last message sent by a Discord user, used for clearing checkmarks - - public static boolean privateMCChat(IChannel channel, boolean start, IUser user, DiscordPlayer dp) { - TBMCPlayer mcp = dp.getAs(TBMCPlayer.class); - if (mcp != null) { // If the accounts aren't connected, can't make a connected sender - val p = Bukkit.getPlayer(mcp.getUUID()); - val op = Bukkit.getOfflinePlayer(mcp.getUUID()); - if (start) { - val sender = new DiscordConnectedPlayer(user, channel, mcp.getUUID(), op.getName()); - addSender(ConnectedSenders, user, sender); - if (p == null)// Player is offline - If the player is online, that takes precedence - MCListener.callEventExcludingSome(new PlayerJoinEvent(sender, "")); - } else { - val sender = removeSender(ConnectedSenders, channel, user); - if (p == null)// Player is offline - If the player is online, that takes precedence - MCListener.callEventExcludingSome(new PlayerQuitEvent(sender, "")); - } - } - if (!start) - lastmsgfromd.remove(channel.getLongID()); - return start // - ? lastmsgPerUser.add(new LastMsgData(channel, user)) // Doesn't support group DMs - : lastmsgPerUser.removeIf(lmd -> lmd.channel.getLongID() == channel.getLongID()); - } - - public static T addSender(HashMap> senders, - IUser user, T sender) { - return addSender(senders, user.getStringID(), sender); - } - - public static T addSender(HashMap> senders, - String did, T sender) { - var map = senders.get(did); - if (map == null) - map = new HashMap<>(); - map.put(sender.getChannel(), sender); - senders.put(did, map); - return sender; - } - - public static T getSender(HashMap> senders, - IChannel channel, IUser user) { - var map = senders.get(user.getStringID()); - if (map != null) - return map.get(channel); - return null; - } - - public static T removeSender(HashMap> senders, - IChannel channel, IUser user) { - var map = senders.get(user.getStringID()); - if (map != null) - return map.remove(channel); - return null; - } // ......................DiscordSender....DiscordConnectedPlayer.DiscordPlayerSender // Offline public chat......x............................................ @@ -289,143 +191,8 @@ public class MCChatListener implements Listener, IListener // If online and disabling private chat, don't logout // The maps may not contain the senders for UnconnectedSenders - public static boolean isMinecraftChatEnabled(DiscordPlayer dp) { - return isMinecraftChatEnabled(dp.getDiscordID()); - } - - public static boolean isMinecraftChatEnabled(String did) { // Don't load the player data just for this - return lastmsgPerUser.stream() - .anyMatch(lmd -> ((IPrivateChannel) lmd.channel).getRecipient().getStringID().equals(did)); - } - - public static void addCustomChat(IChannel channel, String groupid, Channel mcchannel, IUser user, DiscordConnectedPlayer dcp, int toggles) { - val lmd = new CustomLMD(channel, user, groupid, mcchannel, dcp, toggles); - lastmsgCustom.add(lmd); - } - - public static boolean hasCustomChat(IChannel channel) { - return lastmsgCustom.stream().anyMatch(lmd -> lmd.channel.getLongID() == channel.getLongID()); - } - - @Nullable - public static CustomLMD getCustomChat(IChannel channel) { - return lastmsgCustom.stream().filter(lmd -> lmd.channel.getLongID() == channel.getLongID()).findAny().orElse(null); - } - - public static boolean removeCustomChat(IChannel channel) { - lastmsgfromd.remove(channel.getLongID()); - return lastmsgCustom.removeIf(lmd -> lmd.channel.getLongID() == channel.getLongID()); - } - - public static List getCustomChats() { - return Collections.unmodifiableList(lastmsgCustom); - } - - /** - * May contain P<DiscordID> as key for public chat - */ - public static final HashMap> UnconnectedSenders = new HashMap<>(); - public static final HashMap> ConnectedSenders = new HashMap<>(); - /** - * May contain P<DiscordID> as key for public chat - */ - public static final HashMap> OnlineSenders = new HashMap<>(); public static short ListC = 0; - /** - * Resets the last message, so it will start a new one instead of appending to it. - * This is used when someone (even the bot) sends a message to the channel. - * - * @param channel The channel to reset in - the process is slightly different for the public, private and custom chats - */ - public static void resetLastMessage(IChannel channel) { - if (channel.getLongID() == DiscordPlugin.chatchannel.getLongID()) { - (lastmsgdata == null ? lastmsgdata = new LastMsgData(DiscordPlugin.chatchannel, null) - : lastmsgdata).message = null; - return; - } // Don't set the whole object to null, the player and channel information should be preserved - for (LastMsgData data : channel.isPrivate() ? lastmsgPerUser : lastmsgCustom) { - if (data.channel.getLongID() == channel.getLongID()) { - data.message = null; - return; - } - } - //If it gets here, it's sending a message to a non-chat channel - } - - public static void forAllMCChat(Consumer action) { - action.accept(DiscordPlugin.chatchannel); - for (LastMsgData data : lastmsgPerUser) - action.accept(data.channel); - // lastmsgCustom.forEach(cc -> action.accept(cc.channel)); - Only send relevant messages to custom chat - } - - /** - * For custom and all MC chat - * - * @param action The action to act - * @param toggle The toggle to check - * @param hookmsg Whether the message is also sent from the hook - */ - public static void forCustomAndAllMCChat(Consumer action, @Nullable ChannelconBroadcast toggle, boolean hookmsg) { - if (!DiscordPlugin.hooked || !hookmsg) - forAllMCChat(action); - final Consumer customLMDConsumer = cc -> action.accept(cc.channel); - if (toggle == null) - lastmsgCustom.forEach(customLMDConsumer); - else - lastmsgCustom.stream().filter(cc -> (cc.toggles & toggle.flag) != 0).forEach(customLMDConsumer); - } - - /** - * Do the {@code action} for each custom chat the {@code sender} have access to and has that broadcast type enabled. - * - * @param action The action to do - * @param sender The sender to check perms of or null to send to all that has it toggled - * @param toggle The toggle to check or null to send to all allowed - */ - public static void forAllowedCustomMCChat(Consumer action, @Nullable CommandSender sender, @Nullable ChannelconBroadcast toggle) { - lastmsgCustom.stream().filter(clmd -> { - //new TBMCChannelConnectFakeEvent(sender, clmd.mcchannel).shouldSendTo(clmd.dcp) - Thought it was this simple hehe - Wait, it *should* be this simple - if (toggle != null && (clmd.toggles & toggle.flag) == 0) - return false; //If null then allow - if (sender == null) - return true; - return clmd.groupID.equals(clmd.mcchannel.getGroupID(sender)); - }).forEach(cc -> action.accept(cc.channel)); //TODO: Send error messages on channel connect - } - - /** - * Do the {@code action} for each custom chat the {@code sender} have access to and has that broadcast type enabled. - * - * @param action The action to do - * @param sender The sender to check perms of or null to send to all that has it toggled - * @param toggle The toggle to check or null to send to all allowed - * @param hookmsg Whether the message is also sent from the hook - */ - public static void forAllowedCustomAndAllMCChat(Consumer action, @Nullable CommandSender sender, @Nullable ChannelconBroadcast toggle, boolean hookmsg) { - if (!DiscordPlugin.hooked || !hookmsg) - forAllMCChat(action); - forAllowedCustomMCChat(action, sender, toggle); - } - - public static Consumer send(String message) { - return ch -> DiscordPlugin.sendMessageToChannel(ch, DPUtils.sanitizeString(message)); - } - - public static void forAllowedMCChat(Consumer action, TBMCSystemChatEvent event) { - if (event.getChannel().isGlobal()) - action.accept(DiscordPlugin.chatchannel); - for (LastMsgData data : lastmsgPerUser) - if (event.shouldSendTo(getSender(data.channel, data.user))) - action.accept(data.channel); - lastmsgCustom.stream().filter(clmd -> { - if ((clmd.toggles & ChannelconBroadcast.BROADCAST.flag) == 0) - return false; - return event.shouldSendTo(clmd.dcp); - }).map(clmd -> clmd.channel).forEach(action); - } - /** * Stop the listener. Any calls to onMCChat will restart it as long as we're not in safe mode. * @@ -445,13 +212,13 @@ public class MCChatListener implements Listener, IListener if (wait) recthread.join(5000); } - lastmsgdata = null; - lastmsgPerUser.clear(); - lastmsgCustom.clear(); - lastmsgfromd.clear(); - ConnectedSenders.clear(); + MCChatUtils.lastmsgdata = null; + MCChatPrivate.lastmsgPerUser.clear(); + MCChatCustom.lastmsgCustom.clear(); + MCChatUtils.lastmsgfromd.clear(); + MCChatUtils.ConnectedSenders.clear(); lastlist = lastlistp = ListC = 0; - UnconnectedSenders.clear(); + MCChatUtils.UnconnectedSenders.clear(); recthread = sendthread = null; } catch (InterruptedException e) { e.printStackTrace(); //This thread shouldn't be interrupted @@ -463,27 +230,28 @@ public class MCChatListener implements Listener, IListener private Runnable recrun; private static Thread recthread; - @Override // Discord - public void handle(MessageReceivedEvent ev) { - if (DiscordPlugin.SafeMode) - return; + // Discord + public boolean handleDiscord(MessageReceivedEvent ev) { + if (!ComponentManager.isEnabled(MinecraftChatModule.class)) + return false; val author = ev.getMessage().getAuthor(); - if (author.isBot()) - return; - final boolean hasCustomChat = hasCustomChat(ev.getChannel()); - if (!ev.getMessage().getChannel().getStringID().equals(DiscordPlugin.chatchannel.getStringID()) - && !(ev.getMessage().getChannel().isPrivate() && isMinecraftChatEnabled(author.getStringID())) + final boolean hasCustomChat = MCChatCustom.hasCustomChat(ev.getChannel()); + if (ev.getMessage().getChannel().getLongID() != DiscordPlugin.chatchannel.getLongID() + && !(ev.getMessage().getChannel().isPrivate() && MCChatPrivate.isMinecraftChatEnabled(author.getStringID())) && !hasCustomChat) - return; - if (ev.getMessage().getContent().equalsIgnoreCase("mcchat")) - return; // Race condition: If it gets here after it enabled mcchat it says it - I might as well allow disabling with this (CommandListener) + return false; //Chat isn't enabled on this channel + if (ev.getMessage().getChannel().isPrivate() //Only in private chat + && ev.getMessage().getContent().length() < "/mcchat<>".length() + && ev.getMessage().getContent().replace("/", "") + .equalsIgnoreCase("mcchat")) //Either mcchat or /mcchat + return false; //Allow disabling the chat if needed if (CommandListener.runCommand(ev.getMessage(), true)) - return; - resetLastMessage(ev.getChannel()); + return true; //Allow running commands in chat channels + MCChatUtils.resetLastMessage(ev.getChannel()); lastlist++; recevents.add(ev); if (rectask != null) - return; + return true; recrun = () -> { //Don't return in a while loop next time recthread = Thread.currentThread(); processDiscordToMC(); @@ -491,6 +259,7 @@ public class MCChatListener implements Listener, IListener rectask = Bukkit.getScheduler().runTaskAsynchronously(DiscordPlugin.plugin, recrun); //Continue message processing }; rectask = Bukkit.getScheduler().runTaskAsynchronously(DiscordPlugin.plugin, recrun); //Start message processing + return true; } private void processDiscordToMC() { @@ -505,7 +274,7 @@ public class MCChatListener implements Listener, IListener val sender = event.getMessage().getAuthor(); String dmessage = event.getMessage().getContent(); try { - final DiscordSenderBase dsender = getSender(event.getMessage().getChannel(), sender); + final DiscordSenderBase dsender = MCChatUtils.getSender(event.getMessage().getChannel(), sender); val user = dsender.getChromaUser(); for (IUser u : event.getMessage().getMentions()) { @@ -525,7 +294,7 @@ public class MCChatListener implements Listener, IListener .getAttachments().stream().map(IMessage.Attachment::getUrl).collect(Collectors.joining("\n")) : ""); - CustomLMD clmd = getCustomChat(event.getChannel()); + MCChatCustom.CustomLMD clmd = MCChatCustom.getCustomChat(event.getChannel()); boolean react = false; @@ -570,18 +339,13 @@ public class MCChatListener implements Listener, IListener () -> { //TODO: Better handling... val channel = user.channel(); val chtmp = channel.get(); - //System.out.println("1: "+chtmp.ID); - //System.out.println("clmd: "+clmd); if (clmd != null) { channel.set(clmd.mcchannel); //Hack to send command in the channel - //System.out.println("clmd chan: "+clmd.mcchannel.ID); } //TODO: Permcheck isn't implemented for commands - //System.out.println("2: "+channel.get().ID); VanillaCommandListener.runBukkitOrVanillaCommand(dsender, cmd); Bukkit.getLogger().info(dsender.getName() + " issued command from Discord: /" + cmdlowercased); if (clmd != null) channel.set(chtmp); - //System.out.println("3: "+channel.get().ID); - TODO: Remove }); else { Channel chc = ch.get(); @@ -619,8 +383,9 @@ public class MCChatListener implements Listener, IListener } else {// Not a command if (dmessage.length() == 0 && event.getMessage().getAttachments().size() == 0 && !event.getChannel().isPrivate() && event.getMessage().isSystemMessage()) { - val rtr = clmd != null ? clmd.mcchannel.filteranderrormsg.apply(clmd.dcp) : dsender.getChromaUser().channel().get().filteranderrormsg.apply(dsender); - TBMCChatAPI.SendSystemMessage(clmd != null ? clmd.mcchannel : dsender.getChromaUser().channel().get(), rtr.score, rtr.groupID, + val rtr = clmd != null ? clmd.mcchannel.getRTR(clmd.dcp) + : dsender.getChromaUser().channel().get().getRTR(dsender); + TBMCChatAPI.SendSystemMessage(clmd != null ? clmd.mcchannel : dsender.getChromaUser().channel().get(), rtr, (dsender instanceof Player ? ((Player) dsender).getDisplayName() : dsender.getName()) + " pinned a message on Discord."); } @@ -635,7 +400,7 @@ public class MCChatListener implements Listener, IListener } if (react) { try { - val lmfd = lastmsgfromd.get(event.getChannel().getLongID()); + val lmfd = MCChatUtils.lastmsgfromd.get(event.getChannel().getLongID()); if (lmfd != null) { DPUtils.perform(() -> lmfd.removeReaction(DiscordPlugin.dc.getOurUser(), DiscordPlugin.DELIVERED_REACTION)); // Remove it no matter what, we know it's there 99.99% of the time @@ -643,7 +408,7 @@ public class MCChatListener implements Listener, IListener } catch (Exception e) { TBMCCoreAPI.SendException("An error occured while removing reactions from chat!", e); } - lastmsgfromd.put(event.getChannel().getLongID(), event.getMessage()); + MCChatUtils.lastmsgfromd.put(event.getChannel().getLongID(), event.getMessage()); DPUtils.perform(() -> event.getMessage().addReaction(DiscordPlugin.DELIVERED_REACTION)); } } catch (Exception e) { @@ -651,19 +416,6 @@ public class MCChatListener implements Listener, IListener } } - /** - * This method will find the best sender to use: if the player is online, use that, if not but connected then use that etc. - */ - private static DiscordSenderBase getSender(IChannel channel, final IUser author) { - //noinspection OptionalGetWithoutIsPresent - return Stream.>>of( // https://stackoverflow.com/a/28833677/2703239 - () -> Optional.ofNullable(getSender(OnlineSenders, channel, author)), // Find first non-null - () -> Optional.ofNullable(getSender(ConnectedSenders, channel, author)), // This doesn't support the public chat, but it'll always return null for it - () -> Optional.ofNullable(getSender(OnlineSenders, channel, author)), // - () -> Optional.of(addSender(UnconnectedSenders, author, - new DiscordSender(author, channel)))).map(Supplier::get).filter(Optional::isPresent).map(Optional::get).findFirst().get(); - } - @FunctionalInterface private interface InterruptibleConsumer { void accept(T value) throws TimeoutException, InterruptedException; diff --git a/src/main/java/buttondevteam/discordplugin/mcchat/MCChatPrivate.java b/src/main/java/buttondevteam/discordplugin/mcchat/MCChatPrivate.java new file mode 100644 index 0000000..99c1740 --- /dev/null +++ b/src/main/java/buttondevteam/discordplugin/mcchat/MCChatPrivate.java @@ -0,0 +1,69 @@ +package buttondevteam.discordplugin.mcchat; + +import buttondevteam.discordplugin.DiscordConnectedPlayer; +import buttondevteam.discordplugin.DiscordPlayer; +import buttondevteam.discordplugin.DiscordPlugin; +import buttondevteam.lib.player.TBMCPlayer; +import lombok.val; +import org.bukkit.Bukkit; +import org.bukkit.event.Event; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.player.PlayerQuitEvent; +import sx.blah.discord.handle.obj.IChannel; +import sx.blah.discord.handle.obj.IPrivateChannel; +import sx.blah.discord.handle.obj.IUser; + +import java.util.ArrayList; + +import static buttondevteam.discordplugin.listeners.MCListener.callEventExcludingSome; + +public class MCChatPrivate { + + /** + * Used for messages in PMs (mcchat). + */ + static ArrayList lastmsgPerUser = new ArrayList<>(); + + public static boolean privateMCChat(IChannel channel, boolean start, IUser user, DiscordPlayer dp) { + TBMCPlayer mcp = dp.getAs(TBMCPlayer.class); + if (mcp != null) { // If the accounts aren't connected, can't make a connected sender + val p = Bukkit.getPlayer(mcp.getUUID()); + val op = Bukkit.getOfflinePlayer(mcp.getUUID()); + if (start) { + val sender = new DiscordConnectedPlayer(user, channel, mcp.getUUID(), op.getName()); + MCChatUtils.addSender(MCChatUtils.ConnectedSenders, user, sender); + if (p == null)// Player is offline - If the player is online, that takes precedence + callEventSync(new PlayerJoinEvent(sender, "")); + } else { + val sender = MCChatUtils.removeSender(MCChatUtils.ConnectedSenders, channel, user); + if (p == null)// Player is offline - If the player is online, that takes precedence + callEventSync(new PlayerQuitEvent(sender, "")); + } + } // ---- PermissionsEx warning is normal on logout ---- + if (!start) + MCChatUtils.lastmsgfromd.remove(channel.getLongID()); + return start // + ? lastmsgPerUser.add(new MCChatUtils.LastMsgData(channel, user)) // Doesn't support group DMs + : lastmsgPerUser.removeIf(lmd -> lmd.channel.getLongID() == channel.getLongID()); + } + + public static boolean isMinecraftChatEnabled(DiscordPlayer dp) { + return isMinecraftChatEnabled(dp.getDiscordID()); + } + + public static boolean isMinecraftChatEnabled(String did) { // Don't load the player data just for this + return lastmsgPerUser.stream() + .anyMatch(lmd -> ((IPrivateChannel) lmd.channel).getRecipient().getStringID().equals(did)); + } + + public static void logoutAll() { + for (val entry : MCChatUtils.ConnectedSenders.entrySet()) + for (val valueEntry : entry.getValue().entrySet()) + callEventExcludingSome(new PlayerQuitEvent(valueEntry.getValue(), "")); //This is sync + MCChatUtils.ConnectedSenders.clear(); + } + + private static void callEventSync(Event event) { + Bukkit.getScheduler().runTask(DiscordPlugin.plugin, () -> callEventExcludingSome(event)); + } +} diff --git a/src/main/java/buttondevteam/discordplugin/mcchat/MCChatUtils.java b/src/main/java/buttondevteam/discordplugin/mcchat/MCChatUtils.java new file mode 100644 index 0000000..6826f6e --- /dev/null +++ b/src/main/java/buttondevteam/discordplugin/mcchat/MCChatUtils.java @@ -0,0 +1,218 @@ +package buttondevteam.discordplugin.mcchat; + +import buttondevteam.core.ComponentManager; +import buttondevteam.discordplugin.*; +import buttondevteam.discordplugin.broadcaster.GeneralEventBroadcasterModule; +import buttondevteam.lib.TBMCSystemChatEvent; +import buttondevteam.lib.chat.Channel; +import io.netty.util.collection.LongObjectHashMap; +import lombok.RequiredArgsConstructor; +import lombok.experimental.var; +import org.bukkit.Bukkit; +import org.bukkit.command.CommandSender; +import sx.blah.discord.handle.obj.IChannel; +import sx.blah.discord.handle.obj.IMessage; +import sx.blah.discord.handle.obj.IUser; + +import javax.annotation.Nullable; +import java.util.HashMap; +import java.util.Optional; +import java.util.function.Consumer; +import java.util.function.Supplier; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class MCChatUtils { + /** + * May contain P<DiscordID> as key for public chat + */ + public static final HashMap> UnconnectedSenders = new HashMap<>(); + public static final HashMap> ConnectedSenders = new HashMap<>(); + /** + * May contain P<DiscordID> as key for public chat + */ + public static final HashMap> OnlineSenders = new HashMap<>(); + static @Nullable LastMsgData lastmsgdata; + static LongObjectHashMap lastmsgfromd = new LongObjectHashMap<>(); // Last message sent by a Discord user, used for clearing checkmarks + + public static void updatePlayerList() { + if (notEnabled()) return; + DPUtils.performNoWait(() -> { + if (lastmsgdata != null) + updatePL(lastmsgdata); + MCChatCustom.lastmsgCustom.forEach(MCChatUtils::updatePL); + }); + } + + private static boolean notEnabled() { + return !ComponentManager.isEnabled(MinecraftChatModule.class); + } + + private static void updatePL(LastMsgData lmd) { + String topic = lmd.channel.getTopic(); + if (topic == null || topic.length() == 0) + topic = ".\n----\nMinecraft chat\n----\n."; + String[] s = topic.split("\\n----\\n"); + if (s.length < 3) + return; + s[0] = Bukkit.getOnlinePlayers().size() + " player" + (Bukkit.getOnlinePlayers().size() != 1 ? "s" : "") + + " online"; + s[s.length - 1] = "Players: " + Bukkit.getOnlinePlayers().stream() + .map(p -> DPUtils.sanitizeString(p.getDisplayName())).collect(Collectors.joining(", ")); + lmd.channel.changeTopic(String.join("\n----\n", s)); + } + + public static T addSender(HashMap> senders, + IUser user, T sender) { + return addSender(senders, user.getStringID(), sender); + } + + public static T addSender(HashMap> senders, + String did, T sender) { + var map = senders.get(did); + if (map == null) + map = new HashMap<>(); + map.put(sender.getChannel(), sender); + senders.put(did, map); + return sender; + } + + public static T getSender(HashMap> senders, + IChannel channel, IUser user) { + var map = senders.get(user.getStringID()); + if (map != null) + return map.get(channel); + return null; + } + + public static T removeSender(HashMap> senders, + IChannel channel, IUser user) { + var map = senders.get(user.getStringID()); + if (map != null) + return map.remove(channel); + return null; + } + + public static void forAllMCChat(Consumer action) { + if (notEnabled()) return; + action.accept(DiscordPlugin.chatchannel); + for (LastMsgData data : MCChatPrivate.lastmsgPerUser) + action.accept(data.channel); + // lastmsgCustom.forEach(cc -> action.accept(cc.channel)); - Only send relevant messages to custom chat + } + + /** + * For custom and all MC chat + * + * @param action The action to act + * @param toggle The toggle to check + * @param hookmsg Whether the message is also sent from the hook + */ + public static void forCustomAndAllMCChat(Consumer action, @Nullable ChannelconBroadcast toggle, boolean hookmsg) { + if (notEnabled()) return; + if (!GeneralEventBroadcasterModule.isHooked() || !hookmsg) + forAllMCChat(action); + final Consumer customLMDConsumer = cc -> action.accept(cc.channel); + if (toggle == null) + MCChatCustom.lastmsgCustom.forEach(customLMDConsumer); + else + MCChatCustom.lastmsgCustom.stream().filter(cc -> (cc.toggles & toggle.flag) != 0).forEach(customLMDConsumer); + } + + /** + * Do the {@code action} for each custom chat the {@code sender} have access to and has that broadcast type enabled. + * + * @param action The action to do + * @param sender The sender to check perms of or null to send to all that has it toggled + * @param toggle The toggle to check or null to send to all allowed + */ + public static void forAllowedCustomMCChat(Consumer action, @Nullable CommandSender sender, @Nullable ChannelconBroadcast toggle) { + if (notEnabled()) return; + MCChatCustom.lastmsgCustom.stream().filter(clmd -> { + //new TBMCChannelConnectFakeEvent(sender, clmd.mcchannel).shouldSendTo(clmd.dcp) - Thought it was this simple hehe - Wait, it *should* be this simple + if (toggle != null && (clmd.toggles & toggle.flag) == 0) + return false; //If null then allow + if (sender == null) + return true; + return clmd.groupID.equals(clmd.mcchannel.getGroupID(sender)); + }).forEach(cc -> action.accept(cc.channel)); //TODO: Send error messages on channel connect + } + + /** + * Do the {@code action} for each custom chat the {@code sender} have access to and has that broadcast type enabled. + * + * @param action The action to do + * @param sender The sender to check perms of or null to send to all that has it toggled + * @param toggle The toggle to check or null to send to all allowed + * @param hookmsg Whether the message is also sent from the hook + */ + public static void forAllowedCustomAndAllMCChat(Consumer action, @Nullable CommandSender sender, @Nullable ChannelconBroadcast toggle, boolean hookmsg) { + if (notEnabled()) return; + if (!GeneralEventBroadcasterModule.isHooked() || !hookmsg) + forAllMCChat(action); + forAllowedCustomMCChat(action, sender, toggle); + } + + public static Consumer send(String message) { + return ch -> DiscordPlugin.sendMessageToChannel(ch, DPUtils.sanitizeString(message)); + } + + public static void forAllowedMCChat(Consumer action, TBMCSystemChatEvent event) { + if (notEnabled()) return; + if (event.getChannel().isGlobal()) + action.accept(DiscordPlugin.chatchannel); + for (LastMsgData data : MCChatPrivate.lastmsgPerUser) + if (event.shouldSendTo(getSender(data.channel, data.user))) + action.accept(data.channel); + MCChatCustom.lastmsgCustom.stream().filter(clmd -> { + if ((clmd.toggles & ChannelconBroadcast.BROADCAST.flag) == 0) + return false; + return event.shouldSendTo(clmd.dcp); + }).map(clmd -> clmd.channel).forEach(action); + } + + /** + * This method will find the best sender to use: if the player is online, use that, if not but connected then use that etc. + */ + static DiscordSenderBase getSender(IChannel channel, final IUser author) { + //noinspection OptionalGetWithoutIsPresent + return Stream.>>of( // https://stackoverflow.com/a/28833677/2703239 + () -> Optional.ofNullable(getSender(OnlineSenders, channel, author)), // Find first non-null + () -> Optional.ofNullable(getSender(ConnectedSenders, channel, author)), // This doesn't support the public chat, but it'll always return null for it + () -> Optional.ofNullable(getSender(UnconnectedSenders, channel, author)), // + () -> Optional.of(addSender(UnconnectedSenders, author, + new DiscordSender(author, channel)))).map(Supplier::get).filter(Optional::isPresent).map(Optional::get).findFirst().get(); + } + + /** + * Resets the last message, so it will start a new one instead of appending to it. + * This is used when someone (even the bot) sends a message to the channel. + * + * @param channel The channel to reset in - the process is slightly different for the public, private and custom chats + */ + public static void resetLastMessage(IChannel channel) { + if (notEnabled()) return; + if (channel.getLongID() == DiscordPlugin.chatchannel.getLongID()) { + (lastmsgdata == null ? lastmsgdata = new LastMsgData(DiscordPlugin.chatchannel, null) + : lastmsgdata).message = null; + return; + } // Don't set the whole object to null, the player and channel information should be preserved + for (LastMsgData data : channel.isPrivate() ? MCChatPrivate.lastmsgPerUser : MCChatCustom.lastmsgCustom) { + if (data.channel.getLongID() == channel.getLongID()) { + data.message = null; + return; + } + } + //If it gets here, it's sending a message to a non-chat channel + } + + @RequiredArgsConstructor + public static class LastMsgData { + public IMessage message; + public long time; + public String content; + public final IChannel channel; + public Channel mcchannel; + public final IUser user; + } +} diff --git a/src/main/java/buttondevteam/discordplugin/mcchat/MCListener.java b/src/main/java/buttondevteam/discordplugin/mcchat/MCListener.java new file mode 100644 index 0000000..2c5fdb0 --- /dev/null +++ b/src/main/java/buttondevteam/discordplugin/mcchat/MCListener.java @@ -0,0 +1,153 @@ +package buttondevteam.discordplugin.mcchat; + +import buttondevteam.discordplugin.*; +import buttondevteam.discordplugin.commands.ConnectCommand; +import buttondevteam.lib.TBMCCoreAPI; +import buttondevteam.lib.TBMCSystemChatEvent; +import buttondevteam.lib.player.*; +import com.earth2me.essentials.CommandSource; +import lombok.val; +import net.ess3.api.events.AfkStatusChangeEvent; +import net.ess3.api.events.MuteStatusChangeEvent; +import net.ess3.api.events.NickChangeEvent; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.PlayerDeathEvent; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.player.PlayerKickEvent; +import org.bukkit.event.player.PlayerLoginEvent; +import org.bukkit.event.player.PlayerLoginEvent.Result; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.event.server.BroadcastMessageEvent; +import sx.blah.discord.handle.obj.IRole; +import sx.blah.discord.handle.obj.IUser; +import sx.blah.discord.util.DiscordException; +import sx.blah.discord.util.MissingPermissionsException; + +class MCListener implements Listener { + @EventHandler(priority = EventPriority.HIGHEST) + public void onPlayerLogin(PlayerLoginEvent e) { + if (e.getResult() != Result.ALLOWED) + return; + MCChatUtils.ConnectedSenders.values().stream().flatMap(v -> v.values().stream()) //Only private mcchat should be in ConnectedSenders + .filter(s -> s.getUniqueId().equals(e.getPlayer().getUniqueId())).findAny() + .ifPresent(dcp -> buttondevteam.discordplugin.listeners.MCListener.callEventExcludingSome(new PlayerQuitEvent(dcp, ""))); + } + + @EventHandler(priority = EventPriority.LOWEST) + public void onPlayerJoin(TBMCPlayerJoinEvent e) { + if (e.getPlayer() instanceof DiscordConnectedPlayer) + return; // Don't show the joined message for the fake player + Bukkit.getScheduler().runTaskAsynchronously(DiscordPlugin.plugin, () -> { + final Player p = e.getPlayer(); + DiscordPlayer dp = e.GetPlayer().getAs(DiscordPlayer.class); + if (dp != null) { + val user = DiscordPlugin.dc.getUserByID(Long.parseLong(dp.getDiscordID())); + MCChatUtils.addSender(MCChatUtils.OnlineSenders, dp.getDiscordID(), + new DiscordPlayerSender(user, user.getOrCreatePMChannel(), p)); + MCChatUtils.addSender(MCChatUtils.OnlineSenders, dp.getDiscordID(), + new DiscordPlayerSender(user, DiscordPlugin.chatchannel, p)); //Stored per-channel + } + if (ConnectCommand.WaitingToConnect.containsKey(e.GetPlayer().PlayerName().get())) { + IUser user = DiscordPlugin.dc + .getUserByID(Long.parseLong(ConnectCommand.WaitingToConnect.get(e.GetPlayer().PlayerName().get()))); + p.sendMessage("§bTo connect with the Discord account @" + user.getName() + "#" + user.getDiscriminator() + + " do /discord accept"); + p.sendMessage("§bIf it wasn't you, do /discord decline"); + } + final String message = e.GetPlayer().PlayerName().get() + " joined the game"; + MCChatUtils.forAllowedCustomAndAllMCChat(MCChatUtils.send(message), e.getPlayer(), ChannelconBroadcast.JOINLEAVE, true); + MCChatListener.ListC = 0; + ChromaBot.getInstance().updatePlayerList(); + }); + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void onPlayerLeave(TBMCPlayerQuitEvent e) { + if (e.getPlayer() instanceof DiscordConnectedPlayer) + return; // Only care about real users + MCChatUtils.OnlineSenders.entrySet() + .removeIf(entry -> entry.getValue().entrySet().stream().anyMatch(p -> p.getValue().getUniqueId().equals(e.getPlayer().getUniqueId()))); + Bukkit.getScheduler().runTask(DiscordPlugin.plugin, + () -> MCChatUtils.ConnectedSenders.values().stream().flatMap(v -> v.values().stream()) + .filter(s -> s.getUniqueId().equals(e.getPlayer().getUniqueId())).findAny() + .ifPresent(dcp -> buttondevteam.discordplugin.listeners.MCListener.callEventExcludingSome(new PlayerJoinEvent(dcp, "")))); + Bukkit.getScheduler().runTaskLaterAsynchronously(DiscordPlugin.plugin, + ChromaBot.getInstance()::updatePlayerList, 5); + final String message = e.GetPlayer().PlayerName().get() + " left the game"; + MCChatUtils.forAllowedCustomAndAllMCChat(MCChatUtils.send(message), e.getPlayer(), ChannelconBroadcast.JOINLEAVE, true); + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void onPlayerKick(PlayerKickEvent e) { + /*if (!DiscordPlugin.hooked && !e.getReason().equals("The server is restarting") + && !e.getReason().equals("Server closed")) // The leave messages errored with the previous setup, I could make it wait since I moved it here, but instead I have a special + MCChatListener.forAllowedCustomAndAllMCChat(e.getPlayer().getName() + " left the game"); // message for this - Oh wait this doesn't even send normally because of the hook*/ + } + + @EventHandler(priority = EventPriority.LOW) + public void onPlayerDeath(PlayerDeathEvent e) { + MCChatUtils.forAllowedCustomAndAllMCChat(MCChatUtils.send(e.getDeathMessage()), e.getEntity(), ChannelconBroadcast.DEATH, true); + } + + @EventHandler + public void onPlayerAFK(AfkStatusChangeEvent e) { + final Player base = e.getAffected().getBase(); + if (e.isCancelled() || !base.isOnline()) + return; + final String msg = base.getDisplayName() + + " is " + (e.getValue() ? "now" : "no longer") + " AFK."; + MCChatUtils.forAllowedCustomAndAllMCChat(MCChatUtils.send(msg), base, ChannelconBroadcast.AFK, false); + } + + @EventHandler + public void onPlayerMute(MuteStatusChangeEvent e) { + try { + DPUtils.performNoWait(() -> { + final IRole role = DiscordPlugin.dc.getRoleByID(164090010461667328L); + final CommandSource source = e.getAffected().getSource(); + if (!source.isPlayer()) + return; + final DiscordPlayer p = TBMCPlayerBase.getPlayer(source.getPlayer().getUniqueId(), TBMCPlayer.class) + .getAs(DiscordPlayer.class); + if (p == null) return; + final IUser user = DiscordPlugin.dc.getUserByID( + Long.parseLong(p.getDiscordID())); + if (e.getValue()) + user.addRole(role); + else + user.removeRole(role); + DiscordPlugin.sendMessageToChannel(DiscordPlugin.modlogchannel, (e.getValue() ? "M" : "Unm") + "uted user: " + user.getName()); + }); + } catch (DiscordException | MissingPermissionsException ex) { + TBMCCoreAPI.SendException("Failed to give/take Muted role to player " + e.getAffected().getName() + "!", + ex); + } + } + + @EventHandler + public void onChatSystemMessage(TBMCSystemChatEvent event) { + MCChatUtils.forAllowedMCChat(MCChatUtils.send(event.getMessage()), event); + } + + @EventHandler + public void onBroadcastMessage(BroadcastMessageEvent event) { + MCChatUtils.forCustomAndAllMCChat(MCChatUtils.send(event.getMessage()), ChannelconBroadcast.BROADCAST, false); + } + + @EventHandler + public void onYEEHAW(TBMCYEEHAWEvent event) { //TODO: Inherit from the chat event base to have channel support + String name = event.getSender() instanceof Player ? ((Player) event.getSender()).getDisplayName() + : event.getSender().getName(); + //Channel channel = ChromaGamerBase.getFromSender(event.getSender()).channel().get(); - TODO + MCChatUtils.forAllMCChat(MCChatUtils.send(name + " <:YEEHAW:" + DiscordPlugin.mainServer.getEmojiByName("YEEHAW").getStringID() + ">s")); + } + + @EventHandler + public void onNickChange(NickChangeEvent event) { + MCChatUtils.updatePlayerList(); + } +} diff --git a/src/main/java/buttondevteam/discordplugin/mcchat/MinecraftChatModule.java b/src/main/java/buttondevteam/discordplugin/mcchat/MinecraftChatModule.java new file mode 100644 index 0000000..9af7a5e --- /dev/null +++ b/src/main/java/buttondevteam/discordplugin/mcchat/MinecraftChatModule.java @@ -0,0 +1,26 @@ +package buttondevteam.discordplugin.mcchat; + +import buttondevteam.discordplugin.DiscordPlugin; +import buttondevteam.lib.TBMCCoreAPI; +import buttondevteam.lib.architecture.Component; +import lombok.Getter; + +public class MinecraftChatModule extends Component { + private @Getter MCChatListener listener; + + public MCChatListener getListener() { //It doesn't want to generate + return listener; + } + @Override + protected void enable() { + listener = new MCChatListener(); + DiscordPlugin.dc.getDispatcher().registerListener(listener); + TBMCCoreAPI.RegisterEventsForExceptions(listener, getPlugin()); + TBMCCoreAPI.RegisterEventsForExceptions(new MCListener(), getPlugin()); + } + + @Override + protected void disable() { + //These get undone if restarting/resetting - it will ignore events if disabled + } //TODO: Use ComponentManager.isEnabled() at other places too, instead of SafeMode +} diff --git a/src/main/java/buttondevteam/discordplugin/mccommands/AcceptMCCommand.java b/src/main/java/buttondevteam/discordplugin/mccommands/AcceptMCCommand.java index f695dba..2f2ad21 100755 --- a/src/main/java/buttondevteam/discordplugin/mccommands/AcceptMCCommand.java +++ b/src/main/java/buttondevteam/discordplugin/mccommands/AcceptMCCommand.java @@ -2,7 +2,7 @@ package buttondevteam.discordplugin.mccommands; import buttondevteam.discordplugin.DiscordPlayer; import buttondevteam.discordplugin.commands.ConnectCommand; -import buttondevteam.discordplugin.listeners.MCChatListener; +import buttondevteam.discordplugin.mcchat.MCChatUtils; import buttondevteam.lib.chat.CommandClass; import buttondevteam.lib.player.ChromaGamerBase; import buttondevteam.lib.player.TBMCPlayer; @@ -35,7 +35,7 @@ public class AcceptMCCommand extends DiscordMCCommandBase { dp.save(); mcp.save(); ConnectCommand.WaitingToConnect.remove(player.getName()); - MCChatListener.UnconnectedSenders.remove(did); //Remove all unconnected, will be recreated where needed + MCChatUtils.UnconnectedSenders.remove(did); //Remove all unconnected, will be recreated where needed player.sendMessage("§bAccounts connected."); return true; }