From c9f5db014134108e7d9b388ccb14644dbb0936ce Mon Sep 17 00:00:00 2001 From: Allink Date: Wed, 25 May 2022 19:44:45 +0100 Subject: [PATCH] Add Discord commands like the old bot --- .../totalfreedommod/config/ConfigEntry.java | 1 + .../totalfreedommod/discord/Discord.java | 5 + .../discord/DiscordToMinecraftListener.java | 31 +++-- .../discord/command/DiscordCommand.java | 25 +++++ .../discord/command/DiscordCommandImpl.java | 15 +++ .../command/DiscordCommandManager.java | 84 ++++++++++++++ .../discord/commands/HelpCommand.java | 86 ++++++++++++++ .../discord/commands/ListCommand.java | 106 ++++++++++++++++++ .../discord/commands/TPSCommand.java | 60 ++++++++++ .../discord/commands/UptimeCommand.java | 79 +++++++++++++ src/main/resources/config.yml | 2 + 11 files changed, 486 insertions(+), 8 deletions(-) create mode 100644 src/main/java/me/totalfreedom/totalfreedommod/discord/command/DiscordCommand.java create mode 100644 src/main/java/me/totalfreedom/totalfreedommod/discord/command/DiscordCommandImpl.java create mode 100644 src/main/java/me/totalfreedom/totalfreedommod/discord/command/DiscordCommandManager.java create mode 100644 src/main/java/me/totalfreedom/totalfreedommod/discord/commands/HelpCommand.java create mode 100644 src/main/java/me/totalfreedom/totalfreedommod/discord/commands/ListCommand.java create mode 100644 src/main/java/me/totalfreedom/totalfreedommod/discord/commands/TPSCommand.java create mode 100644 src/main/java/me/totalfreedom/totalfreedommod/discord/commands/UptimeCommand.java diff --git a/src/main/java/me/totalfreedom/totalfreedommod/config/ConfigEntry.java b/src/main/java/me/totalfreedom/totalfreedommod/config/ConfigEntry.java index abede2c7..1446e49b 100644 --- a/src/main/java/me/totalfreedom/totalfreedommod/config/ConfigEntry.java +++ b/src/main/java/me/totalfreedom/totalfreedommod/config/ConfigEntry.java @@ -74,6 +74,7 @@ public enum ConfigEntry SERVER_FULL_MOTD(String.class, "server.motds.full"), // DISCORD_TOKEN(String.class, "discord.token"), + DISCORD_PREFIX(String.class, "discord.prefix"), DISCORD_REPORT_CHANNEL_ID(String.class, "discord.report_channel_id"), DISCORD_CHAT_CHANNEL_ID(String.class, "discord.chat_channel_id"), DISCORD_ADMINCHAT_CHANNEL_ID(String.class, "discord.adminchat_channel_id"), diff --git a/src/main/java/me/totalfreedom/totalfreedommod/discord/Discord.java b/src/main/java/me/totalfreedom/totalfreedommod/discord/Discord.java index 6e2a202e..479042be 100644 --- a/src/main/java/me/totalfreedom/totalfreedommod/discord/Discord.java +++ b/src/main/java/me/totalfreedom/totalfreedommod/discord/Discord.java @@ -18,6 +18,7 @@ import com.google.common.collect.ImmutableList; import me.totalfreedom.totalfreedommod.FreedomService; import me.totalfreedom.totalfreedommod.admin.Admin; import me.totalfreedom.totalfreedommod.config.ConfigEntry; +import me.totalfreedom.totalfreedommod.discord.command.DiscordCommandManager; import me.totalfreedom.totalfreedommod.player.PlayerData; import me.totalfreedom.totalfreedommod.rank.Rank; import me.totalfreedom.totalfreedommod.util.FLog; @@ -59,6 +60,7 @@ public class Discord extends FreedomService public static HashMap LINK_CODES = new HashMap<>(); public static JDA bot = null; + public static DiscordCommandManager DISCORD_COMMAND_MANAGER; public ScheduledThreadPoolExecutor RATELIMIT_EXECUTOR; public List> sentMessages = new ArrayList<>(); public Boolean enabled = false; @@ -153,6 +155,9 @@ public class Discord extends FreedomService public void startBot() { + DISCORD_COMMAND_MANAGER = new DiscordCommandManager(); + DISCORD_COMMAND_MANAGER.init(this); + enabled = !Strings.isNullOrEmpty(ConfigEntry.DISCORD_TOKEN.getString()); if (!enabled) { diff --git a/src/main/java/me/totalfreedom/totalfreedommod/discord/DiscordToMinecraftListener.java b/src/main/java/me/totalfreedom/totalfreedommod/discord/DiscordToMinecraftListener.java index 680d3eb4..64c719bf 100644 --- a/src/main/java/me/totalfreedom/totalfreedommod/discord/DiscordToMinecraftListener.java +++ b/src/main/java/me/totalfreedom/totalfreedommod/discord/DiscordToMinecraftListener.java @@ -2,13 +2,12 @@ package me.totalfreedom.totalfreedommod.discord; import me.totalfreedom.totalfreedommod.TotalFreedomMod; import me.totalfreedom.totalfreedommod.config.ConfigEntry; +import me.totalfreedom.totalfreedommod.discord.command.DiscordCommandManager; import me.totalfreedom.totalfreedommod.rank.Rank; import me.totalfreedom.totalfreedommod.rank.Title; import me.totalfreedom.totalfreedommod.util.FLog; import me.totalfreedom.totalfreedommod.util.FUtil; -import net.dv8tion.jda.api.entities.Guild; -import net.dv8tion.jda.api.entities.Member; -import net.dv8tion.jda.api.entities.Message; +import net.dv8tion.jda.api.entities.*; import net.dv8tion.jda.api.events.message.MessageReceivedEvent; import net.dv8tion.jda.api.hooks.ListenerAdapter; import net.md_5.bungee.api.chat.BaseComponent; @@ -25,7 +24,9 @@ public class DiscordToMinecraftListener extends ListenerAdapter { public void onMessageReceived(MessageReceivedEvent event) { - String chat_channel_id = ConfigEntry.DISCORD_CHAT_CHANNEL_ID.getString(); + final String chat_channel_id = ConfigEntry.DISCORD_CHAT_CHANNEL_ID.getString(); + final MessageChannel genericChannel = event.getChannel(); + if (event.getMember() == null) { return; @@ -41,14 +42,28 @@ public class DiscordToMinecraftListener extends ListenerAdapter return; } - if (!event.getChannel().getId().equals(chat_channel_id)) + if (!genericChannel.getId().equals(chat_channel_id)) { return; } - Member member = event.getMember(); - String tag = getDisplay(member); - Message msg = event.getMessage(); + if (!(genericChannel instanceof TextChannel)) + { + return; + } + + final TextChannel textChannel = (TextChannel) genericChannel; + + final Member member = event.getMember(); + final String tag = getDisplay(member); + final Message msg = event.getMessage(); + final String content = msg.getContentStripped(); + + if (content.startsWith(ConfigEntry.DISCORD_PREFIX.getString())) + { + Discord.DISCORD_COMMAND_MANAGER.parse(content, member, textChannel); + return; + } ComponentBuilder emsg = new ComponentBuilder(); diff --git a/src/main/java/me/totalfreedom/totalfreedommod/discord/command/DiscordCommand.java b/src/main/java/me/totalfreedom/totalfreedommod/discord/command/DiscordCommand.java new file mode 100644 index 00000000..94c16ad4 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/discord/command/DiscordCommand.java @@ -0,0 +1,25 @@ +package me.totalfreedom.totalfreedommod.discord.command; + +import net.dv8tion.jda.api.MessageBuilder; +import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.entities.Member; +import net.dv8tion.jda.api.entities.User; + +import java.util.List; + +public interface DiscordCommand +{ + String getCommandName(); + + String getDescription(); + + String getCategory(); + + List getAliases(); + + boolean isAdmin(); + + boolean canExecute(Member member); + + MessageBuilder execute(Member member, List args); +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/discord/command/DiscordCommandImpl.java b/src/main/java/me/totalfreedom/totalfreedommod/discord/command/DiscordCommandImpl.java new file mode 100644 index 00000000..cc273a34 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/discord/command/DiscordCommandImpl.java @@ -0,0 +1,15 @@ +package me.totalfreedom.totalfreedommod.discord.command; + +import net.dv8tion.jda.api.entities.Member; + +import java.util.stream.Collectors; + +public abstract class DiscordCommandImpl implements DiscordCommand +{ + @Override + public boolean canExecute(Member member) + { + //return !isAdmin() || member.getRoles().stream().filter((role -> role.getName().toLowerCase().contains("admin") && !role.getName().toLowerCase().contains("discord"))).toList().size() > 0; + return !isAdmin(); + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/discord/command/DiscordCommandManager.java b/src/main/java/me/totalfreedom/totalfreedommod/discord/command/DiscordCommandManager.java new file mode 100644 index 00000000..93f1c60b --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/discord/command/DiscordCommandManager.java @@ -0,0 +1,84 @@ +package me.totalfreedom.totalfreedommod.discord.command; + +import me.totalfreedom.totalfreedommod.config.ConfigEntry; +import me.totalfreedom.totalfreedommod.discord.Discord; +import me.totalfreedom.totalfreedommod.util.FLog; +import net.dv8tion.jda.api.EmbedBuilder; +import net.dv8tion.jda.api.MessageBuilder; +import net.dv8tion.jda.api.entities.Member; +import net.dv8tion.jda.api.entities.Message; +import net.dv8tion.jda.api.entities.TextChannel; +import org.reflections.Reflections; + +import java.awt.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Set; +import java.util.concurrent.CompletableFuture; + +public class DiscordCommandManager +{ + public static final String PREFIX = ConfigEntry.DISCORD_PREFIX.getString(); + private Discord discord; + public final List commands = new ArrayList<>(); + + public void init(Discord discord) + { + this.discord = discord; + + final Reflections discordCommandsDir = new Reflections("me.totalfreedom.totalfreedommod.discord.commands"); + + final Set> commandClasses = discordCommandsDir.getSubTypesOf(DiscordCommand.class); + + for (Class commandClass : commandClasses) + { + try + { + commands.add(commandClass.getDeclaredConstructor().newInstance()); + } + catch (Exception e) + { + FLog.warning("Failed to load Discord command: " + commandClass.getName()); + } + } + + FLog.info("Loaded " + commands.size() + " Discord commands."); + } + + public void parse(String content, Member member, TextChannel channel) + { + List args = new ArrayList<>(Arrays.asList(content.split(" "))); + + final String alias = args.remove(0).split(PREFIX)[1]; // The joys of command parsing + + for (DiscordCommand command : commands) + { + if (command.getCommandName().equalsIgnoreCase(alias) || command.getAliases().contains(alias.toLowerCase())) + { + if (command.canExecute(member)) + { + final MessageBuilder messageBuilder = command.execute(member, args); + final Message message = messageBuilder.build(); + final CompletableFuture futureMessage = channel.sendMessage(message).submit(true); + + this.discord.sentMessages.add(futureMessage); + } + else + { + final MessageBuilder messageBuilder = new MessageBuilder(); + final EmbedBuilder embedBuilder = new EmbedBuilder(); + embedBuilder.setTitle("Command error"); + embedBuilder.setColor(Color.RED); + embedBuilder.setDescription("You don't have permission to execute this command."); + messageBuilder.setEmbed(embedBuilder.build()); + final Message message = messageBuilder.build(); + + final CompletableFuture futureMessage = channel.sendMessage(message).submit(true); + + this.discord.sentMessages.add(futureMessage); + } + } + } + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/discord/commands/HelpCommand.java b/src/main/java/me/totalfreedom/totalfreedommod/discord/commands/HelpCommand.java new file mode 100644 index 00000000..d9825083 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/discord/commands/HelpCommand.java @@ -0,0 +1,86 @@ +package me.totalfreedom.totalfreedommod.discord.commands; + +import me.totalfreedom.totalfreedommod.discord.Discord; +import me.totalfreedom.totalfreedommod.discord.command.DiscordCommand; +import me.totalfreedom.totalfreedommod.discord.command.DiscordCommandImpl; +import me.totalfreedom.totalfreedommod.discord.command.DiscordCommandManager; +import net.dv8tion.jda.api.EmbedBuilder; +import net.dv8tion.jda.api.MessageBuilder; +import net.dv8tion.jda.api.entities.Member; + +import java.awt.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class HelpCommand extends DiscordCommandImpl +{ + @Override + public String getCommandName() + { + return "help"; + } + + @Override + public String getDescription() + { + return "Displays the help command"; + } + + @Override + public String getCategory() + { + return "Help"; + } + + @Override + public List getAliases() + { + return List.of("cmds", "commands", "elp"); + } + + @Override + public boolean isAdmin() + { + return false; + } + + @Override + public MessageBuilder execute(Member member, List args) + { + final EmbedBuilder embedBuilder = new EmbedBuilder(); + embedBuilder.setColor(Color.GREEN); + embedBuilder.setTitle("Help Command"); + + final Map> commandCategories = new HashMap<>(); + + for (DiscordCommand command : Discord.DISCORD_COMMAND_MANAGER.commands) + { + if (!commandCategories.containsKey(command.getCategory())) + { + commandCategories.put(command.getCategory(), new ArrayList<>(List.of(command))); + } + else + { + commandCategories.get(command.getCategory()).add(command); + } + } + + for (Map.Entry> entry : commandCategories.entrySet()) + { + final String category = entry.getKey(); + final List commands = entry.getValue(); + final StringBuilder fieldValue = new StringBuilder(); + + for (DiscordCommand command : commands) + { + fieldValue.append("**").append(DiscordCommandManager.PREFIX).append(command.getCommandName()).append("** - ").append(command.getDescription()).append("\n"); + } + + embedBuilder.addField(category, fieldValue.toString().trim(), false); + } + + return new MessageBuilder().setEmbed(embedBuilder.build()); + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/discord/commands/ListCommand.java b/src/main/java/me/totalfreedom/totalfreedommod/discord/commands/ListCommand.java new file mode 100644 index 00000000..d81d24ff --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/discord/commands/ListCommand.java @@ -0,0 +1,106 @@ +package me.totalfreedom.totalfreedommod.discord.commands; + +import me.totalfreedom.totalfreedommod.TotalFreedomMod; +import me.totalfreedom.totalfreedommod.admin.AdminList; +import me.totalfreedom.totalfreedommod.config.ConfigEntry; +import me.totalfreedom.totalfreedommod.discord.command.DiscordCommand; +import me.totalfreedom.totalfreedommod.discord.command.DiscordCommandImpl; +import me.totalfreedom.totalfreedommod.rank.Displayable; +import me.totalfreedom.totalfreedommod.rank.RankManager; +import me.totalfreedom.totalfreedommod.util.FUtil; +import net.dv8tion.jda.api.EmbedBuilder; +import net.dv8tion.jda.api.MessageBuilder; +import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.entities.Member; +import net.dv8tion.jda.api.entities.User; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class ListCommand extends DiscordCommandImpl +{ + private static final TotalFreedomMod PLUGIN = TotalFreedomMod.plugin(); + + @Override + public String getCommandName() + { + return "list"; + } + + @Override + public String getDescription() + { + return "Gives a list of online players."; + } + + @Override + public String getCategory() + { + return "Server Commands"; + } + + @Override + public List getAliases() + { + return List.of("online", "who", "l", "lsit"); + } + + @Override + public boolean isAdmin() + { + return false; + } + + @Override + public MessageBuilder execute(Member member, List args) + { + if (PLUGIN == null) + { + throw new IllegalStateException("TotalFreedomMod somehow null while executing a command!"); + } + + final AdminList adminList = PLUGIN.al; + final RankManager rankManager = PLUGIN.rm; + + EmbedBuilder embedBuilder = new EmbedBuilder() + .setTitle("Player List - " + ConfigEntry.SERVER_NAME.getString()) + .setDescription("There are " + FUtil.getFakePlayerCount() + " / " + Bukkit.getMaxPlayers() + " online players"); + + Map> displayables = new HashMap<>(); + + for (Player onlinePlayer : Bukkit.getOnlinePlayers()) + { + if (adminList.isVanished(onlinePlayer.getName())) + { + continue; + } + + + Displayable displayable = rankManager.getDisplay(onlinePlayer); + + if (displayables.containsKey(displayable)) + { + displayables.get(displayable).add(onlinePlayer.getName()); + } + else + { + displayables.put(displayable, new ArrayList<>(List.of(onlinePlayer.getName()))); + } + } + + for (Map.Entry> entry : displayables.entrySet()) + { + final Displayable displayable = entry.getKey(); + final List players = entry.getValue(); + + embedBuilder.addField(displayable.getPlural() + " (" + players.size() + ")", + String.join(", ", players), false); + } + + return new MessageBuilder().setEmbed(embedBuilder.build()); + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/discord/commands/TPSCommand.java b/src/main/java/me/totalfreedom/totalfreedommod/discord/commands/TPSCommand.java new file mode 100644 index 00000000..0067af00 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/discord/commands/TPSCommand.java @@ -0,0 +1,60 @@ +package me.totalfreedom.totalfreedommod.discord.commands; + +import me.totalfreedom.totalfreedommod.discord.command.DiscordCommand; +import me.totalfreedom.totalfreedommod.discord.command.DiscordCommandImpl; +import me.totalfreedom.totalfreedommod.util.FUtil; +import net.dv8tion.jda.api.EmbedBuilder; +import net.dv8tion.jda.api.MessageBuilder; +import net.dv8tion.jda.api.entities.Member; +import org.bukkit.Bukkit; +import org.bukkit.Server; + +import java.util.Collections; +import java.util.List; + +public class TPSCommand extends DiscordCommandImpl +{ + @Override + public String getCommandName() + { + return "tps"; + } + + @Override + public String getDescription() + { + return "Lag information regarding the server."; + } + + @Override + public String getCategory() + { + return "Server Commands"; + } + + @Override + public List getAliases() + { + return Collections.singletonList("lag"); + } + + @Override + public boolean isAdmin() + { + return false; + } + + @Override + public MessageBuilder execute(Member member, List args) + { + final EmbedBuilder builder = new EmbedBuilder(); + builder.setTitle("Server lag information"); + builder.addField("TPS", String.valueOf(Math.round(FUtil.getMeanAverageDouble(Bukkit.getServer().getTPS()))), false); + builder.addField("Uptime", FUtil.getUptime(), false); + builder.addField("Maximum Memory", Math.ceil(FUtil.getMaxMem()) + " MB", false); + builder.addField("Allocated Memory", Math.ceil(FUtil.getTotalMem()) + " MB", false); + builder.addField("Free Memory", Math.ceil(FUtil.getFreeMem()) + " MB", false); + + return new MessageBuilder().setEmbed(builder.build()); + } +} diff --git a/src/main/java/me/totalfreedom/totalfreedommod/discord/commands/UptimeCommand.java b/src/main/java/me/totalfreedom/totalfreedommod/discord/commands/UptimeCommand.java new file mode 100644 index 00000000..47c2141f --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/discord/commands/UptimeCommand.java @@ -0,0 +1,79 @@ +package me.totalfreedom.totalfreedommod.discord.commands; + +import me.totalfreedom.totalfreedommod.discord.command.DiscordCommandImpl; +import me.totalfreedom.totalfreedommod.util.FLog; +import net.dv8tion.jda.api.EmbedBuilder; +import net.dv8tion.jda.api.MessageBuilder; +import net.dv8tion.jda.api.entities.Member; + +import java.awt.*; +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.util.Collections; +import java.util.List; + +public class UptimeCommand extends DiscordCommandImpl +{ + @Override + public String getCommandName() + { + return "uptime"; + } + + @Override + public String getDescription() + { + return "Returns the uptime of the VPS."; + } + + @Override + public String getCategory() + { + return "Server Commands"; + } + + @Override + public List getAliases() + { + return Collections.emptyList(); + } + + @Override + public boolean isAdmin() + { + return false; + } + + @Override + public MessageBuilder execute(Member member, List args) + { + final EmbedBuilder embedBuilder = new EmbedBuilder(); + + try + { + final Process uptimeProcess = Runtime.getRuntime().exec(new String[]{"uptime"}); + BufferedReader input = new BufferedReader(new InputStreamReader(uptimeProcess.getInputStream())); + String line = input.readLine(); + + if (line != null) + { + embedBuilder.setTitle("VPS Uptime Information"); + embedBuilder.setDescription(line.trim()); + } + else + { + throw new IllegalStateException("No output from uptime command."); + } + } + catch (Exception e) + { + FLog.warning("Error while executing uptime Discord command"); + e.printStackTrace(); + embedBuilder.setTitle("Command error"); + embedBuilder.setColor(Color.RED); + embedBuilder.setDescription("Something went wrong"); + } + + return new MessageBuilder().setEmbed(embedBuilder.build()); + } +} diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index b690154b..9d1bd3bc 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -58,6 +58,8 @@ server: discord: # If you do not have a token, make a bot account and get one at https://discordapp.com/developers/applications/me token: '' + # The prefix for the integrated bot commands + prefix: 'tf!' # The official discord server's ID for this server server_id: '' # Channel to send /report messages to