Implement sensitive word filter

This commit is contained in:
2023-03-05 18:50:24 +08:00
parent 2a696b4854
commit 61b9be9888
6 changed files with 756 additions and 38 deletions

View File

@ -63,10 +63,20 @@
### 群服互联参考流程
1. 装好插件启动后,记录下首次生成的 `Token`,或者自己填写一个 `Token`
2. 下载 [go-cqhttp](https://github.com/Mrs4s/go-cqhttp) 并初始化配置
2. 下载 [go-cqhttp](https://github.com/Mrs4s/go-cqhttp) 并初始化配置,打开配置文件 `config.yml`
3. 在 `access-token: ''` 填写前面所述的 `Token` 内容
4. 在 `ws-reverse` 选项下的 `universal` 填写GC的服务器地址加路径例如 `ws://127.0.0.1:443/openchat`
5. 配置你的Bot账号和登录协议建议使用 `Android Watch` 登录。具体参考文档 [配置](https://docs.go-cqhttp.org/guide/config.html)。
5. 配置你的Bot账号和登录协议建议使用 `Android Watch` 登录。具体参考文档 [配置](https://docs.go-cqhttp.org/guide/config.html)。(在 `device.json``"protocol": 5` 修改为 `"protocol": 2`
6. 在GC中使用 `/sc group <groupId>` 来设置要互联的群聊
7. 在GC中使用 `/sc op <userId(QQ)>` 来设置管理员账号
8. 现在,理论上已经完成了群服互联,在群里可以看到玩家上下线和聊天,同时玩家也可以在游戏里看到群里聊天,
你还可以在群里用默认前缀 `/` 来执行命令,
但是暂时**不会**回复结果,你可能需要自己看控制台来查看执行结果。
_值得注意的是本插件支持的是 [OneBot-v11](https://github.com/botuniverse/onebot-11) 协议理论上所有支持OneBot-v11 [反向WebSocket](https://github.com/botuniverse/onebot-11/blob/master/communication/ws-reverse.md) 的机器人框架都可以连接不仅限于cqhttp。_
_TODO: 计划会出一个极简的纯对话协议比OneBot更简单方便第三方对接。_
---
@ -194,3 +204,14 @@
}
```
# 敏感词过滤系统
目前实现了一个最基础的敏感词过滤功能, 并附带了一个精简的敏感词库,
在首次启动时会把词库释放到插件数据目录下。
文件名叫 `SensitiveWordList.txt`,每行包含一个敏感词,你可以自己维护这个文件,修改后可以用 `/sc reload` 重新读取。
当检测到游戏内玩家聊天中包含敏感词,将不会进行转发,并且会在控制台中打印。
目前暂未设定惩罚机制,仅仅只是发出去别人看不到,自己不知道这个没发出去。
如果你有更好的建议,欢迎[提交 issue](https://github.com/jie65535/gc-openchat-plugin/issues/new)

View File

@ -19,16 +19,17 @@ package com.github.jie65535.openchat;
import com.github.jie65535.openchat.commands.ChatPlayerCommands;
import com.github.jie65535.openchat.commands.ChatServerCommands;
import com.github.jie65535.openchat.utils.SensitiveWordFilter;
import emu.grasscutter.plugin.Plugin;
import emu.grasscutter.server.event.EventHandler;
import emu.grasscutter.server.event.HandlerPriority;
import emu.grasscutter.server.event.player.PlayerJoinEvent;
import emu.grasscutter.utils.JsonUtils;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.Objects;
public final class OpenChatPlugin extends Plugin {
private static OpenChatPlugin instance;
@ -36,6 +37,45 @@ public final class OpenChatPlugin extends Plugin {
return instance;
}
@Override
public void onLoad() {
instance = this;
loadConfig();
loadData();
loadSensitiveWordList();
getLogger().info("[OpenChat] Loaded.");
}
@Override
public void onEnable() {
// Register event listeners.
new EventHandler<>(PlayerJoinEvent.class)
.priority(HandlerPriority.NORMAL)
.listener(EventListeners::onJoin)
.register(this);
// Register commands.
getHandle().registerCommand(new ChatServerCommands());
getHandle().registerCommand(new ChatPlayerCommands());
// Set my chat system.
getServer().setChatSystem(new OpenChatSystem(this));
// Log a plugin status message.
getLogger().info("[OpenChat] Enabled, see https://github.com/jie65535/gc-openchat-plugin");
}
@Override
public void onDisable() {
saveData();
saveConfig();
getLogger().info("[OpenChat] Disabled.");
}
// region config
private OpenChatConfig config;
public OpenChatConfig getConfig() {
return config;
@ -65,6 +105,10 @@ public final class OpenChatPlugin extends Plugin {
}
}
// endregion
// region data
private OpenChatData data;
public OpenChatData getData() {
return data;
@ -93,37 +137,33 @@ public final class OpenChatPlugin extends Plugin {
}
}
@Override
public void onLoad() {
instance = this;
loadConfig();
loadData();
getLogger().info("[OpenChat] Loaded.");
// endregion
// region SensitiveWordFilter
private final SensitiveWordFilter sensitiveWordFilter = new SensitiveWordFilter();
public SensitiveWordFilter getSensitiveWordFilter() {
return sensitiveWordFilter;
}
private static final String SENSITIVE_WORD_LIST_FILE_NAME = "SensitiveWordList.txt";
public void loadSensitiveWordList() {
try {
var sensitiveWordListFile = new File(getDataFolder(), SENSITIVE_WORD_LIST_FILE_NAME);
if (!sensitiveWordListFile.exists()) {
var in = OpenChatPlugin.class.getClassLoader().getResourceAsStream(SENSITIVE_WORD_LIST_FILE_NAME);
Files.copy(Objects.requireNonNull(in), sensitiveWordListFile.toPath());
in.close();
}
var wordList = Files.readAllLines(sensitiveWordListFile.toPath());
for (var word : wordList) {
sensitiveWordFilter.addWord(word);
}
getLogger().info("[OpenChat] {} sensitive words loaded", wordList.size());
} catch (Exception ex) {
getLogger().error("[OpenChat] Failed to load sensitive word list!", ex);
}
}
@Override
public void onEnable() {
// Register event listeners.
new EventHandler<>(PlayerJoinEvent.class)
.priority(HandlerPriority.NORMAL)
.listener(EventListeners::onJoin)
.register(this);
// Register commands.
getHandle().registerCommand(new ChatServerCommands());
getHandle().registerCommand(new ChatPlayerCommands());
// Set my chat system.
getServer().setChatSystem(new OpenChatSystem(this));
// Log a plugin status message.
getLogger().info("[OpenChat] Enabled, see https://github.com/jie65535/gc-openchat-plugin");
}
@Override
public void onDisable() {
saveData();
saveConfig();
getLogger().info("[OpenChat] Disabled.");
}
// endregion
}

View File

@ -318,8 +318,7 @@ public class OpenChatSystem extends ChatSystem {
* @return 是否合法合规
*/
private boolean checkMessageModeration(String message) {
// TODO see https://github.com/houbb/sensitive-word
return !message.isEmpty();
return !plugin.getSensitiveWordFilter().isSensitive(message);
}
// region 发言频率限制

View File

@ -107,6 +107,7 @@ public class ChatServerCommands implements CommandHandler {
}
case "reload" -> {
plugin.loadConfig();
plugin.loadSensitiveWordList();
CommandHandler.sendMessage(sender, "OK");
}
case "group" -> {

View File

@ -0,0 +1,81 @@
/*
* gc-openchat
* Copyright (C) 2022 jie65535
*
* 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 <https://www.gnu.org/licenses/>.
*/
package com.github.jie65535.openchat.utils;
import java.util.HashMap;
import java.util.Map;
public class SensitiveWordFilter {
// 定义一个内部类表示图中的节点
private static class Node {
// 用一个哈希表存储该节点的后继节点
private final Map<Character, Node> children = new HashMap<>();
// 用一个布尔值表示该节点是否是某个敏感词的结尾
private boolean isEnd = false;
}
// 定义一个根节点
private final Node root = new Node();
// 定义一个添加敏感词的方法
public void addWord(String word) {
if (word == null || word.isEmpty()) {
return;
}
// 从根节点开始遍历
Node current = root;
for (char c : word.toCharArray()) {
// 如果当前节点没有以c为键的后继节点就创建一个新的节点并添加到哈希表中
if (!current.children.containsKey(c)) {
current.children.put(c, new Node());
}
// 更新当前节点为后继节点
current = current.children.get(c);
}
// 标记当前节点为某个敏感词的结尾
current.isEnd = true;
}
// 定义一个检测聊天消息是否包含敏感词的方法
public boolean isSensitive(String message) {
if (message == null || message.isEmpty()) {
return false;
}
// 遍历聊天消息中的每个字符作为起始位置
for (int i = 0; i < message.length(); i++) {
// 从根节点开始遍历
Node current = root;
for (int j = i; j < message.length(); j++) {
char c = message.charAt(j);
// 如果当前节点没有以c为键的后继节点说明没有匹配到敏感词跳出循环
if (!current.children.containsKey(c)) {
break;
}
// 更新当前节点为后继节点
current = current.children.get(c);
// 如果当前节点是某个敏感词的结尾说明匹配到了敏感词返回true
if (current.isEnd) {
return true;
}
}
}
// 遍历完聊天消息没有匹配到任何敏感词返回false
return false;
}
}

View File

@ -0,0 +1,576 @@
18禁
a片
caonima
cnm
caotama
cao你
cao你妈
fa轮
fuck
hjt
jb
jzm
nmsl
sb
vpn
zhengfu
zheng府
zf
zedong
亂倫
噴精
姦淫
屄毛
幹炮
幹砲
擠乳汁
溫家寶
無毛穴
獸交
爱女人
爱液
扒穴
拔屄自拍
白虎阴穴
白虎少妇
白浆四溅
包二奶
薄熙来
薄码
暴奸
爆乳娘
爆草
爆操
被操
被插
被干
逼痒
逼奸
博彩
擦你妈
操我
操死
操死你
操你妈
操你奶
操你姐
操逼
操你祖宗
操你大爷
操你妹
操穴
操屄
操他妈
操你全家
操烂
操妻
操你嘴
操死她
操b
操它妈
曹刚川
草你祖宗
草你大爷
草你妈
草你吗
草bi
草她妈
草拟吗
草你娘
草他妈
厕所盗摄
厕奴
插阴茎
插逼
插妹妹
插穴止痒
插死她
插比
插b
插你
陈同海
陈水扁
陈良宇
成人片
成人网站
成人图
成人电
成人文
成人视
成人自拍
成人小
惩公安
吃鸡巴
抽插
臭鸡八
臭鸡吧
床上写真
催情藥
催情药
催眠水
催情粉
大奶子
大肉棒
大奶头
大明运气咒
大力抽送
大血逼
大雞巴
大鸡巴
大傻b
戴秉国
盗撮
邓小瓶
邓小平
邓爷爷
东北独立
杜世成
杜德印
法lun
法伦功
法维权
法一轮
法正乾
法车仑
法轮佛
法轮
翻墙
反共复清
反华示威
肥逼
粉穴
风艳阁
干你妈
干的爽
干穴
干你娘
干你妹
干死你
干你全家
肛交
肛门
肛门拳交
给你爽
根达亚文明
共残主义
狗娘养
灌满精液
郭金龙
国峰
国锋
含屌
喝血社会
黑毛屄
胡云松
胡王八
胡谨涛
胡錦濤
胡海峰
胡主席
胡春华
胡温
虎精逃
华国
黄色电影
回良玉
混蛋
激情电
激情妹
激情炮
激情短
激情小说
鸡巴
鸡奸
集体自杀
挤乳汁
几吧
妓女
家宝
奸幼
奸杀
奸污
践货
贱人
贱比
江泽民
江澤民
江x
江某某
脚奴
街头扒衣
金毛穴
锦涛
精子射在
警察说保
警方包庇
警车雷达
警察殴打
警察的幌
就去日
菊花洞
巨乳
恐怖份子
恐怖分子
抠穴
口淫
口交
口活
口内爆射
狂乳激揺
拉登
浪逼
浪叫
雷管
李小鹏
李鹏
李克强
李洪志
李世民
習近平
莲花逼
炼大法
梁光烈
两会又三
两会代
刘延东
露逼
露b
乱伦类
乱奸
乱伦小
轮子功
轮奸
轮功
伦理毛
伦理电影
伦理片
伦理大
裸舞视
裸体
裸聊网
妈个逼
妈了个逼
妈了逼
麻果配
麻果丸
麻古
玛雅历法
卖淫
满狗
肏屄
氓培训
猫贼洞
毛主席
毛遮洞
毛则东
毛泽东
毛贼东
毛澤東
美女高潮
美艳少妇
妹按摩
妹上门
门按摩
门保健
蒙汗药
孟建柱
迷昏口
迷奸
迷魂香
迷幻型
迷幻药
迷情药
迷幻藥
迷魂药
迷藥
迷奸药
迷昏药
迷昏藥
迷魂藥
迷情水
谜奸药
秘唇
蜜穴
密穴
民抗议
明慧网
摸阴蒂
某锦涛
母奸
母子乱伦
母子奸情
奶子
男奴
男女交欢
内射
嫩穴
嫩b
嫩逼
嫩屄
嫩bb
嫩阴
你妈死了
你日妈
你妈逼
娘西皮
娘了个比
娘两腿之间
浓精
怒的志愿
女优
女任职名
女人和狗
女技师
女激情
女優
女上门
女被人家搞
拍肩神药
炮友
喷尿
屁眼
平叫到床
平惨案
仆不怕饮
普通嘌
期货配
奇迹的黄
奇淫散
强暴
强硬发言
强奸你妹
强奸
强权政府
巧淫奸戏
情色
全裸
全家死绝
全家死光
群奸暴
群体性事
群起抗暴
群交
绕过封锁
人类灭亡
人兽
人妻
人妻做爱
人妻熟女
人妻榨乳
人体炸弹
人妻色诱
日中断交
日你妹
日烂
日你全家
日死你
日你妈
日逼
肉棒
肉壶
肉蒲团
肉便器
肉茎
肉淫器吞精
肉棍
肉穴
肉逼
肉棍干骚妇
肉唇
肉洞
乳交
软弱的国
三秒倒
三级片
三挫
三唑
搔逼
骚妇
骚浪美女
骚贱
骚嘴
骚姐姐
骚母
骚洞
骚穴
骚逼
骚妹
骚浪
骚乳
骚女
骚水
色妹妹
色电影
色视频
色猫
色小说
色书库
傻臂
傻逼
傻b
傻避
煞笔
煞逼
少妇
舌头穴
射颜
射爽
社会主义灭亡
沈跃跃
十八禁
兽奸
熟妇人妻
爽片
爽穴
死全家
死逼
苏树林
酥穴
塔利班
蘚鮑
台湾
台独
台湾独立
体奸
天黯门
天岸门
天案们
天安门大屠杀
天氨门
天安门
天胺门
天案门
舔脚
舔屄
铁凝
偷窥图片
推背图
退党
吞精
王胜俊
王岐山
王洛林
王太华
王鸿举
瘟总理
瘟加饱
瘟假饱
温影帝
温家堡
温切斯特
温家宝
温家某
吴定富
吴邦国
吸精少女
习近平
洗肠射尿
下流地带
下贱
销魂洞
小穴
小平遗言
小逼崽子
小平遗嘱
新唐人
性虐
性奴
性爱
性爱图库
颜射
艳妇淫女
要射精了
要泄了
要射了
要人权
耀邦
叶剑英
夜激情
液体炸
一夜欢
遗嘱小平
阴屄
阴b
阴茎
阴唇
阴部
阴締
淫贱
淫叫
淫液
淫娃
淫魔舞
淫情
淫肉
淫蜜
淫蕩
淫汁
淫姐
淫妞
淫逼
淫亂潮吹
淫荡
淫騷妹
淫情女
淫奴
淫兽
淫兽学
淫水
淫妇
淫穴
淫水爱液
淫母
淫亂
幼交
玉穴
原味内裤
原味内衣
援交妹
杂种操的
则民爷爷
泽民
炸立交
炸弹遥控
炸药的制
炸药
炸弹
炸药配
炸弹教
炸鸟巢
炸广州
炸药制
炸学校
张志国
张德江
张荣坤
政治局常委
政fu
政府
制服狩
中国人权
中日断交
朱镕基
猪容鸡
诸世纪
主席
主席像
自焚
自拍美穴
自慰抠穴
自慰
总竖鸡
总理
总书记
钻插
做爱
妞上门
嫖妓指南
嫖鸡
嫖俄罗
門服務
陰道
陰唇
陰戶
掰穴
騷浪