diff --git a/src/main/java/cc/fascinated/Aetheria.java b/src/main/java/cc/fascinated/Aetheria.java index a1ddff2..fc52189 100644 --- a/src/main/java/cc/fascinated/Aetheria.java +++ b/src/main/java/cc/fascinated/Aetheria.java @@ -3,27 +3,15 @@ package cc.fascinated; import cc.fascinated.account.AccountManager; import cc.fascinated.chat.ChatManager; import cc.fascinated.command.CommandManager; -import cc.fascinated.config.Lang; import cc.fascinated.metrics.MetricManager; +import cc.fascinated.motd.MotdManager; import cc.fascinated.placeholder.PlaceholderManager; import cc.fascinated.playercolor.PlayerColorManager; import cc.fascinated.utils.BuildData; import cc.fascinated.worldsize.WorldSizeManager; import lombok.Getter; -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.minimessage.Context; -import net.kyori.adventure.text.minimessage.MiniMessage; -import net.kyori.adventure.text.minimessage.ParsingException; -import net.kyori.adventure.text.minimessage.tag.Tag; -import net.kyori.adventure.text.minimessage.tag.resolver.ArgumentQueue; -import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; -import net.kyori.adventure.text.minimessage.tag.standard.StandardTags; import org.bukkit.plugin.java.JavaPlugin; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; -import java.util.List; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; @@ -55,5 +43,6 @@ public class Aetheria extends JavaPlugin { new MetricManager(); new PlayerColorManager(); new ChatManager(); + new MotdManager(); } } \ No newline at end of file diff --git a/src/main/java/cc/fascinated/account/Account.java b/src/main/java/cc/fascinated/account/Account.java index b5fc8d4..5469dea 100644 --- a/src/main/java/cc/fascinated/account/Account.java +++ b/src/main/java/cc/fascinated/account/Account.java @@ -1,14 +1,15 @@ package cc.fascinated.account; import cc.fascinated.Aetheria; +import cc.fascinated.config.Config; import cc.fascinated.config.Lang; import cc.fascinated.playercolor.PlayerColor; +import cc.fascinated.utils.DiscordWebhook; import cc.fascinated.utils.Style; import lombok.Getter; import lombok.SneakyThrows; import lombok.extern.log4j.Log4j2; import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.minimessage.MiniMessage; import org.bukkit.Bukkit; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.file.FileConfiguration; @@ -16,6 +17,7 @@ import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.entity.Player; import java.io.File; +import java.io.IOException; import java.util.UUID; @Getter @Log4j2 public class Account { @@ -76,7 +78,25 @@ public class Account { this.save(false); // Save default values log.info("Created new account for " + this.uuid); - Bukkit.broadcast(Style.getMiniMessage().deserialize(Lang.FIRST_JOIN_MESSAGE.getAsString())); + Bukkit.broadcast(Style.getMiniMessage().deserialize(Lang.FIRST_JOIN_MESSAGE.getAsString() + .replace("%player%", this.getName()) + )); + + Aetheria.EXECUTOR.execute(() -> { + // todo: re-code this it's so ugly + DiscordWebhook discordWebhook = new DiscordWebhook(Config.DISCORD_LOG_WEBHOOK.getAsString()); + DiscordWebhook.EmbedObject embed = new DiscordWebhook.EmbedObject(); + embed.setTitle("New Player Joined"); + embed.addField("Name", Bukkit.getOfflinePlayer(uuid).getName(), true); + embed.addField("UUID", uuid.toString(), true); + + discordWebhook.addEmbed(embed); + try { + discordWebhook.execute(); + } catch (IOException e) { + throw new RuntimeException(e); + } + }); } this.firstJoin = config.getLong("firstJoin"); diff --git a/src/main/java/cc/fascinated/account/AccountManager.java b/src/main/java/cc/fascinated/account/AccountManager.java index ba43c4e..379c30a 100644 --- a/src/main/java/cc/fascinated/account/AccountManager.java +++ b/src/main/java/cc/fascinated/account/AccountManager.java @@ -18,7 +18,7 @@ import java.util.concurrent.TimeUnit; @Log4j2 public class AccountManager extends Manager { - private final int SAVE_INTERVAL = 60 * 5; // 5 minutes + private final long SAVE_INTERVAL = 5; // in minutes private static final Map ACCOUNTS = new HashMap<>(); public AccountManager() { @@ -28,7 +28,7 @@ public class AccountManager extends Manager { Bukkit.getAsyncScheduler().runAtFixedRate(Aetheria.INSTANCE, (task) -> { this.saveAccounts(); - }, SAVE_INTERVAL, SAVE_INTERVAL, TimeUnit.SECONDS); + }, SAVE_INTERVAL, SAVE_INTERVAL, TimeUnit.MINUTES); } /** @@ -99,6 +99,7 @@ public class AccountManager extends Manager { return; } - this.saveAccounts(); + this.saveAccounts(); // Save the accounts to disk + ACCOUNTS.clear(); // Remove the accounts from the cache } } diff --git a/src/main/java/cc/fascinated/chat/ChatManager.java b/src/main/java/cc/fascinated/chat/ChatManager.java index 0881ed1..6818b6f 100644 --- a/src/main/java/cc/fascinated/chat/ChatManager.java +++ b/src/main/java/cc/fascinated/chat/ChatManager.java @@ -9,23 +9,20 @@ import cc.fascinated.utils.Style; import io.papermc.paper.event.player.AsyncChatEvent; import lombok.Getter; import lombok.RequiredArgsConstructor; -import net.kyori.adventure.text.Component; import net.kyori.adventure.text.TextComponent; -import net.kyori.adventure.text.event.ClickEvent; import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.minimessage.MiniMessage; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; -import java.util.ArrayList; import java.util.HashMap; -import java.util.List; import java.util.regex.Pattern; @RequiredArgsConstructor @Getter enum BlockReason { DOMAIN("Domain was detected in the message."), - DUPLICATE("Duplicate message was detected."); + DUPLICATE("Duplicate message was detected."), + IP("IP address was detected in the message."); private final String reason; } @@ -34,6 +31,7 @@ public class ChatManager extends Manager { private final HashMap lastMessage = new HashMap<>(); private final Pattern domainPattern = Pattern.compile("^((?!-)[A-Za-z0-9-]{1,63}(? motds; + + public MotdManager() { + motdHeader = Config.MOTD_HEADER.getAsString(); + motds = Config.MOTD_LIST.getAsStringList(); + } + + @EventHandler + public void onServerPing(ServerListPingEvent event) { + MiniMessage miniMessage = MiniMessage.miniMessage(); + String motd = motds.get((int) (Math.random() * motds.size())); + + event.motd(miniMessage.deserialize(motdHeader) + .appendNewline() + .append(miniMessage.deserialize(Config.MOTD_FORMAT.getAsString().replace("%motd%", motd))) + ); + } +} diff --git a/src/main/java/cc/fascinated/utils/DiscordWebhook.java b/src/main/java/cc/fascinated/utils/DiscordWebhook.java new file mode 100644 index 0000000..d17ef4b --- /dev/null +++ b/src/main/java/cc/fascinated/utils/DiscordWebhook.java @@ -0,0 +1,385 @@ +package cc.fascinated.utils; + +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.*; + +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; + } + + 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/cc/fascinated/utils/Style.java b/src/main/java/cc/fascinated/utils/Style.java index 4413b79..1877f68 100644 --- a/src/main/java/cc/fascinated/utils/Style.java +++ b/src/main/java/cc/fascinated/utils/Style.java @@ -2,7 +2,6 @@ package cc.fascinated.utils; import cc.fascinated.config.Lang; import lombok.Getter; -import net.kyori.adventure.text.Component; import net.kyori.adventure.text.minimessage.MiniMessage; import net.kyori.adventure.text.minimessage.tag.Tag; import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 9d59628..7ef308b 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -2,4 +2,21 @@ influxdb: url: "http://localhost:8086" token: "aetheria" org: "aetheria" - bucket: "aetheria" \ No newline at end of file + bucket: "aetheria" + +discord: + log-webhook: "https://discord.com/api/webhooks/1223162714822807572/jkqXHdcf4ov7MTC4fxx8MI2rSCoQTHONua8VOJa3ie5OtHBthqd5MGM7WGd7oHF52OOm" + +motd: + header: "Aetheria" + format: "%motd%" + motds: + - "MEOW" + - "no egirls allowed" + - "your mother" + - "no" + - "aetheria best server 2020" + - "the world is scary" + - "i am a bot" + - "i peed a little" + - "deadmau5 was here!!"