diff --git a/pom.xml b/pom.xml index 1007123..7a38cc9 100755 --- a/pom.xml +++ b/pom.xml @@ -173,8 +173,8 @@ com.discord4j - Discord4J - 2.10.1 + discord4j-core + 3.0.2 diff --git a/src/main/java/buttondevteam/discordplugin/ChromaBot.java b/src/main/java/buttondevteam/discordplugin/ChromaBot.java index 56a6fb9..81a43d1 100755 --- a/src/main/java/buttondevteam/discordplugin/ChromaBot.java +++ b/src/main/java/buttondevteam/discordplugin/ChromaBot.java @@ -1,15 +1,14 @@ package buttondevteam.discordplugin; import buttondevteam.discordplugin.mcchat.MCChatUtils; +import discord4j.core.object.entity.Message; +import discord4j.core.object.entity.MessageChannel; import lombok.Getter; -import org.bukkit.entity.Player; import org.bukkit.scheduler.BukkitScheduler; -import sx.blah.discord.api.internal.json.objects.EmbedObject; -import sx.blah.discord.handle.obj.IChannel; -import sx.blah.discord.util.EmbedBuilder; +import reactor.core.publisher.Mono; import javax.annotation.Nullable; -import java.awt.*; +import java.util.function.Function; public class ChromaBot { /** @@ -33,113 +32,26 @@ public class ChromaBot { instance = null; } - /** - * Send a message to the chat channel and private chats. - * - * @param message - * The message to send, duh - */ - public void sendMessage(String message) { - MCChatUtils.forAllMCChat(ch -> DiscordPlugin.sendMessageToChannel(ch, message)); - } - /** * Send a message to the chat channels and private chats. * * @param message - * The message to send, duh - * @param embed - * Custom fancy stuff, use {@link EmbedBuilder} to create one + * The message to send, duh (use {@link MessageChannel#createMessage(String)}) */ - public void sendMessage(String message, EmbedObject embed) { - MCChatUtils.forAllMCChat(ch -> DiscordPlugin.sendMessageToChannel(ch, message, embed)); + public void sendMessage(Function> message) { + MCChatUtils.forAllMCChat(ch -> message.apply(ch).subscribe()); } /** * Send a message to the chat channels, private chats and custom chats. * * @param message The message to send, duh - * @param embed Custom fancy stuff, use {@link EmbedBuilder} to create one * @param toggle The toggle type for channelcon */ - public void sendMessageCustomAsWell(String message, EmbedObject embed, @Nullable ChannelconBroadcast toggle) { - MCChatUtils.forCustomAndAllMCChat(ch -> DiscordPlugin.sendMessageToChannel(ch, message, embed), toggle, false); + public void sendMessageCustomAsWell(Function> message, @Nullable ChannelconBroadcast toggle) { + MCChatUtils.forCustomAndAllMCChat(ch -> message.apply(ch).subscribe(), toggle, false); } - /** - * Send a message to an arbitrary channel. This will not send it to the private chats. - * - * @param channel - * The channel to send to, use the channel variables in {@link DiscordPlugin} - * @param message - * The message to send, duh - * @param embed - * Custom fancy stuff, use {@link EmbedBuilder} to create one - */ - public void sendMessage(IChannel channel, String message, EmbedObject embed) { - DiscordPlugin.sendMessageToChannel(channel, message, embed); - } - - /** - * Send a fancy message to the chat channels. This will show a bold text with a colored line. - * - * @param message - * The message to send, duh - * @param color - * The color of the line before the text - */ - public void sendMessage(String message, Color color) { - MCChatUtils.forAllMCChat(ch -> DiscordPlugin.sendMessageToChannel(ch, message, - new EmbedBuilder().withTitle(message).withColor(color).build())); - } - - /** - * Send a fancy message to the chat channels. This will show a bold text with a colored line. - * - * @param message - * The message to send, duh - * @param color - * The color of the line before the text - * @param mcauthor - * The name of the Minecraft player who is the author of this message - */ - public void sendMessage(String message, Color color, String mcauthor) { - MCChatUtils.forAllMCChat(ch -> DiscordPlugin.sendMessageToChannel(ch, message, - DPUtils.embedWithHead(new EmbedBuilder().withTitle(message).withColor(color), mcauthor).build())); - } - - /** - * Send a fancy message to the chat channels. This will show a bold text with a colored line. - * - * @param message - * The message to send, duh - * @param color - * The color of the line before the text - * @param authorname - * The name of the author of this message - * @param authorimg - * The URL of the avatar image for this message's author - */ - public void sendMessage(String message, Color color, String authorname, String authorimg) { - MCChatUtils.forAllMCChat(ch -> DiscordPlugin.sendMessageToChannel(ch, message, new EmbedBuilder() - .withTitle(message).withColor(color).withAuthorName(authorname).withAuthorIcon(authorimg).build())); - } - - /** - * Send a message to the chat channels. This will show a bold text with a colored line. - * - * @param message - * The message to send, duh - * @param color - * The color of the line before the text - * @param sender - * The player who sends this message - */ - public void sendMessage(String message, Color color, Player sender) { - MCChatUtils.forAllMCChat(ch -> DiscordPlugin.sendMessageToChannel(ch, message, DPUtils - .embedWithHead(new EmbedBuilder().withTitle(message).withColor(color), sender.getName()).build())); - } - public void updatePlayerList() { MCChatUtils.updatePlayerList(); } diff --git a/src/main/java/buttondevteam/discordplugin/DPUtils.java b/src/main/java/buttondevteam/discordplugin/DPUtils.java index c21cceb..4b40968 100755 --- a/src/main/java/buttondevteam/discordplugin/DPUtils.java +++ b/src/main/java/buttondevteam/discordplugin/DPUtils.java @@ -4,20 +4,14 @@ import buttondevteam.lib.TBMCCoreAPI; import buttondevteam.lib.architecture.Component; import buttondevteam.lib.architecture.ConfigData; import buttondevteam.lib.architecture.IHaveConfig; +import discord4j.core.object.entity.Channel; +import discord4j.core.object.entity.Guild; +import discord4j.core.object.entity.Role; +import discord4j.core.object.util.Snowflake; 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; -import sx.blah.discord.util.RequestBuffer; -import sx.blah.discord.util.RequestBuffer.IRequest; -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; @@ -53,55 +47,6 @@ public final class DPUtils { return sanitizedString; } - /** - * Performs Discord actions, retrying when ratelimited. May return null if action fails too many times or in safe mode. - */ - @Nullable - public static T perform(IRequest action, long timeout, TimeUnit unit) throws TimeoutException, InterruptedException { - if (DiscordPlugin.SafeMode) - return null; - if (Bukkit.isPrimaryThread()) // TODO: Ignore shutdown message <-- - // throw new RuntimeException("Tried to wait for a Discord request on the main thread. This could cause lag."); - getLogger().warning("Waiting for a Discord request on the main thread!"); - return RequestBuffer.request(action).get(timeout, unit); // Let the pros handle this - } - - /** - * Performs Discord actions, retrying when ratelimited. May return null if action fails too many times or in safe mode. - */ - @Nullable - public static T perform(IRequest action) { - if (DiscordPlugin.SafeMode) - return null; - if (Bukkit.isPrimaryThread()) // TODO: Ignore shutdown message <-- - // throw new RuntimeException("Tried to wait for a Discord request on the main thread. This could cause lag."); - getLogger().warning("Waiting for a Discord request on the main thread!"); - return RequestBuffer.request(action).get(); // Let the pros handle this - } - - /** - * Performs Discord actions, retrying when ratelimited. - */ - public static Void perform(IVoidRequest action) { - if (DiscordPlugin.SafeMode) - return null; - if (Bukkit.isPrimaryThread()) - throw new RuntimeException("Tried to wait for a Discord request on the main thread. This could cause lag."); - return RequestBuffer.request(action).get(); // Let the pros handle this - } - - public static void performNoWait(IVoidRequest action) { - if (DiscordPlugin.SafeMode) - return; - RequestBuffer.request(action); - } - - public static void performNoWait(IRequest action) { - if (DiscordPlugin.SafeMode) - return; - RequestBuffer.request(action); - } - public static String escape(String message) { return message.replaceAll("([*_~])", Matcher.quoteReplacement("\\")+"$1"); } @@ -112,20 +57,19 @@ public final class DPUtils { return DiscordPlugin.plugin.getLogger(); } - public static ConfigData channelData(IHaveConfig config, String key, long defID) { - return config.getDataPrimDef(key, defID, id -> DiscordPlugin.dc.getChannelByID((long) id), IIDLinkedObject::getLongID); //We can afford to search for the channel in the cache once (instead of using mainServer) + public static ConfigData channelData(IHaveConfig config, String key, long defID) { + return config.getDataPrimDef(key, defID, id -> DiscordPlugin.dc.getChannelById(Snowflake.of((long) id)).block(), ch -> ch.getId().asLong()); //We can afford to search for the channel in the cache once (instead of using mainServer) } - public static ConfigData roleData(IHaveConfig config, String key, String defName) { + 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) { + public static ConfigData roleData(IHaveConfig config, String key, String defName, Guild guild) { return config.getDataPrimDef(key, defName, name -> { if (!(name instanceof String)) return null; - val roles = guild.getRolesByName((String) name); - return roles.size() > 0 ? roles.get(0) : null; - }, IIDLinkedObject::getLongID); + return guild.getRoles().filter(r -> r.getName().equals(name)).blockFirst(); + }, r -> r.getId().asLong()); } /** @@ -134,10 +78,10 @@ public final class DPUtils { * @return The string for mentioning the channel */ public static String botmention() { - IChannel channel; + Channel channel; if (DiscordPlugin.plugin == null || (channel = DiscordPlugin.plugin.CommandChannel().get()) == null) return "#bot"; - return channel.mention(); + return channel.getMention(); } /** diff --git a/src/main/java/buttondevteam/discordplugin/DiscordPlugin.java b/src/main/java/buttondevteam/discordplugin/DiscordPlugin.java index 572e52d..cb73ea1 100755 --- a/src/main/java/buttondevteam/discordplugin/DiscordPlugin.java +++ b/src/main/java/buttondevteam/discordplugin/DiscordPlugin.java @@ -11,15 +11,23 @@ import buttondevteam.discordplugin.mcchat.MCChatPrivate; import buttondevteam.discordplugin.mcchat.MCChatUtils; import buttondevteam.discordplugin.mcchat.MinecraftChatModule; import buttondevteam.discordplugin.mccommands.DiscordMCCommand; -import buttondevteam.discordplugin.mccommands.DiscordMCCommandBase; import buttondevteam.discordplugin.role.GameRoleModule; import buttondevteam.lib.TBMCCoreAPI; import buttondevteam.lib.architecture.ButtonPlugin; import buttondevteam.lib.architecture.Component; import buttondevteam.lib.architecture.ConfigData; -import buttondevteam.lib.chat.TBMCChatAPI; import buttondevteam.lib.player.ChromaGamerBase; import com.google.common.io.Files; +import discord4j.core.DiscordClient; +import discord4j.core.DiscordClientBuilder; +import discord4j.core.event.domain.lifecycle.ReadyEvent; +import discord4j.core.object.entity.Channel; +import discord4j.core.object.entity.Guild; +import discord4j.core.object.entity.Role; +import discord4j.core.object.presence.Activity; +import discord4j.core.object.presence.Presence; +import discord4j.core.object.reaction.ReactionEmoji; +import discord4j.core.object.util.Snowflake; import lombok.Getter; import lombok.val; import net.milkbowl.vault.permission.Permission; @@ -28,212 +36,194 @@ import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.entity.Player; import org.bukkit.plugin.RegisteredServiceProvider; import org.bukkit.scheduler.BukkitTask; -import sx.blah.discord.api.ClientBuilder; -import sx.blah.discord.api.IDiscordClient; -import sx.blah.discord.api.events.IListener; -import sx.blah.discord.api.internal.json.objects.EmbedObject; -import sx.blah.discord.handle.impl.events.ReadyEvent; -import sx.blah.discord.handle.impl.obj.ReactionEmoji; -import sx.blah.discord.handle.obj.*; -import sx.blah.discord.util.EmbedBuilder; -import sx.blah.discord.util.RequestBuffer; import java.awt.*; import java.io.File; import java.nio.charset.StandardCharsets; +import java.time.Duration; import java.util.Optional; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; -public class DiscordPlugin extends ButtonPlugin implements IListener { - public static IDiscordClient dc; - public static DiscordPlugin plugin; - public static boolean SafeMode = true; +public class DiscordPlugin extends ButtonPlugin { + public static DiscordClient dc; + public static DiscordPlugin plugin; + public static boolean SafeMode = true; @Getter private Command2DC manager; public ConfigData Prefix() { - return getIConfig().getData("prefix", '/', str -> ((String) str).charAt(0), Object::toString); - } - - public static char getPrefix() { - if (plugin == null) return '/'; - return plugin.Prefix().get(); - } - - public ConfigData MainServer() { - return getIConfig().getDataPrimDef("mainServer", 219529124321034241L, id -> dc.getGuildByID((long) id), IIDLinkedObject::getLongID); + return getIConfig().getData("prefix", '/', str -> ((String) str).charAt(0), Object::toString); } - public ConfigData CommandChannel() { + public static char getPrefix() { + if (plugin == null) return '/'; + return plugin.Prefix().get(); + } + + public ConfigData MainServer() { + return getIConfig().getDataPrimDef("mainServer", 219529124321034241L, id -> dc.getGuildById(Snowflake.of((long) id)).block(), g -> g.getId().asLong()); + } + + public ConfigData CommandChannel() { return DPUtils.channelData(getIConfig(), "commandChannel", 239519012529111040L); } - public ConfigData ModRole() { + public ConfigData ModRole() { return DPUtils.roleData(getIConfig(), "modRole", "Moderator"); } - @Override - public void pluginEnable() { - try { - getLogger().info("Initializing..."); - plugin = this; - manager = new Command2DC(); - ClientBuilder cb = new ClientBuilder(); - File tokenFile = new File("TBMC", "Token.txt"); - if (tokenFile.exists()) //Legacy support - //noinspection UnstableApiUsage - cb.withToken(Files.readFirstLine(tokenFile, StandardCharsets.UTF_8)); - else { - File privateFile = new File(getDataFolder(), "private.yml"); - val conf = YamlConfiguration.loadConfiguration(privateFile); - String token = conf.getString("token"); - if (token == null) { - conf.set("token", "Token goes here"); - conf.save(privateFile); + @Override + public void pluginEnable() { + try { + getLogger().info("Initializing..."); + plugin = this; + manager = new Command2DC(); + String token; + File tokenFile = new File("TBMC", "Token.txt"); + if (tokenFile.exists()) //Legacy support + //noinspection UnstableApiUsage + token = Files.readFirstLine(tokenFile, StandardCharsets.UTF_8); + else { + File privateFile = new File(getDataFolder(), "private.yml"); + val conf = YamlConfiguration.loadConfiguration(privateFile); + token = conf.getString("token"); + if (token == null) { + conf.set("token", "Token goes here"); + conf.save(privateFile); - getLogger().severe("Token not found! Set it in private.yml"); - Bukkit.getPluginManager().disablePlugin(this); - return; - } else - cb.withToken(token); - } - dc = cb.login(); - dc.getDispatcher().registerListener(this); - } catch (Exception e) { - e.printStackTrace(); - Bukkit.getPluginManager().disablePlugin(this); - } - } + getLogger().severe("Token not found! Set it in private.yml"); + Bukkit.getPluginManager().disablePlugin(this); + return; + } + } + val cb = new DiscordClientBuilder(token); + dc = cb.build(); + dc.getEventDispatcher().on(ReadyEvent.class).subscribe(this::handleReady); + } catch (Exception e) { + e.printStackTrace(); + Bukkit.getPluginManager().disablePlugin(this); + } + } - public static IGuild mainServer; + public static Guild mainServer; - private static volatile BukkitTask task; - private static volatile boolean sent = false; + private static volatile BukkitTask task; + private static volatile boolean sent = false; - @Override - public void handle(ReadyEvent event) { - try { - dc.changePresence(StatusType.DND, ActivityType.PLAYING, "booting"); - val tries = new AtomicInteger(); - task = Bukkit.getScheduler().runTaskTimerAsynchronously(this, () -> { - tries.incrementAndGet(); - if (tries.get() > 10) { //5 seconds - task.cancel(); - 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; - } - 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, "Minecraft"); - } else { - dc.changePresence(StatusType.ONLINE, ActivityType.PLAYING, "testing"); - } - SafeMode = false; - if (task != null) - task.cancel(); - if (!sent) { - DPUtils.disableIfConfigError(null, CommandChannel(), ModRole()); //Won't disable, just prints the warning here + public void handleReady(ReadyEvent event) { + try { + dc.updatePresence(Presence.doNotDisturb(Activity.playing("booting"))).subscribe(); + val tries = new AtomicInteger(); + task = Bukkit.getScheduler().runTaskTimerAsynchronously(this, () -> { + tries.incrementAndGet(); + if (tries.get() > 10) { //5 seconds + task.cancel(); + 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; + } + mainServer = MainServer().get(); //Shouldn't change afterwards + if (mainServer == null) { + val guilds = dc.getGuilds(); + if (guilds.count().blockOptional().orElse(0L) == 0L) + return; //If there are no guilds in cache, retry + mainServer = guilds.blockFirst(); + if (mainServer == null) return; + 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.updatePresence(Presence.online(Activity.playing("Minecraft"))); + } else { + dc.updatePresence(Presence.online(Activity.playing("testing"))); + } + SafeMode = false; + 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()); - Component.registerComponent(this, new GameRoleModule()); //Needs the mainServer to be set - Component.registerComponent(this, new AnnouncerModule()); + Component.registerComponent(this, new GeneralEventBroadcasterModule()); + Component.registerComponent(this, new MinecraftChatModule()); + Component.registerComponent(this, new ExceptionListenerModule()); + Component.registerComponent(this, new GameRoleModule()); //Needs the mainServer to be set + Component.registerComponent(this, new AnnouncerModule()); Component.registerComponent(this, new FunModule()); - new ChromaBot(this).updatePlayerList(); //Initialize ChromaBot - The MCCHatModule is tested to be enabled + new ChromaBot(this).updatePlayerList(); //Initialize ChromaBot - The MCCHatModule is tested to be enabled - getManager().registerCommand(new VersionCommand()); - getManager().registerCommand(new UserinfoCommand()); - getManager().registerCommand(new HelpCommand()); - getManager().registerCommand(new DebugCommand()); - getManager().registerCommand(new ConnectCommand()); - if (DiscordMCCommand.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)) { - ChromaBot.getInstance().sendMessageCustomAsWell("", new EmbedBuilder().withColor(Color.YELLOW) - .withTitle("Server recovered from a crash - chat connected.").build(), ChannelconBroadcast.RESTART); - val thr = new Throwable( - "The server shut down unexpectedly. See the log of the previous run for more details."); - thr.setStackTrace(new StackTraceElement[0]); - TBMCCoreAPI.SendException("The server crashed!", thr); - } else - ChromaBot.getInstance().sendMessageCustomAsWell("", new EmbedBuilder().withColor(Color.GREEN) - .withTitle("Server started - chat connected.").build(), ChannelconBroadcast.RESTART); + getManager().registerCommand(new VersionCommand()); + getManager().registerCommand(new UserinfoCommand()); + getManager().registerCommand(new HelpCommand()); + getManager().registerCommand(new DebugCommand()); + getManager().registerCommand(new ConnectCommand()); + if (DiscordMCCommand.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)) { + ChromaBot.getInstance().sendMessageCustomAsWell("", new EmbedBuilder().withColor(Color.YELLOW) + .withTitle("Server recovered from a crash - chat connected.").build(), ChannelconBroadcast.RESTART); + val thr = new Throwable( + "The server shut down unexpectedly. See the log of the previous run for more details."); + thr.setStackTrace(new StackTraceElement[0]); + TBMCCoreAPI.SendException("The server crashed!", thr); + } else + ChromaBot.getInstance().sendMessageCustomAsWell("", new EmbedBuilder().withColor(Color.GREEN) + .withTitle("Server started - chat connected.").build(), ChannelconBroadcast.RESTART); - DiscordMCCommand.resetting = false; //This is the last event handling this flag + DiscordMCCommand.resetting = false; //This is the last event handling this flag - getConfig().set("serverup", true); - saveConfig(); - sent = true; - if (TBMCCoreAPI.IsTestServer() && !dc.getOurUser().getName().toLowerCase().contains("test")) { - TBMCCoreAPI.SendException( - "Won't load because we're in testing mode and not using a separate account.", - new Exception( - "The plugin refuses to load until you change the token to a testing account. (The account needs to have \"test\" in it's name.)")); - Bukkit.getPluginManager().disablePlugin(this); - } - TBMCCoreAPI.SendUnsentExceptions(); - TBMCCoreAPI.SendUnsentDebugMessages(); - } - }, 0, 10); - for (IListener listener : CommonListeners.getListeners()) - dc.getDispatcher().registerListener(listener); - TBMCCoreAPI.RegisterEventsForExceptions(new MCListener(), this); - getCommand2MC().registerCommand(new DiscordMCCommand()); - TBMCCoreAPI.RegisterUserClass(DiscordPlayer.class); - ChromaGamerBase.addConverter(sender -> Optional.ofNullable(sender instanceof DiscordSenderBase - ? ((DiscordSenderBase) sender).getChromaUser() : null)); - setupProviders(); - } catch (Exception e) { - TBMCCoreAPI.SendException("An error occured while enabling DiscordPlugin!", e); - } - } + getConfig().set("serverup", true); + saveConfig(); + sent = true; + if (TBMCCoreAPI.IsTestServer() && !event.getSelf().getUsername().toLowerCase().contains("test")) { + TBMCCoreAPI.SendException( + "Won't load because we're in testing mode and not using a separate account.", + new Exception( + "The plugin refuses to load until you change the token to a testing account. (The account needs to have \"test\" in it's name.)")); + Bukkit.getPluginManager().disablePlugin(this); + } + TBMCCoreAPI.SendUnsentExceptions(); + TBMCCoreAPI.SendUnsentDebugMessages(); + } + }, 0, 10); + for (IListener listener : CommonListeners.getListeners()) + dc.getDispatcher().registerListener(listener); + TBMCCoreAPI.RegisterEventsForExceptions(new MCListener(), this); + getCommand2MC().registerCommand(new DiscordMCCommand()); + TBMCCoreAPI.RegisterUserClass(DiscordPlayer.class); + ChromaGamerBase.addConverter(sender -> Optional.ofNullable(sender instanceof DiscordSenderBase + ? ((DiscordSenderBase) sender).getChromaUser() : null)); + setupProviders(); + } catch (Exception e) { + TBMCCoreAPI.SendException("An error occured while enabling DiscordPlugin!", e); + } + } /** - * Always true, except when running "stop" from console - */ - public static boolean Restart; + * Always true, except when running "stop" from console + */ + public static boolean Restart; @Override public void pluginPreDisable() { if (ChromaBot.getInstance() == null) return; //Failed to load - EmbedObject embed; - if (DiscordMCCommand.resetting) - embed = new EmbedBuilder().withColor(Color.ORANGE).withTitle("Discord plugin restarting").build(); - else - embed = new EmbedBuilder().withColor(Restart ? Color.ORANGE : Color.RED) - .withTitle(Restart ? "Server restarting" : "Server stopping") - .withDescription( - Bukkit.getOnlinePlayers().size() > 0 - ? (DPUtils - .sanitizeString(Bukkit.getOnlinePlayers().stream() - .map(Player::getDisplayName).collect(Collectors.joining(", "))) - + (Bukkit.getOnlinePlayers().size() == 1 ? " was " : " were ") - + "kicked the hell out.") //TODO: Make configurable - : "") //If 'restart' is disabled then this isn't shown even if joinleave is enabled - .build(); - MCChatUtils.forCustomAndAllMCChat(ch -> { - try { - DiscordPlugin.sendMessageToChannelWait(ch, "", - embed, 5, TimeUnit.SECONDS); - } catch (TimeoutException | InterruptedException e) { - e.printStackTrace(); - } - }, ChannelconBroadcast.RESTART, false); + MCChatUtils.forCustomAndAllMCChat(ch -> ch.createEmbed(ecs -> { + if (DiscordMCCommand.resetting) + ecs.setColor(Color.ORANGE).setTitle("Discord plugin restarting"); + else + ecs.setColor(Restart ? Color.ORANGE : Color.RED) + .setTitle(Restart ? "Server restarting" : "Server stopping") + .setDescription( + Bukkit.getOnlinePlayers().size() > 0 + ? (DPUtils + .sanitizeString(Bukkit.getOnlinePlayers().stream() + .map(Player::getDisplayName).collect(Collectors.joining(", "))) + + (Bukkit.getOnlinePlayers().size() == 1 ? " was " : " were ") + + "kicked the hell out.") //TODO: Make configurable + : ""); //If 'restart' is disabled then this isn't shown even if joinleave is enabled + }).block(Duration.ofSeconds(5)), ChannelconBroadcast.RESTART, false); ChromaBot.getInstance().updatePlayerList(); } @@ -244,93 +234,33 @@ public class DiscordPlugin extends ButtonPlugin implements IListener if (ChromaBot.getInstance() == null) return; //Failed to load saveConfig(); - try { - SafeMode = true; // Stop interacting with Discord - ChromaBot.delete(); - dc.changePresence(StatusType.IDLE, ActivityType.PLAYING, "Chromacraft"); //No longer using the same account for testing - dc.logout(); - //Configs are emptied so channels and servers are fetched again - sent = false; - } catch (Exception e) { - TBMCCoreAPI.SendException("An error occured while disabling DiscordPlugin!", e); - } - } + try { + SafeMode = true; // Stop interacting with Discord + ChromaBot.delete(); + dc.updatePresence(Presence.idle(Activity.playing("Chromacraft"))).block(); //No longer using the same account for testing + dc.logout().block(); + //Configs are emptied so channels and servers are fetched again + sent = false; + } catch (Exception e) { + TBMCCoreAPI.SendException("An error occured while disabling DiscordPlugin!", e); + } + } - public static final ReactionEmoji DELIVERED_REACTION = ReactionEmoji.of("✅"); + public static final ReactionEmoji DELIVERED_REACTION = ReactionEmoji.unicode("✅"); - public static void sendMessageToChannel(IChannel channel, String message) { - sendMessageToChannel(channel, message, null); - } + public static Permission perms; - public static void sendMessageToChannel(IChannel channel, String message, EmbedObject embed) { - try { - sendMessageToChannel(channel, message, embed, false); - } catch (TimeoutException | InterruptedException e) { - e.printStackTrace(); //Shouldn't happen, as we're not waiting on the result - } - } + public boolean setupProviders() { + try { + Class.forName("net.milkbowl.vault.permission.Permission"); + Class.forName("net.milkbowl.vault.chat.Chat"); + } catch (ClassNotFoundException e) { + return false; + } - public static IMessage sendMessageToChannelWait(IChannel channel, String message) throws TimeoutException, InterruptedException { - return sendMessageToChannelWait(channel, message, null); - } - - public static IMessage sendMessageToChannelWait(IChannel channel, String message, EmbedObject embed) throws TimeoutException, InterruptedException { - return sendMessageToChannel(channel, message, embed, true); - } - - public static IMessage sendMessageToChannelWait(IChannel channel, String message, EmbedObject embed, long timeout, TimeUnit unit) throws TimeoutException, InterruptedException { - return sendMessageToChannel(channel, message, embed, true, timeout, unit); - } - - private static IMessage sendMessageToChannel(IChannel channel, String message, EmbedObject embed, boolean wait) throws TimeoutException, InterruptedException { - return sendMessageToChannel(channel, message, embed, wait, -1, null); - } - - private static IMessage sendMessageToChannel(IChannel channel, String message, EmbedObject embed, boolean wait, long timeout, TimeUnit unit) throws TimeoutException, InterruptedException { - if (message.length() > 1980) { - message = message.substring(0, 1980); - DPUtils.getLogger() - .warning("Message was too long to send to discord and got truncated. In " + channel.getName()); - } - try { - 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); - if (wait) { - if (unit != null) - return DPUtils.perform(r, timeout, unit); - else - return DPUtils.perform(r); - } else { - if (unit != null) - plugin.getLogger().warning("Tried to set timeout for non-waiting call."); - else - DPUtils.performNoWait(r); - return null; - } - } catch (TimeoutException | InterruptedException e) { - throw e; - } catch (Exception e) { - DPUtils.getLogger().warning( - "Failed to deliver message to Discord! Channel: " + channel.getName() + " Message: " + message); - throw new RuntimeException(e); - } - } - - public static Permission perms; - - public boolean setupProviders() { - try { - Class.forName("net.milkbowl.vault.permission.Permission"); - Class.forName("net.milkbowl.vault.chat.Chat"); - } catch (ClassNotFoundException e) { - return false; - } - - RegisteredServiceProvider permsProvider = Bukkit.getServer().getServicesManager() - .getRegistration(Permission.class); - perms = permsProvider.getProvider(); - return perms != null; - } + RegisteredServiceProvider permsProvider = Bukkit.getServer().getServicesManager() + .getRegistration(Permission.class); + perms = permsProvider.getProvider(); + return perms != null; + } } diff --git a/src/main/java/buttondevteam/discordplugin/mcchat/MCChatUtils.java b/src/main/java/buttondevteam/discordplugin/mcchat/MCChatUtils.java index 3f04e56..6399d90 100644 --- a/src/main/java/buttondevteam/discordplugin/mcchat/MCChatUtils.java +++ b/src/main/java/buttondevteam/discordplugin/mcchat/MCChatUtils.java @@ -5,6 +5,10 @@ import buttondevteam.core.component.channel.Channel; import buttondevteam.discordplugin.*; import buttondevteam.discordplugin.broadcaster.GeneralEventBroadcasterModule; import buttondevteam.lib.TBMCSystemChatEvent; +import discord4j.core.object.entity.Message; +import discord4j.core.object.entity.MessageChannel; +import discord4j.core.object.entity.TextChannel; +import discord4j.core.object.entity.User; import io.netty.util.collection.LongObjectHashMap; import lombok.RequiredArgsConstructor; import lombok.experimental.var; @@ -16,9 +20,6 @@ import org.bukkit.event.HandlerList; import org.bukkit.plugin.AuthorNagException; import org.bukkit.plugin.Plugin; import org.bukkit.plugin.RegisteredListener; -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.Arrays; @@ -34,23 +35,21 @@ 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<>(); + 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 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 + 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; - DPUtils.performNoWait(() -> { - if (lastmsgdata != null) - updatePL(lastmsgdata); - MCChatCustom.lastmsgCustom.forEach(MCChatUtils::updatePL); - }); + if (lastmsgdata != null) + updatePL(lastmsgdata); + MCChatCustom.lastmsgCustom.forEach(MCChatUtils::updatePL); } private static boolean notEnabled() { @@ -64,8 +63,8 @@ public class MCChatUtils { } private static void updatePL(LastMsgData lmd) { - String topic = lmd.channel.getTopic(); - if (topic == null || topic.length() == 0) + String topic = lmd.channel.getTopic().orElse(""); + if (topic.length() == 0) topic = ".\n----\nMinecraft chat\n----\n."; String[] s = topic.split("\\n----\\n"); if (s.length < 3) @@ -74,16 +73,16 @@ public class MCChatUtils { + " 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)); + lmd.channel.edit(tce -> tce.setTopic(String.join("\n----\n", s)).setReason("Player list update")).subscribe(); //Don't wait } - public static T addSender(HashMap> senders, - IUser user, T sender) { - return addSender(senders, user.getStringID(), sender); + public static T addSender(HashMap> senders, + User user, T sender) { + return addSender(senders, user.getId().asLong(), sender); } - public static T addSender(HashMap> senders, - String did, T sender) { + public static T addSender(HashMap> senders, + long did, T sender) { var map = senders.get(did); if (map == null) map = new HashMap<>(); @@ -92,23 +91,23 @@ public class MCChatUtils { return sender; } - public static T getSender(HashMap> senders, - IChannel channel, IUser user) { - var map = senders.get(user.getStringID()); + public static T getSender(HashMap> senders, + Channel channel, User user) { + var map = senders.get(user.getId().asLong()); 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()); + public static T removeSender(HashMap> senders, + Channel channel, User user) { + var map = senders.get(user.getId().asLong()); if (map != null) return map.remove(channel); return null; } - public static void forAllMCChat(Consumer action) { + public static void forAllMCChat(Consumer action) { if (notEnabled()) return; action.accept(module.chatChannel().get()); for (LastMsgData data : MCChatPrivate.lastmsgPerUser) @@ -123,7 +122,7 @@ public class MCChatUtils { * @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) { + public static void forCustomAndAllMCChat(Consumer action, @Nullable ChannelconBroadcast toggle, boolean hookmsg) { if (notEnabled()) return; if (!GeneralEventBroadcasterModule.isHooked() || !hookmsg) forAllMCChat(action); @@ -141,7 +140,7 @@ public class MCChatUtils { * @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) { + 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 @@ -161,19 +160,19 @@ public class MCChatUtils { * @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) { + 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 Consumer send(String message) { + return ch -> ch.createMessage(DPUtils.sanitizeString(message)).subscribe(); } - public static void forAllowedMCChat(Consumer action, TBMCSystemChatEvent event) { - if (notEnabled()) return; + public static void forAllowedMCChat(Consumer action, TBMCSystemChatEvent event) { + if (notEnabled()) return if (event.getChannel().isGlobal()) action.accept(module.chatChannel().get()); for (LastMsgData data : MCChatPrivate.lastmsgPerUser) @@ -189,7 +188,7 @@ public class MCChatUtils { /** * 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) { + static DiscordSenderBase getSender(Channel channel, final User author) { //noinspection OptionalGetWithoutIsPresent return Stream.>>of( // https://stackoverflow.com/a/28833677/2703239 () -> Optional.ofNullable(getSender(OnlineSenders, channel, author)), // Find first non-null @@ -205,7 +204,7 @@ public class MCChatUtils { * * @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) { + public static void resetLastMessage(Channel channel) { if (notEnabled()) return; if (channel.getLongID() == module.chatChannel().get().getLongID()) { (lastmsgdata == null ? lastmsgdata = new LastMsgData(module.chatChannel().get(), null) @@ -286,11 +285,11 @@ public class MCChatUtils { @RequiredArgsConstructor public static class LastMsgData { - public IMessage message; + public Message message; public long time; public String content; - public final IChannel channel; + public final TextChannel channel; public Channel mcchannel; - public final IUser user; + public final User user; } }