From cf1d47e3daf2a1501cf95f86a577aa17f17ac021 Mon Sep 17 00:00:00 2001 From: dowoge Date: Tue, 31 Jan 2023 23:38:19 -0500 Subject: [PATCH] real af (i hate java) --- .gitignore | 113 +++++ pom.xml | 85 ++++ src/main/java/testplugin/plugin/Config.java | 31 ++ .../testplugin/plugin/DiscordWebhook.java | 392 ++++++++++++++++++ src/main/java/testplugin/plugin/Events.java | 128 ++++++ .../plugin/WebhookChangedEvent.java | 25 ++ .../testplugin/plugin/WebhookEventLogger.java | 55 +++ .../testplugin/plugin/setWebhookCommand.java | 28 ++ src/main/resources/plugin.yml | 22 + 9 files changed, 879 insertions(+) create mode 100644 .gitignore create mode 100644 pom.xml create mode 100644 src/main/java/testplugin/plugin/Config.java create mode 100644 src/main/java/testplugin/plugin/DiscordWebhook.java create mode 100644 src/main/java/testplugin/plugin/Events.java create mode 100644 src/main/java/testplugin/plugin/WebhookChangedEvent.java create mode 100644 src/main/java/testplugin/plugin/WebhookEventLogger.java create mode 100644 src/main/java/testplugin/plugin/setWebhookCommand.java create mode 100644 src/main/resources/plugin.yml diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4788b4b --- /dev/null +++ b/.gitignore @@ -0,0 +1,113 @@ +# User-specific stuff +.idea/ + +*.iml +*.ipr +*.iws + +# IntelliJ +out/ + +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +target/ + +pom.xml.tag +pom.xml.releaseBackup +pom.xml.versionsBackup +pom.xml.next + +release.properties +dependency-reduced-pom.xml +buildNumber.properties +.mvn/timing.properties +.mvn/wrapper/maven-wrapper.jar +.flattened-pom.xml + +# Common working directory +run/ diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..3ce3630 --- /dev/null +++ b/pom.xml @@ -0,0 +1,85 @@ + + + 4.0.0 + + serverLog + serverStatusLogger + 1.1 + jar + + Server status logger + + + plugin to log server events to discord via webhook + + + 1.8 + UTF-8 + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + 17 + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + ${java.version} + ${java.version} + + + + org.apache.maven.plugins + maven-shade-plugin + 3.2.4 + + + package + + shade + + + false + + + + + + + + src/main/resources + true + + + + + + + paper + https://repo.papermc.io/repository/maven-public/ + + + sonatype + https://oss.sonatype.org/content/groups/public/ + + + + + + io.papermc.paper + paper-api + 1.19.3-R0.1-SNAPSHOT + provided + + + diff --git a/src/main/java/testplugin/plugin/Config.java b/src/main/java/testplugin/plugin/Config.java new file mode 100644 index 0000000..10b37b1 --- /dev/null +++ b/src/main/java/testplugin/plugin/Config.java @@ -0,0 +1,31 @@ +package testplugin.plugin; + +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.plugin.Plugin; + +public class Config { + + private static Plugin plugin; + + public Config(Plugin plugin) { + this.plugin = plugin; + } + public static void loadConfig() { + + String webhookURLPath = "webhookURL"; + + FileConfiguration config = plugin.getConfig(); + + config.addDefault(webhookURLPath,"https://discord.com/api/webhooks/xxx/aaaa-bbbb-ccc"); + config.options().copyDefaults(true); + + plugin.saveConfig(); + } + + public static void setValue(String path, String value) { + plugin.getConfig().set(path,value); + } + public static Object getValue(String path) { + return plugin.getConfig().get(path); + } +} diff --git a/src/main/java/testplugin/plugin/DiscordWebhook.java b/src/main/java/testplugin/plugin/DiscordWebhook.java new file mode 100644 index 0000000..8c68dae --- /dev/null +++ b/src/main/java/testplugin/plugin/DiscordWebhook.java @@ -0,0 +1,392 @@ +package testplugin.plugin; + +import javax.net.ssl.HttpsURLConnection; +import java.awt.Color; +import java.io.IOException; +import java.io.OutputStream; +import java.lang.reflect.Array; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * 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(StandardCharsets.UTF_8)); + 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; + } + + private class Footer { + private String text; + private String iconUrl; + + private Footer(String text, String iconUrl) { + this.text = text; + this.iconUrl = iconUrl; + } + + private String getText() { + return text; + } + + private 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/src/main/java/testplugin/plugin/Events.java b/src/main/java/testplugin/plugin/Events.java new file mode 100644 index 0000000..fc652c8 --- /dev/null +++ b/src/main/java/testplugin/plugin/Events.java @@ -0,0 +1,128 @@ +package testplugin.plugin; + +import org.bukkit.Bukkit; +import org.bukkit.command.defaults.BukkitCommand; +import org.bukkit.entity.Player; +import org.bukkit.event.Event; +import org.bukkit.event.Event; +import org.bukkit.event.EventHandler; +import org.bukkit.event.HandlerList; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.PlayerDeathEvent; +import org.bukkit.event.player.AsyncPlayerChatEvent; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.player.PlayerQuitEvent; + +import java.awt.*; +import java.io.IOException; +import java.util.logging.Logger; + +public class Events implements Listener { + + private Logger logger; + private WebhookEventLogger plugin; + private String webhookURL; + + public Events(Logger logger, WebhookEventLogger plugin, String webhookURL) { + this.logger = logger; + this.plugin = plugin; + this.webhookURL = webhookURL; + } + + @EventHandler(priority = EventPriority.LOW) + public void onPlayerJoin(PlayerJoinEvent event) { + int playerCount = Bukkit.getOnlinePlayers().size(); + + Player player = event.getPlayer(); + + DiscordWebhook Webhook = new DiscordWebhook(webhookURL); + + Webhook.setContent(player.getName() + " joined the game (" + playerCount + '/' + Bukkit.getMaxPlayers() + ')'); + + try { + Webhook.execute(); + } catch (IOException e) { + logger.severe(e.getStackTrace().toString()); + } + } + @EventHandler(priority = EventPriority.LOW) + public void onPlayerLeave(PlayerQuitEvent event) { + int playerCount = Bukkit.getOnlinePlayers().size() - 1; + + Player player = event.getPlayer(); + + DiscordWebhook Webhook = new DiscordWebhook(webhookURL); + + Webhook.setContent(player.getName() + " left the game (" + playerCount + '/' + Bukkit.getMaxPlayers() + ')'); + + try { + Webhook.execute(); + } catch (IOException e) { + logger.severe(e.getStackTrace().toString()); + } + } + + @EventHandler(priority = EventPriority.LOW) + public void onPlayerChat(AsyncPlayerChatEvent event) { + String message = event.getMessage(); + Player player = event.getPlayer(); + + DiscordWebhook Webhook = new DiscordWebhook(webhookURL); + + Webhook.setContent('<' + player.getName() + "> " + message.replaceAll("(@everyone|@here)", "`$&`")); + + try { + Webhook.execute(); + } catch (IOException e) { + logger.severe(e.getStackTrace().toString()); + } + } + + public static int getLvlForXP(int xp) { + if (xp <= 255) { + return xp / 17; + } else if (xp > 272 && xp < 887) { + return (int) ((Math.sqrt(24 * xp - 5159) + 59) / 6); + } else if (xp > 825) { + return (int) ((Math.sqrt(56 * xp - 32511) + 303) / 14); + } + return 0; + } + + public static int toLevel(int xp) { + if (xp <= 352) { + return (int) Math.round(Math.sqrt(xp+9)-3); + } else if (xp >= 394 && xp <= 1507) { + return (int) Math.round((Math.sqrt(40*xp-7839)+81)*0.1); + } else if (xp >= 1628) { + return (int) Math.round((Math.sqrt(72*xp-54215)+325)/18); + } + return 0; + } + + @EventHandler(priority = EventPriority.LOW) + public void onPlayerDeath(PlayerDeathEvent event) { + DiscordWebhook Webhook = new DiscordWebhook(webhookURL); + + String deathMessage = event.getDeathMessage(); + int level = event.getPlayer().getLevel(); + + Webhook.setContent(deathMessage + " (had " + level + (level != 1 ? " levels" : " level") + ")"); + + try { + Webhook.execute(); + } catch (IOException e) { + logger.severe(e.getStackTrace().toString()); + } + } + + @EventHandler(priority = EventPriority.LOW) + public void onWebhookChanged(WebhookChangedEvent event) { + this.webhookURL = event.getMessage(); + Config config = new Config(this.plugin); + logger.warning("webhook changed!"); + config.setValue("webhookURL",this.webhookURL); + this.plugin.saveConfig(); + } +} \ No newline at end of file diff --git a/src/main/java/testplugin/plugin/WebhookChangedEvent.java b/src/main/java/testplugin/plugin/WebhookChangedEvent.java new file mode 100644 index 0000000..05e8974 --- /dev/null +++ b/src/main/java/testplugin/plugin/WebhookChangedEvent.java @@ -0,0 +1,25 @@ +package testplugin.plugin; + +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; + +public final class WebhookChangedEvent extends Event { + private static final HandlerList handlers = new HandlerList(); + private String message; + + public WebhookChangedEvent(String msg) { + message = msg; + } + + public String getMessage() { + return message; + } + + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/src/main/java/testplugin/plugin/WebhookEventLogger.java b/src/main/java/testplugin/plugin/WebhookEventLogger.java new file mode 100644 index 0000000..20aeb0b --- /dev/null +++ b/src/main/java/testplugin/plugin/WebhookEventLogger.java @@ -0,0 +1,55 @@ +package testplugin.plugin; + +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.plugin.java.JavaPlugin; + +import java.awt.*; +import java.io.IOException; + +public final class WebhookEventLogger extends JavaPlugin { + private String webhookURL; + + @Override + public void onEnable() { + // Plugin startup logic + Config config = new Config(this); + + config.loadConfig(); + + getLogger().info("Starting"); + + this.webhookURL = config.getValue("webhookURL").toString(); + + getServer().getPluginManager().registerEvents(new Events(getLogger(), this, webhookURL), this); + + getCommand("webhook").setExecutor(new setWebhookCommand()); + + + DiscordWebhook Webhook = new DiscordWebhook(webhookURL); + + Webhook.addEmbed(new DiscordWebhook.EmbedObject() + .setTitle("Server started.") + .setColor(new Color(155, 225, 120)) + ); + try { + Webhook.execute(); + } catch (IOException e) { + getLogger().severe(e.getStackTrace().toString()); + } + } + @Override + public void onDisable() { + // Plugin shutdown logic + DiscordWebhook Webhook = new DiscordWebhook(webhookURL); + + Webhook.addEmbed(new DiscordWebhook.EmbedObject() + .setTitle("Server stopped.") + .setColor(new Color(220, 75, 40)) + ); + try { + Webhook.execute(); + } catch (IOException e) { + getLogger().severe(e.getStackTrace().toString()); + } + } +} diff --git a/src/main/java/testplugin/plugin/setWebhookCommand.java b/src/main/java/testplugin/plugin/setWebhookCommand.java new file mode 100644 index 0000000..6487e9c --- /dev/null +++ b/src/main/java/testplugin/plugin/setWebhookCommand.java @@ -0,0 +1,28 @@ +package testplugin.plugin; + + +import org.bukkit.Bukkit; +import org.bukkit.Server; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.entity.Player; +import org.bukkit.plugin.Plugin; + +public class setWebhookCommand implements CommandExecutor { + // This method is called, when somebody uses our command + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + if (sender instanceof Player | sender instanceof Server && args.length == 1) { +// Server server = sender.getServer(); +// FileConfiguration config = ; + String webhookURL = args[0]; + WebhookChangedEvent event = new WebhookChangedEvent(webhookURL); + Bukkit.getServer().getPluginManager().callEvent(event); + sender.sendMessage("Set webhook URL to " + webhookURL); + return true; + } + return false; + } +} diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml new file mode 100644 index 0000000..9ef0c4a --- /dev/null +++ b/src/main/resources/plugin.yml @@ -0,0 +1,22 @@ +name: WebhookEventLogger +version: '${project.version}' +main: testplugin.plugin.WebhookEventLogger +api-version: 1.19 +authors: [ tommy ] +description: test + +commands: + webhook: + + permission: webhook.can_set + + description: Set the webhook url used to send in-game event logs + + permission-message: You do not have permission to use this command. + + usage: / [url] + +permissions: + webhook.can_set: + description: Allows to set webhook url + default: op \ No newline at end of file