diff --git a/src/main/java/buttondevteam/discordplugin/DPUtils.java b/src/main/java/buttondevteam/discordplugin/DPUtils.java index b0c0990..d118672 100755 --- a/src/main/java/buttondevteam/discordplugin/DPUtils.java +++ b/src/main/java/buttondevteam/discordplugin/DPUtils.java @@ -96,13 +96,16 @@ public final class DPUtils { */ public static ReadOnlyConfigData> roleData(IHaveConfig config, String key, String defName, Mono guild) { return config.getReadOnlyDataPrimDef(key, defName, name -> { - if (!(name instanceof String)) return Mono.empty(); - return guild.flatMapMany(Guild::getRoles).filter(r -> r.getName().equals(name)).next(); + if (!(name instanceof String) || ((String) name).length() == 0) return Mono.empty(); + return guild.flatMapMany(Guild::getRoles).filter(r -> r.getName().equals(name)).onErrorResume(e -> { + getLogger().warning("Failed to get role data for " + key + "=" + name + " - " + e.getMessage()); + return Mono.empty(); + }).next(); }, r -> defName); } - public static ConfigData snowflakeData(IHaveConfig config, String key, long defID) { - return config.getDataPrimDef(key, defID, id -> Snowflake.of((long) id), Snowflake::asLong); + public static ReadOnlyConfigData snowflakeData(IHaveConfig config, String key, long defID) { + return config.getReadOnlyDataPrimDef(key, defID, id -> Snowflake.of((long) id), Snowflake::asLong); } /** diff --git a/src/main/java/buttondevteam/discordplugin/DiscordPlugin.java b/src/main/java/buttondevteam/discordplugin/DiscordPlugin.java index 81bb8bd..a38389a 100755 --- a/src/main/java/buttondevteam/discordplugin/DiscordPlugin.java +++ b/src/main/java/buttondevteam/discordplugin/DiscordPlugin.java @@ -55,6 +55,9 @@ public class DiscordPlugin extends ButtonPlugin { @Getter private Command2DC manager; + /** + * The prefix to use with Discord commands like /role. It only works in the bot channel. + */ private ConfigData prefix() { return getIConfig().getData("prefix", '/', str -> ((String) str).charAt(0), Object::toString); } @@ -64,6 +67,9 @@ public class DiscordPlugin extends ButtonPlugin { return plugin.prefix().get(); } + /** + * The main server where the roles and other information is pulled from. It's automatically set to the first server the bot's invited to. + */ private ConfigData> mainServer() { return getIConfig().getDataPrimDef("mainServer", 0L, id -> { @@ -76,12 +82,16 @@ public class DiscordPlugin extends ButtonPlugin { g -> g.map(gg -> gg.getId().asLong()).orElse(0L)); } + /** + * The (bot) channel to use for Discord commands like /role. + */ public ConfigData commandChannel() { - return DPUtils.snowflakeData(getIConfig(), "commandChannel", 239519012529111040L); + return DPUtils.snowflakeData(getIConfig(), "commandChannel", 0L); } /** - * If the role doesn't exist, then it will only allow for the owner. + * The role that allows using mod-only Discord commands. + * If empty (''), then it will only allow for the owner. */ public ConfigData> modRole() { return DPUtils.roleData(getIConfig(), "modRole", "Moderator"); @@ -164,7 +174,7 @@ public class DiscordPlugin extends ButtonPlugin { } SafeMode = false; DPUtils.disableIfConfigErrorRes(null, commandChannel(), DPUtils.getMessageChannel(commandChannel())); - DPUtils.disableIfConfigError(null, modRole()); //Won't disable, just prints the warning here + //Won't disable, just prints the warning here Component.registerComponent(this, new GeneralEventBroadcasterModule()); Component.registerComponent(this, new MinecraftChatModule()); diff --git a/src/main/java/buttondevteam/discordplugin/announcer/AnnouncerModule.java b/src/main/java/buttondevteam/discordplugin/announcer/AnnouncerModule.java index 5a28ece..2b68de0 100644 --- a/src/main/java/buttondevteam/discordplugin/announcer/AnnouncerModule.java +++ b/src/main/java/buttondevteam/discordplugin/announcer/AnnouncerModule.java @@ -5,6 +5,7 @@ import buttondevteam.discordplugin.DiscordPlayer; import buttondevteam.discordplugin.DiscordPlugin; import buttondevteam.lib.TBMCCoreAPI; import buttondevteam.lib.architecture.Component; +import buttondevteam.lib.architecture.ComponentMetadata; import buttondevteam.lib.architecture.ConfigData; import buttondevteam.lib.architecture.ReadOnlyConfigData; import buttondevteam.lib.player.ChromaGamerBase; @@ -15,12 +16,13 @@ import com.google.gson.JsonParser; import discord4j.core.object.entity.Message; import discord4j.core.object.entity.MessageChannel; import lombok.val; -import org.bukkit.configuration.file.YamlConfiguration; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; -import java.io.File; - +/** + * Posts new posts from Reddit to the specified channel(s). It will pin the regular posts (not the mod posts). + */ +@ComponentMetadata(enabledByDefault = false) public class AnnouncerModule extends Component { /** * Channel to post new posts. @@ -51,7 +53,13 @@ public class AnnouncerModule extends Component { return getConfig().getData("lastSeenTime", 0L); } - private static final String SubredditURL = "https://www.reddit.com/r/ChromaGamers"; + /** + * The subreddit to pull the posts from + */ + private ConfigData subredditURL() { + return getConfig().getData("subredditURL", "https://www.reddit.com/r/ChromaGamers"); + } + private static boolean stop = false; @Override @@ -62,11 +70,6 @@ public class AnnouncerModule extends Component { if (keepPinned == 0) return; Flux msgs = channel().get().flatMapMany(MessageChannel::getPinnedMessages); msgs.subscribe(Message::unpin); - val yc = YamlConfiguration.loadConfiguration(new File("plugins/DiscordPlugin", "config.yml")); //Name change - if (lastAnnouncementTime().get() == 0) //Load old data - lastAnnouncementTime().set(yc.getLong("lastannouncementtime")); - if (lastSeenTime().get() == 0) - lastSeenTime().set(yc.getLong("lastseentime")); new Thread(this::AnnouncementGetterThreadMethod).start(); } @@ -82,7 +85,7 @@ public class AnnouncerModule extends Component { Thread.sleep(10000); continue; } - String body = TBMCCoreAPI.DownloadString(SubredditURL + "/new/.json?limit=10"); + String body = TBMCCoreAPI.DownloadString(subredditURL().get() + "/new/.json?limit=10"); JsonArray json = new JsonParser().parse(body).getAsJsonObject().get("data").getAsJsonObject() .get("children").getAsJsonArray(); StringBuilder msgsb = new StringBuilder(); diff --git a/src/main/java/buttondevteam/discordplugin/broadcaster/GeneralEventBroadcasterModule.java b/src/main/java/buttondevteam/discordplugin/broadcaster/GeneralEventBroadcasterModule.java index 44c2d79..5a0317e 100644 --- a/src/main/java/buttondevteam/discordplugin/broadcaster/GeneralEventBroadcasterModule.java +++ b/src/main/java/buttondevteam/discordplugin/broadcaster/GeneralEventBroadcasterModule.java @@ -6,6 +6,10 @@ import buttondevteam.lib.TBMCCoreAPI; import buttondevteam.lib.architecture.Component; import lombok.Getter; +/** + * Uses a bit of a hacky method of getting all broadcasted messages, including advancements and any other message that's for everyone. + * If this component is enabled then these messages will show up on Discord. + */ public class GeneralEventBroadcasterModule extends Component { private static @Getter boolean hooked = false; diff --git a/src/main/java/buttondevteam/discordplugin/commands/Command2DCSender.java b/src/main/java/buttondevteam/discordplugin/commands/Command2DCSender.java index 8b705fd..ab22e37 100644 --- a/src/main/java/buttondevteam/discordplugin/commands/Command2DCSender.java +++ b/src/main/java/buttondevteam/discordplugin/commands/Command2DCSender.java @@ -3,6 +3,7 @@ package buttondevteam.discordplugin.commands; import buttondevteam.discordplugin.DPUtils; import buttondevteam.lib.chat.Command2Sender; import discord4j.core.object.entity.Message; +import discord4j.core.object.entity.User; import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.val; @@ -30,4 +31,9 @@ public class Command2DCSender implements Command2Sender { public void sendMessage(String[] message) { sendMessage(String.join("\n", message)); } + + @Override + public String getName() { + return message.getAuthor().map(User::getUsername).orElse("Discord"); + } } diff --git a/src/main/java/buttondevteam/discordplugin/exceptions/ExceptionListenerModule.java b/src/main/java/buttondevteam/discordplugin/exceptions/ExceptionListenerModule.java index bfb3048..6263e85 100755 --- a/src/main/java/buttondevteam/discordplugin/exceptions/ExceptionListenerModule.java +++ b/src/main/java/buttondevteam/discordplugin/exceptions/ExceptionListenerModule.java @@ -23,6 +23,9 @@ import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; +/** + * Listens for errors from the Chroma plugins and posts them to Discord, ignoring repeating errors so it's not that spammy. + */ public class ExceptionListenerModule extends Component implements Listener { private List lastthrown = new ArrayList<>(); private List lastsourcemsg = new ArrayList<>(); @@ -84,10 +87,16 @@ public class ExceptionListenerModule extends Component implements return Mono.empty(); } + /** + * The channel to post the errors to. + */ private ReadOnlyConfigData> channel() { return DPUtils.channelData(getConfig(), "channel"); } + /** + * The role to ping if an error occurs. Set to empty ('') to disable. + */ private ConfigData> pingRole(Mono guild) { return DPUtils.roleData(getConfig(), "pingRole", "Coder", guild); } diff --git a/src/main/java/buttondevteam/discordplugin/fun/FunModule.java b/src/main/java/buttondevteam/discordplugin/fun/FunModule.java index 46e98c5..189a026 100644 --- a/src/main/java/buttondevteam/discordplugin/fun/FunModule.java +++ b/src/main/java/buttondevteam/discordplugin/fun/FunModule.java @@ -26,23 +26,24 @@ import java.util.concurrent.TimeUnit; import java.util.stream.IntStream; /** + * All kinds of random things. * The YEEHAW event uses an emoji named :YEEHAW: if available */ public class FunModule extends Component implements Listener { - 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[] 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 }; /** @@ -52,14 +53,14 @@ public class FunModule extends Component implements Listener { return getConfig().getData("serverReady", () -> 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?"}); + "vhen vill ze server be open?"}); } /** * Answers for a recognized question. Selected randomly. */ private ConfigData> serverReadyAnswers() { - return getConfig().getData("serverReadyAnswers", () -> Lists.newArrayList(serverReadyStrings)); //TODO: Test + return getConfig().getData("serverReadyAnswers", () -> Lists.newArrayList(serverReadyStrings)); } private static final Random serverReadyRandom = new Random(); @@ -119,11 +120,17 @@ public class FunModule extends Component implements Listener { ListC = 0; } + /** + * If all of the people who have this role are online, the bot will post a full house. + */ private ConfigData> fullHouseDevRole(Mono guild) { return DPUtils.roleData(getConfig(), "fullHouseDevRole", "Developer", guild); } + /** + * The channel to post the full house to. + */ private ReadOnlyConfigData> fullHouseChannel() { return DPUtils.channelData(getConfig(), "fullHouseChannel"); } diff --git a/src/main/java/buttondevteam/discordplugin/mcchat/MCChatListener.java b/src/main/java/buttondevteam/discordplugin/mcchat/MCChatListener.java index 401d6e3..202fd93 100755 --- a/src/main/java/buttondevteam/discordplugin/mcchat/MCChatListener.java +++ b/src/main/java/buttondevteam/discordplugin/mcchat/MCChatListener.java @@ -102,7 +102,8 @@ public class MCChatListener implements Listener { if (lastmsgdata.message == null || !authorPlayer.equals(lastmsgdata.message.getEmbeds().get(0).getAuthor().map(Embed.Author::getName).orElse(null)) || lastmsgdata.time / 1000000000f < nanoTime / 1000000000f - 120 - || !lastmsgdata.mcchannel.ID.equals(e.getChannel().ID)) { + || !lastmsgdata.mcchannel.ID.equals(e.getChannel().ID) + || lastmsgdata.content.length() + e.getMessage().length() + 1 > 2048) { lastmsgdata.message = lastmsgdata.channel.createEmbed(embed).block(); lastmsgdata.time = nanoTime; lastmsgdata.mcchannel = e.getChannel(); diff --git a/src/main/java/buttondevteam/discordplugin/mcchat/MinecraftChatModule.java b/src/main/java/buttondevteam/discordplugin/mcchat/MinecraftChatModule.java index eacf823..1018ba8 100644 --- a/src/main/java/buttondevteam/discordplugin/mcchat/MinecraftChatModule.java +++ b/src/main/java/buttondevteam/discordplugin/mcchat/MinecraftChatModule.java @@ -28,12 +28,7 @@ import java.util.stream.Collectors; * Provides Minecraft chat connection to Discord. Commands may be used either in a public chat (limited) or in a DM. */ public class MinecraftChatModule extends Component { - private @Getter - MCChatListener listener; - - /*public MCChatListener getListener() { //It doesn't want to generate - return listener; - And now ButtonProcessor didn't look beyond this - return instead of continue... - }*/ + private @Getter MCChatListener listener; /** * A list of commands that can be used in public chats - Warning: Some plugins will treat players as OPs, always test before allowing a command! @@ -46,8 +41,8 @@ public class MinecraftChatModule extends Component { /** * The channel to use as the public Minecraft chat - everything public gets broadcasted here */ - public ConfigData chatChannel() { - return DPUtils.snowflakeData(getConfig(), "chatChannel", 239519012529111040L); + public ReadOnlyConfigData chatChannel() { + return DPUtils.snowflakeData(getConfig(), "chatChannel", 0L); } public Mono chatChannelMono() { diff --git a/src/main/java/buttondevteam/discordplugin/mccommands/DiscordMCCommand.java b/src/main/java/buttondevteam/discordplugin/mccommands/DiscordMCCommand.java index 1f2d7a7..61d9d6b 100644 --- a/src/main/java/buttondevteam/discordplugin/mccommands/DiscordMCCommand.java +++ b/src/main/java/buttondevteam/discordplugin/mccommands/DiscordMCCommand.java @@ -27,6 +27,7 @@ import java.lang.reflect.Method; public class DiscordMCCommand extends ICommand2MC { @Command2.Subcommand public boolean accept(Player player) { + if (checkSafeMode(player)) return true; String did = ConnectCommand.WaitingToConnect.get(player.getName()); if (did == null) { player.sendMessage("§cYou don't have a pending connection to Discord."); @@ -45,6 +46,7 @@ public class DiscordMCCommand extends ICommand2MC { @Command2.Subcommand public boolean decline(Player player) { + if (checkSafeMode(player)) return true; String did = ConnectCommand.WaitingToConnect.remove(player.getName()); if (did == null) { player.sendMessage("§cYou don't have a pending connection to Discord."); @@ -101,6 +103,7 @@ public class DiscordMCCommand extends ICommand2MC { "Shows an invite link to the server" }) public void invite(CommandSender sender) { + if (checkSafeMode(sender)) return; String invi = DiscordPlugin.plugin.inviteLink().get(); if (invi.length() > 0) { sender.sendMessage("§bInvite link: " + invi); @@ -132,4 +135,12 @@ public class DiscordMCCommand extends ICommand2MC { return super.getHelpText(method, ann); } } + + private boolean checkSafeMode(CommandSender sender) { + if (DiscordPlugin.SafeMode) { + sender.sendMessage("§cThe plugin isn't initialized. Check console for details."); + return true; + } + return false; + } }