Skip to content
This repository has been archived by the owner on Jan 20, 2024. It is now read-only.

Commit

Permalink
Plugin: add swear protection
Browse files Browse the repository at this point in the history
  • Loading branch information
TheFaser committed Sep 15, 2023
1 parent 718d7ca commit 66900b8
Show file tree
Hide file tree
Showing 8 changed files with 337 additions and 4 deletions.
11 changes: 7 additions & 4 deletions src/main/java/net/flectone/Main.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,16 @@
import net.flectone.sqlite.Database;
import net.flectone.sqlite.SQLite;
import net.flectone.tickers.PlayerPingTicker;
import net.flectone.utils.CommandsUtil;
import net.flectone.utils.MetricsUtil;
import net.flectone.utils.NMSUtil;
import net.flectone.utils.WebUtil;
import net.flectone.utils.*;
import org.bukkit.Bukkit;
import org.bukkit.command.PluginCommand;
import org.bukkit.event.Listener;
import org.bukkit.plugin.java.JavaPlugin;

import java.sql.SQLException;

import static net.flectone.managers.FileManager.config;

public final class Main extends JavaPlugin implements Listener {

private static Main instance;
Expand Down Expand Up @@ -77,6 +76,10 @@ public void onEnable() {
PlayerDeathEventListener.reload();
PlayerAdvancementDoneListener.reload();

if (config.getBoolean("chat.swear-protection.enable")) {
BlackListUtil.loadSwears();
}

info("✔ Plugin enabled");

Bukkit.getScheduler().runTaskAsynchronously(this, WebUtil::checkNewerVersion);
Expand Down
5 changes: 5 additions & 0 deletions src/main/java/net/flectone/commands/CommandFlectonechat.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import net.flectone.misc.commands.FCommand;
import net.flectone.misc.commands.FTabCompleter;
import net.flectone.misc.files.FYamlConfiguration;
import net.flectone.utils.BlackListUtil;
import net.flectone.utils.ObjectUtil;
import org.bukkit.Bukkit;
import org.bukkit.command.Command;
Expand Down Expand Up @@ -90,6 +91,10 @@ public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command
PlayerDeathEventListener.reload();
PlayerAdvancementDoneListener.reload();

if (config.getBoolean("chat.swear-protection.enable")) {
BlackListUtil.loadSwears();
}

if (config.getBoolean("server.brand.enable")) {
ServerBrand.getInstance().updateEveryBrand();
}
Expand Down
52 changes: 52 additions & 0 deletions src/main/java/net/flectone/messages/MessageBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import net.flectone.misc.components.FPlayerComponent;
import net.flectone.misc.components.FURLComponent;
import net.flectone.misc.entity.FPlayer;
import net.flectone.utils.BlackListUtil;
import net.flectone.utils.ObjectUtil;
import net.flectone.utils.Pair;
import net.md_5.bungee.api.chat.BaseComponent;
Expand All @@ -23,6 +24,7 @@
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import static net.flectone.managers.FileManager.config;
import static net.flectone.managers.FileManager.locale;
Expand Down Expand Up @@ -51,6 +53,10 @@ public MessageBuilder(@NotNull String command, @NotNull String text, @Nullable C
this.clickable = clickable;
this.sender = sender;

if (config.getBoolean("chat.swear-protection.enable")) {
text = replaceSwears(text);
}

List<Pair<String, Integer>> sortedPairs = new ArrayList<>();
Map<String, Integer> patternIndexes = new HashMap<>();

Expand Down Expand Up @@ -135,6 +141,52 @@ public MessageBuilder(@NotNull String command, @NotNull String text, @Nullable C

}

private String replaceSwears(String text) {
String[] words = text.split(" ");

StringBuilder stringBuilder = new StringBuilder();

int lastX = 0;

for (int x = 0; x < words.length; x++) {
String word = words[x];

stringBuilder.append(word);
if (BlackListUtil.contains(stringBuilder.toString())) {
word = locale.getFormatString("chat.swear-protection.message", sender).repeat(3);

String textWithSwear = stringBuilder.toString();

boolean remove = false;
for (int y = lastX; y < x; y++) {
if (remove) {
words[y] = "";
continue;
}

textWithSwear = textWithSwear.substring(words[y].length());
if (!BlackListUtil.contains(textWithSwear)) {
words[y] = "";
remove = true;
}
}

words[x] = word;

stringBuilder = new StringBuilder();
lastX = x + 1;
}
}

if (lastX != 0 && sender instanceof Player player) {
ObjectUtil.playSound(player, "swear");
}

return Arrays.stream(words)
.filter(word -> !word.isEmpty())
.collect(Collectors.joining(" "));
}

private boolean isPatternEnabled(String patterName) {
return config.getBoolean("chat." + patterName + ".enable")
&& (sender == null || sender.hasPermission("flectonechat.chat." + patterName));
Expand Down
170 changes: 170 additions & 0 deletions src/main/java/net/flectone/utils/BlackListUtil.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
package net.flectone.utils;

import net.flectone.managers.FileManager;
import net.flectone.misc.files.FYamlConfiguration;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/*
This file is part of DonationExecutor
https://github.com/link1107/DonationExecutor/blob/master/src/main/java/igorlink/service/Utils.java
DonationExecutor
Copyright (C) 2022 link1107
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

public class BlackListUtil {

private static final List<String> swears = new ArrayList<>();
private static final HashMap<Character, List<Character>> synonymousChars = new HashMap<>();

static {
loadSwears();

synonymousChars.put('h', (Arrays.asList('x', 'х', 'н', 'n')));
synonymousChars.put('n', (Arrays.asList('н', 'й', 'и')));
synonymousChars.put('н', (Arrays.asList('h', 'n', 'й', 'и')));
synonymousChars.put('e', (Arrays.asList('е', '3', 'з')));
synonymousChars.put('е', (Arrays.asList('e', '3', 'з')));
synonymousChars.put('г', (Arrays.asList('r', 'я', 'g', '7', '6')));
synonymousChars.put('r', (Arrays.asList('г', 'я', 'g', '7', '6')));
synonymousChars.put('g', (Arrays.asList('г', 'r', '7', '6')));
synonymousChars.put('p', (Arrays.asList('п', 'р', 'n', 'я', 'r')));
synonymousChars.put('р', (Arrays.asList('p', 'r', 'я')));
synonymousChars.put('п', (Arrays.asList('p', 'n', 'и', 'р')));
synonymousChars.put('o', (Arrays.asList('о', '0')));
synonymousChars.put('о', (Arrays.asList('o', '0')));
synonymousChars.put('a', (List.of('а')));
synonymousChars.put('а', (List.of('a')));
synonymousChars.put('и', (Arrays.asList('i', 'n', 'e', 'е', '|', 'l', '!', '1', '3', 'й')));
synonymousChars.put('i', (Arrays.asList('1', 'и', 'e', 'е', '|', 'l', '!', 'й')));
synonymousChars.put('с', (Arrays.asList('c', 's', '$', '5')));
synonymousChars.put('s', (Arrays.asList('c', 'с', '$', '5')));
synonymousChars.put('c', (Arrays.asList('s', 'с', '$', '5')));
synonymousChars.put('л', (Arrays.asList('l', '1', '|')));
synonymousChars.put('l', (Arrays.asList('л', '1', '|', '!')));
synonymousChars.put('1', (Arrays.asList('л', 'i', 'l', '|')));
synonymousChars.put('d', (Arrays.asList('д', 'л')));
synonymousChars.put('д', (Arrays.asList('d', 'л', '9')));
synonymousChars.put('y', (Arrays.asList('у', 'u', 'ы')));
synonymousChars.put('у', (Arrays.asList('y', 'u', 'ы')));
synonymousChars.put('x', (Arrays.asList('х', 'h')));
synonymousChars.put('х', (Arrays.asList('x', 'h')));
synonymousChars.put('ы', (Arrays.asList('у', 'u', 'y')));
synonymousChars.put('ч', (List.of('4')));
synonymousChars.put('k', (List.of('к')));
synonymousChars.put('к', (List.of('k')));
synonymousChars.put('0', (Arrays.asList('о', 'o')));
synonymousChars.put('3', (Arrays.asList('e', 'е','з')));
synonymousChars.put('4', (List.of('ч')));
synonymousChars.put('5', (Arrays.asList('с', 'c', 's')));
synonymousChars.put('9', (Arrays.asList('r', 'я')));
}

public static void loadSwears() {
FYamlConfiguration swearsYaml = FileManager.load("swears.yml");
swears.clear();
swears.addAll(swearsYaml.getStringList("list"));
}

public static Boolean contains(String text) {

String validationText = text.toLowerCase();

Pattern pattern = Pattern.compile("[l1i]*[\\-]*[l1i]*");
Matcher matcher = pattern.matcher(validationText);
if ( (matcher.find()) && (!matcher.group().isEmpty()) ) {
validationText = validationText.replace(matcher.group(), "н");
}

validationText = validationText.replace("_", "");
validationText = validationText.replace(" ", "");
validationText = validationText.replace(",", "");
validationText = validationText.replace(".", "");
validationText = validationText.replace("-", "");
validationText = validationText.replace("%", "");
validationText = validationText.replace("*", "");
validationText = validationText.replace("?", "");

if (validationText.isEmpty()) {
return false;
}


if (!(validationText.matches("[a-zа-я0-9$!ё]*"))) {
return true;
}

for (String ss : swears) {
for (int i = 0; i <= validationText.length() - ss.length(); i++) {
int tempi = i;
for (int j = 0; j <= ss.length(); j++) {

if (j == ss.length()) {
return true;
}

if (validationText.charAt(tempi + j) == ss.charAt(j)) {
continue;
} else if ((synonymousChars.containsKey(ss.charAt(j))) && (synonymousChars.get(ss.charAt(j)).contains(validationText.charAt(tempi + j)))) {
continue;
}

while (true) {
if (j==0) {
break;
}
if (validationText.charAt(tempi + j) != validationText.charAt(tempi + j - 1)) {
if (!(synonymousChars.containsKey(validationText.charAt(tempi + j)))) {
break;
} else if (!(synonymousChars.get(validationText.charAt(tempi + j)).contains(validationText.charAt(tempi + j - 1)))) {
break;
}
}
tempi++;
if ((validationText.length()-tempi-j) < (ss.length()-j)) {
break;
}
}

if ((validationText.length()-tempi-j) < (ss.length()-j)) {
break;
}

if (validationText.charAt(tempi + j) == ss.charAt(j)) {
continue;
} else if ((synonymousChars.containsKey(ss.charAt(j)))) {
if ((synonymousChars.get(ss.charAt(j)).contains(validationText.charAt(tempi + j)))) {
continue;
}
}

break;

}
}
}

return false;
}
}
7 changes: 7 additions & 0 deletions src/main/resources/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@ chat:

set-cancelled: false

# list of blacklisted words can be found in swears.yml
swear-protection:
enable: false

patterns:
- ":) , ☺"
- ":D , ☻"
Expand Down Expand Up @@ -511,6 +515,9 @@ cool-down:
# Sound type format
# SOUND_NAME:VOLUME:PITCH
sound:
swear:
enable: false
type: "BLOCK_NOTE_BLOCK_BELL:1:1"
kick:
enable: false
type: "BLOCK_NOTE_BLOCK_BELL:1:1"
Expand Down
3 changes: 3 additions & 0 deletions src/main/resources/language/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,9 @@ chat:

all-disabled: "#ff4e4e⁉ Chat is disabled on this server"

swear-protection:
message: ""

tab-complete:
message: "(message)"
reason: "(reason)"
Expand Down
3 changes: 3 additions & 0 deletions src/main/resources/language/ru.yml
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,9 @@ chat:

all-disabled: "#ff4e4e⁉ На этом сервере отключён чат"

swear-protection:
symbol: ""

tab-complete:
message: "(сообщение)"
reason: "(причина)"
Expand Down

0 comments on commit 66900b8

Please sign in to comment.