diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..00a51af --- /dev/null +++ b/.gitattributes @@ -0,0 +1,6 @@ +# +# https://help.github.com/articles/dealing-with-line-endings/ +# +# These are explicitly windows files and should use crlf +*.bat text eol=crlf + diff --git a/.gitignore b/.gitignore index 2b3de74..e87d4f1 100644 --- a/.gitignore +++ b/.gitignore @@ -117,3 +117,6 @@ run/ # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) !gradle-wrapper.jar + +# Ignore Gradle build output directory +build diff --git a/BetterStaffChat-bungeecord/build.gradle b/BetterStaffChat-bungeecord/build.gradle deleted file mode 100644 index 6d56e23..0000000 --- a/BetterStaffChat-bungeecord/build.gradle +++ /dev/null @@ -1,45 +0,0 @@ -/* - * BetterStaffChat - build.gradle - * Copyright (C) 2021 AusTech Development Team - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -import org.apache.tools.ant.filters.ReplaceTokens - -plugins { - id 'com.github.johnrengelman.shadow' -} - -repositories { - maven { - name = 'sonatype' - url = 'https://oss.sonatype.org/content/groups/public/' - } -} - -dependencies { - compileOnly 'net.md-5:bungeecord-api:1.17-R0.1-SNAPSHOT' - compileOnly 'net.luckperms:api:5.3' - implementation 'org.bstats:bstats-bungeecord:2.2.1' - implementation project(":BetterStaffChat-common") -} - -processResources { - filter ReplaceTokens, tokens: [ - "version": parent.getVersion() - ] -} - -tasks.build.dependsOn tasks.shadowJar diff --git a/BetterStaffChat-bungeecord/src/main/java/dev/austech/betterstaffchat/bungeecord/BetterStaffChatBungeeCord.java b/BetterStaffChat-bungeecord/src/main/java/dev/austech/betterstaffchat/bungeecord/BetterStaffChatBungeeCord.java deleted file mode 100755 index 0679df8..0000000 --- a/BetterStaffChat-bungeecord/src/main/java/dev/austech/betterstaffchat/bungeecord/BetterStaffChatBungeeCord.java +++ /dev/null @@ -1,180 +0,0 @@ -/* - * BetterStaffChat - BetterStaffChatBungeeCord.java - * Copyright (C) 2021 AusTech Development Team - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package dev.austech.betterstaffchat.bungeecord; - -import com.google.common.collect.Lists; -import dev.austech.betterstaffchat.bungeecord.command.BetterStaffChatCommand; -import dev.austech.betterstaffchat.bungeecord.command.MuteStaffChatCommand; -import dev.austech.betterstaffchat.bungeecord.command.StaffChatCommand; -import dev.austech.betterstaffchat.bungeecord.command.ToggleStaffChatCommand; -import dev.austech.betterstaffchat.bungeecord.listener.PlayerListener; -import dev.austech.betterstaffchat.bungeecord.util.Config; -import dev.austech.betterstaffchat.bungeecord.util.LuckPermsUtil; -import dev.austech.betterstaffchat.bungeecord.util.StaffChatUtil; -import dev.austech.betterstaffchat.common.plugin.BetterStaffChatPlugin; -import dev.austech.betterstaffchat.common.dependency.BetterStaffChatDependencyProvider; -import dev.austech.betterstaffchat.common.dependency.DependencyEngine; -import dev.austech.betterstaffchat.common.discord.JDAImplementation; -import dev.austech.betterstaffchat.common.plugin.Platform; -import dev.austech.betterstaffchat.common.util.TextUtil; -import dev.austech.betterstaffchat.common.util.UpdateChecker; -import lombok.Getter; -import lombok.Setter; -import net.md_5.bungee.api.CommandSender; -import net.md_5.bungee.api.chat.TextComponent; -import net.md_5.bungee.api.connection.ProxiedPlayer; -import net.md_5.bungee.api.plugin.Plugin; -import net.md_5.bungee.config.Configuration; -import org.bstats.bungeecord.Metrics; - -import javax.naming.ConfigurationException; -import javax.security.auth.login.LoginException; -import java.io.File; -import java.util.ArrayList; -import java.util.Optional; -import java.util.UUID; -import java.util.concurrent.TimeUnit; -import java.util.logging.Level; - -public final class BetterStaffChatBungeeCord extends Plugin implements BetterStaffChatPlugin { - @Getter private static BetterStaffChatBungeeCord instance; - @Getter @Setter private Configuration config; - @Getter private final ArrayList ignoreStaffChat = Lists.newArrayList(); - @Getter private final ArrayList toggledStaffChat = Lists.newArrayList(); - @Getter private JDAImplementation jda; - @Getter boolean discordEnabled; - - @Override - public void onEnable() { - instance = this; - - TextUtil.init(getPlatform(), this); - Config.load(); - - if (getConfig().getBoolean("check-for-updates")) - getProxy().getScheduler().runAsync(this, () -> { - if (UpdateChecker.needsUpdate(this, getDescription().getVersion())) { - getProxy().getScheduler().schedule(this, () -> { - logPrefix("&eA new update for BetterStaffChat is available..."); - logPrefix("&ehttps://www.spigotmc.org/resources/91991"); - }, 3, TimeUnit.SECONDS); - } - }); - - new Metrics(this, 10954); - - if (getConfig().getBoolean("discord.bot.enabled") && getConfig().getBoolean("discord.webhook.enabled")) { - new ConfigurationException("Both Discord types are enabled").printStackTrace(); - return; - } - - if (this.getProxy().getPluginManager().getPlugin("LuckPerms") != null) { - LuckPermsUtil.setLuckPerms(net.luckperms.api.LuckPermsProvider.get()); - } - - this.discordEnabled = getConfig().getBoolean("discord.bot.enabled"); - - DependencyEngine dependencyEngine = DependencyEngine.createNew(new File(getPluginDataFolder(), "libs").toPath()); - dependencyEngine.addDependenciesFromProvider(BetterStaffChatDependencyProvider.getDependencies()); - - dependencyEngine.loadDependencies().thenAccept((empty) -> { - if (!dependencyEngine.getErrors().isEmpty()) { - Optional opt = dependencyEngine.getErrors().stream().filter(throwable -> throwable.getMessage().contains("Unable to make protected void java.net.URLClassLoader.addURL(java.net.URL) accessible: module java.base does not")).findFirst(); - if (opt.isPresent()) { - getLogger().log(Level.SEVERE, "An error occurred whilst starting BetterStaffChat - This is due to Java 16 and up being incompatible by default."); - getLogger().log(Level.SEVERE, "This error is fixable, please add the following flags to your startup after the \"java\":"); - getLogger().log(Level.SEVERE, ""); - getLogger().log(Level.SEVERE, "--add-opens java.base/java.net=ALL-UNNAMED"); - } else { - dependencyEngine.getErrors().forEach(Throwable::printStackTrace); - getLogger().log(Level.SEVERE, "Errors occurred whilst loading BSC."); - } - return; - } - - if (discordEnabled) { - this.getProxy().getScheduler().runAsync(this, () -> { - try { - this.jda = new JDAImplementation(net.dv8tion.jda.api.JDABuilder.createLight(getConfig().getString("discord.bot.token")).build(), StaffChatUtil.getInstance()); - jda.asJda().getPresence().setActivity(net.dv8tion.jda.api.entities.Activity.of( - net.dv8tion.jda.api.entities.Activity.ActivityType.valueOf(getConfig().getString("discord.bot.activity-type").toUpperCase().replace("PLAYING", "DEFAULT")), - getConfig().getString("discord.bot.activity") - )); - } catch (LoginException exception) { - exception.printStackTrace(); - } - }); - } - - this.getProxy().getPluginManager().registerListener(this, new PlayerListener()); - registerCommands(); - }); - } - - public File getPluginDataFolder() { - return getDataFolder(); - } - - public void logPrefix(String string) { - getProxy().getConsole().sendMessage(new TextComponent("[BetterStaffChat] " + TextUtil.colorize(string))); - } - - public void logPrefixDebug(String string) { - if (getConfig().getBoolean("debug")) - getProxy().getConsole().sendMessage(new TextComponent("[BetterStaffChat] Debug - " + TextUtil.colorize(string))); - } - - public void log(String string) { - getProxy().getConsole().sendMessage(new TextComponent(TextUtil.colorize(string))); - } - - @Override public Platform getPlatform() { - return Platform.BUNGEECORD; - } - - public boolean reloadConfig(CommandSender sender) { - boolean discordLoaded = getConfig().getBoolean("discord.bot.enabled"); - Config.load(); - - getProxy().getPluginManager().unregisterCommands(this); - registerCommands(); - - if (getConfig().getBoolean("discord.bot.enabled") != discordLoaded) { - logPrefix("&cYou enabled the discord bot in the config. Please restart the server for changes to take effect."); - if (sender instanceof ProxiedPlayer) - sender.sendMessage(TextUtil.colorizeToComponent("&cYou enabled the discord bot in the config. Please restart the server for changes to take effect.")); - return true; - } - return false; - } - - private void registerCommands() { - this.getProxy().getScheduler().runAsync(this, () -> { - this.getProxy().getPluginManager().registerCommand(this, new BetterStaffChatCommand()); - this.getProxy().getPluginManager().registerCommand(this, new StaffChatCommand("staffchat", "betterstaffchat.messages.send", getConfig().getStringList("commands.staffchat.aliases").toArray(new String[0]))); - this.getProxy().getPluginManager().registerCommand(this, new MuteStaffChatCommand("mutestaffchat", "betterstaffchat.mutestaffchat", getConfig().getStringList("commands.mutestaffchat.aliases").toArray(new String[0]))); - this.getProxy().getPluginManager().registerCommand(this, new ToggleStaffChatCommand("togglestaffchat", "betterstaffchat.togglestaffchat", getConfig().getStringList("commands.togglestaffchat.aliases").toArray(new String[0]))); - }); - } - - @Override - public void onDisable() { - if (isDiscordEnabled()) (getJda()).shutdown(); - } -} diff --git a/BetterStaffChat-bungeecord/src/main/java/dev/austech/betterstaffchat/bungeecord/command/BetterStaffChatCommand.java b/BetterStaffChat-bungeecord/src/main/java/dev/austech/betterstaffchat/bungeecord/command/BetterStaffChatCommand.java deleted file mode 100755 index a21f61a..0000000 --- a/BetterStaffChat-bungeecord/src/main/java/dev/austech/betterstaffchat/bungeecord/command/BetterStaffChatCommand.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * BetterStaffChat - BetterStaffChatCommand.java - * Copyright (C) 2021 AusTech Development Team - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package dev.austech.betterstaffchat.bungeecord.command; - -import dev.austech.betterstaffchat.bungeecord.BetterStaffChatBungeeCord; -import dev.austech.betterstaffchat.common.util.TextUtil; -import net.dv8tion.jda.api.entities.Activity; -import net.md_5.bungee.api.CommandSender; -import net.md_5.bungee.api.plugin.Command; - -public class BetterStaffChatCommand extends Command { - public BetterStaffChatCommand() { - super("betterstaffchat", null, "bsc"); - } - - @Override - public void execute(CommandSender sender, String[] args) { - if (args.length == 1 && args[0].equalsIgnoreCase("reload") && sender.hasPermission("betterstaffchat.reload")) { - boolean reload = BetterStaffChatBungeeCord.getInstance().reloadConfig(sender); - if (!reload && BetterStaffChatBungeeCord.getInstance().getConfig().getBoolean("discord.bot.enabled")) { - BetterStaffChatBungeeCord.getInstance().getJda().asJda().getPresence().setActivity(Activity.of( - Activity.ActivityType.valueOf(BetterStaffChatBungeeCord.getInstance().getConfig().getString("discord.bot.activity-type").toUpperCase().replace("PLAYING", "DEFAULT")), - BetterStaffChatBungeeCord.getInstance().getConfig().getString("discord.bot.activity") - )); - } - - sender.sendMessage(TextUtil.colorizeToComponent("&8&l&m-------------------------------")); - sender.sendMessage(TextUtil.colorizeToComponent(TextUtil.spacer(18) + "&c&l&oBetter&4&l&oStaffChat")); - sender.sendMessage(TextUtil.colorizeToComponent(TextUtil.spacer(13) + "&7Configuration Reloaded")); - sender.sendMessage(TextUtil.colorizeToComponent("&8&l&m-------------------------------")); - return; - } - - sender.sendMessage(TextUtil.colorizeToComponent("&8&l&m-------------------------------")); - sender.sendMessage(TextUtil.colorizeToComponent(TextUtil.spacer(18) + "&c&l&oBetter&4&l&oStaffChat")); - sender.sendMessage(TextUtil.colorizeToComponent(TextUtil.spacer(13) + "&7The &7&o\"better\"&r &7staff chat.")); - sender.sendMessage(TextUtil.colorizeToComponent("&8&l&m-------------------------------")); - } -} diff --git a/BetterStaffChat-bungeecord/src/main/java/dev/austech/betterstaffchat/bungeecord/command/MuteStaffChatCommand.java b/BetterStaffChat-bungeecord/src/main/java/dev/austech/betterstaffchat/bungeecord/command/MuteStaffChatCommand.java deleted file mode 100644 index 965faff..0000000 --- a/BetterStaffChat-bungeecord/src/main/java/dev/austech/betterstaffchat/bungeecord/command/MuteStaffChatCommand.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * BetterStaffChat - MuteStaffChatCommand.java - * Copyright (C) 2021 AusTech Development Team - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package dev.austech.betterstaffchat.bungeecord.command; - -import dev.austech.betterstaffchat.bungeecord.BetterStaffChatBungeeCord; -import dev.austech.betterstaffchat.common.util.TextUtil; -import net.md_5.bungee.api.CommandSender; -import net.md_5.bungee.api.connection.ProxiedPlayer; -import net.md_5.bungee.api.plugin.Command; - -public class MuteStaffChatCommand extends Command { - public MuteStaffChatCommand(String command, String permission, String... aliases) { - super(command, permission, aliases); - setPermissionMessage(TextUtil.colorize("&cNo permission.")); - } - - @Override - public void execute(CommandSender sender, String[] args) { - if (!(sender instanceof ProxiedPlayer)) { - sender.sendMessage(TextUtil.colorizeToComponent("&cThis command can only be used by players.")); - return; - } - - if (BetterStaffChatBungeeCord.getInstance().getIgnoreStaffChat().contains(((ProxiedPlayer) sender).getUniqueId())) { - BetterStaffChatBungeeCord.getInstance().getIgnoreStaffChat().remove(((ProxiedPlayer) sender).getUniqueId()); - } else { - if (BetterStaffChatBungeeCord.getInstance().getToggledStaffChat().contains(((ProxiedPlayer) sender).getUniqueId())) { - BetterStaffChatBungeeCord.getInstance().getToggledStaffChat().remove(((ProxiedPlayer) sender).getUniqueId()); - } - BetterStaffChatBungeeCord.getInstance().getIgnoreStaffChat().add(((ProxiedPlayer) sender).getUniqueId()); - } - - sender.sendMessage(TextUtil.colorizeToComponent(BetterStaffChatBungeeCord.getInstance().getIgnoreStaffChat().contains(((ProxiedPlayer) sender).getUniqueId()) ? - BetterStaffChatBungeeCord.getInstance().getConfig().getString("staffchat.mute-on") : - BetterStaffChatBungeeCord.getInstance().getConfig().getString("staffchat.mute-off")) - ); - } -} diff --git a/BetterStaffChat-bungeecord/src/main/java/dev/austech/betterstaffchat/bungeecord/command/StaffChatCommand.java b/BetterStaffChat-bungeecord/src/main/java/dev/austech/betterstaffchat/bungeecord/command/StaffChatCommand.java deleted file mode 100644 index afb6c63..0000000 --- a/BetterStaffChat-bungeecord/src/main/java/dev/austech/betterstaffchat/bungeecord/command/StaffChatCommand.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * BetterStaffChat - StaffChatCommand.java - * Copyright (C) 2021 AusTech Development Team - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package dev.austech.betterstaffchat.bungeecord.command; - -import dev.austech.betterstaffchat.bungeecord.BetterStaffChatBungeeCord; -import dev.austech.betterstaffchat.bungeecord.util.StaffChatUtil; -import dev.austech.betterstaffchat.common.util.TextUtil; -import net.md_5.bungee.api.ChatColor; -import net.md_5.bungee.api.CommandSender; -import net.md_5.bungee.api.connection.ProxiedPlayer; -import net.md_5.bungee.api.plugin.Command; - -import java.util.Arrays; - -public class StaffChatCommand extends Command { - public StaffChatCommand(String command, String permission, String... aliases) { - super(command, permission, aliases); - setPermissionMessage(TextUtil.colorize("&cNo permission.")); - } - - @Override - public void execute(CommandSender sender, String[] args) { - if (sender instanceof ProxiedPlayer && BetterStaffChatBungeeCord.getInstance().getIgnoreStaffChat().contains(((ProxiedPlayer) sender).getUniqueId())) { - sender.sendMessage(TextUtil.colorizeToComponent("&cYour staff chat is currently disabled.")); - return; - } - - if (args.length == 0) { - sender.sendMessage(TextUtil.colorizeToComponent("&cUsage: /" + this.getName() + " ")); - return; - } - - StaffChatUtil.getInstance().broadcast(StaffChatUtil.getInstance().getFormattedMessage(sender, String.join(" ", Arrays.copyOfRange(args, 0, args.length))), "betterstaffchat.messages.read"); - - if (sender instanceof ProxiedPlayer) - StaffChatUtil.getInstance().discord(sender, ChatColor.stripColor(TextUtil.colorize(BetterStaffChatBungeeCord.getInstance().getConfig().getString("discord.discord-messages.staffchat-format") - .replace("%player_name%", sender.getName()) - .replace("%message%", String.join(" ", Arrays.copyOfRange(args, 0, args.length))) - .replace("%server%", StaffChatUtil.getFormattedServerName(((ProxiedPlayer) sender).getServer().getInfo().getName()))))); - else - StaffChatUtil.getInstance().discord(sender, ChatColor.stripColor(TextUtil.colorize(BetterStaffChatBungeeCord.getInstance().getConfig().getString("discord.discord-messages.staffchat-format") - .replace("%player_name%", BetterStaffChatBungeeCord.getInstance().getConfig().getString("staffchat.console-replacement")) - .replace("%message%", String.join(" ", Arrays.copyOfRange(args, 0, args.length))) - .replace("%server%", BetterStaffChatBungeeCord.getInstance().getConfig().getString("staffchat.console-server-replacement")) - ))); - } -} diff --git a/BetterStaffChat-bungeecord/src/main/java/dev/austech/betterstaffchat/bungeecord/command/ToggleStaffChatCommand.java b/BetterStaffChat-bungeecord/src/main/java/dev/austech/betterstaffchat/bungeecord/command/ToggleStaffChatCommand.java deleted file mode 100755 index 4af7f74..0000000 --- a/BetterStaffChat-bungeecord/src/main/java/dev/austech/betterstaffchat/bungeecord/command/ToggleStaffChatCommand.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * BetterStaffChat - ToggleStaffChatCommand.java - * Copyright (C) 2021 AusTech Development Team - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package dev.austech.betterstaffchat.bungeecord.command; - -import dev.austech.betterstaffchat.bungeecord.BetterStaffChatBungeeCord; -import dev.austech.betterstaffchat.common.util.TextUtil; -import net.md_5.bungee.api.CommandSender; -import net.md_5.bungee.api.connection.ProxiedPlayer; -import net.md_5.bungee.api.plugin.Command; - -public class ToggleStaffChatCommand extends Command { - public ToggleStaffChatCommand(String command, String permission, String... aliases) { - super(command, permission, aliases); - setPermissionMessage(TextUtil.colorize("&cNo permission.")); - } - - @Override - public void execute(CommandSender sender, String[] args) { - if (!(sender instanceof ProxiedPlayer)) { - sender.sendMessage(TextUtil.colorizeToComponent("&cThis command can only be used by players.")); - return; - } - - if (BetterStaffChatBungeeCord.getInstance().getIgnoreStaffChat().contains(((ProxiedPlayer) sender).getUniqueId())) { - sender.sendMessage(TextUtil.colorizeToComponent("&cYour staff chat is currently disabled.")); - return; - } - - if (BetterStaffChatBungeeCord.getInstance().getToggledStaffChat().contains(((ProxiedPlayer) sender).getUniqueId())) { - BetterStaffChatBungeeCord.getInstance().getToggledStaffChat().remove(((ProxiedPlayer) sender).getUniqueId()); - } else { - BetterStaffChatBungeeCord.getInstance().getToggledStaffChat().add(((ProxiedPlayer) sender).getUniqueId()); - } - - sender.sendMessage(TextUtil.colorizeToComponent(BetterStaffChatBungeeCord.getInstance().getToggledStaffChat().contains(((ProxiedPlayer) sender).getUniqueId()) ? - BetterStaffChatBungeeCord.getInstance().getConfig().getString("staffchat.toggle-on") : - BetterStaffChatBungeeCord.getInstance().getConfig().getString("staffchat.toggle-off")) - ); - } -} diff --git a/BetterStaffChat-bungeecord/src/main/java/dev/austech/betterstaffchat/bungeecord/listener/PlayerListener.java b/BetterStaffChat-bungeecord/src/main/java/dev/austech/betterstaffchat/bungeecord/listener/PlayerListener.java deleted file mode 100755 index 83f39ab..0000000 --- a/BetterStaffChat-bungeecord/src/main/java/dev/austech/betterstaffchat/bungeecord/listener/PlayerListener.java +++ /dev/null @@ -1,163 +0,0 @@ -/* - * BetterStaffChat - PlayerListener.java - * Copyright (C) 2021 AusTech Development Team - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package dev.austech.betterstaffchat.bungeecord.listener; - -import dev.austech.betterstaffchat.bungeecord.BetterStaffChatBungeeCord; -import dev.austech.betterstaffchat.bungeecord.util.LuckPermsUtil; -import dev.austech.betterstaffchat.bungeecord.util.StaffChatUtil; -import dev.austech.betterstaffchat.common.util.TextUtil; -import net.md_5.bungee.api.ChatColor; -import net.md_5.bungee.api.connection.ProxiedPlayer; -import net.md_5.bungee.api.event.ChatEvent; -import net.md_5.bungee.api.event.PlayerDisconnectEvent; -import net.md_5.bungee.api.event.PostLoginEvent; -import net.md_5.bungee.api.event.ServerSwitchEvent; -import net.md_5.bungee.api.plugin.Listener; -import net.md_5.bungee.api.scheduler.ScheduledTask; -import net.md_5.bungee.event.EventHandler; - -import java.util.concurrent.TimeUnit; - -public class PlayerListener implements Listener { - @EventHandler - public void onPlayerChat(ChatEvent event) { - if (event.getMessage().startsWith("/")) return; - - String prefix = BetterStaffChatBungeeCord.getInstance().getConfig().getString("staffchat.prefix"); - if (prefix.startsWith("/")) return; - - ProxiedPlayer player = (ProxiedPlayer) event.getSender(); - - if (player.hasPermission("betterstaffchat.messages.send") && event.getMessage().startsWith(prefix) && event.getMessage().length() > prefix.length() && !prefix.equals("")) { - event.setCancelled(true); - - if (BetterStaffChatBungeeCord.getInstance().getIgnoreStaffChat().contains(player.getUniqueId())) { - player.sendMessage(TextUtil.colorizeToComponent("&cYour staff chat is currently disabled.")); - return; - } - - StaffChatUtil.getInstance().broadcast(StaffChatUtil.getInstance().getFormattedMessage(player, event.getMessage().substring(prefix.length())), "betterstaffchat.messages.read"); - StaffChatUtil.getInstance().discord( - player, - ChatColor.stripColor(TextUtil.colorize(BetterStaffChatBungeeCord.getInstance().getConfig().getString("discord.discord-messages.staffchat-format") - .replace("%player_name%", player.getName()) - .replace("%message%", event.getMessage().substring(prefix.length())) - .replace("%server%", StaffChatUtil.getFormattedServerName(player.getServer().getInfo().getName())) - ))); - } else if (player.hasPermission("betterstaffchat.messages.send") && BetterStaffChatBungeeCord.getInstance().getToggledStaffChat().contains(player.getUniqueId())) { - event.setCancelled(true); - - StaffChatUtil.getInstance().broadcast(StaffChatUtil.getInstance().getFormattedMessage(player, event.getMessage()), "betterstaffchat.messages.read"); - StaffChatUtil.getInstance().discord( - player, - ChatColor.stripColor(TextUtil.colorize(BetterStaffChatBungeeCord.getInstance().getConfig().getString("discord.discord-messages.staffchat-format") - .replace("%player_name%", player.getName()) - .replace("%message%", event.getMessage()) - .replace("%server%", StaffChatUtil.getFormattedServerName(player.getServer().getInfo().getName())) - ))); - } - } - - @EventHandler - public void onPlayerPostLogin(PostLoginEvent event) { - if (event.getPlayer().hasPermission("betterstaffchat.messages.join") && !BetterStaffChatBungeeCord.getInstance().getConfig().getString("staffchat.join").equals("")) { - BetterStaffChatBungeeCord.getInstance().getProxy().getScheduler().schedule(BetterStaffChatBungeeCord.getInstance(), () -> { - // This is temporarily disable so a fix can be added. - // Although it does prevent some errors, it stops the staff-chat working in some instances. - - // if (event.getPlayer().getServer() == null) return; - - - int timeDelay = 100; - if (BetterStaffChatBungeeCord.getInstance().getConfig().get("join-delay")) timeDelay = BetterStaffChatBungeeCord.getInstance().getConfig().getInt("join-delay"); - - ScheduledTask task = BetterStaffChatBungeeCord.getInstance().getProxy().getScheduler().schedule(BetterStaffChatBungeeCord.getInstance(), () -> { - - String message = TextUtil.colorize( - BetterStaffChatBungeeCord.getInstance().getConfig().getString("staffchat.join") - .replace("%player_name%", event.getPlayer().getName()) - .replace("%server%", StaffChatUtil.getFormattedServerName(event.getPlayer().getServer().getInfo().getName())) - ); - - if (BetterStaffChatBungeeCord.getInstance().getProxy().getPluginManager().getPlugin("LuckPerms") != null) { - message = message.replace("%luckperms_prefix%", LuckPermsUtil.getPrefix(event.getPlayer())).replace("%luckperms_suffix%", LuckPermsUtil.getSuffix(event.getPlayer())); - } - - message = TextUtil.colorize(message); - - StaffChatUtil.getInstance().broadcast(message, "betterstaffchat.messages.read"); - StaffChatUtil.getInstance().discord(event.getPlayer(), ChatColor.stripColor(TextUtil.colorize(BetterStaffChatBungeeCord.getInstance().getConfig().getString("discord.discord-messages.staffchat-join") - .replace("%player_name%", event.getPlayer().getName()) - .replace("%server%", StaffChatUtil.getFormattedServerName(event.getPlayer().getServer().getInfo().getName()))))); - }, 100, TimeUnit.MILLISECONDS); - }, timeDelay, TimeUnit.SECONDS); - } - } - - @EventHandler - public void onPlayerDisconnect(PlayerDisconnectEvent event) { - if (event.getPlayer().hasPermission("betterstaffchat.messages.leave") && !BetterStaffChatBungeeCord.getInstance().getConfig().getString("staffchat.leave").equals("")) { - BetterStaffChatBungeeCord.getInstance().getProxy().getScheduler().schedule(BetterStaffChatBungeeCord.getInstance(), () -> { - if (event.getPlayer().getServer() == null) return; - String message = TextUtil.colorize( - BetterStaffChatBungeeCord.getInstance().getConfig().getString("staffchat.leave") - .replace("%player_name%", event.getPlayer().getName()) - .replace("%server%", StaffChatUtil.getFormattedServerName(event.getPlayer().getServer().getInfo().getName())) - ); - - if (BetterStaffChatBungeeCord.getInstance().getProxy().getPluginManager().getPlugin("LuckPerms") != null) { - message = message.replace("%luckperms_prefix%", LuckPermsUtil.getPrefix(event.getPlayer())).replace("%luckperms_suffix%", LuckPermsUtil.getSuffix(event.getPlayer())); - } - - message = TextUtil.colorize(message); - - StaffChatUtil.getInstance().broadcast(message, "betterstaffchat.messages.read"); - StaffChatUtil.getInstance().discord(event.getPlayer(), ChatColor.stripColor(TextUtil.colorize(BetterStaffChatBungeeCord.getInstance().getConfig().getString("discord.discord-messages.staffchat-leave") - .replace("%player_name%", event.getPlayer().getName()) - .replace("%server%", StaffChatUtil.getFormattedServerName(event.getPlayer().getServer().getInfo().getName()))))); - }, 1, TimeUnit.SECONDS); - } - } - - @EventHandler - public void onServerSwitch(ServerSwitchEvent event) { - if (event.getPlayer().hasPermission("betterstaffchat.messages.switch") && !BetterStaffChatBungeeCord.getInstance().getConfig().getString("staffchat.switch").equals("")) { - if (event.getFrom() == null) return; - - String message = TextUtil.colorize( - BetterStaffChatBungeeCord.getInstance().getConfig().getString("staffchat.switch") - .replace("%player_name%", event.getPlayer().getName()) - .replace("%from%", StaffChatUtil.getFormattedServerName(event.getFrom().getName())) - .replace("%to%", StaffChatUtil.getFormattedServerName(event.getPlayer().getServer().getInfo().getName())) - ); - - if (BetterStaffChatBungeeCord.getInstance().getProxy().getPluginManager().getPlugin("LuckPerms") != null) { - message = message.replace("%luckperms_prefix%", LuckPermsUtil.getPrefix(event.getPlayer())).replace("%luckperms_suffix%", LuckPermsUtil.getSuffix(event.getPlayer())); - } - - message = TextUtil.colorize(message); - - StaffChatUtil.getInstance().broadcast(message, "betterstaffchat.messages.read"); - StaffChatUtil.getInstance().discord(event.getPlayer(), ChatColor.stripColor(TextUtil.colorize(BetterStaffChatBungeeCord.getInstance().getConfig().getString("discord.discord-messages.staffchat-switch") - .replace("%player_name%", event.getPlayer().getName())) - .replace("%from%", StaffChatUtil.getFormattedServerName(event.getFrom().getName())) - .replace("%to%", StaffChatUtil.getFormattedServerName(event.getPlayer().getServer().getInfo().getName())))); - } - } -} diff --git a/BetterStaffChat-bungeecord/src/main/java/dev/austech/betterstaffchat/bungeecord/util/Config.java b/BetterStaffChat-bungeecord/src/main/java/dev/austech/betterstaffchat/bungeecord/util/Config.java deleted file mode 100644 index 69cea46..0000000 --- a/BetterStaffChat-bungeecord/src/main/java/dev/austech/betterstaffchat/bungeecord/util/Config.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * BetterStaffChat - Config.java - * Copyright (C) 2021 AusTech Development Team - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package dev.austech.betterstaffchat.bungeecord.util; - -import dev.austech.betterstaffchat.bungeecord.BetterStaffChatBungeeCord; -import lombok.experimental.UtilityClass; -import net.md_5.bungee.config.Configuration; -import net.md_5.bungee.config.ConfigurationProvider; -import net.md_5.bungee.config.YamlConfiguration; -import org.yaml.snakeyaml.parser.ParserException; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.nio.file.Files; -import java.util.Optional; - -@UtilityClass -public class Config { - public void load() { - BetterStaffChatBungeeCord.getInstance().getDataFolder().mkdir(); - - File file = new File(BetterStaffChatBungeeCord.getInstance().getPluginDataFolder(), "config.yml"); - - if (!file.exists()) { - try (InputStream in = BetterStaffChatBungeeCord.getInstance().getClass().getResourceAsStream("/config.yml")) { - Files.copy(in, file.toPath()); - } catch (IOException e) { - e.printStackTrace(); - } - } - try { - Configuration config = ConfigurationProvider.getProvider(YamlConfiguration.class).load(new File(BetterStaffChatBungeeCord.getInstance().getPluginDataFolder(), "config.yml")); - - int currentVersion = ConfigurationProvider.getProvider(YamlConfiguration.class).load(BetterStaffChatBungeeCord.getInstance().getClass().getResourceAsStream("/config.yml")).getInt("config-version"); - - if (config.getInt("config-version") != currentVersion) - newVersion(config.getInt("config-version")); - else - BetterStaffChatBungeeCord.getInstance().setConfig(config); - } catch (IOException e) { - e.printStackTrace(); - } catch (ParserException exception) { - File broken = new File(file.getAbsolutePath() + ".broken." + System.currentTimeMillis()); - file.renameTo(broken); - BetterStaffChatBungeeCord.getInstance().logPrefix("&cThe config file is broken, and has been renamed to config.yml.broken." + System.currentTimeMillis()); - - try (InputStream in = BetterStaffChatBungeeCord.getInstance().getClass().getResourceAsStream("/config.yml")) { - Files.copy(in, file.toPath()); - } catch (IOException e) { - e.printStackTrace(); - } - - try { - BetterStaffChatBungeeCord.getInstance().setConfig(ConfigurationProvider.getProvider(YamlConfiguration.class).load(new File(BetterStaffChatBungeeCord.getInstance().getPluginDataFolder(), "config.yml"))); - } catch (IOException ex1) { - ex1.printStackTrace(); - } - } - } - - public Optional getOptional(String path) { - String find = BetterStaffChatBungeeCord.getInstance().getConfig().getString(path); - return Optional.ofNullable(find.isEmpty() ? null : find); - } - - public void newVersion(int ver) throws IOException { - File file = new File(BetterStaffChatBungeeCord.getInstance().getPluginDataFolder(), "config.yml"); - - File broken = new File(file.getAbsolutePath() + ".old." + ver); - file.renameTo(broken); - BetterStaffChatBungeeCord.getInstance().logPrefix("&cThe config file is old, and has been renamed to config.yml.old." + ver); - - try (InputStream inputStream = BetterStaffChatBungeeCord.getInstance().getClass().getResourceAsStream("/config.yml")) { - Files.copy(inputStream, file.toPath()); - } catch (IOException e) { - e.printStackTrace(); - } - - BetterStaffChatBungeeCord.getInstance().setConfig(ConfigurationProvider.getProvider(YamlConfiguration.class).load(new File(BetterStaffChatBungeeCord.getInstance().getPluginDataFolder(), "config.yml"))); - } -} diff --git a/BetterStaffChat-bungeecord/src/main/java/dev/austech/betterstaffchat/bungeecord/util/LuckPermsUtil.java b/BetterStaffChat-bungeecord/src/main/java/dev/austech/betterstaffchat/bungeecord/util/LuckPermsUtil.java deleted file mode 100755 index 7e7129c..0000000 --- a/BetterStaffChat-bungeecord/src/main/java/dev/austech/betterstaffchat/bungeecord/util/LuckPermsUtil.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * BetterStaffChat - LuckPermsUtil.java - * Copyright (C) 2021 AusTech Development Team - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package dev.austech.betterstaffchat.bungeecord.util; - -import dev.austech.betterstaffchat.common.util.TextUtil; -import lombok.Setter; -import lombok.experimental.UtilityClass; -import net.luckperms.api.LuckPerms; -import net.luckperms.api.model.user.User; -import net.md_5.bungee.api.connection.ProxiedPlayer; - -@UtilityClass -public class LuckPermsUtil { - @Setter - private LuckPerms luckPerms; - - /** - * Gets a player's LuckPerms prefix. - * - * @param player the player to get the prefix - * @return the prefix - */ - public String getPrefix(ProxiedPlayer player) { - User lpUser = luckPerms.getUserManager().getUser(player.getUniqueId()); - if (lpUser.getCachedData().getMetaData().getPrefix() == null) return ""; - return TextUtil.colorize(lpUser.getCachedData().getMetaData().getPrefix()); - } - - /** - * Gets a player's LuckPerms suffix. - * - * @param player the player to get the suffix - * @return the suffix - */ - public String getSuffix(ProxiedPlayer player) { - User lpUser = luckPerms.getUserManager().getUser(player.getUniqueId()); - if (lpUser.getCachedData().getMetaData().getSuffix() == null) return ""; - return TextUtil.colorize(lpUser.getCachedData().getMetaData().getSuffix()); - } -} diff --git a/BetterStaffChat-bungeecord/src/main/java/dev/austech/betterstaffchat/bungeecord/util/StaffChatUtil.java b/BetterStaffChat-bungeecord/src/main/java/dev/austech/betterstaffchat/bungeecord/util/StaffChatUtil.java deleted file mode 100755 index 42d48a8..0000000 --- a/BetterStaffChat-bungeecord/src/main/java/dev/austech/betterstaffchat/bungeecord/util/StaffChatUtil.java +++ /dev/null @@ -1,176 +0,0 @@ -/* - * BetterStaffChat - StaffChatUtil.java - * Copyright (C) 2021 AusTech Development Team - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package dev.austech.betterstaffchat.bungeecord.util; - -import dev.austech.betterstaffchat.bungeecord.BetterStaffChatBungeeCord; -import dev.austech.betterstaffchat.common.discord.DiscordWebhook; -import dev.austech.betterstaffchat.common.util.AbstractStaffChatUtil; -import dev.austech.betterstaffchat.common.util.TextUtil; -import lombok.Getter; -import net.md_5.bungee.api.ChatColor; -import net.md_5.bungee.api.connection.ProxiedPlayer; - -import java.awt.*; -import java.io.IOException; - -public class StaffChatUtil extends AbstractStaffChatUtil { - @Getter - private static final StaffChatUtil instance = new StaffChatUtil(); - - /** - * Gets the formatted server name from the config, if exists. - * - * @param serverName the server to replace - * @return the server with the formatted name or the provided server name if not found - */ - public static String getFormattedServerName(String serverName) { - return Config.getOptional("staffchat.server-replacement." + serverName).orElse(serverName); - } - - @Override - public String getFormattedMessage(Object sender, String message) { - StringBuilder builder = new StringBuilder(); - message = BetterStaffChatBungeeCord.getInstance().getConfig().getString("staffchat.format") - .replace("%player_name%", (sender instanceof ProxiedPlayer) ? ((ProxiedPlayer) sender).getName() : BetterStaffChatBungeeCord.getInstance().getConfig().getString("staffchat.console-replacement")) - .replace("%message%", BetterStaffChatBungeeCord.getInstance().getConfig().getBoolean("staffchat.strip-color-codes") ? ChatColor.stripColor(TextUtil.colorize(message)) : message) - .replace("%server%", (sender instanceof ProxiedPlayer) ? getFormattedServerName(((ProxiedPlayer) sender).getServer().getInfo().getName()) : BetterStaffChatBungeeCord.getInstance().getConfig().getString("staffchat.console-server-replacement")); - - if (BetterStaffChatBungeeCord.getInstance().getProxy().getPluginManager().getPlugin("LuckPerms") != null && sender instanceof ProxiedPlayer) { - message = message.replace("%luckperms_prefix%", LuckPermsUtil.getPrefix((ProxiedPlayer) sender)).replace("%luckperms_suffix%", LuckPermsUtil.getSuffix((ProxiedPlayer) sender)); - } else if (BetterStaffChatBungeeCord.getInstance().getProxy().getPluginManager().getPlugin("LuckPerms") != null) { - message = message.replace("%luckperms_prefix%", "").replace("%luckperms_suffix%", ""); - } - - for (String word : message.split(" ")) { - builder.append(TextUtil.colorize(word)); - builder.append(" "); - } - - return builder.substring(0, builder.length() - 1); - } - - @Override - public void broadcast(String string, String permission) { - for (ProxiedPlayer player : BetterStaffChatBungeeCord.getInstance().getProxy().getPlayers()) { - if (!BetterStaffChatBungeeCord.getInstance().getIgnoreStaffChat().contains(player.getUniqueId()) && player.hasPermission(permission)) { - player.sendMessage(TextUtil.colorizeToComponent(string)); - } - } - - if (BetterStaffChatBungeeCord.getInstance().getConfig().getBoolean("staffchat.log-to-console")) { - BetterStaffChatBungeeCord.getInstance().log(TextUtil.colorize(string)); - } - } - - public void discord(Object sender, String string) { - if (BetterStaffChatBungeeCord.getInstance().getProxy().getPluginManager().getPlugin("LuckPerms") != null && sender instanceof ProxiedPlayer) { - string = string.replace("%luckperms_prefix%", ChatColor.stripColor(TextUtil.colorize(LuckPermsUtil.getPrefix((ProxiedPlayer) sender)))).replace("%luckperms_suffix%", ChatColor.stripColor(TextUtil.colorize(LuckPermsUtil.getSuffix((ProxiedPlayer) sender)))); - } else if (BetterStaffChatBungeeCord.getInstance().getProxy().getPluginManager().getPlugin("LuckPerms") != null) { - string = string.replace("%luckperms_prefix%", "").replace("%luckperms_suffix%", ""); - } - - String finalString = string; - BetterStaffChatBungeeCord.getInstance().getProxy().getScheduler().runAsync(BetterStaffChatBungeeCord.getInstance(), () -> { - if (BetterStaffChatBungeeCord.getInstance().getConfig().getBoolean("discord.webhook.enabled")) { - DiscordWebhook webhook = new DiscordWebhook(BetterStaffChatBungeeCord.getInstance().getConfig().getString("discord.webhook.url")); - - if (BetterStaffChatBungeeCord.getInstance().getConfig().getBoolean("discord.discord-messages.embed.enabled")) { - webhook.addEmbed(generateEmbed(sender, finalString)); - } else { - webhook.setContent(finalString); - } - - try { - webhook.execute(); - } catch (IOException exception) { - exception.printStackTrace(); - BetterStaffChatBungeeCord.getInstance().logPrefix("&cFailed to send Discord webhook."); - } - } else if (BetterStaffChatBungeeCord.getInstance().getConfig().getBoolean("discord.bot.enabled")) { - if (BetterStaffChatBungeeCord.getInstance().getConfig().getBoolean("discord.discord-messages.embed.enabled")) { - for (String guildChannelPair : BetterStaffChatBungeeCord.getInstance().getConfig().getStringList("discord.bot.channels")) { - String[] parts = guildChannelPair.split(": "); - BetterStaffChatBungeeCord.getInstance().getJda().sendEmbed(parts[0], parts[1], generateEmbed(sender, finalString)); - } - } else { - for (String guildChannelPair : BetterStaffChatBungeeCord.getInstance().getConfig().getStringList("discord.bot.channels")) { - String[] parts = guildChannelPair.split(": "); - BetterStaffChatBungeeCord.getInstance().getJda().sendMessage(parts[0], parts[1], finalString); - } - } - } - }); - } - - @Override - public void handleDiscord(Object event) { - if (BetterStaffChatBungeeCord.getInstance().getConfig().getStringList("discord.bot.channels").stream().anyMatch(a -> a.equals(((net.dv8tion.jda.api.events.message.guild.GuildMessageReceivedEvent) event).getGuild().getId() + ": " + ((net.dv8tion.jda.api.events.message.guild.GuildMessageReceivedEvent) event).getChannel().getId()))) { - StringBuilder builder = new StringBuilder(); - - StringBuilder discordMessage = new StringBuilder(((net.dv8tion.jda.api.events.message.guild.GuildMessageReceivedEvent) event).getMessage().getContentStripped().trim()); - - if (!((net.dv8tion.jda.api.events.message.guild.GuildMessageReceivedEvent) event).getMessage().getAttachments().isEmpty()) { - for (net.dv8tion.jda.api.entities.Message.Attachment attachment : ((net.dv8tion.jda.api.events.message.guild.GuildMessageReceivedEvent) event).getMessage().getAttachments()) { - discordMessage.append(" ").append(attachment.getUrl()); - } - } - - String nickName = ((net.dv8tion.jda.api.events.message.guild.GuildMessageReceivedEvent) event).getMember().getNickname(); - - String name = BetterStaffChatBungeeCord.getInstance().getConfig().getString("discord.discord-messages.name-format") - .replace("%username%", ((net.dv8tion.jda.api.events.message.guild.GuildMessageReceivedEvent) event).getAuthor().getName()) - .replace("%discriminator%", ((net.dv8tion.jda.api.events.message.guild.GuildMessageReceivedEvent) event).getAuthor().getDiscriminator()) - .replace("%nickname%", nickName != null ? nickName : ((net.dv8tion.jda.api.events.message.guild.GuildMessageReceivedEvent) event).getAuthor().getName()); - - String message = BetterStaffChatBungeeCord.getInstance().getConfig().getString("staffchat.format") - .replace("%player_name%", name) - .replace("%message%", discordMessage.toString()) - .replace("%server%", "Discord") - .replaceAll("%\\S*%", ""); - - - for (String word : message.split(" ")) { - builder.append(ChatColor.translateAlternateColorCodes('&', word)); - builder.append(" "); - } - - broadcast(builder.substring(0, builder.length() - 1), "betterstaffchat.messages.read"); - } - } - - protected DiscordWebhook.EmbedObject generateEmbed(Object sender, String string) { - DiscordWebhook.EmbedObject embed = new DiscordWebhook.EmbedObject(); - embed.setColor(Color.decode(BetterStaffChatBungeeCord.getInstance().getConfig().getString("discord.discord-messages.embed.embed-color"))); - embed.setDescription(string); - - String footer = BetterStaffChatBungeeCord.getInstance().getConfig().getString("discord.discord-messages.embed.embed-footer") - .replace("%player_name%", (sender instanceof ProxiedPlayer) ? TextUtil.cleanForDiscord(((ProxiedPlayer) sender).getName()) : BetterStaffChatBungeeCord.getInstance().getConfig().getString("staffchat.console-replacement")) - .replace("%uuid%", (sender instanceof ProxiedPlayer) ? ((ProxiedPlayer) sender).getUniqueId().toString() : BetterStaffChatBungeeCord.getInstance().getConfig().getString("staffchat.console-uuid-replacement")) - .replace("%server%", (sender instanceof ProxiedPlayer) ? getFormattedServerName(((ProxiedPlayer) sender).getServer().getInfo().getName()) : BetterStaffChatBungeeCord.getInstance().getConfig().getString("staffchat.console-server-replacement")); - - String icon = BetterStaffChatBungeeCord.getInstance().getConfig().getString("discord.discord-messages.embed.embed-footer-icon").replace("%player_name%", (sender instanceof ProxiedPlayer) ? TextUtil.cleanForDiscord(((ProxiedPlayer) sender).getName()) : BetterStaffChatBungeeCord.getInstance().getConfig().getString("staffchat.console-replacement")) - .replace("%uuid%", (sender instanceof ProxiedPlayer) ? ((ProxiedPlayer) sender).getUniqueId().toString() : BetterStaffChatBungeeCord.getInstance().getConfig().getString("staffchat.console-uuid-replacement")) - .replace("%server%", (sender instanceof ProxiedPlayer) ? getFormattedServerName(((ProxiedPlayer) sender).getServer().getInfo().getName()) : BetterStaffChatBungeeCord.getInstance().getConfig().getString("staffchat.console-server-replacement")); - - if (!BetterStaffChatBungeeCord.getInstance().getConfig().getString("discord.discord-messages.embed.embed-footer").equals("")) { - embed.setFooter(footer, icon); - } else embed.setFooter(footer, null); - return embed; - } -} diff --git a/BetterStaffChat-bungeecord/src/main/resources/bungee.yml b/BetterStaffChat-bungeecord/src/main/resources/bungee.yml deleted file mode 100644 index d0728b8..0000000 --- a/BetterStaffChat-bungeecord/src/main/resources/bungee.yml +++ /dev/null @@ -1,4 +0,0 @@ -name: BetterStaffChat -version: @version@ -main: dev.austech.betterstaffchat.bungeecord.BetterStaffChatBungeeCord -author: AusTechDev \ No newline at end of file diff --git a/BetterStaffChat-common/build.gradle b/BetterStaffChat-common/build.gradle deleted file mode 100644 index f3cf87d..0000000 --- a/BetterStaffChat-common/build.gradle +++ /dev/null @@ -1,24 +0,0 @@ -/* - * BetterStaffChat - build.gradle - * Copyright (C) 2021 AusTech Development Team - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -dependencies { - compileOnly 'me.lucko:jar-relocator:1.4' - compileOnly 'net.md-5:bungeecord-chat:1.16-R0.4' -} - -tasks.build.dependsOn tasks.shadowJar \ No newline at end of file diff --git a/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/dependency/BetterStaffChatDependencyProvider.java b/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/dependency/BetterStaffChatDependencyProvider.java deleted file mode 100755 index a59fe1a..0000000 --- a/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/dependency/BetterStaffChatDependencyProvider.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2021 Justin Heflin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - * This file is apart of PluginBase (https://github.com/Demeng7215/PluginBase) - */ - -package dev.austech.betterstaffchat.common.dependency; - -import dev.austech.betterstaffchat.common.dependency.maven.builder.MavenDependencyProvider; -import dev.austech.betterstaffchat.common.dependency.maven.builder.MavenDependencyProviderBuilder; -import dev.austech.betterstaffchat.common.dependency.relocation.RelocationInfo; - -public class BetterStaffChatDependencyProvider { - public static MavenDependencyProvider getDependencies() { - MavenDependencyProviderBuilder builder = new MavenDependencyProviderBuilder(); - - builder.dependency("org|slf4j", "slf4j-api", "1.7.25"); - builder.dependency("org|slf4j", "slf4j-nop", "1.7.25"); - builder.relocation("org|slf4j", "dev.austech.betterstaffchat.shaded.slf4j"); - - builder.repository("https://m2.dv8tion.net/releases/"); - builder.dependency("net|dv8tion", "JDA", "4.2.1_262"); - - builder.relocation(RelocationInfo.of("net|dv8tion|jda", "dev.austech.betterstaffchat.shaded.jda")); - - builder.dependency("com|squareup|okhttp3", "okhttp", "3.13.0"); - builder.dependency("com|squareup|okio", "okio", "1.17.2"); - builder.dependency("org|apache|commons", "commons-collections4", "4.1"); - builder.dependency("com|fasterxml|jackson|core", "jackson-databind", "2.10.1"); - builder.dependency("com|fasterxml|jackson|core", "jackson-core", "2.10.1"); - builder.dependency("com|fasterxml|jackson|core", "jackson-annotations", "2.10.1"); - - builder.dependency("net|sf|trove4j", "trove4j", "3.0.3"); - builder.dependency("com|neovisionaries", "nv-websocket-client", "2.14"); - - return (MavenDependencyProvider) builder.build(); - } -} diff --git a/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/dependency/DependencyEngine.java b/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/dependency/DependencyEngine.java deleted file mode 100755 index 88290a4..0000000 --- a/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/dependency/DependencyEngine.java +++ /dev/null @@ -1,221 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2021 Justin Heflin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - * This file is apart of PluginBase (https://github.com/Demeng7215/PluginBase) - */ - -package dev.austech.betterstaffchat.common.dependency; - -import dev.austech.betterstaffchat.common.dependency.annotations.LoaderPriority; -import dev.austech.betterstaffchat.common.dependency.maven.DependencyLoader; -import dev.austech.betterstaffchat.common.dependency.maven.MavenDependencyLoader; -import dev.austech.betterstaffchat.common.dependency.maven.builder.DependencyProvider; -import dev.austech.betterstaffchat.common.dependency.relocation.RelocatableDependencyLoader; -import lombok.Getter; -import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NotNull; - -import java.net.URLClassLoader; -import java.nio.file.Path; -import java.util.*; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Executor; -import java.util.concurrent.ForkJoinPool; - -/** - * A dependency engine is used as the starter point for adding dependencies to be downloaded and - * loaded at runtime. - */ -public final class DependencyEngine { - - /** - * The base path for dependencies. - */ - private final Path basePath; - /** - * A map containing all the dependency loaders for this dependency engine. - */ - private final Map, DependencyLoader> dependencyLoaders; - - /** - * The errors that occurred during the loading process. - */ - @Getter private final Set errors; - - /** - * Creates a new dependency engine with the specified base path. - * - * @param basePath The base path for this dependency engine. - */ - public DependencyEngine(final @NotNull Path basePath) { - this.basePath = basePath; - this.dependencyLoaders = new IdentityHashMap<>(); - this.errors = new HashSet<>(); - } - - /** - * Creates a new Dependency Engine with the specified base path. It includes all the default - * DependencyLoader's by default. - * - * @param basePath The base path for all dependencies to be downloaded - * @return A new {@link DependencyEngine} - */ - @Contract("_ -> new") - public static @NotNull DependencyEngine createNew(final @NotNull Path basePath) { - return DependencyEngine.createNew(basePath, true); - } - - /** - * Creates a new Dependency Engine with the specified base path. - * - * @param basePath The base path for all dependencies to be downloaded - * @param addDefaultLoader Whether or not to include the default dependency loaders - * @return A new {@link DependencyEngine} - */ - @Contract("_,_ -> new") - public static @NotNull DependencyEngine createNew( - final @NotNull Path basePath, - final boolean addDefaultLoader - ) { - final DependencyEngine engine = new DependencyEngine(basePath); - - if (addDefaultLoader) { - engine.addDefaultDependencyLoaders(); - } - - return engine; - } - - private void addDefaultDependencyLoaders() { - this.addDependencyLoader(new MavenDependencyLoader(this.basePath)); - } - - /** - * Adds a new dependency loader to the dependency engine. If a duplicate dependency loader is - * added an error is thrown. - * - * @param loader The loader to add to the dependency engine - * @return this - */ - @Contract("_ -> this") - public @NotNull DependencyEngine addDependencyLoader(final @NotNull DependencyLoader loader) { - if (this.dependencyLoaders.containsKey(loader.getClass())) { - throw new IllegalArgumentException(String.format( - "Loader with type '%s' already loaded.", - loader.getClass().getName() - )); - } - - this.dependencyLoaders.put(loader.getClass(), loader); - return this; - } - - /** - * Adds dependency annotations from a class to the currently loaded dependency loaders in this - * engine. These are not added retroactively, you need to add the dependency loaders first. - * - * @param clazz The class to load the annotations from - * @return this - */ - @Contract("_ -> this") - public @NotNull DependencyEngine addDependenciesFromClass(final @NotNull Class clazz) { - this.dependencyLoaders - .values() - .forEach(loader -> loader.loadDependenciesFrom(clazz)); - return this; - } - - /** - * Adds dependencies to the dependency loaders based on the input dependency provider. These are - * not added retroactively, you need to add the dependency loaders first. - * - * @param provider The provider to add the dependencies from - * @return this - */ - @Contract("_ -> this") - @SuppressWarnings("unchecked, rawtypes") - public @NotNull DependencyEngine addDependenciesFromProvider( - final @NotNull DependencyProvider provider) { - this.dependencyLoaders - .values() - .stream() - .filter(loader -> loader.getGenericType() == provider.getGenericType()) - .forEach(loader -> loader.loadDependenciesFrom((DependencyProvider) provider)); - return this; - } - - - /** - * Loads all the dependencies in the specified executor. - * - * @param executor An executor to load the dependencies in - * @return A completable future that completes when all dependencies are loaded - */ - @Contract("_ -> new") - public @NotNull CompletableFuture loadDependencies(final @NotNull Executor executor) { - return CompletableFuture.runAsync( - () -> this.dependencyLoaders - .values() - .stream() - .sorted( - Comparator.comparingInt(loader -> - loader.getClass() - .getAnnotation(LoaderPriority.class) - .value()) - ) - .forEach(loader -> { - if (!this.errors.isEmpty()) { - return; - } - - try { - loader.downloadDependencies(); - - this.errors.addAll(loader.getErrors()); - - if (this.errors.isEmpty() && loader instanceof RelocatableDependencyLoader) { - ((RelocatableDependencyLoader) loader).relocateDependencies(); - this.errors.addAll(loader.getErrors()); - } - - if (this.errors.isEmpty()) { - loader.loadDependencies((URLClassLoader) this.getClass().getClassLoader()); - } - } catch (final Exception ex) { - this.errors.add(ex); - } - }), - executor - ); - } - - /** - * Loads all dependencies in {@link ForkJoinPool#commonPool()}. - * - * @return A completable future that completes when all dependencies are loaded - */ - @Contract("-> new") - public @NotNull CompletableFuture loadDependencies() { - return this.loadDependencies(ForkJoinPool.commonPool()); - } -} \ No newline at end of file diff --git a/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/dependency/annotations/LoaderPriority.java b/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/dependency/annotations/LoaderPriority.java deleted file mode 100755 index 2c7741d..0000000 --- a/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/dependency/annotations/LoaderPriority.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2021 Justin Heflin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - * This file is apart of PluginBase (https://github.com/Demeng7215/PluginBase) - */ - -package dev.austech.betterstaffchat.common.dependency.annotations; - -import java.lang.annotation.*; - -/** - * This is used to define the order dependency loaders should be executed in. - */ -@Documented -@Target(ElementType.TYPE) -@Retention(RetentionPolicy.RUNTIME) -public @interface LoaderPriority { - - /** - * The priority of the loader. Lower values go first. Default value 1000. - * - * @return The loader priority - */ - int value() default 1000; -} \ No newline at end of file diff --git a/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/dependency/annotations/MavenDependency.java b/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/dependency/annotations/MavenDependency.java deleted file mode 100755 index 323a686..0000000 --- a/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/dependency/annotations/MavenDependency.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2021 Justin Heflin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - * This file is apart of PluginBase (https://github.com/Demeng7215/PluginBase) - */ - -package dev.austech.betterstaffchat.common.dependency.annotations; - -import dev.austech.betterstaffchat.common.dependency.maven.DependencyLoader; -import org.jetbrains.annotations.NotNull; - -import java.lang.annotation.*; - -@Documented -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.TYPE) -@Repeatable(MavenDependency.List.class) -public @interface MavenDependency { - - /** - * Used to declare variables in a single line Gradle notation. - * - *

eg: 'com.google.guava:guava:30.1-jre'; if you would like to declare dependencies in a - * structured manner use the other variables. - * - * @return A Gradle style single line dependency string - */ - @NotNull String value() default ""; - - /** - * Used to define a group ID for a Maven dependency. - * - * @return The group ID - */ - @NotNull String groupId() default ""; - - /** - * Used to define an artifact ID for a Maven dependency. - * - * @return The artifact ID - */ - @NotNull String artifactId() default ""; - - /** - * Used to define the version for a Maven dependency. - * - * @return The version - */ - @NotNull String version() default ""; - - /** - * Used to define the package path separator. - * - * @return The separator to use instead of '.' or '/' - */ - @NotNull String separator() default DependencyLoader.DEFAULT_SEPARATOR; - - /** - * Used to store multiple {@link MavenDependency} annotations on a single class type. - */ - @Documented - @Retention(RetentionPolicy.RUNTIME) - @Target(ElementType.TYPE) - @interface List { - - /** - * Used to define an array of {@link MavenDependency} annotations. - * - * @return An array of {@link MavenDependency} annotations - */ - @NotNull MavenDependency[] value() default {}; - } - -} \ No newline at end of file diff --git a/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/dependency/annotations/MavenRepository.java b/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/dependency/annotations/MavenRepository.java deleted file mode 100755 index b5919d3..0000000 --- a/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/dependency/annotations/MavenRepository.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2021 Justin Heflin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - * This file is apart of PluginBase (https://github.com/Demeng7215/PluginBase) - */ - -package dev.austech.betterstaffchat.common.dependency.annotations; - -import dev.austech.betterstaffchat.common.dependency.maven.MavenRepositoryInfo; -import org.jetbrains.annotations.NotNull; - -import java.lang.annotation.*; - -/** - * Used to define a Maven repo URL for the dependencies. - * - * @see MavenRepositoryInfo - */ -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.TYPE) -@Repeatable(MavenRepository.List.class) -public @interface MavenRepository { - - /** - * The URL to the Maven repository. - * - * @return A string that represents the URL to a Maven repo - */ - @NotNull String value() default "https://repo1.maven.org/maven2/"; - - /** - * Used to store multiple {@link MavenRepository} annotations on a single class type. - */ - @Documented - @Retention(RetentionPolicy.RUNTIME) - @Target(ElementType.TYPE) - @interface List { - - /** - * An array of maven repositories. - * - * @return An array of {@link MavenRepository} annotations. - */ - @NotNull MavenRepository @NotNull [] value() default {}; - } -} \ No newline at end of file diff --git a/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/dependency/annotations/Relocation.java b/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/dependency/annotations/Relocation.java deleted file mode 100755 index 17915ba..0000000 --- a/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/dependency/annotations/Relocation.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2021 Justin Heflin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - * This file is apart of PluginBase (https://github.com/Demeng7215/PluginBase) - */ - -package dev.austech.betterstaffchat.common.dependency.annotations; - -import dev.austech.betterstaffchat.common.dependency.maven.DependencyLoader; -import dev.austech.betterstaffchat.common.dependency.relocation.RelocationInfo; -import org.jetbrains.annotations.NotNull; - -import java.lang.annotation.*; - -/** - * Used to define relocations for any imported {@link MavenDependency} dependencies. - * - *

You can't use '.' or '/' for the package names due to maven/Gradle relocation changing those - * at compile time. The separator by default is '|' you can change the separator by changing the - * separator value in this annotation. - * - *

Examples:

- *
    - * ~Relocation(from = "com|google|guava", to = "my|package|guava") - * ~Relocation(from = "com{}google{}guava", to = "my{}package{}guava", separator = "{}" - *
- * - * @see RelocationInfo - */ -@Documented -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.TYPE) -@Repeatable(Relocation.List.class) -public @interface Relocation { - - /** - * The original package location. - * - * @return The original package location - */ - @NotNull String from(); - - /** - * Where to move the package to. - * - * @return Where to move the package to - */ - @NotNull String to(); - - /** - * The separator to use. - * - * @return The separator to use instead of '.' or '/' - */ - @NotNull String separator() default DependencyLoader.DEFAULT_SEPARATOR; - - /** - * Used to store multiple {@link Relocation} annotations on a single class type. - */ - @Documented - @Retention(RetentionPolicy.RUNTIME) - @Target(ElementType.TYPE) - @interface List { - - /** - * An array of {@link Relocation} annotations. - * - * @return An array of {@link Relocation} annotations - */ - @NotNull Relocation[] value() default {}; - } -} diff --git a/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/dependency/exception/DependencyLoadException.java b/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/dependency/exception/DependencyLoadException.java deleted file mode 100755 index cc278ed..0000000 --- a/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/dependency/exception/DependencyLoadException.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2021 Justin Heflin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - * This file is apart of PluginBase (https://github.com/Demeng7215/PluginBase) - */ - -package dev.austech.betterstaffchat.common.dependency.exception; - -import dev.austech.betterstaffchat.common.dependency.maven.Dependency; -import lombok.Getter; -import org.jetbrains.annotations.NotNull; - -/** - * Used when there is an error loading a dependency. - */ -public final class DependencyLoadException extends RuntimeException { - - /** - * The dependency that had an error while loading. - */ - @Getter private final transient @NotNull Dependency dependency; - - /** - * Creates a new dependency load exception. - * - * @param dependency The dependency that had an error while loading - * @param message The message to pass - */ - public DependencyLoadException( - final @NotNull Dependency dependency, - final @NotNull String message - ) { - super(message); - this.dependency = dependency; - } - - /** - * Creates a new dependency load exception. - * - * @param dependency The dependency that had an error while loading - * @param cause The cause of the error - */ - public DependencyLoadException( - final @NotNull Dependency dependency, - final @NotNull Throwable cause - ) { - super(cause); - this.dependency = dependency; - } -} \ No newline at end of file diff --git a/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/dependency/exception/InvalidDependencyException.java b/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/dependency/exception/InvalidDependencyException.java deleted file mode 100755 index f88d141..0000000 --- a/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/dependency/exception/InvalidDependencyException.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2021 Justin Heflin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - * This file is apart of PluginBase (https://github.com/Demeng7215/PluginBase) - */ - -package dev.austech.betterstaffchat.common.dependency.exception; - -import org.jetbrains.annotations.NotNull; - -/** - * When a dependency isn't able to process this error is thrown. - */ -public final class InvalidDependencyException extends RuntimeException { - - /** - * A basic error that indicates there was an error parsing a dependency. - */ - public InvalidDependencyException() { - super(); - } - - /** - * Creates a new invalid dependency exception. - * - * @param message The message to pass - * @param cause The cause of the error - */ - public InvalidDependencyException( - final @NotNull String message, - final @NotNull Throwable cause) { - super(message, cause); - } - - /** - * Creates a new invalid dependency exception. - * - * @param cause The cause of the error - */ - public InvalidDependencyException(final @NotNull Throwable cause) { - super(cause); - } - - /** - * Creates a new invalid dependency exception. - * - * @param message The message to pass - */ - public InvalidDependencyException(final @NotNull String message) { - super(message); - } -} \ No newline at end of file diff --git a/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/dependency/generics/TypeDefinition.java b/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/dependency/generics/TypeDefinition.java deleted file mode 100755 index 3101a5c..0000000 --- a/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/dependency/generics/TypeDefinition.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2021 Justin Heflin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - * This file is apart of PluginBase (https://github.com/Demeng7215/PluginBase) - */ - -package dev.austech.betterstaffchat.common.dependency.generics; - -import org.jetbrains.annotations.NotNull; - -/** - * Used to store the generic type of a class for comparison. - * - * @param The generic type of this definition. - */ -public interface TypeDefinition { - - /** - * Returns the generic type of this class. - * - * @return The generic type - */ - @NotNull Class getGenericType(); -} \ No newline at end of file diff --git a/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/dependency/maven/Dependency.java b/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/dependency/maven/Dependency.java deleted file mode 100755 index 4bbcac9..0000000 --- a/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/dependency/maven/Dependency.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2021 Justin Heflin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - * This file is apart of PluginBase (https://github.com/Demeng7215/PluginBase) - */ - -package dev.austech.betterstaffchat.common.dependency.maven; - -import org.jetbrains.annotations.NotNull; - -/** - * Base dependency abstraction used to define basic information for dependency retrieval. - */ -public interface Dependency { - - /** - * If there is an issue downloading the dependency this is used to get the manual download - * location. - * - * @return The manual download url - */ - @NotNull String getManualDownloadString(); - - /** - * Used to get the download URL of a dependency. - * - * @return The relative download url - */ - @NotNull String getRelativeDownloadString(); - - /** - * Used to get where the dependency should be stored on download. The path is relative. - * - * @return The downloaded location of this dependency - */ - @NotNull String getDownloadedFileName(); - - /** - * Used to get the name of the dependency for logging information. - * - * @return The name of this dependency - */ - @NotNull String getName(); - - /** - * Set where or not this dependency has been loaded. - * - * @param loaded Whether or not this dependency has been loaded - */ - void setLoaded(boolean loaded); - - /** - * Whether or not this dependency has been loaded into the class path. - * - * @return True if it has been loaded already, false otherwise - */ - boolean isLoaded(); -} \ No newline at end of file diff --git a/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/dependency/maven/DependencyLoader.java b/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/dependency/maven/DependencyLoader.java deleted file mode 100755 index f3759c6..0000000 --- a/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/dependency/maven/DependencyLoader.java +++ /dev/null @@ -1,163 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2021 Justin Heflin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - * This file is apart of PluginBase (https://github.com/Demeng7215/PluginBase) - */ - -package dev.austech.betterstaffchat.common.dependency.maven; - -import dev.austech.betterstaffchat.common.dependency.generics.TypeDefinition; -import dev.austech.betterstaffchat.common.dependency.maven.builder.DependencyProvider; -import lombok.Getter; -import org.jetbrains.annotations.NotNull; - -import java.lang.reflect.Method; -import java.net.URLClassLoader; -import java.nio.file.Path; -import java.security.AccessController; -import java.security.PrivilegedAction; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -/** - * Abstract implementation of a dependency loader. - * - * @param Type of dependency implementation. - * @see MavenDependencyLoader - */ -public abstract class DependencyLoader implements TypeDefinition { - - /** - * The default separator used to have compatibility with gradle/Maven relocation. - */ - public static final @NotNull String DEFAULT_SEPARATOR = "|"; - - /** - * The base path for files in this dependency loader. - */ - @Getter private final @NotNull Path basePath; - - /** - * The dependencies that this dependency loader handles. - */ - @Getter private final @NotNull List dependencies; - - /** - * The errors that occurred while loading dependencies. - */ - @Getter private final @NotNull Set errors; - - /** - * Creates a new dependency loader with the specified base path. - * - * @param basePath The base path for dependencies. - */ - protected DependencyLoader(final @NotNull Path basePath) { - this.basePath = basePath; - this.dependencies = new ArrayList<>(); - this.errors = new HashSet<>(); - this.openClassLoaderJava9(); - } - - /** - * Creates a new dependency loader with the specified base path. Storage destination is used as a - * relative sub directory for dependencies. - * - * @param basePath The base path for dependencies - * @param storageDestination The relative path to dependencies in this dependency loader - */ - protected DependencyLoader( - final @NotNull Path basePath, - final @NotNull String storageDestination - ) { - this(basePath.resolve(storageDestination)); - } - - /** - * Gives this module access to the URLClassLoader. - */ - private void openClassLoaderJava9() { - AccessController.doPrivileged((PrivilegedAction) () -> { - try { - final Class moduleClass = Class.forName("java.lang.Module"); - final Method getModuleMethod = Class.class.getMethod("getModule"); - final Method addOpensMethod = moduleClass.getMethod("addOpens", String.class, moduleClass); - - final Object urlClassLoaderModule = getModuleMethod.invoke(URLClassLoader.class); - final Object thisModule = getModuleMethod.invoke(DependencyLoader.class); - - addOpensMethod - .invoke(urlClassLoaderModule, URLClassLoader.class.getPackage().getName(), thisModule); - } catch (final Exception ignored) { - // Will throw error on clazz); - - /** - * Used to load dependencies from a dependency provider. - * - * @param dependencyProvider The dependency provider to load dependencies from - */ - public abstract void loadDependenciesFrom(@NotNull DependencyProvider dependencyProvider); - - /** - * Downloads handled dependencies. - */ - public abstract void downloadDependencies(); - - /** - * Loads the dependencies to be used by the plugin. - * - * @param classLoader The class loader to add the dependencies to - */ - public abstract void loadDependencies(@NotNull URLClassLoader classLoader); -} \ No newline at end of file diff --git a/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/dependency/maven/MavenDependencyInfo.java b/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/dependency/maven/MavenDependencyInfo.java deleted file mode 100755 index 1217a61..0000000 --- a/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/dependency/maven/MavenDependencyInfo.java +++ /dev/null @@ -1,274 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2021 Justin Heflin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - * This file is apart of PluginBase (https://github.com/Demeng7215/PluginBase) - */ - -package dev.austech.betterstaffchat.common.dependency.maven; - -import dev.austech.betterstaffchat.common.dependency.annotations.MavenDependency; -import dev.austech.betterstaffchat.common.dependency.exception.InvalidDependencyException; -import dev.austech.betterstaffchat.common.dependency.relocation.RelocatableDependency; -import lombok.Getter; -import lombok.Setter; -import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Objects; -import java.util.regex.Pattern; - -/** - * This is the backend class for {@link MavenDependency} dependencies. It's responsible for - * providing basic information needed to download and relocate a dependency jar. - * - * @see MavenDependency - */ -public final class MavenDependencyInfo implements RelocatableDependency { - - /** - * This is used to validate a single line dependency. A single line dependency should have a size - * of 3 always. - */ - private static final int DEPENDENCY_SPLIT_SIZE = 3; - - /** - * This is used to make sure a dependency string is valid before we try to do extra processing on - * it. - */ - private static final @NotNull Pattern DEPENDENCY_PATTERN = Pattern.compile("^([\\w.\\-])+$"); - - /** - * The group ID of this Maven dependency. - */ - @Getter private @Nullable String groupId; - - /** - * The artifact ID of this Maven dependency. - */ - @Getter private @Nullable String artifactId; - - /** - * The version of this Maven dependency. - */ - @Getter private @Nullable String version; - - /** - * Whether or not this dependency has been loaded. - */ - @Getter @Setter private boolean loaded; - - private MavenDependencyInfo( - final @NotNull String separator, - final @NotNull String groupId, - final @NotNull String artifactId, - final @NotNull String version - ) { - this.validateAndSetValues(separator, groupId, artifactId, version); - } - - private MavenDependencyInfo(final @NotNull String separator, - final @NotNull String singleLineDependency) { - if (singleLineDependency.isEmpty()) { - throw new InvalidDependencyException(String.format( - "Invalid single line dependency passed. dependency = '%s'.", - singleLineDependency - )); - } - - final List values = new ArrayList<>(MavenDependencyInfo.DEPENDENCY_SPLIT_SIZE); - values.addAll(Arrays.asList(singleLineDependency.split(":"))); - - if (values.size() != MavenDependencyInfo.DEPENDENCY_SPLIT_SIZE - || values.get(0) == null - || values.get(1) == null - || values.get(2) == null) { - throw new InvalidDependencyException(String.format( - "Invalid dependency format '%s'", - singleLineDependency - )); - } - - this.validateAndSetValues(separator, values.get(0), values.get(1), values.get(2)); - } - - private static boolean hasInvalidCharacters(final @NotNull String validate) { - return validate.contains(".") || validate.contains("/"); - } - - private static boolean patternMismatchString(final @NotNull String validate) { - return !DEPENDENCY_PATTERN.matcher(validate).matches(); - } - - /** - * Creates a new {@link MavenDependencyInfo} based off the passed in information. - * - * @param separator The separator to use instead of '.' or '/' - * @param groupId The group ID of the dependency - * @param artifactId The artifact ID of the dependency - * @param version The version of the dependency - * @return A {@link MavenDependencyInfo} representing the passed in arguments - */ - @Contract("_,_,_,_ -> new") - public static @NotNull MavenDependencyInfo of( - final @NotNull String separator, - final @NotNull String groupId, - final @NotNull String artifactId, - final @NotNull String version - ) { - return new MavenDependencyInfo(separator, groupId, artifactId, version); - } - - /** - * Create a new dependency information object. - * - * @param separator The separator to use instead of '.' or '/' - * @param singleLineDependency A Gradle style single line dependency - * @return A {@link MavenDependencyInfo} representing the passed in arguments - */ - @Contract("_,_ -> new") - public static @NotNull MavenDependencyInfo of( - final @NotNull String separator, - final @NotNull String singleLineDependency - ) { - return new MavenDependencyInfo(separator, singleLineDependency); - } - - /** - * Creates a new {@link MavenDependencyInfo} based off of an {@link MavenDependency} annotation. - * - * @param dependency The {@link MavenDependency} annotation - * @return A {@link MavenDependencyInfo} representing the passed in arguments - */ - @Contract("_ -> new") - public static @NotNull MavenDependencyInfo of( - final @NotNull MavenDependency dependency - ) { - return dependency.value().isEmpty() - ? MavenDependencyInfo.of( - dependency.separator(), - dependency.groupId(), - dependency.artifactId(), - dependency.version() - ) - : MavenDependencyInfo.of( - dependency.separator(), - dependency.value() - ); - } - - private void validateAndSetValues( - final @NotNull String separator, - final @NotNull String groupId, - final @NotNull String artifactId, - final @NotNull String version - ) { - if (separator.isEmpty() || MavenDependencyInfo.hasInvalidCharacters(separator)) { - throw new InvalidDependencyException(String.format( - "Separator '%s' cannot contain '.' or '/'", - separator - )); - } - - if (groupId.isEmpty() || MavenDependencyInfo.hasInvalidCharacters(groupId)) { - throw new InvalidDependencyException(String.format( - "Group ID '%s' cannot contain '.' or '/', use a separator instead", - groupId - )); - } - - if (artifactId.isEmpty() || MavenDependencyInfo.hasInvalidCharacters(artifactId)) { - throw new InvalidDependencyException(String.format( - "Artifact ID '%s' cannot contain '.' or '/', use a separator instead", - artifactId - )); - } - - if (version.isEmpty()) { - throw new InvalidDependencyException("Dependency version cannot be empty"); - } - - final String validateGroupId = groupId.replace(separator, "."); - final String validateArtifactId = artifactId.replace(separator, "."); - - if (MavenDependencyInfo.patternMismatchString(validateGroupId)) { - throw new InvalidDependencyException(String.format( - "Group ID '%s' contains invalid characters", - validateGroupId - )); - } - - if (MavenDependencyInfo.patternMismatchString(validateArtifactId)) { - throw new InvalidDependencyException(String.format( - "Artifact ID '%s' contains invalid characters", - validateArtifactId - )); - } - - if (MavenDependencyInfo.patternMismatchString(version)) { - throw new InvalidDependencyException(String.format( - "Version '%s' contains invalid characters", - version - )); - } - - this.groupId = validateGroupId; - this.artifactId = validateArtifactId; - this.version = version; - } - - @Override - public @NotNull String getManualDownloadString() { - return this.getRelativeDownloadString(); - } - - @Override - public @NotNull String getRelativeDownloadString() { - return String.format( - "%s/%s/%s/%s-%s.jar", - Objects.requireNonNull(this.groupId).replace(".", "/"), - Objects.requireNonNull(this.artifactId), - Objects.requireNonNull(this.version), - Objects.requireNonNull(this.artifactId), - Objects.requireNonNull(this.version) - ); - } - - @Override - public @NotNull String getDownloadedFileName() { - return this.getName() + ".jar"; - } - - @Override - public @NotNull String getName() { - return this.artifactId + "-" + this.version; - } - - @Override - public @NotNull String getRelocatedFileName() { - return this.getName() + "-relocated.jar"; - } -} \ No newline at end of file diff --git a/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/dependency/maven/MavenDependencyLoader.java b/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/dependency/maven/MavenDependencyLoader.java deleted file mode 100755 index 3f7a7d0..0000000 --- a/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/dependency/maven/MavenDependencyLoader.java +++ /dev/null @@ -1,302 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2021 Justin Heflin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - * This file is apart of PluginBase (https://github.com/Demeng7215/PluginBase) - */ - -package dev.austech.betterstaffchat.common.dependency.maven; - -import dev.austech.betterstaffchat.common.dependency.annotations.LoaderPriority; -import dev.austech.betterstaffchat.common.dependency.annotations.MavenDependency; -import dev.austech.betterstaffchat.common.dependency.annotations.MavenRepository; -import dev.austech.betterstaffchat.common.dependency.annotations.Relocation; -import dev.austech.betterstaffchat.common.dependency.exception.DependencyLoadException; -import dev.austech.betterstaffchat.common.dependency.exception.InvalidDependencyException; -import dev.austech.betterstaffchat.common.dependency.maven.builder.DependencyProvider; -import dev.austech.betterstaffchat.common.dependency.maven.builder.MavenDependencyProvider; -import dev.austech.betterstaffchat.common.dependency.relocation.RelocatableDependencyLoader; -import dev.austech.betterstaffchat.common.dependency.relocation.RelocationInfo; -import dev.austech.betterstaffchat.common.dependency.relocation.Relocator; -import org.jetbrains.annotations.NotNull; - -import java.io.IOException; -import java.io.InputStream; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.net.HttpURLConnection; -import java.net.MalformedURLException; -import java.net.URL; -import java.net.URLClassLoader; -import java.nio.file.Files; -import java.nio.file.Path; -import java.security.AccessController; -import java.security.PrivilegedAction; -import java.util.*; -import java.util.stream.Collectors; - -/** - * The dependency loader responsible for loading Maven dependencies. - */ -@LoaderPriority -public final class MavenDependencyLoader extends - RelocatableDependencyLoader<@NotNull MavenDependencyInfo> { - - /** - * This is used as the user agent for requesting the direct download jar link. - */ - private static final AbstractMap.SimpleImmutableEntry REQUEST_USER_AGENT = - new AbstractMap.SimpleImmutableEntry<>("User-Agent", - "Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.1.2) Gecko/20090729 " - + "Firefox/3.5.2" + " (.NET CLR 3.5.30729)" - ); - - /** - * The repos attached to these dependencies. - */ - private final @NotNull Set repos; - /** - * Any relocations that should be ran on the incoming dependencies. - */ - private final @NotNull Set relocations; - - /** - * Creates a new Maven dependency loader with the specified base path. - * - * @param basePath The base path used to resolve relative file names. - */ - public MavenDependencyLoader(final @NotNull Path basePath) { - this(basePath, "maven"); - } - - /** - * Creates a new Maven dependency loader with the specified base path and destination directory - * name. - * - * @param basePath The base path used to resolve relative file names. - * @param storageDestination relative location for Maven dependencies to be stored. - */ - public MavenDependencyLoader( - final @NotNull Path basePath, - final @NotNull String storageDestination - ) { - super(basePath, storageDestination); - this.repos = new HashSet<>(); - this.repos.add(MavenRepositoryInfo.of("https://repo1.maven.org/maven2/")); - this.relocations = new HashSet<>(); - } - - @Override - public void loadDependenciesFrom(final @NotNull Class<@NotNull ?> clazz) { - if (clazz.isAnnotationPresent(MavenRepository.class)) { - this.repos.add(MavenRepositoryInfo.of(clazz.getAnnotation(MavenRepository.class))); - } - - if (clazz.isAnnotationPresent(MavenRepository.List.class)) { - Arrays.stream(clazz.getAnnotation(MavenRepository.List.class).value()) - .map(MavenRepositoryInfo::of) - .forEach(this.repos::add); - } - - if (clazz.isAnnotationPresent(Relocation.class)) { - this.relocations.add(RelocationInfo.of(clazz.getAnnotation(Relocation.class))); - } - - if (clazz.isAnnotationPresent(Relocation.List.class)) { - this.relocations.addAll( - Arrays.stream(clazz.getAnnotation(Relocation.List.class).value()) - .map(RelocationInfo::of) - .collect(Collectors.toList()) - ); - } - - if (clazz.isAnnotationPresent(MavenDependency.class)) { - super.addDependency(MavenDependencyInfo.of(clazz.getAnnotation(MavenDependency.class))); - } - - if (clazz.isAnnotationPresent(MavenDependency.List.class)) { - Arrays.stream(clazz.getAnnotation(MavenDependency.List.class).value()) - .map(MavenDependencyInfo::of) - .forEach(super::addDependency); - } - } - - @Override - public void loadDependenciesFrom( - final @NotNull DependencyProvider<@NotNull MavenDependencyInfo> dependencyProvider - ) { - final MavenDependencyProvider provider = (MavenDependencyProvider) dependencyProvider; - - this.repos.addAll(provider.getRepositories()); - this.relocations.addAll(provider.getRelocations()); - provider.getDependencies().forEach(super::addDependency); - } - - private @NotNull Optional> findRepoForDependency( - final @NotNull MavenDependencyInfo dependency - ) throws IOException { - Optional> downloadUrl = Optional.empty(); - for (final MavenRepositoryInfo repo : this.repos) { - final URL tempUrl = new URL((repo.getUrl().endsWith("/") - ? repo.getUrl() - : repo.getUrl() + "/") + dependency.getRelativeDownloadString()); - - final HttpURLConnection connection = (HttpURLConnection) tempUrl.openConnection(); - connection.setInstanceFollowRedirects(false); - connection.setRequestProperty( - MavenDependencyLoader.REQUEST_USER_AGENT.getKey(), - MavenDependencyLoader.REQUEST_USER_AGENT.getValue() - ); - connection.setRequestMethod("HEAD"); - - if (connection.getResponseCode() == HttpURLConnection.HTTP_OK - || connection.getResponseCode() == HttpURLConnection.HTTP_ACCEPTED) { - downloadUrl = Optional.of(new AbstractMap.SimpleImmutableEntry<>(repo.getUrl(), tempUrl)); - break; - } - } - - return downloadUrl; - } - - @Override - public void downloadDependencies() { - super.getDependencies() - .parallelStream() - .forEach(dependency -> { - if (dependency.isLoaded()) { - return; - } - - final Path downloadLocation = super.getBasePath() - .resolve(dependency.getDownloadedFileName()); - - if (!Files.exists(downloadLocation) - && !Files.exists(super.getBasePath().resolve(dependency.getRelocatedFileName()))) { - - try { - final Optional> downloadUrl = - this.findRepoForDependency(dependency); - - if (!downloadUrl.isPresent()) { - throw new InvalidDependencyException(String.format( - "Couldn't download dependency: '%s'.", - dependency.getName() - )); - } - - try (final InputStream is = downloadUrl.get().getValue().openStream()) { - Files.createDirectories(downloadLocation.getParent()); - Files.deleteIfExists(downloadLocation); - Files.copy(is, downloadLocation); - } - } catch (final IOException ex) { - super.addError(new DependencyLoadException(dependency, ex)); - } - } - }); - } - - private void relocateDependency( - final @NotNull Relocator relocator, - final @NotNull MavenDependencyInfo dependency - ) { - try { - final Path relocatedLocation = super.getBasePath().resolve(dependency.getRelocatedFileName()); - - if (!Files.exists(relocatedLocation)) { - relocator.relocate(this.relocations, dependency); - - Files.delete(super.getBasePath().resolve(dependency.getDownloadedFileName())); - } - } catch (final DependencyLoadException ex) { - super.addError(ex); - } catch (final Exception ex) { - super.addError(new DependencyLoadException(dependency, ex)); - } - } - - @Override - public void relocateDependencies() { - try { - - final Relocator relocator = new Relocator(super.getBasePath()); - - - super.getDependencies() - .parallelStream() - .forEach( dependency -> { - if (dependency.isLoaded()) { - return; - } - this.relocateDependency(relocator, dependency); - }); - } catch (final DependencyLoadException ex) { - super.addError(ex); - } - } - - @Override - public void loadDependencies(final @NotNull URLClassLoader classLoader) { - try { - final Method method = URLClassLoader.class.getDeclaredMethod("addURL", URL.class); - AccessController.doPrivileged((PrivilegedAction) () -> { - method.setAccessible(true); - return null; - }); - - super.getDependencies() - .parallelStream() - .forEach(dependency -> { - if (dependency.isLoaded()) { - return; - } - - try { - final Path downloadLocation = super.getBasePath() - .resolve(dependency.getDownloadedFileName()); - final Path relocatedLocation = super.getBasePath() - .resolve(dependency.getRelocatedFileName()); - - if (Files.exists(relocatedLocation)) { - method.invoke(classLoader, relocatedLocation.toUri().toURL()); - } else { - method.invoke(classLoader, downloadLocation.toUri().toURL()); - } - - dependency.setLoaded(true); - } catch (final IllegalAccessException - | InvocationTargetException - | MalformedURLException ex) { - super.addError(new DependencyLoadException(dependency, ex)); - } - }); - } catch (final Exception ex) { - super.addError(ex); - } - } - - @Override - public @NotNull Class<@NotNull MavenDependencyInfo> getGenericType() { - return MavenDependencyInfo.class; - } -} \ No newline at end of file diff --git a/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/dependency/maven/MavenRepositoryInfo.java b/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/dependency/maven/MavenRepositoryInfo.java deleted file mode 100755 index 824def7..0000000 --- a/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/dependency/maven/MavenRepositoryInfo.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2021 Justin Heflin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - * This file is apart of PluginBase (https://github.com/Demeng7215/PluginBase) - */ - -package dev.austech.betterstaffchat.common.dependency.maven; - -import dev.austech.betterstaffchat.common.dependency.annotations.MavenRepository; -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.RequiredArgsConstructor; -import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NotNull; - -import java.net.URL; - -/** - * Implementation representing {@link MavenRepository}. - * - * @see MavenRepository - */ -@RequiredArgsConstructor -@EqualsAndHashCode -public final class MavenRepositoryInfo { - - /** - * The URL for this Maven repository. - */ - @Getter private final @NotNull String url; - - /** - * Creates a new Maven repository from a {@link URL}. - * - * @param url The url - * @return A new {@link MavenRepositoryInfo}. - */ - @Contract("_ -> new") - public static @NotNull MavenRepositoryInfo of(final @NotNull URL url) { - return new MavenRepositoryInfo(String.valueOf(url)); - } - - /** - * Creates a new Maven repository from a {@link MavenRepository} annotation. - * - * @param repository The annotation - * @return A new {@link MavenRepositoryInfo}. - */ - @Contract("_ -> new") - public static @NotNull MavenRepositoryInfo of(final @NotNull MavenRepository repository) { - return new MavenRepositoryInfo(repository.value()); - } - - /** - * Creates a new Maven repository from a string representing a url. - * - * @param url the url - * @return A new {@link MavenRepositoryInfo}. - */ - @Contract("_ -> new") - public static @NotNull MavenRepositoryInfo of(final @NotNull String url) { - return new MavenRepositoryInfo(url); - } -} \ No newline at end of file diff --git a/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/dependency/maven/builder/DependencyBuilder.java b/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/dependency/maven/builder/DependencyBuilder.java deleted file mode 100755 index 2cee6ea..0000000 --- a/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/dependency/maven/builder/DependencyBuilder.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2021 Justin Heflin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - * This file is apart of PluginBase (https://github.com/Demeng7215/PluginBase) - */ - -package dev.austech.betterstaffchat.common.dependency.maven.builder; - -import dev.austech.betterstaffchat.common.dependency.maven.Dependency; -import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NotNull; - -/** - * Base level implementation of DependencyBuilder. - * - * @param The dependency type - */ -public interface DependencyBuilder { - - /** - * Add a dependency to the dependency builder. - * - * @param dependency The dependency to add - * @return this - */ - @Contract("_ -> this") - @NotNull DependencyBuilder dependency(@NotNull T dependency); - - /** - * Creates a new {@link DependencyProvider} from the builder. - * - * @return A new {@link DependencyProvider} - */ - @Contract("-> new") - @NotNull DependencyProvider build(); -} \ No newline at end of file diff --git a/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/dependency/maven/builder/DependencyProvider.java b/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/dependency/maven/builder/DependencyProvider.java deleted file mode 100755 index d5a5e11..0000000 --- a/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/dependency/maven/builder/DependencyProvider.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2021 Justin Heflin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - * This file is apart of PluginBase (https://github.com/Demeng7215/PluginBase) - */ - -package dev.austech.betterstaffchat.common.dependency.maven.builder; - -import dev.austech.betterstaffchat.common.dependency.generics.TypeDefinition; -import dev.austech.betterstaffchat.common.dependency.maven.Dependency; -import org.jetbrains.annotations.NotNull; - -import java.util.Set; - -/** - * Base level dependency provider. - * - * @param The dependency this class provides - */ -public interface DependencyProvider extends TypeDefinition { - - /** - * Gets the set of dependencies attached to this provider. - * - * @return The set of dependencies attached to this provider - */ - @NotNull Set getDependencies(); -} \ No newline at end of file diff --git a/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/dependency/maven/builder/MavenDependencyProvider.java b/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/dependency/maven/builder/MavenDependencyProvider.java deleted file mode 100755 index d3cf5bc..0000000 --- a/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/dependency/maven/builder/MavenDependencyProvider.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2021 Justin Heflin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - * This file is apart of PluginBase (https://github.com/Demeng7215/PluginBase) - */ - -package dev.austech.betterstaffchat.common.dependency.maven.builder; - -import dev.austech.betterstaffchat.common.dependency.maven.MavenDependencyInfo; -import dev.austech.betterstaffchat.common.dependency.maven.MavenDependencyLoader; -import dev.austech.betterstaffchat.common.dependency.maven.MavenRepositoryInfo; -import dev.austech.betterstaffchat.common.dependency.relocation.RelocationInfo; -import lombok.AccessLevel; -import lombok.Getter; -import lombok.RequiredArgsConstructor; -import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NotNull; - -import java.util.Set; - -/** - * This class is used to attach dependencies to a {@link MavenDependencyLoader}. - */ -@RequiredArgsConstructor(access = AccessLevel.PACKAGE) -public final class MavenDependencyProvider implements DependencyProvider { - - /** - * The repositories in this provider. - */ - @Getter private final @NotNull Set repositories; - - /** - * The dependencies in this provider. - */ - @Getter private final @NotNull Set dependencies; - - /** - * The relocations in this provider. - */ - @Getter private final @NotNull Set relocations; - - /** - * Creates a new {@link MavenDependencyProviderBuilder} instance. - * - * @return new {@link MavenDependencyProviderBuilder} - */ - @Contract("-> new") - public static MavenDependencyProviderBuilder builder() { - return new MavenDependencyProviderBuilder(); - } - - @Override - public @NotNull Class getGenericType() { - return MavenDependencyInfo.class; - } -} \ No newline at end of file diff --git a/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/dependency/maven/builder/MavenDependencyProviderBuilder.java b/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/dependency/maven/builder/MavenDependencyProviderBuilder.java deleted file mode 100755 index 4b0f512..0000000 --- a/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/dependency/maven/builder/MavenDependencyProviderBuilder.java +++ /dev/null @@ -1,234 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2021 Justin Heflin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - * This file is apart of PluginBase (https://github.com/Demeng7215/PluginBase) - */ - -package dev.austech.betterstaffchat.common.dependency.maven.builder; - -import dev.austech.betterstaffchat.common.dependency.maven.DependencyLoader; -import dev.austech.betterstaffchat.common.dependency.maven.MavenDependencyInfo; -import dev.austech.betterstaffchat.common.dependency.maven.MavenDependencyLoader; -import dev.austech.betterstaffchat.common.dependency.maven.MavenRepositoryInfo; -import dev.austech.betterstaffchat.common.dependency.relocation.RelocationInfo; -import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NotNull; - -import java.net.URL; -import java.util.HashSet; -import java.util.Set; - -/** - * Used to build a dependency provider that should be attached to a {@link MavenDependencyLoader} - * then loaded. - */ -public final class MavenDependencyProviderBuilder implements - DependencyBuilder { - - /** - * The set of repositories to add to the provider. - */ - private final @NotNull Set repositories; - - /** - * The set of Maven dependencies to add to the provider. - */ - private final @NotNull Set dependencies; - - /** - * The set of relocations to add to the provider. - */ - private final @NotNull Set relocations; - - /** - * Creates a new dependency builder. - */ - public MavenDependencyProviderBuilder() { - this.repositories = new HashSet<>(); - this.dependencies = new HashSet<>(); - this.relocations = new HashSet<>(); - } - - @Contract("_ -> this") - @Override - public @NotNull MavenDependencyProviderBuilder dependency( - final @NotNull MavenDependencyInfo dependency - ) { - this.dependencies.add(dependency); - return this; - } - - /** - * Creates a new Maven dependency provider builder. - * - * @param groupId The group ID of the Maven dependency - * @param artifactId The artifact ID of the Maven dependency - * @param version The version of the Maven dependency - * @return this - */ - @Contract("_,_,_ -> this") - public @NotNull MavenDependencyProviderBuilder dependency( - final @NotNull String groupId, - final @NotNull String artifactId, - final @NotNull String version - ) { - return this.dependency(MavenDependencyInfo.of( - DependencyLoader.DEFAULT_SEPARATOR, - groupId, - artifactId, - version - )); - } - - /** - * Creates a new Maven dependency provider builder. - * - * @param separator The separator used for package names - * @param groupId The group ID of the Maven dependency - * @param artifactId The artifact ID of the Maven dependency - * @param version The version of the Maven dependency - * @return this - */ - @Contract("_,_,_,_ -> this") - public @NotNull MavenDependencyProviderBuilder dependency( - final @NotNull String separator, - final @NotNull String groupId, - final @NotNull String artifactId, - final @NotNull String version - ) { - return this.dependency(MavenDependencyInfo.of(separator, groupId, artifactId, version)); - } - - /** - * Creates a new Maven dependency provider builder. - * - * @param separator The separator used for package separation - * @param singleLineDependency The Gradle style single line dependency string - * @return this - */ - @Contract("_,_ -> this") - public @NotNull MavenDependencyProviderBuilder dependency( - final @NotNull String separator, - final @NotNull String singleLineDependency - ) { - return this.dependency(MavenDependencyInfo.of(separator, singleLineDependency)); - } - - /** - * Creates a new Maven dependency provider builder. - * - * @param singleLineDependency The Gradle style single line dependency string - * @return this - */ - @Contract("_ -> this") - public @NotNull MavenDependencyProviderBuilder dependency( - final @NotNull String singleLineDependency - ) { - return this.dependency( - MavenDependencyInfo.of(DependencyLoader.DEFAULT_SEPARATOR, singleLineDependency)); - } - - /** - * Creates a new Maven dependency provider builder. - * - * @param repository The repository - * @return The same instance - * @see MavenRepositoryInfo - */ - @Contract("_ -> this") - public MavenDependencyProviderBuilder repository(final @NotNull MavenRepositoryInfo repository) { - this.repositories.add(repository); - return this; - } - - /** - * Sets the repository URL. - * - * @param url String URL representation of a Maven repository - * @return The same instance - */ - @Contract("_ -> this") - public MavenDependencyProviderBuilder repository(final @NotNull String url) { - return this.repository(MavenRepositoryInfo.of(url)); - } - - /** - * Sets the repository URL. - * - * @param url URL representation of a Maven repository - * @return The same instance - */ - @Contract("_ -> this") - public MavenDependencyProviderBuilder repository(final @NotNull URL url) { - return this.repository(MavenRepositoryInfo.of(url)); - } - - /** - * Sets the relocation. - * - * @param relocation The relocation - * @return The same instance - * @see RelocationInfo - */ - @Contract("_ -> this") - public MavenDependencyProviderBuilder relocation(final @NotNull RelocationInfo relocation) { - this.relocations.add(relocation); - return this; - } - - /** - * Sets the relocation. - * - * @param from Original package destination name - * @param to Target package destination name - * @param separator The separator to use instead of '.' or '/' - * @return The same instance - */ - @Contract("_,_,_ -> this") - public MavenDependencyProviderBuilder relocation( - final @NotNull String from, - final @NotNull String to, - final @NotNull String separator - ) { - return this.relocation(RelocationInfo.of(from, to, separator)); - } - - /** - * Sets the relocation. - * - * @param from The package to relocate from - * @param to The package to relocate to - * @return this - */ - @Contract("_,_ -> this") - public MavenDependencyProviderBuilder relocation(final @NotNull String from, - final @NotNull String to) { - return this.relocation(RelocationInfo.of(from, to, DependencyLoader.DEFAULT_SEPARATOR)); - } - - @Contract("-> new") - @Override - public @NotNull DependencyProvider build() { - return new MavenDependencyProvider(this.repositories, this.dependencies, this.relocations); - } -} \ No newline at end of file diff --git a/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/dependency/relocation/IsolatedClassLoader.java b/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/dependency/relocation/IsolatedClassLoader.java deleted file mode 100755 index f855f1f..0000000 --- a/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/dependency/relocation/IsolatedClassLoader.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2021 Justin Heflin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - * This file is apart of PluginBase (https://github.com/Demeng7215/PluginBase) - */ -package dev.austech.betterstaffchat.common.dependency.relocation; - -import org.jetbrains.annotations.NotNull; - -import java.net.MalformedURLException; -import java.net.URL; -import java.net.URLClassLoader; -import java.nio.file.Path; -import java.util.Objects; - -/** - * This class handles loading some dependencies that are needed for loading other dependencies, but - * not need afterwards. - */ -public final class IsolatedClassLoader extends URLClassLoader { - - static { - ClassLoader.registerAsParallelCapable(); - } - - /** - * Instantiates a new Isolated class loader. - * - * @param urls The URLs - */ - public IsolatedClassLoader(final @NotNull URL... urls) { - super(Objects.requireNonNull(urls), ClassLoader.getSystemClassLoader().getParent()); - } - - @Override - public void addURL(final @NotNull URL url) { - super.addURL(url); - } - - /** - * Add path boolean. - * - * @param path The path - * @return The boolean - */ - public boolean addPath(final @NotNull Path path) { - try { - this.addURL(path.toUri().toURL()); - return true; - } catch (final MalformedURLException ex) { - return false; - } - } -} \ No newline at end of file diff --git a/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/dependency/relocation/RelocatableDependency.java b/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/dependency/relocation/RelocatableDependency.java deleted file mode 100755 index 5fab9b8..0000000 --- a/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/dependency/relocation/RelocatableDependency.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2021 Justin Heflin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - * This file is apart of PluginBase (https://github.com/Demeng7215/PluginBase) - */ - -package dev.austech.betterstaffchat.common.dependency.relocation; - -import dev.austech.betterstaffchat.common.dependency.maven.Dependency; -import dev.austech.betterstaffchat.common.dependency.maven.MavenDependencyInfo; -import org.jetbrains.annotations.NotNull; - -/** - * Used to define dependencies that need to be relocated at runtime. - * - * @see MavenDependencyInfo - */ -public interface RelocatableDependency extends Dependency { - - /** - * Used to get the relocated location of a dependency. This path is relative. - * - * @return The relocation jar location of this dependency - */ - @NotNull String getRelocatedFileName(); -} \ No newline at end of file diff --git a/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/dependency/relocation/RelocatableDependencyLoader.java b/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/dependency/relocation/RelocatableDependencyLoader.java deleted file mode 100755 index ed31c40..0000000 --- a/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/dependency/relocation/RelocatableDependencyLoader.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2021 Justin Heflin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - * This file is apart of PluginBase (https://github.com/Demeng7215/PluginBase) - */ - -package dev.austech.betterstaffchat.common.dependency.relocation; - -import dev.austech.betterstaffchat.common.dependency.maven.DependencyLoader; -import org.jetbrains.annotations.NotNull; - -import java.nio.file.Path; - -/** - * A {@link DependencyLoader} that supports relocations. - * - * @param A Relocatable Dependency to handle. - */ -public abstract class RelocatableDependencyLoader extends - DependencyLoader { - - /** - * Creates a new dependency loader that supports relocations with the specified base path. - * - * @param basePath The base path for dependencies in this dependency loader - */ - protected RelocatableDependencyLoader(final @NotNull Path basePath) { - super(basePath); - } - - /** - * Creates a new dependency loader that supports relocations with the specified base path. The - * storage destination is a relative sub directory for dependencies specific to this loader. - * - * @param basePath The base path for this dependency loader - * @param storageDestination The relative sub directory for dependencies - */ - protected RelocatableDependencyLoader( - final @NotNull Path basePath, - final @NotNull String storageDestination - ) { - super(basePath, storageDestination); - } - - /** - * Relocates dependencies based on provided relocations. - */ - public abstract void relocateDependencies(); -} \ No newline at end of file diff --git a/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/dependency/relocation/RelocationInfo.java b/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/dependency/relocation/RelocationInfo.java deleted file mode 100755 index 29891ec..0000000 --- a/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/dependency/relocation/RelocationInfo.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2021 Justin Heflin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - * This file is apart of PluginBase (https://github.com/Demeng7215/PluginBase) - */ - -package dev.austech.betterstaffchat.common.dependency.relocation; - -import dev.austech.betterstaffchat.common.dependency.annotations.Relocation; -import dev.austech.betterstaffchat.common.dependency.maven.DependencyLoader; -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.RequiredArgsConstructor; -import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NotNull; - -/** - * Implementation representing {@link Relocation}. - * - * @see Relocation - */ -@RequiredArgsConstructor -@EqualsAndHashCode -public final class RelocationInfo { - - /** - * The package to search for. - */ - @Getter private final @NotNull String from; - - /** - * The package to place. - */ - @Getter private final @NotNull String to; - - /** - * The separate used to indicate package names. - */ - @Getter private final @NotNull String separator; - - /** - * Creates a new relocation based off of the passed in information. - * - * @param from The package to find - * @param to The package to replace with - * @param separator The separator used to indicate package names - * @return A new {@link RelocationInfo} instance - */ - @Contract("_,_,_ -> new") - public static @NotNull RelocationInfo of( - final @NotNull String from, - final @NotNull String to, - final @NotNull String separator - ) { - return new RelocationInfo(from, to, separator); - } - - /** - * Creates a new relocation based off of the passed in information. Uses the {@link - * DependencyLoader#DEFAULT_SEPARATOR} as the separator. - * - * @param from The package to find - * @param to The package to replace with - * @return A new {@link RelocationInfo} instance - */ - @Contract("_,_-> new") - public static @NotNull RelocationInfo of( - final @NotNull String from, - final @NotNull String to - ) { - return new RelocationInfo(from, to, DependencyLoader.DEFAULT_SEPARATOR); - } - - /** - * Creates a new relocation from the passed in {@link Relocation} annotation. - * - * @param relocation The {@link Relocation} annotation - * @return A new {@link RelocationInfo} instance - */ - @Contract("_ -> new") - public static @NotNull RelocationInfo of(final @NotNull Relocation relocation) { - return new RelocationInfo(relocation.from(), relocation.to(), relocation.separator()); - } -} diff --git a/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/dependency/relocation/Relocator.java b/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/dependency/relocation/Relocator.java deleted file mode 100755 index 2e143ed..0000000 --- a/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/dependency/relocation/Relocator.java +++ /dev/null @@ -1,178 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2021 Justin Heflin - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - * This file is apart of PluginBase (https://github.com/Demeng7215/PluginBase) - */ - -package dev.austech.betterstaffchat.common.dependency.relocation; - -import dev.austech.betterstaffchat.common.dependency.exception.DependencyLoadException; -import dev.austech.betterstaffchat.common.dependency.maven.DependencyLoader; -import dev.austech.betterstaffchat.common.dependency.maven.MavenDependencyInfo; -import dev.austech.betterstaffchat.common.dependency.maven.MavenDependencyLoader; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.io.File; -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.nio.file.Path; -import java.security.AccessController; -import java.security.PrivilegedAction; -import java.util.ArrayList; -import java.util.Collection; -import java.util.LinkedHashSet; -import java.util.Set; - -/** - * This class is used for relocating the packages inside of a downloaded jar. - */ -public final class Relocator { - - /** - * The ASM Dependency information. - */ - private static final MavenDependencyInfo ASM; - /** - * The ASM commons dependency information. - */ - private static final MavenDependencyInfo ASM_COMMONS; - /** - * The jar relocator dependency information. - */ - private static final MavenDependencyInfo JAR_RELOCATOR; - - static { - ASM = MavenDependencyInfo.of("|", "org|ow2|asm:asm:7.0"); - ASM_COMMONS = MavenDependencyInfo.of("|", "org|ow2|asm:asm-commons:7.0"); - JAR_RELOCATOR = MavenDependencyInfo.of("|", "me|lucko:jar-relocator:1.4"); - } - - /** - * The base directory for the relocator dependencies. - */ - private final @NotNull Path basePath; - /** - * The constructor for the jar relocator class. - */ - private final @NotNull Constructor jarRelocatorConstructor; - /** - * The method to run the jar relocator. - */ - private final @NotNull Method jarRelocatorRunMethod; - /** - * The relocation constructor. - */ - private final @NotNull Constructor relocationConstructor; - /** - * The isolated class loader instance. This is used to separate the classes used at only for - * relocating from everywhere else. - */ - private @Nullable IsolatedClassLoader isolatedClassLoader; - - - /** - * Creates a new relocator instance. - * - * @param basePath The base path for relocations - */ - public Relocator(final @NotNull Path basePath) { - this.basePath = basePath; - AccessController.doPrivileged( - (PrivilegedAction) () -> this.isolatedClassLoader = new IsolatedClassLoader()); - - final DependencyLoader dependencyHandler = - new MavenDependencyLoader(this.basePath.resolve("relocator")); - dependencyHandler.addDependency(ASM); - dependencyHandler.addDependency(ASM_COMMONS); - dependencyHandler.addDependency(JAR_RELOCATOR); - - dependencyHandler.downloadDependencies(); - dependencyHandler.loadDependencies(this.isolatedClassLoader); - - - if (!dependencyHandler.getErrors().isEmpty()) { - final Exception e = dependencyHandler.getErrors().stream().iterator().next(); - - throw e instanceof DependencyLoadException - ? (DependencyLoadException) e - : new DependencyLoadException(ASM, e); - } - - try { - final Class jarRelocatorClass = this.isolatedClassLoader - .loadClass("me.lucko.jarrelocator.JarRelocator"); - final Class relocationClass = this.isolatedClassLoader - .loadClass("me.lucko.jarrelocator.Relocation"); - - this.jarRelocatorConstructor = jarRelocatorClass.getConstructor( - File.class, - File.class, - Collection.class - ); - this.jarRelocatorRunMethod = jarRelocatorClass.getMethod("run"); - - this.relocationConstructor = relocationClass.getConstructor( - String.class, - String.class, - Collection.class, - Collection.class - ); - } catch (final Exception ex) { - throw new DependencyLoadException(ASM, ex); - } - } - - /** - * Relocates the packages in a jar file based on the specified relocations. - * - * @param relocations A collection of {@link RelocationInfo} used for relocation information - * @param dependency The dependency to relocate - * @throws IllegalAccessException If the relocator was denied access to any of the methods - * @throws InvocationTargetException If there was an error while relocating the jar files - * @throws InstantiationException If there was an error while creating a new relocation - * instance - */ - public void relocate( - final @NotNull Collection relocations, - final @NotNull RelocatableDependency dependency - ) throws IllegalAccessException, InvocationTargetException, InstantiationException { - final Set rules = new LinkedHashSet<>(); - - for (final RelocationInfo relocation : relocations) { - rules.add(this.relocationConstructor.newInstance( - relocation.getFrom().replace(relocation.getSeparator(), "."), - relocation.getTo().replace(relocation.getSeparator(), "."), - new ArrayList<>(), - new ArrayList<>() - )); - } - - this.jarRelocatorRunMethod.invoke(this.jarRelocatorConstructor.newInstance( - this.basePath.resolve(dependency.getDownloadedFileName()).toFile(), - this.basePath.resolve(dependency.getRelocatedFileName()).toFile(), - rules - )); - } -} \ No newline at end of file diff --git a/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/discord/DiscordWebhook.java b/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/discord/DiscordWebhook.java deleted file mode 100644 index 26a2bdc..0000000 --- a/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/discord/DiscordWebhook.java +++ /dev/null @@ -1,406 +0,0 @@ -/* - * BetterStaffChat - DiscordWebhook.java - * Copyright (C) 2021 AusTech Development Team - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package dev.austech.betterstaffchat.common.discord; - -import javax.net.ssl.HttpsURLConnection; -import java.awt.*; -import java.io.IOException; -import java.io.OutputStream; -import java.lang.reflect.Array; -import java.net.URL; -import java.util.List; -import java.util.*; - -/** - * Class used to execute Discord Webhooks with low effort - */ -public class DiscordWebhook { - - private final String url; - private String content; - private String username; - private String avatarUrl; - private boolean tts; - private List embeds = new ArrayList<>(); - - /** - * Constructs a new DiscordWebhook instance - * - * @param url The webhook URL obtained in Discord - */ - public DiscordWebhook(String url) { - this.url = url; - } - - public void setContent(String content) { - this.content = content; - } - - public void setUsername(String username) { - this.username = username; - } - - public void setAvatarUrl(String avatarUrl) { - this.avatarUrl = avatarUrl; - } - - public void setTts(boolean tts) { - this.tts = tts; - } - - public void addEmbed(EmbedObject embed) { - this.embeds.add(embed); - } - - public void execute() throws IOException { - if (this.content == null && this.embeds.isEmpty()) { - throw new IllegalArgumentException("Set content or add at least one EmbedObject"); - } - - JSONObject json = new JSONObject(); - - json.put("content", this.content); - json.put("username", this.username); - json.put("avatar_url", this.avatarUrl); - json.put("tts", this.tts); - - if (!this.embeds.isEmpty()) { - List embedObjects = new ArrayList<>(); - - for (EmbedObject embed : this.embeds) { - JSONObject jsonEmbed = new JSONObject(); - - jsonEmbed.put("title", embed.getTitle()); - jsonEmbed.put("description", embed.getDescription()); - jsonEmbed.put("url", embed.getUrl()); - - if (embed.getColor() != null) { - Color color = embed.getColor(); - int rgb = color.getRed(); - rgb = (rgb << 8) + color.getGreen(); - rgb = (rgb << 8) + color.getBlue(); - - jsonEmbed.put("color", rgb); - } - - EmbedObject.Footer footer = embed.getFooter(); - EmbedObject.Image image = embed.getImage(); - EmbedObject.Thumbnail thumbnail = embed.getThumbnail(); - EmbedObject.Author author = embed.getAuthor(); - List fields = embed.getFields(); - - if (footer != null) { - JSONObject jsonFooter = new JSONObject(); - - jsonFooter.put("text", footer.getText()); - jsonFooter.put("icon_url", footer.getIconUrl()); - jsonEmbed.put("footer", jsonFooter); - } - - if (image != null) { - JSONObject jsonImage = new JSONObject(); - - jsonImage.put("url", image.getUrl()); - jsonEmbed.put("image", jsonImage); - } - - if (thumbnail != null) { - JSONObject jsonThumbnail = new JSONObject(); - - jsonThumbnail.put("url", thumbnail.getUrl()); - jsonEmbed.put("thumbnail", jsonThumbnail); - } - - if (author != null) { - JSONObject jsonAuthor = new JSONObject(); - - jsonAuthor.put("name", author.getName()); - jsonAuthor.put("url", author.getUrl()); - jsonAuthor.put("icon_url", author.getIconUrl()); - jsonEmbed.put("author", jsonAuthor); - } - - List jsonFields = new ArrayList<>(); - for (EmbedObject.Field field : fields) { - JSONObject jsonField = new JSONObject(); - - jsonField.put("name", field.getName()); - jsonField.put("value", field.getValue()); - jsonField.put("inline", field.isInline()); - - jsonFields.add(jsonField); - } - - jsonEmbed.put("fields", jsonFields.toArray()); - embedObjects.add(jsonEmbed); - } - - json.put("embeds", embedObjects.toArray()); - } - - URL url = new URL(this.url); - HttpsURLConnection connection = (HttpsURLConnection) url.openConnection(); - connection.addRequestProperty("Content-Type", "application/json"); - connection.addRequestProperty("User-Agent", "Java-DiscordWebhook-BY-Gelox_"); - connection.setDoOutput(true); - connection.setRequestMethod("POST"); - - OutputStream stream = connection.getOutputStream(); - stream.write(json.toString().getBytes()); - stream.flush(); - stream.close(); - - connection.getInputStream().close(); //I'm not sure why but it doesn't work without getting the InputStream - connection.disconnect(); - } - - public static class EmbedObject { - private String title; - private String description; - private String url; - private Color color; - - private Footer footer; - private Thumbnail thumbnail; - private Image image; - private Author author; - private List fields = new ArrayList<>(); - - public String getTitle() { - return title; - } - - public String getDescription() { - return description; - } - - public String getUrl() { - return url; - } - - public Color getColor() { - return color; - } - - public Footer getFooter() { - return footer; - } - - public Thumbnail getThumbnail() { - return thumbnail; - } - - public Image getImage() { - return image; - } - - public Author getAuthor() { - return author; - } - - public List getFields() { - return fields; - } - - public EmbedObject setTitle(String title) { - this.title = title; - return this; - } - - public EmbedObject setDescription(String description) { - this.description = description; - return this; - } - - public EmbedObject setUrl(String url) { - this.url = url; - return this; - } - - public EmbedObject setColor(Color color) { - this.color = color; - return this; - } - - public EmbedObject setFooter(String text, String icon) { - this.footer = new Footer(text, icon); - return this; - } - - public EmbedObject setThumbnail(String url) { - this.thumbnail = new Thumbnail(url); - return this; - } - - public EmbedObject setImage(String url) { - this.image = new Image(url); - return this; - } - - public EmbedObject setAuthor(String name, String url, String icon) { - this.author = new Author(name, url, icon); - return this; - } - - public EmbedObject addField(String name, String value, boolean inline) { - this.fields.add(new Field(name, value, inline)); - return this; - } - - class Footer { - private String text; - private String iconUrl; - - private Footer(String text, String iconUrl) { - this.text = text; - this.iconUrl = iconUrl; - } - - public String getText() { - return text; - } - - public String getIconUrl() { - return iconUrl; - } - } - - private class Thumbnail { - private String url; - - private Thumbnail(String url) { - this.url = url; - } - - private String getUrl() { - return url; - } - } - - private class Image { - private String url; - - private Image(String url) { - this.url = url; - } - - private String getUrl() { - return url; - } - } - - private class Author { - private String name; - private String url; - private String iconUrl; - - private Author(String name, String url, String iconUrl) { - this.name = name; - this.url = url; - this.iconUrl = iconUrl; - } - - private String getName() { - return name; - } - - private String getUrl() { - return url; - } - - private String getIconUrl() { - return iconUrl; - } - } - - private class Field { - private String name; - private String value; - private boolean inline; - - private Field(String name, String value, boolean inline) { - this.name = name; - this.value = value; - this.inline = inline; - } - - private String getName() { - return name; - } - - private String getValue() { - return value; - } - - private boolean isInline() { - return inline; - } - } - } - - private class JSONObject { - - private final HashMap map = new HashMap<>(); - - void put(String key, Object value) { - if (value != null) { - map.put(key, value); - } - } - - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - Set> entrySet = map.entrySet(); - builder.append("{"); - - int i = 0; - for (Map.Entry entry : entrySet) { - Object val = entry.getValue(); - builder.append(quote(entry.getKey())).append(":"); - - if (val instanceof String) { - builder.append(quote(String.valueOf(val))); - } else if (val instanceof Integer) { - builder.append(Integer.valueOf(String.valueOf(val))); - } else if (val instanceof Boolean) { - builder.append(val); - } else if (val instanceof JSONObject) { - builder.append(val.toString()); - } else if (val.getClass().isArray()) { - builder.append("["); - int len = Array.getLength(val); - for (int j = 0; j < len; j++) { - builder.append(Array.get(val, j).toString()).append(j != len - 1 ? "," : ""); - } - builder.append("]"); - } - - builder.append(++i == entrySet.size() ? "}" : ","); - } - - return builder.toString(); - } - - private String quote(String string) { - return "\"" + string + "\""; - } - } - -} \ No newline at end of file diff --git a/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/discord/JDAImplementation.java b/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/discord/JDAImplementation.java deleted file mode 100755 index ee82039..0000000 --- a/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/discord/JDAImplementation.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * BetterStaffChat - JDAImplementation.java - * Copyright (C) 2021 AusTech Development Team - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package dev.austech.betterstaffchat.common.discord; - -import dev.austech.betterstaffchat.common.util.AbstractStaffChatUtil; -import net.dv8tion.jda.api.EmbedBuilder; -import net.dv8tion.jda.api.JDA; - -import java.util.logging.Logger; - -/* We suppress this, because if the - channel or guild is null, we don't care. - - An error to the client is fine, as they - can read it and see what is wrong. - */ -@SuppressWarnings({ "ConstantConditions" }) -public class JDAImplementation { - private final JDA jda; - - public JDAImplementation(JDA jda, AbstractStaffChatUtil staffChatUtil) { - this.jda = jda; - - jda.addEventListener(new MessageListener(staffChatUtil)); - } - - public void sendMessage(String guild, String channel, String message) { - jda.getGuildById(guild).getTextChannelById(channel).sendMessage(message).queue(); - } - - public void sendEmbed(String guild, String channel, DiscordWebhook.EmbedObject embed) { - EmbedBuilder embedBuilder = new EmbedBuilder(); - embedBuilder.setColor(embed.getColor()); - embedBuilder.setDescription(embed.getDescription()); - - if (embed.getFooter() != null) { - embedBuilder.setFooter(embed.getFooter().getText(), embed.getFooter().getIconUrl()); - } - jda.getGuildById(guild).getTextChannelById(channel).sendMessage(embedBuilder.build()).queue(); - } - - public void shutdown() { - Logger.getLogger(JDA.class.getName()).info("Shutting down"); - this.jda.shutdown(); - } - - public JDA asJda() { - return this.jda; - } -} diff --git a/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/discord/MessageListener.java b/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/discord/MessageListener.java deleted file mode 100644 index e2f4cba..0000000 --- a/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/discord/MessageListener.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * BetterStaffChat - MessageListener.java - * Copyright (C) 2021 AusTech Development Team - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package dev.austech.betterstaffchat.common.discord; - -import dev.austech.betterstaffchat.common.util.AbstractStaffChatUtil; -import net.dv8tion.jda.api.events.message.guild.GuildMessageReceivedEvent; -import net.dv8tion.jda.api.hooks.ListenerAdapter; -import org.jetbrains.annotations.NotNull; - -public class MessageListener extends ListenerAdapter { - private final AbstractStaffChatUtil staffChatUtil; - - public MessageListener(AbstractStaffChatUtil staffChatUtil) { - this.staffChatUtil = staffChatUtil; - } - - @Override - public void onGuildMessageReceived(@NotNull GuildMessageReceivedEvent event) { - if (event.getAuthor().isBot()) return; - - staffChatUtil.handleDiscord(event); - } -} diff --git a/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/plugin/BetterStaffChatPlugin.java b/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/plugin/BetterStaffChatPlugin.java deleted file mode 100755 index ae3393b..0000000 --- a/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/plugin/BetterStaffChatPlugin.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * BetterStaffChat - BetterStaffChatPlugin.java - * Copyright (C) 2021 AusTech Development Team - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package dev.austech.betterstaffchat.common.plugin; - -import java.io.File; - -public interface BetterStaffChatPlugin { - /** - * Used to get a plugin's plugins/{name} folder. - * - * @return the plugin's data folder - */ - File getPluginDataFolder(); - - /** - * Logs a message with a prefix. - * - * @param string the message to log - */ - void logPrefix(String string); - - /** - * Logs a message with a prefix if debug is set to true in the console - * - * @param string the message to log - */ - void logPrefixDebug(String string); - - /** - * Logs a message. - * - * @param string the message to log - */ - void log(String string); - - /** - * Gets a server's "platform". - * - * @return the platform of the server - */ - Platform getPlatform(); -} diff --git a/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/plugin/Platform.java b/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/plugin/Platform.java deleted file mode 100755 index 64e1709..0000000 --- a/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/plugin/Platform.java +++ /dev/null @@ -1,5 +0,0 @@ -package dev.austech.betterstaffchat.common.plugin; - -public enum Platform { - BUKKIT, BUNGEECORD -} diff --git a/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/util/AbstractStaffChatUtil.java b/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/util/AbstractStaffChatUtil.java deleted file mode 100755 index 2662341..0000000 --- a/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/util/AbstractStaffChatUtil.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * BetterStaffChat - AbstractStaffChatUtil.java - * Copyright (C) 2021 AusTech Development Team - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package dev.austech.betterstaffchat.common.util; - -import dev.austech.betterstaffchat.common.discord.DiscordWebhook; - -public abstract class AbstractStaffChatUtil { - /** - * Gets a formatted message, that has placeholders replaced and colour codes (&) replaced. - * - * @param sender the sender - * @param message the original message - * @return a formatted message - */ - public abstract String getFormattedMessage(Object sender, String message); - - /** - * Alternative to Bukkit's broadcast method with bungeecord support and console logging. - * - * @param string the message to broadcast - * @param permission the required permission node to view the message - */ - public abstract void broadcast(String string, String permission); - - /** - * Sends a message to Discord via a Discord bot or a {@link DiscordWebhook}. - * - * @param sender the sender - * @param string the message to send - */ - public abstract void discord(Object sender, String string); - - /** - * Method to handle a received GuildMessageCreateEvent from Discord. - * - * @param event the event to handle - */ - public abstract void handleDiscord(Object event); - - /** - * Generates an embed with placeholders replaced - * - * @param sender the sender - * @param string the message - * @return an {@link dev.austech.betterstaffchat.common.discord.DiscordWebhook.EmbedObject} - */ - protected abstract DiscordWebhook.EmbedObject generateEmbed(Object sender, String string); -} diff --git a/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/util/Callback.java b/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/util/Callback.java deleted file mode 100644 index 815ddd4..0000000 --- a/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/util/Callback.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * BetterStaffChat - Callback.java - * Copyright (C) 2021 AusTech Development Team - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package dev.austech.betterstaffchat.common.util; - -public interface Callback { - void accept(T paramT); -} \ No newline at end of file diff --git a/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/util/TextUtil.java b/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/util/TextUtil.java deleted file mode 100755 index b0395f8..0000000 --- a/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/util/TextUtil.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * BetterStaffChat - TextUtil.java - * Copyright (C) 2021 AusTech Development Team - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package dev.austech.betterstaffchat.common.util; - -import dev.austech.betterstaffchat.common.plugin.Platform; -import lombok.experimental.UtilityClass; -import net.md_5.bungee.api.ChatColor; -import net.md_5.bungee.api.chat.TextComponent; - -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -@UtilityClass -public final class TextUtil { - private boolean rgbAvailable; - private Platform platform; - - public void init(Platform serverPlatform, Object plugin) { - platform = serverPlatform; - if (serverPlatform.equals(Platform.BUKKIT)) { - String packageName = plugin.getClass().getPackage().getName(); - if (Integer.parseInt(packageName.substring(packageName.lastIndexOf('.') + 1).split("_")[1]) >= 16) - rgbAvailable = true; - } - } - - /** - * Colors a specific message using the ChatColor API. - * @param string input - * @return Colored string - */ - public String colorize(String string) { - if (platform.equals(Platform.BUNGEECORD) || rgbAvailable) - return ChatColor.translateAlternateColorCodes('&', colorizeRgb(string)); - else return ChatColor.translateAlternateColorCodes('&', string); - } - - /* Pulled right from https://www.spigotmc.org/threads/hex-chat-class.449300/ */ - public String colorizeRgb(String string) { - final Pattern hexPattern = Pattern.compile("&#" + "([A-Fa-f0-9]{6})" + ""); - Matcher matcher = hexPattern.matcher(string); - StringBuffer buffer = new StringBuffer(string.length() + 4 * 8); - while (matcher.find()) - { - String group = matcher.group(1); - matcher.appendReplacement(buffer, ChatColor.COLOR_CHAR + "x" - + ChatColor.COLOR_CHAR + group.charAt(0) + ChatColor.COLOR_CHAR + group.charAt(1) - + ChatColor.COLOR_CHAR + group.charAt(2) + ChatColor.COLOR_CHAR + group.charAt(3) - + ChatColor.COLOR_CHAR + group.charAt(4) + ChatColor.COLOR_CHAR + group.charAt(5) - ); - } - return matcher.appendTail(buffer).toString(); - } - - public TextComponent colorizeToComponent(String string) { - return new TextComponent(TextComponent.fromLegacyText(string)); - } - - public String spacer(int amount) { - StringBuilder stringBuilder = new StringBuilder(); - for (int i = 0; i < amount; i++) - stringBuilder.append("&r "); - return stringBuilder.toString(); - } - - public String cleanForDiscord(String string) { - return string.replaceAll("/\\*/", "\\*").replaceAll("/\\//", "\\/"); - } -} diff --git a/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/util/UpdateChecker.java b/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/util/UpdateChecker.java deleted file mode 100755 index 9b8c743..0000000 --- a/BetterStaffChat-common/src/main/java/dev/austech/betterstaffchat/common/util/UpdateChecker.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * BetterStaffChat - UpdateChecker.java - * Copyright (C) 2021 AusTech Development Team - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package dev.austech.betterstaffchat.common.util; - -import dev.austech.betterstaffchat.common.plugin.BetterStaffChatPlugin; -import lombok.experimental.UtilityClass; - -import java.io.IOException; -import java.io.InputStream; -import java.net.URL; -import java.util.Scanner; - -@UtilityClass -public class UpdateChecker { - /** - * Checks whether or not the plugin needs an update. - * - * @param plugin the plugin object to use for logging - * @param currentVersion the current version of the plugin - * @return if the plugin needs an update - * @see SpigotMC - Creating an update checker that checks for updates - */ - public boolean needsUpdate(BetterStaffChatPlugin plugin, String currentVersion) { - if (currentVersion.contains("dev")) return false; - - try (InputStream inputStream = new URL("https://api.spigotmc.org/legacy/update.php?resource=91991").openStream()) { - Scanner scanner = new Scanner(inputStream); - if (scanner.hasNext()) { - return !currentVersion.equals(scanner.next()); - } - } catch (IOException exception) { - plugin.logPrefix("&cFailed to check for updates..."); - } - return false; - } -} diff --git a/BetterStaffChat-common/src/main/resources/config.yml b/BetterStaffChat-common/src/main/resources/config.yml deleted file mode 100755 index efbaf9b..0000000 --- a/BetterStaffChat-common/src/main/resources/config.yml +++ /dev/null @@ -1,167 +0,0 @@ -# BetterStaffChat - -# Copyright (c) 2021 AusTech Development Team -# Licensed under GNU General Public License v3 - -# Github profile: https://github.com/AusTechDev/ -# Spigot Profile: https://www.spigotmc.org/members/919057/ -# Discord Server: https://austech.dev/to/support - -staffchat: - # PlaceholderAPI is supported only on Spigot - # Placeholders for both spigot and bungeecord: - # %player_name% - The player's name, %message% - The message, %server% - The server the player is on (ONLY BUNGEE) - # %luckperms_prefix% - The prefix of the player from LuckPerms, %luckperms_suffix% - The suffix of the player from LuckPerms - format: "&8[&dS&8] &7%player_name%&8: &f%message%" - - # Putting whatever is below before your message will send it to staffchat - # e.g # hello there - prefix: "# " # Set to "" to disable - - # Set the name of the sender when console runs /staffchat - console-replacement: "Console" - - # Set the UUID of the console when %uuid% is used in embeds - console-uuid-replacement: "f78a4d8d-d51b-4b39-98a3-230f2de0c670" - - # Set the name of the sender when console runs /staffchat - # ONLY BUNGEE - console-server-replacement: "Global" - - # Whether or not to send staffchat messages to console - log-to-console: true - - # If a staff member puts color codes in their message, it will remove them if the below is true. - strip-color-codes: false - - # Changes the display name of servers on Bungeecord. Color codes supported - server-replacement: # Format - servername: "Displayname" - hub: "Hub" # Make sure that the first value is the one equal to in the bungeecord config.yml - - # The message that shows when someone has "betterstaffchat.messages.join" joins the network - # PlaceholderAPI is supported only on Spigot - # Placeholders for both spigot and bungeecord: - # %player_name% - The player's name, %server% - The server the player is on (ONLY BUNGEE) - # %luckperms_prefix% - The prefix of the player from LuckPerms, %luckperms_suffix% - The suffix of the player from LuckPerms - join: "&8[&dS&8] &7%player_name% &fjoined &7the network." # Set to "" to disable - - # The message that shows when someone has "betterstaffchat.messages.leave" leaves the network - # PlaceholderAPI is supported only on Spigot - # Placeholders for both spigot and bungeecord: - # %player_name% - The player's name, %server% - The server the player is on (ONLY BUNGEE) - # %luckperms_prefix% - The prefix of the player from LuckPerms, %luckperms_suffix% - The suffix of the player from LuckPerms - leave: "&8[&dS&8] &7%player_name% &fleft &7the network." # Set to "" to disable - - # The message that shows when someone has "betterstaffchat.messages.switch" switches between servers/worlds - # PlaceholderAPI is supported only on Spigot - # Note, if on bungee it will show the server, however on spigot it will show the world. - # %player_name% - The player's name, %from% - The server/world the player left, %to% - The server/world the player joined - # %luckperms_prefix% - The prefix of the player from LuckPerms, %luckperms_suffix% - The suffix of the player from LuckPerms - switch: "&8[&dS&8] &7%player_name% &fswitched &7from &f%from% &7to &f%to%" # Set to "" to disable - - # The message that is shown when staff chat messages are toggled on with /mutestaffchat - # /mutestaffchat required permission betterstaffchat.mutestaffchat - mute-off: "&8[&dS&8] &7Your staff chat is now &aenabled&7." - - # The message that is shown when staff chat messages are toggled off with /mutestaffchat - # /mutestaffchat required permission betterstaffchat.mutestaffchat - mute-on: "&8[&dS&8] &7Your staff chat is now &cdisabled&7." - - # The message that is shown when all the player's messages go to the staffchat with /togglestaffchat - # /togglestaffchat required permission betterstaffchat.togglestaffchat - toggle-off: "&8[&dS&8] &7All your messages will &cno longer&7 go to staffchat." - - # The message that is shown when staff chat messages are toggled on with /mutestaffchat - # /mutestaffchat required permission betterstaffchat.mutestaffchat - toggle-on: "&8[&dS&8] &7All your messages will &anow&7 go to staffchat." - -discord: - - # If you need help, we're here to assist you at https://austech.dev/to/support - - # Don't enable if webhook is enabled below - bot: - enabled: false - - # The token of the Discord bot. If you need help setting it up we're here to assist you at https://austech.dev/to/support - token: "N/A" - - # What the bot is doing in its status, e.g Playing with staffchat - activity: "with staffchat" - - # The different types of activities (Must be in caps) - # - PLAYING, WATCHING, LISTENING - activity-type: "PLAYING" - - # The channels that staff messages are sent to - # Format - GuildID: ChannelID - channels: - - "N/A: N/A" - - - # Don't enable if bot is enabled above - webhook: - enabled: false - - # The webhook URL. If you need help setting it up we're here to assist you at https://austech.dev/to/support - url: "" - - discord-messages: - - # The format in game, for when someone sends a message in Discord - # Available placeholders: %username%, %discriminator% - The 4 numbers in the username - # %nickname% - The nickname on Discord - name-format: "%username%#%discriminator%" - - embed: - # Whether or not the message sent to Discord should be an embed - # true for yes, false for no. - enabled: true - - # The text that is at the bottom of the embed - # %player_name% - The player's name, %uuid% - The player's UUID, %server% - The server the player is on (ONLY BUNGEE) - embed-footer: "BetterStaffChat" # Setting this to "" will disable this and the icon. - - # The little icon next to the footer - # %player_name% - The player's name, %uuid% - The player's UUID, %server% - The server the player is on (ONLY BUNGEE) - embed-footer-icon: "https://crafatar.com/avatars/%uuid%?overlay" # Set to "" to disable - - # The color of the embed, MUST be a hex code. - embed-color: "#FFD555" - - # The format of the message sent to Discord - # %player_name% - The player_name's name, %message% - The message, %server% - The server the player is on (ONLY BUNGEE) - # %luckperms_prefix% - The prefix of the player from LuckPerms, %luckperms_suffix% - The suffix of the player from LuckPerms - staffchat-format: "**%player_name%**: %message%" - - # The message that is sent to Discord when a player with the "betterstaffchat.messages.join" permission joins - # %player_name% - The player's name, %server% - The server the player is on (ONLY BUNGEE) - # %luckperms_prefix% - The prefix of the player from LuckPerms, %luckperms_suffix% - The suffix of the player from LuckPerms - staffchat-join: "%player_name% **joined** the network" - - # The message that is sent to Discord when a player with the "betterstaffchat.messages.leave" permission leaves - # %player_name% - The player's name, %server% - The server the player is on (ONLY BUNGEE) - # %luckperms_prefix% - The prefix of the player from LuckPerms, %luckperms_suffix% - The suffix of the player from LuckPerms - staffchat-leave: "%player_name% **left** the network" - - # The message that is sent to Discord when a player with the "betterstaffchat.messages.switch" permission servers/worlds - # %player_name% - The player's name, %from% - The server/world the player left, %to% - The server/world the player joined - # %luckperms_prefix% - The prefix of the player from LuckPerms, %luckperms_suffix% - The suffix of the player from LuckPerms - staffchat-switch: "%player_name% **switched** from **%from%** to **%to%**" - -commands: # Options for commands | Aliases can not be the same, nor can they be any base command. - staffchat: # /staffchat command - aliases: [sc, schat] - mutestaffchat: # /mutestaffchat command - aliases: [mutesc, scmute, scm, msc, ignorestaffchat] - togglestaffchat: # /togglestaffchat command - aliases: [tsc, sct, sctoggle, staffchattoggle, togglesc] - -# Disable this to stop the plugin checking for updates on startup -check-for-updates: true - -# Don't touch unless you know what you are doing -debug: false - -# no touchy me please [DO NOT TOUCH AS IT CAN BREAK YOUR CONFIG] -config-version: 4 \ No newline at end of file diff --git a/BetterStaffChat-spigot/build.gradle b/BetterStaffChat-spigot/build.gradle deleted file mode 100644 index f909b80..0000000 --- a/BetterStaffChat-spigot/build.gradle +++ /dev/null @@ -1,54 +0,0 @@ -/* - * BetterStaffChat - build.gradle - * Copyright (C) 2021 AusTech Development Team - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -import org.apache.tools.ant.filters.ReplaceTokens - -plugins { - id 'com.github.johnrengelman.shadow' -} - -repositories { - maven { - name = 'spigotmc-repo' - url = 'https://hub.spigotmc.org/nexus/content/repositories/snapshots/' - } - maven { - name = 'sonatype' - url = 'https://oss.sonatype.org/content/groups/public/' - } -} - -repositories { - maven { url "https://hub.spigotmc.org/nexus/content/repositories/snapshots/" } - maven { url "https://repo.extendedclip.com/content/repositories/placeholderapi/" } -} - -dependencies { - compileOnly 'org.spigotmc:spigot-api:1.17.1-R0.1-SNAPSHOT' - compileOnly 'me.clip:placeholderapi:2.10.9' - implementation 'org.bstats:bstats-bukkit:2.2.1' - implementation project(':BetterStaffChat-common') -} - -processResources { - filter ReplaceTokens, tokens: [ - "version": parent.getVersion() - ] -} - -tasks.build.dependsOn tasks.shadowJar diff --git a/BetterStaffChat-spigot/src/main/java/dev/austech/betterstaffchat/spigot/BetterStaffChatSpigot.java b/BetterStaffChat-spigot/src/main/java/dev/austech/betterstaffchat/spigot/BetterStaffChatSpigot.java deleted file mode 100755 index 3c15f4d..0000000 --- a/BetterStaffChat-spigot/src/main/java/dev/austech/betterstaffchat/spigot/BetterStaffChatSpigot.java +++ /dev/null @@ -1,181 +0,0 @@ -/* - * BetterStaffChat - BetterStaffChatSpigot.java - * Copyright (C) 2021 AusTech Development Team - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package dev.austech.betterstaffchat.spigot; - -import com.google.common.collect.Lists; -import dev.austech.betterstaffchat.common.plugin.BetterStaffChatPlugin; -import dev.austech.betterstaffchat.common.dependency.BetterStaffChatDependencyProvider; -import dev.austech.betterstaffchat.common.dependency.DependencyEngine; -import dev.austech.betterstaffchat.common.discord.JDAImplementation; -import dev.austech.betterstaffchat.common.plugin.Platform; -import dev.austech.betterstaffchat.common.util.TextUtil; -import dev.austech.betterstaffchat.common.util.UpdateChecker; -import dev.austech.betterstaffchat.spigot.command.BetterStaffChatCommand; -import dev.austech.betterstaffchat.spigot.command.MuteStaffChatCommand; -import dev.austech.betterstaffchat.spigot.command.StaffChatCommand; -import dev.austech.betterstaffchat.spigot.command.ToggleStaffChatCommand; -import dev.austech.betterstaffchat.spigot.listener.PlayerListener; -import dev.austech.betterstaffchat.spigot.reflection.ReflectionUtil; -import dev.austech.betterstaffchat.spigot.util.Config; -import dev.austech.betterstaffchat.spigot.util.StaffChatUtil; -import lombok.Getter; -import lombok.Setter; -import net.dv8tion.jda.api.JDABuilder; -import net.dv8tion.jda.api.entities.Activity; -import org.bstats.bukkit.Metrics; -import org.bukkit.Bukkit; -import org.bukkit.command.CommandSender; -import org.bukkit.configuration.file.FileConfiguration; -import org.bukkit.entity.Player; -import org.bukkit.plugin.java.JavaPlugin; - -import javax.naming.ConfigurationException; -import javax.security.auth.login.LoginException; -import java.io.File; -import java.util.ArrayList; -import java.util.Optional; -import java.util.UUID; -import java.util.logging.Level; - -public final class BetterStaffChatSpigot extends JavaPlugin implements BetterStaffChatPlugin { - @Getter private static BetterStaffChatSpigot instance; - @Getter private final ArrayList ignoreStaffChat = Lists.newArrayList(); - @Getter private final ArrayList toggledStaffChat = Lists.newArrayList(); - @Getter private JDAImplementation jda; - @Getter boolean discordEnabled; - @Getter @Setter private FileConfiguration config; - - @Override - public void onEnable() { - instance = this; - - TextUtil.init(getPlatform(), this); - Config.load(); - - if (getConfig().getBoolean("check-for-updates")) - Bukkit.getScheduler().runTaskLaterAsynchronously(this, () -> { - if (UpdateChecker.needsUpdate(this, getDescription().getVersion())) { - logPrefix("&eA new update for BetterStaffChat is available..."); - logPrefix("&ehttps://www.spigotmc.org/resources/91991"); - } - }, 20 * 3); - - new Metrics(this, 10952); - - - if (getConfig().getBoolean("discord.bot.enabled") && getConfig().getBoolean("discord.webhook.enabled")) { - new ConfigurationException("Both Discord types are enabled").printStackTrace(); - this.getPluginLoader().disablePlugin(this); - return; - } - - this.discordEnabled = getConfig().getBoolean("discord.bot.enabled"); - - - DependencyEngine dependencyEngine = DependencyEngine.createNew(new File(getPluginDataFolder(), "libs").toPath()); - dependencyEngine.addDependenciesFromProvider(BetterStaffChatDependencyProvider.getDependencies()); - dependencyEngine.loadDependencies().thenAccept((empty) -> { - if (!dependencyEngine.getErrors().isEmpty()) { - Optional opt = dependencyEngine.getErrors().stream().filter(throwable -> throwable.getMessage().contains("Unable to make protected void java.net.URLClassLoader.addURL(java.net.URL) accessible: module java.base does not")).findFirst(); - if (opt.isPresent()) { - getLogger().log(Level.SEVERE, "An error occurred whilst starting BetterStaffChat - This is due to Java 16 and up being incompatible by default."); - getLogger().log(Level.SEVERE, "This error is fixable, please add the following flags to your startup after the \"java\":"); - getLogger().log(Level.SEVERE, ""); - getLogger().log(Level.SEVERE, "--add-opens java.base/java.net=ALL-UNNAMED"); - } else { - dependencyEngine.getErrors().forEach(Throwable::printStackTrace); - getLogger().log(Level.SEVERE, "Errors occurred whilst loading BSC."); - } - this.getPluginLoader().disablePlugin(this); - return; - } - - if (discordEnabled) { - Bukkit.getScheduler().runTaskAsynchronously(this, () -> { - try { - this.jda = new JDAImplementation(JDABuilder.createLight(getConfig().getString("discord.bot.token")).build(), StaffChatUtil.getInstance()); - jda.asJda().getPresence().setActivity(Activity.of( - Activity.ActivityType.valueOf(getConfig().getString("discord.bot.activity-type").toUpperCase().replace("PLAYING", "DEFAULT")), - getConfig().getString("discord.bot.activity") - )); - } catch (LoginException exception) { - exception.printStackTrace(); - this.getPluginLoader().disablePlugin(this); - } - }); - } - }); - - registerCommands(); - - Bukkit.getPluginManager().registerEvents(new PlayerListener(), this); - } - - public boolean reloadConfig(CommandSender sender) { - boolean discordLoaded = getConfig().getBoolean("discord.bot.enabled"); - Config.load(); - - /* - * We can't unregister commands easily, without doing hacky reflection stuff - * For now, restarting the server will fix it. - */ - Bukkit.getScheduler().runTaskAsynchronously(this, this::registerCommands); - - if (getConfig().getBoolean("discord.bot.enabled") != discordLoaded) { - logPrefix(TextUtil.colorize("&cYou enabled the discord bot in the config. Please restart the server for changes to take effect.")); - if (sender instanceof Player) - sender.sendMessage(TextUtil.colorize("&cYou enabled the discord bot in the config. Please restart the server for changes to take effect.")); - return true; - } - return false; - } - - private void registerCommands() { - ReflectionUtil.registerCmd(new BetterStaffChatCommand()); - ReflectionUtil.registerCmd(new StaffChatCommand("staffchat", "The command that lets you send a message to the staffchat.", getConfig().getStringList("commands.staffchat.aliases"))); - ReflectionUtil.registerCmd(new MuteStaffChatCommand("mutestaffchat", "Allows you to toggle whether you receive staff chat or not.", getConfig().getStringList("commands.mutestaffchat.aliases"))); - ReflectionUtil.registerCmd(new ToggleStaffChatCommand("togglestaffchat", "Allows you to toggle whether or not all your messages to to staffchat.", getConfig().getStringList("commands.togglestaffchat.aliases"))); - } - - public File getPluginDataFolder() { - return getDataFolder(); - } - - public void log(String string) { - Bukkit.getConsoleSender().sendMessage(TextUtil.colorize(string)); - } - - @Override public Platform getPlatform() { - return Platform.BUKKIT; - } - - public void logPrefix(String string) { - Bukkit.getConsoleSender().sendMessage("[BetterStaffChat] " + TextUtil.colorize(string)); - } - - public void logPrefixDebug(String string) { - if (getConfig().getBoolean("debug")) - Bukkit.getConsoleSender().sendMessage("[BetterStaffChat] Debug - " + TextUtil.colorize(string)); - } - - @Override - public void onDisable() { - if (isDiscordEnabled()) (getJda()).shutdown(); - } -} diff --git a/BetterStaffChat-spigot/src/main/java/dev/austech/betterstaffchat/spigot/command/BetterStaffChatCommand.java b/BetterStaffChat-spigot/src/main/java/dev/austech/betterstaffchat/spigot/command/BetterStaffChatCommand.java deleted file mode 100755 index 2af5499..0000000 --- a/BetterStaffChat-spigot/src/main/java/dev/austech/betterstaffchat/spigot/command/BetterStaffChatCommand.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * BetterStaffChat - BetterStaffChatCommand.java - * Copyright (C) 2021 AusTech Development Team - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package dev.austech.betterstaffchat.spigot.command; - -import com.google.common.collect.Lists; -import dev.austech.betterstaffchat.common.util.TextUtil; -import dev.austech.betterstaffchat.spigot.BetterStaffChatSpigot; -import net.dv8tion.jda.api.entities.Activity; -import org.bukkit.command.Command; -import org.bukkit.command.CommandSender; - -public class BetterStaffChatCommand extends Command { - public BetterStaffChatCommand() { - super("betterstaffchat", "The main command for BetterStaffChat", "", Lists.newArrayList("bsc")); - } - - @Override - public boolean execute(CommandSender sender, String commandLabel, String[] args) { - if (args.length == 1 && args[0].equalsIgnoreCase("reload") && sender.hasPermission("betterstaffchat.reload")) { - boolean reload = BetterStaffChatSpigot.getInstance().reloadConfig(sender); - if (!reload && BetterStaffChatSpigot.getInstance().getConfig().getBoolean("discord.bot.enabled")) { - BetterStaffChatSpigot.getInstance().getJda().asJda().getPresence().setActivity(Activity.of( - Activity.ActivityType.valueOf(BetterStaffChatSpigot.getInstance().getConfig().getString("discord.bot.activity-type").toUpperCase().replace("PLAYING", "DEFAULT")), - BetterStaffChatSpigot.getInstance().getConfig().getString("discord.bot.activity") - )); - } - - sender.sendMessage(TextUtil.colorize("&8&l&m-------------------------------")); - sender.sendMessage(TextUtil.colorize(TextUtil.spacer(18) + "&c&l&oBetter&4&l&oStaffChat")); - sender.sendMessage(TextUtil.colorize(TextUtil.spacer(13) + "&7Configuration Reloaded")); - sender.sendMessage(TextUtil.colorize("&8&l&m-------------------------------")); - return true; - } - - sender.sendMessage(TextUtil.colorize("&8&l&m-------------------------------")); - sender.sendMessage(TextUtil.colorize(TextUtil.spacer(18) + "&c&l&oBetter&4&l&oStaffChat")); - sender.sendMessage(TextUtil.colorize(TextUtil.spacer(13) + "&7The &7&o\"better\"&r &7staff chat.")); - sender.sendMessage(TextUtil.colorize("&8&l&m-------------------------------")); - return true; - } -} diff --git a/BetterStaffChat-spigot/src/main/java/dev/austech/betterstaffchat/spigot/command/MuteStaffChatCommand.java b/BetterStaffChat-spigot/src/main/java/dev/austech/betterstaffchat/spigot/command/MuteStaffChatCommand.java deleted file mode 100644 index 778bb09..0000000 --- a/BetterStaffChat-spigot/src/main/java/dev/austech/betterstaffchat/spigot/command/MuteStaffChatCommand.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * BetterStaffChat - MuteStaffChatCommand.java - * Copyright (C) 2021 AusTech Development Team - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package dev.austech.betterstaffchat.spigot.command; - -import dev.austech.betterstaffchat.common.util.TextUtil; -import dev.austech.betterstaffchat.spigot.BetterStaffChatSpigot; -import org.bukkit.command.Command; -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; -import org.jetbrains.annotations.NotNull; - -import java.util.List; - -public class MuteStaffChatCommand extends Command { - public MuteStaffChatCommand(@NotNull String name, @NotNull String description, @NotNull List aliases) { - super(name, description, "", aliases); - } - - @Override - public boolean execute(@NotNull CommandSender sender, @NotNull String commandLabel, @NotNull String[] args) { - if (!sender.hasPermission("betterstaffchat.mutestaffchat")) { - sender.sendMessage(TextUtil.colorize("&cNo permission.")); - return true; - } - - if (!(sender instanceof Player)) { - sender.sendMessage(TextUtil.colorize("&cThis command can only be used by players.")); - return true; - } - - if (BetterStaffChatSpigot.getInstance().getIgnoreStaffChat().contains(((Player) sender).getUniqueId())) { - BetterStaffChatSpigot.getInstance().getIgnoreStaffChat().remove(((Player) sender).getUniqueId()); - } else { - if (BetterStaffChatSpigot.getInstance().getToggledStaffChat().contains(((Player) sender).getUniqueId())) { - BetterStaffChatSpigot.getInstance().getToggledStaffChat().remove(((Player) sender).getUniqueId()); - } - BetterStaffChatSpigot.getInstance().getIgnoreStaffChat().add(((Player) sender).getUniqueId()); - } - - sender.sendMessage(TextUtil.colorize(BetterStaffChatSpigot.getInstance().getIgnoreStaffChat().contains(((Player) sender).getUniqueId()) ? - BetterStaffChatSpigot.getInstance().getConfig().getString("staffchat.mute-on") : - BetterStaffChatSpigot.getInstance().getConfig().getString("staffchat.mute-off")) - ); - - return true; - } -} diff --git a/BetterStaffChat-spigot/src/main/java/dev/austech/betterstaffchat/spigot/command/StaffChatCommand.java b/BetterStaffChat-spigot/src/main/java/dev/austech/betterstaffchat/spigot/command/StaffChatCommand.java deleted file mode 100644 index fc50c8e..0000000 --- a/BetterStaffChat-spigot/src/main/java/dev/austech/betterstaffchat/spigot/command/StaffChatCommand.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * BetterStaffChat - StaffChatCommand.java - * Copyright (C) 2021 AusTech Development Team - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package dev.austech.betterstaffchat.spigot.command; - -import com.google.common.collect.Lists; -import dev.austech.betterstaffchat.common.util.TextUtil; -import dev.austech.betterstaffchat.spigot.BetterStaffChatSpigot; -import dev.austech.betterstaffchat.spigot.util.StaffChatUtil; -import net.md_5.bungee.api.ChatColor; -import org.bukkit.command.Command; -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; -import org.jetbrains.annotations.NotNull; - -import java.util.Arrays; -import java.util.List; - -public class StaffChatCommand extends Command { - public StaffChatCommand(@NotNull String name, @NotNull String description, @NotNull List aliases) { - super(name, description, "", aliases); - } - - @Override - public boolean execute(@NotNull CommandSender sender, @NotNull String commandLabel, @NotNull String[] args) { - if (!sender.hasPermission("betterstaffchat.messages.send")) { - sender.sendMessage(TextUtil.colorize("&cNo permission.")); - return true; - } - - if (sender instanceof Player && BetterStaffChatSpigot.getInstance().getIgnoreStaffChat().contains(((Player) sender).getUniqueId())) { - sender.sendMessage(TextUtil.colorize("&cYour staff chat is currently disabled.")); - return true; - } - - if (args.length == 0) { - sender.sendMessage(TextUtil.colorize("&cUsage: /" + commandLabel + " " + this.getUsage())); - return true; - } - - StaffChatUtil.getInstance().broadcast(StaffChatUtil.getInstance().getFormattedMessage(sender, String.join(" ", Arrays.copyOfRange(args, 0, args.length))), "betterstaffchat.messages.read"); - - if (sender instanceof Player) - StaffChatUtil.getInstance().discord(sender, ChatColor.stripColor(TextUtil.colorize(StaffChatUtil.getInstance().placeholder((Player) sender, BetterStaffChatSpigot.getInstance().getConfig().getString("discord.discord-messages.staffchat-format").replace("%player_name%", sender.getName()).replace("%message%", String.join(" ", Arrays.copyOfRange(args, 0, args.length))))))); - else - StaffChatUtil.getInstance().discord(sender, ChatColor.stripColor(TextUtil.colorize(BetterStaffChatSpigot.getInstance().getConfig().getString("discord.discord-messages.staffchat-format").replace("%player_name%", - BetterStaffChatSpigot.getInstance().getConfig().getString("staffchat.console-replacement")).replace("%message%", String.join(" ", Arrays.copyOfRange(args, 0, args.length)))))); - - return true; - } -} diff --git a/BetterStaffChat-spigot/src/main/java/dev/austech/betterstaffchat/spigot/command/ToggleStaffChatCommand.java b/BetterStaffChat-spigot/src/main/java/dev/austech/betterstaffchat/spigot/command/ToggleStaffChatCommand.java deleted file mode 100644 index ad98af7..0000000 --- a/BetterStaffChat-spigot/src/main/java/dev/austech/betterstaffchat/spigot/command/ToggleStaffChatCommand.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * BetterStaffChat - ToggleStaffChatCommand.java - * Copyright (C) 2021 AusTech Development Team - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package dev.austech.betterstaffchat.spigot.command; - -import dev.austech.betterstaffchat.common.util.TextUtil; -import dev.austech.betterstaffchat.spigot.BetterStaffChatSpigot; -import org.bukkit.command.Command; -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; -import org.jetbrains.annotations.NotNull; - -import java.util.List; - -public class ToggleStaffChatCommand extends Command { - public ToggleStaffChatCommand(@NotNull String name, @NotNull String description, @NotNull List aliases) { - super(name, description, "", aliases); - } - - @Override - public boolean execute(@NotNull CommandSender sender, @NotNull String commandLabel, @NotNull String[] args) { - if (!sender.hasPermission("betterstaffchat.togglestaffchat")) { - sender.sendMessage(TextUtil.colorize("&cNo permission.")); - return true; - } - - if (!(sender instanceof Player)) { - sender.sendMessage(TextUtil.colorize("&cThis command can only be used by players.")); - return true; - } - - if (sender instanceof Player && BetterStaffChatSpigot.getInstance().getIgnoreStaffChat().contains(((Player) sender).getUniqueId())) { - sender.sendMessage(TextUtil.colorize("&cYour staff chat is currently disabled.")); - return true; - } - - if (BetterStaffChatSpigot.getInstance().getToggledStaffChat().contains(((Player) sender).getUniqueId())) { - BetterStaffChatSpigot.getInstance().getToggledStaffChat().remove(((Player) sender).getUniqueId()); - } else { - BetterStaffChatSpigot.getInstance().getToggledStaffChat().add(((Player) sender).getUniqueId()); - } - - sender.sendMessage(TextUtil.colorize(BetterStaffChatSpigot.getInstance().getToggledStaffChat().contains(((Player) sender).getUniqueId()) ? - BetterStaffChatSpigot.getInstance().getConfig().getString("staffchat.toggle-on") : - BetterStaffChatSpigot.getInstance().getConfig().getString("staffchat.toggle-off")) - ); - - return true; - } -} diff --git a/BetterStaffChat-spigot/src/main/java/dev/austech/betterstaffchat/spigot/listener/PlayerListener.java b/BetterStaffChat-spigot/src/main/java/dev/austech/betterstaffchat/spigot/listener/PlayerListener.java deleted file mode 100644 index 9af4d49..0000000 --- a/BetterStaffChat-spigot/src/main/java/dev/austech/betterstaffchat/spigot/listener/PlayerListener.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * BetterStaffChat - PlayerListener.java - * Copyright (C) 2021 AusTech Development Team - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package dev.austech.betterstaffchat.spigot.listener; - -import dev.austech.betterstaffchat.common.util.TextUtil; -import dev.austech.betterstaffchat.spigot.BetterStaffChatSpigot; -import dev.austech.betterstaffchat.spigot.util.StaffChatUtil; -import net.md_5.bungee.api.ChatColor; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.Listener; -import org.bukkit.event.player.AsyncPlayerChatEvent; -import org.bukkit.event.player.PlayerChangedWorldEvent; -import org.bukkit.event.player.PlayerJoinEvent; -import org.bukkit.event.player.PlayerQuitEvent; - -public class PlayerListener implements Listener { - @EventHandler(priority = EventPriority.HIGH) - public void onAsyncPlayerChat(AsyncPlayerChatEvent event) { - String prefix = BetterStaffChatSpigot.getInstance().getConfig().getString("staffchat.prefix"); - - if (event.getPlayer().hasPermission("betterstaffchat.messages.send") && event.getMessage().startsWith(prefix) && event.getMessage().length() > prefix.length() && !prefix.equals("")) { - event.setCancelled(true); - - if (BetterStaffChatSpigot.getInstance().getIgnoreStaffChat().contains(event.getPlayer().getUniqueId())) { - event.getPlayer().sendMessage(TextUtil.colorize("&cYour staff chat is currently disabled.")); - return; - } - - StaffChatUtil.getInstance().broadcast(StaffChatUtil.getInstance().getFormattedMessage(event.getPlayer(), event.getMessage().substring(prefix.length())), "betterstaffchat.messages.read"); - StaffChatUtil.getInstance().discord(event.getPlayer(), ChatColor.stripColor(TextUtil.colorize(StaffChatUtil.getInstance().placeholder(event.getPlayer(), BetterStaffChatSpigot.getInstance().getConfig().getString("discord.discord-messages.staffchat-format").replace("%player_name%", event.getPlayer().getName()).replace("%message%", event.getMessage().substring(prefix.length())))))); - } else if (event.getPlayer().hasPermission("betterstaffchat.messages.send") && BetterStaffChatSpigot.getInstance().getToggledStaffChat().contains(event.getPlayer().getUniqueId())) { - event.setCancelled(true); - - StaffChatUtil.getInstance().broadcast(StaffChatUtil.getInstance().getFormattedMessage(event.getPlayer(), event.getMessage()), "betterstaffchat.messages.read"); - StaffChatUtil.getInstance().discord(event.getPlayer(), ChatColor.stripColor(TextUtil.colorize(StaffChatUtil.getInstance().placeholder(event.getPlayer(), BetterStaffChatSpigot.getInstance().getConfig().getString("discord.discord-messages.staffchat-format").replace("%player_name%", event.getPlayer().getName()).replace("%message%", event.getMessage()))))); - } - } - - @EventHandler - public void onPlayerJoin(PlayerJoinEvent event) { - if (event.getPlayer().hasPermission("betterstaffchat.messages.join") && !BetterStaffChatSpigot.getInstance().getConfig().getString("staffchat.join").equals("")) { - String message = TextUtil.colorize( - BetterStaffChatSpigot.getInstance().getConfig().getString("staffchat.join") - .replace("%player_name%", event.getPlayer().getName()) - ); - - message = TextUtil.colorize(StaffChatUtil.getInstance().placeholder(event.getPlayer(), message)); - - StaffChatUtil.getInstance().broadcast(message, "betterstaffchat.messages.read"); - StaffChatUtil.getInstance().discord(event.getPlayer(), ChatColor.stripColor(TextUtil.colorize(StaffChatUtil.getInstance().placeholder(event.getPlayer(), BetterStaffChatSpigot.getInstance().getConfig().getString("discord.discord-messages.staffchat-join").replace("%player_name%", event.getPlayer().getName()))))); - } - } - - @EventHandler - public void onPlayerQuit(PlayerQuitEvent event) { - if (event.getPlayer().hasPermission("betterstaffchat.messages.leave") && !BetterStaffChatSpigot.getInstance().getConfig().getString("staffchat.leave").equals("")) { - String message = TextUtil.colorize( - BetterStaffChatSpigot.getInstance().getConfig().getString("staffchat.leave") - .replace("%player_name%", event.getPlayer().getName()) - ); - - message = TextUtil.colorize(StaffChatUtil.getInstance().placeholder(event.getPlayer(), message)); - - StaffChatUtil.getInstance().broadcast(message, "betterstaffchat.messages.read"); - StaffChatUtil.getInstance().discord(event.getPlayer(), ChatColor.stripColor(TextUtil.colorize(StaffChatUtil.getInstance().placeholder(event.getPlayer(), BetterStaffChatSpigot.getInstance().getConfig().getString("discord.discord-messages.staffchat-leave").replace("%player_name%", event.getPlayer().getName()))))); - } - } - - @EventHandler - public void onPlayerChangeWorld(PlayerChangedWorldEvent event) { - if (event.getPlayer().hasPermission("betterstaffchat.messages.switch") && !BetterStaffChatSpigot.getInstance().getConfig().getString("staffchat.switch").equals("")) { - String message = TextUtil.colorize( - BetterStaffChatSpigot.getInstance().getConfig().getString("staffchat.switch") - .replace("%player_name%", event.getPlayer().getName()) - .replace("%from%", event.getFrom().getName()) - .replace("%to%", event.getPlayer().getWorld().getName()) - ); - - message = TextUtil.colorize(StaffChatUtil.getInstance().placeholder(event.getPlayer(), message)); - - StaffChatUtil.getInstance().broadcast(message, "betterstaffchat.messages.read"); - StaffChatUtil.getInstance().discord(event.getPlayer(), ChatColor.stripColor(TextUtil.colorize(StaffChatUtil.getInstance().placeholder(event.getPlayer(), BetterStaffChatSpigot.getInstance().getConfig().getString("discord.discord-messages.staffchat-switch") - .replace("%player_name%", event.getPlayer().getName()) - .replace("%from%", event.getFrom().getName()) - .replace("%to%", event.getPlayer().getWorld().getName()))))); - } - } -} diff --git a/BetterStaffChat-spigot/src/main/java/dev/austech/betterstaffchat/spigot/reflection/ReflectionUtil.java b/BetterStaffChat-spigot/src/main/java/dev/austech/betterstaffchat/spigot/reflection/ReflectionUtil.java deleted file mode 100644 index b0ccfc2..0000000 --- a/BetterStaffChat-spigot/src/main/java/dev/austech/betterstaffchat/spigot/reflection/ReflectionUtil.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * BetterStaffChat - ReflectionUtil.java - * Copyright (C) 2021 AusTech Development Team - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package dev.austech.betterstaffchat.spigot.reflection; - -import org.bukkit.Bukkit; -import org.bukkit.command.Command; -import org.bukkit.command.CommandMap; -import org.bukkit.command.SimpleCommandMap; - -import java.lang.reflect.InvocationTargetException; - -public class ReflectionUtil { - protected static SimpleCommandMap getCommandMap() { - try { - return (SimpleCommandMap) Class.forName("org.bukkit.craftbukkit." + Bukkit.getServer().getClass().getPackage().getName().substring(23) + ".CraftServer").getDeclaredMethod("getCommandMap").invoke(Bukkit.getServer()); - } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException | ClassNotFoundException ex) { - ex.printStackTrace(); - return null; - } - } - - public static void registerCmd(Command command) { - CommandMap commandMap = getCommandMap(); - if (commandMap != null) - commandMap.register("betterstaffchat", command); - if (!command.isRegistered()) throw new NullPointerException("CommandMap is null"); - } -} \ No newline at end of file diff --git a/BetterStaffChat-spigot/src/main/java/dev/austech/betterstaffchat/spigot/util/Config.java b/BetterStaffChat-spigot/src/main/java/dev/austech/betterstaffchat/spigot/util/Config.java deleted file mode 100644 index 0bb0642..0000000 --- a/BetterStaffChat-spigot/src/main/java/dev/austech/betterstaffchat/spigot/util/Config.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * BetterStaffChat - Config.java - * Copyright (C) 2021 AusTech Development Team - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package dev.austech.betterstaffchat.spigot.util; - -import dev.austech.betterstaffchat.spigot.BetterStaffChatSpigot; -import lombok.experimental.UtilityClass; -import org.bukkit.configuration.InvalidConfigurationException; -import org.bukkit.configuration.file.YamlConfiguration; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.nio.file.Files; - -@UtilityClass -public class Config { - public void load() { - BetterStaffChatSpigot.getInstance().getDataFolder().mkdir(); - - File file = new File(BetterStaffChatSpigot.getInstance().getPluginDataFolder(), "config.yml"); - - if (!file.exists()) { - try (InputStream inputStream = BetterStaffChatSpigot.getInstance().getClass().getResourceAsStream("/config.yml")) { - Files.copy(inputStream, file.toPath()); - } catch (IOException e) { - e.printStackTrace(); - } - } - - YamlConfiguration config = new YamlConfiguration(); - - try { - config.load(file); - - int currentVersion = YamlConfiguration.loadConfiguration(new InputStreamReader(BetterStaffChatSpigot.getInstance().getClass().getResourceAsStream("/config.yml"))).getInt("config-version"); - - if (config.getInt("config-version") != currentVersion) - newVersion(config.getInt("config-version")); - else - BetterStaffChatSpigot.getInstance().setConfig(config); - } catch (InvalidConfigurationException exception) { - exception.printStackTrace(); - File broken = new File(file.getAbsolutePath() + ".broken." + System.currentTimeMillis()); - file.renameTo(broken); - BetterStaffChatSpigot.getInstance().logPrefix("&cThe config file is broken, and has been renamed to config.yml.broken." + System.currentTimeMillis()); - - try (InputStream in = BetterStaffChatSpigot.getInstance().getClass().getResourceAsStream("/config.yml")) { - Files.copy(in, file.toPath()); - } catch (IOException e) { - e.printStackTrace(); - } - - BetterStaffChatSpigot.getInstance().setConfig(YamlConfiguration.loadConfiguration(file)); - } catch (IOException e) { - e.printStackTrace(); - } - } - - private void newVersion(int ver) throws IOException { - File file = new File(BetterStaffChatSpigot.getInstance().getPluginDataFolder(), "config.yml"); - - File broken = new File(file.getAbsolutePath() + ".old." + ver); - file.renameTo(broken); - BetterStaffChatSpigot.getInstance().logPrefix("&cThe config file is old, and has been renamed to config.yml.old." + ver); - - try (InputStream inputStream = BetterStaffChatSpigot.getInstance().getClass().getResourceAsStream("/config.yml")) { - Files.copy(inputStream, file.toPath()); - } catch (IOException e) { - e.printStackTrace(); - } - - BetterStaffChatSpigot.getInstance().setConfig(YamlConfiguration.loadConfiguration(new File(BetterStaffChatSpigot.getInstance().getPluginDataFolder(), "config.yml"))); - } -} diff --git a/BetterStaffChat-spigot/src/main/java/dev/austech/betterstaffchat/spigot/util/StaffChatUtil.java b/BetterStaffChat-spigot/src/main/java/dev/austech/betterstaffchat/spigot/util/StaffChatUtil.java deleted file mode 100755 index 7a3417e..0000000 --- a/BetterStaffChat-spigot/src/main/java/dev/austech/betterstaffchat/spigot/util/StaffChatUtil.java +++ /dev/null @@ -1,164 +0,0 @@ -/* - * BetterStaffChat - StaffChatUtil.java - * Copyright (C) 2021 AusTech Development Team - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package dev.austech.betterstaffchat.spigot.util; - -import dev.austech.betterstaffchat.common.discord.DiscordWebhook; -import dev.austech.betterstaffchat.common.util.AbstractStaffChatUtil; -import dev.austech.betterstaffchat.common.util.TextUtil; -import dev.austech.betterstaffchat.spigot.BetterStaffChatSpigot; -import lombok.Getter; -import net.md_5.bungee.api.ChatColor; -import org.bukkit.Bukkit; -import org.bukkit.entity.Player; - -import java.awt.*; -import java.io.IOException; - -public class StaffChatUtil extends AbstractStaffChatUtil { - @Getter private static final StaffChatUtil instance = new StaffChatUtil(); - - public String getFormattedMessage(Object sender, String message) { - message = TextUtil.colorize( - BetterStaffChatSpigot.getInstance().getConfig().getString("staffchat.format") - .replace("%player_name%", (sender instanceof Player) ? ((Player) sender).getName() : BetterStaffChatSpigot.getInstance().getConfig().getString("staffchat.console-replacement")) - .replace("%message%", BetterStaffChatSpigot.getInstance().getConfig().getBoolean("staffchat.strip-color-codes") ? ChatColor.stripColor(TextUtil.colorize(message)) : message) - ); - - if (sender instanceof Player) { - message = TextUtil.colorize(placeholder((Player) sender, message)); - } - return message; - } - - public String placeholder(Player player, String string) { - if (Bukkit.getPluginManager().getPlugin("PlaceholderAPI") != null) { - return me.clip.placeholderapi.PlaceholderAPI.setPlaceholders(player, string); - } else return string; - } - - - /** - * An alternative to {@link Bukkit#broadcast} with support for staffchat toggle and Discord - */ - public void broadcast(String string, String permission) { - for (Player player : Bukkit.getOnlinePlayers()) { - if (!BetterStaffChatSpigot.getInstance().getIgnoreStaffChat().contains(player.getUniqueId()) && player.hasPermission(permission)) { - player.sendMessage(string); - } - } - - if (BetterStaffChatSpigot.getInstance().getConfig().getBoolean("staffchat.log-to-console")) { - Bukkit.getConsoleSender().sendMessage(TextUtil.colorize(string)); - } - } - - public void discord(Object sender, String string) { - if (sender instanceof Player) string = placeholder((Player) sender, string); - string = ChatColor.stripColor(TextUtil.colorize(string)); - String finalString = string; - Bukkit.getScheduler().runTaskAsynchronously(BetterStaffChatSpigot.getInstance(), () -> { - - if (BetterStaffChatSpigot.getInstance().getConfig().getBoolean("discord.webhook.enabled")) { - DiscordWebhook webhook = new DiscordWebhook(BetterStaffChatSpigot.getInstance().getConfig().getString("discord.webhook.url")); - - if (BetterStaffChatSpigot.getInstance().getConfig().getBoolean("discord.discord-messages.embed.enabled")) { - webhook.addEmbed(generateEmbed(sender, finalString)); - } else { - webhook.setContent(finalString); - } - - try { - webhook.execute(); - } catch (IOException exception) { - exception.printStackTrace(); - BetterStaffChatSpigot.getInstance().logPrefix("&cFailed to send Discord webhook."); - } - } - else if (BetterStaffChatSpigot.getInstance().getConfig().getBoolean("discord.bot.enabled")) { - if (BetterStaffChatSpigot.getInstance().getConfig().getBoolean("discord.discord-messages.embed.enabled")) { - for (String guildChannelPair : BetterStaffChatSpigot.getInstance().getConfig().getStringList("discord.bot.channels")) { - String[] parts = guildChannelPair.split(": "); - BetterStaffChatSpigot.getInstance().getJda().sendEmbed(parts[0], parts[1], generateEmbed(sender, finalString)); - } - } else { - for (String guildChannelPair : BetterStaffChatSpigot.getInstance().getConfig().getStringList("discord.bot.channels")) { - String[] parts = guildChannelPair.split(": "); - BetterStaffChatSpigot.getInstance().getJda().sendMessage(parts[0], parts[1], finalString); - } - } - } - }); - } - - @Override - public void handleDiscord(Object event) { - if (BetterStaffChatSpigot.getInstance().getConfig().getStringList("discord.bot.channels").stream().anyMatch(a -> a.equals(((net.dv8tion.jda.api.events.message.guild.GuildMessageReceivedEvent) event).getGuild().getId() + ": " + ((net.dv8tion.jda.api.events.message.guild.GuildMessageReceivedEvent) event).getChannel().getId()))) { - StringBuilder builder = new StringBuilder(); - - StringBuilder discordMessage = new StringBuilder(((net.dv8tion.jda.api.events.message.guild.GuildMessageReceivedEvent) event).getMessage().getContentStripped().trim()); - - if (!((net.dv8tion.jda.api.events.message.guild.GuildMessageReceivedEvent) event).getMessage().getAttachments().isEmpty()) { - for (net.dv8tion.jda.api.entities.Message.Attachment attachment : ((net.dv8tion.jda.api.events.message.guild.GuildMessageReceivedEvent) event).getMessage().getAttachments()) { - discordMessage.append(" ").append(attachment.getUrl()); - } - } - - String nickName = ((net.dv8tion.jda.api.events.message.guild.GuildMessageReceivedEvent) event).getMember().getNickname(); - - String name = BetterStaffChatSpigot.getInstance().getConfig().getString("discord.discord-messages.name-format") - .replace("%username%", ((net.dv8tion.jda.api.events.message.guild.GuildMessageReceivedEvent) event).getAuthor().getName()) - .replace("%discriminator%", ((net.dv8tion.jda.api.events.message.guild.GuildMessageReceivedEvent) event).getAuthor().getDiscriminator()) - .replace("%nickname%", nickName != null ? nickName : ((net.dv8tion.jda.api.events.message.guild.GuildMessageReceivedEvent) event).getAuthor().getName()); - - String message = BetterStaffChatSpigot.getInstance().getConfig().getString("staffchat.format") - .replace("%player_name%", name) - .replace("%message%", discordMessage.toString()) - .replace("%server%", "Discord") - .replaceAll("%\\S*%", ""); - - - for (String word : message.split(" ")) { - builder.append(ChatColor.translateAlternateColorCodes('&', word)); - builder.append(" "); - } - - broadcast(builder.substring(0, builder.length() - 1), "betterstaffchat.messages.read"); - } - } - - protected DiscordWebhook.EmbedObject generateEmbed(Object sender, String string) { - DiscordWebhook.EmbedObject embed = new DiscordWebhook.EmbedObject(); - embed.setColor(Color.decode(BetterStaffChatSpigot.getInstance().getConfig().getString("discord.discord-messages.embed.embed-color"))); - embed.setDescription(string); - - String footer = BetterStaffChatSpigot.getInstance().getConfig().getString("discord.discord-messages.embed.embed-footer") - .replace("%player_name%", (sender instanceof Player) ? TextUtil.cleanForDiscord(((Player) sender).getName()) : BetterStaffChatSpigot.getInstance().getConfig().getString("staffchat.console-replacement")) - .replace("%uuid%", (sender instanceof Player) ? ((Player) sender).getUniqueId().toString() : BetterStaffChatSpigot.getInstance().getConfig().getString("staffchat.console-uuid-replacement")); - if (sender instanceof Player) footer = ChatColor.stripColor(TextUtil.colorize(placeholder(((Player) sender), footer))); - - String icon = BetterStaffChatSpigot.getInstance().getConfig().getString("discord.discord-messages.embed.embed-footer-icon").replace("%player_name%", (sender instanceof Player) ? TextUtil.cleanForDiscord(((Player) sender).getName()) : BetterStaffChatSpigot.getInstance().getConfig().getString("staffchat.console-replacement")) - .replace("%uuid%", (sender instanceof Player) ? ((Player) sender).getUniqueId().toString() : BetterStaffChatSpigot.getInstance().getConfig().getString("staffchat.console-uuid-replacement")); - if (sender instanceof Player) icon = ChatColor.stripColor(TextUtil.colorize(placeholder(((Player) sender), icon))); - - if (!BetterStaffChatSpigot.getInstance().getConfig().getString("discord.discord-messages.embed.embed-footer").equals("")) { - embed.setFooter(footer, icon); - } else embed.setFooter(footer, null); - return embed; - } -} diff --git a/BetterStaffChat-spigot/src/main/resources/plugin.yml b/BetterStaffChat-spigot/src/main/resources/plugin.yml deleted file mode 100644 index c5a88f2..0000000 --- a/BetterStaffChat-spigot/src/main/resources/plugin.yml +++ /dev/null @@ -1,4 +0,0 @@ -name: BetterStaffChat -version: @version@ -main: dev.austech.betterstaffchat.spigot.BetterStaffChatSpigot -author: AusTechDev \ No newline at end of file diff --git a/build.gradle b/build.gradle deleted file mode 100755 index 44387de..0000000 --- a/build.gradle +++ /dev/null @@ -1,158 +0,0 @@ -/* - * BetterStaffChat - build.gradle - * Copyright (C) 2021 AusTech Development Team - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - - -import org.apache.tools.ant.filters.ReplaceTokens -import org.eclipse.jgit.lib.Ref -import org.eclipse.jgit.lib.Repository -import org.eclipse.jgit.revwalk.RevWalk - -plugins { - id 'java' - id 'org.ajoberstar.grgit' version '4.1.1' - id 'com.github.johnrengelman.shadow' version '7.0.0' - id "io.freefair.lombok" version "6.0.0-m2" - id 'net.kyori.indra' version "2.0.4" apply false - id 'net.kyori.indra.git' version "2.0.4" -} - -group = 'dev.austech' -version = '1.1.2-SNAPSHOT' - -// https://github.com/EssentialsX/Essentials/blob/2.x/build.gradle -@SuppressWarnings('GrMethodMayBeStatic') -def commitsSinceLastTag() { - if (indraGit == null || !indraGit.isPresent() || indraGit.tags().isEmpty()) { - return -1 - } - def tags = indraGit.tags() - def depth = 0 - def walk = new RevWalk(indraGit.git().getRepository()) - def commit = walk.parseCommit(indraGit.commit()) - while (true) { - for (tag in tags) { - if (walk.parseCommit(tag.getLeaf().getObjectId()) == commit) { - walk.dispose() - indraGit.git().close() - return depth - } - } - depth++ - commit = walk.parseCommit(commit.getParents()[0]) - } -} - -@SuppressWarnings('GrMethodMayBeStatic') -def headBranchName() { - if (System.getenv("GITHUB_HEAD_REF") != null && !System.getenv("GITHUB_HEAD_REF").isEmpty()) { - return System.getenv("GITHUB_HEAD_REF") - } else if (System.getenv("GITHUB_REF") != null && !System.getenv("GITHUB_REF").isEmpty()) { - return System.getenv("GITHUB_REF").replaceFirst("refs/heads/", "") - } - - if (!indraGit.isPresent()) { - return "detached-head" - } - - Ref ref = indraGit.git().getRepository().exactRef('HEAD')?.target - if (ref == null) { - return "detached-head" - } - - return Repository.shortenRefName(ref.name) -} - -ext { - GIT_COMMIT = !indraGit.isPresent() ? "unknown" : indraGit.commit().abbreviate(7).name() - GIT_DEPTH = commitsSinceLastTag() - GIT_BRANCH = headBranchName() - - fullVersion = "${version}".replace("-SNAPSHOT", "-dev+${GIT_DEPTH}-${GIT_COMMIT}") -} - -subprojects { - apply plugin: 'java' - apply plugin: 'idea' - apply plugin: 'com.github.johnrengelman.shadow' - apply plugin: "io.freefair.lombok" - - group = project.group - version = project.version - - sourceCompatibility = '1.8' - targetCompatibility = '1.8' - - repositories { - mavenCentral() - maven { url 'https://m2.dv8tion.net/releases' } - } - - dependencies { - compileOnly 'net.dv8tion:JDA:4.2.1_262' - compileOnly 'org.projectlombok:lombok:1.18.20' - } - - processResources { - inputs.property('fullVersion', fullVersion) - filter ReplaceTokens, tokens: [ - "version": fullVersion - ] - } - - tasks.withType(JavaCompile) { - options.encoding = 'UTF-8' - } - - tasks.withType(Jar) { - archiveVersion.set(fullVersion) - } -} - -repositories { - mavenCentral() -} - -dependencies { - implementation project(path: ':BetterStaffChat-common', configuration: 'shadow') - implementation project(path: ':BetterStaffChat-spigot', configuration: 'shadow') - implementation project(path: ':BetterStaffChat-bungeecord', configuration: 'shadow') -} - -shadowJar { - relocate 'net.dv8tion.jda', 'dev.austech.betterstaffchat.shaded.jda' - relocate 'org.bstats', 'dev.austech.betterstaffchat.shaded.metrics' -} - -task copyJars(type: Copy) { - outputTasks().forEach({ from(it) }) - rename '(.*)-all.jar', ('$1' + '.jar') - rename project.name + "-" + project.version + ".jar", project.name + "-" + project.fullVersion + ".jar" - into file('jars') -} - -def outputTasks() { - ["shadowJar", ":BetterStaffChat-spigot:shadowJar", ":BetterStaffChat-bungeecord:shadowJar"].stream().map({ tasks.findByPath(it) }) -} - -task cleanJars() { - delete file('jars') -} - -clean.dependsOn cleanJars -build.dependsOn shadowJar -build.dependsOn copyJars diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 0000000..86ccd15 --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,115 @@ +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile + +plugins { + java + kotlin("jvm") version "1.6.10" apply false + id("io.freefair.lombok") version "6.0.0-m2" + id("com.github.johnrengelman.shadow") version "7.0.0" +} + +group = "dev.austech" +version = "2.0.0-SNAPSHOT" + +repositories { + mavenCentral() + gradlePluginPortal() +} + +dependencies { + implementation(project(":common", "shadow")) + implementation(project(":spigot", "shadow")) + implementation(project(":bungeecord", "shadow")) +} + +tasks { + withType { + expand("version" to project.version) + } +} + +subprojects { + apply(plugin = "java") + apply(plugin = "idea") + apply(plugin = "kotlin") + apply(plugin = "com.github.johnrengelman.shadow") + apply(plugin = "io.freefair.lombok") + + group = project.group + version = project.version + + repositories { + mavenCentral() + maven { + url = uri("https://jitpack.io") + } + maven { + url = uri("https://repo.alessiodp.com/releases/") + } + } + + dependencies { + compileOnly("org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.6.10") + compileOnly("com.github.simplix-softworks:SimplixStorage:3.2.3") + compileOnly("net.kyori:adventure-api:4.10.1") + compileOnly("net.kyori:adventure-text-minimessage:4.10.1") + implementation("com.squareup.okio:okio:3.0.0") { + exclude("org.jetbrains.kotlin") + } + compileOnly("net.dv8tion:JDA:5.0.0-alpha.9") { + exclude("opus-java") + } + } + + sourceSets { + main { + java.srcDir("src/main/kotlin") + resources.srcDir("src/main/resources") + } + } + + java { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + + tasks { + withType { + kotlinOptions { + jvmTarget = "1.8" + } + } + } +} + +tasks.named("shadowJar") { + relocate("net.kyori.adventure", "dev.austech.betterstaffchat.depend.adventure") + relocate("de.leonhard.storage", "dev.austech.betterstaffchat.depend.storage") + relocate("kotlin", "dev.austech.betterstaffchat.depend.kotlin") + relocate("net.dv8tion.jda", "dev.austech.betterstaffchat.depend.discord") + relocate("okio", "dev.austech.betterstaffchat.depend.okio") +} + +task("copyJars") { + outputTasks().forEach { from(it) } + rename("(.*)-all.jar", "$1.jar") + into("jars") +} + +fun outputTasks(): List { + return arrayOf("shadowJar", ":common:shadowJar", ":spigot:shadowJar", ":bungeecord:shadowJar").map { + tasks.findByPath(it) + } +} + +task("cleanJars") { + delete("jars") +} + +tasks.named("clean") { + dependsOn("cleanJars") +} + +tasks.named("build") { + dependsOn("shadowJar") + dependsOn("copyJars") +} \ No newline at end of file diff --git a/bungeecord/build.gradle.kts b/bungeecord/build.gradle.kts new file mode 100644 index 0000000..4121dc3 --- /dev/null +++ b/bungeecord/build.gradle.kts @@ -0,0 +1,11 @@ +repositories { + maven { + url = uri("https://oss.sonatype.org/content/repositories/snapshots/") + } +} + +dependencies { + compileOnly(project(":common")) + compileOnly("net.md-5:bungeecord-api:1.18-R0.1-SNAPSHOT") + compileOnly("net.kyori:adventure-platform-bungeecord:4.1.0") +} \ No newline at end of file diff --git a/bungeecord/src/main/java/dev/austech/betterstaffchat/bungeecord/BSCBungeeBootstrap.java b/bungeecord/src/main/java/dev/austech/betterstaffchat/bungeecord/BSCBungeeBootstrap.java new file mode 100644 index 0000000..37a3ef2 --- /dev/null +++ b/bungeecord/src/main/java/dev/austech/betterstaffchat/bungeecord/BSCBungeeBootstrap.java @@ -0,0 +1,29 @@ +package dev.austech.betterstaffchat.bungeecord; + +import dev.austech.betterstaffchat.bungeecord.libby.BungeeLibraryManager; +import dev.austech.betterstaffchat.common.libraries.KotlinLibraries; +import dev.austech.betterstaffchat.common.libraries.libby.LibraryManager; +import net.md_5.bungee.api.plugin.Plugin; + +public class BSCBungeeBootstrap extends Plugin { + private BSCBungee implPlugin; + + @Override + public void onLoad() { + LibraryManager libraryManager = new BungeeLibraryManager(this); + libraryManager.addMavenCentral(); + libraryManager.addJitPack(); + + libraryManager.loadLibrary(KotlinLibraries.STDLIB); + libraryManager.loadLibrary(KotlinLibraries.STDLIB_COMMON); + libraryManager.loadLibrary(KotlinLibraries.STDLIB_JDK8); + + implPlugin = new BSCBungee(this); + implPlugin.onLoad(); + } + + @Override + public void onEnable() { + implPlugin.onEnable(); + } +} diff --git a/bungeecord/src/main/java/dev/austech/betterstaffchat/bungeecord/libby/BungeeLibraryManager.java b/bungeecord/src/main/java/dev/austech/betterstaffchat/bungeecord/libby/BungeeLibraryManager.java new file mode 100644 index 0000000..854a279 --- /dev/null +++ b/bungeecord/src/main/java/dev/austech/betterstaffchat/bungeecord/libby/BungeeLibraryManager.java @@ -0,0 +1,51 @@ +package dev.austech.betterstaffchat.bungeecord.libby; + +import dev.austech.betterstaffchat.common.libraries.libby.LibraryManager; +import dev.austech.betterstaffchat.common.libraries.libby.classloader.URLClassLoaderHelper; +import dev.austech.betterstaffchat.common.libraries.libby.logging.adapters.JDKLogAdapter; +import net.md_5.bungee.api.plugin.Plugin; + +import java.net.URLClassLoader; +import java.nio.file.Path; + +import static java.util.Objects.requireNonNull; + +/** + * A runtime dependency manager for Bungee plugins. + */ +public class BungeeLibraryManager extends LibraryManager { + /** + * Plugin classpath helper + */ + private final URLClassLoaderHelper classLoader; + + /** + * Creates a new Bungee library manager. + * + * @param plugin the plugin to manage + */ + public BungeeLibraryManager(Plugin plugin) { + this(plugin, "lib"); + } + + /** + * Creates a new Bungee library manager. + * + * @param plugin the plugin to manage + * @param directoryName download directory name + */ + public BungeeLibraryManager(Plugin plugin, String directoryName) { + super(new JDKLogAdapter(requireNonNull(plugin, "plugin").getLogger()), plugin.getDataFolder().toPath(), directoryName); + classLoader = new URLClassLoaderHelper((URLClassLoader) plugin.getClass().getClassLoader(), this); + } + + /** + * Adds a file to the Bungee plugin's classpath. + * + * @param file the file to add + */ + @Override + protected void addToClasspath(Path file) { + classLoader.addToClasspath(file); + } +} diff --git a/bungeecord/src/main/kotlin/dev/austech/betterstaffchat/bungeecord/BSCBungee.kt b/bungeecord/src/main/kotlin/dev/austech/betterstaffchat/bungeecord/BSCBungee.kt new file mode 100644 index 0000000..cf43738 --- /dev/null +++ b/bungeecord/src/main/kotlin/dev/austech/betterstaffchat/bungeecord/BSCBungee.kt @@ -0,0 +1,62 @@ +package dev.austech.betterstaffchat.bungeecord + +import de.leonhard.storage.Json +import de.leonhard.storage.Yaml +import dev.austech.betterstaffchat.bungeecord.commands.CommandManager +import dev.austech.betterstaffchat.bungeecord.libby.BungeeLibraryManager +import dev.austech.betterstaffchat.bungeecord.listeners.PlayerListener +import dev.austech.betterstaffchat.bungeecord.util.PlayerUtil +import dev.austech.betterstaffchat.common.BSCPlugin +import dev.austech.betterstaffchat.common.discord.DiscordManager +import dev.austech.betterstaffchat.common.libraries.BSCLibraries +import dev.austech.betterstaffchat.common.util.Config +import dev.austech.betterstaffchat.common.util.Data +import dev.austech.betterstaffchat.common.util.IPlayerUtil +import dev.austech.betterstaffchat.common.util.StaffChatUtil +import net.kyori.adventure.audience.Audience +import net.kyori.adventure.platform.bungeecord.BungeeAudiences +import net.md_5.bungee.api.ProxyServer +import net.md_5.bungee.api.plugin.Plugin +import java.io.File + +class BSCBungee(private val plugin: Plugin): BSCPlugin { + companion object { + lateinit var instance: BSCBungee + } + + override val platform: BSCPlugin.Platform = BSCPlugin.Platform.BUNGEECORD + override val pluginDataFile: File = plugin.dataFolder + override lateinit var config: Yaml + override lateinit var consoleAudience: Audience + override lateinit var staffChatUtil: StaffChatUtil + override val playerUtil: IPlayerUtil = PlayerUtil + override val discordManager: DiscordManager + get() = TODO("Not yet implemented") + lateinit var audience: BungeeAudiences + private lateinit var data: Json + lateinit var dataWrapper: Data.Wrapper; + + override fun onLoad() { + val libraryManager = BungeeLibraryManager(plugin) + libraryManager.addMavenCentral() + libraryManager.addJitPack() + + BSCLibraries.getLibraries(platform).forEach { + libraryManager.loadLibrary(it) + } + } + + override fun onEnable() { + instance = this + audience = BungeeAudiences.create(plugin) + consoleAudience = audience.console() + config = Config(this).load() + staffChatUtil = StaffChatUtil(this) + data = Data(this).load() + dataWrapper = Data.Wrapper(data) + + CommandManager(this).registerCommands(plugin) + + ProxyServer.getInstance().pluginManager.registerListener(plugin, PlayerListener(plugin)) + } +} \ No newline at end of file diff --git a/bungeecord/src/main/kotlin/dev/austech/betterstaffchat/bungeecord/commands/BSCBungeeCommand.kt b/bungeecord/src/main/kotlin/dev/austech/betterstaffchat/bungeecord/commands/BSCBungeeCommand.kt new file mode 100644 index 0000000..26dad65 --- /dev/null +++ b/bungeecord/src/main/kotlin/dev/austech/betterstaffchat/bungeecord/commands/BSCBungeeCommand.kt @@ -0,0 +1,60 @@ +package dev.austech.betterstaffchat.bungeecord.commands + +import dev.austech.betterstaffchat.bungeecord.BSCBungee +import net.md_5.bungee.api.ChatColor +import net.md_5.bungee.api.CommandSender +import net.md_5.bungee.api.chat.TextComponent +import net.md_5.bungee.api.connection.ProxiedPlayer +import net.md_5.bungee.api.plugin.Command + +abstract class BSCBungeeCommand(name: String, permission: String?, aliases: List): Command(name, permission, *aliases.toTypedArray()) { + private lateinit var sender: CommandSender + protected lateinit var plugin: BSCBungee + + override fun execute(sender: CommandSender?, args: Array?) { + requireNotNull(sender) { "Sender cannot be null" } + + this.sender = sender + this.plugin = BSCBungee.instance + + run(sender, args ?: arrayOf()) + } + + abstract fun run(sender: CommandSender, args: Array) + + fun CommandSender.isConsole(): Boolean { + return this !is ProxiedPlayer + } + + fun CommandSender.requirePlayer(): Boolean { + if (this.isConsole()) { + errorTell("You must be a player to use this command.") + return false + } + return true + } + + fun CommandSender.requirePermission(permission: String): Boolean { + return requirePermission(permission, "You do not have permission.") + } + + fun CommandSender.requirePermission(permission: String, message: String): Boolean { + if (!hasPermission(permission)) { + errorTell(message) + return false + } + return true + } + + fun legacyTell(s: String) { + return this.sender.sendMessage(TextComponent(*TextComponent.fromLegacyText(ChatColor.translateAlternateColorCodes('&', s)))) + } + + fun errorTell(s: String) { + return legacyTell("&c$s") + } + + fun warnTell(s: String) { + legacyTell("&e$s") + } +} \ No newline at end of file diff --git a/bungeecord/src/main/kotlin/dev/austech/betterstaffchat/bungeecord/commands/CommandManager.kt b/bungeecord/src/main/kotlin/dev/austech/betterstaffchat/bungeecord/commands/CommandManager.kt new file mode 100644 index 0000000..c087902 --- /dev/null +++ b/bungeecord/src/main/kotlin/dev/austech/betterstaffchat/bungeecord/commands/CommandManager.kt @@ -0,0 +1,35 @@ +package dev.austech.betterstaffchat.bungeecord.commands + +import dev.austech.betterstaffchat.bungeecord.BSCBungee +import dev.austech.betterstaffchat.bungeecord.commands.impl.BetterStaffChatCommand +import dev.austech.betterstaffchat.bungeecord.commands.impl.StaffChatCommand +import dev.austech.betterstaffchat.bungeecord.commands.impl.StaffChatMuteCommand +import dev.austech.betterstaffchat.bungeecord.commands.impl.StaffChatToggleCommand +import dev.austech.betterstaffchat.common.util.Config +import net.md_5.bungee.api.ProxyServer +import net.md_5.bungee.api.plugin.Command +import net.md_5.bungee.api.plugin.Plugin + +class CommandManager(private val plugin: BSCBungee) { + private val commands: List = listOf( + StaffChatCommand( + plugin.config.getString(Config.Paths.Command.STAFFCHAT_COMMAND.toString())?: "staffchat", + plugin.config.getStringList(Config.Paths.Command.STAFFCHAT_ALIASES.toString()) + ), + StaffChatMuteCommand( + plugin.config.getString(Config.Paths.Command.MUTE_COMMAND.toString())?: "mutestaffchat", + plugin.config.getStringList(Config.Paths.Command.MUTE_ALIASES.toString()) + ), + StaffChatToggleCommand( + plugin.config.getString(Config.Paths.Command.TOGGLE_COMMAND.toString())?: "togglestaffchat", + plugin.config.getStringList(Config.Paths.Command.TOGGLE_ALIASES.toString()) + ), + BetterStaffChatCommand() + ) + + fun registerCommands(plugin: Plugin) { + commands.forEach { + ProxyServer.getInstance().pluginManager.registerCommand(plugin, it) + } + } +} \ No newline at end of file diff --git a/bungeecord/src/main/kotlin/dev/austech/betterstaffchat/bungeecord/commands/impl/BetterStaffChatCommand.kt b/bungeecord/src/main/kotlin/dev/austech/betterstaffchat/bungeecord/commands/impl/BetterStaffChatCommand.kt new file mode 100644 index 0000000..65c81a0 --- /dev/null +++ b/bungeecord/src/main/kotlin/dev/austech/betterstaffchat/bungeecord/commands/impl/BetterStaffChatCommand.kt @@ -0,0 +1,21 @@ +package dev.austech.betterstaffchat.bungeecord.commands.impl + +import dev.austech.betterstaffchat.bungeecord.commands.BSCBungeeCommand +import net.md_5.bungee.api.CommandSender + +class BetterStaffChatCommand: BSCBungeeCommand("betterstaffchat", null, listOf("bsc")) { + override fun run(sender: CommandSender, args: Array) { + when (args[0]) { + "reload" -> { + if (!sender.requirePermission("betterstaffchat.reload")) return + warnTell("It is not recommended to forcefully reload BetterStaffChat. It will automatically reload when the config is edited.") + plugin.config.forceReload() + } + else -> { + legacyTell("&7Running &9BetterStaffChat &7by &6AusTech Development&7.") + } + } + + return + } +} \ No newline at end of file diff --git a/bungeecord/src/main/kotlin/dev/austech/betterstaffchat/bungeecord/commands/impl/StaffChatCommand.kt b/bungeecord/src/main/kotlin/dev/austech/betterstaffchat/bungeecord/commands/impl/StaffChatCommand.kt new file mode 100644 index 0000000..c35d60e --- /dev/null +++ b/bungeecord/src/main/kotlin/dev/austech/betterstaffchat/bungeecord/commands/impl/StaffChatCommand.kt @@ -0,0 +1,45 @@ +package dev.austech.betterstaffchat.bungeecord.commands.impl + +import dev.austech.betterstaffchat.bungeecord.commands.BSCBungeeCommand +import dev.austech.betterstaffchat.bungeecord.util.PlayerUtil +import dev.austech.betterstaffchat.common.BSCMetadata +import net.md_5.bungee.api.CommandSender +import net.md_5.bungee.api.connection.ProxiedPlayer +import java.util.* + +class StaffChatCommand(name: String, aliases: List) : + BSCBungeeCommand(name, "betterstaffchat.messages.send", aliases) { + override fun run(sender: CommandSender, args: Array) { + if (args.isEmpty()) { + errorTell("Usage: /$name ") + return; + } + + if (sender is ProxiedPlayer) { + if (plugin.dataWrapper.getMutedPlayers().contains(sender.uniqueId)) { + errorTell("Your staffchat is currently muted.") + return; + } + + if (PlayerUtil.isDisabledServer(sender.server.info)) { + errorTell("You cannot use staffchat on this server.") + return; + } + } + + + plugin.staffChatUtil.sendMessage( + PlayerUtil.getReceiveAudience(), args.joinToString(separator = " "), + BSCMetadata( + BSCMetadata.Player( + if (sender is ProxiedPlayer) sender.uniqueId else null, + PlayerUtil.getSenderName(sender) + ), + BSCMetadata.Proxy( + PlayerUtil.getServer(sender), + PlayerUtil.getRawServer(sender) + ) + ) + ) + } +} \ No newline at end of file diff --git a/bungeecord/src/main/kotlin/dev/austech/betterstaffchat/bungeecord/commands/impl/StaffChatMuteCommand.kt b/bungeecord/src/main/kotlin/dev/austech/betterstaffchat/bungeecord/commands/impl/StaffChatMuteCommand.kt new file mode 100644 index 0000000..4e4d341 --- /dev/null +++ b/bungeecord/src/main/kotlin/dev/austech/betterstaffchat/bungeecord/commands/impl/StaffChatMuteCommand.kt @@ -0,0 +1,64 @@ +package dev.austech.betterstaffchat.bungeecord.commands.impl + +import dev.austech.betterstaffchat.bungeecord.commands.BSCBungeeCommand +import dev.austech.betterstaffchat.bungeecord.util.PlayerUtil +import dev.austech.betterstaffchat.common.util.Config +import net.md_5.bungee.api.CommandSender +import net.md_5.bungee.api.connection.ProxiedPlayer + +class StaffChatMuteCommand(name: String, aliases: List): BSCBungeeCommand(name, "betterstaffchat.mutestaffchat", aliases) { + override fun run(sender: CommandSender, args: Array) { + if (!plugin.config.getBoolean(Config.Paths.Staffchat.MUTE_ENABLED.toString())) { + errorTell("Staffchat mute is not enabled.") + return + } + + if (sender.requirePlayer()) { + return + } + + if (sender !is ProxiedPlayer) { + throw IllegalStateException("Sender is not a player!") + } + + if (PlayerUtil.isDisabledServer(sender.server.info)) { + errorTell("You cannot use staffchat on this server.") + return; + } + + val mute: Boolean + val mutedPlayers = plugin.dataWrapper.getMutedPlayers() + + if (args.isNotEmpty()) { + if (args[0].equals("off", true)) { + if (!mutedPlayers.contains(sender.uniqueId)) { + errorTell("Your staffchat is not muted.") + return + } + + mute = false + } else if (args[0].equals("on", true)) { + if (mutedPlayers.contains(sender.uniqueId)) { + errorTell("Your staffchat is already muted.") + return + } + + mute = true + } else { + mute = !mutedPlayers.contains(sender.uniqueId) + } + } else { + mute = !mutedPlayers.contains(sender.uniqueId) + } + + if (mute) { + plugin.dataWrapper.addMutedPlayer(sender.uniqueId) + legacyTell(plugin.config.getString(Config.Paths.Staffchat.MUTE_MESSAGE_ON.toString())) + } else { + plugin.dataWrapper.removeMutedPlayer(sender.uniqueId) + legacyTell(plugin.config.getString(Config.Paths.Staffchat.MUTE_MESSAGE_OFF.toString())) + } + + return + } +} \ No newline at end of file diff --git a/bungeecord/src/main/kotlin/dev/austech/betterstaffchat/bungeecord/commands/impl/StaffChatToggleCommand.kt b/bungeecord/src/main/kotlin/dev/austech/betterstaffchat/bungeecord/commands/impl/StaffChatToggleCommand.kt new file mode 100644 index 0000000..5813795 --- /dev/null +++ b/bungeecord/src/main/kotlin/dev/austech/betterstaffchat/bungeecord/commands/impl/StaffChatToggleCommand.kt @@ -0,0 +1,71 @@ +package dev.austech.betterstaffchat.bungeecord.commands.impl + +import dev.austech.betterstaffchat.bungeecord.commands.BSCBungeeCommand +import dev.austech.betterstaffchat.bungeecord.util.PlayerUtil +import dev.austech.betterstaffchat.common.util.Config +import net.md_5.bungee.api.CommandSender +import net.md_5.bungee.api.connection.ProxiedPlayer + +class StaffChatToggleCommand(name: String, aliases: List): BSCBungeeCommand(name, "betterstaffchat.mutestaffchat", aliases) { + override fun run(sender: CommandSender, args: Array) { + if (!plugin.config.getBoolean(Config.Paths.Staffchat.TOGGLE_ENABLED.toString())) { + errorTell("Staffchat toggle is not enabled.") + return + } + + if (sender.requirePlayer()) { + return + } + + if (sender !is ProxiedPlayer) { + throw IllegalStateException("Sender is not a player!") + } + + if (PlayerUtil.isDisabledServer(sender.server.info)) { + errorTell("You cannot use staffchat on this server.") + return; + } + + val toggleOn: Boolean + val toggledPlayers = plugin.dataWrapper.getToggledPlayers() + + if (args.isNotEmpty()) { + if (args[0].equals("off", true)) { + if (!toggledPlayers.contains(sender.uniqueId)) { + errorTell("Your staffchat is not toggled.") + return + } + + toggleOn = false + } else if (args[0].equals("on", true)) { + if (toggledPlayers.contains(sender.uniqueId)) { + errorTell("Your staffchat is already toggled.") + return + } + + toggleOn = true + } else { + toggleOn = !toggledPlayers.contains(sender.uniqueId) + } + } else { + toggleOn = !toggledPlayers.contains(sender.uniqueId) + } + + if (toggleOn) { + val mutedPlayers = plugin.dataWrapper.getMutedPlayers() + + if (mutedPlayers.contains(sender.uniqueId)) { + errorTell("Your staffchat is muted. Please unmute it before toggling.") + return + } + + plugin.dataWrapper.addToggledPlayer(sender.uniqueId) + legacyTell(plugin.config.getString(Config.Paths.Staffchat.TOGGLE_MESSAGE_ON.toString())) + } else { + plugin.dataWrapper.removeToggledPlayer(sender.uniqueId) + legacyTell(plugin.config.getString(Config.Paths.Staffchat.TOGGLE_MESSAGE_OFF.toString())) + } + + return + } +} \ No newline at end of file diff --git a/bungeecord/src/main/kotlin/dev/austech/betterstaffchat/bungeecord/listeners/PlayerListener.kt b/bungeecord/src/main/kotlin/dev/austech/betterstaffchat/bungeecord/listeners/PlayerListener.kt new file mode 100644 index 0000000..34f303a --- /dev/null +++ b/bungeecord/src/main/kotlin/dev/austech/betterstaffchat/bungeecord/listeners/PlayerListener.kt @@ -0,0 +1,149 @@ +package dev.austech.betterstaffchat.bungeecord.listeners + +import dev.austech.betterstaffchat.bungeecord.BSCBungee +import dev.austech.betterstaffchat.bungeecord.util.PlayerUtil +import dev.austech.betterstaffchat.common.BSCMetadata +import dev.austech.betterstaffchat.common.util.Config +import net.kyori.adventure.audience.Audience +import net.kyori.adventure.text.Component +import net.kyori.adventure.text.format.NamedTextColor +import net.md_5.bungee.api.ProxyServer +import net.md_5.bungee.api.connection.ProxiedPlayer +import net.md_5.bungee.api.event.ChatEvent +import net.md_5.bungee.api.event.PlayerDisconnectEvent +import net.md_5.bungee.api.event.PostLoginEvent +import net.md_5.bungee.api.event.ServerSwitchEvent +import net.md_5.bungee.api.plugin.Listener +import net.md_5.bungee.api.plugin.Plugin +import net.md_5.bungee.event.EventHandler +import java.util.concurrent.TimeUnit + +class PlayerListener(private val plugin: Plugin): Listener { + @EventHandler + fun onPlayerChat(event: ChatEvent) { + if (event.isCommand || event.isProxyCommand) return + + val player = event.sender as ProxiedPlayer + + if (player.hasPermission("betterstaffchat.messages.send")) { + val prefix = BSCBungee.instance.config.getString(Config.Paths.Staffchat.PREFIX.toString()) ?: ""; + + if (event.message.startsWith(prefix) && event.message.length > prefix.length && prefix.isNotBlank() && !prefix.startsWith("/")) { + if (PlayerUtil.isDisabledServer(player.server.info)) { + BSCBungee.instance.audience.player(player).sendMessage(Component.text("You cannot use staffchat on this server.").color(NamedTextColor.RED)) + return + } + + event.isCancelled = true; + BSCBungee.instance.staffChatUtil.sendMessage(PlayerUtil.getReceiveAudience(), event.message.substring(prefix.length), + BSCMetadata( + BSCMetadata.Player( + player.uniqueId, + player.name + ), + BSCMetadata.Proxy( + PlayerUtil.getServer(player), + PlayerUtil.getRawServer(player) + ) + ) + ) + } else if (BSCBungee.instance.dataWrapper.getToggledPlayers().contains(player.uniqueId) && BSCBungee.instance.config.getBoolean(Config.Paths.Staffchat.TOGGLE_ENABLED.toString())) { + if (PlayerUtil.isDisabledServer(player.server.info)) { + BSCBungee.instance.audience.player(player).sendMessage(Component.text("You cannot use staffchat on this server.").color(NamedTextColor.RED)) + return + } + + event.isCancelled = true; + BSCBungee.instance.staffChatUtil.sendMessage(PlayerUtil.getReceiveAudience(), event.message, + BSCMetadata( + BSCMetadata.Player( + player.uniqueId, + player.name + ), + BSCMetadata.Proxy( + PlayerUtil.getServer(player), + PlayerUtil.getRawServer(player) + ) + ) + ) + } + } + } + + @EventHandler + fun onPlayerJoin(event: PostLoginEvent) { + if (!BSCBungee.instance.config.getBoolean(Config.Paths.Staffchat.EVENTS_JOIN_ENABLED.toString())) return + if (event.player.hasPermission("betterstaffchat.messages.join")) { + if (PlayerUtil.isDisabledServer(event.player.server.info)) { + return + } + + val aud: Audience = if (!BSCBungee.instance.config.getBoolean(Config.Paths.Staffchat.EVENTS_JOIN_SEE_OWN.toString())) + PlayerUtil.getReceiveAudienceWithIgnored(event.player.uniqueId) + else PlayerUtil.getReceiveAudience() + + ProxyServer.getInstance().scheduler.schedule(plugin, { + BSCBungee.instance.staffChatUtil.sendJoinMessage(aud, + BSCMetadata( + BSCMetadata.Player( + event.player.uniqueId, + event.player.name + ), + BSCMetadata.Proxy( + PlayerUtil.getServer(event.player), + PlayerUtil.getRawServer(event.player) + ) + ) + ) + }, 1, TimeUnit.SECONDS) + } + } + + @EventHandler + fun onPlayerQuit(event: PlayerDisconnectEvent) { + if (!BSCBungee.instance.config.getBoolean(Config.Paths.Staffchat.EVENTS_LEAVE_ENABLED.toString())) return + if (event.player.hasPermission("betterstaffchat.messages.leave")) { + if (PlayerUtil.isDisabledServer(event.player.server.info)) { + return + } + + BSCBungee.instance.staffChatUtil.sendLeaveMessage(PlayerUtil.getReceiveAudience(), + BSCMetadata( + BSCMetadata.Player( + event.player.uniqueId, + event.player.name + ), + BSCMetadata.Proxy( + PlayerUtil.getServer(event.player), + PlayerUtil.getRawServer(event.player) + ) + ) + ) + } + } + + @EventHandler + fun onPlayerSwitch(event: ServerSwitchEvent) { + if (!BSCBungee.instance.config.getBoolean(Config.Paths.Staffchat.EVENTS_SWITCH_ENABLED.toString())) return + if (event.player.hasPermission("betterstaffchat.messages.switch")) { + requireNotNull(event.from) { "From server is null! This means you are using an old version of FlameCord or Aegis." } + + if (PlayerUtil.isDisabledServer(event.player.server.info)) { + return + } + + BSCBungee.instance.staffChatUtil.sendSwitchMessage(PlayerUtil.getReceiveAudience(), PlayerUtil.getServerReplacement(event.from), PlayerUtil.getServer(event.player), + BSCMetadata( + BSCMetadata.Player( + event.player.uniqueId, + event.player.name + ), + BSCMetadata.Proxy( + PlayerUtil.getServer(event.player), + PlayerUtil.getRawServer(event.player) + ) + ) + ) + } + } +} \ No newline at end of file diff --git a/bungeecord/src/main/kotlin/dev/austech/betterstaffchat/bungeecord/util/PlayerUtil.kt b/bungeecord/src/main/kotlin/dev/austech/betterstaffchat/bungeecord/util/PlayerUtil.kt new file mode 100644 index 0000000..0427d40 --- /dev/null +++ b/bungeecord/src/main/kotlin/dev/austech/betterstaffchat/bungeecord/util/PlayerUtil.kt @@ -0,0 +1,75 @@ +package dev.austech.betterstaffchat.bungeecord.util + +import dev.austech.betterstaffchat.bungeecord.BSCBungee +import dev.austech.betterstaffchat.common.util.Config +import dev.austech.betterstaffchat.common.util.IPlayerUtil +import net.kyori.adventure.audience.Audience +import net.md_5.bungee.api.CommandSender +import net.md_5.bungee.api.ProxyServer +import net.md_5.bungee.api.config.ServerInfo +import net.md_5.bungee.api.connection.ProxiedPlayer +import java.util.* + +object PlayerUtil: IPlayerUtil { + fun getSenderName(sender: CommandSender): String { + return if (sender is ProxiedPlayer) sender.name + else BSCBungee.instance.config.getString(Config.Paths.Staffchat.CONSOLE_NAME.toString()) + } + + private fun getMutedPlayers(): List { + return BSCBungee.instance.dataWrapper.getMutedPlayers() + } + + private fun getAudienceWithIgnored(permission: String, ignored: UUID) = BSCBungee.instance.audience.filter { + it.hasPermission(permission) && it is ProxiedPlayer && it.uniqueId != ignored && !getMutedPlayers().contains(it.uniqueId) + } + + private fun getAudienceMixedWithIgnored(permission: String, ignored: UUID) = BSCBungee.instance.audience.filter { + it.hasPermission(permission) && if (it is ProxiedPlayer) it.uniqueId != ignored && !getMutedPlayers().contains(it.uniqueId) else true + } + + fun getReceiveAudienceWithIgnored(ignored: UUID): Audience { + return if (BSCBungee.instance.config.getBoolean(Config.Paths.Staffchat.CONSOLE_LOG.toString())) getAudienceMixedWithIgnored( + "betterstaffchat.messages.read", + ignored + ) + else getAudienceWithIgnored("betterstaffchat.messages.read", ignored) + } + + private fun getPermissionAudience(permission: String): Audience = BSCBungee.instance.audience.filter { + it.hasPermission(permission) && it is ProxiedPlayer && !getMutedPlayers().contains(it.uniqueId) + } + + private fun getMixedAudience(permission: String): Audience = BSCBungee.instance.audience.filter { + it.hasPermission(permission) && if (it is ProxiedPlayer) !getMutedPlayers().contains(it.uniqueId) else true + } + + override fun getReceiveAudience(): Audience { + return if (BSCBungee.instance.config.getBoolean(Config.Paths.Staffchat.CONSOLE_LOG.toString())) getMixedAudience( + "betterstaffchat.messages.read" + ) + else getPermissionAudience("betterstaffchat.messages.read") + } + + fun getServer(sender: CommandSender): String { + return if (sender !is ProxyServer) BSCBungee.instance.config.getString(Config.Paths.Staffchat.CONSOLE_SERVER.toString()) + else getServerReplacement((sender as ProxiedPlayer).server.info) + } + + fun getRawServer(sender: CommandSender): String { + return if (sender !is ProxyServer) BSCBungee.instance.config.getString(Config.Paths.Staffchat.CONSOLE_SERVER.toString()) + else (sender as ProxiedPlayer).server.info.name + } + + fun getServerReplacement(info: ServerInfo): String { + BSCBungee.instance.config.getString(Config.Paths.Staffchat.SERVER_REPLACEMENTS.toString() + "." + info.name)?.let { + return it + } + + return info.name + } + + fun isDisabledServer(info: ServerInfo): Boolean { + return BSCBungee.instance.config.getStringList(Config.Paths.Staffchat.DISABLED_SERVERS.toString())?.contains(info.name) ?: false + } +} \ No newline at end of file diff --git a/common/build.gradle.kts b/common/build.gradle.kts new file mode 100644 index 0000000..374aa04 --- /dev/null +++ b/common/build.gradle.kts @@ -0,0 +1 @@ +// Nothing needs to be in this file \ No newline at end of file diff --git a/common/src/main/java/dev/austech/betterstaffchat/common/libraries/KotlinLibraries.java b/common/src/main/java/dev/austech/betterstaffchat/common/libraries/KotlinLibraries.java new file mode 100644 index 0000000..279e065 --- /dev/null +++ b/common/src/main/java/dev/austech/betterstaffchat/common/libraries/KotlinLibraries.java @@ -0,0 +1,24 @@ +package dev.austech.betterstaffchat.common.libraries; + +import dev.austech.betterstaffchat.common.libraries.libby.Library; + +public class KotlinLibraries { + private static final String kotlinString = "_kotlin".replace("_", ""); + + private static final Library.Builder builder = new Library.Builder() + .groupId("org{}jetbrains{}kotlin") + .version("1.6.10") + .relocate(kotlinString, "dev{}austech{}betterstaffchat{}depend{}kotlin"); + + public static final Library STDLIB_JDK8 = builder + .artifactId(kotlinString + "-stdlib-jdk8") // Break here because shadowJar tries to replace it. + .build(); + + public static final Library STDLIB = builder + .artifactId(kotlinString + "-stdlib") + .build(); + + public static final Library STDLIB_COMMON = builder + .artifactId(kotlinString + "-stdlib-common") + .build(); +} diff --git a/common/src/main/java/dev/austech/betterstaffchat/common/libraries/libby/Library.java b/common/src/main/java/dev/austech/betterstaffchat/common/libraries/libby/Library.java new file mode 100644 index 0000000..4967775 --- /dev/null +++ b/common/src/main/java/dev/austech/betterstaffchat/common/libraries/libby/Library.java @@ -0,0 +1,509 @@ +package dev.austech.betterstaffchat.common.libraries.libby; + +import dev.austech.betterstaffchat.common.libraries.libby.relocation.Relocation; + +import java.util.Base64; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedList; +import java.util.UUID; + +import static java.util.Objects.requireNonNull; + +/** + * An immutable representation of a Maven artifact that can be downloaded, + * relocated and then loaded into a plugin's classpath at runtime. + * + * @see #builder() + */ +public class Library { + /** + * Direct download URLs for this library + */ + private final Collection urls; + + /** + * Repository URLs for this library + */ + private final Collection repositories; + + /** + * Library id (used by Isolated Class Loaders) + */ + private final String id; + + /** + * Maven group ID + */ + private final String groupId; + + /** + * Maven artifact ID + */ + private final String artifactId; + + /** + * Artifact version + */ + private final String version; + + /** + * Artifact classifier + */ + private final String classifier; + + /** + * Binary SHA-256 checksum for this library's jar file + */ + private final byte[] checksum; + + /** + * Jar relocations to apply + */ + private final Collection relocations; + + /** + * Relative Maven path to this library's artifact + */ + private final String path; + + /** + * Relative path to this library's relocated jar + */ + private final String relocatedPath; + + /** + * Should this library be loaded in an isolated class loader? + */ + private final boolean isolatedLoad; + + /** + * Creates a new library. + * + * @param urls direct download URLs + * @param id library ID + * @param groupId Maven group ID + * @param artifactId Maven artifact ID + * @param version artifact version + * @param classifier artifact classifier or null + * @param checksum binary SHA-256 checksum or null + * @param relocations jar relocations or null + * @param isolatedLoad isolated load for this library + */ + private Library(Collection urls, + String id, + String groupId, + String artifactId, + String version, + String classifier, + byte[] checksum, + Collection relocations, + boolean isolatedLoad) { + + this(urls, null, id, groupId, artifactId, version, classifier, checksum, relocations, isolatedLoad); + } + + /** + * Creates a new library. + * + * @param urls direct download URLs + * @param repositories repository URLs + * @param id library ID + * @param groupId Maven group ID + * @param artifactId Maven artifact ID + * @param version artifact version + * @param classifier artifact classifier or null + * @param checksum binary SHA-256 checksum or null + * @param relocations jar relocations or null + * @param isolatedLoad isolated load for this library + */ + private Library(Collection urls, + Collection repositories, + String id, + String groupId, + String artifactId, + String version, + String classifier, + byte[] checksum, + Collection relocations, + boolean isolatedLoad) { + + this.urls = urls != null ? Collections.unmodifiableList(new LinkedList<>(urls)) : Collections.emptyList(); + this.id = id != null ? id : UUID.randomUUID().toString(); + this.groupId = requireNonNull(groupId, "groupId").replace("{}", "."); + this.artifactId = requireNonNull(artifactId, "artifactId"); + this.version = requireNonNull(version, "version"); + this.classifier = classifier; + this.checksum = checksum; + this.relocations = relocations != null ? Collections.unmodifiableList(new LinkedList<>(relocations)) : Collections.emptyList(); + + String path = this.groupId.replace('.', '/') + '/' + artifactId + '/' + version + '/' + artifactId + '-' + version; + if (hasClassifier()) { + path += '-' + classifier; + } + + this.path = path + ".jar"; + + this.repositories = repositories != null ? Collections.unmodifiableList(new LinkedList<>(repositories)) : Collections.emptyList(); + relocatedPath = hasRelocations() ? artifactId + '-' + version + "-relocated.jar" : null; + this.isolatedLoad = isolatedLoad; + } + + /** + * Gets the direct download URLs for this library. + * + * @return direct download URLs + */ + public Collection getUrls() { + return urls; + } + + /** + * Gets the repositories URLs for this library. + * + * @return repositories URLs + */ + public Collection getRepositories() { + return repositories; + } + + /** + * Gets the library ID + * + * @return the library id + */ + public String getId() { + return id; + } + + /** + * Gets the Maven group ID for this library. + * + * @return Maven group ID + */ + public String getGroupId() { + return groupId; + } + + /** + * Gets the Maven artifact ID for this library. + * + * @return Maven artifact ID + */ + public String getArtifactId() { + return artifactId; + } + + /** + * Gets the artifact version for this library. + * + * @return artifact version + */ + public String getVersion() { + return version; + } + + /** + * Gets the artifact classifier for this library. + * + * @return artifact classifier or null + */ + public String getClassifier() { + return classifier; + } + + /** + * Gets whether this library has an artifact classifier. + * + * @return true if library has classifier, false otherwise + */ + public boolean hasClassifier() { + return classifier != null; + } + + /** + * Gets the binary SHA-256 checksum of this library's jar file. + * + * @return checksum or null + */ + public byte[] getChecksum() { + return checksum; + } + + /** + * Gets whether this library has a checksum. + * + * @return true if library has checksum, false otherwise + */ + public boolean hasChecksum() { + return checksum != null; + } + + /** + * Gets the jar relocations to apply to this library. + * + * @return jar relocations to apply + */ + public Collection getRelocations() { + return relocations; + } + + /** + * Gets whether this library has any jar relocations. + * + * @return true if library has relocations, false otherwise + */ + public boolean hasRelocations() { + return !relocations.isEmpty(); + } + + /** + * Gets the relative Maven path to this library's artifact. + * + * @return Maven path for this library + */ + public String getPath() { + return path; + } + + /** + * Gets the relative path to this library's relocated jar. + * + * @return path to relocated artifact or null if has no relocations + */ + public String getRelocatedPath() { + return relocatedPath; + } + + /** + * Is the library loaded isolated? + * + * @return true if the library is loaded isolated + */ + public boolean isIsolatedLoad() { + return isolatedLoad; + } + + /** + * Gets a concise, human-readable string representation of this library. + * + * @return string representation + */ + @Override + public String toString() { + String name = groupId + ':' + artifactId + ':' + version; + if (hasClassifier()) { + name += ':' + classifier; + } + + return name; + } + + /** + * Creates a new library builder. + * + * @return new library builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Due to the constructor complexity of an immutable {@link Library}, + * instead this fluent builder is used to configure and then construct + * a new library. + */ + public static class Builder { + /** + * Direct download URLs for this library + */ + private final Collection urls = new LinkedList<>(); + + /** + * Repository URLs for this library + */ + private final Collection repositories = new LinkedList<>(); + + /** + * The library ID + */ + private String id; + + /** + * Maven group ID + */ + private String groupId; + + /** + * Maven artifact ID + */ + private String artifactId; + + /** + * Artifact version + */ + private String version; + + /** + * Artifact classifier + */ + private String classifier; + + /** + * Binary SHA-256 checksum for this library's jar file + */ + private byte[] checksum; + + /** + * Isolated load + */ + private boolean isolatedLoad; + + /** + * Jar relocations to apply + */ + private final Collection relocations = new LinkedList<>(); + + /** + * Adds a direct download URL for this library. + * + * @param url direct download URL + * @return this builder + */ + public Builder url(String url) { + urls.add(requireNonNull(url, "url")); + return this; + } + + /** + * Adds a repository URL for this library. + *

Most common repositories can be found in {@link Repositories} class as constants. + *

Note that repositories should be preferably added to the {@link LibraryManager} via {@link LibraryManager#addRepository(String)}. + * + * @param url repository URL + * @return this builder + */ + public Builder repository(String url) { + repositories.add(requireNonNull(url, "repository").endsWith("/") ? url : url + '/'); + return this; + } + + /** + * Sets the id for this library. + * + * @param id the ID + * @return this builder + */ + public Builder id(String id) { + this.id = id != null ? id : UUID.randomUUID().toString(); + return this; + } + + /** + * Sets the Maven group ID for this library. + * + * @param groupId Maven group ID + * @return this builder + */ + public Builder groupId(String groupId) { + this.groupId = requireNonNull(groupId, "groupId"); + return this; + } + + /** + * Sets the Maven artifact ID for this library. + * + * @param artifactId Maven artifact ID + * @return this builder + */ + public Builder artifactId(String artifactId) { + this.artifactId = requireNonNull(artifactId, "artifactId"); + return this; + } + + /** + * Sets the artifact version for this library. + * + * @param version artifact version + * @return this builder + */ + public Builder version(String version) { + this.version = requireNonNull(version, "version"); + return this; + } + + /** + * Sets the artifact classifier for this library. + * + * @param classifier artifact classifier + * @return this builder + */ + public Builder classifier(String classifier) { + this.classifier = requireNonNull(classifier, "classifier"); + return this; + } + + /** + * Sets the binary SHA-256 checksum for this library. + * + * @param checksum binary SHA-256 checksum + * @return this builder + */ + public Builder checksum(byte[] checksum) { + this.checksum = requireNonNull(checksum, "checksum"); + return this; + } + + /** + * Sets the Base64-encoded SHA-256 checksum for this library. + * + * @param checksum Base64-encoded SHA-256 checksum + * @return this builder + */ + public Builder checksum(String checksum) { + return checksum(Base64.getDecoder().decode(requireNonNull(checksum, "checksum"))); + } + + /** + * Sets the isolated load for this library. + * + * @param isolatedLoad the isolated load boolean + * @return this builder + */ + public Builder isolatedLoad(boolean isolatedLoad) { + this.isolatedLoad = isolatedLoad; + return this; + } + + /** + * Adds a jar relocation to apply to this library. + * + * @param relocation jar relocation to apply + * @return this builder + */ + public Builder relocate(Relocation relocation) { + relocations.add(requireNonNull(relocation, "relocation")); + return this; + } + + /** + * Adds a jar relocation to apply to this library. + * + * @param pattern search pattern + * @param relocatedPattern replacement pattern + * @return this builder + */ + public Builder relocate(String pattern, String relocatedPattern) { + return relocate(new Relocation(pattern, relocatedPattern)); + } + + /** + * Creates a new library using this builder's configuration. + * + * @return new library + */ + public Library build() { + return new Library(urls, repositories, id, groupId, artifactId, version, classifier, checksum, relocations, isolatedLoad); + } + } +} diff --git a/common/src/main/java/dev/austech/betterstaffchat/common/libraries/libby/LibraryManager.java b/common/src/main/java/dev/austech/betterstaffchat/common/libraries/libby/LibraryManager.java new file mode 100644 index 0000000..812e4d1 --- /dev/null +++ b/common/src/main/java/dev/austech/betterstaffchat/common/libraries/libby/LibraryManager.java @@ -0,0 +1,453 @@ +package dev.austech.betterstaffchat.common.libraries.libby; + +import dev.austech.betterstaffchat.common.libraries.libby.classloader.IsolatedClassLoader; +import dev.austech.betterstaffchat.common.libraries.libby.logging.LogLevel; +import dev.austech.betterstaffchat.common.libraries.libby.logging.Logger; +import dev.austech.betterstaffchat.common.libraries.libby.logging.adapters.LogAdapter; +import dev.austech.betterstaffchat.common.libraries.libby.relocation.Relocation; +import dev.austech.betterstaffchat.common.libraries.libby.relocation.RelocationHelper; + +import java.io.ByteArrayOutputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.UncheckedIOException; +import java.net.MalformedURLException; +import java.net.SocketTimeoutException; +import java.net.URL; +import java.net.URLConnection; +import java.net.UnknownHostException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Base64; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static java.util.Objects.requireNonNull; + +/** + * A runtime dependency manager for plugins. + *

+ * The library manager can resolve a dependency jar through the configured + * Maven repositories, download it into a local cache, relocate it and then + * load it into the plugin's classpath. + *

+ * Transitive dependencies for a library aren't downloaded automatically and + * must be explicitly loaded like every other library. + *

+ * It's recommended that libraries are relocated to prevent any namespace + * conflicts with different versions of the same library bundled with other + * plugins or maybe even bundled with the server itself. + * + * @see Library + */ +public abstract class LibraryManager { + /** + * Wrapped plugin logger + */ + protected final Logger logger; + + /** + * Directory where downloaded library jars are saved to + */ + protected final Path saveDirectory; + + /** + * Maven repositories used to resolve artifacts + */ + private final Set repositories = new LinkedHashSet<>(); + + /** + * Lazily-initialized relocation helper that uses reflection to call into + * Luck's Jar Relocator + */ + private RelocationHelper relocator; + + /** + * Map of isolated class loaders and theirs id + */ + private final Map isolatedLibraries = new HashMap<>(); + + /** + * Creates a new library manager. + * + * @param logAdapter plugin logging adapter + * @param dataDirectory plugin's data directory + * + * @deprecated Use {@link LibraryManager#LibraryManager(LogAdapter, Path, String)} + */ + @Deprecated + protected LibraryManager(LogAdapter logAdapter, Path dataDirectory) { + logger = new Logger(requireNonNull(logAdapter, "logAdapter")); + saveDirectory = requireNonNull(dataDirectory, "dataDirectory").toAbsolutePath().resolve("lib"); + } + + /** + * Creates a new library manager. + * + * @param logAdapter plugin logging adapter + * @param dataDirectory plugin's data directory + * @param directoryName download directory name + */ + protected LibraryManager(LogAdapter logAdapter, Path dataDirectory, String directoryName) { + logger = new Logger(requireNonNull(logAdapter, "logAdapter")); + saveDirectory = requireNonNull(dataDirectory, "dataDirectory").toAbsolutePath().resolve(requireNonNull(directoryName, "directoryName")); + } + + /** + * Adds a file to the plugin's classpath. + * + * @param file the file to add + */ + protected abstract void addToClasspath(Path file); + + /** + * Adds a file to the isolated class loader + * + * @param library the library to add + * @param file the file to add + */ + protected void addToIsolatedClasspath(Library library, Path file) { + IsolatedClassLoader classLoader; + String id = library.getId(); + if (id != null) { + classLoader = isolatedLibraries.computeIfAbsent(id, s -> new IsolatedClassLoader()); + } else { + classLoader = new IsolatedClassLoader(); + } + classLoader.addPath(file); + } + + /** + * Get the isolated class loader of the library + * + * @param libraryId the id of the library + */ + public IsolatedClassLoader getIsolatedClassLoaderOf(String libraryId) { + return isolatedLibraries.get(libraryId); + } + + /** + * Gets the logging level for this library manager. + * + * @return log level + */ + public LogLevel getLogLevel() { + return logger.getLevel(); + } + + /** + * Sets the logging level for this library manager. + *

+ * By setting this value, the library manager's logger will not log any + * messages with a level less severe than the configured level. This can be + * useful for silencing the download and relocation logging. + *

+ * Setting this value to {@link LogLevel#WARN} would silence informational + * logging but still print important things like invalid checksum warnings. + * + * @param level the log level to set + */ + public void setLogLevel(LogLevel level) { + logger.setLevel(level); + } + + /** + * Gets the currently added repositories used to resolve artifacts. + *

+ * For each library this list is traversed to download artifacts after the + * direct download URLs have been attempted. + * + * @return current repositories + */ + public Collection getRepositories() { + List urls; + synchronized (repositories) { + urls = new LinkedList<>(repositories); + } + + return Collections.unmodifiableList(urls); + } + + /** + * Adds a repository URL to this library manager. + *

+ * Artifacts will be resolved using this repository when attempts to locate + * the artifact through previously added repositories are all unsuccessful. + * + * @param url repository URL to add + */ + public void addRepository(String url) { + String repo = requireNonNull(url, "url").endsWith("/") ? url : url + '/'; + synchronized (repositories) { + repositories.add(repo); + } + } + + /** + * Adds the current user's local Maven repository. + */ + public void addMavenLocal() { + addRepository(Paths.get(System.getProperty("user.home")).resolve(".m2/repository").toUri().toString()); + } + + /** + * Adds the Maven Central repository. + */ + public void addMavenCentral() { + addRepository(Repositories.MAVEN_CENTRAL); + } + + /** + * Adds the Sonatype OSS repository. + */ + public void addSonatype() { + addRepository(Repositories.SONATYPE); + } + + /** + * Adds the Bintray JCenter repository. + */ + public void addJCenter() { + addRepository(Repositories.JCENTER); + } + + /** + * Adds the JitPack repository. + */ + public void addJitPack() { + addRepository(Repositories.JITPACK); + } + + /** + * Gets all of the possible download URLs for this library. Entries are + * ordered by direct download URLs first and then repository download URLs. + * + * @param library the library to resolve + * @return download URLs + */ + public Collection resolveLibrary(Library library) { + Set urls = new LinkedHashSet<>(requireNonNull(library, "library").getUrls()); + + // Try from library-declared repos first + for (String repository : library.getRepositories()) { + urls.add(repository + library.getPath()); + } + + for (String repository : getRepositories()) { + urls.add(repository + library.getPath()); + } + + return Collections.unmodifiableSet(urls); + } + + /** + * Downloads a library jar and returns the contents as a byte array. + * + * @param url the URL to the library jar + * @return downloaded jar as byte array or null if nothing was downloaded + */ + private byte[] downloadLibrary(String url) { + try { + URLConnection connection = new URL(requireNonNull(url, "url")).openConnection(); + + connection.setConnectTimeout(5000); + connection.setReadTimeout(5000); + connection.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36"); + + try (InputStream in = connection.getInputStream()) { + int len; + byte[] buf = new byte[8192]; + ByteArrayOutputStream out = new ByteArrayOutputStream(); + + try { + while ((len = in.read(buf)) != -1) { + out.write(buf, 0, len); + } + } catch (SocketTimeoutException e) { + logger.warn("Download timed out: " + connection.getURL()); + return null; + } + + logger.info("Downloaded library " + connection.getURL()); + return out.toByteArray(); + } + } catch (MalformedURLException e) { + throw new IllegalArgumentException(e); + } catch (IOException e) { + if (e instanceof FileNotFoundException) { + logger.debug("File not found: " + url); + } else if (e instanceof SocketTimeoutException) { + logger.debug("Connect timed out: " + url); + } else if (e instanceof UnknownHostException) { + logger.debug("Unknown host: " + url); + } else { + logger.debug("Unexpected IOException", e); + } + + return null; + } + } + + /** + * Downloads a library jar to the save directory if it doesn't already + * exist and returns the local file path. + *

+ * If the library has a checksum, it will be compared against the + * downloaded jar's checksum to verify the integrity of the download. If + * the checksums don't match, a warning is generated and the next download + * URL is attempted. + *

+ * Checksum comparison is ignored if the library doesn't have a checksum + * or if the library jar already exists in the save directory. + *

+ * Most of the time it is advised to use {@link #loadLibrary(Library)} + * instead of this method because this one is only concerned with + * downloading the jar and returning the local path. It's usually more + * desirable to download the jar and add it to the plugin's classpath in + * one operation. + * + * @param library the library to download + * @return local file path to library + * @see #loadLibrary(Library) + */ + public Path downloadLibrary(Library library) { + requireNonNull(library, "library"); + Path file = saveDirectory.resolve(library.getArtifactId() + "-" + library.getVersion() + ".jar"); + if (Files.exists(file)) { + return file; + } + + Collection urls = resolveLibrary(library); + if (urls.isEmpty()) { + throw new RuntimeException("Library '" + library + "' couldn't be resolved, add a repository"); + } + + MessageDigest md = null; + if (library.hasChecksum()) { + try { + md = MessageDigest.getInstance("SHA-256"); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException(e); + } + } + + Path out = file.resolveSibling(file.getFileName() + ".tmp"); + out.toFile().deleteOnExit(); + + try { + Files.createDirectories(file.getParent()); + + for (String url : urls) { + byte[] bytes = downloadLibrary(url); + if (bytes == null) { + continue; + } + + if (md != null) { + byte[] checksum = md.digest(bytes); + if (!MessageDigest.isEqual(checksum, library.getChecksum())) { + logger.warn("*** INVALID CHECKSUM ***"); + logger.warn(" Library : " + library); + logger.warn(" URL : " + url); + logger.warn(" Expected : " + Base64.getEncoder().encodeToString(library.getChecksum())); + logger.warn(" Actual : " + Base64.getEncoder().encodeToString(checksum)); + continue; + } + } + + Files.write(out, bytes); + Files.move(out, file); + + return file; + } + } catch (IOException e) { + throw new UncheckedIOException(e); + } finally { + try { + Files.deleteIfExists(out); + } catch (IOException ignored) { + } + } + + throw new RuntimeException("Failed to download library '" + library + "'"); + } + + /** + * Processes the input jar and generates an output jar with the provided + * relocation rules applied, then returns the path to the relocated jar. + * + * @param in input jar + * @param out output jar + * @param relocations relocations to apply + * @return the relocated file + * @see RelocationHelper#relocate(Path, Path, Collection) + */ + private Path relocate(Path in, String out, Collection relocations) { + requireNonNull(in, "in"); + requireNonNull(out, "out"); + requireNonNull(relocations, "relocations"); + + Path file = saveDirectory.resolve(out); + if (Files.exists(file)) { + return file; + } + + Path tmpOut = file.resolveSibling(file.getFileName() + ".tmp"); + tmpOut.toFile().deleteOnExit(); + + synchronized (this) { + if (relocator == null) { + relocator = new RelocationHelper(this); + } + } + + try { + relocator.relocate(in, tmpOut, relocations); + Files.move(tmpOut, file); + + logger.info("Relocations applied to " + saveDirectory.getParent().relativize(in)); + + return file; + } catch (IOException e) { + throw new UncheckedIOException(e); + } finally { + try { + Files.deleteIfExists(tmpOut); + } catch (IOException ignored) { + } + } + } + + /** + * Loads a library jar into the plugin's classpath. If the library jar + * doesn't exist locally, it will be downloaded. + *

+ * If the provided library has any relocations, they will be applied to + * create a relocated jar and the relocated jar will be loaded instead. + * + * @param library the library to load + * @see #downloadLibrary(Library) + */ + public void loadLibrary(Library library) { + Path file = downloadLibrary(requireNonNull(library, "library")); + if (library.hasRelocations()) { + file = relocate(file, library.getRelocatedPath(), library.getRelocations()); + } + + if (library.isIsolatedLoad()) { + addToIsolatedClasspath(library, file); + } else { + addToClasspath(file); + } + } +} diff --git a/common/src/main/java/dev/austech/betterstaffchat/common/libraries/libby/Repositories.java b/common/src/main/java/dev/austech/betterstaffchat/common/libraries/libby/Repositories.java new file mode 100644 index 0000000..1d7f40b --- /dev/null +++ b/common/src/main/java/dev/austech/betterstaffchat/common/libraries/libby/Repositories.java @@ -0,0 +1,31 @@ +package dev.austech.betterstaffchat.common.libraries.libby; + +/** + * Class containing URLs of public repositories. + */ +public class Repositories { + + /** + * Maven Central repository URL. + */ + public static final String MAVEN_CENTRAL = "https://repo1.maven.org/maven2/"; + + /** + * Sonatype OSS repository URL. + */ + public static final String SONATYPE = "https://oss.sonatype.org/content/groups/public/"; + + /** + * Bintray JCenter repository URL. + */ + public static final String JCENTER = "https://jcenter.bintray.com/"; + + /** + * JitPack repository URL. + */ + public static final String JITPACK = "https://jitpack.io/"; + + private Repositories() { + throw new UnsupportedOperationException("Private constructor"); + } +} diff --git a/common/src/main/java/dev/austech/betterstaffchat/common/libraries/libby/classloader/IsolatedClassLoader.java b/common/src/main/java/dev/austech/betterstaffchat/common/libraries/libby/classloader/IsolatedClassLoader.java new file mode 100644 index 0000000..39c3737 --- /dev/null +++ b/common/src/main/java/dev/austech/betterstaffchat/common/libraries/libby/classloader/IsolatedClassLoader.java @@ -0,0 +1,51 @@ +package dev.austech.betterstaffchat.common.libraries.libby.classloader; + +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.file.Path; + +import static java.util.Objects.requireNonNull; + +/** + * This class loader is a simple child of {@code URLClassLoader} that uses + * the JVM's Extensions Class Loader as the parent instead of the system class + * loader to provide an unpolluted classpath. + */ +public class IsolatedClassLoader extends URLClassLoader { + static { + ClassLoader.registerAsParallelCapable(); + } + + /** + * Creates a new isolated class loader for the given URLs. + * + * @param urls the URLs to add to the classpath + */ + public IsolatedClassLoader(URL... urls) { + super(requireNonNull(urls, "urls"), ClassLoader.getSystemClassLoader().getParent()); + } + + /** + * Adds a URL to the classpath. + * + * @param url the URL to add + */ + @Override + public void addURL(URL url) { + super.addURL(url); + } + + /** + * Adds a path to the classpath. + * + * @param path the path to add + */ + public void addPath(Path path) { + try { + addURL(requireNonNull(path, "path").toUri().toURL()); + } catch (MalformedURLException e) { + throw new IllegalArgumentException(e); + } + } +} diff --git a/common/src/main/java/dev/austech/betterstaffchat/common/libraries/libby/classloader/URLClassLoaderHelper.java b/common/src/main/java/dev/austech/betterstaffchat/common/libraries/libby/classloader/URLClassLoaderHelper.java new file mode 100644 index 0000000..ba41207 --- /dev/null +++ b/common/src/main/java/dev/austech/betterstaffchat/common/libraries/libby/classloader/URLClassLoaderHelper.java @@ -0,0 +1,233 @@ +package dev.austech.betterstaffchat.common.libraries.libby.classloader; + +import dev.austech.betterstaffchat.common.libraries.libby.Library; +import dev.austech.betterstaffchat.common.libraries.libby.LibraryManager; +import dev.austech.betterstaffchat.common.libraries.libby.Repositories; +import sun.misc.Unsafe; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.file.Path; +import java.util.Collections; +import java.util.Map; +import java.util.Set; + +import static java.util.Objects.requireNonNull; + +/** + * A reflection-based wrapper around {@link URLClassLoader} for adding URLs to + * the classpath. + */ +public class URLClassLoaderHelper { + + /** + * Unsafe class instance. Used in {@link #getPrivilegedMethodHandle(Method)}. + */ + private static final Unsafe theUnsafe; + + static { + Unsafe unsafe = null; // Used to make theUnsafe field final + + // getDeclaredField("theUnsafe") is not used to avoid breakage on JVMs with changed field name + for (Field f : Unsafe.class.getDeclaredFields()) { + try { + if (f.getType() == Unsafe.class && Modifier.isStatic(f.getModifiers())) { + f.setAccessible(true); + unsafe = (Unsafe) f.get(null); + } + } catch (Exception ignored) { + } + } + theUnsafe = unsafe; + } + + /** + * The class loader being managed by this helper. + */ + private final URLClassLoader classLoader; + + /** + * A reflected method in {@link URLClassLoader}, when invoked adds a URL to the classpath. + */ + private MethodHandle addURLMethodHandle = null; + + /** + * Creates a new URL class loader helper. + * + * @param classLoader the class loader to manage + * @param libraryManager the library manager used to download dependencies + */ + public URLClassLoaderHelper(URLClassLoader classLoader, LibraryManager libraryManager) { + requireNonNull(libraryManager, "libraryManager"); + this.classLoader = requireNonNull(classLoader, "classLoader"); + + try { + Method addURLMethod = URLClassLoader.class.getDeclaredMethod("addURL", URL.class); + + try { + openUrlClassLoaderModule(); + } catch (Exception ignored) { + } + + try { + addURLMethod.setAccessible(true); + } catch (Exception exception) { + // InaccessibleObjectException has been added in Java 9 + if (exception.getClass().getName().equals("java.lang.reflect.InaccessibleObjectException")) { + // It is Java 9+, try to open java.net package + if (theUnsafe != null) + try { + addURLMethodHandle = getPrivilegedMethodHandle(addURLMethod).bindTo(classLoader); + return; // We're done + } catch (Exception ignored) { + addURLMethodHandle = null; // Just to be sure the field is set to null + } + // Cannot use privileged MethodHandles.Lookup, trying with java agent + try { + addOpensWithAgent(libraryManager); + addURLMethod.setAccessible(true); + } catch (Exception e) { + // Cannot access at all + System.err.println("Cannot access URLClassLoader#addURL(URL), if you are using Java 9+ try to add the following option to your java command: --add-opens java.base/java.net=ALL-UNNAMED"); + throw new RuntimeException("Cannot access URLClassLoader#addURL(URL)", e); + } + } else { + throw new RuntimeException("Cannot set accessible URLClassLoader#addURL(URL)", exception); + } + } + this.addURLMethodHandle = MethodHandles.lookup().unreflect(addURLMethod).bindTo(classLoader); + } catch (NoSuchMethodException | IllegalAccessException e) { + throw new RuntimeException(e); + } + } + + /** + * Adds a URL to the class loader's classpath. + * + * @param url the URL to add + */ + public void addToClasspath(URL url) { + try { + addURLMethodHandle.invokeWithArguments(requireNonNull(url, "url")); + } catch (Throwable e) { + throw new RuntimeException(e); + } + } + + /** + * Adds a path to the class loader's classpath. + * + * @param path the path to add + */ + public void addToClasspath(Path path) { + try { + addToClasspath(requireNonNull(path, "path").toUri().toURL()); + } catch (MalformedURLException e) { + throw new IllegalArgumentException(e); + } + } + + private static void openUrlClassLoaderModule() throws Exception { + // + // Thanks to lucko (Luck) for this snippet used in his own class loader + // + // This is a workaround used to maintain Java 9+ support with reflections + // Thanks to this you will be able to run this class loader with Java 8+ + + // This is effectively calling: + // + // URLClassLoader.class.getModule().addOpens( + // URLClassLoader.class.getPackageName(), + // URLClassLoaderHelper.class.getModule() + // ); + // + // We use reflection since we build against Java 8. + + Class moduleClass = Class.forName("java.lang.Module"); + Method getModuleMethod = Class.class.getMethod("getModule"); + Method addOpensMethod = moduleClass.getMethod("addOpens", String.class, moduleClass); + + Object urlClassLoaderModule = getModuleMethod.invoke(URLClassLoader.class); + Object thisModule = getModuleMethod.invoke(URLClassLoaderHelper.class); + + addOpensMethod.invoke(urlClassLoaderModule, URLClassLoader.class.getPackage().getName(), thisModule); + } + + private MethodHandle getPrivilegedMethodHandle(Method method) throws Exception { + // Try to get a MethodHandle to URLClassLoader#addURL. + // The Unsafe class is used to get a privileged MethodHandles.Lookup instance. + + // Looking for MethodHandles.Lookup#IMPL_LOOKUP private static field + // getDeclaredField("IMPL_LOOKUP") is not used to avoid breakage on JVMs with changed field name + for (Field trustedLookup : MethodHandles.Lookup.class.getDeclaredFields()) { + if (trustedLookup.getType() != MethodHandles.Lookup.class || !Modifier.isStatic(trustedLookup.getModifiers()) || trustedLookup.isSynthetic()) + continue; + + try { + MethodHandles.Lookup lookup = (MethodHandles.Lookup) theUnsafe.getObject(theUnsafe.staticFieldBase(trustedLookup), theUnsafe.staticFieldOffset(trustedLookup)); + return lookup.unreflect(method); + } catch (Exception ignored) { + // Unreflect went wrong, trying the next field + } + } + + // Every field has been tried + throw new RuntimeException("Cannot get privileged method handle."); + } + + private void addOpensWithAgent(LibraryManager libraryManager) throws Exception { + // To open URLClassLoader's module we need permissions. + // Try to add a java agent at runtime (specifically, ByteBuddy's agent) and use it to open the module, + // since java agents should have such permission. + + // Download ByteBuddy's agent and load it through an IsolatedClassLoader + IsolatedClassLoader isolatedClassLoader = new IsolatedClassLoader(); + try { + isolatedClassLoader.addPath(libraryManager.downloadLibrary( + Library.builder() + .groupId("net.bytebuddy") + .artifactId("byte-buddy-agent") + .version("1.12.1") + .checksum("mcCtBT9cljUEniB5ESpPDYZMfVxEs1JRPllOiWTP+bM=") + .repository(Repositories.MAVEN_CENTRAL) + .build() + )); + + Class byteBuddyAgent = isolatedClassLoader.loadClass("net.bytebuddy.agent.ByteBuddyAgent"); + + // This is effectively calling: + // + // Instrumentation instrumentation = ByteBuddyAgent.install(); + // instrumentation.redefineModule( + // URLClassLoader.class.getModule(), + // Collections.emptySet(), + // Collections.emptyMap(), + // Collections.singletonMap("java.net", Collections.singleton(getClass().getModule())), + // Collections.emptySet(), + // Collections.emptyMap() + // ); + // + // For more information see https://docs.oracle.com/en/java/javase/16/docs/api/java.instrument/java/lang/instrument/Instrumentation.html + // + // We use reflection since we build against Java 8. + + Object instrumentation = byteBuddyAgent.getDeclaredMethod("install").invoke(null); + Class instrumentationClass = Class.forName("java.lang.instrument.Instrumentation"); + Method redefineModule = instrumentationClass.getDeclaredMethod("redefineModule", Class.forName("java.lang.Module"), Set.class, Map.class, Map.class, Set.class, Map.class); + Method getModule = Class.class.getDeclaredMethod("getModule"); + Map> toOpen = Collections.singletonMap("java.net", Collections.singleton(getModule.invoke(getClass()))); + redefineModule.invoke(instrumentation, getModule.invoke(URLClassLoader.class), Collections.emptySet(), Collections.emptyMap(), toOpen, Collections.emptySet(), Collections.emptyMap()); + } finally { + try { + isolatedClassLoader.close(); + } catch (Exception ignored) { + } + } + } +} diff --git a/common/src/main/java/dev/austech/betterstaffchat/common/libraries/libby/logging/LogLevel.java b/common/src/main/java/dev/austech/betterstaffchat/common/libraries/libby/logging/LogLevel.java new file mode 100644 index 0000000..759c687 --- /dev/null +++ b/common/src/main/java/dev/austech/betterstaffchat/common/libraries/libby/logging/LogLevel.java @@ -0,0 +1,26 @@ +package dev.austech.betterstaffchat.common.libraries.libby.logging; + +/** + * Represents the severity of a log message in {@link Logger}. + */ +public enum LogLevel { + /** + * Stuff that isn't useful to end-users + */ + DEBUG, + + /** + * Stuff that might be useful to know + */ + INFO, + + /** + * Non-fatal, often recoverable errors or notices + */ + WARN, + + /** + * Probably an unrecoverable error + */ + ERROR +} diff --git a/common/src/main/java/dev/austech/betterstaffchat/common/libraries/libby/logging/Logger.java b/common/src/main/java/dev/austech/betterstaffchat/common/libraries/libby/logging/Logger.java new file mode 100644 index 0000000..53be405 --- /dev/null +++ b/common/src/main/java/dev/austech/betterstaffchat/common/libraries/libby/logging/Logger.java @@ -0,0 +1,195 @@ +package dev.austech.betterstaffchat.common.libraries.libby.logging; + +import dev.austech.betterstaffchat.common.libraries.libby.logging.adapters.LogAdapter; + +import static java.util.Objects.requireNonNull; + +/** + * A logging wrapper that logs to a log adapter and can be configured to filter + * log messages by severity. + */ +public class Logger { + /** + * Log adapter for the current platform + */ + private final LogAdapter adapter; + + /** + * Log level controlling which messages are logged + */ + private LogLevel level = LogLevel.INFO; + + /** + * Creates a new logger with the provided adapter. + * + * @param adapter the adapter to wrap + */ + public Logger(LogAdapter adapter) { + this.adapter = requireNonNull(adapter, "adapter"); + } + + /** + * Gets the current log level. + * + * @return current log level + */ + public LogLevel getLevel() { + return level; + } + + /** + * Sets a new log level. + * + * @param level new log level + */ + public void setLevel(LogLevel level) { + this.level = requireNonNull(level, "level"); + } + + /** + * Gets whether messages matching the provided level can be logged under + * the current log level setting. + *

+ * Returns true if provided log level is equal to or more severe than the + * logger's configured log level. + * + * @param level the level to check + * @return true if message can be logged, or false + */ + private boolean canLog(LogLevel level) { + return requireNonNull(level, "level").compareTo(this.level) >= 0; + } + + /** + * Logs a message with the provided level. + *

+ * If the provided log level is less severe than the logger's + * configured log level, this message won't be logged. + * + * @param level message severity level + * @param message the message to log + * @see #debug(String) + * @see #info(String) + * @see #warn(String) + * @see #error(String) + */ + public void log(LogLevel level, String message) { + if (canLog(level)) { + adapter.log(level, message); + } + } + + /** + * Logs a message and stack trace with the provided level. + *

+ * If the provided log level is less severe than the logger's + * configured log level, this message won't be logged. + * + * @param level message severity level + * @param message the message to log + * @param throwable the throwable to print + * @see #debug(String, Throwable) + * @see #info(String, Throwable) + * @see #warn(String, Throwable) + * @see #error(String, Throwable) + */ + public void log(LogLevel level, String message, Throwable throwable) { + if (canLog(level)) { + adapter.log(level, message, throwable); + } + } + + /** + * Logs a debug message. + *

+ * If the logger's configured log level is more severe than + * {@link LogLevel#DEBUG}, this message won't be logged. + * + * @param message the message to log + */ + public void debug(String message) { + log(LogLevel.DEBUG, message); + } + + /** + * Logs a debug message with a stack trace. + *

+ * If the logger's configured log level is more severe than + * {@link LogLevel#DEBUG}, this message won't be logged. + * + * @param message the message to log + * @param throwable the throwable to print + */ + public void debug(String message, Throwable throwable) { + log(LogLevel.DEBUG, message, throwable); + } + + /** + * Logs an informational message. + *

+ * If the logger's configured log level is more severe than + * {@link LogLevel#INFO}, this message won't be logged. + * + * @param message the message to log + */ + public void info(String message) { + log(LogLevel.INFO, message); + } + + /** + * Logs an informational message with a stack trace. + *

+ * If the logger's configured log level is more severe than + * {@link LogLevel#INFO}, this message won't be logged. + * + * @param message the message to log + * @param throwable the throwable to print + */ + public void info(String message, Throwable throwable) { + log(LogLevel.INFO, message, throwable); + } + + /** + * Logs a warning message. + *

+ * If the logger's configured log level is more severe than + * {@link LogLevel#WARN}, this message won't be logged. + * + * @param message the message to log + */ + public void warn(String message) { + log(LogLevel.WARN, message); + } + + /** + * Logs a warning message with a stack trace. + *

+ * If the logger's configured log level is more severe than + * {@link LogLevel#WARN}, this message won't be logged. + * + * @param message the message to log + * @param throwable the throwable to print + */ + public void warn(String message, Throwable throwable) { + log(LogLevel.WARN, message, throwable); + } + + /** + * Logs an error message. + * + * @param message the message to log + */ + public void error(String message) { + log(LogLevel.ERROR, message); + } + + /** + * Logs an error message with a stack trace. + * + * @param message message to log + * @param throwable the throwable to print + */ + public void error(String message, Throwable throwable) { + log(LogLevel.ERROR, message, throwable); + } +} diff --git a/common/src/main/java/dev/austech/betterstaffchat/common/libraries/libby/logging/adapters/JDKLogAdapter.java b/common/src/main/java/dev/austech/betterstaffchat/common/libraries/libby/logging/adapters/JDKLogAdapter.java new file mode 100644 index 0000000..f49f4e5 --- /dev/null +++ b/common/src/main/java/dev/austech/betterstaffchat/common/libraries/libby/logging/adapters/JDKLogAdapter.java @@ -0,0 +1,77 @@ +package dev.austech.betterstaffchat.common.libraries.libby.logging.adapters; + +import dev.austech.betterstaffchat.common.libraries.libby.logging.LogLevel; + +import java.util.logging.Level; +import java.util.logging.Logger; + +import static java.util.Objects.requireNonNull; + +/** + * Logging adapter that logs to a JDK logger. + */ +public class JDKLogAdapter implements LogAdapter { + /** + * JDK logger + */ + private final Logger logger; + + /** + * Creates a new JDK log adapter that logs to a {@link Logger}. + * + * @param logger the JDK logger to wrap + */ + public JDKLogAdapter(Logger logger) { + this.logger = requireNonNull(logger, "logger"); + } + + /** + * Logs a message with the provided level to the JDK logger. + * + * @param level message severity level + * @param message the message to log + */ + @Override + public void log(LogLevel level, String message) { + switch (requireNonNull(level, "level")) { + case DEBUG: + logger.log(Level.FINE, message); + break; + case INFO: + logger.log(Level.INFO, message); + break; + case WARN: + logger.log(Level.WARNING, message); + break; + case ERROR: + logger.log(Level.SEVERE, message); + break; + } + } + + /** + * Logs a message and stack trace with the provided level to the JDK + * logger. + * + * @param level message severity level + * @param message the message to log + * @param throwable the throwable to print + */ + @Override + public void log(LogLevel level, String message, Throwable throwable) { + switch (requireNonNull(level, "level")) { + case DEBUG: + logger.log(Level.FINE, message, throwable); + break; + case INFO: + logger.log(Level.INFO, message, throwable); + break; + case WARN: + logger.log(Level.WARNING, message, throwable); + break; + case ERROR: + logger.log(Level.SEVERE, message, throwable); + break; + } + } +} diff --git a/common/src/main/java/dev/austech/betterstaffchat/common/libraries/libby/logging/adapters/LogAdapter.java b/common/src/main/java/dev/austech/betterstaffchat/common/libraries/libby/logging/adapters/LogAdapter.java new file mode 100644 index 0000000..a5f4aaf --- /dev/null +++ b/common/src/main/java/dev/austech/betterstaffchat/common/libraries/libby/logging/adapters/LogAdapter.java @@ -0,0 +1,25 @@ +package dev.austech.betterstaffchat.common.libraries.libby.logging.adapters; + +import dev.austech.betterstaffchat.common.libraries.libby.logging.LogLevel; + +/** + * Logging interface for adapting platform-specific loggers to our logging API. + */ +public interface LogAdapter { + /** + * Logs a message with the provided level. + * + * @param level message severity level + * @param message the message to log + */ + void log(LogLevel level, String message); + + /** + * Logs a message and stack trace with the provided level. + * + * @param level message severity level + * @param message the message to log + * @param throwable the throwable to print + */ + void log(LogLevel level, String message, Throwable throwable); +} diff --git a/common/src/main/java/dev/austech/betterstaffchat/common/libraries/libby/relocation/Relocation.java b/common/src/main/java/dev/austech/betterstaffchat/common/libraries/libby/relocation/Relocation.java new file mode 100644 index 0000000..6c9b411 --- /dev/null +++ b/common/src/main/java/dev/austech/betterstaffchat/common/libraries/libby/relocation/Relocation.java @@ -0,0 +1,184 @@ +package dev.austech.betterstaffchat.common.libraries.libby.relocation; + +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedList; + +import static java.util.Objects.requireNonNull; + +/** + * Relocations are used to describe a search and replace pattern for renaming + * packages in a library jar for the purpose of preventing namespace conflicts + * with other plugins that bundle their own version of the same library. + */ +public class Relocation { + /** + * Search pattern + */ + private final String pattern; + + /** + * Replacement pattern + */ + private final String relocatedPattern; + + /** + * Classes and resources to include + */ + private final Collection includes; + + /** + * Classes and resources to exclude + */ + private final Collection excludes; + + /** + * Creates a new relocation. + * + * @param pattern search pattern + * @param relocatedPattern replacement pattern + * @param includes classes and resources to include + * @param excludes classes and resources to exclude + */ + public Relocation(String pattern, String relocatedPattern, Collection includes, Collection excludes) { + this.pattern = requireNonNull(pattern, "pattern").replace("{}", "."); + this.relocatedPattern = requireNonNull(relocatedPattern, "relocatedPattern").replace("{}", "."); + this.includes = includes != null ? Collections.unmodifiableList(new LinkedList<>(includes)) : Collections.emptyList(); + this.excludes = excludes != null ? Collections.unmodifiableList(new LinkedList<>(excludes)) : Collections.emptyList(); + } + + /** + * Creates a new relocation with empty includes and excludes. + * + * @param pattern search pattern + * @param relocatedPattern replacement pattern + */ + public Relocation(String pattern, String relocatedPattern) { + this(pattern, relocatedPattern, null, null); + } + + /** + * Gets the search pattern. + * + * @return pattern to search + */ + public String getPattern() { + return pattern; + } + + /** + * Gets the replacement pattern. + * + * @return pattern to replace with + */ + public String getRelocatedPattern() { + return relocatedPattern; + } + + /** + * Gets included classes and resources. + * + * @return classes and resources to include + */ + public Collection getIncludes() { + return includes; + } + + /** + * Gets excluded classes and resources. + * + * @return classes and resources to exclude + */ + public Collection getExcludes() { + return excludes; + } + + /** + * Creates a new relocation builder. + * + * @return new relocation builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Provides an alternative method of creating a {@link Relocation}. This + * builder may be more intuitive for configuring relocations that also have + * any includes or excludes. + */ + public static class Builder { + /** + * Search pattern + */ + private String pattern; + + /** + * Replacement pattern + */ + private String relocatedPattern; + + /** + * Classes and resources to include + */ + private final Collection includes = new LinkedList<>(); + + /** + * Classes and resources to exclude + */ + private final Collection excludes = new LinkedList<>(); + + /** + * Sets the search pattern. + * + * @param pattern pattern to search + * @return this builder + */ + public Builder pattern(String pattern) { + this.pattern = requireNonNull(pattern, "pattern"); + return this; + } + + /** + * Sets the replacement pattern. + * + * @param relocatedPattern pattern to replace with + * @return this builder + */ + public Builder relocatedPattern(String relocatedPattern) { + this.relocatedPattern = requireNonNull(relocatedPattern, "relocatedPattern"); + return this; + } + + /** + * Adds a class or resource to be included. + * + * @param include class or resource to include + * @return this builder + */ + public Builder include(String include) { + includes.add(requireNonNull(include, "include")); + return this; + } + + /** + * Adds a class or resource to be excluded. + * + * @param exclude class or resource to exclude + * @return this builder + */ + public Builder exclude(String exclude) { + excludes.add(requireNonNull(exclude, "exclude")); + return this; + } + + /** + * Creates a new relocation using this builder's configuration. + * + * @return new relocation + */ + public Relocation build() { + return new Relocation(pattern, relocatedPattern, includes, excludes); + } + } +} diff --git a/common/src/main/java/dev/austech/betterstaffchat/common/libraries/libby/relocation/RelocationHelper.java b/common/src/main/java/dev/austech/betterstaffchat/common/libraries/libby/relocation/RelocationHelper.java new file mode 100644 index 0000000..565e668 --- /dev/null +++ b/common/src/main/java/dev/austech/betterstaffchat/common/libraries/libby/relocation/RelocationHelper.java @@ -0,0 +1,130 @@ +package dev.austech.betterstaffchat.common.libraries.libby.relocation; + +import dev.austech.betterstaffchat.common.libraries.libby.Library; +import dev.austech.betterstaffchat.common.libraries.libby.LibraryManager; +import dev.austech.betterstaffchat.common.libraries.libby.Repositories; +import dev.austech.betterstaffchat.common.libraries.libby.classloader.IsolatedClassLoader; + +import java.io.File; +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.nio.file.Path; +import java.util.Collection; +import java.util.LinkedList; +import java.util.List; + +import static java.util.Objects.requireNonNull; + +/** + * A reflection-based helper for relocating library jars. It automatically + * downloads and invokes Luck's Jar Relocator to perform jar relocations. + * + * @see Luck's Jar Relocator + */ +public class RelocationHelper { + /** + * Reflected constructor for creating new jar relocator instances + */ + private final Constructor jarRelocatorConstructor; + + /** + * Reflected method for running a jar relocator + */ + private final Method jarRelocatorRunMethod; + + /** + * Reflected constructor for creating relocation instances + */ + private final Constructor relocationConstructor; + + /** + * Creates a new relocation helper using the provided library manager to + * download the dependencies required for runtime relocation. + * + * @param libraryManager the library manager used to download dependencies + */ + public RelocationHelper(LibraryManager libraryManager) { + requireNonNull(libraryManager, "libraryManager"); + + IsolatedClassLoader classLoader = new IsolatedClassLoader(); + + // ObjectWeb ASM Commons + classLoader.addPath(libraryManager.downloadLibrary( + Library.builder() + .groupId("org.ow2.asm") + .artifactId("asm-commons") + .version("9.2") + .checksum("vkzlMTiiOLtSLNeBz5Hzulzi9sqT7GLUahYqEnIl4KY=") + .repository(Repositories.MAVEN_CENTRAL) + .build() + )); + + // ObjectWeb ASM + classLoader.addPath(libraryManager.downloadLibrary( + Library.builder() + .groupId("org.ow2.asm") + .artifactId("asm") + .version("9.2") + .checksum("udT+TXGTjfOIOfDspCqqpkz4sxPWeNoDbwyzyhmbR/U=") + .repository(Repositories.MAVEN_CENTRAL) + .build() + )); + + // Luck's Jar Relocator + classLoader.addPath(libraryManager.downloadLibrary( + Library.builder() + .groupId("me.lucko") + .artifactId("jar-relocator") + .version("1.5") + .checksum("0D6eM99gKpEYFNDydgnto3Df0ygZGdRVqy5ahtj0oIs=") + .repository(Repositories.MAVEN_CENTRAL) + .build() + )); + + try { + Class jarRelocatorClass = classLoader.loadClass("me.lucko.jarrelocator.JarRelocator"); + Class relocationClass = classLoader.loadClass("me.lucko.jarrelocator.Relocation"); + + // me.lucko.jarrelocator.JarRelocator(File, File, Collection) + jarRelocatorConstructor = jarRelocatorClass.getConstructor(File.class, File.class, Collection.class); + + // me.lucko.jarrelocator.JarRelocator#run() + jarRelocatorRunMethod = jarRelocatorClass.getMethod("run"); + + // me.lucko.jarrelocator.Relocation(String, String, Collection, Collection) + relocationConstructor = relocationClass.getConstructor(String.class, String.class, Collection.class, Collection.class); + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } + + /** + * Invokes the jar relocator to process the input jar and generate an + * output jar with the provided relocation rules applied. + * + * @param in input jar + * @param out output jar + * @param relocations relocations to apply + */ + public void relocate(Path in, Path out, Collection relocations) { + requireNonNull(in, "in"); + requireNonNull(out, "out"); + requireNonNull(relocations, "relocations"); + + try { + List rules = new LinkedList<>(); + for (Relocation relocation : relocations) { + rules.add(relocationConstructor.newInstance( + relocation.getPattern(), + relocation.getRelocatedPattern(), + relocation.getIncludes(), + relocation.getExcludes() + )); + } + + jarRelocatorRunMethod.invoke(jarRelocatorConstructor.newInstance(in.toFile(), out.toFile(), rules)); + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } +} diff --git a/common/src/main/kotlin/dev/austech/betterstaffchat/common/BSCMetadata.kt b/common/src/main/kotlin/dev/austech/betterstaffchat/common/BSCMetadata.kt new file mode 100644 index 0000000..8bb4562 --- /dev/null +++ b/common/src/main/kotlin/dev/austech/betterstaffchat/common/BSCMetadata.kt @@ -0,0 +1,77 @@ +package dev.austech.betterstaffchat.common + +import lombok.AllArgsConstructor +import java.util.UUID + +class BSCMetadata { + var playerMeta: Player? = null + var luckpermsMeta: LuckPerms? = null + var proxyMeta: Proxy? = null + var spigotMeta: Spigot? = null + var discordMeta: Discord? = null + + constructor(discordMeta: Discord) { + this.discordMeta = discordMeta + } + + constructor(playerMeta: Player) { + this.playerMeta = playerMeta + } + + constructor(playerMeta: Player, luckpermsMeta: LuckPerms) { + this.playerMeta = playerMeta + this.luckpermsMeta = luckpermsMeta + } + + constructor(playerMeta: Player, proxyMeta: Proxy) { + this.playerMeta = playerMeta + this.proxyMeta = proxyMeta + } + + constructor(playerMeta: Player, spigotMeta: Spigot) { + this.playerMeta = playerMeta + this.spigotMeta = spigotMeta + } + + constructor(playerMeta: Player, proxyMeta: Proxy, luckpermsMeta: LuckPerms) { + this.playerMeta = playerMeta + this.luckpermsMeta = luckpermsMeta + this.proxyMeta = proxyMeta + } + + constructor(playerMeta: Player, spigotMeta: Spigot, luckpermsMeta: LuckPerms) { + this.playerMeta = playerMeta + this.luckpermsMeta = luckpermsMeta + this.spigotMeta = spigotMeta + } + + @AllArgsConstructor + class LuckPerms( + val prefix: String?, + val suffix: String?, + val group: String?, + val groupDisplayName: String?, + ) + + @AllArgsConstructor + class Player( + val uuid: UUID?, + val name: String, + ) + + @AllArgsConstructor + class Proxy( + val server: String, + val serverRaw: String + ) + + @AllArgsConstructor + class Spigot( + val world: String? + ) + + @AllArgsConstructor + class Discord( + val displayName: String? + ) +} \ No newline at end of file diff --git a/common/src/main/kotlin/dev/austech/betterstaffchat/common/BSCPlugin.kt b/common/src/main/kotlin/dev/austech/betterstaffchat/common/BSCPlugin.kt new file mode 100644 index 0000000..0af59f3 --- /dev/null +++ b/common/src/main/kotlin/dev/austech/betterstaffchat/common/BSCPlugin.kt @@ -0,0 +1,26 @@ +package dev.austech.betterstaffchat.common + +import de.leonhard.storage.Yaml +import dev.austech.betterstaffchat.common.discord.DiscordManager +import dev.austech.betterstaffchat.common.util.IPlayerUtil +import dev.austech.betterstaffchat.common.util.StaffChatUtil +import net.kyori.adventure.audience.Audience +import java.io.File + +interface BSCPlugin { + val platform: Platform + val pluginDataFile: File + val config: Yaml + val consoleAudience: Audience + val staffChatUtil: StaffChatUtil + val playerUtil: IPlayerUtil + val discordManager: DiscordManager + + fun onLoad() + fun onEnable() + + enum class Platform { + BUKKIT, + BUNGEECORD + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/dev/austech/betterstaffchat/common/discord/DiscordClient.kt b/common/src/main/kotlin/dev/austech/betterstaffchat/common/discord/DiscordClient.kt new file mode 100644 index 0000000..5e03983 --- /dev/null +++ b/common/src/main/kotlin/dev/austech/betterstaffchat/common/discord/DiscordClient.kt @@ -0,0 +1,34 @@ +package dev.austech.betterstaffchat.common.discord + +import dev.austech.betterstaffchat.common.BSCPlugin +import dev.austech.betterstaffchat.common.discord.impl.bot.DiscordListener +import net.dv8tion.jda.api.JDABuilder +import dev.austech.betterstaffchat.common.util.Config +import net.dv8tion.jda.api.OnlineStatus +import net.dv8tion.jda.api.entities.Activity +import net.dv8tion.jda.api.requests.GatewayIntent +import net.dv8tion.jda.api.utils.cache.CacheFlag + +class DiscordClient(private val plugin: BSCPlugin) { + val jda = JDABuilder.create(plugin.config.getString(Config.Paths.Discord.BOT_TOKEN.toString()), GatewayIntent.GUILD_MESSAGES, GatewayIntent.GUILD_MEMBERS) + .addEventListeners(DiscordListener(plugin)) + .disableCache(CacheFlag.ACTIVITY, CacheFlag.VOICE_STATE, CacheFlag.EMOTE, CacheFlag.CLIENT_STATUS, CacheFlag.ONLINE_STATUS) + .build() + + init { + jda.awaitReady() + setup() + } + + fun setup() { + if (plugin.config.getBoolean(Config.Paths.Discord.BOT_ACTIVITY.toString())) { + jda.presence.activity = Activity.of( + Activity.ActivityType.valueOf(plugin.config.getString(Config.Paths.Discord.BOT_ACTIVITY_TYPE.toString()).uppercase()), + plugin.config.getString(Config.Paths.Discord.BOT_ACTIVITY_STATUS.toString()), + "https://twitch.tv/" + ) + } + + jda.presence.setStatus(OnlineStatus.fromKey(plugin.config.getString(Config.Paths.Discord.BOT_STATUS.toString()))) + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/dev/austech/betterstaffchat/common/discord/DiscordManager.kt b/common/src/main/kotlin/dev/austech/betterstaffchat/common/discord/DiscordManager.kt new file mode 100644 index 0000000..0113d25 --- /dev/null +++ b/common/src/main/kotlin/dev/austech/betterstaffchat/common/discord/DiscordManager.kt @@ -0,0 +1,45 @@ +package dev.austech.betterstaffchat.common.discord + +import dev.austech.betterstaffchat.common.BSCPlugin +import dev.austech.betterstaffchat.common.discord.impl.bot.DiscordEmbedProvider +import dev.austech.betterstaffchat.common.discord.impl.bot.DiscordTextProvider +import dev.austech.betterstaffchat.common.util.Config + +class DiscordManager(private val plugin: BSCPlugin) { + lateinit var discordClient: DiscordClient + private val messageProviderMap = mutableMapOf() + + init { + start() + } + + fun setupMessageProviderForBot() { + arrayOf("staffchat", "join", "leave", "switch").forEach { + plugin.config.getString(Config.Paths.Discord.Message(it).mode).let { m -> + if (m == "embed") messageProviderMap[it] = DiscordEmbedProvider(discordClient.jda) + else if (m == "text") messageProviderMap[it] = DiscordTextProvider(discordClient.jda) + } + } + } + + fun getMessageProvider(mode: String): DiscordMessageProvider? { + return messageProviderMap[mode] + } + + private fun start() { + plugin.config.getBoolean(Config.Paths.Discord.ENABLED.toString()) + .takeIf { it } + ?.let { + when (plugin.config.getString(Config.Paths.Discord.TYPE.toString())) { + "bot" -> { + discordClient = DiscordClient(plugin) + setupMessageProviderForBot() + } + "webhook" -> { + + } + else -> {} + } + } + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/dev/austech/betterstaffchat/common/discord/DiscordMessageProvider.kt b/common/src/main/kotlin/dev/austech/betterstaffchat/common/discord/DiscordMessageProvider.kt new file mode 100644 index 0000000..9012338 --- /dev/null +++ b/common/src/main/kotlin/dev/austech/betterstaffchat/common/discord/DiscordMessageProvider.kt @@ -0,0 +1,11 @@ +package dev.austech.betterstaffchat.common.discord + +import dev.austech.betterstaffchat.common.BSCMetadata +import dev.austech.betterstaffchat.common.BSCPlugin + +interface DiscordMessageProvider { + fun handleMessage(plugin: BSCPlugin, type: String, meta: BSCMetadata, placeholderProvider: (String) -> String) +// fun handleJoin(plugin: BSCPlugin, s: String, meta: BSCMetadata) +// fun handleLeave(plugin: BSCPlugin, s: String, meta: BSCMetadata) +// fun handleSwitch(plugin: BSCPlugin, s: String, from: String, to: String, meta: BSCMetadata) +} \ No newline at end of file diff --git a/common/src/main/kotlin/dev/austech/betterstaffchat/common/discord/impl/bot/DiscordEmbedProvider.kt b/common/src/main/kotlin/dev/austech/betterstaffchat/common/discord/impl/bot/DiscordEmbedProvider.kt new file mode 100644 index 0000000..df1e0df --- /dev/null +++ b/common/src/main/kotlin/dev/austech/betterstaffchat/common/discord/impl/bot/DiscordEmbedProvider.kt @@ -0,0 +1,25 @@ +package dev.austech.betterstaffchat.common.discord.impl.bot + +import dev.austech.betterstaffchat.common.BSCMetadata +import dev.austech.betterstaffchat.common.BSCPlugin +import dev.austech.betterstaffchat.common.discord.DiscordMessageProvider +import dev.austech.betterstaffchat.common.discord.util.DiscordUtil +import dev.austech.betterstaffchat.common.util.Config +import net.dv8tion.jda.api.JDA + +class DiscordEmbedProvider(private val jda: JDA): DiscordMessageProvider { + override fun handleMessage(plugin: BSCPlugin, type: String, meta: BSCMetadata, placeholderProvider: (String) -> String) { + val messages = Config.Paths.Discord.Message(type) + + val embed = DiscordUtil.buildEmbed(plugin.config.getSection(messages.embed)) { + placeholderProvider(plugin.staffChatUtil.replacePlaceholders(it, meta)) + }.build() + + DiscordUtil.getAllChannels(plugin.config, type).map { + jda.getTextChannelById(it) + }.forEach { + if (it == null) throw NullPointerException("Text channel could not be resolved!") + it.sendMessageEmbeds(embed).queue() + } + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/dev/austech/betterstaffchat/common/discord/impl/bot/DiscordListener.kt b/common/src/main/kotlin/dev/austech/betterstaffchat/common/discord/impl/bot/DiscordListener.kt new file mode 100644 index 0000000..a16f876 --- /dev/null +++ b/common/src/main/kotlin/dev/austech/betterstaffchat/common/discord/impl/bot/DiscordListener.kt @@ -0,0 +1,48 @@ +package dev.austech.betterstaffchat.common.discord.impl.bot + +import dev.austech.betterstaffchat.common.BSCMetadata +import dev.austech.betterstaffchat.common.BSCPlugin +import dev.austech.betterstaffchat.common.discord.util.DiscordUtil +import dev.austech.betterstaffchat.common.util.Config +import net.dv8tion.jda.api.entities.ChannelType +import net.dv8tion.jda.api.events.message.MessageReceivedEvent +import net.dv8tion.jda.api.hooks.ListenerAdapter + +class DiscordListener(private val plugin: BSCPlugin) : ListenerAdapter() { + override fun onMessageReceived(event: MessageReceivedEvent) { + if (event.channelType != ChannelType.TEXT && !event.isFromGuild) return + if (event.author.isBot) return + if (!DiscordUtil.isStaffChatChannel( + plugin.config.getSection(Config.Paths.Discord.BOT_CHANNELS.toString()), + event.channel.id, + event.guildChannel.guild.id + ) + ) return; + + val formattedUsername = plugin.config.getString(Config.Paths.Discord.NAME_FORMAT.toString()) + .replace("%username%", event.author.name) + .replace("%discriminator%", event.author.discriminator) + .replace("%tag%", event.author.asTag) + .replace("%nickname", event.member!!.effectiveName) + + val message = StringBuilder(event.message.contentStripped.trim()) + + if (event.message.attachments.isNotEmpty()) { + if (message.isBlank()) { + message.append("[Attachments] ") + + message.append( + event.message.attachments.joinToString(" ") { + it.url + } + ) + } + } + + plugin.staffChatUtil.sendMessage( + plugin.playerUtil.getReceiveAudience(), + message.toString(), + BSCMetadata(BSCMetadata.Discord(formattedUsername)) + ) + } +} diff --git a/common/src/main/kotlin/dev/austech/betterstaffchat/common/discord/impl/bot/DiscordTextProvider.kt b/common/src/main/kotlin/dev/austech/betterstaffchat/common/discord/impl/bot/DiscordTextProvider.kt new file mode 100644 index 0000000..59c8d8a --- /dev/null +++ b/common/src/main/kotlin/dev/austech/betterstaffchat/common/discord/impl/bot/DiscordTextProvider.kt @@ -0,0 +1,22 @@ +package dev.austech.betterstaffchat.common.discord.impl.bot + +import dev.austech.betterstaffchat.common.BSCMetadata +import dev.austech.betterstaffchat.common.BSCPlugin +import dev.austech.betterstaffchat.common.discord.DiscordMessageProvider +import dev.austech.betterstaffchat.common.discord.util.DiscordUtil +import dev.austech.betterstaffchat.common.util.Config +import net.dv8tion.jda.api.JDA + +class DiscordTextProvider(private val jda: JDA): DiscordMessageProvider { + override fun handleMessage(plugin: BSCPlugin, type: String, meta: BSCMetadata, placeholderProvider: (String) -> String) { + val messages = Config.Paths.Discord.Message(type) + val message = placeholderProvider(plugin.staffChatUtil.replacePlaceholders(messages.text, meta)) + + DiscordUtil.getAllChannels(plugin.config, type).map { + jda.getTextChannelById(it) + }.forEach { + if (it == null) throw NullPointerException("Text channel could not be resolved!") + it.sendMessage(message).queue() + } + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/dev/austech/betterstaffchat/common/discord/util/DiscordUtil.kt b/common/src/main/kotlin/dev/austech/betterstaffchat/common/discord/util/DiscordUtil.kt new file mode 100644 index 0000000..c9a35f0 --- /dev/null +++ b/common/src/main/kotlin/dev/austech/betterstaffchat/common/discord/util/DiscordUtil.kt @@ -0,0 +1,126 @@ +package dev.austech.betterstaffchat.common.discord.util + +import de.leonhard.storage.Yaml +import de.leonhard.storage.sections.FlatFileSection +import dev.austech.betterstaffchat.common.util.Config +import net.dv8tion.jda.api.EmbedBuilder +import java.awt.Color +import java.time.Instant +import java.util.* + +object DiscordUtil { + fun buildEmbed(section: FlatFileSection, replaceProvider: ((String) -> String)?): EmbedBuilder { + val embedBuilder = EmbedBuilder() + + if ( + section.getString("author.name") != null && + section.getString("author.name").isNotBlank() + ) { + embedBuilder.setAuthor( + applyPlaceholders(replaceProvider, section.getString("author.name")), + if (section.getString("author.url") != null && section.getString("author.url").isNotBlank()) + applyPlaceholders(replaceProvider, section.getString("author.url")) else null, + if (section.getString("author.icon-url") != null && section.getString("author.icon-url").isNotBlank()) + applyPlaceholders(replaceProvider, section.getString("author.icon-url")) else null, + ) + } + + if ( + section.getString("body.title") != null && + section.getString("body.title").isNotBlank() + ) { + embedBuilder.setTitle( + applyPlaceholders(replaceProvider, section.getString("body.title")), + if (section.getString("body.url") != null && section.getString("body.url").isNotBlank()) + applyPlaceholders(replaceProvider, section.getString("body.url")) + else null + ) + } + + embedBuilder.setDescription( + applyPlaceholders(replaceProvider, section.getString("body.description")) + ) + + embedBuilder.setColor(Color.decode(section.getString("body.color"))) + + if ( + section.getString("images.thumbnail") != null && + section.getString("images.thumbnail").isNotBlank() + ) { + embedBuilder.setThumbnail( + applyPlaceholders(replaceProvider, section.getString("images.thumbnail")) + ) + } + + if ( + section.getString("images.image") != null && + section.getString("images.image").isNotBlank() + ) { + embedBuilder.setImage( + applyPlaceholders(replaceProvider, section.getString("images.image")) + ) + } + + if (section.getBoolean("footer.timestamp")) { + embedBuilder.setTimestamp(Instant.now()) + } + + if ( + section.getString("footer.text") != null && + section.getString("footer.text").isNotBlank() + ) { + embedBuilder.setFooter( + applyPlaceholders(replaceProvider, section.getString("footer.text")), + if (section.getString("footer.icon-url") != null && section.getString("footer.icon-url").isNotBlank()) + applyPlaceholders(replaceProvider, section.getString("footer.icon-url")) + else null + ) + } + + if (section.get("fields") != null) { + loop@ for (i in 1..25) { + if (section.get("fields.$i") != null) { + embedBuilder.addField( + applyPlaceholders(replaceProvider, section.getString("fields.$i.name")), + applyPlaceholders(replaceProvider, section.getString("fields.$i.value")), + section.getBoolean("fields.$i.inline") + ) + } else break@loop + } + + if (section.get("fields.26") != null) { + throw IllegalArgumentException("Embed has 26+ fields, Discord only supports 25.") + } + } + + return embedBuilder + } + + private fun applyPlaceholders(replaceProvider: ((String) -> String)?, s: String): String { + if (replaceProvider == null) return s + return replaceProvider.invoke(s) + } + + fun isStaffChatChannel(section: FlatFileSection, channelId: String, guildId: String): Boolean { + return getChannels(section, guildId, "staffchat").contains(channelId) + } + + fun getChannel(section: FlatFileSection, type: String) { + section.getStringList("events.$type") + } + + fun getChannels(section: FlatFileSection, guildId: String, type: String): List { + return section.getStringList("$guildId.events.$type") + } + + fun getAllChannels(config: Yaml, type: String): List { + val channels = arrayListOf() + val section = config.getSection(Config.Paths.Discord.BOT_CHANNELS.toString()) + + section.singleLayerKeySet().map { + channels.addAll(section.getStringList("$it.events.$type")) + } + + return channels + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/dev/austech/betterstaffchat/common/libraries/BSCLibraries.kt b/common/src/main/kotlin/dev/austech/betterstaffchat/common/libraries/BSCLibraries.kt new file mode 100644 index 0000000..53475ec --- /dev/null +++ b/common/src/main/kotlin/dev/austech/betterstaffchat/common/libraries/BSCLibraries.kt @@ -0,0 +1,207 @@ +package dev.austech.betterstaffchat.common.libraries + +import dev.austech.betterstaffchat.common.BSCPlugin +import dev.austech.betterstaffchat.common.libraries.libby.Library + +object BSCLibraries { + private const val relocatePath = "dev{}austech{}betterstaffchat{}depend" + + private object Adventure { + private val adventureBuilder = Library.builder() + .groupId("net{}kyori") + .relocate("net{}kyori{}adventure", "$relocatePath{}adventure") + .relocate("net{}kyori{}examination", "$relocatePath{}adventure{}util{}examination") + + val API: Library = adventureBuilder + .artifactId("adventure-api") + .version("4.10.1") + .build() + + val NBT: Library = adventureBuilder + .artifactId("adventure-nbt") + .version("4.10.1") + .build() + + val KEY: Library = adventureBuilder + .artifactId("adventure-key") + .version("4.10.1") + .build() + + val MINIMESSAGE: Library = adventureBuilder + .artifactId("adventure-text-minimessage") + .version("4.10.1") + .build() + + val PLATFORM: Library = adventureBuilder + .artifactId("adventure-platform-api") + .version("4.1.0") + .build() + + val PLATFORM_FACET: Library = adventureBuilder + .artifactId("adventure-platform-facet") + .version("4.1.0") + .build() + + val PLATFORM_BUKKIT: Library = adventureBuilder + .artifactId("adventure-platform-bukkit") + .version("4.1.0") + .build() + + val PLATFORM_BUNGEECORD: Library = adventureBuilder + .artifactId("adventure-platform-bungee") + .version("4.1.0") + .build() + + val SERIALIZER_BUNGEECORD: Library = adventureBuilder + .artifactId("adventure-text-serializer-bungeecord") + .version("4.1.0") + .build() + + val SERIALIZER_LEGACY: Library = adventureBuilder + .artifactId("adventure-text-serializer-legacy") + .version("4.10.1") + .build() + + val SERIALIZER_GSON: Library = adventureBuilder + .artifactId("adventure-text-serializer-gson") + .version("4.10.1") + .build() + + val SERIALIZER_GSON_LEGACY_IMPL: Library = adventureBuilder + .artifactId("adventure-text-serializer-gson-legacy-impl") + .version("4.10.1") + .build() + + val EXAMINATION: Library = adventureBuilder + .artifactId("examination-api") + .version("1.3.0") + .build() + + val EXAMINATION_STRING: Library = adventureBuilder + .artifactId("examination-string") + .version("1.3.0") + .build() + } + + private val SIMPLIX_STORAGE = Library.builder() + .groupId("com{}github{}simplix-softworks") + .artifactId("SimplixStorage") + .version("3.2.3") + .relocate("de{}leonhard{}storage", "$relocatePath{}storage") + .build() + + private val JDA = Library.builder() + .groupId("net{}dv8tion") + .artifactId("JDA") + .version("5.0.0-alpha.9") + .relocate("net{}dv8tion{}jda", "$relocatePath{}discord") + .relocate("com{}neovisionaries{}nv-websocket-client", "$relocatePath{}nv-websocket-client") + .relocate("okhttp", "$relocatePath{}okhttp") + .relocate("org{}slf4j", "$relocatePath{}slf4j") + .relocate("org{}apache{}commons{}commons-collections4", "$relocatePath{}commons-collections4") + .relocate("com{}squareup{}okio", "$relocatePath{}okio") + .relocate("net{}sf{}trove4j", "$relocatePath{}trove4j") + .relocate("com{}fasterxml{}jackson{}databind", "$relocatePath{}jackson-databind") + .relocate("com{}fasterxml{}jackson{}annotations", "$relocatePath{}jackson-annotations") + .relocate("com{}fasterxml{}jackson{}core", "$relocatePath{}jackson-core") + .build() + + private val OKHTTP = Library.builder() + .groupId("com{}squareup{}okhttp3") + .artifactId("okhttp") + .version("4.9.3") + .relocate("okhttp", "$relocatePath{}okhttp") + .relocate("_okio".replace("_", ""), "$relocatePath{}okio") + .relocate("_kotlin".replace("_", ""), "$relocatePath{}kotlin") + .build() + + private val NV_WEBSOCKET_CLIENT = Library.builder() + .groupId("com{}neovisionaries") + .artifactId("nv-websocket-client") + .version("2.14") + .relocate("com{}neovisionaries{}nv-websocket-client", "$relocatePath{}nv-websocket-client") + .build() + + private val SLF4J = Library.builder() + .groupId("org{}slf4j") + .artifactId("slf4j-api") + .version("1.7.25") + .relocate("org{}slf4j", "$relocatePath{}slf4j") + .build() + + private val COMMONS_COLLECTIONS4 = Library.builder() + .groupId("org{}apache{}commons") + .artifactId("commons-collections4") + .version("4.1") + .relocate("org{}apache{}commons{}commons-collections4", "$relocatePath{}commons-collections4") + .build() + + private val TROVE4J = Library.builder() + .groupId("net{}sf{}trove4j") + .artifactId("trove4j") + .version("3.0.3") + .relocate("net{}sf{}trove4j", "$relocatePath{}trove4j") + .build() + + private val JACKSON_DATABIND = Library.builder() + .groupId("com{}fasterxml{}jackson{}core") + .artifactId("jackson-databind") + .version("2.10.1") + .relocate("com{}fasterxml{}jackson{}databind", "$relocatePath{}jackson-databind") + .relocate("com{}fasterxml{}jackson{}annotations", "$relocatePath{}jackson-annotations") + .relocate("com{}fasterxml{}jackson{}core", "$relocatePath{}jackson-core") + .build() + + private val JACKSON_ANNOTATIONS = Library.builder() + .groupId("com{}fasterxml{}jackson{}core") + .artifactId("jackson-annotations") + .version("2.10.1") + .relocate("com{}fasterxml{}jackson{}databind", "$relocatePath{}jackson-databind") + .relocate("com{}fasterxml{}jackson{}annotations", "$relocatePath{}jackson-annotations") + .relocate("com{}fasterxml{}jackson{}core", "$relocatePath{}jackson-core") + .build() + + private val JACKSON_CORE = Library.builder() + .groupId("com{}fasterxml{}jackson{}core") + .artifactId("jackson-core") + .version("2.10.1") + .relocate("com{}fasterxml{}jackson{}databind", "$relocatePath{}jackson-databind") + .relocate("com{}fasterxml{}jackson{}annotations", "$relocatePath{}jackson-annotations") + .relocate("com{}fasterxml{}jackson{}core", "$relocatePath{}jackson-core") + .build() + + fun getLibraries(platform: BSCPlugin.Platform?): List { + var libraryList: List = listOf( + Adventure.API, + Adventure.NBT, + Adventure.KEY, + Adventure.MINIMESSAGE, + Adventure.SERIALIZER_LEGACY, + Adventure.SERIALIZER_GSON, + Adventure.SERIALIZER_GSON_LEGACY_IMPL, + Adventure.PLATFORM, + Adventure.PLATFORM_FACET, + Adventure.EXAMINATION, + Adventure.EXAMINATION_STRING, + SIMPLIX_STORAGE, + JDA, + OKHTTP, + NV_WEBSOCKET_CLIENT, + SLF4J, + COMMONS_COLLECTIONS4, + TROVE4J, + JACKSON_DATABIND, + JACKSON_ANNOTATIONS, + JACKSON_CORE + ) + + if (platform == BSCPlugin.Platform.BUKKIT) { + libraryList = libraryList.plus(Adventure.PLATFORM_BUKKIT) + } else if (platform == BSCPlugin.Platform.BUNGEECORD) { + libraryList = libraryList.plus(Adventure.PLATFORM_BUNGEECORD) + .plus(Adventure.SERIALIZER_BUNGEECORD) + } + + return libraryList + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/dev/austech/betterstaffchat/common/update/UpdateChecker.kt b/common/src/main/kotlin/dev/austech/betterstaffchat/common/update/UpdateChecker.kt new file mode 100644 index 0000000..9936a30 --- /dev/null +++ b/common/src/main/kotlin/dev/austech/betterstaffchat/common/update/UpdateChecker.kt @@ -0,0 +1,48 @@ +package dev.austech.betterstaffchat.common.update + +import de.leonhard.storage.shaded.json.JSONObject // might as well steal it +import java.io.BufferedReader +import java.io.InputStreamReader +import java.net.URL +import javax.net.ssl.HttpsURLConnection + +class UpdateChecker(private val plugin: Any) { + val needsUpdate: Boolean + val currentVersion: String + val latestVersion: String + + init { + currentVersion = pluginVersion + latestVersion = fetchUpdate() ?: "ERROR" + needsUpdate = latestVersion != currentVersion + } + + private val pluginVersion: String + get() { + val method = plugin.javaClass.getMethod("getDescription") + method.isAccessible = true + val objDescription = method.invoke(plugin) + val versionMethod = objDescription.javaClass.getDeclaredMethod("getVersion") + versionMethod.isAccessible = true + return versionMethod.invoke(objDescription) as String? ?: "" + } + + private fun fetchUpdate(): String? { + val url = URL("https://api.spigotmc.org/simple/0.2/index.php?action=getResource&id=91991") + val connection = url.openConnection() as HttpsURLConnection + connection.requestMethod = "GET" + connection.readTimeout = 5_000 + connection.connectTimeout = 5_000 + + if (connection.responseCode != 200) { + throw Exception("Spigot API returned a non-200 response code") + } + + val bufferedReader = BufferedReader(InputStreamReader(connection.inputStream)) + + val jsonObject = JSONObject(bufferedReader.readLines().joinToString("")) + + bufferedReader.close() + return jsonObject.getString("current_version") + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/dev/austech/betterstaffchat/common/util/Config.kt b/common/src/main/kotlin/dev/austech/betterstaffchat/common/util/Config.kt new file mode 100644 index 0000000..49c8107 --- /dev/null +++ b/common/src/main/kotlin/dev/austech/betterstaffchat/common/util/Config.kt @@ -0,0 +1,115 @@ +package dev.austech.betterstaffchat.common.util + +import de.leonhard.storage.LightningBuilder +import de.leonhard.storage.Yaml +import dev.austech.betterstaffchat.common.BSCPlugin +import net.kyori.adventure.text.Component +import java.io.File +import java.nio.file.Files + +class Config(private val plugin: BSCPlugin) { + private val configFile: File = File(plugin.pluginDataFile, "config.yml") + + fun load(): Yaml { + plugin.pluginDataFile.mkdirs() + + if (!configFile.exists()) { + plugin.javaClass.getResourceAsStream((if (plugin.platform == BSCPlugin.Platform.BUKKIT) "/" else "") + "config.yml") + .use { + Files.copy(it, configFile.toPath()) + } + } + + return LightningBuilder.fromFile(configFile) + .reloadCallback { + plugin.consoleAudience.sendMessage( + Component.text("[BetterStaffChat] Reload requested or config.yml change. Reloading.") + ) + } + .createConfig() + } + + class Paths { + enum class Staffchat(private val path: String) { + FORMAT("staffchat.format"), + PREFIX("staffchat.prefix"), + CONSOLE_NAME("staffchat.console-replacement"), + CONSOLE_UUID("staffchat.console-uuid-replacement"), + CONSOLE_SERVER("staffchat.console-server-replacement"), + CONSOLE_LOG("staffchat.log-to-console"), + STRIP_COLOR_CODES("staffchat.strip-color-codes"), + SERVER_REPLACEMENTS("staffchat.server-replacement"), + DISABLED_SERVERS("staffchat.disabled-servers"), + + EVENTS_JOIN_ENABLED("staffchat.events.join.enabled"), + EVENTS_JOIN_SEE_OWN("staffchat.events.join.see-own-join"), + EVENTS_JOIN_MESSAGE("staffchat.events.join.message"), + EVENTS_LEAVE_ENABLED("staffchat.events.leave.enabled"), + EVENTS_LEAVE_MESSAGE("staffchat.events.leave.message"), + EVENTS_SWITCH_ENABLED("staffchat.events.switch.enabled"), + EVENTS_SWITCH_USE_WORLDS("staffchat.events.switch.use-worlds-on-spigot"), + EVENTS_SWITCH_MESSAGE("staffchat.events.switch.message"), + + MUTE_ENABLED("staffchat.mute.enabled"), + MUTE_MESSAGE_ON("staffchat.mute.on"), + MUTE_MESSAGE_OFF("staffchat.mute.off"), + TOGGLE_ENABLED("staffchat.toggle.enabled"), + TOGGLE_MESSAGE_ON("staffchat.toggle.on"), + TOGGLE_MESSAGE_OFF("staffchat.toggle.off"); + + override fun toString(): String { + return path + } + } + + enum class Command(private val path: String) { + STAFFCHAT_COMMAND("commands.staffchat.command"), + STAFFCHAT_ALIASES("commands.staffchat.aliases"), + MUTE_COMMAND("commands.mutestaffchat.command"), + MUTE_ALIASES("commands.mutestaffchat.aliases"), + TOGGLE_COMMAND("commands.togglestaffchat.command"), + TOGGLE_ALIASES("commands.togglestaffchat.aliases"); + + override fun toString(): String { + return path + } + } + + enum class Hooks(private val path: String) { + LUCKPERMS("hooks.luckperms"), + PLACEHOLDERAPI("hooks.placeholderapi"); + + override fun toString(): String { + return path + } + } + + enum class Discord(private val path: String) { + ENABLED("discord.enabled"), + TYPE("discord.type"), + + BOT_TOKEN("discord.bot.token"), + BOT_STATUS("discord.bot.status"), + BOT_ACTIVITY("discord.bot.activity.enabled"), + BOT_ACTIVITY_TYPE("discord.bot.activity.type"), + BOT_ACTIVITY_STATUS("discord.bot.activity.status"), + BOT_CHANNELS("discord.bot.channels"), + + WEBHOOK_URL("discord.webhook.url"), + + ALLOWED_MENTIONS("discord.allowed-mentions"), + + NAME_FORMAT("discord.messages.name-format"); + + override fun toString(): String { + return path + } + + class Message(event: String) { + val mode = "discord.messages.$event.mode" + val embed = "discord.messages.$event.embed" + val text = "discord.messages.$event.text" + } + } + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/dev/austech/betterstaffchat/common/util/Data.kt b/common/src/main/kotlin/dev/austech/betterstaffchat/common/util/Data.kt new file mode 100644 index 0000000..80dd2cb --- /dev/null +++ b/common/src/main/kotlin/dev/austech/betterstaffchat/common/util/Data.kt @@ -0,0 +1,46 @@ +package dev.austech.betterstaffchat.common.util + +import de.leonhard.storage.Json +import dev.austech.betterstaffchat.common.BSCPlugin +import java.io.File +import java.util.* + +class Data(private val plugin: BSCPlugin) { + private val dataFile: File = File(plugin.pluginDataFile, "data.json") + + class Wrapper(private val `data`: Json) { + fun getToggledPlayers(): List { + return `data`.getOrSetDefault>("toggledPlayers", listOf()).map { UUID.fromString(it) } + } + + fun addToggledPlayer(uuid: UUID) { + return `data`.set("toggledPlayers", getToggledPlayers().plus(uuid.toString())) + } + + fun removeToggledPlayer(uuid: UUID) { + return `data`.set("toggledPlayers", getToggledPlayers().minus(uuid.toString())) + } + + fun getMutedPlayers(): List { + return `data`.getOrSetDefault>("mutedPlayers", listOf()).map { UUID.fromString(it) } + } + + fun addMutedPlayer(uuid: UUID) { + return `data`.set("mutedPlayers", getMutedPlayers().plus(uuid.toString())) + } + + fun removeMutedPlayer(uuid: UUID) { + return `data`.set("mutedPlayers", getMutedPlayers().minus(uuid.toString())) + } + } + + fun load(): Json { + plugin.pluginDataFile.mkdirs() + + if (!dataFile.exists()) { + dataFile.createNewFile() + } + + return Json(dataFile) + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/dev/austech/betterstaffchat/common/util/IPlayerUtil.kt b/common/src/main/kotlin/dev/austech/betterstaffchat/common/util/IPlayerUtil.kt new file mode 100644 index 0000000..be5f7a8 --- /dev/null +++ b/common/src/main/kotlin/dev/austech/betterstaffchat/common/util/IPlayerUtil.kt @@ -0,0 +1,7 @@ +package dev.austech.betterstaffchat.common.util + +import net.kyori.adventure.audience.Audience + +interface IPlayerUtil { + fun getReceiveAudience(): Audience +} \ No newline at end of file diff --git a/common/src/main/kotlin/dev/austech/betterstaffchat/common/util/StaffChatUtil.kt b/common/src/main/kotlin/dev/austech/betterstaffchat/common/util/StaffChatUtil.kt new file mode 100644 index 0000000..4932392 --- /dev/null +++ b/common/src/main/kotlin/dev/austech/betterstaffchat/common/util/StaffChatUtil.kt @@ -0,0 +1,139 @@ +package dev.austech.betterstaffchat.common.util + +import dev.austech.betterstaffchat.common.BSCPlugin +import dev.austech.betterstaffchat.common.BSCMetadata +import net.kyori.adventure.audience.Audience +import java.util.* + +class StaffChatUtil(private val plugin: BSCPlugin) { + + private lateinit var replaceProvider: (UUID, String) -> String + + private val discordEnabled: Boolean + get() = plugin.config.getBoolean(Config.Paths.Discord.ENABLED.toString()) + + constructor(plugin: BSCPlugin, consumer: (u: UUID, s: String) -> String) : this(plugin) { + replaceProvider = consumer + } + + fun replacePlaceholders(s: String, meta: BSCMetadata): String { + if (meta.discordMeta != null) + return s + .replace("%player_name%", meta.discordMeta!!.displayName ?: "") + .replace("%player_server%", "Discord") + .replace("%player_server_raw%", "Discord") + + if (meta.playerMeta == null) return s + + val playerMeta = meta.playerMeta!! + + var newString = s + .replace("%player_name%", playerMeta.name) + + if (playerMeta.uuid != null) { + newString = newString.replace("%player_uuid%", playerMeta.uuid.toString()) + } + + if (meta.proxyMeta != null) { + val proxy: BSCMetadata.Proxy = meta.proxyMeta!! + newString = newString + .replace("%player_server%", proxy.server) + .replace("%player_server_raw%", proxy.serverRaw) + } + + if (meta.spigotMeta != null) { + val spigot: BSCMetadata.Spigot = meta.spigotMeta!! + + if (spigot.world != null) { + newString = newString + .replace("%player_world%", spigot.world) + } + } + + if (meta.luckpermsMeta != null) { + val luckperms: BSCMetadata.LuckPerms = meta.luckpermsMeta!! + + if (luckperms.group != null && luckperms.groupDisplayName != null) { + newString = newString + .replace("%luckperms_group%", luckperms.group) + .replace("%luckperms_group_displayname%", luckperms.groupDisplayName) + } + + if (luckperms.prefix != null) { + newString = newString.replace("%luckperms_prefix%", luckperms.prefix) + } + + if (luckperms.suffix != null) { + newString = newString.replace("%luckperms_suffix%", luckperms.suffix) + } + } + + if (playerMeta.uuid != null) { + replaceProvider.invoke(playerMeta.uuid, newString) + } + + return newString; + } + + fun sendMessage(audience: Audience, message: String, meta: BSCMetadata) { + var configMessage: String = plugin.config.getString(Config.Paths.Staffchat.FORMAT.toString()) ?: return; + val removeColorCodes = plugin.config.getBoolean(Config.Paths.Staffchat.STRIP_COLOR_CODES.toString()); + + configMessage = replacePlaceholders(configMessage, meta) + .replace("%message%", + if (removeColorCodes) + TextUtil.escapeTags(message) + else message + ) + + audience.sendMessage(TextUtil.parseText(configMessage)) + + if (discordEnabled && meta.discordMeta == null) { + plugin.discordManager.getMessageProvider("staffchat")?.handleMessage(plugin, "staffchat", meta) { + it.replace("%message%", message) + } + } + } + + fun sendJoinMessage(audience: Audience, meta: BSCMetadata) { + var configMessage: String = plugin.config.getString(Config.Paths.Staffchat.EVENTS_JOIN_MESSAGE.toString()) ?: return; + configMessage = replacePlaceholders(configMessage, meta) + + audience.sendMessage(TextUtil.parseText(configMessage)) + + if (discordEnabled) { + plugin.discordManager.getMessageProvider("staffchat")?.handleMessage(plugin, "staffchat", meta) { + it + } + } + } + + fun sendLeaveMessage(audience: Audience, meta: BSCMetadata) { + var configMessage: String = plugin.config.getString(Config.Paths.Staffchat.EVENTS_LEAVE_MESSAGE.toString()) ?: return; + configMessage = replacePlaceholders(configMessage, meta) + + audience.sendMessage(TextUtil.parseText(configMessage)) + + if (discordEnabled) { + plugin.discordManager.getMessageProvider("staffchat")?.handleMessage(plugin, "staffchat", meta) { + it + } + } + } + + fun sendSwitchMessage(audience: Audience, from: String, to: String, meta: BSCMetadata) { + var configMessage: String = plugin.config.getString(Config.Paths.Staffchat.EVENTS_SWITCH_MESSAGE.toString()) ?: return; + configMessage = replacePlaceholders(configMessage, meta) + configMessage = configMessage + .replace("%from%", from) + .replace("%to%", to) + + audience.sendMessage(TextUtil.parseText(configMessage)) + + if (discordEnabled) { + plugin.discordManager.getMessageProvider("staffchat")?.handleMessage(plugin, "staffchat", meta) { + it.replace("%from%", from).replace("%to%", to) + } + } + } +} \ No newline at end of file diff --git a/common/src/main/kotlin/dev/austech/betterstaffchat/common/util/TextUtil.kt b/common/src/main/kotlin/dev/austech/betterstaffchat/common/util/TextUtil.kt new file mode 100644 index 0000000..b3de47b --- /dev/null +++ b/common/src/main/kotlin/dev/austech/betterstaffchat/common/util/TextUtil.kt @@ -0,0 +1,16 @@ +package dev.austech.betterstaffchat.common.util + +import net.kyori.adventure.text.Component +import net.kyori.adventure.text.minimessage.MiniMessage + +object TextUtil { + private val miniMessage: MiniMessage = MiniMessage.miniMessage() + + fun escapeTags(s: String): String { + return miniMessage.escapeTags(s) + } + + fun parseText(s: String): Component { + return miniMessage.deserialize(s) + } +} \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 5c2d1cf..e708b1c 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 6ecaf8a..0f80bbf 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,21 +1,3 @@ -# -# BetterStaffChat - gradle-wrapper.properties -# Copyright (C) 2021 AusTech Development Team -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# - distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip diff --git a/gradlew b/gradlew index f45fe38..4f906e0 100755 --- a/gradlew +++ b/gradlew @@ -1,21 +1,19 @@ #!/usr/bin/env sh # -# BetterStaffChat - gradlew -# Copyright (C) 2021 AusTech Development Team +# Copyright 2015 the original author or authors. # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# https://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. # ############################################################################## @@ -84,6 +82,7 @@ esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then @@ -131,6 +130,7 @@ fi if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` # We build the pattern for arguments to be converted via cygpath @@ -156,19 +156,19 @@ if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then else eval `echo args$i`="\"$arg\"" fi - i=$((i+1)) + i=`expr $i + 1` done case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; esac fi @@ -177,14 +177,9 @@ save () { for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done echo " " } -APP_ARGS=$(save "$@") +APP_ARGS=`save "$@"` # Collect all arguments for the java command, following the shell quoting and substitution rules eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" -# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong -if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then - cd "$(dirname "$0")" -fi - exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index 9618d8d..107acd3 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -29,6 +29,9 @@ if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" @@ -37,7 +40,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init +if "%ERRORLEVEL%" == "0" goto execute echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. @@ -51,7 +54,7 @@ goto fail set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe -if exist "%JAVA_EXE%" goto init +if exist "%JAVA_EXE%" goto execute echo. echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% @@ -61,28 +64,14 @@ echo location of your Java installation. goto fail -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* - :execute @rem Setup the command line set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* :end @rem End local scope for the variables with windows NT shell diff --git a/settings.gradle b/settings.gradle deleted file mode 100644 index e709c2d..0000000 --- a/settings.gradle +++ /dev/null @@ -1,23 +0,0 @@ -/* - * BetterStaffChat - settings.gradle - * Copyright (C) 2021 AusTech Development Team - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -rootProject.name = 'BetterStaffChat' - -include 'BetterStaffChat-common' -include 'BetterStaffChat-spigot' -include 'BetterStaffChat-bungeecord' diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 0000000..e463722 --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1,11 @@ +/* + * This file was generated by the Gradle 'init' task. + * + * The settings file is used to specify which projects to include in your build. + * + * Detailed information about configuring a multi-project build in Gradle can be found + * in the user manual at https://docs.gradle.org/7.0.2/userguide/multi_project_builds.html + */ + +rootProject.name = "BetterStaffChat" +include("common", "spigot", "bungeecord") diff --git a/spigot/build.gradle.kts b/spigot/build.gradle.kts new file mode 100644 index 0000000..ddcc58f --- /dev/null +++ b/spigot/build.gradle.kts @@ -0,0 +1,18 @@ +repositories { + mavenCentral() + + maven { + url = uri("https://hub.spigotmc.org/nexus/content/repositories/snapshots/") + } + + maven { + url = uri("https://repo.extendedclip.com/content/repositories/placeholderapi/") + } +} + +dependencies { + compileOnly(project(":common")) + compileOnly("org.spigotmc:spigot-api:1.18.2-R0.1-SNAPSHOT") + compileOnly("net.kyori:adventure-platform-bukkit:4.1.0") + compileOnly("me.clip:placeholderapi:2.11.1") +} \ No newline at end of file diff --git a/spigot/src/main/java/dev/austech/betterstaffchat/spigot/BSCSpigotBootstrap.java b/spigot/src/main/java/dev/austech/betterstaffchat/spigot/BSCSpigotBootstrap.java new file mode 100644 index 0000000..ee547b2 --- /dev/null +++ b/spigot/src/main/java/dev/austech/betterstaffchat/spigot/BSCSpigotBootstrap.java @@ -0,0 +1,29 @@ +package dev.austech.betterstaffchat.spigot; + +import dev.austech.betterstaffchat.common.libraries.KotlinLibraries; +import dev.austech.betterstaffchat.common.libraries.libby.LibraryManager; +import dev.austech.betterstaffchat.spigot.libby.BukkitLibraryManager; +import org.bukkit.plugin.java.JavaPlugin; + +public class BSCSpigotBootstrap extends JavaPlugin { + private BSCSpigot implPlugin; + + @Override + public void onLoad() { + LibraryManager libraryManager = new BukkitLibraryManager(this); + libraryManager.addMavenCentral(); + libraryManager.addJitPack(); + + libraryManager.loadLibrary(KotlinLibraries.STDLIB); + libraryManager.loadLibrary(KotlinLibraries.STDLIB_COMMON); + libraryManager.loadLibrary(KotlinLibraries.STDLIB_JDK8); + + implPlugin = new BSCSpigot(this); + implPlugin.onLoad(); + } + + @Override + public void onEnable() { + implPlugin.onEnable(); + } +} diff --git a/spigot/src/main/java/dev/austech/betterstaffchat/spigot/libby/BukkitLibraryManager.java b/spigot/src/main/java/dev/austech/betterstaffchat/spigot/libby/BukkitLibraryManager.java new file mode 100644 index 0000000..6548e5c --- /dev/null +++ b/spigot/src/main/java/dev/austech/betterstaffchat/spigot/libby/BukkitLibraryManager.java @@ -0,0 +1,51 @@ +package dev.austech.betterstaffchat.spigot.libby; + +import dev.austech.betterstaffchat.common.libraries.libby.LibraryManager; +import dev.austech.betterstaffchat.common.libraries.libby.classloader.URLClassLoaderHelper; +import dev.austech.betterstaffchat.common.libraries.libby.logging.adapters.JDKLogAdapter; +import org.bukkit.plugin.Plugin; + +import java.net.URLClassLoader; +import java.nio.file.Path; + +import static java.util.Objects.requireNonNull; + +/** + * A runtime dependency manager for Bukkit plugins. + */ +public class BukkitLibraryManager extends LibraryManager { + /** + * Plugin classpath helper + */ + private final URLClassLoaderHelper classLoader; + + /** + * Creates a new Bukkit library manager. + * + * @param plugin the plugin to manage + */ + public BukkitLibraryManager(Plugin plugin) { + this(plugin, "libs"); + } + + /** + * Creates a new Bukkit library manager. + * + * @param plugin the plugin to manage + * @param directoryName download directory name + */ + public BukkitLibraryManager(Plugin plugin, String directoryName) { + super(new JDKLogAdapter(requireNonNull(plugin, "plugin").getLogger()), plugin.getDataFolder().toPath(), directoryName); + classLoader = new URLClassLoaderHelper((URLClassLoader) plugin.getClass().getClassLoader(), this); + } + + /** + * Adds a file to the Bukkit plugin's classpath. + * + * @param file the file to add + */ + @Override + protected void addToClasspath(Path file) { + classLoader.addToClasspath(file); + } +} diff --git a/spigot/src/main/kotlin/dev/austech/betterstaffchat/spigot/BSCSpigot.kt b/spigot/src/main/kotlin/dev/austech/betterstaffchat/spigot/BSCSpigot.kt new file mode 100644 index 0000000..a96227d --- /dev/null +++ b/spigot/src/main/kotlin/dev/austech/betterstaffchat/spigot/BSCSpigot.kt @@ -0,0 +1,88 @@ +package dev.austech.betterstaffchat.spigot + +import de.leonhard.storage.Json +import de.leonhard.storage.Yaml +import dev.austech.betterstaffchat.common.BSCPlugin +import dev.austech.betterstaffchat.common.discord.DiscordManager +import dev.austech.betterstaffchat.common.libraries.BSCLibraries +import dev.austech.betterstaffchat.common.update.UpdateChecker +import dev.austech.betterstaffchat.common.util.Config +import dev.austech.betterstaffchat.common.util.Data +import dev.austech.betterstaffchat.common.util.IPlayerUtil +import dev.austech.betterstaffchat.common.util.StaffChatUtil +import dev.austech.betterstaffchat.spigot.commands.CommandManager +import dev.austech.betterstaffchat.spigot.listeners.PlayerListener +import dev.austech.betterstaffchat.spigot.libby.BukkitLibraryManager +import dev.austech.betterstaffchat.spigot.util.PlayerUtil +import me.clip.placeholderapi.PlaceholderAPI +import net.kyori.adventure.audience.Audience +import net.kyori.adventure.platform.bukkit.BukkitAudiences +import org.bukkit.Bukkit +import org.bukkit.ChatColor +import org.bukkit.plugin.java.JavaPlugin +import java.io.File + +class BSCSpigot(private val plugin: JavaPlugin) : BSCPlugin { + companion object { + lateinit var instance: BSCSpigot + } + + override lateinit var staffChatUtil: StaffChatUtil + override val playerUtil = PlayerUtil + lateinit var audience: BukkitAudiences + private lateinit var data: Json + lateinit var dataWrapper: Data.Wrapper; + override lateinit var config: Yaml + override lateinit var consoleAudience: Audience; + override lateinit var discordManager: DiscordManager + override val platform = BSCPlugin.Platform.BUKKIT + override val pluginDataFile: File = plugin.dataFolder + lateinit var updateChecker: UpdateChecker + + override fun onLoad() { + val libraryManager = BukkitLibraryManager(plugin) + libraryManager.addMavenCentral() + libraryManager.addJitPack() + + BSCLibraries.getLibraries(platform).forEach { + libraryManager.loadLibrary(it) + } + } + + override fun onEnable() { + instance = this; + audience = BukkitAudiences.create(plugin) + consoleAudience = audience.console() + config = Config(this).load() + data = Data(this).load() + dataWrapper = Data.Wrapper(data) + updateChecker = UpdateChecker(plugin) + + Bukkit.getScheduler().runTaskAsynchronously(plugin, Runnable { + discordManager = DiscordManager(this) + }) + + Bukkit.getScheduler().runTaskLater(plugin, Runnable { + if (updateChecker.needsUpdate) { + plugin.logger.warning("An update for BetterStaffChat is available.") + plugin.logger.warning("Latest version: ${ChatColor.GREEN}${updateChecker.latestVersion}${ChatColor.YELLOW}. Your version: ${ChatColor.RED}${updateChecker.currentVersion}") + plugin.logger.warning("Update: ${ChatColor.BLUE}https://www.spigotmc.org/resources/91991/") + } + }, 1L) + + val shouldHookPAPI = Bukkit.getPluginManager().isPluginEnabled("PlaceholderAPI") && + config.getBoolean(Config.Paths.Hooks.PLACEHOLDERAPI.toString()) + + staffChatUtil = if (shouldHookPAPI) { + StaffChatUtil(this) { uuid, s -> + PlaceholderAPI.setPlaceholders(Bukkit.getPlayer(uuid), s) + } + } else { + StaffChatUtil(this) + } + + CommandManager(this).registerCommands() + + Bukkit.getPluginManager().registerEvents(PlayerListener(), plugin) + } +} \ No newline at end of file diff --git a/spigot/src/main/kotlin/dev/austech/betterstaffchat/spigot/commands/BSCSpigotCommand.kt b/spigot/src/main/kotlin/dev/austech/betterstaffchat/spigot/commands/BSCSpigotCommand.kt new file mode 100644 index 0000000..4b534f1 --- /dev/null +++ b/spigot/src/main/kotlin/dev/austech/betterstaffchat/spigot/commands/BSCSpigotCommand.kt @@ -0,0 +1,62 @@ +package dev.austech.betterstaffchat.spigot.commands + +import dev.austech.betterstaffchat.spigot.BSCSpigot +import net.kyori.adventure.text.Component +import org.bukkit.ChatColor +import org.bukkit.command.Command +import org.bukkit.command.CommandSender +import org.bukkit.entity.Player + +abstract class BSCSpigotCommand(name: String, description: String, usage: String, aliases: List) : Command(name, description, usage, aliases) { + private lateinit var sender: CommandSender + protected lateinit var plugin: BSCSpigot + + abstract fun run(sender: CommandSender, commandLabel: String, args: Array): Boolean; + + override fun execute(sender: CommandSender, commandLabel: String, args: Array): Boolean { + this.sender = sender; + this.plugin = BSCSpigot.instance + + return run(sender, commandLabel, args) + } + + fun CommandSender.isConsole(): Boolean { + return this !is Player + } + + fun CommandSender.requirePlayer(): Boolean { + if (this.isConsole()) { + errorTell("You must be a player to use this command.") + return false + } + return true + } + + fun CommandSender.requirePermission(permission: String): Boolean { + return requirePermission(permission, "You do not have permission.") + } + + fun CommandSender.requirePermission(permission: String, message: String): Boolean { + if (!hasPermission(permission)) { + errorTell(message) + return false + } + return true + } + + fun tell(component: Component) { + plugin.audience.sender(this.sender).sendMessage(component) + } + + fun legacyTell(s: String) { + return this.sender.sendMessage(ChatColor.translateAlternateColorCodes('&', s)) + } + + fun errorTell(s: String) { + return legacyTell("&c$s") + } + + fun warnTell(s: String) { + legacyTell("&e$s") + } +} \ No newline at end of file diff --git a/spigot/src/main/kotlin/dev/austech/betterstaffchat/spigot/commands/CommandManager.kt b/spigot/src/main/kotlin/dev/austech/betterstaffchat/spigot/commands/CommandManager.kt new file mode 100644 index 0000000..ec8319c --- /dev/null +++ b/spigot/src/main/kotlin/dev/austech/betterstaffchat/spigot/commands/CommandManager.kt @@ -0,0 +1,40 @@ +package dev.austech.betterstaffchat.spigot.commands + +import dev.austech.betterstaffchat.common.util.Config +import dev.austech.betterstaffchat.spigot.BSCSpigot +import dev.austech.betterstaffchat.spigot.commands.impl.BetterStaffChatCommand +import dev.austech.betterstaffchat.spigot.commands.impl.StaffChatCommand +import dev.austech.betterstaffchat.spigot.commands.impl.StaffChatMuteCommand +import dev.austech.betterstaffchat.spigot.commands.impl.StaffChatToggleCommand +import dev.austech.betterstaffchat.spigot.util.ReflectionUtil +import org.bukkit.command.Command + +class CommandManager(private val plugin: BSCSpigot) { + private val commands: List = listOf( + StaffChatCommand( + plugin.config.getString(Config.Paths.Command.STAFFCHAT_COMMAND.toString())?: "staffchat", + "This command lets you send a message to the staffchat.", + "", + plugin.config.getStringList(Config.Paths.Command.STAFFCHAT_ALIASES.toString()) + ), + StaffChatMuteCommand( + plugin.config.getString(Config.Paths.Command.MUTE_COMMAND.toString())?: "mutestaffchat", + "This command lets you ignore / disable your staffchat.", + "[on/off]", + plugin.config.getStringList(Config.Paths.Command.MUTE_ALIASES.toString()) + ), + StaffChatToggleCommand( + plugin.config.getString(Config.Paths.Command.TOGGLE_COMMAND.toString())?: "togglestaffchat", + "This command lets you ignore / disable your staffchat.", + "[on/off]", + plugin.config.getStringList(Config.Paths.Command.TOGGLE_ALIASES.toString()) + ), + BetterStaffChatCommand() + ) + + fun registerCommands() { + commands.forEach { + ReflectionUtil.registerCommand(it) + } + } +} \ No newline at end of file diff --git a/spigot/src/main/kotlin/dev/austech/betterstaffchat/spigot/commands/impl/BetterStaffChatCommand.kt b/spigot/src/main/kotlin/dev/austech/betterstaffchat/spigot/commands/impl/BetterStaffChatCommand.kt new file mode 100644 index 0000000..8966816 --- /dev/null +++ b/spigot/src/main/kotlin/dev/austech/betterstaffchat/spigot/commands/impl/BetterStaffChatCommand.kt @@ -0,0 +1,21 @@ +package dev.austech.betterstaffchat.spigot.commands.impl + +import dev.austech.betterstaffchat.spigot.commands.BSCSpigotCommand +import org.bukkit.command.CommandSender + +class BetterStaffChatCommand: BSCSpigotCommand("betterstaffchat", "The main command for BetterStaffChat", "", listOf("bsc")) { + override fun run(sender: CommandSender, commandLabel: String, args: Array): Boolean { + when (args[0]) { + "reload" -> { + if (!sender.requirePermission("betterstaffchat.reload")) return true; + warnTell("It is not recommended to forcefully reload BetterStaffChat. It will automatically reload when the config is edited.") + plugin.config.forceReload() + } + else -> { + legacyTell("&7Running &9BetterStaffChat &7by &6AusTech Development&7.") + } + } + + return true; + } +} \ No newline at end of file diff --git a/spigot/src/main/kotlin/dev/austech/betterstaffchat/spigot/commands/impl/StaffChatCommand.kt b/spigot/src/main/kotlin/dev/austech/betterstaffchat/spigot/commands/impl/StaffChatCommand.kt new file mode 100644 index 0000000..bbc6398 --- /dev/null +++ b/spigot/src/main/kotlin/dev/austech/betterstaffchat/spigot/commands/impl/StaffChatCommand.kt @@ -0,0 +1,39 @@ +package dev.austech.betterstaffchat.spigot.commands.impl + +import dev.austech.betterstaffchat.common.BSCMetadata +import dev.austech.betterstaffchat.spigot.commands.BSCSpigotCommand +import dev.austech.betterstaffchat.spigot.util.PlayerUtil +import org.bukkit.command.CommandSender +import org.bukkit.entity.Player + +class StaffChatCommand(name: String, description: String, usage: String, aliases: List) : BSCSpigotCommand(name, description, usage, aliases) { + override fun run(sender: CommandSender, commandLabel: String, args: Array): Boolean { + if (!sender.requirePermission("betterstaffchat.messages.send")) { + return true + } + + if (args.isEmpty()) { + errorTell("&cUsage: /$commandLabel ") + return true; + } + + if (sender is Player && plugin.dataWrapper.getMutedPlayers().contains(sender.uniqueId)) { + errorTell("Your staffchat is currently muted.") + return true + } + + plugin.staffChatUtil.sendMessage(PlayerUtil.getReceiveAudience(), args.joinToString(separator = " "), + BSCMetadata( + BSCMetadata.Player( + if (sender is Player) sender.uniqueId else null, + PlayerUtil.getSenderName(sender) + ), + BSCMetadata.Spigot( + if (sender is Player) sender.world.name else null, + ) + ) + ) + + return true; + } +} \ No newline at end of file diff --git a/spigot/src/main/kotlin/dev/austech/betterstaffchat/spigot/commands/impl/StaffChatMuteCommand.kt b/spigot/src/main/kotlin/dev/austech/betterstaffchat/spigot/commands/impl/StaffChatMuteCommand.kt new file mode 100644 index 0000000..5e25f30 --- /dev/null +++ b/spigot/src/main/kotlin/dev/austech/betterstaffchat/spigot/commands/impl/StaffChatMuteCommand.kt @@ -0,0 +1,62 @@ +package dev.austech.betterstaffchat.spigot.commands.impl + +import dev.austech.betterstaffchat.common.util.Config +import dev.austech.betterstaffchat.spigot.commands.BSCSpigotCommand +import org.bukkit.command.CommandSender +import org.bukkit.entity.Player + +class StaffChatMuteCommand(name: String, description: String, usage: String, aliases: List) : BSCSpigotCommand(name, description, usage, aliases) { + override fun run(sender: CommandSender, commandLabel: String, args: Array): Boolean { + if (!sender.requirePermission("betterstaffchat.mutestaffchat")) { + return true + } + + if (!plugin.config.getBoolean(Config.Paths.Staffchat.MUTE_ENABLED.toString())) { + errorTell("Staffchat mute is not enabled.") + return true + } + + if (!sender.requirePlayer()) { + return true + } + + if (sender !is Player) { + throw IllegalStateException("Sender is not a player!") + } + + val mute: Boolean + val mutedPlayers = plugin.dataWrapper.getMutedPlayers() + + if (args.isNotEmpty()) { + if (args[0].equals("off", true)) { + if (!mutedPlayers.contains(sender.uniqueId)) { + errorTell("Your staffchat is not muted.") + return true + } + + mute = false + } else if (args[0].equals("on", true)) { + if (mutedPlayers.contains(sender.uniqueId)) { + errorTell("Your staffchat is already muted.") + return true + } + + mute = true + } else { + mute = !mutedPlayers.contains(sender.uniqueId) + } + } else { + mute = !mutedPlayers.contains(sender.uniqueId) + } + + if (mute) { + plugin.dataWrapper.addMutedPlayer(sender.uniqueId) + legacyTell(plugin.config.getString(Config.Paths.Staffchat.MUTE_MESSAGE_ON.toString())) + } else { + plugin.dataWrapper.removeMutedPlayer(sender.uniqueId) + legacyTell(plugin.config.getString(Config.Paths.Staffchat.MUTE_MESSAGE_OFF.toString())) + } + + return true + } +} \ No newline at end of file diff --git a/spigot/src/main/kotlin/dev/austech/betterstaffchat/spigot/commands/impl/StaffChatToggleCommand.kt b/spigot/src/main/kotlin/dev/austech/betterstaffchat/spigot/commands/impl/StaffChatToggleCommand.kt new file mode 100644 index 0000000..598900c --- /dev/null +++ b/spigot/src/main/kotlin/dev/austech/betterstaffchat/spigot/commands/impl/StaffChatToggleCommand.kt @@ -0,0 +1,69 @@ +package dev.austech.betterstaffchat.spigot.commands.impl + +import dev.austech.betterstaffchat.common.util.Config +import dev.austech.betterstaffchat.spigot.commands.BSCSpigotCommand +import org.bukkit.command.CommandSender +import org.bukkit.entity.Player + +class StaffChatToggleCommand(name: String, description: String, usage: String, aliases: List) : BSCSpigotCommand(name, description, usage, aliases) { + override fun run(sender: CommandSender, commandLabel: String, args: Array): Boolean { + if (!sender.requirePermission("betterstaffchat.togglestaffchat")) { + return true + } + + if (!plugin.config.getBoolean(Config.Paths.Staffchat.TOGGLE_ENABLED.toString())) { + errorTell("Staffchat toggle is not enabled.") + return true + } + + if (!sender.requirePlayer()) { + return true + } + + if (sender !is Player) { + throw IllegalStateException("Sender is not a player!") + } + + val toggleOn: Boolean + val toggledPlayers = plugin.dataWrapper.getToggledPlayers() + + if (args.isNotEmpty()) { + if (args[0].equals("off", true)) { + if (!toggledPlayers.contains(sender.uniqueId)) { + errorTell("Your staffchat is not toggled.") + return true + } + + toggleOn = false + } else if (args[0].equals("on", true)) { + if (toggledPlayers.contains(sender.uniqueId)) { + errorTell("Your staffchat is already toggled.") + return true + } + + toggleOn = true + } else { + toggleOn = !toggledPlayers.contains(sender.uniqueId) + } + } else { + toggleOn = !toggledPlayers.contains(sender.uniqueId) + } + + if (toggleOn) { + val mutedPlayers = plugin.dataWrapper.getMutedPlayers() + + if (mutedPlayers.contains(sender.uniqueId)) { + errorTell("Your staffchat is muted. Please unmute it before toggling.") + return true + } + + plugin.dataWrapper.addToggledPlayer(sender.uniqueId) + legacyTell(plugin.config.getString(Config.Paths.Staffchat.TOGGLE_MESSAGE_ON.toString())) + } else { + plugin.dataWrapper.removeToggledPlayer(sender.uniqueId) + legacyTell(plugin.config.getString(Config.Paths.Staffchat.TOGGLE_MESSAGE_OFF.toString())) + } + + return true + } +} \ No newline at end of file diff --git a/spigot/src/main/kotlin/dev/austech/betterstaffchat/spigot/listeners/PlayerListener.kt b/spigot/src/main/kotlin/dev/austech/betterstaffchat/spigot/listeners/PlayerListener.kt new file mode 100644 index 0000000..9fd25ce --- /dev/null +++ b/spigot/src/main/kotlin/dev/austech/betterstaffchat/spigot/listeners/PlayerListener.kt @@ -0,0 +1,119 @@ +package dev.austech.betterstaffchat.spigot.listeners + +import dev.austech.betterstaffchat.common.BSCMetadata +import dev.austech.betterstaffchat.common.util.Config +import dev.austech.betterstaffchat.spigot.BSCSpigot +import dev.austech.betterstaffchat.spigot.util.PlayerUtil +import net.kyori.adventure.audience.Audience +import org.bukkit.event.EventHandler +import org.bukkit.event.EventPriority +import org.bukkit.event.Listener +import org.bukkit.event.player.AsyncPlayerChatEvent +import org.bukkit.event.player.PlayerChangedWorldEvent +import org.bukkit.event.player.PlayerJoinEvent +import org.bukkit.event.player.PlayerQuitEvent + +class PlayerListener : Listener { + @EventHandler(priority = EventPriority.HIGH) + fun onAsyncPlayerChat(event: AsyncPlayerChatEvent) { + if (event.player.hasPermission("betterstaffchat.messages.send")) { + val prefix = BSCSpigot.instance.config.getString(Config.Paths.Staffchat.PREFIX.toString()) ?: "" + + if (event.message.startsWith(prefix) && event.message.length > prefix.length && prefix.isNotBlank()) { + event.isCancelled = true; + BSCSpigot.instance.staffChatUtil.sendMessage( + PlayerUtil.getReceiveAudience(), event.message.substring(prefix.length), + BSCMetadata( + BSCMetadata.Player( + event.player.uniqueId, + PlayerUtil.getSenderName(event.player) + ), + BSCMetadata.Spigot( + event.player.world.name, + ) + ) + ) + } else if (BSCSpigot.instance.dataWrapper.getToggledPlayers() + .contains(event.player.uniqueId) && BSCSpigot.instance.config.getBoolean(Config.Paths.Staffchat.TOGGLE_ENABLED.toString()) + ) { + event.isCancelled = true; + BSCSpigot.instance.staffChatUtil.sendMessage( + PlayerUtil.getReceiveAudience(), event.message, + BSCMetadata( + BSCMetadata.Player( + event.player.uniqueId, + PlayerUtil.getSenderName(event.player) + ), + BSCMetadata.Spigot( + event.player.world.name, + ) + ) + ) + } + } + } + + @EventHandler + fun onPlayerJoin(event: PlayerJoinEvent) { + if (!BSCSpigot.instance.config.getBoolean(Config.Paths.Staffchat.EVENTS_JOIN_ENABLED.toString())) return + if (event.player.hasPermission("betterstaffchat.messages.join")) { + val aud: Audience = + if (!BSCSpigot.instance.config.getBoolean(Config.Paths.Staffchat.EVENTS_JOIN_SEE_OWN.toString())) + PlayerUtil.getReceiveAudienceWithIgnored(event.player.uniqueId) + else PlayerUtil.getReceiveAudience() + + BSCSpigot.instance.staffChatUtil.sendJoinMessage( + aud, + BSCMetadata( + BSCMetadata.Player( + event.player.uniqueId, + PlayerUtil.getSenderName(event.player) + ), + BSCMetadata.Spigot( + event.player.world.name, + ) + ) + ) + } + } + + @EventHandler + fun onPlayerQuit(event: PlayerQuitEvent) { + if (!BSCSpigot.instance.config.getBoolean(Config.Paths.Staffchat.EVENTS_LEAVE_ENABLED.toString())) return + if (event.player.hasPermission("betterstaffchat.messages.leave")) { + BSCSpigot.instance.staffChatUtil.sendLeaveMessage( + PlayerUtil.getReceiveAudience(), + BSCMetadata( + BSCMetadata.Player( + event.player.uniqueId, + PlayerUtil.getSenderName(event.player) + ), + BSCMetadata.Spigot( + event.player.world.name, + ) + ) + ) + } + } + + @EventHandler + fun onPlayerSwitch(event: PlayerChangedWorldEvent) { + if (!BSCSpigot.instance.config.getBoolean(Config.Paths.Staffchat.EVENTS_SWITCH_ENABLED.toString())) return + if (!BSCSpigot.instance.config.getBoolean(Config.Paths.Staffchat.EVENTS_SWITCH_USE_WORLDS.toString())) return + + if (event.player.hasPermission("betterstaffchat.messages.leave")) { + BSCSpigot.instance.staffChatUtil.sendSwitchMessage( + PlayerUtil.getReceiveAudience(), event.from.name, event.player.world.name, + BSCMetadata( + BSCMetadata.Player( + event.player.uniqueId, + PlayerUtil.getSenderName(event.player) + ), + BSCMetadata.Spigot( + event.player.world.name, + ) + ) + ) + } + } +} \ No newline at end of file diff --git a/spigot/src/main/kotlin/dev/austech/betterstaffchat/spigot/util/PlayerUtil.kt b/spigot/src/main/kotlin/dev/austech/betterstaffchat/spigot/util/PlayerUtil.kt new file mode 100644 index 0000000..e762fbb --- /dev/null +++ b/spigot/src/main/kotlin/dev/austech/betterstaffchat/spigot/util/PlayerUtil.kt @@ -0,0 +1,51 @@ +package dev.austech.betterstaffchat.spigot.util + +import dev.austech.betterstaffchat.common.util.Config +import dev.austech.betterstaffchat.common.util.IPlayerUtil +import dev.austech.betterstaffchat.spigot.BSCSpigot +import net.kyori.adventure.audience.Audience +import org.bukkit.command.CommandSender +import org.bukkit.entity.Player +import java.util.* + +object PlayerUtil: IPlayerUtil { + fun getSenderName(sender: CommandSender): String { + return if (sender is Player) sender.name + else BSCSpigot.instance.config.getString(Config.Paths.Staffchat.CONSOLE_NAME.toString()) ?: "Console" + } + + private fun getMutedPlayers(): List { + return BSCSpigot.instance.dataWrapper.getMutedPlayers() + } + + private fun getAudienceWithIgnored(permission: String, ignored: UUID) = BSCSpigot.instance.audience.filter { + it.hasPermission(permission) && it is Player && it.uniqueId != ignored && !getMutedPlayers().contains(it.uniqueId) + } + + private fun getAudienceMixedWithIgnored(permission: String, ignored: UUID) = BSCSpigot.instance.audience.filter { + it.hasPermission(permission) && if (it is Player) it.uniqueId != ignored && !getMutedPlayers().contains(it.uniqueId) else true + } + + fun getReceiveAudienceWithIgnored(ignored: UUID): Audience { + return if (BSCSpigot.instance.config.getBoolean(Config.Paths.Staffchat.CONSOLE_LOG.toString())) getAudienceMixedWithIgnored( + "betterstaffchat.messages.read", + ignored + ) + else getAudienceWithIgnored("betterstaffchat.messages.read", ignored) + } + + private fun getPermissionAudience(permission: String): Audience = BSCSpigot.instance.audience.filter { + it.hasPermission(permission) && it is Player && !getMutedPlayers().contains(it.uniqueId) + } + + private fun getMixedAudience(permission: String): Audience = BSCSpigot.instance.audience.filter { + it.hasPermission(permission) && if (it is Player) !getMutedPlayers().contains(it.uniqueId) else true + } + + override fun getReceiveAudience(): Audience { + return if (BSCSpigot.instance.config.getBoolean(Config.Paths.Staffchat.CONSOLE_LOG.toString())) getMixedAudience( + "betterstaffchat.messages.read" + ) + else getPermissionAudience("betterstaffchat.messages.read") + } +} \ No newline at end of file diff --git a/spigot/src/main/kotlin/dev/austech/betterstaffchat/spigot/util/ReflectionUtil.kt b/spigot/src/main/kotlin/dev/austech/betterstaffchat/spigot/util/ReflectionUtil.kt new file mode 100644 index 0000000..632cf40 --- /dev/null +++ b/spigot/src/main/kotlin/dev/austech/betterstaffchat/spigot/util/ReflectionUtil.kt @@ -0,0 +1,27 @@ +package dev.austech.betterstaffchat.spigot.util + +import org.bukkit.Bukkit +import org.bukkit.command.Command +import org.bukkit.command.SimpleCommandMap + +object ReflectionUtil { + private val serverClass by lazy { + "org.bukkit.craftbukkit." + Bukkit.getServer().javaClass.`package`.name.substring(23) + ".CraftServer" + } + + private fun getCommandMap(): SimpleCommandMap? { + return try { + Class.forName(serverClass).getDeclaredMethod("getCommandMap").invoke(Bukkit.getServer()) as SimpleCommandMap + } catch (e: Exception) { + e.printStackTrace() + null + } + } + + fun registerCommand(command: Command) { + getCommandMap()?.register(command.name, "betterstaffchat", command) + if (!command.isRegistered) { + throw RuntimeException("Failed to register command: " + command.name); + } + } +} \ No newline at end of file diff --git a/src/main/resources/bungeecord.yml b/src/main/resources/bungeecord.yml new file mode 100644 index 0000000..6ec62a2 --- /dev/null +++ b/src/main/resources/bungeecord.yml @@ -0,0 +1,3 @@ +name: BetterStaffChat +main: dev.austech.betterstaffchat.bungeecord.BSCBungeeBootstrap +version: ${version} diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml new file mode 100644 index 0000000..8688914 --- /dev/null +++ b/src/main/resources/config.yml @@ -0,0 +1,404 @@ +# BetterStaffChat +# The better staffchat plugin. + +# Copyright (c) 2022 AusTech Development Team +# Licensed under GNU General Public License v3 + +# Github profile: https://github.com/AusTechDev/ +# Spigot Profile: https://www.spigotmc.org/members/919057/ +# Discord Server: https://austech.dev/to/support + +# You can request a feature or make a suggestion at https://github.com/AusTechDev/BetterStaffChat/discussions/new?category=ideas-features + +# Message Placeholders in all messages (except specified otherwise): +# %player_name% - The player's name +# %player_uuid% - The player's UUID +# %player_server% - The replaced name of the server the player is on (staffchat.server-replacement) (BungeeCord Only) +# %player_server_raw% - The raw name of the server the player is on (BungeeCord Only) +# %player_world% - The name of the world the player is in (Spigot Only) + +# Hook Placeholders: +# LuckPerms +# %luckperms_group% - The player's group +# %luckperms_group_displayname% - The player's group's display name +# %luckperms_prefix% - The player's prefix +# %luckperms_suffix% - The player's suffix +# %luckperms_meta_% - The player's LP metadata for the given key + +# Message Format: +# We use modern libraries to format messages. +# Every message can be configured with MiniMessage. +# https://docs.adventure.kyori.net/minimessage/format.html#standard-tags +# +# Using legacy '&'-style formatting is not supported. It will throw an error. +# You can use this site to convert legacy formatting to MiniMessage: +# - https://tehbrian.xyz/legacy-to-minimessage/ + +# PlaceholderAPI is available in every message on Spigot ONLY. PlaceholderAPI will not work on BungeeCord. + +staffchat: + # Extra Placeholders available: + # %message% - The message sent by the player + format: "[S] %player_name%: %message%" + + # Putting whatever is below before your message will send it to staffchat + # e.g # hello there + prefix: "# " # Set to "" to disable + + # Set the name of the sender when console runs /staffchat + console-replacement: "Console" + + # Set the UUID of the console when %uuid% is used in embeds + console-uuid-replacement: "f78a4d8d-d51b-4b39-98a3-230f2de0c670" + + # Set the name of the sender when console runs /staffchat + # ONLY BUNGEE + console-server-replacement: "Global" + + # Whether to send staffchat messages to console + log-to-console: true + + # If a staff member puts color codes in their message, it will remove them if the below is true. + strip-color-codes: false + + # BungeeCord Only + # Changes the display name of servers on BungeeCord. + # MiniMessage formatting is supported. + # Placeholders are not available. + server-replacement: # Format - servername: "displayname" + hub: "Hub" # Make sure that the first value is the one equal to in the BungeeCord config.yml + + # BungeeCord Only + # Servers where the StaffChat has no effect. + disabled-servers: + - "testserver123" + + events: + + join: + # Enable or disable the join message event. + enabled: true + + # Whether a player sees their own join message. + see-own-join: true + + # This message supports all the placeholders above. + message: "[S] %player_name% joined the network." + leave: + # Enable or disable the leave message event. + enabled: true + + # This message supports all the placeholders above. + message: "[S] %player_name% left the network." + + switch: + # Enable or disable the switch message event. + enabled: true + + # Whether to enable this on Spigot and use worlds instead of servers. + use-worlds-on-spigot: false + + # This message supports all the placeholders above. + message: "[S] %player_name% switched from %from% to %to%." + + # BetterStaffChat has a feature called /staffchatmute. This mutes/ignores your own staffchat, and does not + # mute the staffchat of other players. This can also be called staffchatignore. + mute: + + # Whether to enable the self-mute / ignore feature. + enabled: true + + # The message to send the player when they enable their mute. + # This message supports all the placeholders above. + on: "[S] Your staff chat is now disabled." + + # The message to send the player when they disable their mute. + # This message supports all the placeholders above. + off: "[S] Your staff chat is now enabled." + + # The two above messages are "reversed", as their staffchat is disabled when they enable mute. + + # BetterStaffChat also has a feature called /togglestaffchat. This makes ALL of the + # player's messages go to staffchat, and not to a public chat. + toggle: + # Whether to enable the toggle feature. + enabled: true + + # The message to send the player when they enable their toggle. + # This message supports all the placeholders above. + on: "[S] All your messages will now go to staffchat." + + # The message to send the player when they enable their toggle. + # This message supports all the placeholders above. + off: "[S] All your messages will no longer go to staffchat." + +discord: + # BetterStaffChat has two types of Discord integration. + # The first type is using a Discord Bot. This allows two way chat between + # the in-game staffchat and a staffchat channel on Discord. + # This is the recommended type. + # + # The second type is using a Discord Webhook. This allows the staffchat to be + # sent to a Discord channel, but the messages in Discord will not get sent to + # the game. + # + # Bot: In-Game StaffChat -> Discord AND Discord -> In-Game StaffChat + # Webhook: In-Game StaffChat -> Discord + + enabled: false + + # The type of integration to use. + # This can be either "bot" or "webhook". + type: "bot" + + bot: + # If you are using a Discord Bot, you need to set the token here. + # You can create a Discord bot at: + # https://discord.com/developers/applications + # + # You can follow this guide: + # https://discordpy.readthedocs.io/en/stable/discord.html + + # !!! [IMPORTANT] Please make sure the "Server Members Intent" and "Message Content Intent" are enabled. + # If you do not enable it, Discord -> In-Game StaffChat will not work. + + token: "" + + # The status of the bot. + # This can be either "online", "idle", "dnd", or "invisible". + status: "online" + + activity: + # Whether to enable the bot's activity. + # For example, this is the "Playing with staffchat" under the bot. + enabled: true + + # The type of activity. + # This can be either "playing", "streaming", "listening", or "watching". + type: "playing" + + # The name of the activity. + status: "with staffchat" + + # The channels and guilds to send the staffchat to. + # You can find the ID of a guild and channel with this guide: + # https://support.discord.com/hc/en-us/articles/206346498-Where-can-I-find-my-User-Server-Message-ID- + # + # The format for this section can be found below. + # If you wish to disable a certain event, set it to [] + # For example: + # events: + # leave: [] + # + # This would disable leave messages being sent to Discord, however, they will still function in-game. + + channels: + GuildID: + events: + staffchat: [ ChannelID ] + join: [ ChannelID ] + leave: [ ChannelID ] + switch: [ ChannelID ] + + webhook: + # https://support.discord.com/hc/en-us/articles/228383668-Intro-to-Webhooks + webhook-url: "" + + # Whether the bot / webhook can mention certain roles & users. + # It is recommended to keep this on none, even if you don't use any mentions. + # This is to prevent people from mentioning the bot or webhook in-game which can lead to some spammy situations. + # + # This can be either "none (no-one can be mentioned), "users" (all users can be mentioned), "roles" (all roles nad users can be mentioned) + # or "all" (all roles, users and @everyone/here can be mentioned). + allowed-mentions: "none" + + messages: + # The format in game, for when someone sends a message in Discord + # The placeholders available are: + # %username% - The username of the person who sent the message + # %discriminator% - The discriminator of the person who sent the message (4 digits in their tag) + # %tag% - The tag of the person who sent the message. This is the username and discriminator combined. + # %nickname% - The nickname of the person who sent the message, or, if they don't have one, their username. + # %message% - The message that was sent. + + name-format: "%username%#%discriminator%" + + staffchat: + # This dictates how messages are sent. + # This can be either "embed" or "text". + mode: "embed" + + # You can view the parts to an embed here: + # https://anidiots.guide/.gitbook/assets/first-bot-embed-example.png + + # Please see the relative event sections for more information about the placeholders. + + embed: + author: + name: "%player_name%" + url: "" # This is optional. + icon-url: "https://minotar.net/helm/%player_name%/128.png" + body: + title: "" + title-url: "" + description: | + %message% + color: "#FFD555" + # fields: # This is commented out by default. If you wish to enable fields, uncomment this. + # 1: + # name: "" + # value: "" + # inline: false + images: + image: "" + thumbnail: "" + + footer: + timestamp: false # If this is true, the current time will be used. + text: "BetterStaffChat" + icon-url: "" + + text: "**%player_name%**: %message%" + join: + # This dictates how messages are sent. + # This can be either "embed" or "text". + mode: "embed" + + # You can view the parts to an embed here: + # https://anidiots.guide/.gitbook/assets/first-bot-embed-example.png + + # Please see the relative event sections for more information about the placeholders. + + embed: + author: + name: "%player_name%" + url: "" # This is optional. + icon-url: "https://minotar.net/helm/%player_name%/128.png" + body: + title: "" + title-url: "" + description: | + %player_name% **joined** the network. + color: "#FFD555" + # fields: # This is commented out by default. If you wish to enable fields, uncomment this. + # 1: + # name: "" + # value: "" + # inline: false + images: + image: "" + thumbnail: "" + + footer: + timestamp: false # If this is true, the current time will be used. + text: "BetterStaffChat" + icon-url: "" + + text: "%player_name% **joined** the network." + leave: + # This dictates how messages are sent. + # This can be either "embed" or "text". + mode: "embed" + + # You can view the parts to an embed here: + # https://anidiots.guide/.gitbook/assets/first-bot-embed-example.png + + # Please see the relative event sections for more information about the placeholders. + + embed: + author: + name: "%player_name%" + url: "" # This is optional. + icon-url: "https://minotar.net/helm/%player_name%/128.png" + body: + title: "" + title-url: "" + description: | + %player_name% **left** the network. + color: "#FFD555" + # fields: # This is commented out by default. If you wish to enable fields, uncomment this. + # 1: + # name: "" + # value: "" + # inline: false + images: + image: "" + thumbnail: "" + + footer: + timestamp: false # If this is true, the current time will be used. + text: "BetterStaffChat" + icon-url: "" + + text: "%player_name% **left** the network." + switch: + # This dictates how messages are sent. + # This can be either "embed" or "text". + mode: "embed" + + # You can view the parts to an embed here: + # https://anidiots.guide/.gitbook/assets/first-bot-embed-example.png + + # Please see the relative event sections for more information about the placeholders. + + embed: + author: + name: "%player_name%" + url: "" # This is optional. + icon-url: "https://minotar.net/helm/%player_name%/128.png" + body: + title: "" + title-url: "" + description: | + %player_name% **switched** from **%from%** to **%to%**. + color: "#FFD555" + # fields: # This is commented out by default. If you wish to enable fields, uncomment this. + # 1: + # name: "" + # value: "" + # inline: false + images: + image: "" + thumbnail: "" + + footer: + timestamp: false # If this is true, the current time will be used. + text: "BetterStaffChat" + icon-url: "" + + text: "%player_name% **switched** from **%from%** to **%to%**." + +commands: + # This section allows you to change the commands and aliases used for the staffchat. + + staffchat: + command: "staffchat" # The main command to be used. + aliases: [ sc, schat ] # The aliases for the command. Using the main command or duplicates won't affect anything. + + mutestaffchat: + command: "mutestaffchat" # The main command to be used. + aliases: [ msc, isc, hidesc, staffchatmute, ignorestaffchat ] # The aliases for the command. Using the main command or duplicates won't affect anything. + + togglestaffchat: + command: "staffchat" # The main command to be used. + aliases: [ tsc, sct, sctoggle, staffchattoggle, togglesc ] # The aliases for the command. Using the main command or duplicates won't affect anything. + +hooks: + # Enables hooking into LuckPerms. See placeholders above. + luckperms: true + + # Enabled hooking into PlaceHolderAPI. This will only have an effect on Spigot. + placeholderapi: true + +# Disable this to stop the plugin checking for updates on startup. +check-for-updates: true + +# Disable this to stop the plugin sending a message to those who have the `betterstaffchat.updatenotify` permission on join. +send-updates-to-admins: true + +# Don't touch unless you know what you are doing +# Most of the time nothing should debug. +debug: false + +# no touchy me please [DO NOT TOUCH AS IT CAN BREAK YOUR CONFIG] +config-version: 5 \ No newline at end of file diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml new file mode 100644 index 0000000..cf88abc --- /dev/null +++ b/src/main/resources/plugin.yml @@ -0,0 +1,6 @@ +name: BetterStaffChat +main: dev.austech.betterstaffchat.spigot.BSCSpigotBootstrap +version: ${version} +api-version: 1.13 + +softdepend: [ PlaceholderAPI ] \ No newline at end of file