Added cancel_msg config and update docs

+ cancel_msg allows a category to cancel the offending message, blocking it from going to chat.
+ Updated docs to be a bit more clear
+ Switched to woodpecker
+ Fixes #1 and #2
master
Joey Hines 2022-02-12 09:15:37 -07:00
parent 237328ab50
commit a71714ae7b
No known key found for this signature in database
GPG Key ID: 80F567B5C968F91B
9 changed files with 124 additions and 61 deletions

27
.woodpeck.yml 100644
View File

@ -0,0 +1,27 @@
clone:
git:
image: "woodpeckerci/plugin-git:next"
pipeline:
compliance:
commands:
- "gradle build"
- "gradle test"
image: "gradle:7.4-jdk17"
when:
event: pull_request
build:
commands:
- "gradle shadowJar"
image: "gradle:7.4-jdk17"
when:
branch: main
release:
image: jolheiser/drone-gitea-main:latest
settings:
token:
from_secret: gitea_token
base: https://git.jojodev.com
files:
- "build/libs/*.jar"
when:
branch: main

View File

@ -1,26 +1,37 @@
# Hush [![Build Status](https://drone.etztech.xyz/api/badges/Minecraft/Hush/status.svg)](https://drone.etztech.xyz/Minecraft/Hush) # Hush [![Build Status](https://ci.jojodev.com/api/badges/Minecraft/Hush/status.svg)(https://ci.jojodev.com/Minecraft/Hush)
A plugin to monitor chat for forbidden phrases. A plugin to monitor chat for forbidden phrases.
## Watch Lists ## Watch Lists
Watch lists specifies phrases to monitor. Each watch list has an associated permission. In the form of Watchlist are groups of categories Hush monitors for. To enable a watchlist for a player,
`hush.<watchlist_name>`. A user with this permission will have their chat messages checked to see if they should be given the `hush.<watchlist_name>` permission.
they match the corresponding watch list.
Watch lists contain two types of phrases, Monitored and Banned. ### Categories
A category is a group of regex filters, and the actions to run when a player matches one of those filters.
An example of a category is shown below:
### Monitored ```yaml
Phrases that should be checked for context. If a user says one of these phrases, a Discord webhook is # "ban" category
sent out. ban:
# Filters to search for in chat
filters:
- "heck"
- "fricks"
# Optional, commands to run when a filter matches. {player} is replaced with the player's name.
commands:
- "ban {player}"
# Optional, should the message that matched the filter be cancelled? Default is "false".
cancel_msg: true
```
### Banned ## Permissions
A banned phrase results in an immediate ban. If a user says a banned phrase, they are kicked and added * `hush.admin` - allows access to the `hush` command
to the ban list. A webhook is also sent to Discord. * `hush.<watchlist_name>` - used to determine if a player's messages should be checked by a watchlist
### Commands
* `hush reload` - reloads the plugin config
## Example Config ## Example Config
[Config](src/main/resources/config.yml) [Config](src/main/resources/config.yml)
## Permissions
[Permissions](src/main/resources/plugin.yml)
## License ## License
[MIT](LICENSE) [MIT](LICENSE)

View File

@ -4,7 +4,7 @@ plugins {
} }
group = 'com.zerohighdef' group = 'com.zerohighdef'
version = '0.2.1' version = '0.2.2'
sourceCompatibility = '8' sourceCompatibility = '8'
@ -16,7 +16,7 @@ repositories {
} }
maven { maven {
name = 'etztech-repo' name = 'etztech-repo'
url = 'http://repo.etztech.xyz/' url = 'https://mvn.jojodev.com/releases/'
} }
maven {url = 'https://jcenter.bintray.com'} maven {url = 'https://jcenter.bintray.com'}
maven {url = 'https://jitpack.io'} maven {url = 'https://jitpack.io'}

View File

@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.6-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists

View File

@ -44,7 +44,8 @@ public final class Hush extends JavaPlugin {
ConfigurationSection categorySection = permissionSection.getConfigurationSection(category); ConfigurationSection categorySection = permissionSection.getConfigurationSection(category);
List<String> filters = categorySection.getStringList("filters"); List<String> filters = categorySection.getStringList("filters");
List<String> commands = categorySection.getStringList("commands"); List<String> commands = categorySection.getStringList("commands");
watchCategories.add(new WatchCategory(category, filters, commands)); boolean cancelMsg = categorySection.getBoolean("cancel_msg", false);
watchCategories.add(new WatchCategory(category, filters, commands, cancelMsg));
} }
watchLists.put(permission, watchCategories); watchLists.put(permission, watchCategories);
} }

View File

@ -9,11 +9,13 @@ public class WatchCategory {
private final String categoryName; private final String categoryName;
private final List<Pattern> patterns; private final List<Pattern> patterns;
private final List<String> commands; private final List<String> commands;
private final boolean cancelMsg;
public WatchCategory(String categoryName, List<String> patterns, List<String> commands) { public WatchCategory(String categoryName, List<String> patterns, List<String> commands, boolean cancelMsg) {
this.categoryName = categoryName; this.categoryName = categoryName;
this.patterns = buildPattern(patterns); this.patterns = buildPattern(patterns);
this.commands = commands; this.commands = commands;
this.cancelMsg = cancelMsg;
} }
private static List<Pattern> buildPattern(List<String> patternList) { private static List<Pattern> buildPattern(List<String> patternList) {
@ -42,4 +44,8 @@ public class WatchCategory {
public List<String> getCommands() { public List<String> getCommands() {
return commands; return commands;
} }
public boolean getCancelMsg() {
return cancelMsg;
}
} }

View File

@ -21,6 +21,10 @@ public class MainCommand implements CommandExecutor {
@Override @Override
public boolean onCommand(CommandSender commandSender, Command command, String s, String[] args) { public boolean onCommand(CommandSender commandSender, Command command, String s, String[] args) {
if (!commandSender.hasPermission("hush.admin")) { if (!commandSender.hasPermission("hush.admin")) {
BaseComponent[] message = new ComponentBuilder()
.append("You don't have permission to to use this command.").color(ChatColor.RED)
.create();
commandSender.spigot().sendMessage(message);
return true; return true;
} }
if (args.length == 0) { if (args.length == 0) {

View File

@ -26,59 +26,69 @@ public class HushAsyncChatListener implements Listener {
} }
@EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
public boolean onChat(AsyncPlayerChatEvent event) { public void onChat(AsyncPlayerChatEvent event) {
String chatMessage = event.getMessage(); String chatMessage = event.getMessage();
Player sender = event.getPlayer(); Player sender = event.getPlayer();
for (String permission : plugin.getWatchLists().keySet()) { for (String permission : plugin.getWatchLists().keySet()) {
if (sender.hasPermission("hush." + permission)) { if (sender.hasPermission("hush." + permission)) {
for (WatchCategory category : plugin.getWatchLists().get(permission)) { for (WatchCategory category : plugin.getWatchLists().get(permission)) {
checkMessage(category, permission, sender, chatMessage); boolean is_match = checkMessage(category, permission, sender, chatMessage);
// If the message matched, and the category requires the event to be canceled
if (is_match && category.getCancelMsg()) {
// cancel event
event.setCancelled(true);
}
} }
} }
} }
return true;
} }
private void runCommand(String command) { private void runCommand(String command) {
Bukkit.getScheduler().runTask(plugin, () -> Bukkit.dispatchCommand(Bukkit.getConsoleSender(), command)); Bukkit.getScheduler().runTask(plugin, () -> Bukkit.dispatchCommand(Bukkit.getConsoleSender(), command));
} }
private void checkMessage(WatchCategory category, String watchList, Player player, String chatMessage) { private boolean checkMessage(WatchCategory category, String watchList, Player player, String chatMessage) {
Matcher match = category.checkPatterns(chatMessage); Matcher match = category.checkPatterns(chatMessage);
if (match != null) {
String playerName = player.getName();
String webhookURL = plugin.getConfig().getString("webhook");
for (String command: category.getCommands()) {
runCommand(command.replace("{player}", playerName));
}
plugin.log(String.format("%s matched filter in %s.%s", playerName, watchList, category.getCategoryName()));
if (webhookURL != null && !plugin.isInCoolDown(player)) {
plugin.addCoolDown(player);
chatMessage = match.replaceAll("**$0**");
String message = Javacord.escapeFormat(playerName) + " said: " + chatMessage;
Embed embed = new Embed()
.color(0xC70039)
.description(message)
.addField(new Field("Watchlist", String.format("`%s`", watchList)))
.addField(new Field("Category", String.format("`%s`", category.getCategoryName())))
.author(new Author(playerName, "", String.format("https://minotar.net/helm/%s/100.png", playerName), ""))
.timestamp(OffsetDateTime.now());
Webhook webhook = new Webhook("@here", embed);
this.plugin.getServer().getScheduler().runTaskAsynchronously(this.plugin, () -> {
try {
Javacord.sendWebhook(webhookURL, webhook);
} catch (Exception e) {
this.plugin.log("Webhook failed to send.");
}
});
}
if (match == null) {
// message was not a match
return false;
} }
String playerName = player.getName();
String webhookURL = plugin.getConfig().getString("webhook");
for (String command: category.getCommands()) {
runCommand(command.replace("{player}", playerName));
}
plugin.log(String.format("%s matched filter in %s.%s", playerName, watchList, category.getCategoryName()));
if (webhookURL != null && !plugin.isInCoolDown(player)) {
plugin.addCoolDown(player);
chatMessage = match.replaceAll("**$0**");
String message = Javacord.escapeFormat(playerName) + " said: " + chatMessage;
Embed embed = new Embed()
.color(0xC70039)
.description(message)
.addField(new Field("Watchlist", String.format("`%s`", watchList)))
.addField(new Field("Category", String.format("`%s`", category.getCategoryName())))
.author(new Author(playerName, "", String.format("https://minotar.net/helm/%s/100.png", playerName), ""))
.timestamp(OffsetDateTime.now());
Webhook webhook = new Webhook("@here", embed);
this.plugin.getServer().getScheduler().runTaskAsynchronously(this.plugin, () -> {
try {
Javacord.sendWebhook(webhookURL, webhook);
} catch (Exception e) {
this.plugin.log("Webhook failed to send.");
}
});
}
return true;
} }
} }

View File

@ -5,18 +5,22 @@ cooldown: 5
watch_lists: watch_lists:
# Permission to check # Permission to check
# To enable the check, give the group the 'hush.<watch_list>' permission # To enable the check for a user, give the 'hush.<watch_list>' permission
guest: guest:
# "ban" category
ban: ban:
# Filters to search for # Filters to search for in chat
filters: filters:
- "heck" - "heck"
- "fricks" - "fricks"
# Commands to run {player} is replaced with the player's name # Optional, commands to run when a filter matches. {player} is replaced with the player's name.
commands: commands:
- "ban {player}" - "ban {player}"
# Optional, should the message that matched the filter be cancelled. Default is "false".
cancel_msg: true
# "monitor" category
monitor: monitor:
# Filters to search for # Filters to search for in chat
filters: filters:
- "stupid" - "stupid"
- "dumb" - "dumb"