From cadb53d886e602196ad13e18a8481d9805ddcf7f Mon Sep 17 00:00:00 2001 From: NorbiPeti Date: Sat, 9 Mar 2019 02:07:56 +0100 Subject: [PATCH] Made the plugin work cleanly (#51) Clean install works pretty well Automatically setting first server as main server Removed devServer variable, getting the guild from the channels Disabling each component that doesn't have its channel(s)/role(s) set correctly (unless it's not required) Removed redundant variable from the FunModule Correctly handling missing channel settings in some places Checking any role position for game roles, not just "ChromaBot" The bot is playing Minecraft now Always giving permission for commands for now --- .../discordplugin/AnnouncerModule.java | 1 + .../buttondevteam/discordplugin/DPUtils.java | 41 +++++++++++++++++-- .../discordplugin/DiscordPlugin.java | 26 +++++++----- .../discordplugin/commands/Command2DC.java | 3 +- .../exceptions/ExceptionListenerModule.java | 17 +++++--- .../discordplugin/fun/FunModule.java | 27 +++++------- .../listeners/CommonListeners.java | 3 +- .../mcchat/ChannelconCommand.java | 2 +- .../discordplugin/mcchat/MCListener.java | 9 +++- .../mcchat/MinecraftChatModule.java | 1 + .../discordplugin/role/GameRoleModule.java | 25 ++++++----- 11 files changed, 104 insertions(+), 51 deletions(-) diff --git a/src/main/java/buttondevteam/discordplugin/AnnouncerModule.java b/src/main/java/buttondevteam/discordplugin/AnnouncerModule.java index a4b8fda..d0efe1e 100644 --- a/src/main/java/buttondevteam/discordplugin/AnnouncerModule.java +++ b/src/main/java/buttondevteam/discordplugin/AnnouncerModule.java @@ -45,6 +45,7 @@ public class AnnouncerModule extends Component { @Override protected void enable() { + if (DPUtils.disableIfConfigError(this, channel(), modChannel())) return; stop = false; //If not the first time DPUtils.performNoWait(() -> { try { diff --git a/src/main/java/buttondevteam/discordplugin/DPUtils.java b/src/main/java/buttondevteam/discordplugin/DPUtils.java index fba8a7b..48d5963 100755 --- a/src/main/java/buttondevteam/discordplugin/DPUtils.java +++ b/src/main/java/buttondevteam/discordplugin/DPUtils.java @@ -1,10 +1,13 @@ package buttondevteam.discordplugin; +import buttondevteam.lib.TBMCCoreAPI; +import buttondevteam.lib.architecture.Component; import buttondevteam.lib.architecture.ConfigData; import buttondevteam.lib.architecture.IHaveConfig; import lombok.val; import org.bukkit.Bukkit; import sx.blah.discord.handle.obj.IChannel; +import sx.blah.discord.handle.obj.IGuild; import sx.blah.discord.handle.obj.IIDLinkedObject; import sx.blah.discord.handle.obj.IRole; import sx.blah.discord.util.EmbedBuilder; @@ -114,10 +117,14 @@ public final class DPUtils { } public static ConfigData roleData(IHaveConfig config, String key, String defName) { + return roleData(config, key, defName, DiscordPlugin.mainServer); + } + + public static ConfigData roleData(IHaveConfig config, String key, String defName, IGuild guild) { return config.getDataPrimDef(key, defName, name -> { - val roles = DiscordPlugin.mainServer.getRolesByName((String) name); - return roles.size() > 0 ? roles.get(0) : null; //TODO: May not handle null properly - }, IIDLinkedObject::getLongID); //We can afford to search for the channel in the cache once (instead of using mainServer) + val roles = guild.getRolesByName((String) name); + return roles.size() > 0 ? roles.get(0) : null; + }, IIDLinkedObject::getLongID); } /** @@ -132,4 +139,32 @@ public final class DPUtils { return channel.mention(); } + /** + * Disables the component if one of the given configs return null. Useful for channel/role configs. + * + * @param component The component to disable if needed + * @param configs The configs to check for null + * @return Whether the component got disabled and a warning logged + */ + public static boolean disableIfConfigError(@Nullable Component component, ConfigData... configs) { + for (val config : configs) { + if (config.get() == null) { + String path = null; + try { + if (component != null) + Component.setComponentEnabled(component, false); + val f = ConfigData.class.getDeclaredField("path"); + f.setAccessible(true); //Hacking my own plugin + path = (String) f.get(config); + } catch (Exception e) { + TBMCCoreAPI.SendException("Failed to disable component after config error!", e); + } + getLogger().warning("The config value " + path + " isn't set correctly " + (component == null ? "in global settings!" : "for component " + component.getClass().getSimpleName() + "!")); + getLogger().warning("Set the correct ID in the config" + (component == null ? "" : " or disable this component") + " to remove this message."); + return true; + } + } + return false; + } + } diff --git a/src/main/java/buttondevteam/discordplugin/DiscordPlugin.java b/src/main/java/buttondevteam/discordplugin/DiscordPlugin.java index fd5f2fa..498ba31 100755 --- a/src/main/java/buttondevteam/discordplugin/DiscordPlugin.java +++ b/src/main/java/buttondevteam/discordplugin/DiscordPlugin.java @@ -108,7 +108,6 @@ public class DiscordPlugin extends ButtonPlugin implements IListener } public static IGuild mainServer; - public static IGuild devServer; private static volatile BukkitTask task; private static volatile boolean sent = false; @@ -122,17 +121,22 @@ public class DiscordPlugin extends ButtonPlugin implements IListener tries.incrementAndGet(); if (tries.get() > 10) { //5 seconds task.cancel(); - getLogger().severe("Main or dev server not found! Set ID and do /discord reset"); + getLogger().severe("Main server not found! Invite the bot and do /discord reset"); + //getIConfig().getConfig().set("mainServer", 219529124321034241L); //Needed because it won't save as long as it's null - made it save + saveConfig(); //Put default there return; } - if (mainServer == null || devServer == null) { - mainServer = event.getClient().getGuildByID(125813020357165056L); - devServer = event.getClient().getGuildByID(219529124321034241L); - } - if (mainServer == null || devServer == null) - return; // Retry + mainServer = MainServer().get(); //Shouldn't change afterwards + if (mainServer == null) { + val guilds = dc.getGuilds(); + if (guilds.size() == 0) + return; //If there are no guilds in cache, retry + mainServer = guilds.get(0); + getLogger().warning("Main server set to first one: " + mainServer.getName()); + MainServer().set(mainServer); //Save in config + } if (!TBMCCoreAPI.IsTestServer()) { //Don't change conditions here, see mainServer=devServer=null in onDisable() - dc.changePresence(StatusType.ONLINE, ActivityType.PLAYING, "Chromacraft"); + dc.changePresence(StatusType.ONLINE, ActivityType.PLAYING, "Minecraft"); } else { dc.changePresence(StatusType.ONLINE, ActivityType.PLAYING, "testing"); } @@ -140,6 +144,8 @@ public class DiscordPlugin extends ButtonPlugin implements IListener if (task != null) task.cancel(); if (!sent) { + DPUtils.disableIfConfigError(null, CommandChannel(), ModRole()); //Won't disable, just prints the warning here + Component.registerComponent(this, new GeneralEventBroadcasterModule()); Component.registerComponent(this, new MinecraftChatModule()); Component.registerComponent(this, new ExceptionListenerModule()); @@ -242,7 +248,7 @@ public class DiscordPlugin extends ButtonPlugin implements IListener ChromaBot.delete(); dc.changePresence(StatusType.IDLE, ActivityType.PLAYING, "Chromacraft"); //No longer using the same account for testing dc.logout(); - mainServer = devServer = null; //Fetch servers and channels again + //Configs are emptied so channels and servers are fetched again sent = false; } catch (Exception e) { TBMCCoreAPI.SendException("An error occured while disabling DiscordPlugin!", e); diff --git a/src/main/java/buttondevteam/discordplugin/commands/Command2DC.java b/src/main/java/buttondevteam/discordplugin/commands/Command2DC.java index c2decdb..a0b8fda 100644 --- a/src/main/java/buttondevteam/discordplugin/commands/Command2DC.java +++ b/src/main/java/buttondevteam/discordplugin/commands/Command2DC.java @@ -11,6 +11,7 @@ public class Command2DC extends Command2 { @Override public boolean hasPermission(Command2DCSender sender, ICommand2DC command) { - return !command.isModOnly() || sender.getMessage().getAuthor().hasRole(DiscordPlugin.plugin.ModRole().get()); + //return !command.isModOnly() || sender.getMessage().getAuthor().hasRole(DiscordPlugin.plugin.ModRole().get()); //TODO: ModRole may be null; more customisable way? + return true; } } diff --git a/src/main/java/buttondevteam/discordplugin/exceptions/ExceptionListenerModule.java b/src/main/java/buttondevteam/discordplugin/exceptions/ExceptionListenerModule.java index b868586..5d21bb1 100755 --- a/src/main/java/buttondevteam/discordplugin/exceptions/ExceptionListenerModule.java +++ b/src/main/java/buttondevteam/discordplugin/exceptions/ExceptionListenerModule.java @@ -12,6 +12,7 @@ import org.bukkit.Bukkit; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import sx.blah.discord.handle.obj.IChannel; +import sx.blah.discord.handle.obj.IGuild; import sx.blah.discord.handle.obj.IRole; import java.util.ArrayList; @@ -43,15 +44,14 @@ public class ExceptionListenerModule extends Component implements e.setHandled(); } - private static IRole coderRole; - private static void SendException(Throwable e, String sourcemessage) { if (instance == null) return; try { - if (coderRole == null) - coderRole = DiscordPlugin.devServer.getRolesByName("Coder").get(0); + IChannel channel = getChannel(); + assert channel != null; + IRole coderRole = instance.pingRole(channel.getGuild()).get(); StringBuilder sb = TBMCCoreAPI.IsTestServer() ? new StringBuilder() - : new StringBuilder(coderRole.mention()).append("\n"); + : new StringBuilder(coderRole == null ? "" : coderRole.mention()).append("\n"); sb.append(sourcemessage).append("\n"); sb.append("```").append("\n"); String stackTrace = Arrays.stream(ExceptionUtils.getStackTrace(e).split("\\n")) @@ -61,7 +61,7 @@ public class ExceptionListenerModule extends Component implements stackTrace = stackTrace.substring(0, 1800); sb.append(stackTrace).append("\n"); sb.append("```"); - DiscordPlugin.sendMessageToChannel(getChannel(), sb.toString()); //Instance isn't null here + DiscordPlugin.sendMessageToChannel(channel, sb.toString()); //Instance isn't null here } catch (Exception ex) { ex.printStackTrace(); } @@ -78,8 +78,13 @@ public class ExceptionListenerModule extends Component implements return DPUtils.channelData(getConfig(), "channel", 239519012529111040L); } + private ConfigData pingRole(IGuild guild) { + return DPUtils.roleData(getConfig(), "pingRole", "Coder", guild); + } + @Override protected void enable() { + if (DPUtils.disableIfConfigError(this, channel())) return; instance = this; Bukkit.getPluginManager().registerEvents(new ExceptionListenerModule(), getPlugin()); TBMCCoreAPI.RegisterEventsForExceptions(new DebugMessageListener(), getPlugin()); diff --git a/src/main/java/buttondevteam/discordplugin/fun/FunModule.java b/src/main/java/buttondevteam/discordplugin/fun/FunModule.java index d7fcc86..f5adf0d 100644 --- a/src/main/java/buttondevteam/discordplugin/fun/FunModule.java +++ b/src/main/java/buttondevteam/discordplugin/fun/FunModule.java @@ -13,10 +13,7 @@ import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerJoinEvent; 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.IRole; -import sx.blah.discord.handle.obj.StatusType; +import sx.blah.discord.handle.obj.*; import sx.blah.discord.util.EmbedBuilder; import java.util.ArrayList; @@ -27,8 +24,6 @@ import java.util.concurrent.TimeUnit; import java.util.stream.IntStream; public class FunModule extends Component implements Listener { - 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 @@ -68,7 +63,6 @@ public class FunModule extends Component implements Listener { @Override protected void enable() { - mod = this; registerListener(this); } @@ -99,7 +93,7 @@ public class FunModule extends Component implements Listener { return true; //Handled } lastlistp = (short) Bukkit.getOnlinePlayers().size(); //Didn't handle - if (mod.serverReady().get()) { + if (fm.serverReady().get()) { if (!TBMCCoreAPI.IsTestServer() && Arrays.stream(serverReadyQuestions).anyMatch(msglowercased::contains)) { int next; @@ -118,11 +112,8 @@ public class FunModule extends Component implements Listener { ListC = 0; } - private ConfigData fullHouseDevRole() { - return getConfig().getDataPrimDef("fullHouseDevRole", "Developer", name -> { - val list = DiscordPlugin.devServer.getRolesByName((String) name); - return list.size() > 0 ? list.get(0) : null; - }, IRole::getName); + private ConfigData fullHouseDevRole(IGuild guild) { + return DPUtils.roleData(getConfig(), "fullHouseDevRole", "Developer", guild); } @@ -135,17 +126,19 @@ public class FunModule extends Component implements Listener { public static void handleFullHouse(PresenceUpdateEvent event) { val fm = ComponentManager.getIfEnabled(FunModule.class); if (fm == null) return; - val devrole = fm.fullHouseDevRole().get(); + val channel = fm.fullHouseChannel().get(); + if (channel == null) return; + val devrole = fm.fullHouseDevRole(channel.getGuild()).get(); if (devrole == null) return; if (event.getOldPresence().getStatus().equals(StatusType.OFFLINE) && !event.getNewPresence().getStatus().equals(StatusType.OFFLINE) - && event.getUser().getRolesForGuild(DiscordPlugin.devServer).stream() + && event.getUser().getRolesForGuild(channel.getGuild()).stream() .anyMatch(r -> r.getLongID() == devrole.getLongID()) - && DiscordPlugin.devServer.getUsersByRole(devrole).stream() + && channel.getGuild().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(mod.fullHouseChannel().get(), "Full house!", + DiscordPlugin.sendMessageToChannel(channel, "Full house!", new EmbedBuilder() .withImage( "https://cdn.discordapp.com/attachments/249295547263877121/249687682618359808/poker-hand-full-house-aces-kings-playing-cards-15553791.png") diff --git a/src/main/java/buttondevteam/discordplugin/listeners/CommonListeners.java b/src/main/java/buttondevteam/discordplugin/listeners/CommonListeners.java index fa11f78..5350b32 100755 --- a/src/main/java/buttondevteam/discordplugin/listeners/CommonListeners.java +++ b/src/main/java/buttondevteam/discordplugin/listeners/CommonListeners.java @@ -38,7 +38,8 @@ public class CommonListeners { return; try { boolean handled = false; - if (event.getChannel().getLongID() == DiscordPlugin.plugin.CommandChannel().get().getLongID() //If mentioned, that's higher than chat + val commandChannel = DiscordPlugin.plugin.CommandChannel().get(); + if ((commandChannel != null && event.getChannel().getLongID() == commandChannel.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; diff --git a/src/main/java/buttondevteam/discordplugin/mcchat/ChannelconCommand.java b/src/main/java/buttondevteam/discordplugin/mcchat/ChannelconCommand.java index ceb184b..2bd2bb6 100644 --- a/src/main/java/buttondevteam/discordplugin/mcchat/ChannelconCommand.java +++ b/src/main/java/buttondevteam/discordplugin/mcchat/ChannelconCommand.java @@ -144,7 +144,7 @@ public class ChannelconCommand extends ICommand2DC { "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 " + DiscordPlugin.getPrefix() + " prefix only works in " + DPUtils.botmention() + ".", // - "Invite link: " // + "Invite link: " // TODO: Set correct client ID }; } } diff --git a/src/main/java/buttondevteam/discordplugin/mcchat/MCListener.java b/src/main/java/buttondevteam/discordplugin/mcchat/MCListener.java index fbf9d27..6cc9f5b 100644 --- a/src/main/java/buttondevteam/discordplugin/mcchat/MCListener.java +++ b/src/main/java/buttondevteam/discordplugin/mcchat/MCListener.java @@ -108,6 +108,7 @@ class MCListener implements Listener { try { DPUtils.performNoWait(() -> { final IRole role = muteRole().get(); + if (role == null) return; final CommandSource source = e.getAffected().getSource(); if (!source.isPlayer()) return; @@ -120,7 +121,11 @@ class MCListener implements Listener { user.addRole(role); else user.removeRole(role); - DiscordPlugin.sendMessageToChannel(module.modlogChannel().get(), (e.getValue() ? "M" : "Unm") + "uted user: " + user.getName()); + val modlog = module.modlogChannel().get(); + String msg = (e.getValue() ? "M" : "Unm") + "uted user: " + user.getName(); + if (modlog != null) + DiscordPlugin.sendMessageToChannel(modlog, msg); + DPUtils.getLogger().info(msg); }); } catch (DiscordException | MissingPermissionsException ex) { TBMCCoreAPI.SendException("Failed to give/take Muted role to player " + e.getAffected().getName() + "!", @@ -143,7 +148,7 @@ class MCListener implements Listener { 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")); + MCChatUtils.forAllMCChat(MCChatUtils.send(name + " <:YEEHAW:" + DiscordPlugin.mainServer.getEmojiByName("YEEHAW").getStringID() + ">s")); //TODO: Don't require emoji } @EventHandler diff --git a/src/main/java/buttondevteam/discordplugin/mcchat/MinecraftChatModule.java b/src/main/java/buttondevteam/discordplugin/mcchat/MinecraftChatModule.java index 2d6e50b..789fd2d 100644 --- a/src/main/java/buttondevteam/discordplugin/mcchat/MinecraftChatModule.java +++ b/src/main/java/buttondevteam/discordplugin/mcchat/MinecraftChatModule.java @@ -41,6 +41,7 @@ public class MinecraftChatModule extends Component { @Override protected void enable() { + if (DPUtils.disableIfConfigError(this, chatChannel())) return; listener = new MCChatListener(this); DiscordPlugin.dc.getDispatcher().registerListener(listener); TBMCCoreAPI.RegisterEventsForExceptions(listener, getPlugin()); diff --git a/src/main/java/buttondevteam/discordplugin/role/GameRoleModule.java b/src/main/java/buttondevteam/discordplugin/role/GameRoleModule.java index 45fe9ec..8806647 100644 --- a/src/main/java/buttondevteam/discordplugin/role/GameRoleModule.java +++ b/src/main/java/buttondevteam/discordplugin/role/GameRoleModule.java @@ -40,30 +40,34 @@ public class GameRoleModule extends Component { val grm = ComponentManager.getIfEnabled(GameRoleModule.class); if (grm == null) return; val GameRoles = grm.GameRoles; + val logChannel = grm.logChannel().get(); if (roleEvent instanceof RoleCreateEvent) { Bukkit.getScheduler().runTaskLaterAsynchronously(DiscordPlugin.plugin, () -> { if (roleEvent.getRole().isDeleted() || !grm.isGameRole(roleEvent.getRole())) return; //Deleted or not a game role 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."); + if (logChannel != null) + DiscordPlugin.sendMessageToChannel(logChannel, "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 (GameRoles.remove(roleEvent.getRole().getName())) - DiscordPlugin.sendMessageToChannel(grm.logChannel().get(), "Removed " + roleEvent.getRole().getName() + " as a game role."); + if (GameRoles.remove(roleEvent.getRole().getName()) && logChannel != null) + DiscordPlugin.sendMessageToChannel(logChannel, "Removed " + roleEvent.getRole().getName() + " as a game role."); } else if (roleEvent instanceof RoleUpdateEvent) { val event = (RoleUpdateEvent) roleEvent; 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."); + if (GameRoles.remove(event.getOldRole().getName()) && logChannel != null) + DiscordPlugin.sendMessageToChannel(logChannel, "Removed " + event.getOldRole().getName() + " as a game role because it's color changed."); } else { if (GameRoles.contains(event.getOldRole().getName()) && event.getOldRole().getName().equals(event.getNewRole().getName())) return; 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 - DiscordPlugin.sendMessageToChannel(grm.logChannel().get(), "Added " + event.getNewRole().getName() + " as game role because it has the default color."); + if (logChannel != null) { + if (removed) + DiscordPlugin.sendMessageToChannel(logChannel, "Changed game role from " + event.getOldRole().getName() + " to " + event.getNewRole().getName() + "."); + else + DiscordPlugin.sendMessageToChannel(logChannel, "Added " + event.getNewRole().getName() + " as game role because it has the default color."); + } } } } @@ -73,6 +77,7 @@ public class GameRoleModule extends Component { 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 + && DiscordPlugin.dc.getOurUser().getRolesForGuild(DiscordPlugin.mainServer) + .stream().anyMatch(or -> r.getPosition() < or.getPosition()); //Below one of our roles } }