diff --git a/src/main/java/buttondevteam/discordplugin/AnnouncerModule.java b/src/main/java/buttondevteam/discordplugin/AnnouncerModule.java new file mode 100644 index 0000000..8821b94 --- /dev/null +++ b/src/main/java/buttondevteam/discordplugin/AnnouncerModule.java @@ -0,0 +1,135 @@ +package buttondevteam.discordplugin; + +import buttondevteam.lib.TBMCCoreAPI; +import buttondevteam.lib.architecture.Component; +import buttondevteam.lib.architecture.ConfigData; +import buttondevteam.lib.player.ChromaGamerBase; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import lombok.val; +import sx.blah.discord.handle.obj.IChannel; +import sx.blah.discord.handle.obj.IMessage; + +import java.util.List; + +public class AnnouncerModule extends Component { + public ConfigData channel() { + return DPUtils.channelData(getConfig(), "channel", 239519012529111040L); + } + + public ConfigData modChannel() { + return DPUtils.channelData(getConfig(), "modChannel", 239519012529111040L); + } + + /** + * Set to 0 or >50 to disable + */ + public ConfigData keepPinned() { + return getConfig().getData("keepPinned", (short) 40, i -> ((Integer) i).shortValue(), Short::intValue); + } + + private ConfigData lastannouncementtime() { + return getConfig().getData("lastAnnouncementTime", 0L); + } + + private ConfigData lastseentime() { + return getConfig().getData("lastSeenTime", 0L); + } + + private static final String SubredditURL = "https://www.reddit.com/r/ChromaGamers"; + private static boolean stop = false; + + @Override + protected void enable() { + stop = false; //If not the first time + DPUtils.performNoWait(() -> { + try { + val keepPinned = keepPinned().get(); + if (keepPinned == 0) return; + val channel = channel().get(); + List msgs = channel.getPinnedMessages(); + for (int i = msgs.size() - 1; i >= keepPinned; i--) { // Unpin all pinned messages except the newest 10 + channel.unpin(msgs.get(i)); + Thread.sleep(10); + } + } catch (InterruptedException ignore) { + } + }); + if (lastannouncementtime().get() == 0) //Load old data + lastannouncementtime().set(getConfig().getConfig().getRoot().getLong("lastannouncementtime")); + if (lastseentime().get() == 0) + lastseentime().set(getConfig().getConfig().getLong("lastseentime")); + new Thread(this::AnnouncementGetterThreadMethod).start(); + } + + @Override + protected void disable() { + stop = true; + } + + private void AnnouncementGetterThreadMethod() { + while (!stop) { + try { + if (!isEnabled()) { + Thread.sleep(10000); + continue; + } + String body = TBMCCoreAPI.DownloadString(SubredditURL + "/new/.json?limit=10"); + JsonArray json = new JsonParser().parse(body).getAsJsonObject().get("data").getAsJsonObject() + .get("children").getAsJsonArray(); + StringBuilder msgsb = new StringBuilder(); + StringBuilder modmsgsb = new StringBuilder(); + long lastanntime = lastannouncementtime().get(); + for (int i = json.size() - 1; i >= 0; i--) { + JsonObject item = json.get(i).getAsJsonObject(); + final JsonObject data = item.get("data").getAsJsonObject(); + String author = data.get("author").getAsString(); + JsonElement distinguishedjson = data.get("distinguished"); + String distinguished; + if (distinguishedjson.isJsonNull()) + distinguished = null; + else + distinguished = distinguishedjson.getAsString(); + String permalink = "https://www.reddit.com" + data.get("permalink").getAsString(); + long date = data.get("created_utc").getAsLong(); + if (date > lastseentime().get()) + lastseentime().set(date); + else if (date > lastannouncementtime().get()) { + do { + val reddituserclass = ChromaGamerBase.getTypeForFolder("reddit"); + if (reddituserclass == null) + break; + val user = ChromaGamerBase.getUser(author, reddituserclass); + String id = user.getConnectedID(DiscordPlayer.class); + if (id != null) + author = "<@" + id + ">"; + } while (false); + if (!author.startsWith("<")) + author = "/u/" + author; + (distinguished != null && distinguished.equals("moderator") ? modmsgsb : msgsb) + .append("A new post was submitted to the subreddit by ").append(author).append("\n") + .append(permalink).append("\n"); + lastanntime = date; + } + } + if (msgsb.length() > 0) + channel().get().pin(DiscordPlugin.sendMessageToChannelWait(channel().get(), msgsb.toString())); + if (modmsgsb.length() > 0) + DiscordPlugin.sendMessageToChannel(modChannel().get(), modmsgsb.toString()); + if (lastannouncementtime().get() != lastanntime) { + lastannouncementtime().set(lastanntime); // If sending succeeded + getPlugin().saveConfig(); //TODO: Won't be needed if I implement auto-saving + } + } catch (Exception e) { + e.printStackTrace(); + } + try { + Thread.sleep(10000); + } catch (InterruptedException ex) { + Thread.currentThread().interrupt(); + } + } + } +} diff --git a/src/main/java/buttondevteam/discordplugin/DiscordPlugin.java b/src/main/java/buttondevteam/discordplugin/DiscordPlugin.java index 557ecd4..6431712 100755 --- a/src/main/java/buttondevteam/discordplugin/DiscordPlugin.java +++ b/src/main/java/buttondevteam/discordplugin/DiscordPlugin.java @@ -1,14 +1,16 @@ package buttondevteam.discordplugin; -import buttondevteam.component.channel.Channel; import buttondevteam.discordplugin.broadcaster.GeneralEventBroadcasterModule; import buttondevteam.discordplugin.commands.DiscordCommandBase; import buttondevteam.discordplugin.exceptions.ExceptionListenerModule; import buttondevteam.discordplugin.listeners.CommonListeners; import buttondevteam.discordplugin.listeners.MCListener; -import buttondevteam.discordplugin.mcchat.*; +import buttondevteam.discordplugin.mcchat.MCChatPrivate; +import buttondevteam.discordplugin.mcchat.MCChatUtils; +import buttondevteam.discordplugin.mcchat.MinecraftChatModule; import buttondevteam.discordplugin.mccommands.DiscordMCCommandBase; import buttondevteam.discordplugin.mccommands.ResetMCCommand; +import buttondevteam.discordplugin.role.GameRoleModule; import buttondevteam.lib.TBMCCoreAPI; import buttondevteam.lib.architecture.ButtonPlugin; import buttondevteam.lib.architecture.Component; @@ -16,10 +18,6 @@ import buttondevteam.lib.architecture.ConfigData; import buttondevteam.lib.chat.TBMCChatAPI; import buttondevteam.lib.player.ChromaGamerBase; import com.google.common.io.Files; -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; import lombok.val; import net.milkbowl.vault.permission.Permission; import org.bukkit.Bukkit; @@ -39,22 +37,17 @@ import sx.blah.discord.util.RequestBuffer; import java.awt.*; import java.io.File; import java.nio.charset.StandardCharsets; -import java.util.List; import java.util.Optional; -import java.util.UUID; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.stream.Collectors; 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 ConfigData Prefix() { + public ConfigData Prefix() { return getIConfig().getData("prefix", '/', str -> ((String) str).charAt(0), Object::toString); } @@ -73,12 +66,9 @@ public class DiscordPlugin extends ButtonPlugin implements IListener @Override public void pluginEnable() { - stop = false; //If not the first time try { Bukkit.getLogger().info("Initializing DiscordPlugin..."); plugin = this; - lastannouncementtime = getConfig().getLong("lastannouncementtime"); - lastseentime = getConfig().getLong("lastseentime"); ClientBuilder cb = new ClientBuilder(); cb.withToken(Files.readFirstLine(new File("TBMC", "Token.txt"), StandardCharsets.UTF_8)); dc = cb.login(); @@ -148,31 +138,10 @@ public class DiscordPlugin extends ButtonPlugin implements IListener if (task != null) task.cancel(); if (!sent) { - new ChromaBot(this).updatePlayerList(); - GameRoles = mainServer.getRoles().stream().filter(this::isGameRole).map(IRole::getName).collect(Collectors.toList()); - - val chcons = getConfig().getConfigurationSection("chcons"); - if (chcons != null) { - val chconkeys = chcons.getKeys(false); - for (val chconkey : chconkeys) { - val chcon = chcons.getConfigurationSection(chconkey); - val mcch = Channel.getChannels().filter(ch -> ch.ID.equals(chcon.getString("mcchid"))).findAny(); - val ch = dc.getChannelByID(chcon.getLong("chid")); - val did = chcon.getLong("did"); - val user = dc.fetchUser(did); - val groupid = chcon.getString("groupid"); - val toggles = chcon.getInt("toggles"); - if (!mcch.isPresent() || ch == null || user == null || groupid == null) - continue; - Bukkit.getScheduler().runTask(this, () -> { //<-- Needed because of occasional ConcurrentModificationExceptions when creating the player (PermissibleBase) - val dcp = new DiscordConnectedPlayer(user, ch, UUID.fromString(chcon.getString("mcuid")), chcon.getString("mcname")); - MCChatCustom.addCustomChat(ch, groupid, mcch.get(), user, dcp, toggles); - }); - } - } + new ChromaBot(this).updatePlayerList(); //Initialize ChromaBot - The MCCHatModule is tested to be enabled DiscordCommandBase.registerCommands(); - if (ResetMCCommand.resetting) + if (ResetMCCommand.resetting) //These will only execute if the chat is enabled ChromaBot.getInstance().sendMessageCustomAsWell("", new EmbedBuilder().withColor(Color.CYAN) .withTitle("Discord plugin restarted - chat connected.").build(), ChannelconBroadcast.RESTART); //Really important to note the chat, hmm else if (getConfig().getBoolean("serverup", false)) { @@ -190,16 +159,6 @@ public class DiscordPlugin extends ButtonPlugin implements IListener getConfig().set("serverup", true); saveConfig(); - DPUtils.performNoWait(() -> { - try { - List msgs = genchannel.getPinnedMessages(); - for (int i = msgs.size() - 1; i >= 10; i--) { // Unpin all pinned messages except the newest 10 - genchannel.unpin(msgs.get(i)); - Thread.sleep(10); - } - } catch (InterruptedException ignore) { - } - }); sent = true; if (TBMCCoreAPI.IsTestServer() && !dc.getOurUser().getName().toLowerCase().contains("test")) { TBMCCoreAPI.SendException( @@ -228,52 +187,29 @@ public class DiscordPlugin extends ButtonPlugin implements IListener Component.registerComponent(this, new GeneralEventBroadcasterModule()); Component.registerComponent(this, new MinecraftChatModule()); Component.registerComponent(this, new ExceptionListenerModule()); + Component.registerComponent(this, new GameRoleModule()); + Component.registerComponent(this, new AnnouncerModule()); TBMCCoreAPI.RegisterEventsForExceptions(new MCListener(), this); TBMCChatAPI.AddCommands(this, DiscordMCCommandBase.class); TBMCCoreAPI.RegisterUserClass(DiscordPlayer.class); ChromaGamerBase.addConverter(sender -> Optional.ofNullable(sender instanceof DiscordSenderBase ? ((DiscordSenderBase) sender).getChromaUser() : null)); - new Thread(this::AnnouncementGetterThreadMethod).start(); setupProviders(); } catch (Exception e) { TBMCCoreAPI.SendException("An error occured while enabling DiscordPlugin!", e); } } - public boolean isGameRole(IRole r) { - if (r.getGuild().getLongID() != mainServer.getLongID()) - return false; //Only allow on the main server - val rc = new Color(149, 165, 166, 0); - return r.getColor().equals(rc) - && r.getPosition() < mainServer.getRoleByID(234343495735836672L).getPosition(); //Below the ChromaBot role - } - - /** + /** * Always true, except when running "stop" from console */ public static boolean Restart; @Override public void pluginDisable() { - stop = true; MCChatPrivate.logoutAll(); - getConfig().set("lastannouncementtime", lastannouncementtime); - getConfig().set("lastseentime", lastseentime); getConfig().set("serverup", false); - val chcons = MCChatCustom.getCustomChats(); - val chconsc = getConfig().createSection("chcons"); - for (val chcon : chcons) { - val chconc = chconsc.createSection(chcon.channel.getStringID()); - chconc.set("mcchid", chcon.mcchannel.ID); - chconc.set("chid", chcon.channel.getLongID()); - chconc.set("did", chcon.user.getLongID()); - chconc.set("mcuid", chcon.dcp.getUniqueId().toString()); - chconc.set("mcname", chcon.dcp.getName()); - chconc.set("groupid", chcon.groupID); - chconc.set("toggles", chcon.toggles); - } - saveConfig(); EmbedObject embed; if (ResetMCCommand.resetting) @@ -301,7 +237,6 @@ public class DiscordPlugin extends ButtonPlugin implements IListener ChromaBot.getInstance().updatePlayerList(); try { SafeMode = true; // Stop interacting with Discord - MCChatListener.stop(true); ChromaBot.delete(); dc.changePresence(StatusType.IDLE, ActivityType.PLAYING, "Chromacraft"); //No longer using the same account for testing dc.logout(); @@ -312,76 +247,8 @@ public class DiscordPlugin extends ButtonPlugin implements IListener } } - private long lastannouncementtime = 0; - private long lastseentime = 0; public static final ReactionEmoji DELIVERED_REACTION = ReactionEmoji.of("✅"); - private void AnnouncementGetterThreadMethod() { - while (!stop) { - try { - if (SafeMode) { - Thread.sleep(10000); - continue; - } - String body = TBMCCoreAPI.DownloadString(SubredditURL + "/new/.json?limit=10"); - JsonArray json = new JsonParser().parse(body).getAsJsonObject().get("data").getAsJsonObject() - .get("children").getAsJsonArray(); - StringBuilder msgsb = new StringBuilder(); - StringBuilder modmsgsb = new StringBuilder(); - long lastanntime = lastannouncementtime; - for (int i = json.size() - 1; i >= 0; i--) { - JsonObject item = json.get(i).getAsJsonObject(); - final JsonObject data = item.get("data").getAsJsonObject(); - String author = data.get("author").getAsString(); - JsonElement distinguishedjson = data.get("distinguished"); - String distinguished; - if (distinguishedjson.isJsonNull()) - distinguished = null; - else - distinguished = distinguishedjson.getAsString(); - String permalink = "https://www.reddit.com" + data.get("permalink").getAsString(); - long date = data.get("created_utc").getAsLong(); - if (date > lastseentime) - lastseentime = date; - else if (date > lastannouncementtime) { - do { - val reddituserclass = ChromaGamerBase.getTypeForFolder("reddit"); - if (reddituserclass == null) - break; - val user = ChromaGamerBase.getUser(author, reddituserclass); - String id = user.getConnectedID(DiscordPlayer.class); - if (id != null) - author = "<@" + id + ">"; - } while (false); - if (!author.startsWith("<")) - author = "/u/" + author; - (distinguished != null && distinguished.equals("moderator") ? modmsgsb : msgsb) - .append("A new post was submitted to the subreddit by ").append(author).append("\n") - .append(permalink).append("\n"); - lastanntime = date; - } - } - if (msgsb.length() > 0) - genchannel.pin(sendMessageToChannelWait(genchannel, msgsb.toString())); - if (modmsgsb.length() > 0) - sendMessageToChannel(annchannel, modmsgsb.toString()); - if (lastannouncementtime != lastanntime) { - lastannouncementtime = lastanntime; // If sending succeeded - getConfig().set("lastannouncementtime", lastannouncementtime); - getConfig().set("lastseentime", lastseentime); - saveConfig(); - } - } catch (Exception e) { - e.printStackTrace(); - } - try { - Thread.sleep(10000); - } catch (InterruptedException ex) { - Thread.currentThread().interrupt(); - } - } - } - public static void sendMessageToChannel(IChannel channel, String message) { sendMessageToChannel(channel, message, null); } diff --git a/src/main/java/buttondevteam/discordplugin/commands/ChannelconCommand.java b/src/main/java/buttondevteam/discordplugin/commands/ChannelconCommand.java index 8d20eb9..df9398a 100644 --- a/src/main/java/buttondevteam/discordplugin/commands/ChannelconCommand.java +++ b/src/main/java/buttondevteam/discordplugin/commands/ChannelconCommand.java @@ -1,6 +1,6 @@ package buttondevteam.discordplugin.commands; -import buttondevteam.component.channel.Channel; +import buttondevteam.core.component.channel.Channel; import buttondevteam.discordplugin.*; import buttondevteam.discordplugin.mcchat.MCChatCustom; import buttondevteam.lib.player.TBMCPlayer; @@ -38,6 +38,7 @@ public class ChannelconCommand extends DiscordCommandBase { } if (args.toLowerCase().startsWith("toggle")) { val cc = MCChatCustom.getCustomChat(message.getChannel()); + assert cc != null; //It's not null 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) { diff --git a/src/main/java/buttondevteam/discordplugin/listeners/MCListener.java b/src/main/java/buttondevteam/discordplugin/listeners/MCListener.java index a39d5fc..2f99b56 100755 --- a/src/main/java/buttondevteam/discordplugin/listeners/MCListener.java +++ b/src/main/java/buttondevteam/discordplugin/listeners/MCListener.java @@ -1,103 +1,116 @@ -package buttondevteam.discordplugin.listeners; - -import buttondevteam.discordplugin.DiscordPlayer; -import buttondevteam.discordplugin.DiscordPlugin; -import buttondevteam.lib.player.TBMCPlayerGetInfoEvent; -import lombok.val; -import org.bukkit.Bukkit; -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.IUser; - -import java.util.Arrays; -import java.util.logging.Level; - -public class MCListener implements Listener { - @EventHandler - public void onGetInfo(TBMCPlayerGetInfoEvent e) { - if (DiscordPlugin.SafeMode) - return; - DiscordPlayer dp = e.getPlayer().getAs(DiscordPlayer.class); - if (dp == null || dp.getDiscordID() == null || dp.getDiscordID().equals("")) - return; - IUser user = DiscordPlugin.dc.getUserByID(Long.parseLong(dp.getDiscordID())); - e.addInfo("Discord tag: " + user.getName() + "#" + user.getDiscriminator()); - e.addInfo(user.getPresence().getStatus().toString()); - if (user.getPresence().getActivity().isPresent() && user.getPresence().getText().isPresent()) - e.addInfo(user.getPresence().getActivity().get() + ": " + user.getPresence().getText().get()); - } - - @EventHandler - public void onServerCommand(ServerCommandEvent e) { - DiscordPlugin.Restart = !e.getCommand().equalsIgnoreCase("stop"); // The variable is always true except if stopped - } - - private static final String[] EXCLUDED_PLUGINS = {"ProtocolLib", "LibsDisguises", "JourneyMapServer"}; //TODO: Make configurable - - public static void callEventExcludingSome(Event event) { - callEventExcluding(event, false, EXCLUDED_PLUGINS); - } - - /** - * 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); - } - } - } - - 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(); - - for (RegisteredListener registration : listeners) { - if (!registration.getPlugin().isEnabled() - || Arrays.stream(plugins).anyMatch(p -> only ^ 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); - } - } - } -} +package buttondevteam.discordplugin.listeners; + +import buttondevteam.discordplugin.DiscordPlayer; +import buttondevteam.discordplugin.DiscordPlugin; +import buttondevteam.discordplugin.commands.ConnectCommand; +import buttondevteam.lib.player.TBMCPlayerGetInfoEvent; +import buttondevteam.lib.player.TBMCPlayerJoinEvent; +import lombok.val; +import org.bukkit.Bukkit; +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.IUser; + +import java.util.Arrays; +import java.util.logging.Level; + +public class MCListener implements Listener { + @EventHandler + public void onPlayerJoin(TBMCPlayerJoinEvent e) { + if (ConnectCommand.WaitingToConnect.containsKey(e.GetPlayer().PlayerName().get())) { + @SuppressWarnings("ConstantConditions") IUser user = DiscordPlugin.dc + .getUserByID(Long.parseLong(ConnectCommand.WaitingToConnect.get(e.GetPlayer().PlayerName().get()))); + e.getPlayer().sendMessage("§bTo connect with the Discord account @" + user.getName() + "#" + user.getDiscriminator() + + " do /discord accept"); + e.getPlayer().sendMessage("§bIf it wasn't you, do /discord decline"); + } + } + + @EventHandler + public void onGetInfo(TBMCPlayerGetInfoEvent e) { + if (DiscordPlugin.SafeMode) + return; + DiscordPlayer dp = e.getPlayer().getAs(DiscordPlayer.class); + if (dp == null || dp.getDiscordID() == null || dp.getDiscordID().equals("")) + return; + IUser user = DiscordPlugin.dc.getUserByID(Long.parseLong(dp.getDiscordID())); + e.addInfo("Discord tag: " + user.getName() + "#" + user.getDiscriminator()); + e.addInfo(user.getPresence().getStatus().toString()); + if (user.getPresence().getActivity().isPresent() && user.getPresence().getText().isPresent()) + e.addInfo(user.getPresence().getActivity().get() + ": " + user.getPresence().getText().get()); + } + + @EventHandler + public void onServerCommand(ServerCommandEvent e) { + DiscordPlugin.Restart = !e.getCommand().equalsIgnoreCase("stop"); // The variable is always true except if stopped + } + + private static final String[] EXCLUDED_PLUGINS = {"ProtocolLib", "LibsDisguises", "JourneyMapServer"}; //TODO: Make configurable + + public static void callEventExcludingSome(Event event) { + callEventExcluding(event, false, EXCLUDED_PLUGINS); + } + + /** + * 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); + } + } + } + + 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(); + + for (RegisteredListener registration : listeners) { + if (!registration.getPlugin().isEnabled() + || Arrays.stream(plugins).anyMatch(p -> only ^ 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); + } + } + } +} diff --git a/src/main/java/buttondevteam/discordplugin/mcchat/MCChatCustom.java b/src/main/java/buttondevteam/discordplugin/mcchat/MCChatCustom.java index 44fe077..fe0769a 100644 --- a/src/main/java/buttondevteam/discordplugin/mcchat/MCChatCustom.java +++ b/src/main/java/buttondevteam/discordplugin/mcchat/MCChatCustom.java @@ -1,6 +1,6 @@ package buttondevteam.discordplugin.mcchat; -import buttondevteam.component.channel.Channel; +import buttondevteam.core.component.channel.Channel; import buttondevteam.discordplugin.DiscordConnectedPlayer; import lombok.NonNull; import lombok.val; diff --git a/src/main/java/buttondevteam/discordplugin/mcchat/MCChatListener.java b/src/main/java/buttondevteam/discordplugin/mcchat/MCChatListener.java index bd9a6d1..6c90870 100755 --- a/src/main/java/buttondevteam/discordplugin/mcchat/MCChatListener.java +++ b/src/main/java/buttondevteam/discordplugin/mcchat/MCChatListener.java @@ -1,8 +1,8 @@ package buttondevteam.discordplugin.mcchat; -import buttondevteam.component.channel.Channel; -import buttondevteam.component.channel.ChatRoom; import buttondevteam.core.ComponentManager; +import buttondevteam.core.component.channel.Channel; +import buttondevteam.core.component.channel.ChatRoom; import buttondevteam.discordplugin.DPUtils; import buttondevteam.discordplugin.DiscordPlugin; import buttondevteam.discordplugin.DiscordSender; @@ -128,9 +128,9 @@ public class MCChatListener implements Listener { || ((DiscordSenderBase) e.getSender()).getChannel().getLongID() != ch.getLongID(); if (e.getChannel().isGlobal() - && (e.isFromCommand() || isdifferentchannel.test(DiscordPlugin.chatchannel))) + && (e.isFromCommand() || isdifferentchannel.test(module.chatChannel().get()))) doit.accept(MCChatUtils.lastmsgdata == null - ? MCChatUtils.lastmsgdata = new MCChatUtils.LastMsgData(DiscordPlugin.chatchannel, null) + ? MCChatUtils.lastmsgdata = new MCChatUtils.LastMsgData(module.chatChannel().get(), null) : MCChatUtils.lastmsgdata); for (MCChatUtils.LastMsgData data : MCChatPrivate.lastmsgPerUser) { @@ -234,7 +234,7 @@ public class MCChatListener implements Listener { return false; val author = ev.getMessage().getAuthor(); final boolean hasCustomChat = MCChatCustom.hasCustomChat(ev.getChannel()); - if (ev.getMessage().getChannel().getLongID() != DiscordPlugin.chatchannel.getLongID() + if (ev.getMessage().getChannel().getLongID() != module.chatChannel().get().getLongID() && !(ev.getMessage().getChannel().isPrivate() && MCChatPrivate.isMinecraftChatEnabled(author.getStringID())) && !hasCustomChat) return false; //Chat isn't enabled on this channel diff --git a/src/main/java/buttondevteam/discordplugin/mcchat/MCChatUtils.java b/src/main/java/buttondevteam/discordplugin/mcchat/MCChatUtils.java index c246ddb..cd45dbe 100644 --- a/src/main/java/buttondevteam/discordplugin/mcchat/MCChatUtils.java +++ b/src/main/java/buttondevteam/discordplugin/mcchat/MCChatUtils.java @@ -1,7 +1,7 @@ package buttondevteam.discordplugin.mcchat; -import buttondevteam.component.channel.Channel; import buttondevteam.core.ComponentManager; +import buttondevteam.core.component.channel.Channel; import buttondevteam.discordplugin.*; import buttondevteam.discordplugin.broadcaster.GeneralEventBroadcasterModule; import buttondevteam.lib.TBMCSystemChatEvent; @@ -34,6 +34,7 @@ public class MCChatUtils { 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 + private static MinecraftChatModule module; public static void updatePlayerList() { if (notEnabled()) return; @@ -45,7 +46,13 @@ public class MCChatUtils { } private static boolean notEnabled() { - return !ComponentManager.isEnabled(MinecraftChatModule.class); + return getModule() == null; + } + + private static MinecraftChatModule getModule() { + if (module == null) module = ComponentManager.getIfEnabled(MinecraftChatModule.class); + else if (!module.isEnabled()) module = null; //Reset if disabled + return module; } private static void updatePL(LastMsgData lmd) { @@ -95,7 +102,7 @@ public class MCChatUtils { public static void forAllMCChat(Consumer action) { if (notEnabled()) return; - action.accept(DiscordPlugin.chatchannel); + action.accept(module.chatChannel().get()); for (LastMsgData data : MCChatPrivate.lastmsgPerUser) action.accept(data.channel); // lastmsgCustom.forEach(cc -> action.accept(cc.channel)); - Only send relevant messages to custom chat @@ -160,7 +167,7 @@ public class MCChatUtils { public static void forAllowedMCChat(Consumer action, TBMCSystemChatEvent event) { if (notEnabled()) return; if (event.getChannel().isGlobal()) - action.accept(DiscordPlugin.chatchannel); + action.accept(module.chatChannel().get()); for (LastMsgData data : MCChatPrivate.lastmsgPerUser) if (event.shouldSendTo(getSender(data.channel, data.user))) action.accept(data.channel); @@ -192,8 +199,8 @@ public class MCChatUtils { */ public static void resetLastMessage(IChannel channel) { if (notEnabled()) return; - if (channel.getLongID() == DiscordPlugin.chatchannel.getLongID()) { - (lastmsgdata == null ? lastmsgdata = new LastMsgData(DiscordPlugin.chatchannel, null) + if (channel.getLongID() == module.chatChannel().get().getLongID()) { + (lastmsgdata == null ? lastmsgdata = new LastMsgData(module.chatChannel().get(), null) : lastmsgdata).message = null; return; } // Don't set the whole object to null, the player and channel information should be preserved diff --git a/src/main/java/buttondevteam/discordplugin/mcchat/MCListener.java b/src/main/java/buttondevteam/discordplugin/mcchat/MCListener.java index 35abeac..ca34d8c 100644 --- a/src/main/java/buttondevteam/discordplugin/mcchat/MCListener.java +++ b/src/main/java/buttondevteam/discordplugin/mcchat/MCListener.java @@ -1,7 +1,6 @@ package buttondevteam.discordplugin.mcchat; import buttondevteam.discordplugin.*; -import buttondevteam.discordplugin.commands.ConnectCommand; import buttondevteam.lib.TBMCCoreAPI; import buttondevteam.lib.TBMCSystemChatEvent; import buttondevteam.lib.player.*; @@ -51,13 +50,6 @@ class MCListener implements Listener { 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); ChromaBot.getInstance().updatePlayerList(); @@ -106,7 +98,7 @@ class MCListener implements Listener { public void onPlayerMute(MuteStatusChangeEvent e) { try { DPUtils.performNoWait(() -> { - final IRole role = DiscordPlugin.dc.getRoleByID(164090010461667328L); + final IRole role = DiscordPlugin.dc.getRoleByID(164090010461667328L); //TODO: Config final CommandSource source = e.getAffected().getSource(); if (!source.isPlayer()) return; diff --git a/src/main/java/buttondevteam/discordplugin/mcchat/MinecraftChatModule.java b/src/main/java/buttondevteam/discordplugin/mcchat/MinecraftChatModule.java index 46b1c61..55d68d6 100644 --- a/src/main/java/buttondevteam/discordplugin/mcchat/MinecraftChatModule.java +++ b/src/main/java/buttondevteam/discordplugin/mcchat/MinecraftChatModule.java @@ -1,13 +1,20 @@ package buttondevteam.discordplugin.mcchat; +import buttondevteam.core.component.channel.Channel; +import buttondevteam.discordplugin.DPUtils; +import buttondevteam.discordplugin.DiscordConnectedPlayer; import buttondevteam.discordplugin.DiscordPlugin; import buttondevteam.lib.TBMCCoreAPI; import buttondevteam.lib.architecture.Component; import buttondevteam.lib.architecture.ConfigData; import com.google.common.collect.Lists; import lombok.Getter; +import lombok.val; +import org.bukkit.Bukkit; +import sx.blah.discord.handle.obj.IChannel; import java.util.ArrayList; +import java.util.UUID; public class MinecraftChatModule extends Component { private @Getter MCChatListener listener; @@ -21,16 +28,54 @@ public class MinecraftChatModule extends Component { "yeehaw", "lenny", "rp", "plugins")); } + public ConfigData chatChannel() { + return DPUtils.channelData(getConfig(), "chatChannel", 239519012529111040L); + } + @Override protected void enable() { listener = new MCChatListener(this); DiscordPlugin.dc.getDispatcher().registerListener(listener); TBMCCoreAPI.RegisterEventsForExceptions(listener, getPlugin()); - TBMCCoreAPI.RegisterEventsForExceptions(new MCListener(), getPlugin()); + TBMCCoreAPI.RegisterEventsForExceptions(new MCListener(), getPlugin());//These get undone if restarting/resetting - it will ignore events if disabled + + val chcons = getConfig().getConfig().getConfigurationSection("chcons"); + if (chcons == null) //Fallback to old place + getConfig().getConfig().getRoot().getConfigurationSection("chcons"); + if (chcons != null) { + val chconkeys = chcons.getKeys(false); + for (val chconkey : chconkeys) { + val chcon = chcons.getConfigurationSection(chconkey); + val mcch = Channel.getChannels().filter(ch -> ch.ID.equals(chcon.getString("mcchid"))).findAny(); + val ch = DiscordPlugin.dc.getChannelByID(chcon.getLong("chid")); + val did = chcon.getLong("did"); + val user = DiscordPlugin.dc.fetchUser(did); + val groupid = chcon.getString("groupid"); + val toggles = chcon.getInt("toggles"); + if (!mcch.isPresent() || ch == null || user == null || groupid == null) + continue; + Bukkit.getScheduler().runTask(getPlugin(), () -> { //<-- Needed because of occasional ConcurrentModificationExceptions when creating the player (PermissibleBase) + val dcp = new DiscordConnectedPlayer(user, ch, UUID.fromString(chcon.getString("mcuid")), chcon.getString("mcname")); + MCChatCustom.addCustomChat(ch, groupid, mcch.get(), user, dcp, toggles); + }); + } + } } @Override protected void disable() { - //These get undone if restarting/resetting - it will ignore events if disabled + val chcons = MCChatCustom.getCustomChats(); + val chconsc = getConfig().getConfig().createSection("chcons"); + for (val chcon : chcons) { + val chconc = chconsc.createSection(chcon.channel.getStringID()); + chconc.set("mcchid", chcon.mcchannel.ID); + chconc.set("chid", chcon.channel.getLongID()); + chconc.set("did", chcon.user.getLongID()); + chconc.set("mcuid", chcon.dcp.getUniqueId().toString()); + chconc.set("mcname", chcon.dcp.getName()); + chconc.set("groupid", chcon.groupID); + chconc.set("toggles", chcon.toggles); + } + MCChatListener.stop(true); } //TODO: Use ComponentManager.isEnabled() at other places too, instead of SafeMode } diff --git a/src/main/java/buttondevteam/discordplugin/role/GameRoleModule.java b/src/main/java/buttondevteam/discordplugin/role/GameRoleModule.java index 0587cfc..7bf3d2b 100644 --- a/src/main/java/buttondevteam/discordplugin/role/GameRoleModule.java +++ b/src/main/java/buttondevteam/discordplugin/role/GameRoleModule.java @@ -13,11 +13,19 @@ import sx.blah.discord.handle.impl.events.guild.role.RoleDeleteEvent; import sx.blah.discord.handle.impl.events.guild.role.RoleEvent; import sx.blah.discord.handle.impl.events.guild.role.RoleUpdateEvent; import sx.blah.discord.handle.obj.IChannel; +import sx.blah.discord.handle.obj.IRole; + +import java.awt.*; +import java.util.List; +import java.util.stream.Collectors; public class GameRoleModule extends Component { + public List GameRoles; + @Override protected void enable() { - DiscordCommandBase.registerCommand("role", new RoleCommand()); + DiscordCommandBase.registerCommand("role", new RoleCommand(this)); + GameRoles = DiscordPlugin.mainServer.getRoles().stream().filter(this::isGameRole).map(IRole::getName).collect(Collectors.toList()); } @Override @@ -32,26 +40,27 @@ public class GameRoleModule extends Component { public static void handleRoleEvent(RoleEvent roleEvent) { val grm = ComponentManager.getIfEnabled(GameRoleModule.class); if (grm == null) return; + val GameRoles = grm.GameRoles; if (roleEvent instanceof RoleCreateEvent) { Bukkit.getScheduler().runTaskLaterAsynchronously(DiscordPlugin.plugin, () -> { - if (roleEvent.getRole().isDeleted() || !DiscordPlugin.plugin.isGameRole(roleEvent.getRole())) + if (roleEvent.getRole().isDeleted() || !grm.isGameRole(roleEvent.getRole())) return; //Deleted or not a game role - DiscordPlugin.GameRoles.add(roleEvent.getRole().getName()); + GameRoles.add(roleEvent.getRole().getName()); DiscordPlugin.sendMessageToChannel(grm.logChannel().get(), "Added " + roleEvent.getRole().getName() + " as game role. If you don't want this, change the role's color from the default."); }, 100); } else if (roleEvent instanceof RoleDeleteEvent) { - if (DiscordPlugin.GameRoles.remove(roleEvent.getRole().getName())) + if (GameRoles.remove(roleEvent.getRole().getName())) DiscordPlugin.sendMessageToChannel(grm.logChannel().get(), "Removed " + roleEvent.getRole().getName() + " as a game role."); } else if (roleEvent instanceof RoleUpdateEvent) { val event = (RoleUpdateEvent) roleEvent; - if (!DiscordPlugin.plugin.isGameRole(event.getNewRole())) { - if (DiscordPlugin.GameRoles.remove(event.getOldRole().getName())) + if (!grm.isGameRole(event.getNewRole())) { + if (GameRoles.remove(event.getOldRole().getName())) DiscordPlugin.sendMessageToChannel(grm.logChannel().get(), "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())) + if (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 + boolean removed = GameRoles.remove(event.getOldRole().getName()); //Regardless of whether it was a game role + GameRoles.add(event.getNewRole().getName()); //Add it because it has no color if (removed) DiscordPlugin.sendMessageToChannel(grm.logChannel().get(), "Changed game role from " + event.getOldRole().getName() + " to " + event.getNewRole().getName() + "."); else @@ -59,4 +68,12 @@ public class GameRoleModule extends Component { } } } + + private boolean isGameRole(IRole r) { + if (r.getGuild().getLongID() != DiscordPlugin.mainServer.getLongID()) + return false; //Only allow on the main server + val rc = new Color(149, 165, 166, 0); + return r.getColor().equals(rc) + && r.getPosition() < DiscordPlugin.mainServer.getRoleByID(234343495735836672L).getPosition(); //Below the ChromaBot role + } } diff --git a/src/main/java/buttondevteam/discordplugin/role/RoleCommand.java b/src/main/java/buttondevteam/discordplugin/role/RoleCommand.java index 1d80e51..1534e2b 100755 --- a/src/main/java/buttondevteam/discordplugin/role/RoleCommand.java +++ b/src/main/java/buttondevteam/discordplugin/role/RoleCommand.java @@ -12,6 +12,12 @@ import java.util.stream.Collectors; public class RoleCommand extends DiscordCommandBase { //TODO: Use Command2's parser + private GameRoleModule grm; + + RoleCommand(GameRoleModule grm) { + this.grm = grm; + } + @Override public String getCommandName() { return "role"; @@ -52,7 +58,7 @@ public class RoleCommand extends DiscordCommandBase { //TODO: Use Command2's par private void listRoles(IMessage message) { DiscordPlugin.sendMessageToChannel(message.getChannel(), - "List of roles:\n" + DiscordPlugin.GameRoles.stream().sorted().collect(Collectors.joining("\n"))); + "List of roles:\n" + grm.GameRoles.stream().sorted().collect(Collectors.joining("\n"))); } private IRole checkAndGetRole(IMessage message, String[] argsa, String usage) { @@ -63,7 +69,7 @@ public class RoleCommand extends DiscordCommandBase { //TODO: Use Command2's par StringBuilder rolename = new StringBuilder(argsa[1]); for (int i = 2; i < argsa.length; i++) rolename.append(" ").append(argsa[i]); - if (!DiscordPlugin.GameRoles.contains(rolename.toString())) { + if (!grm.GameRoles.contains(rolename.toString())) { DiscordPlugin.sendMessageToChannel(message.getChannel(), "That role cannot be found."); listRoles(message); return null; @@ -72,7 +78,7 @@ public class RoleCommand extends DiscordCommandBase { //TODO: Use Command2's par if (roles.size() == 0) { DiscordPlugin.sendMessageToChannel(message.getChannel(), "The specified role cannot be found on Discord! Removing from the list."); - DiscordPlugin.GameRoles.remove(rolename.toString()); + grm.GameRoles.remove(rolename.toString()); return null; } if (roles.size() > 1) {