mirror of
https://github.com/zhenxun-org/zhenxun_bot.git
synced 2025-12-14 21:52:56 +08:00
update black_word
This commit is contained in:
parent
b0ab78340a
commit
56573d1d34
1
.gitignore
vendored
1
.gitignore
vendored
@ -142,6 +142,5 @@ test.py
|
||||
server_ip.py
|
||||
member_activity_handle.py
|
||||
Yu-Gi-Oh/
|
||||
black_word/
|
||||
csgo/
|
||||
fantasy_card/
|
||||
|
||||
@ -242,6 +242,11 @@ __Docker 最新版本由 [Sakuracio](https://github.com/Sakuracio) 提供__
|
||||
|
||||
## 更新
|
||||
|
||||
### 2022/5/1
|
||||
|
||||
* 删除了`group_last_chat`插件(该功能可由`chat_history`替代
|
||||
* 新增敏感词检测(全新反击系统,是时候重拳出击了
|
||||
|
||||
### 2022/4/26
|
||||
|
||||
* 修复了群白名单无法正确添加
|
||||
|
||||
@ -42,7 +42,7 @@ __plugin_settings__ = {
|
||||
}
|
||||
|
||||
|
||||
msg_handler = on_regex(r"(周|月)?消息统计(des|DES)?(n=[0-9]{1,2})?", priority=5, block=True)
|
||||
msg_handler = on_regex(r"^(周|月)?消息统计(des|DES)?(n=[0-9]{1,2})?$", priority=5, block=True)
|
||||
|
||||
|
||||
@msg_handler.handle()
|
||||
|
||||
@ -277,7 +277,7 @@ def _create_help_img(
|
||||
0,
|
||||
plain_text=msg,
|
||||
font_size=24,
|
||||
font="yuanshen.ttf",
|
||||
font="HYWenHei-85W.ttf",
|
||||
)
|
||||
B.paste(text, (w, h), True)
|
||||
h += 50
|
||||
@ -289,7 +289,7 @@ def _create_help_img(
|
||||
0,
|
||||
plain_text="注: 红字代表功能被群管理员禁用,红线代表功能正在维护",
|
||||
font_size=24,
|
||||
font="yuanshen.ttf",
|
||||
font="HYWenHei-85W.ttf",
|
||||
font_color=(231, 74, 57)
|
||||
),
|
||||
(300, 10),
|
||||
|
||||
@ -126,35 +126,39 @@ async def xie_ai(text: str) -> str:
|
||||
"""
|
||||
res = await AsyncHttpx.get(f"http://api.qingyunke.com/api.php?key=free&appid=0&msg={text}")
|
||||
content = ""
|
||||
data = json.loads(res.text)
|
||||
if data["result"] == 0:
|
||||
content = data["content"]
|
||||
if "菲菲" in content:
|
||||
content = content.replace("菲菲", NICKNAME)
|
||||
if "艳儿" in content:
|
||||
content = content.replace("艳儿", NICKNAME)
|
||||
if "公众号" in content:
|
||||
content = ""
|
||||
if "{br}" in content:
|
||||
content = content.replace("{br}", "\n")
|
||||
if "提示" in content:
|
||||
content = content[: content.find("提示")]
|
||||
if "淘宝" in content or "taobao.com" in content:
|
||||
return ""
|
||||
while True:
|
||||
r = re.search("{face:(.*)}", content)
|
||||
if r:
|
||||
id_ = r.group(1)
|
||||
content = content.replace(
|
||||
"{" + f"face:{id_}" + "}", str(face(int(id_)))
|
||||
)
|
||||
else:
|
||||
break
|
||||
return (
|
||||
content
|
||||
if not content and not Config.get_config("ai", "ALAPI_AI_CHECK")
|
||||
else await check_text(content)
|
||||
)
|
||||
try:
|
||||
data = json.loads(res.text)
|
||||
if data["result"] == 0:
|
||||
content = data["content"]
|
||||
if "菲菲" in content:
|
||||
content = content.replace("菲菲", NICKNAME)
|
||||
if "艳儿" in content:
|
||||
content = content.replace("艳儿", NICKNAME)
|
||||
if "公众号" in content:
|
||||
content = ""
|
||||
if "{br}" in content:
|
||||
content = content.replace("{br}", "\n")
|
||||
if "提示" in content:
|
||||
content = content[: content.find("提示")]
|
||||
if "淘宝" in content or "taobao.com" in content:
|
||||
return ""
|
||||
while True:
|
||||
r = re.search("{face:(.*)}", content)
|
||||
if r:
|
||||
id_ = r.group(1)
|
||||
content = content.replace(
|
||||
"{" + f"face:{id_}" + "}", str(face(int(id_)))
|
||||
)
|
||||
else:
|
||||
break
|
||||
return (
|
||||
content
|
||||
if not content and not Config.get_config("ai", "ALAPI_AI_CHECK")
|
||||
else await check_text(content)
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"Ai xie_ai 发生错误 {type(e)}:{e}")
|
||||
return ""
|
||||
|
||||
|
||||
def hello() -> str:
|
||||
|
||||
249
plugins/black_word/__init__.py
Normal file
249
plugins/black_word/__init__.py
Normal file
@ -0,0 +1,249 @@
|
||||
from nonebot.adapters.onebot.v11 import (
|
||||
Event,
|
||||
MessageEvent,
|
||||
GroupMessageEvent,
|
||||
Message,
|
||||
Bot,
|
||||
)
|
||||
from nonebot.matcher import Matcher
|
||||
from nonebot.message import run_preprocessor
|
||||
from utils.image_utils import BuildImage
|
||||
from utils.utils import get_message_text, is_number
|
||||
from nonebot.params import RegexGroup, CommandArg
|
||||
from .utils import black_word_manager
|
||||
from nonebot import on_command, on_message, on_regex
|
||||
from configs.config import Config, NICKNAME
|
||||
from nonebot.permission import SUPERUSER
|
||||
from .data_source import show_black_text_image, set_user_punish
|
||||
from services.log import logger
|
||||
from models.ban_user import BanUser
|
||||
from datetime import datetime
|
||||
from utils.message_builder import image
|
||||
from .model import BlackWord
|
||||
from typing import Tuple, Any
|
||||
|
||||
|
||||
__zx_plugin_name__ = "敏感词检测"
|
||||
__plugin_usage__ = """
|
||||
usage:
|
||||
注意你的发言!
|
||||
指令:
|
||||
惩罚机制
|
||||
""".strip()
|
||||
__plugin_superuser_usage__ = """
|
||||
usage:
|
||||
查看和设置惩罚
|
||||
Regex:^记录名单(u:\d*)?(g:\d*)?(d[=><]\d*-\d{1,2}-\d{1,2})?$
|
||||
设置惩罚id需要通过 '记录名单u:xxxxxxxx' 获取
|
||||
指令:
|
||||
记录名单
|
||||
设置惩罚 [user_id] [id] [punish_level]
|
||||
示例:记录名单
|
||||
示例:记录名单u:12345678
|
||||
示例:设置惩罚 12345678 1 4
|
||||
""".strip()
|
||||
__plugin_des__ = "请注意你的发言!!"
|
||||
__plugin_type__ = ("其他",)
|
||||
__plugin_version__ = 0.1
|
||||
__plugin_author__ = "HibiKier"
|
||||
__plugin_cmd__ = ["惩罚机制", "记录名单 [_superuser]", "设置惩罚 [_superuser]"]
|
||||
__plugin_settings__ = {
|
||||
"cmd": ["敏感词检测"],
|
||||
}
|
||||
|
||||
|
||||
Config.add_plugin_config(
|
||||
"black_word", "CYCLE_DAYS", 30, name="敏感词检测与惩罚", help_="黑名单词汇记录周期", default_value=30
|
||||
)
|
||||
|
||||
Config.add_plugin_config(
|
||||
"black_word",
|
||||
"TOLERATE_COUNT",
|
||||
[5, 1, 1, 1, 1],
|
||||
help_="各个级别惩罚的容忍次数,依次为:1, 2, 3, 4, 5",
|
||||
default_value=[5, 1, 1, 1, 1],
|
||||
)
|
||||
|
||||
Config.add_plugin_config(
|
||||
"black_word", "AUTO_PUNISH", True, help_="是否启动自动惩罚机制", default_value=True
|
||||
)
|
||||
|
||||
# Config.add_plugin_config(
|
||||
# "black_word", "IGNORE_GROUP", [], help_="退出群聊惩罚中忽略的群聊,即不会退出的群聊", default_value=[]
|
||||
# )
|
||||
|
||||
Config.add_plugin_config(
|
||||
"black_word",
|
||||
"BAN_4_DURATION",
|
||||
360,
|
||||
help_="Union[int, List[int, int]]Ban时长(分钟),四级惩罚,可以为指定数字或指定列表区间(随机),例如 [30, 360]",
|
||||
default_value=360,
|
||||
)
|
||||
|
||||
Config.add_plugin_config(
|
||||
"black_word",
|
||||
"BAN_3_DURATION",
|
||||
7,
|
||||
help_="Union[int, List[int, int]]Ban时长(天),三级惩罚,可以为指定数字或指定列表区间(随机),例如 [7, 30]",
|
||||
default_value=360,
|
||||
)
|
||||
|
||||
Config.add_plugin_config(
|
||||
"black_word",
|
||||
"WARNING_RESULT",
|
||||
f"请注意对{NICKNAME}的发言内容",
|
||||
help_="口头警告内容",
|
||||
default_value=f"请注意对{NICKNAME}的发言内容",
|
||||
)
|
||||
|
||||
Config.add_plugin_config(
|
||||
"black_word",
|
||||
"AUTO_ADD_PUNISH_LEVEL",
|
||||
True,
|
||||
help_="自动提级机制,当周期内处罚次数大于某一特定值就提升惩罚等级",
|
||||
default_value=True,
|
||||
)
|
||||
|
||||
Config.add_plugin_config(
|
||||
"black_word",
|
||||
"ADD_PUNISH_LEVEL_TO_COUNT",
|
||||
3,
|
||||
help_="在CYCLE_DAYS周期内触发指定惩罚次数后提升惩罚等级",
|
||||
default_value=3,
|
||||
)
|
||||
|
||||
Config.add_plugin_config(
|
||||
"black_word",
|
||||
"ALAPI_CHECK_FLAG",
|
||||
False,
|
||||
help_="当未检测到已收录的敏感词时,开启ALAPI文本检测并将疑似文本发送给超级用户",
|
||||
default_value=False,
|
||||
)
|
||||
|
||||
Config.add_plugin_config(
|
||||
"black_word",
|
||||
"CONTAIN_BLACK_STOP_PROPAGATION",
|
||||
True,
|
||||
help_="当文本包含任意敏感词时,停止向下级插件传递,即不触发ai",
|
||||
default_value=True,
|
||||
)
|
||||
|
||||
message_matcher = on_message(priority=1, block=False)
|
||||
|
||||
set_punish = on_command("设置惩罚", priority=1, permission=SUPERUSER, block=True)
|
||||
|
||||
show_black = on_regex(
|
||||
r"^记录名单(u:\d*)?(g:\d*)?(d[=><]\d*-\d{1,2}-\d{1,2})?$",
|
||||
priority=1,
|
||||
permission=SUPERUSER,
|
||||
block=True,
|
||||
)
|
||||
|
||||
show_punish = on_command("惩罚机制", aliases={"敏感词检测"}, priority=1, block=True)
|
||||
|
||||
|
||||
# 黑名单词汇检测
|
||||
@run_preprocessor
|
||||
async def _(
|
||||
bot: Bot,
|
||||
matcher: Matcher,
|
||||
event: Event,
|
||||
):
|
||||
if (
|
||||
isinstance(event, MessageEvent)
|
||||
and event.is_tome()
|
||||
and matcher.plugin_name == "black_word"
|
||||
and not await BanUser.is_ban(event.user_id)
|
||||
and str(event.user_id) not in bot.config.superusers
|
||||
):
|
||||
user_id = event.user_id
|
||||
group_id = event.group_id if isinstance(event, GroupMessageEvent) else None
|
||||
msg = get_message_text(event.json())
|
||||
if await black_word_manager.check(user_id, group_id, msg) and Config.get_config(
|
||||
"black_word", "CONTAIN_BLACK_STOP_PROPAGATION"
|
||||
):
|
||||
matcher.stop_propagation()
|
||||
|
||||
|
||||
@show_black.handle()
|
||||
async def _(bot: Bot, reg_group: Tuple[Any, ...] = RegexGroup()):
|
||||
user_id, group_id, date = reg_group
|
||||
date_type = "="
|
||||
if date:
|
||||
date_type = date[1]
|
||||
date = date[2:]
|
||||
try:
|
||||
date = datetime.strptime(date, "%Y-%m-%d")
|
||||
except ValueError:
|
||||
await show_black.finish("日期格式错误,需要:年-月-日")
|
||||
pic = await show_black_text_image(
|
||||
bot,
|
||||
int(user_id.split(":")[1]) if user_id else None,
|
||||
int(group_id.split(":")[1]) if group_id else None,
|
||||
date,
|
||||
date_type,
|
||||
)
|
||||
await show_black.send(image(b64=pic.pic2bs4()))
|
||||
|
||||
|
||||
@show_punish.handle()
|
||||
async def _():
|
||||
text = f"""
|
||||
** 惩罚机制 **
|
||||
|
||||
惩罚前包含容忍机制,在指定周期内会容忍偶尔少次数的敏感词只会进行警告提醒
|
||||
|
||||
多次触发同级惩罚会使惩罚等级提高,即惩罚自动提级机制
|
||||
|
||||
目前公开的惩罚等级:
|
||||
|
||||
1级:永久ban
|
||||
|
||||
2级:删除好友
|
||||
|
||||
3级:ban指定/随机天数
|
||||
|
||||
4级:ban指定/随机时长
|
||||
|
||||
5级:警告
|
||||
|
||||
备注:
|
||||
|
||||
该功能为测试阶段,如果你有被误封情况,请联系管理员,会从数据库中提取出你的数据进行审核后判断
|
||||
|
||||
目前该功能暂不完善,部分情况会由管理员鉴定,请注意对真寻的发言
|
||||
|
||||
关于敏感词:
|
||||
|
||||
记住不要骂{NICKNAME}就对了!
|
||||
""".strip()
|
||||
max_width = 0
|
||||
for m in text.split("\n"):
|
||||
max_width = len(m) * 20 if len(m) * 20 > max_width else max_width
|
||||
max_height = len(text.split("\n")) * 24
|
||||
A = BuildImage(
|
||||
max_width, max_height, font="CJGaoDeGuo.otf", font_size=24, color="#E3DBD1"
|
||||
)
|
||||
A.text((10, 10), text)
|
||||
await show_punish.send(image(b64=A.pic2bs4()))
|
||||
|
||||
|
||||
@set_punish.handle()
|
||||
async def _(event: MessageEvent, arg: Message = CommandArg()):
|
||||
msg = arg.extract_plain_text().strip().split()
|
||||
if (
|
||||
len(msg) < 3
|
||||
or not is_number(msg[0])
|
||||
or not is_number(msg[1])
|
||||
or not is_number(msg[2])
|
||||
):
|
||||
await set_punish.finish("参数错误,请查看帮助...", at_sender=True)
|
||||
uid = int(msg[0])
|
||||
id_ = int(msg[1])
|
||||
punish_level = int(msg[2])
|
||||
print(uid, id_, punish_level)
|
||||
rst = await set_user_punish(uid, id_, punish_level)
|
||||
await set_punish.send(rst)
|
||||
logger.info(
|
||||
f"USER {event.user_id} 设置惩罚 uid:{uid} id_:{id_} punish_level:{punish_level} --> {rst}"
|
||||
)
|
||||
118
plugins/black_word/data_source.py
Normal file
118
plugins/black_word/data_source.py
Normal file
@ -0,0 +1,118 @@
|
||||
from nonebot.adapters.onebot.v11 import Bot
|
||||
from utils.image_utils import BuildImage, text2image
|
||||
from services.log import logger
|
||||
from typing import Optional
|
||||
from datetime import datetime
|
||||
from .model import BlackWord
|
||||
from .utils import _get_punish, Config
|
||||
|
||||
|
||||
async def show_black_text_image(
|
||||
bot: Bot,
|
||||
user: Optional[int],
|
||||
group_id: Optional[int],
|
||||
date: Optional[datetime],
|
||||
data_type: str = "=",
|
||||
) -> BuildImage:
|
||||
"""
|
||||
展示记录名单
|
||||
:param bot: bot
|
||||
:param user: 用户qq
|
||||
:param group_id: 群聊
|
||||
:param date: 日期
|
||||
:param data_type: 日期搜索类型
|
||||
:return:
|
||||
"""
|
||||
data = await BlackWord.get_black_data(user, group_id, date, data_type)
|
||||
A = BuildImage(0, 0, color="#f9f6f2", font_size=20)
|
||||
image_list = []
|
||||
friend_str = await bot.get_friend_list()
|
||||
id_str = ""
|
||||
uname_str = ""
|
||||
uid_str = ""
|
||||
gid_str = ""
|
||||
plant_text_str = ""
|
||||
black_word_str = ""
|
||||
punish_str = ""
|
||||
punish_level_str = ""
|
||||
create_time_str = ""
|
||||
for i, x in enumerate(data):
|
||||
try:
|
||||
if x.group_id:
|
||||
user_name = (
|
||||
await bot.get_group_member_info(
|
||||
group_id=x.group_id, user_id=x.user_qq
|
||||
)
|
||||
)["card"]
|
||||
else:
|
||||
user_name = [
|
||||
u["nickname"] for u in friend_str if u["user_id"] == x.user_qq
|
||||
][0]
|
||||
except Exception as e:
|
||||
logger.warning(
|
||||
f"show_black_text_image 获取 USER {x.user_qq} user_name 失败 {type(e)}:{e}"
|
||||
)
|
||||
user_name = x.user_qq
|
||||
id_str += f"{i}\n"
|
||||
uname_str += f"{user_name}\n"
|
||||
uid_str += f"{x.user_qq}\n"
|
||||
gid_str += f"{x.group_id}\n"
|
||||
plant_text = " ".join(x.plant_text.split("\n"))
|
||||
if A.getsize(plant_text)[0] > 200:
|
||||
plant_text = plant_text[:20] + "..."
|
||||
plant_text_str += f"{plant_text}\n"
|
||||
black_word_str += f"{x.black_word}\n"
|
||||
punish_str += f"{x.punish}\n"
|
||||
punish_level_str += f"{x.punish_level}\n"
|
||||
create_time_str += f"{x.create_time.replace(microsecond=0)}\n"
|
||||
_tmp_img = BuildImage(0, 0, font_size=35, font="CJGaoDeGuo.otf")
|
||||
for s, type_ in [
|
||||
(id_str, "Id"),
|
||||
(uname_str, "昵称"),
|
||||
(uid_str, "UID"),
|
||||
(gid_str, "GID"),
|
||||
(plant_text_str, "文本"),
|
||||
(black_word_str, "检测"),
|
||||
(punish_str, "惩罚"),
|
||||
(punish_level_str, "等级"),
|
||||
(create_time_str, "记录日期"),
|
||||
]:
|
||||
img = await text2image(s, color="#f9f6f2", _add_height=3.32)
|
||||
w = _tmp_img.getsize(type_)[0] if _tmp_img.getsize(type_)[0] > img.w else img.w
|
||||
A = BuildImage(w + 11, img.h + 50, color="#f9f6f2", font_size=35, font="CJGaoDeGuo.otf")
|
||||
await A.atext((10, 10), type_)
|
||||
await A.apaste(img, (0, 50))
|
||||
image_list.append(A)
|
||||
horizontal_line = []
|
||||
w, h = 0, 0
|
||||
for img in image_list:
|
||||
w += img.w + 20
|
||||
h = img.h if img.h > h else h
|
||||
horizontal_line.append(img.w)
|
||||
A = BuildImage(w, h, color="#f9f6f2")
|
||||
current_w = 0
|
||||
for img in image_list:
|
||||
await A.apaste(img, (current_w, 0))
|
||||
current_w += img.w + 20
|
||||
return A
|
||||
|
||||
|
||||
async def set_user_punish(user_id: int, id_: int, punish_level: int) -> str:
|
||||
"""
|
||||
设置惩罚
|
||||
:param user_id: 用户id
|
||||
:param id_: 记录下标
|
||||
:param punish_level: 惩罚等级
|
||||
"""
|
||||
result = await _get_punish(punish_level, user_id)
|
||||
punish = {
|
||||
1: "永久ban",
|
||||
2: "删除好友",
|
||||
3: f"ban {result} 天",
|
||||
4: f"ban {result} 分钟",
|
||||
5: "口头警告"
|
||||
}
|
||||
if await BlackWord.set_user_punish(user_id, punish[punish_level], id_=id_):
|
||||
return f"已对 USER {user_id} 进行 {punish[punish_level]} 处罚。"
|
||||
else:
|
||||
return "操作失败,可能未找到用户,id或敏感词"
|
||||
149
plugins/black_word/model.py
Normal file
149
plugins/black_word/model.py
Normal file
@ -0,0 +1,149 @@
|
||||
from services.db_context import db
|
||||
from typing import Optional, List
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
|
||||
class BlackWord(db.Model):
|
||||
__tablename__ = "black_word"
|
||||
|
||||
id = db.Column(db.Integer(), primary_key=True, autoincrement=True)
|
||||
user_qq = db.Column(db.BigInteger(), nullable=False, primary_key=True)
|
||||
group_id = db.Column(db.BigInteger())
|
||||
plant_text = db.Column(db.String())
|
||||
black_word = db.Column(db.String())
|
||||
punish = db.Column(db.String(), default="")
|
||||
punish_level = db.Column(db.Integer())
|
||||
create_time = db.Column(db.DateTime(timezone=True), nullable=False)
|
||||
|
||||
@classmethod
|
||||
async def add_user_black_word(
|
||||
cls,
|
||||
user_qq: int,
|
||||
group_id: Optional[int],
|
||||
black_word: str,
|
||||
plant_text: str,
|
||||
punish_level: int,
|
||||
):
|
||||
"""
|
||||
说明:
|
||||
添加用户发送的敏感词
|
||||
参数:
|
||||
:param user_qq: 用户id
|
||||
:param group_id: 群号
|
||||
:param black_word: 黑名单词汇
|
||||
:param plant_text: 消息文本
|
||||
:param punish_level: 惩罚等级
|
||||
"""
|
||||
await cls.create(
|
||||
user_qq=user_qq,
|
||||
group_id=group_id,
|
||||
plant_text=plant_text,
|
||||
black_word=black_word,
|
||||
punish_level=punish_level,
|
||||
create_time=datetime.now(),
|
||||
)
|
||||
|
||||
@classmethod
|
||||
async def set_user_punish(
|
||||
cls,
|
||||
user_qq: int,
|
||||
punish: str,
|
||||
black_word: Optional[str] = None,
|
||||
id_: Optional[int] = None,
|
||||
) -> bool:
|
||||
"""
|
||||
说明:
|
||||
设置处罚
|
||||
参数:
|
||||
:param user_qq: 用户id
|
||||
:param punish: 处罚
|
||||
:param black_word: 黑名单词汇
|
||||
:param id_: 记录下标
|
||||
"""
|
||||
user = None
|
||||
if (not black_word and not id_) or not punish:
|
||||
return False
|
||||
query = cls.query.where(cls.user_qq == user_qq).with_for_update()
|
||||
if black_word:
|
||||
user = await query.where(cls.black_word == black_word).order_by(cls.id.desc()).gino.first()
|
||||
elif id_:
|
||||
user_list = await query.gino.all()
|
||||
print(len(user_list))
|
||||
if len(user_list) == 0 or (id_ < 0 or id_ > len(user_list)):
|
||||
return False
|
||||
user = user_list[id_]
|
||||
if not user:
|
||||
return False
|
||||
await user.update(punish=cls.punish + punish + " ").apply()
|
||||
return True
|
||||
|
||||
@classmethod
|
||||
async def get_user_count(
|
||||
cls, user_qq: int, days: int = 7, punish_level: Optional[int] = None
|
||||
) -> int:
|
||||
"""
|
||||
说明:
|
||||
获取用户规定周期内的犯事次数
|
||||
参数:
|
||||
:param user_qq: 用户qq
|
||||
:param days: 周期天数
|
||||
:param punish_level: 惩罚等级
|
||||
"""
|
||||
setattr(BlackWord, "count", db.func.count(cls.id).label("count"))
|
||||
query = cls.select("count").where(
|
||||
(cls.user_qq == user_qq)
|
||||
& (cls.punish_level != -1)
|
||||
& (cls.create_time > datetime.now() - timedelta(days=days))
|
||||
)
|
||||
if punish_level is not None:
|
||||
query = query.where(cls.punish_level == punish_level)
|
||||
return (await query.gino.first())[0]
|
||||
|
||||
@classmethod
|
||||
async def get_user_punish_level(cls, user_qq: int, days: int = 7) -> Optional[int]:
|
||||
"""
|
||||
说明:
|
||||
获取用户最近一次的惩罚记录等级
|
||||
参数:
|
||||
:param user_qq: 用户qq
|
||||
:param days: 周期天数
|
||||
"""
|
||||
if (
|
||||
query := await cls.query.where(cls.user_qq == user_qq)
|
||||
.where(cls.create_time > datetime.now() - timedelta(days=days))
|
||||
.order_by(cls.id.desc())
|
||||
.gino.first()
|
||||
):
|
||||
return query.punish_level
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
async def get_black_data(
|
||||
cls,
|
||||
user_qq: Optional[int],
|
||||
group_id: Optional[int],
|
||||
date: Optional[datetime],
|
||||
date_type: str = "=",
|
||||
) -> List["BlackWord"]:
|
||||
"""
|
||||
说明:
|
||||
通过指定条件查询数据
|
||||
参数:
|
||||
:param user_qq: 用户qq
|
||||
:param group_id: 群号
|
||||
:param date: 日期
|
||||
:param date_type: 日期查询类型
|
||||
"""
|
||||
query = cls.query
|
||||
if user_qq:
|
||||
query = query.where(cls.user_qq == user_qq)
|
||||
if group_id:
|
||||
query = query.where(cls.group_id == group_id)
|
||||
if date:
|
||||
if date_type == "=":
|
||||
query = query.where(cls.create_time == date)
|
||||
elif date_type == ">":
|
||||
query = query.where(cls.create_time > date)
|
||||
elif date_type == "<":
|
||||
query = query.where(cls.create_time < date)
|
||||
return await query.gino.all()
|
||||
324
plugins/black_word/utils.py
Normal file
324
plugins/black_word/utils.py
Normal file
@ -0,0 +1,324 @@
|
||||
from utils.utils import cn2py, get_bot
|
||||
from configs.path_config import DATA_PATH
|
||||
from typing import Optional, Union, Tuple
|
||||
from .model import BlackWord
|
||||
from configs.config import Config
|
||||
from pathlib import Path
|
||||
from services.log import logger
|
||||
from models.ban_user import BanUser
|
||||
from nonebot.adapters.onebot.v11.exception import ActionFailed
|
||||
from models.group_member_info import GroupInfoUser
|
||||
from utils.http_utils import AsyncHttpx
|
||||
import random
|
||||
|
||||
try:
|
||||
import ujson as json
|
||||
except ModuleNotFoundError:
|
||||
import json
|
||||
|
||||
|
||||
class BlackWordManager:
|
||||
|
||||
"""
|
||||
敏感词管理( 拒绝恶意
|
||||
"""
|
||||
|
||||
def __init__(self, word_file: Path, py_file: Path):
|
||||
self._word_list = {
|
||||
"1": [],
|
||||
"2": [],
|
||||
"3": [],
|
||||
"4": ["sb", "nmsl", "mdzz", "2b", "jb", "操", "废物", "憨憨", "cnm", "rnm"],
|
||||
"5": [],
|
||||
}
|
||||
self._py_list = {
|
||||
"1": [],
|
||||
"2": [],
|
||||
"3": [],
|
||||
"4": [
|
||||
"shabi",
|
||||
"wocaonima",
|
||||
"sima",
|
||||
"sabi",
|
||||
"zhizhang",
|
||||
"naocan",
|
||||
"caonima",
|
||||
"rinima",
|
||||
"simadongxi",
|
||||
"simawanyi",
|
||||
"hanbi",
|
||||
"hanpi",
|
||||
"laji",
|
||||
"fw"
|
||||
],
|
||||
"5": [],
|
||||
}
|
||||
word_file.parent.mkdir(parents=True, exist_ok=True)
|
||||
if word_file.exists():
|
||||
# 清空默认配置
|
||||
with open(word_file, "r", encoding="utf8") as f:
|
||||
self._word_list = json.load(f)
|
||||
else:
|
||||
with open(word_file, "w", encoding="utf8") as f:
|
||||
json.dump(
|
||||
self._word_list,
|
||||
f,
|
||||
ensure_ascii=False,
|
||||
indent=4,
|
||||
)
|
||||
if py_file.exists():
|
||||
# 清空默认配置
|
||||
with open(py_file, "r", encoding="utf8") as f:
|
||||
self._py_list = json.load(f)
|
||||
else:
|
||||
with open(py_file, "w", encoding="utf8") as f:
|
||||
json.dump(
|
||||
self._py_list,
|
||||
f,
|
||||
ensure_ascii=False,
|
||||
indent=4,
|
||||
)
|
||||
|
||||
async def check(
|
||||
self, user_id: int, group_id: Optional[int], message: str
|
||||
) -> Optional[Union[str, bool]]:
|
||||
"""
|
||||
检查是否包含黑名单词汇
|
||||
:param user_id: 用户id
|
||||
:param group_id: 群号
|
||||
:param message: 消息
|
||||
"""
|
||||
print(user_id, group_id, message)
|
||||
if data := self._check(message):
|
||||
print(data)
|
||||
if data[0]:
|
||||
await _add_user_black_word(
|
||||
user_id, group_id, data[0], message, int(data[1])
|
||||
)
|
||||
return True
|
||||
if Config.get_config(
|
||||
"black_word", "ALAPI_CHECK_FLAG"
|
||||
) and not await check_text(message):
|
||||
await send_msg(
|
||||
0, None, f"USER {user_id} GROUP {group_id} ALAPI 疑似检测:{message}"
|
||||
)
|
||||
return False
|
||||
|
||||
def _check(self, message: str) -> Tuple[Optional[str], int]:
|
||||
"""
|
||||
检测文本是否违规
|
||||
:param message: 检测消息
|
||||
"""
|
||||
# 移除空格
|
||||
message = message.replace(" ", "")
|
||||
py_msg = cn2py(message).lower()
|
||||
# 完全匹配
|
||||
for x in [self._word_list, self._py_list]:
|
||||
for level in x:
|
||||
if message in x[level] or py_msg in x[level]:
|
||||
return message if message in x[level] else py_msg, level
|
||||
# 模糊匹配
|
||||
for x in [self._word_list, self._py_list]:
|
||||
for level in x:
|
||||
for m in x[level]:
|
||||
if m in message or m in py_msg:
|
||||
return m, -1
|
||||
return None, 0
|
||||
|
||||
|
||||
async def _add_user_black_word(
|
||||
user_id: int,
|
||||
group_id: Optional[int],
|
||||
black_word: str,
|
||||
message: str,
|
||||
punish_level: int,
|
||||
):
|
||||
"""
|
||||
添加敏感词数据
|
||||
:param user_id: 用户id
|
||||
:param group_id: 群号
|
||||
:param black_word: 触发的黑名单词汇
|
||||
:param message: 原始文本
|
||||
:param punish_level: 惩罚等级
|
||||
"""
|
||||
cycle_days = Config.get_config("black_word", "CYCLE_DAYS") or 7
|
||||
user_count = await BlackWord.get_user_count(user_id, cycle_days, punish_level)
|
||||
# 周期内超过次数直接提升惩罚
|
||||
if Config.get_config(
|
||||
"black_word", "AUTO_ADD_PUNISH_LEVEL"
|
||||
) and user_count > Config.get_config("black_word", "ADD_PUNISH_LEVEL_TO_COUNT"):
|
||||
punish_level -= 1
|
||||
await BlackWord.add_user_black_word(
|
||||
user_id, group_id, black_word, message, punish_level
|
||||
)
|
||||
logger.info(
|
||||
f"已将 USER {user_id} GROUP {group_id} 添加至黑名单词汇记录 Black_word:{black_word} Plant_text:{message}"
|
||||
)
|
||||
# 自动惩罚
|
||||
if Config.get_config("black_word", "AUTO_PUNISH") and punish_level != -1:
|
||||
await _punish_handle(user_id, group_id, punish_level, black_word)
|
||||
|
||||
|
||||
async def _punish_handle(
|
||||
user_id: int, group_id: Optional[int], punish_level: int, black_word: str
|
||||
):
|
||||
"""
|
||||
惩罚措施,级别越低惩罚越严
|
||||
:param user_id: 用户id
|
||||
:param group_id: 群号
|
||||
:param black_word: 触发的黑名单词汇
|
||||
"""
|
||||
logger.info(f"BlackWord USER {user_id} 触发 {punish_level} 级惩罚...")
|
||||
# 周期天数
|
||||
cycle_days = Config.get_config("black_word", "CYCLE_DAYS") or 7
|
||||
# 用户周期内触发punish_level级惩罚的次数
|
||||
user_count = await BlackWord.get_user_count(user_id, cycle_days, punish_level)
|
||||
# 获取最近一次的惩罚等级,将在此基础上增加
|
||||
punish_level = await BlackWord.get_user_punish_level(user_id, cycle_days) or punish_level
|
||||
# 容忍次数:List[int]
|
||||
tolerate_count = Config.get_config("black_word", "TOLERATE_COUNT")
|
||||
if not tolerate_count or len(tolerate_count) < 5:
|
||||
tolerate_count = [5, 2, 2, 2, 2]
|
||||
if punish_level == 1 and user_count > tolerate_count[punish_level - 1]:
|
||||
# 永久ban
|
||||
await _get_punish(1, user_id, group_id)
|
||||
await BlackWord.set_user_punish(user_id, "永久ban 删除好友", black_word)
|
||||
elif punish_level == 2 and user_count > tolerate_count[punish_level - 1]:
|
||||
# 删除好友
|
||||
await _get_punish(2, user_id, group_id)
|
||||
await BlackWord.set_user_punish(user_id, "删除好友", black_word)
|
||||
elif punish_level == 3 and user_count > tolerate_count[punish_level - 1]:
|
||||
# 永久ban
|
||||
ban_day = await _get_punish(3, user_id, group_id)
|
||||
await BlackWord.set_user_punish(user_id, f"ban {ban_day} 天", black_word)
|
||||
elif punish_level == 4 and user_count > tolerate_count[punish_level - 1]:
|
||||
# ban指定时长
|
||||
ban_time = await _get_punish(4, user_id, group_id)
|
||||
await BlackWord.set_user_punish(user_id, f"ban {ban_time} 分钟", black_word)
|
||||
elif punish_level == 5 and user_count > tolerate_count[punish_level - 1]:
|
||||
# 口头警告
|
||||
warning_result = await _get_punish(5, user_id, group_id)
|
||||
await BlackWord.set_user_punish(user_id, f"口头警告:{warning_result}", black_word)
|
||||
else:
|
||||
await BlackWord.set_user_punish(user_id, f"提示!", black_word)
|
||||
await send_msg(
|
||||
user_id,
|
||||
group_id,
|
||||
f"BlackWordChecker:该条发言已被记录,目前你在{cycle_days}天内的发表{punish_level}级"
|
||||
f"言论记录次数为:{user_count}次,请注意你的发言\n"
|
||||
f"* 如果你不清楚惩罚机制,请发送“惩罚机制” *",
|
||||
)
|
||||
|
||||
|
||||
async def _get_punish(
|
||||
id_: int, user_id: int, group_id: Optional[int] = None
|
||||
) -> Optional[Union[int, str]]:
|
||||
"""
|
||||
通过id_获取惩罚
|
||||
:param id_: id
|
||||
:param user_id: 用户id
|
||||
:param group_id: 群号
|
||||
"""
|
||||
bot = get_bot()
|
||||
# 忽略的群聊
|
||||
# _ignore_group = Config.get_config("black_word", "IGNORE_GROUP")
|
||||
# 处罚 id 4 ban 时间:int,List[int]
|
||||
ban_3_duration = Config.get_config("black_word", "BAN_3_DURATION")
|
||||
# 处罚 id 4 ban 时间:int,List[int]
|
||||
ban_4_duration = Config.get_config("black_word", "BAN_4_DURATION")
|
||||
# 口头警告内容
|
||||
warning_result = Config.get_config("black_word", "WARNING_RESULT")
|
||||
try:
|
||||
uname = (await GroupInfoUser.get_member_info(user_id, group_id)).user_name
|
||||
except AttributeError:
|
||||
uname = user_id
|
||||
# 永久ban
|
||||
if id_ == 1:
|
||||
if str(user_id) not in bot.config.superusers:
|
||||
await BanUser.ban(user_id, 10, 99999999)
|
||||
await send_msg(user_id, group_id, f"BlackWordChecker 永久ban USER {uname}({user_id})")
|
||||
logger.info(f"BlackWord 永久封禁 USER {user_id}...")
|
||||
# 删除好友(有的话
|
||||
elif id_ == 2:
|
||||
if str(user_id) not in bot.config.superusers:
|
||||
try:
|
||||
await bot.delete_friend(user_id=user_id)
|
||||
await send_msg(
|
||||
user_id, group_id, f"BlackWordChecker 删除好友 USER {uname}({user_id})"
|
||||
)
|
||||
logger.info(f"BlackWord 删除好友 {user_id}...")
|
||||
except ActionFailed:
|
||||
pass
|
||||
# 封禁用户指定时间,默认7天
|
||||
elif id_ == 3:
|
||||
if isinstance(ban_3_duration, list):
|
||||
ban_3_duration = random.randint(ban_3_duration[0], ban_3_duration[1])
|
||||
await BanUser.ban(user_id, 9, ban_4_duration * 60 * 60 * 24)
|
||||
await send_msg(
|
||||
user_id,
|
||||
group_id,
|
||||
f"BlackWordChecker 对用户 USER {uname}({user_id}) 进行封禁 {ban_3_duration} 天处罚。",
|
||||
)
|
||||
logger.info(f"BlackWord 封禁 USER {uname}({user_id}) {ban_3_duration} 天...")
|
||||
return ban_3_duration
|
||||
# 封禁用户指定时间,默认360分钟
|
||||
elif id_ == 4:
|
||||
if isinstance(ban_4_duration, list):
|
||||
ban_4_duration = random.randint(ban_4_duration[0], ban_4_duration[1])
|
||||
await BanUser.ban(user_id, 9, ban_4_duration * 60)
|
||||
await send_msg(
|
||||
user_id,
|
||||
group_id,
|
||||
f"BlackWordChecker 对用户 USER {uname}({user_id}) 进行封禁 {ban_4_duration} 分钟处罚。",
|
||||
)
|
||||
logger.info(f"BlackWord 封禁 USER {uname}({user_id}) {ban_4_duration} 分钟...")
|
||||
return ban_4_duration
|
||||
# 口头警告
|
||||
elif id_ == 5:
|
||||
if group_id:
|
||||
await bot.send_group_msg(group_id=group_id, message=warning_result)
|
||||
else:
|
||||
await bot.send_private_msg(user_id=user_id, message=warning_result)
|
||||
logger.info(f"BlackWord 口头警告 USER {user_id}")
|
||||
return warning_result
|
||||
return None
|
||||
|
||||
|
||||
async def send_msg(user_id: int, group_id: Optional[int], message: str):
|
||||
"""
|
||||
发送消息
|
||||
:param user_id: user_id
|
||||
:param group_id: group_id
|
||||
:param message: message
|
||||
"""
|
||||
bot = get_bot()
|
||||
if not user_id:
|
||||
user_id = int(list(bot.config.superusers)[0])
|
||||
if group_id:
|
||||
await bot.send_group_msg(group_id=group_id, message=message)
|
||||
else:
|
||||
await bot.send_private_msg(user_id=user_id, message=message)
|
||||
|
||||
|
||||
async def check_text(text: str) -> bool:
|
||||
"""
|
||||
ALAPI文本检测,检测输入违规
|
||||
:param text: 回复
|
||||
"""
|
||||
if not Config.get_config("alapi", "ALAPI_TOKEN"):
|
||||
return True
|
||||
params = {"token": Config.get_config("alapi", "ALAPI_TOKEN"), "text": text}
|
||||
try:
|
||||
data = (
|
||||
await AsyncHttpx.get(
|
||||
"https://v2.alapi.cn/api/censor/text", timeout=4, params=params
|
||||
)
|
||||
).json()
|
||||
if data["code"] == 200:
|
||||
return data["data"]["conclusion_type"] == 2
|
||||
except Exception as e:
|
||||
logger.error(f"检测违规文本错误...{type(e)}:{e}")
|
||||
return True
|
||||
|
||||
|
||||
black_word_manager = BlackWordManager(DATA_PATH / "black_word" / "black_word.json", DATA_PATH / "black_word" / "black_py.json")
|
||||
@ -1,34 +0,0 @@
|
||||
from nonebot import on_message
|
||||
from nonebot.adapters.onebot.v11.permission import GROUP
|
||||
from nonebot.adapters.onebot.v11 import GroupMessageEvent
|
||||
from .data_source import cancel_all_notice, save_data, get_data, set_data_value
|
||||
from services.log import logger
|
||||
import time
|
||||
|
||||
|
||||
__zx_plugin_name__ = "群聊最后聊天时间记录 [Hidden]"
|
||||
__plugin_version__ = 0.1
|
||||
__plugin_author__ = "HibiKier"
|
||||
|
||||
|
||||
last_chat = on_message(priority=1, block=False, permission=GROUP)
|
||||
|
||||
|
||||
@last_chat.handle()
|
||||
async def _(event: GroupMessageEvent):
|
||||
time_data = await get_data()
|
||||
set_data_value(event.group_id, time.time())
|
||||
if event.group_id in time_data["_group"]:
|
||||
time_data["_group"].remove(event.group_id)
|
||||
set_data_value("_group", time_data["_group"])
|
||||
for key in time_data.keys():
|
||||
if key not in ["check_time", "_group"]:
|
||||
if key not in time_data["_group"]:
|
||||
if time.time() - time_data[key] > 60 * 60 * 36:
|
||||
await cancel_all_notice(key)
|
||||
time_data["_group"].append(key)
|
||||
set_data_value("_group", time_data["_group"])
|
||||
logger.info(f"GROUP {event.group_id} 因群内发言时间大于36小时被取消全部通知")
|
||||
if time.time() - time_data["check_time"] > 60 * 60 * 1:
|
||||
set_data_value("check_time", time.time())
|
||||
save_data()
|
||||
@ -1,67 +0,0 @@
|
||||
from configs.path_config import DATA_PATH
|
||||
from utils.utils import get_bot
|
||||
from datetime import datetime
|
||||
import time
|
||||
from services.log import logger
|
||||
from utils.manager import group_manager
|
||||
try:
|
||||
import ujson as json
|
||||
except ModuleNotFoundError:
|
||||
import json
|
||||
|
||||
|
||||
time_data = {}
|
||||
|
||||
|
||||
async def init():
|
||||
global time_data
|
||||
bot = get_bot()
|
||||
gl = await bot.get_group_list()
|
||||
gl = [g["group_id"] for g in gl]
|
||||
data = read_data("group_last_chat_time.json")
|
||||
for g in gl:
|
||||
if not data.get(g):
|
||||
time_data[g] = time.time()
|
||||
if not time_data.get("check_time"):
|
||||
time_data["check_time"] = time.time()
|
||||
if not time_data.get("_group"):
|
||||
time_data["_group"] = []
|
||||
save_data()
|
||||
return time_data
|
||||
|
||||
|
||||
def read_data(file_name: str):
|
||||
try:
|
||||
with open(DATA_PATH / file_name, "r", encoding="utf8") as f:
|
||||
return json.load(f)
|
||||
except (ValueError, FileNotFoundError):
|
||||
return {}
|
||||
|
||||
|
||||
def save_data():
|
||||
with open(DATA_PATH / "group_last_chat_time.json", "w") as f:
|
||||
json.dump(time_data, f, indent=4)
|
||||
logger.info(
|
||||
f'自动存储 group_last_chat_time.json 时间:{str(datetime.now()).split(".")[0]}'
|
||||
)
|
||||
|
||||
|
||||
# 取消全部通知
|
||||
async def cancel_all_notice(group_id):
|
||||
group_id = int(group_id)
|
||||
for command in group_manager.get_task_data():
|
||||
if await group_manager.check_group_task_status(group_id, command):
|
||||
await group_manager.close_group_task(group_id, command)
|
||||
logger.info(f"关闭了 {group_id} 群的全部通知")
|
||||
|
||||
|
||||
async def get_data():
|
||||
global time_data
|
||||
if not time_data:
|
||||
time_data = await init()
|
||||
return time_data
|
||||
|
||||
|
||||
def set_data_value(key, value):
|
||||
global time_data
|
||||
time_data[key] = value
|
||||
0
utils/depends/__init__.py
Normal file
0
utils/depends/__init__.py
Normal file
@ -1336,6 +1336,7 @@ async def text2image(
|
||||
font: str = "CJGaoDeGuo.otf",
|
||||
font_color: Union[str, Tuple[int, int, int]] = "black",
|
||||
padding: Union[int, Tuple[int, int, int, int]] = 0,
|
||||
_add_height: float = 0,
|
||||
) -> BuildImage:
|
||||
"""
|
||||
说明:
|
||||
@ -1358,6 +1359,7 @@ async def text2image(
|
||||
:param font: 普通字体
|
||||
:param font_color: 普通字体颜色
|
||||
:param padding: 文本外边距,元组类型时为 (上,左,下,右)
|
||||
:param _add_height: 由于get_size无法返回正确的高度,采用手动方式额外添加高度
|
||||
"""
|
||||
pw = ph = top_padding = left_padding = 0
|
||||
if padding:
|
||||
@ -1489,17 +1491,17 @@ async def text2image(
|
||||
else:
|
||||
width = 0
|
||||
height = 0
|
||||
_tmp = BuildImage(0, 0, font_size=font_size)
|
||||
_tmp = BuildImage(0, 0, font=font, font_size=font_size)
|
||||
for x in text.split("\n"):
|
||||
x = x if x.strip() else "正"
|
||||
w, h = _tmp.getsize(x)
|
||||
height += h
|
||||
height += h + _add_height
|
||||
width = width if width > w else w
|
||||
width += pw
|
||||
height += ph
|
||||
A = BuildImage(
|
||||
width + left_padding,
|
||||
height + top_padding,
|
||||
height + top_padding + 2,
|
||||
font_size=font_size,
|
||||
color=color,
|
||||
font=font,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user