From db46624712de640b44d740626689a597dc61abb4 Mon Sep 17 00:00:00 2001 From: Teriuihi Date: Sat, 11 Sep 2021 17:47:46 +0200 Subject: [PATCH] Added everything needed for adding the discordBot to the plugin Shared pom --- .gitignore | 2 +- pom.xml | 118 ++++++++++++ .../alttd/proxydiscordlink/DiscordLink.java | 27 ++- .../com/alttd/proxydiscordlink/bot/Bot.java | 85 ++++++++ .../proxydiscordlink/bot/DiscordCommand.java | 37 ++++ .../proxydiscordlink/bot/JDAListener.java | 47 +++++ .../bot/api/DiscordSendMessage.java | 28 +++ .../bot/commands/DiscordBroadCast.java | 47 +++++ .../bot/commands/DiscordServerList.java | 133 +++++++++++++ .../bot/commands/DiscordStaffList.java | 147 ++++++++++++++ ...cordCommand.java => MinecraftCommand.java} | 4 +- .../proxydiscordlink/config/BotConfig.java | 181 ++++++++++++++++++ .../proxydiscordlink/util/JarLoader.java | 131 +++++++++++++ .../proxydiscordlink/util/Utilities.java | 16 +- 14 files changed, 996 insertions(+), 7 deletions(-) create mode 100644 pom.xml create mode 100644 src/main/java/com/alttd/proxydiscordlink/bot/Bot.java create mode 100644 src/main/java/com/alttd/proxydiscordlink/bot/DiscordCommand.java create mode 100644 src/main/java/com/alttd/proxydiscordlink/bot/JDAListener.java create mode 100644 src/main/java/com/alttd/proxydiscordlink/bot/api/DiscordSendMessage.java create mode 100644 src/main/java/com/alttd/proxydiscordlink/bot/commands/DiscordBroadCast.java create mode 100644 src/main/java/com/alttd/proxydiscordlink/bot/commands/DiscordServerList.java create mode 100644 src/main/java/com/alttd/proxydiscordlink/bot/commands/DiscordStaffList.java rename src/main/java/com/alttd/proxydiscordlink/commands/{DiscordCommand.java => MinecraftCommand.java} (97%) create mode 100644 src/main/java/com/alttd/proxydiscordlink/config/BotConfig.java create mode 100644 src/main/java/com/alttd/proxydiscordlink/util/JarLoader.java diff --git a/.gitignore b/.gitignore index 71dd23b..a55f705 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,7 @@ .idea/ *.iml *.bat -*.xml +dependency-reduced-pom.xml target/ src/test/ src/main/resources/ \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..72c3157 --- /dev/null +++ b/pom.xml @@ -0,0 +1,118 @@ + + + 4.0.0 + + org.example + ProxyDiscordLink + 1.0-SNAPSHOT + + + 16 + 16 + + + + + maven-snapshots + https://repository.apache.org/content/repositories/snapshots/ + + + + + clean package + ${project.name} + + + org.apache.maven.plugins + maven-compiler-plugin + 3.7.0 + + 16 + 16 + + + + org.apache.maven.plugins + maven-shade-plugin + 3.3.0-SNAPSHOT + + + package + + shade + + + + + net.kyori.adventure.text.minimessage + com.alttd.proxyutils.libs.net.kyori.adventure.text.minimessage + + + net.dv8tion + com.alttd.proxyutils.libs.net.dv8tion + + + + + + + + + + src/main + true + + + + + + + velocity + https://repo.velocitypowered.com/snapshots/ + + + + + + com.velocitypowered + velocity-api + 1.1.5 + provided + + + net.luckperms + api + 5.3 + provided + + + com.google.inject + guice + 4.1.0 + provided + + + mysql + mysql-connector-java + 8.0.26 + + + net.kyori + adventure-text-minimessage + 4.1.0-SNAPSHOT + + + net.dv8tion + JDA + 4.2.0_168 + + + org.slf4j + slf4j-api + 1.7.30 + + + + \ No newline at end of file diff --git a/src/main/java/com/alttd/proxydiscordlink/DiscordLink.java b/src/main/java/com/alttd/proxydiscordlink/DiscordLink.java index 8308500..8facd81 100644 --- a/src/main/java/com/alttd/proxydiscordlink/DiscordLink.java +++ b/src/main/java/com/alttd/proxydiscordlink/DiscordLink.java @@ -1,6 +1,7 @@ package com.alttd.proxydiscordlink; -import com.alttd.proxydiscordlink.commands.DiscordCommand; +import com.alttd.proxydiscordlink.bot.Bot; +import com.alttd.proxydiscordlink.commands.MinecraftCommand; import com.alttd.proxydiscordlink.config.Config; import com.alttd.proxydiscordlink.database.Database; import com.alttd.proxydiscordlink.database.DatabaseConnection; @@ -8,6 +9,7 @@ import com.alttd.proxydiscordlink.listeners.PlayerJoin; import com.alttd.proxydiscordlink.listeners.PlayerLeave; import com.alttd.proxydiscordlink.util.ALogger; import com.alttd.proxydiscordlink.util.Cache; +import com.alttd.proxydiscordlink.util.JarLoader; import com.google.inject.Inject; import com.velocitypowered.api.event.Subscribe; import com.velocitypowered.api.event.proxy.ProxyInitializeEvent; @@ -32,6 +34,7 @@ public class DiscordLink { private final Path dataDirectory; private final Database database; private final Cache cache; + private Bot bot; @Inject public DiscordLink(ProxyServer proxyServer, Logger proxyLogger, @DataDirectory Path proxydataDirectory) @@ -58,6 +61,7 @@ public class DiscordLink { } loadCommands(); loadEvents(); + loadBot(); } public void reloadConfig() { @@ -66,7 +70,7 @@ public class DiscordLink { } public void loadCommands() {// all (proxy)commands go here - server.getCommandManager().register("discord", new DiscordCommand(), "discordlink"); + server.getCommandManager().register("discord", new MinecraftCommand(), "discordlink"); } public void loadEvents() { @@ -74,6 +78,21 @@ public class DiscordLink { server.getEventManager().register(this, new PlayerLeave()); } + public void loadBot() { +// String JDAVersion = "4.2.0"; +// String JDABuild = "168"; +// String JDAUrl = "https://github.com/DV8FromTheWorld/JDA/releases/download/v" + JDAVersion + "/JDA-" + JDAVersion + "_" + JDABuild + "-withDependencies.jar"; +// String JDAFile = "jda-" + JDAVersion + "_" + JDABuild + ".jar"; +// if (new JarLoader().loadJar(JDAUrl, new File(new File(getDataDirectory(), "libs"), JDAFile))) { +// ALogger.info("JDA successfully loaded"); +// } else { +// ALogger.error("JDA could not be loaded!"); +// } + bot = new Bot(); + bot.connect(); + } + + public File getDataDirectory() { return dataDirectory.toFile(); } @@ -97,4 +116,8 @@ public class DiscordLink { public Cache getCache() { return cache; } + + public Bot getBot() { + return bot; + } } diff --git a/src/main/java/com/alttd/proxydiscordlink/bot/Bot.java b/src/main/java/com/alttd/proxydiscordlink/bot/Bot.java new file mode 100644 index 0000000..9dd0c02 --- /dev/null +++ b/src/main/java/com/alttd/proxydiscordlink/bot/Bot.java @@ -0,0 +1,85 @@ +package com.alttd.proxydiscordlink.bot; + +import com.alttd.proxydiscordlink.config.BotConfig; +import com.alttd.proxydiscordlink.util.ALogger; +import net.dv8tion.jda.api.EmbedBuilder; +import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.JDABuilder; +import net.dv8tion.jda.api.entities.TextChannel; + +import javax.security.auth.login.LoginException; +import java.util.concurrent.TimeUnit; + +public class Bot { + private JDA jda = null; + + public void connect() { + disconnect(); + try { + jda = JDABuilder.createDefault(BotConfig.BOT_TOKEN).build(); + jda.setAutoReconnect(true); + jda.addEventListener(new JDAListener()); + DiscordCommand.loadCommands(); + } catch (LoginException e) { + jda = null; + } + } + + public void disconnect() { + if (jda != null) { + JDA tmp = jda; + jda = null; + new Thread(() -> { + try { + Thread.sleep(2000); + } catch (InterruptedException ignore) { + } + tmp.shutdownNow(); + }).start(); + } + } + + public void sendMessageToDiscord(String channelid, String message) { + //sendMessageToDiscord(client.getTextChannelById(channel), message, blocking); + TextChannel channel = jda.getTextChannelById(channelid); + if (jda == null) return; + + if (channel == null) return; + + if (message == null) return; + + // is this even used/needed? + //message = ChatColor.stripColor(ChatColor.translateAlternateColorCodes('&', message)); + + if (message.isEmpty()) return; + + try { + channel.sendMessage(message).queue(); + } catch (Exception e) { + ALogger.error("caught some exception, " + e); + } + } + + public void sendEmbedToDiscord(String channelid, EmbedBuilder embedBuilder, long secondsTillDelete) { + //sendMessageToDiscord(client.getTextChannelById(channel), message, blocking); + TextChannel channel = jda.getTextChannelById(channelid); + if (jda == null) return; + + if (channel == null) return; + + if (embedBuilder == null) return; + + if (!embedBuilder.isValidLength()) return; + + if (embedBuilder.isEmpty()) return; + try { + if (secondsTillDelete < 0){ + channel.sendMessage(embedBuilder.build()).queue(); + } else { + channel.sendMessage(embedBuilder.build()).queue(message -> message.delete().queueAfter(secondsTillDelete, TimeUnit.SECONDS)); + } + } catch (Exception e) { + ALogger.error("caught some exception, " + e); + } + } +} diff --git a/src/main/java/com/alttd/proxydiscordlink/bot/DiscordCommand.java b/src/main/java/com/alttd/proxydiscordlink/bot/DiscordCommand.java new file mode 100644 index 0000000..5353d34 --- /dev/null +++ b/src/main/java/com/alttd/proxydiscordlink/bot/DiscordCommand.java @@ -0,0 +1,37 @@ +package com.alttd.proxydiscordlink.bot; + +import com.alttd.proxydiscordlink.bot.commands.DiscordServerList; +import com.alttd.proxydiscordlink.bot.commands.DiscordStaffList; +import net.dv8tion.jda.api.entities.Message; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public abstract class DiscordCommand { + + private static List commands; + + public abstract String getCommand(); + public abstract String getPermission();// TODO discord and LP permissions + public abstract String getDescription(); + public abstract String getSyntax(); + + public abstract void handleCommand(Message message, String sender, String command, String[] args); + + protected static void loadCommands() { + commands = new ArrayList<>(); + + loadCommand(new DiscordStaffList(), + new DiscordServerList() + ); + } + + private static void loadCommand(DiscordCommand ... discordCommands) { + Collections.addAll(commands, discordCommands); + } + + protected static List getCommands() { + return commands; + } +} diff --git a/src/main/java/com/alttd/proxydiscordlink/bot/JDAListener.java b/src/main/java/com/alttd/proxydiscordlink/bot/JDAListener.java new file mode 100644 index 0000000..f08c896 --- /dev/null +++ b/src/main/java/com/alttd/proxydiscordlink/bot/JDAListener.java @@ -0,0 +1,47 @@ +package com.alttd.proxydiscordlink.bot; + +import com.alttd.proxydiscordlink.DiscordLink; +import com.alttd.proxydiscordlink.config.BotConfig; +import net.dv8tion.jda.api.events.message.guild.GuildMessageReceivedEvent; +import net.dv8tion.jda.api.hooks.ListenerAdapter; + +import java.util.Arrays; + +public class JDAListener extends ListenerAdapter { + + private DiscordLink plugin; + private final Bot bot; + + public JDAListener() { + plugin = DiscordLink.getPlugin(); + bot = plugin.getBot(); + } + + @Override + public void onGuildMessageReceived(GuildMessageReceivedEvent event) { + if (event.getAuthor() == event.getJDA().getSelfUser()) { + return; + } + if (event.isWebhookMessage()) { + return; + } + if (event.getMessage().getChannel().getId().equals(BotConfig.COMMAND_CHANNEL)) { + String content = event.getMessage().getContentRaw(); + if (content.startsWith("!") && content.length() > 1) { + String[] split = content.split(" "); + String cmd = split[0].substring(1).toLowerCase(); + String[] args = Arrays.copyOfRange(split, 1, split.length); + for(DiscordCommand command : DiscordCommand.getCommands()) { + if(!command.getCommand().equalsIgnoreCase(cmd)) { + continue; + } + if(command.getPermission() != null) { + // TODO permission check? do we need this? + } + command.handleCommand(event.getMessage(), event.getAuthor().getName(), cmd, args); + } + } + } + } + +} diff --git a/src/main/java/com/alttd/proxydiscordlink/bot/api/DiscordSendMessage.java b/src/main/java/com/alttd/proxydiscordlink/bot/api/DiscordSendMessage.java new file mode 100644 index 0000000..9125b13 --- /dev/null +++ b/src/main/java/com/alttd/proxydiscordlink/bot/api/DiscordSendMessage.java @@ -0,0 +1,28 @@ +package com.alttd.proxydiscordlink.bot.api; + +import com.alttd.proxydiscordlink.DiscordLink; +import com.alttd.proxydiscordlink.bot.Bot; +import net.dv8tion.jda.api.EmbedBuilder; + +import java.awt.*; + +public class DiscordSendMessage { + public static void sendMessage(String channelId, String message) + { + Bot bot = DiscordLink.getPlugin().getBot(); + + bot.sendMessageToDiscord(channelId, message); + } + + public static void sendEmbed(String channelId, String title, String description) + { + Bot bot = DiscordLink.getPlugin().getBot(); + EmbedBuilder embedBuilder = new EmbedBuilder(); + + embedBuilder.setColor(Color.CYAN); + embedBuilder.setTitle(title); + embedBuilder.setDescription(description); + + bot.sendEmbedToDiscord(channelId, embedBuilder, -1); + } +} diff --git a/src/main/java/com/alttd/proxydiscordlink/bot/commands/DiscordBroadCast.java b/src/main/java/com/alttd/proxydiscordlink/bot/commands/DiscordBroadCast.java new file mode 100644 index 0000000..0239099 --- /dev/null +++ b/src/main/java/com/alttd/proxydiscordlink/bot/commands/DiscordBroadCast.java @@ -0,0 +1,47 @@ +package com.alttd.proxydiscordlink.bot.commands; + +import com.alttd.proxydiscordlink.DiscordLink; +import com.alttd.proxydiscordlink.bot.Bot; +import com.alttd.proxydiscordlink.bot.DiscordCommand; +import com.alttd.proxydiscordlink.config.BotConfig; +import com.alttd.proxydiscordlink.util.Utilities; +import net.dv8tion.jda.api.entities.Message; + +public class DiscordBroadCast extends DiscordCommand { + + private DiscordLink plugin; + private final Bot bot; + + public DiscordBroadCast() { + plugin = DiscordLink.getPlugin(); + bot = plugin.getBot(); + } + + @Override + public String getCommand() { + return "broadcast"; + } + + @Override + public String getPermission() { + return null; + } + + @Override + public String getDescription() { + return "Broadcast a message to all online players"; + } + + @Override + public String getSyntax() { + return "!broadcast"; + } + + @Override + public void handleCommand(Message message, String sender, String command, String[] args) { + //TODO also send this to the bot channel, optional command args for color and decoration? + String msg = String.join(" ", args); + bot.sendMessageToDiscord(BotConfig.COMMAND_CHANNEL, msg); + Utilities.broadcast(msg); + } +} diff --git a/src/main/java/com/alttd/proxydiscordlink/bot/commands/DiscordServerList.java b/src/main/java/com/alttd/proxydiscordlink/bot/commands/DiscordServerList.java new file mode 100644 index 0000000..bc37251 --- /dev/null +++ b/src/main/java/com/alttd/proxydiscordlink/bot/commands/DiscordServerList.java @@ -0,0 +1,133 @@ +package com.alttd.proxydiscordlink.bot.commands; + +import com.alttd.proxydiscordlink.DiscordLink; +import com.alttd.proxydiscordlink.bot.Bot; +import com.alttd.proxydiscordlink.bot.DiscordCommand; +import com.alttd.proxydiscordlink.config.BotConfig; +import com.alttd.proxydiscordlink.util.Utilities; +import com.velocitypowered.api.proxy.Player; +import com.velocitypowered.api.proxy.server.RegisteredServer; +import com.velocitypowered.api.proxy.server.ServerInfo; +import net.dv8tion.jda.api.EmbedBuilder; +import net.dv8tion.jda.api.entities.Message; +import net.luckperms.api.LuckPerms; +import net.luckperms.api.model.user.User; + +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Optional; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +public class DiscordServerList extends DiscordCommand { + + private DiscordLink plugin; + private final Bot bot; + + public DiscordServerList() { + plugin = DiscordLink.getPlugin(); + bot = plugin.getBot(); + } + + @Override + public String getCommand() { + return "serverlist"; + } + + @Override + public String getPermission() { + return null; + } + + @Override + public String getDescription() { + return "Lists all online players on the server or a specific server"; + } + + @Override + public String getSyntax() { + return "!serverlist"; + } + + @Override + public void handleCommand(Message message, String sender, String command, String[] args) { + String serverName = "Altitude"; + Collection onlinePlayer = plugin.getProxy().getAllPlayers(); + ServerInfo server; + if (args.length != 0) { + Optional registeredServer = plugin.getProxy().getServer(args[0]); + if (registeredServer.isEmpty()) { + return; + } + onlinePlayer = registeredServer.get().getPlayersConnected(); + serverName = registeredServer.get().getServerInfo().getName(); + } + LuckPerms luckPerms = Utilities.getLuckPerms(); + List players = onlinePlayer + .stream() + .map(player -> luckPerms.getUserManager().getUser(player.getUniqueId())) + .sorted((o1, o2) -> { + int i = Integer.compare(luckPerms.getGroupManager().getGroup(o2.getPrimaryGroup()).getWeight().orElse(0), luckPerms.getGroupManager().getGroup(o1.getPrimaryGroup()).getWeight().orElse(0)); + return i != 0 ? i : o1.getUsername().compareToIgnoreCase(o2.getUsername()); + }) + .collect(Collectors.toList()); + EmbedBuilder embedBuilder = new EmbedBuilder(); + String title = "Players online on " + serverName + ": " + players.size(); + embedBuilder.setTitle(title); + String separator = "\n"; + String rankname = ""; + StringBuilder currentFieldText = new StringBuilder(); + int entryCounter = 0; + int totalCharacters = title.length(); + int fieldCounter = 0; + + Iterator iterator = players.iterator(); + while (iterator.hasNext()) { + User user = iterator.next(); + if(user != null) { + if(!rankname.equalsIgnoreCase(user.getPrimaryGroup())) { + if (currentFieldText.length() != 0) { + totalCharacters += rankname.length() + currentFieldText.length(); + fieldCounter++; + if (totalCharacters > 6000 || fieldCounter > 25) { + bot.sendEmbedToDiscord(BotConfig.COMMAND_CHANNEL, embedBuilder, 300); + embedBuilder.clearFields(); + totalCharacters = title.length() + rankname.length() + currentFieldText.length(); + fieldCounter = 1; + } + embedBuilder.addField(rankname, currentFieldText.toString(), true); + entryCounter = 0; + currentFieldText = new StringBuilder(); + } + rankname = Utilities.capitalize(user.getPrimaryGroup()); + } else if(rankname.equalsIgnoreCase(user.getPrimaryGroup())) { + currentFieldText.append(separator); + } + if (entryCounter <= 50) { + Optional optionalPlayer = plugin.getProxy().getPlayer(user.getUniqueId()); + if(optionalPlayer.isPresent()) { + Player player = optionalPlayer.get(); + currentFieldText.append("`").append(player.getUsername()).append("`"); + } + } else if (entryCounter == 51){ + currentFieldText.append("..."); + } + entryCounter++; + } + } + + if (currentFieldText.length() > 0) { + totalCharacters = title.length() + rankname.length() + currentFieldText.length(); + fieldCounter++; + if (totalCharacters > 6000 || fieldCounter > 25) { + bot.sendEmbedToDiscord(BotConfig.COMMAND_CHANNEL, embedBuilder, 300); + embedBuilder.clearFields(); + } + embedBuilder.addField(rankname, currentFieldText.toString(), true); + } + + message.delete().queueAfter(300, TimeUnit.SECONDS); + bot.sendEmbedToDiscord(BotConfig.COMMAND_CHANNEL, embedBuilder, 300); + } +} diff --git a/src/main/java/com/alttd/proxydiscordlink/bot/commands/DiscordStaffList.java b/src/main/java/com/alttd/proxydiscordlink/bot/commands/DiscordStaffList.java new file mode 100644 index 0000000..8b8c227 --- /dev/null +++ b/src/main/java/com/alttd/proxydiscordlink/bot/commands/DiscordStaffList.java @@ -0,0 +1,147 @@ +package com.alttd.proxydiscordlink.bot.commands; + +import com.alttd.proxydiscordlink.DiscordLink; +import com.alttd.proxydiscordlink.bot.Bot; +import com.alttd.proxydiscordlink.bot.DiscordCommand; +import com.alttd.proxydiscordlink.config.BotConfig; +import com.alttd.proxydiscordlink.util.Utilities; +import com.velocitypowered.api.proxy.Player; +import net.dv8tion.jda.api.EmbedBuilder; +import net.dv8tion.jda.api.entities.Message; +import net.luckperms.api.LuckPerms; +import net.luckperms.api.model.user.User; + +import java.util.*; +import java.util.stream.Collectors; + +public class DiscordStaffList extends DiscordCommand { + + private DiscordLink plugin; + private final Bot bot; + + public DiscordStaffList() { + plugin = DiscordLink.getPlugin(); + bot = plugin.getBot(); + } + + @Override + public String getCommand() { + return "stafflist"; + } + + @Override + public String getPermission() { + return null; + } + + @Override + public String getDescription() { + return "Lists all online staff on the server"; + } + + @Override + public String getSyntax() { + return "!StaffList"; + } + + @Override + public void handleCommand(Message message, String sender, String command, String[] args) { + LuckPerms luckPerms = Utilities.getLuckPerms(); + List staff = plugin.getProxy().getAllPlayers() + .stream().filter(player-> player.hasPermission("group." + BotConfig.SL_MINIMUMRANK)) + .map(player -> luckPerms.getUserManager().getUser(player.getUniqueId())) + .sorted((o1, o2) -> { + int i = Integer.compare(luckPerms.getGroupManager().getGroup(o2.getPrimaryGroup()).getWeight().orElse(0), luckPerms.getGroupManager().getGroup(o1.getPrimaryGroup()).getWeight().orElse(0)); + return i != 0 ? i : o1.getUsername().compareToIgnoreCase(o2.getUsername()); + }) + .collect(Collectors.toList()); + EmbedBuilder embedBuilder = new EmbedBuilder(); + String title = "Online Staff: " + staff.size() + " - Online Players: " + plugin.getProxy().getAllPlayers().size(); + embedBuilder.setTitle(title); + String separator = "\n"; + String rankname = ""; + + Map onlineStaff = new HashMap<>(); + StringBuilder currentFieldText = new StringBuilder(); + int entryCounter = 0; + int totalCharacters = title.length(); + int fieldCounter = 0; + + Iterator iterator = staff.iterator(); + while (iterator.hasNext()) { + User user = iterator.next(); + if(user != null) { + if(!rankname.equalsIgnoreCase(user.getPrimaryGroup())) { + if (currentFieldText.length() != 0) { + totalCharacters += rankname.length() + currentFieldText.length(); + fieldCounter++; + if (totalCharacters > 6000 || fieldCounter > 25) { + bot.sendEmbedToDiscord(BotConfig.COMMAND_CHANNEL, embedBuilder, -1); + embedBuilder.clearFields(); + totalCharacters = title.length() + rankname.length() + currentFieldText.length(); + fieldCounter = 1; + } + embedBuilder.addField(rankname, currentFieldText.toString(), true); + entryCounter = 0; + currentFieldText = new StringBuilder(); + } + rankname = Utilities.capitalize(user.getPrimaryGroup()); + } else if(rankname.equalsIgnoreCase(user.getPrimaryGroup())) { + currentFieldText.append(separator); + } + + Optional optionalPlayer = plugin.getProxy().getPlayer(user.getUniqueId()); + if(optionalPlayer.isPresent()) { + Player player = optionalPlayer.get(); + String currentServerName = player.getCurrentServer().isPresent() ? player.getCurrentServer().get().getServerInfo().getName() : ""; + if (onlineStaff.containsKey(currentServerName)){ + onlineStaff.put(currentServerName, onlineStaff.get(currentServerName) + 1); + } else { + onlineStaff.put(currentServerName, 1); + } + + if (entryCounter <= 50) { + currentFieldText.append("`").append(player.getUsername()).append("`"); + } else if (entryCounter == 51){ + currentFieldText.append("..."); + } + entryCounter++; + } + } + } + + if (currentFieldText.length() > 0) { + totalCharacters = title.length() + rankname.length() + currentFieldText.length(); + fieldCounter++; + if (totalCharacters > 6000 || fieldCounter > 25) { + bot.sendEmbedToDiscord(BotConfig.COMMAND_CHANNEL, embedBuilder, -1); + embedBuilder.clearFields(); + } + embedBuilder.addField(rankname, currentFieldText.toString(), true); + currentFieldText = new StringBuilder(); + } + + for (Map.Entry entry : onlineStaff.entrySet()){ + String serverName = entry.getKey(); + Integer amountOfStaff = entry.getValue(); + // this might error:/ + int playerCount = plugin.getProxy().getServer(serverName).isPresent() ? plugin.getProxy().getServer(serverName).get().getPlayersConnected().size() - amountOfStaff : 1; + currentFieldText.append(serverName).append(" online staff per player ") + .append(amountOfStaff).append(" / ").append(Math.max(playerCount, 0)).append(" = ") + .append(playerCount > 0 ? Math.round(((double)amountOfStaff / playerCount) * 100.0) / 100.0 : "-").append("\n"); + } + + if (currentFieldText.length() > 0) { + rankname = "Staff per server"; + totalCharacters = title.length() + rankname.length() + currentFieldText.length(); + fieldCounter++; + if (totalCharacters > 6000 || fieldCounter > 25) { + bot.sendEmbedToDiscord(BotConfig.COMMAND_CHANNEL, embedBuilder, -1); + embedBuilder.clearFields(); + } + embedBuilder.addField(rankname, currentFieldText.toString(), true); + } + + bot.sendEmbedToDiscord(BotConfig.COMMAND_CHANNEL, embedBuilder, -1); + } +} diff --git a/src/main/java/com/alttd/proxydiscordlink/commands/DiscordCommand.java b/src/main/java/com/alttd/proxydiscordlink/commands/MinecraftCommand.java similarity index 97% rename from src/main/java/com/alttd/proxydiscordlink/commands/DiscordCommand.java rename to src/main/java/com/alttd/proxydiscordlink/commands/MinecraftCommand.java index d86425f..ac11707 100644 --- a/src/main/java/com/alttd/proxydiscordlink/commands/DiscordCommand.java +++ b/src/main/java/com/alttd/proxydiscordlink/commands/MinecraftCommand.java @@ -17,12 +17,12 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; -public class DiscordCommand implements SimpleCommand { +public class MinecraftCommand implements SimpleCommand { private final List subCommands; private final MiniMessage miniMessage; - public DiscordCommand() { + public MinecraftCommand() { subCommands = Arrays.asList(new CheckLinked(), new Link(), new Unlink(), new Reload()); miniMessage = MiniMessage.get(); } diff --git a/src/main/java/com/alttd/proxydiscordlink/config/BotConfig.java b/src/main/java/com/alttd/proxydiscordlink/config/BotConfig.java new file mode 100644 index 0000000..d50c7c4 --- /dev/null +++ b/src/main/java/com/alttd/proxydiscordlink/config/BotConfig.java @@ -0,0 +1,181 @@ +package com.alttd.proxydiscordlink.config; + +import com.google.common.base.Throwables; +import com.google.common.reflect.TypeToken; +import ninja.leaping.configurate.ConfigurationNode; +import ninja.leaping.configurate.ConfigurationOptions; +import ninja.leaping.configurate.objectmapping.ObjectMappingException; +import ninja.leaping.configurate.yaml.YAMLConfigurationLoader; +import org.yaml.snakeyaml.DumperOptions; + +import java.io.File; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Pattern; + +public class BotConfig { + private static final Pattern PATH_PATTERN = Pattern.compile("\\."); + private static final String HEADER = ""; + + private static File CONFIG_FILE; + public static ConfigurationNode config; + public static YAMLConfigurationLoader configLoader; + + static int version; + static boolean verbose; + + public static File CONFIGPATH; + + public static void init() { // todo setup share for the config + CONFIGPATH = new File(System.getProperty("user.home") + File.separator + "share" + File.separator + "configs" + File.separator + "DiscordLink"); + CONFIG_FILE = new File(CONFIGPATH, "bot-config.yml"); + + configLoader = YAMLConfigurationLoader.builder() + .setFile(CONFIG_FILE) + .setFlowStyle(DumperOptions.FlowStyle.BLOCK) + .build(); + if (!CONFIG_FILE.getParentFile().exists()) { + if (!CONFIG_FILE.getParentFile().mkdirs()) { + return; + } + } + if (!CONFIG_FILE.exists()) { + try { + if (!CONFIG_FILE.createNewFile()) { + return; + } + } catch (IOException error) { + error.printStackTrace(); + } + } + + try { + config = configLoader.load(ConfigurationOptions.defaults().setHeader(HEADER)); + } catch (IOException e) { + e.printStackTrace(); + } + + verbose = getBoolean("verbose", true); + version = getInt("config-version", 1); + + readConfig(Config.class, null); + try { + configLoader.save(config); + } catch (IOException e) { + e.printStackTrace(); + } + } + + public static void readConfig(Class clazz, Object instance) { + for (Method method : clazz.getDeclaredMethods()) { + if (Modifier.isPrivate(method.getModifiers())) { + if (method.getParameterTypes().length == 0 && method.getReturnType() == Void.TYPE) { + try { + method.setAccessible(true); + method.invoke(instance); + } catch (InvocationTargetException | IllegalAccessException ex) { + throw Throwables.propagate(ex.getCause()); + } + } + } + } + try { + configLoader.save(config); + } catch (IOException ex) { + throw Throwables.propagate(ex.getCause()); + } + } + + public static void saveConfig() { + try { + configLoader.save(config); + } catch (IOException ex) { + throw Throwables.propagate(ex.getCause()); + } + } + + private static Object[] splitPath(String key) { + return PATH_PATTERN.split(key); + } + + private static void set(String path, Object def) { + if (config.getNode(splitPath(path)).isVirtual()) + config.getNode(splitPath(path)).setValue(def); + } + + private static void setString(String path, String def) { + try { + if (config.getNode(splitPath(path)).isVirtual()) + config.getNode(splitPath(path)).setValue(TypeToken.of(String.class), def); + } catch (ObjectMappingException ex) { + } + } + + private static boolean getBoolean(String path, boolean def) { + set(path, def); + return config.getNode(splitPath(path)).getBoolean(def); + } + + private static double getDouble(String path, double def) { + set(path, def); + return config.getNode(splitPath(path)).getDouble(def); + } + + private static int getInt(String path, int def) { + set(path, def); + return config.getNode(splitPath(path)).getInt(def); + } + + private static String getString(String path, String def) { + setString(path, def); + return config.getNode(splitPath(path)).getString(def); + } + + private static Long getLong(String path, Long def) { + set(path, def); + return config.getNode(splitPath(path)).getLong(def); + } + + private static List getList(String path, T def) { + try { + set(path, def); + return config.getNode(splitPath(path)).getList(TypeToken.of(String.class)); + } catch (ObjectMappingException ex) { + } + return new ArrayList<>(); + } + + private static ConfigurationNode getNode(String path) { + if (config.getNode(splitPath(path)).isVirtual()) { + //new RegexConfig("Dummy"); + } + config.getChildrenMap(); + return config.getNode(splitPath(path)); + } + + + /** + * ONLY EDIT ANYTHING BELOW THIS LINE + **/ + + public static String BOT_TOKEN = ""; + public static String COMMAND_CHANNEL = ""; + private static void settings() + { + BOT_TOKEN = getString("settings.token", BOT_TOKEN); + COMMAND_CHANNEL = getString("settings.command_channel", COMMAND_CHANNEL); + } + + public static String SL_MINIMUMRANK = "trainee"; + public static String SL_HOVERMESSAGE = "Click here to message %player% on %servername%."; + public static String SL_CLICKCOMMAND = "/msg %player%"; + private static void Stafflist() { + SL_MINIMUMRANK = getString("commands.staff-list.minimum-rank", SL_MINIMUMRANK); + SL_HOVERMESSAGE = getString("commands.staff-list.hover-message", SL_HOVERMESSAGE); + SL_CLICKCOMMAND = getString("commands.staff-list.click-command", SL_CLICKCOMMAND); + } +} diff --git a/src/main/java/com/alttd/proxydiscordlink/util/JarLoader.java b/src/main/java/com/alttd/proxydiscordlink/util/JarLoader.java new file mode 100644 index 0000000..c2724b8 --- /dev/null +++ b/src/main/java/com/alttd/proxydiscordlink/util/JarLoader.java @@ -0,0 +1,131 @@ +package com.alttd.proxydiscordlink.util; + +import com.alttd.proxydiscordlink.DiscordLink; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.net.HttpURLConnection; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.file.Files; + +public class JarLoader { + + private static final Method ADD_URL_METHOD; + + static { + try { + ADD_URL_METHOD = URLClassLoader.class.getDeclaredMethod("addURL", URL.class); + ADD_URL_METHOD.setAccessible(true); + } catch (NoSuchMethodException e) { + throw new ExceptionInInitializerError(e); + } + } + + private URLClassLoader classLoader; + + public JarLoader() { + classLoader = ((URLClassLoader) DiscordLink.getPlugin().getClass().getClassLoader()); + } + + public boolean loadJar(String url, File file) { + try { + if (!file.getParentFile().exists()) { + if (!file.getParentFile().mkdirs()) { + ALogger.warn("Could not create directory: " + file.getParentFile().getAbsolutePath()); + } + } + + if (file.exists() && file.isDirectory()) { + Files.delete(file.toPath()); + } + + if (!file.exists()) { + ALogger.info("Jar not found! (" + file.getName() + ")"); + ALogger.info("Downloading jar from " + url); + downloadJar(url, file); + } + + ADD_URL_METHOD.invoke(classLoader, file.toPath().toUri().toURL()); + return true; + } catch (IOException | IllegalAccessException | InvocationTargetException e) { + e.printStackTrace(); + return false; + } + } + + private void downloadJar(String url, File file) throws IOException { + HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection(); + conn.setInstanceFollowRedirects(true); + + boolean redirect; + + do { + int status = conn.getResponseCode(); + redirect = status == HttpURLConnection.HTTP_MOVED_TEMP || + status == HttpURLConnection.HTTP_MOVED_PERM || + status == HttpURLConnection.HTTP_SEE_OTHER; + + if (redirect) { + String newUrl = conn.getHeaderField("Location"); + String cookies = conn.getHeaderField("Set-Cookie"); + + conn = (HttpURLConnection) new URL(newUrl).openConnection(); + conn.setRequestProperty("Cookie", cookies); + conn.addRequestProperty("Accept-Language", "en-US,en;q=0.8"); + } + } while (redirect); + + Progress progress = new Progress(conn.getContentLength()); + progress.start(); + + try (BufferedInputStream in = new BufferedInputStream(conn.getInputStream()); + FileOutputStream fileOutputStream = new FileOutputStream(file)) { + byte[] dataBuffer = new byte[1024]; + int bytesRead; + while ((bytesRead = in.read(dataBuffer, 0, 1024)) != -1) { + progress.current += bytesRead; + fileOutputStream.write(dataBuffer, 0, bytesRead); + } + } + + progress.interrupt(); + } + + private class Progress extends Thread { + private final long total; + private long current; + + private Progress(long total) { + this.total = total; + } + + @Override + public void run() { + try { + //noinspection InfiniteLoopStatement + while (true) { + logProgress(); + sleep(1000); + } + } catch (InterruptedException ignore) { + } + } + + @Override + public void interrupt() { + logProgress(); + super.interrupt(); + } + + private void logProgress() { + ALogger.info(String.format("progress: %s%% (%s/%s)", + (int) ((((double) current) / ((double) total)) * 100D), + current, total)); + } + } +} diff --git a/src/main/java/com/alttd/proxydiscordlink/util/Utilities.java b/src/main/java/com/alttd/proxydiscordlink/util/Utilities.java index b7ea72b..052de82 100644 --- a/src/main/java/com/alttd/proxydiscordlink/util/Utilities.java +++ b/src/main/java/com/alttd/proxydiscordlink/util/Utilities.java @@ -1,10 +1,10 @@ package com.alttd.proxydiscordlink.util; +import com.alttd.proxydiscordlink.DiscordLink; import com.alttd.proxydiscordlink.config.Config; -import com.velocitypowered.api.command.CommandSource; import com.velocitypowered.api.proxy.Player; +import com.velocitypowered.api.proxy.ProxyServer; import net.kyori.adventure.text.minimessage.MiniMessage; -import net.kyori.adventure.text.minimessage.Template; import net.luckperms.api.LuckPerms; import net.luckperms.api.LuckPermsProvider; import net.luckperms.api.model.user.User; @@ -86,4 +86,16 @@ public class Utilities { public static String getRankName(Player player) { return getLuckPerms().getUserManager().getUser(player.getUniqueId()).getPrimaryGroup(); } + + public static String capitalize(String str) { + if(str == null || str.isEmpty()) { + return str; + } + return str.substring(0, 1).toUpperCase() + str.substring(1); + } + + public static void broadcast(String message) { + ProxyServer server = DiscordLink.getPlugin().getProxy(); + server.sendMessage(miniMessage.parse(message)); + } }