diff --git a/pom.xml b/pom.xml index b8f1f35..cd2634e 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ ChatSentinel Advanced chat management plugin - 1.0.2 + 1.1.0 https://builtbybit.com/resources/23698/ @@ -29,6 +29,10 @@ velocity-repo https://repo.papermc.io/repository/maven-public/ + + jitpack.io + https://jitpack.io + @@ -48,9 +52,14 @@ com.velocitypowered velocity-api - 3.3.0-SNAPSHOT + 3.4.0-SNAPSHOT provided + + com.github.micartey + webhookly + master + @@ -90,6 +99,36 @@ + + org.apache.maven.plugins + maven-shade-plugin + 3.6.0 + + + package + + shade + + + + + me.micartey.webhookly + dev._2lstudios.chatsentinel.libs.webhookly + + + + + com.github.micartey:webhookly + + META-INF/*.MF + + + + true + + + + diff --git a/src/main/java/dev/_2lstudios/chatsentinel/bukkit/ChatSentinel.java b/src/main/java/dev/_2lstudios/chatsentinel/bukkit/ChatSentinel.java index b817a96..15c6555 100644 --- a/src/main/java/dev/_2lstudios/chatsentinel/bukkit/ChatSentinel.java +++ b/src/main/java/dev/_2lstudios/chatsentinel/bukkit/ChatSentinel.java @@ -1,6 +1,7 @@ package dev._2lstudios.chatsentinel.bukkit; import dev._2lstudios.chatsentinel.shared.chat.ChatNotificationManager; +import dev._2lstudios.chatsentinel.shared.modules.*; import org.bukkit.Bukkit; import org.bukkit.Server; import org.bukkit.command.ConsoleCommandSender; @@ -18,11 +19,6 @@ import dev._2lstudios.chatsentinel.shared.chat.ChatEventResult; import dev._2lstudios.chatsentinel.shared.chat.ChatPlayer; import dev._2lstudios.chatsentinel.shared.chat.ChatPlayerManager; -import dev._2lstudios.chatsentinel.shared.modules.CooldownModerationModule; -import dev._2lstudios.chatsentinel.shared.modules.GeneralModule; -import dev._2lstudios.chatsentinel.shared.modules.MessagesModule; -import dev._2lstudios.chatsentinel.shared.modules.ModerationModule; -import dev._2lstudios.chatsentinel.shared.modules.SyntaxModerationModule; public class ChatSentinel extends JavaPlugin { // Static instance @@ -102,13 +98,14 @@ public void dispatchNotification(ModerationModule moderationModule, String[][] p public String[][] getPlaceholders(Player player, ChatPlayer chatPlayer, ModerationModule moderationModule, String message) { String playerName = player.getName(); + String customModuleName = moderationModule.getCustomName(); int warns = chatPlayer.getWarns(moderationModule); int maxWarns = moderationModule.getMaxWarns(); float remainingTime = moduleManager.getCooldownModule().getRemainingTime(chatPlayer, message); return new String[][] { - { "%player%", "%message%", "%warns%", "%maxwarns%", "%cooldown%" }, - { playerName, message, String.valueOf(warns), String.valueOf(maxWarns), String.valueOf(remainingTime) } + { "%player%", "%module%", "%message%", "%warns%", "%maxwarns%", "%cooldown%" }, + { playerName, customModuleName, message, String.valueOf(warns), String.valueOf(maxWarns), String.valueOf(remainingTime) } }; } @@ -176,6 +173,11 @@ public ChatEventResult processEvent(ChatPlayer chatPlayer, Player player, String // Send admin notification ChatSentinel.getInstance().dispatchNotification(moderationModule, placeholders, chatNotificationManager); + // Send discord webhook notification + Server server = getServer(); + DiscordWebhookModule discordWebhookModule = moduleManager.getDiscordWebhookModule(); + server.getScheduler().runTaskAsynchronously(this, () -> discordWebhookModule.dispatchWebhookNotification(moderationModule, placeholders)); + // Update message finalResult.setMessage(result.getMessage()); diff --git a/src/main/java/dev/_2lstudios/chatsentinel/bukkit/modules/BukkitModuleManager.java b/src/main/java/dev/_2lstudios/chatsentinel/bukkit/modules/BukkitModuleManager.java index 44036ed..469e13b 100644 --- a/src/main/java/dev/_2lstudios/chatsentinel/bukkit/modules/BukkitModuleManager.java +++ b/src/main/java/dev/_2lstudios/chatsentinel/bukkit/modules/BukkitModuleManager.java @@ -44,16 +44,18 @@ public void reloadData() { locales.put(lang, messages); } - getCapsModule().loadData(configYml.getBoolean("caps.enabled"), configYml.getBoolean("caps.replace"), + getCapsModule().loadData(configYml.getBoolean("caps.enabled"), configYml.getString("caps.custom-module-name"), configYml.getBoolean("caps.replace"), configYml.getInt("caps.max"), configYml.getInt("caps.warn.max"), configYml.getString("caps.warn.notification"), + configYml.getBoolean("caps.warn.webhook-notification"), configYml.getStringList("caps.punishments").toArray(new String[0])); getCooldownModule().loadData(configYml.getBoolean("cooldown.enabled"), configYml.getInt("cooldown.time.repeat-global"), configYml.getInt("cooldown.time.repeat"), configYml.getInt("cooldown.time.normal"), configYml.getInt("cooldown.time.command")); - getFloodModule().loadData(configYml.getBoolean("flood.enabled"), configYml.getBoolean("flood.replace"), + getFloodModule().loadData(configYml.getBoolean("flood.enabled"), configYml.getString("flood.custom-module-name"), configYml.getBoolean("flood.replace"), configYml.getInt("flood.warn.max"), configYml.getString("flood.pattern"), configYml.getString("flood.warn.notification"), + configYml.getBoolean("flood.warn.webhook-notification"), configYml.getStringList("flood.punishments").toArray(new String[0])); getMessagesModule().loadData(messagesYml.getString("default"), locales); getGeneralModule().loadData(configYml.getBoolean("general.sanitize", true), @@ -64,15 +66,31 @@ public void reloadData() { whitelistYml.getStringList("expressions").toArray(new String[0])); boolean censorshipEnabled = configYml.getBoolean("blacklist.censorship.enabled", false); String censorshipReplacement = configYml.getString("blacklist.censorship.replacement", "***"); - getBlacklistModule().loadData(configYml.getBoolean("blacklist.enabled"), + getBlacklistModule().loadData(configYml.getBoolean("blacklist.enabled"), configYml.getString("blacklist.custom-module-name"), configYml.getBoolean("blacklist.fake_message"), censorshipEnabled, censorshipReplacement, configYml.getInt("blacklist.warn.max"), configYml.getString("blacklist.warn.notification"), + configYml.getBoolean("blacklist.warn.webhook-notification"), configYml.getStringList("blacklist.punishments").toArray(new String[0]), blacklistYml.getStringList("expressions").toArray(new String[0]), configYml.getBoolean("blacklist.block_raw_message")); - getSyntaxModule().loadData(configYml.getBoolean("syntax.enabled"), configYml.getInt("syntax.warn.max"), + getSyntaxModule().loadData(configYml.getBoolean("syntax.enabled"), configYml.getString("syntax.custom-module-name"), configYml.getInt("syntax.warn.max"), configYml.getString("syntax.warn.notification"), + configYml.getBoolean("syntax.warn.webhook-notification"), configYml.getStringList("syntax.whitelist").toArray(new String[0]), configYml.getStringList("syntax.punishments").toArray(new String[0])); + getDiscordWebhookModule().loadData(configYml.getBoolean("discord-webhook.enabled"), configYml.getString("discord-webhook.webhook-url"), + configYml.getString("discord-webhook.warn.max"), + configYml.getString("discord-webhook.sender.avatar-url"), + configYml.getString("discord-webhook.sender.username"), + configYml.getString("discord-webhook.author.name"), + configYml.getString("discord-webhook.author.url"), + configYml.getString("discord-webhook.title"), + configYml.getString("discord-webhook.color"), + configYml.getString("discord-webhook.description"), + configYml.getString("discord-webhook.field-names.message"), + configYml.getString("discord-webhook.field-names.server"), + configYml.getString("discord-webhook.footer.text"), + configYml.getString("discord-webhook.footer.icon-url"), + configYml.getString("discord-webhook.thumbnail-url")); } } diff --git a/src/main/java/dev/_2lstudios/chatsentinel/bungee/ChatSentinel.java b/src/main/java/dev/_2lstudios/chatsentinel/bungee/ChatSentinel.java index b163a5e..90b5750 100644 --- a/src/main/java/dev/_2lstudios/chatsentinel/bungee/ChatSentinel.java +++ b/src/main/java/dev/_2lstudios/chatsentinel/bungee/ChatSentinel.java @@ -12,11 +12,7 @@ import dev._2lstudios.chatsentinel.shared.chat.ChatNotificationManager; import dev._2lstudios.chatsentinel.shared.chat.ChatPlayer; import dev._2lstudios.chatsentinel.shared.chat.ChatPlayerManager; -import dev._2lstudios.chatsentinel.shared.modules.CooldownModerationModule; -import dev._2lstudios.chatsentinel.shared.modules.GeneralModule; -import dev._2lstudios.chatsentinel.shared.modules.MessagesModule; -import dev._2lstudios.chatsentinel.shared.modules.ModerationModule; -import dev._2lstudios.chatsentinel.shared.modules.SyntaxModerationModule; +import dev._2lstudios.chatsentinel.shared.modules.*; import net.md_5.bungee.api.CommandSender; import net.md_5.bungee.api.ProxyServer; import net.md_5.bungee.api.connection.ProxiedPlayer; @@ -61,7 +57,7 @@ public void onEnable() { ChatNotificationManager chatNotificationManager = new ChatNotificationManager(); PluginManager pluginManager = server.getPluginManager(); - pluginManager.registerListener(this, new ChatListener(chatPlayerManager, chatNotificationManager)); + pluginManager.registerListener(this, new ChatListener(moduleManager.getWhitelistModule(), chatPlayerManager, chatNotificationManager)); pluginManager.registerListener(this, new PlayerDisconnectListener(generalModule, chatPlayerManager, chatNotificationManager)); pluginManager.registerListener(this, new PostLoginListener(generalModule, chatPlayerManager, chatNotificationManager)); @@ -106,6 +102,7 @@ public void dispatchNotification(ModerationModule moderationModule, String[][] p public String[][] getPlaceholders(ProxiedPlayer player, ChatPlayer chatPlayer, ModerationModule moderationModule, String message) { String playerName = player.getName(); + String customModuleName = moderationModule.getCustomName(); int warns = chatPlayer.getWarns(moderationModule); int maxWarns = moderationModule.getMaxWarns(); float remainingTime = moduleManager.getCooldownModule().getRemainingTime(chatPlayer, message); @@ -113,8 +110,8 @@ public String[][] getPlaceholders(ProxiedPlayer player, ChatPlayer chatPlayer, M String serverName = server != null ? server.getInfo().getName() : ""; return new String[][] { - { "%player%", "%message%", "%warns%", "%maxwarns%", "%cooldown%", "%server_name%" }, - { playerName, message, String.valueOf(warns), String.valueOf(maxWarns), String.valueOf(remainingTime), serverName } + { "%player%", "%module%", "%message%", "%warns%", "%maxwarns%", "%cooldown%", "%server_name%" }, + { playerName, customModuleName, message, String.valueOf(warns), String.valueOf(maxWarns), String.valueOf(remainingTime), serverName } }; } @@ -182,6 +179,11 @@ public ChatEventResult processEvent(ChatPlayer chatPlayer, ProxiedPlayer player, // Send admin notification ChatSentinel.getInstance().dispatchNotification(moderationModule, placeholders, chatNotificationManager); + // Send discord webhook notification + ProxyServer server = getProxy(); + DiscordWebhookModule discordWebhookModule = moduleManager.getDiscordWebhookModule(); + server.getScheduler().runAsync(this, () -> discordWebhookModule.dispatchWebhookNotification(moderationModule, placeholders)); + // Update message finalResult.setMessage(result.getMessage()); diff --git a/src/main/java/dev/_2lstudios/chatsentinel/bungee/listeners/ChatListener.java b/src/main/java/dev/_2lstudios/chatsentinel/bungee/listeners/ChatListener.java index c581c82..da83748 100644 --- a/src/main/java/dev/_2lstudios/chatsentinel/bungee/listeners/ChatListener.java +++ b/src/main/java/dev/_2lstudios/chatsentinel/bungee/listeners/ChatListener.java @@ -5,6 +5,7 @@ import dev._2lstudios.chatsentinel.shared.chat.ChatNotificationManager; import dev._2lstudios.chatsentinel.shared.chat.ChatPlayer; import dev._2lstudios.chatsentinel.shared.chat.ChatPlayerManager; +import dev._2lstudios.chatsentinel.shared.modules.WhitelistModule; import net.md_5.bungee.api.connection.Connection; import net.md_5.bungee.api.connection.ProxiedPlayer; import net.md_5.bungee.api.event.ChatEvent; @@ -13,10 +14,12 @@ import net.md_5.bungee.event.EventPriority; public class ChatListener implements Listener { + private WhitelistModule whitelistModule; private ChatPlayerManager chatPlayerManager; private ChatNotificationManager chatNotificationManager; - public ChatListener(ChatPlayerManager chatPlayerManager, ChatNotificationManager chatNotificationManager) { + public ChatListener(WhitelistModule whitelistModule, ChatPlayerManager chatPlayerManager, ChatNotificationManager chatNotificationManager) { + this.whitelistModule = whitelistModule; this.chatPlayerManager = chatPlayerManager; this.chatNotificationManager = chatNotificationManager; } @@ -37,6 +40,14 @@ public void onChatEvent(ChatEvent event) { // Get player ProxiedPlayer player = (ProxiedPlayer) sender; + // Check if the player's current server is on the whitelist + if (player.getServer() != null) { + String playerCurrentServer = player.getServer().getInfo().getName(); + if (whitelistModule.getWhitelistedServers().contains(playerCurrentServer)) { + return; + } + } + // Check if player has bypass if (player.hasPermission("chatsentinel.bypass")) { return; diff --git a/src/main/java/dev/_2lstudios/chatsentinel/bungee/modules/BungeeModuleManager.java b/src/main/java/dev/_2lstudios/chatsentinel/bungee/modules/BungeeModuleManager.java index 1c837de..b683c30 100644 --- a/src/main/java/dev/_2lstudios/chatsentinel/bungee/modules/BungeeModuleManager.java +++ b/src/main/java/dev/_2lstudios/chatsentinel/bungee/modules/BungeeModuleManager.java @@ -41,16 +41,18 @@ public void reloadData() { locales.put(lang, messages); } - getCapsModule().loadData(configYml.getBoolean("caps.enabled"), configYml.getBoolean("caps.replace"), + getCapsModule().loadData(configYml.getBoolean("caps.enabled"), configYml.getString("caps.custom-module-name"), configYml.getBoolean("caps.replace"), configYml.getInt("caps.max"), configYml.getInt("caps.warn.max"), configYml.getString("caps.warn.notification"), + configYml.getBoolean("caps.warn.webhook-notification"), configYml.getStringList("caps.punishments").toArray(new String[0])); getCooldownModule().loadData(configYml.getBoolean("cooldown.enabled"), configYml.getInt("cooldown.time.repeat-global"), configYml.getInt("cooldown.time.repeat"), configYml.getInt("cooldown.time.normal"), configYml.getInt("cooldown.time.command")); - getFloodModule().loadData(configYml.getBoolean("flood.enabled"), configYml.getBoolean("flood.replace"), + getFloodModule().loadData(configYml.getBoolean("flood.enabled"), configYml.getString("flood.custom-module-name"), configYml.getBoolean("flood.replace"), configYml.getInt("flood.warn.max"), configYml.getString("flood.pattern"), configYml.getString("flood.warn.notification"), + configYml.getBoolean("flood.warn.webhook-notification"), configYml.getStringList("flood.punishments").toArray(new String[0])); getMessagesModule().loadData(messagesYml.getString("default"), locales); getGeneralModule().loadData(configYml.getBoolean("general.sanitize", true), @@ -58,18 +60,35 @@ public void reloadData() { configYml.getBoolean("general.filter-other", false), configYml.getStringList("general.commands")); getWhitelistModule().loadData(configYml.getBoolean("whitelist.enabled"), + whitelistYml.getStringList("servers"), whitelistYml.getStringList("expressions").toArray(new String[0])); boolean censorshipEnabled = configYml.getBoolean("blacklist.censorship.enabled", false); String censorshipReplacement = configYml.getString("blacklist.censorship.replacement", "***"); - getBlacklistModule().loadData(configYml.getBoolean("blacklist.enabled"), + getBlacklistModule().loadData(configYml.getBoolean("blacklist.enabled"), configYml.getString("blacklist.custom-module-name"), configYml.getBoolean("blacklist.fake_message"), censorshipEnabled, censorshipReplacement, configYml.getInt("blacklist.warn.max"), configYml.getString("blacklist.warn.notification"), + configYml.getBoolean("blacklist.warn.webhook-notification"), configYml.getStringList("blacklist.punishments").toArray(new String[0]), blacklistYml.getStringList("expressions").toArray(new String[0]), configYml.getBoolean("blacklist.block_raw_message")); - getSyntaxModule().loadData(configYml.getBoolean("syntax.enabled"), configYml.getInt("syntax.warn.max"), + getSyntaxModule().loadData(configYml.getBoolean("syntax.enabled"), configYml.getString("syntax.custom-module-name"), configYml.getInt("syntax.warn.max"), configYml.getString("syntax.warn.notification"), + configYml.getBoolean("syntax.warn.webhook-notification"), configYml.getStringList("syntax.whitelist").toArray(new String[0]), configYml.getStringList("syntax.punishments").toArray(new String[0])); + getDiscordWebhookModule().loadData(configYml.getBoolean("discord-webhook.enabled"), configYml.getString("discord-webhook.webhook-url"), + configYml.getString("discord-webhook.warn.max"), + configYml.getString("discord-webhook.sender.avatar-url"), + configYml.getString("discord-webhook.sender.username"), + configYml.getString("discord-webhook.author.name"), + configYml.getString("discord-webhook.author.url"), + configYml.getString("discord-webhook.title"), + configYml.getString("discord-webhook.color"), + configYml.getString("discord-webhook.description"), + configYml.getString("discord-webhook.field-names.message"), + configYml.getString("discord-webhook.field-names.server"), + configYml.getString("discord-webhook.footer.text"), + configYml.getString("discord-webhook.footer.icon-url"), + configYml.getString("discord-webhook.thumbnail-url")); } } diff --git a/src/main/java/dev/_2lstudios/chatsentinel/shared/modules/BlacklistModerationModule.java b/src/main/java/dev/_2lstudios/chatsentinel/shared/modules/BlacklistModerationModule.java index 3b5956b..595b801 100644 --- a/src/main/java/dev/_2lstudios/chatsentinel/shared/modules/BlacklistModerationModule.java +++ b/src/main/java/dev/_2lstudios/chatsentinel/shared/modules/BlacklistModerationModule.java @@ -20,12 +20,14 @@ public BlacklistModerationModule(ModuleManager moduleManager) { this.moduleManager = moduleManager; } - public void loadData(boolean enabled, boolean fakeMessage, boolean censorshipEnabled, String censorshipReplacement, int maxWarns, - String warnNotification, String[] commands, String[] patterns, boolean blockRawMessage) { + public void loadData(boolean enabled, String customName, boolean fakeMessage, boolean censorshipEnabled, String censorshipReplacement, int maxWarns, + String warnNotification, boolean webhookEnabled, String[] commands, String[] patterns, boolean blockRawMessage) { setEnabled(enabled); setMaxWarns(maxWarns); setWarnNotification(warnNotification); + setWebhookEnabled(webhookEnabled); setCommands(commands); + setCustomName(customName); this.fakeMessage = fakeMessage; this.censorshipEnabled = censorshipEnabled; this.censorshipReplacement = censorshipReplacement; diff --git a/src/main/java/dev/_2lstudios/chatsentinel/shared/modules/CapsModerationModule.java b/src/main/java/dev/_2lstudios/chatsentinel/shared/modules/CapsModerationModule.java index 3f6855d..2bed8f4 100644 --- a/src/main/java/dev/_2lstudios/chatsentinel/shared/modules/CapsModerationModule.java +++ b/src/main/java/dev/_2lstudios/chatsentinel/shared/modules/CapsModerationModule.java @@ -7,12 +7,14 @@ public class CapsModerationModule extends ModerationModule { private boolean replace; private int maxCaps; - public void loadData(boolean enabled, boolean replace, int max, int maxWarns, - String warnNotification, String[] commands) { + public void loadData(boolean enabled, String customName, boolean replace, int max, int maxWarns, + String warnNotification, boolean webhookEnabled, String[] commands) { setEnabled(enabled); setMaxWarns(maxWarns); setWarnNotification(warnNotification); + setWebhookEnabled(webhookEnabled); setCommands(commands); + setCustomName(customName); this.replace = replace; this.maxCaps = max; } diff --git a/src/main/java/dev/_2lstudios/chatsentinel/shared/modules/CooldownModerationModule.java b/src/main/java/dev/_2lstudios/chatsentinel/shared/modules/CooldownModerationModule.java index c606207..8687fb1 100644 --- a/src/main/java/dev/_2lstudios/chatsentinel/shared/modules/CooldownModerationModule.java +++ b/src/main/java/dev/_2lstudios/chatsentinel/shared/modules/CooldownModerationModule.java @@ -62,6 +62,11 @@ public String getName() { return "Cooldown"; } + @Override + public String getCustomName() { + return getName(); + } + @Override public String getWarnNotification(String[][] placeholders) { return null; diff --git a/src/main/java/dev/_2lstudios/chatsentinel/shared/modules/DiscordWebhookModule.java b/src/main/java/dev/_2lstudios/chatsentinel/shared/modules/DiscordWebhookModule.java new file mode 100644 index 0000000..e7b1b89 --- /dev/null +++ b/src/main/java/dev/_2lstudios/chatsentinel/shared/modules/DiscordWebhookModule.java @@ -0,0 +1,87 @@ +package dev._2lstudios.chatsentinel.shared.modules; + +import dev._2lstudios.chatsentinel.shared.utils.PlaceholderUtil; +import me.micartey.webhookly.DiscordWebhook; +import me.micartey.webhookly.embeds.*; + +import java.awt.*; +import java.io.IOException; +import java.time.OffsetDateTime; +import java.util.LinkedHashSet; + +public class DiscordWebhookModule { + private boolean enabled; + private String webhookUrl; + private String senderAvatarUrl; + private String senderUsername; + private String authorName; + private String authorUrl; + private String authorIconUrl; + private String title; + private String color; + private String description; + private String messageFieldName; + private String serverFieldName; + private String footerText; + private String footerIconUrl; + private String thumbnailUrl; + private DiscordWebhook webhook; + + public void loadData(boolean enabled, String webhookUrl, String senderAvatarUrl, String senderUsername, String authorName, String authorUrl, String authorIconUrl, String title, String color, String description, String messageFieldName, String serverFieldName, String footerText, String footerIconUrl, String thumbnailUrl) { + this.enabled = enabled; + this.webhookUrl = webhookUrl; + this.senderAvatarUrl = senderAvatarUrl; + this.senderUsername = senderUsername; + this.authorName = authorName; + this.authorUrl = authorUrl; + this.authorIconUrl = authorIconUrl; + this.title = title; + this.color = color; + this.description = description; + this.messageFieldName = messageFieldName; + this.serverFieldName = serverFieldName; + this.footerText = footerText; + this.footerIconUrl = footerIconUrl; + this.thumbnailUrl = thumbnailUrl; + if (isEnabled()) setupDiscordWebhook(); + } + + public boolean isEnabled() { + return enabled; + } + + private void setupDiscordWebhook() { + this.webhook = new DiscordWebhook(webhookUrl); + webhook.setAvatarUrl(senderAvatarUrl); + webhook.setUsername(senderUsername); + } + + public void dispatchWebhookNotification(ModerationModule moderationModule, String[][] placeholders) { + if (!isEnabled() || !moderationModule.isWebhookEnabled() || moderationModule.getWarnNotification(placeholders) == null) { + return; + } + + EmbedObject embed = new EmbedObject() + .setAuthor(new Author(authorName, authorUrl, authorIconUrl)) + .setTitle(title) + .setColor(Color.decode(color)) + .setDescription(PlaceholderUtil.replacePlaceholders(description, placeholders)) + .setFooter(new Footer(footerText, footerIconUrl)) + .setThumbnail(new Thumbnail(thumbnailUrl)) + .setTimestamp(OffsetDateTime.now()); + + embed.getFields().addAll(new LinkedHashSet(){{ + add(new Field(messageFieldName, PlaceholderUtil.replacePlaceholders("%message%", placeholders), true)); + add(new Field(serverFieldName, PlaceholderUtil.replacePlaceholders("%server_name%", placeholders), true)); + }}); + + webhook.getEmbeds().add(embed); + try { + webhook.execute(); + } catch (IOException e) { + throw new RuntimeException(e); + } finally { + webhook.getEmbeds().clear(); + } + } +} diff --git a/src/main/java/dev/_2lstudios/chatsentinel/shared/modules/FloodModerationModule.java b/src/main/java/dev/_2lstudios/chatsentinel/shared/modules/FloodModerationModule.java index 4489866..73825e2 100644 --- a/src/main/java/dev/_2lstudios/chatsentinel/shared/modules/FloodModerationModule.java +++ b/src/main/java/dev/_2lstudios/chatsentinel/shared/modules/FloodModerationModule.java @@ -9,12 +9,14 @@ public class FloodModerationModule extends ModerationModule { private boolean replace; private Pattern pattern; - public void loadData(boolean enabled, boolean replace, int maxWarns, String pattern, - String warnNotification, String[] commands) { + public void loadData(boolean enabled, String customName, boolean replace, int maxWarns, String pattern, + String warnNotification, boolean webhookEnabled, String[] commands) { setEnabled(enabled); setMaxWarns(maxWarns); setWarnNotification(warnNotification); + setWebhookEnabled(webhookEnabled); setCommands(commands); + setCustomName(customName); this.replace = replace; this.pattern = Pattern.compile(pattern); } diff --git a/src/main/java/dev/_2lstudios/chatsentinel/shared/modules/ModerationModule.java b/src/main/java/dev/_2lstudios/chatsentinel/shared/modules/ModerationModule.java index bd497ca..1c5b3da 100644 --- a/src/main/java/dev/_2lstudios/chatsentinel/shared/modules/ModerationModule.java +++ b/src/main/java/dev/_2lstudios/chatsentinel/shared/modules/ModerationModule.java @@ -8,12 +8,18 @@ public abstract class ModerationModule { private boolean enabled = true; private int maxWarns = 0; private String warnNotification = null; + private boolean webhookEnabled = true; private String[] commands = new String[0]; + private String customName; public boolean isEnabled() { return enabled; } + public boolean isWebhookEnabled() { + return webhookEnabled; + } + public int getMaxWarns() { return maxWarns; } @@ -22,6 +28,10 @@ public void setEnabled(boolean enabled) { this.enabled = enabled; } + public void setWebhookEnabled(boolean webhookEnabled) { + this.webhookEnabled = webhookEnabled; + } + public void setMaxWarns(int maxWarns) { this.maxWarns = maxWarns; } @@ -44,6 +54,14 @@ public boolean hasExceededWarns(ChatPlayer chatPlayer) { public abstract String getName(); + public String getCustomName() { + return customName; + } + + public void setCustomName(String customName) { + this.customName = customName; + } + public abstract ChatEventResult processEvent(ChatPlayer chatPlayer, MessagesModule messagesModule, String playerName, String originalMessage, String lang); public String getBypassPermission() { diff --git a/src/main/java/dev/_2lstudios/chatsentinel/shared/modules/ModuleManager.java b/src/main/java/dev/_2lstudios/chatsentinel/shared/modules/ModuleManager.java index 330977a..0cff874 100644 --- a/src/main/java/dev/_2lstudios/chatsentinel/shared/modules/ModuleManager.java +++ b/src/main/java/dev/_2lstudios/chatsentinel/shared/modules/ModuleManager.java @@ -9,6 +9,7 @@ public abstract class ModuleManager { private BlacklistModerationModule blacklistModule; private SyntaxModerationModule syntaxModule; private WhitelistModule whitelistModule; + private DiscordWebhookModule discordWebhookModule; public ModuleManager() { this.capsModule = new CapsModerationModule(); @@ -19,6 +20,7 @@ public ModuleManager() { this.messagesModule = new MessagesModule(); this.generalModule = new GeneralModule(); this.whitelistModule = new WhitelistModule(); + this.discordWebhookModule = new DiscordWebhookModule(); } public CooldownModerationModule getCooldownModule() { @@ -53,5 +55,9 @@ public WhitelistModule getWhitelistModule() { return whitelistModule; } + public DiscordWebhookModule getDiscordWebhookModule() { + return discordWebhookModule; + } + public abstract void reloadData(); } diff --git a/src/main/java/dev/_2lstudios/chatsentinel/shared/modules/SyntaxModerationModule.java b/src/main/java/dev/_2lstudios/chatsentinel/shared/modules/SyntaxModerationModule.java index b0bdf2d..0298119 100644 --- a/src/main/java/dev/_2lstudios/chatsentinel/shared/modules/SyntaxModerationModule.java +++ b/src/main/java/dev/_2lstudios/chatsentinel/shared/modules/SyntaxModerationModule.java @@ -6,12 +6,14 @@ public class SyntaxModerationModule extends ModerationModule { private String[] whitelist; - public void loadData(boolean enabled, int maxWarns, String warnNotification, - String[] whitelist, String[] commands) { + public void loadData(boolean enabled, String customName, int maxWarns, String warnNotification, + boolean webhookEnabled, String[] whitelist, String[] commands) { setEnabled(enabled); setMaxWarns(maxWarns); setWarnNotification(warnNotification); + setWebhookEnabled(webhookEnabled); setCommands(commands); + setCustomName(customName); this.whitelist = whitelist; } diff --git a/src/main/java/dev/_2lstudios/chatsentinel/shared/modules/WhitelistModule.java b/src/main/java/dev/_2lstudios/chatsentinel/shared/modules/WhitelistModule.java index c7c85af..67e8916 100644 --- a/src/main/java/dev/_2lstudios/chatsentinel/shared/modules/WhitelistModule.java +++ b/src/main/java/dev/_2lstudios/chatsentinel/shared/modules/WhitelistModule.java @@ -1,18 +1,29 @@ package dev._2lstudios.chatsentinel.shared.modules; +import java.util.Collection; import java.util.regex.Pattern; import dev._2lstudios.chatsentinel.shared.utils.PatternUtil; public class WhitelistModule { private boolean enabled; + private Collection whitelistedServers; private Pattern pattern; public void loadData(boolean enabled, String[] patterns) { + loadData(enabled, null, patterns); + } + + public void loadData(boolean enabled, Collection whitelistedServers, String[] patterns) { this.enabled = enabled; + this.whitelistedServers = whitelistedServers; this.pattern = PatternUtil.compile(patterns); } + public Collection getWhitelistedServers() { + return whitelistedServers; + } + public Pattern getPattern() { return pattern; } diff --git a/src/main/java/dev/_2lstudios/chatsentinel/velocity/ChatSentinel.java b/src/main/java/dev/_2lstudios/chatsentinel/velocity/ChatSentinel.java index e3ac67d..3b34538 100644 --- a/src/main/java/dev/_2lstudios/chatsentinel/velocity/ChatSentinel.java +++ b/src/main/java/dev/_2lstudios/chatsentinel/velocity/ChatSentinel.java @@ -12,6 +12,7 @@ import com.velocitypowered.api.plugin.annotation.DataDirectory; import com.velocitypowered.api.proxy.Player; import com.velocitypowered.api.proxy.ProxyServer; +import com.velocitypowered.api.proxy.ServerConnection; import dev._2lstudios.chatsentinel.shared.chat.ChatEventResult; import dev._2lstudios.chatsentinel.shared.chat.ChatNotificationManager; import dev._2lstudios.chatsentinel.shared.chat.ChatPlayer; @@ -68,7 +69,7 @@ public void onProxyInitialize(ProxyInitializeEvent event) { chatNotificationManager = new ChatNotificationManager(); EventManager eventManager = server.getEventManager(); - eventManager.register(this, new ChatListener(this)); + eventManager.register(this, new ChatListener(this, moduleManager.getWhitelistModule())); eventManager.register(this, new PlayerDisconnectListener(generalModule, chatPlayerManager, chatNotificationManager)); eventManager.register(this, new PostLoginListener(generalModule, chatPlayerManager, chatNotificationManager)); @@ -115,13 +116,16 @@ public void dispatchNotification(ModerationModule moderationModule, String[][] p public String[][] getPlaceholders(Player player, ChatPlayer chatPlayer, ModerationModule moderationModule, String message) { String playerName = player.getUsername(); + String customModuleName = moderationModule.getCustomName(); int warns = chatPlayer.getWarns(moderationModule); int maxWarns = moderationModule.getMaxWarns(); float remainingTime = moduleManager.getCooldownModule().getRemainingTime(chatPlayer, message); + Optional serverConnection = player.getCurrentServer(); + String serverName = serverConnection.isPresent() ? serverConnection.get().getServerInfo().getName() : ""; return new String[][] { - { "%player%", "%message%", "%warns%", "%maxwarns%", "%cooldown%" }, - { playerName, message, String.valueOf(warns), String.valueOf(maxWarns), String.valueOf(remainingTime) } + { "%player%", "%module%", "%message%", "%warns%", "%maxwarns%", "%cooldown%", "%server_name%" }, + { playerName, customModuleName, message, String.valueOf(warns), String.valueOf(maxWarns), String.valueOf(remainingTime), serverName } }; } @@ -189,6 +193,10 @@ public ChatEventResult processEvent(ChatPlayer chatPlayer, Player player, String // Send admin notification dispatchNotification(moderationModule, placeholders); + // Send discord webhook notification + DiscordWebhookModule discordWebhookModule = moduleManager.getDiscordWebhookModule(); + server.getScheduler().buildTask(this, () -> discordWebhookModule.dispatchWebhookNotification(moderationModule, placeholders)).schedule(); + // Update message finalResult.setMessage(result.getMessage()); diff --git a/src/main/java/dev/_2lstudios/chatsentinel/velocity/listeners/ChatListener.java b/src/main/java/dev/_2lstudios/chatsentinel/velocity/listeners/ChatListener.java index 0f6886a..3ab5c0e 100644 --- a/src/main/java/dev/_2lstudios/chatsentinel/velocity/listeners/ChatListener.java +++ b/src/main/java/dev/_2lstudios/chatsentinel/velocity/listeners/ChatListener.java @@ -4,16 +4,19 @@ import com.velocitypowered.api.event.Subscribe; import com.velocitypowered.api.event.player.PlayerChatEvent; import com.velocitypowered.api.proxy.Player; +import dev._2lstudios.chatsentinel.shared.modules.WhitelistModule; import dev._2lstudios.chatsentinel.velocity.ChatSentinel; import dev._2lstudios.chatsentinel.shared.chat.ChatEventResult; import dev._2lstudios.chatsentinel.shared.chat.ChatPlayer; public class ChatListener { private final ChatSentinel plugin; + private final WhitelistModule whitelistModule; - public ChatListener(ChatSentinel plugin) { + public ChatListener(ChatSentinel plugin, WhitelistModule whitelistModule) { this.plugin = plugin; - } + this.whitelistModule = whitelistModule; + } @Subscribe(order = PostOrder.LAST) public void onChatEvent(PlayerChatEvent event) { @@ -28,6 +31,14 @@ public void onChatEvent(PlayerChatEvent event) { return; } + // Check if the player's current server is on the whitelist + if (player.getCurrentServer().isPresent()) { + String playerCurrentServer = player.getCurrentServer().get().getServerInfo().getName(); + if (whitelistModule.getWhitelistedServers().contains(playerCurrentServer)) { + return; + } + } + // Check if player has bypass if (player.hasPermission("chatsentinel.bypass")) { return; diff --git a/src/main/java/dev/_2lstudios/chatsentinel/velocity/modules/VelocityModuleManager.java b/src/main/java/dev/_2lstudios/chatsentinel/velocity/modules/VelocityModuleManager.java index fcb1188..aa8fdae 100644 --- a/src/main/java/dev/_2lstudios/chatsentinel/velocity/modules/VelocityModuleManager.java +++ b/src/main/java/dev/_2lstudios/chatsentinel/velocity/modules/VelocityModuleManager.java @@ -44,16 +44,11 @@ public void reloadData() { } getCapsModule().loadData(configYml.node("caps", "enabled").getBoolean(), + configYml.node("caps", "custom-module-name").getString(), configYml.node("caps", "replace").getBoolean(), configYml.node("caps", "max").getInt(), configYml.node("caps", "warn", "max").getInt(), configYml.node("caps", "warn", "notification").getString(), - configYml.node("caps", "punishments").childrenList().stream() - .map(ConfigurationNode::getString) - .toArray(String[]::new)); - getCapsModule().loadData(configYml.node("caps", "enabled").getBoolean(), - configYml.node("caps", "replace").getBoolean(), - configYml.node("caps", "max").getInt(), configYml.node("caps", "warn", "max").getInt(), - configYml.node("caps", "warn", "notification").getString(), + configYml.node("caps", "warn", "webhook-notification").getBoolean(), configYml.node("caps", "punishments").childrenList().stream() .map(ConfigurationNode::getString) .toArray(String[]::new)); @@ -63,9 +58,11 @@ public void reloadData() { configYml.node("cooldown", "time", "normal").getInt(), configYml.node("cooldown", "time", "command").getInt()); getFloodModule().loadData(configYml.node("flood", "enabled").getBoolean(), + configYml.node("flood", "custom-module-name").getString(), configYml.node("flood", "replace").getBoolean(), configYml.node("flood", "warn", "max").getInt(), configYml.node("flood", "pattern").getString(), configYml.node("flood", "warn", "notification").getString(), + configYml.node("flood", "warn", "webhook-notification").getBoolean(), configYml.node("flood", "punishments").childrenList().stream() .map(ConfigurationNode::getString) .toArray(String[]::new)); @@ -77,17 +74,22 @@ public void reloadData() { .map(ConfigurationNode::getString) .collect(Collectors.toList())); getWhitelistModule().loadData(configYml.node("whitelist", "enabled").getBoolean(), + whitelistYml.node("servers").childrenList().stream() + .map(ConfigurationNode::getString) + .collect(Collectors.toList()), whitelistYml.node("expressions").childrenList().stream() .map(ConfigurationNode::getString) .toArray(String[]::new)); boolean censorshipEnabled = configYml.node("blacklist", "censorship", "enabled").getBoolean(false); String censorshipReplacement = configYml.node("blacklist", "censorship", "replacement").getString("***"); getBlacklistModule().loadData(configYml.node("blacklist", "enabled").getBoolean(), + configYml.node("blacklist", "custom-module-name").getString(), configYml.node("blacklist", "fake_message").getBoolean(), censorshipEnabled, censorshipReplacement, configYml.node("blacklist", "warn", "max").getInt(), configYml.node("blacklist", "warn", "notification").getString(), + configYml.node("blacklist", "warn", "webhook-notification").getBoolean(), configYml.node("blacklist", "punishments").childrenList().stream() .map(ConfigurationNode::getString) .toArray(String[]::new), @@ -96,13 +98,30 @@ public void reloadData() { .toArray(String[]::new), configYml.node("blacklist", "block_raw_message").getBoolean()); getSyntaxModule().loadData(configYml.node("syntax", "enabled").getBoolean(), + configYml.node("syntax", "custom-module-name").getString(), configYml.node("syntax", "warn", "max").getInt(), configYml.node("syntax", "warn", "notification").getString(), + configYml.node("syntax", "warn", "webhook-notification").getBoolean(), configYml.node("syntax", "whitelist").childrenList().stream() .map(ConfigurationNode::getString) .toArray(String[]::new), configYml.node("syntax", "punishments").childrenList().stream() .map(ConfigurationNode::getString) .toArray(String[]::new)); + getDiscordWebhookModule().loadData(configYml.node("discord-webhook", "enabled").getBoolean(), + configYml.node("discord-webhook", "webhook-url").getString(), + configYml.node("discord-webhook", "sender", "avatar-url").getString(), + configYml.node("discord-webhook", "sender", "username").getString(), + configYml.node("discord-webhook", "author", "name").getString(), + configYml.node("discord-webhook", "author", "url").getString(), + configYml.node("discord-webhook", "author", "icon-url").getString(), + configYml.node("discord-webhook", "title").getString(), + configYml.node("discord-webhook", "color").getString(), + configYml.node("discord-webhook", "description").getString(), + configYml.node("discord-webhook", "field-names", "message").getString(), + configYml.node("discord-webhook", "field-names", "server").getString(), + configYml.node("discord-webhook", "footer", "text").getString(), + configYml.node("discord-webhook", "footer", "icon-url").getString(), + configYml.node("discord-webhook", "thumbnail-url").getString()); } } diff --git a/src/main/java/dev/_2lstudios/chatsentinel/velocity/utils/Constants.java b/src/main/java/dev/_2lstudios/chatsentinel/velocity/utils/Constants.java index ce468f6..c4d09e0 100644 --- a/src/main/java/dev/_2lstudios/chatsentinel/velocity/utils/Constants.java +++ b/src/main/java/dev/_2lstudios/chatsentinel/velocity/utils/Constants.java @@ -3,7 +3,7 @@ public class Constants { public static final String ID = "chatsentinel"; public static final String NAME = "ChatSentinel"; - public static final String VERSION = "1.0.1"; + public static final String VERSION = "1.1.0"; public static final String DESCRIPTION = "Advanced chat management plugin"; public static final String URL = "https://builtbybit.com/resources/23698/"; public static final String AUTHOR = "2LS"; diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 06c664c..2ee6fd7 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -34,6 +34,9 @@ general: blacklist: enabled: true + # Custom module name. + custom-module-name: "Swearing" + # Show a fake message to the player to make him think his message was sent. # This doesnt work if the plugin is on BungeeCord. fake_message: false @@ -52,7 +55,10 @@ blacklist: # Sends a notification to players with chatsentinel.notify permission. # Set to "" to disable this feature completely. # You can use %server% on BungeeCord to get the server name. - notification: "&c&lCS: &e%player% &ffailed &6Swearing &7(&c%message%&7)" + notification: "&c&lCS: &e%player% &ffailed &6%module% &7(&c%message%&7)" + + # Discord webhook notification. + webhook-notification: true # You need a mute plugin for ChatSentinel to mute players. (Recommended: LiteBans/AdvancedBans) # Set to [] to disable this feature completely. @@ -68,6 +74,9 @@ blacklist: caps: enabled: true + # Custom module name. + custom-module-name: "Caps" + # If this is true it will replace the caps with low-case letters. replace: true @@ -81,7 +90,10 @@ caps: # Sends a notification to players with chatsentinel.notify permission. # Set to "" to disable this feature completely. - notification: "&c&lCS: &e%player% &ffailed &6Caps &7(&c%message%&7)" + notification: "&c&lCS: &e%player% &ffailed &6%module% &7(&c%message%&7)" + + # Discord webhook notification. + webhook-notification: true # You need a mute plugin for ChatSentinel to mute players. (Recommended: LiteBans/AdvancedBans) # Set to [] to disable this feature completely. @@ -102,6 +114,9 @@ cooldown: flood: enabled: true + # Custom module name. + custom-module-name: "Flood" + # If this is true it will replace the flood instead of cancelling it. # Example: Heloooooo would be converted to just Helloo replace: true @@ -116,7 +131,10 @@ flood: # Sends a notification to players with chatsentinel.notify permission. # Set to "" to disable this feature completely. - notification: "&c&lCS: &e%player% &ffailed &6Flood &7(&c%message%&7)" + notification: "&c&lCS: &e%player% &ffailed &6%module% &7(&c%message%&7)" + + # Discord webhook notification. + webhook-notification: true # You need a mute plugin for ChatSentinel to mute players. (Recommended: LiteBans/AdvancedBans) # Set to [] to disable this feature completely. @@ -128,6 +146,9 @@ syntax: # Do you want to enable this module? enabled: true + # Custom module name. + custom-module-name: "Syntax" + # Syntax commands that will not be checked. # Set to {} to disable whitelist. whitelist: @@ -140,8 +161,62 @@ syntax: # Sends a notification to players with chatsentinel.notify permission. # Set to "" to disable this feature completely. - notification: "&c&lCS: &e%player% &ffailed &6Syntax &7(&c%message%&7)" + notification: "&c&lCS: &e%player% &ffailed &6%module% &7(&c%message%&7)" + + # Discord webhook notification. + webhook-notification: true # You need a mute plugin for ChatSentinel to mute players. (Recommended: LiteBans/AdvancedBans) # Set to [] to disable this feature completely. punishments: [] + +# Discord webhook configuration. +discord-webhook: + # Do you want to enable this module? + enabled: false + + # Discord webhook URL. + webhook-url: "Your Discord Webhook URL here" + + sender: + # Sender avatar URL. + avatar-url: "https://avatars.githubusercontent.com/u/53847752?s=128&v=4" + + # Sender username. + username: "ArkFlame Development" + + author: + # Author name. + name: "ChatSentinel" + + # Author URL. + url: "https://builtbybit.com/resources/23698/" + + # Author icon URL. + icon-url: "https://avatars.githubusercontent.com/u/53847752?s=48&v=4" + + # Discord webhook title. + title: "Notification" + + # Discord webhook color (in hexadecimal format). + color: "#55FFFF" + + # Discord webhook description. + description: "**%player%** failed **%module%** (%warns%/%maxwarns%)" + + field-names: + # Message field name. + message: "Message" + + # Server field name. + server: "Server" + + footer: + # Footer text. + text: "ChatSentinel Detection" + + # Footer icon url. + icon-url: "https://avatars.githubusercontent.com/u/53847752?s=48&v=4" + + # Discord webhook thumbnail URL. + thumbnail-url: "https://avatars.githubusercontent.com/u/53847752?s=128&v=4" diff --git a/src/main/resources/whitelist.yml b/src/main/resources/whitelist.yml index 5f4ac4e..a8f3377 100644 --- a/src/main/resources/whitelist.yml +++ b/src/main/resources/whitelist.yml @@ -1,3 +1,8 @@ +# List of servers on which the plugin will not check messages (only for Bungeecord and Velocity). +servers: + - example + - example2 + # This plugin uses Regex for checking messages. # Set to [] to disable whitelist completely. expressions: