From 59d72c3b3d204d7c7b32f9901dfb34a11f9bbbd5 Mon Sep 17 00:00:00 2001 From: molanp <104612722+molanp@users.noreply.github.com> Date: Tue, 29 Jul 2025 17:22:27 +0800 Subject: [PATCH] =?UTF-8?q?feat(admin):=20=E5=A2=9E=E5=8A=A0=E5=B0=81?= =?UTF-8?q?=E7=A6=81=E7=94=A8=E6=88=B7=E7=90=86=E7=94=B1=E5=B9=B6=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E7=9B=B8=E5=85=B3=E9=80=BB=E8=BE=91=20(#1992)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(admin): 增加封禁用户理由并优化相关逻辑 - 在 ban 命令中添加了 -r 或 --reason 选项,用于指定封禁理由 - 优化了 ban 命令的参数解析和处理逻辑 - 更新了数据库模型,增加了 ban_reason 字段用于存储封禁理由 - 修复了部分逻辑错误,如永久封禁的处理方式 * :rotating_light: auto fix by pre-commit hooks * refactor(ban): 优化 ban 命令和相关功能 - 修复 ban 命令中的 reason 参数可选标记 - 完善恶意触发检测和用户昵称违规的禁言信息 - 统一禁言操作的参数顺序,提高代码可读性 --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- zhenxun/builtin_plugins/admin/ban/__init__.py | 27 ++++++++++++++----- .../builtin_plugins/admin/ban/_data_source.py | 24 ++++++++++++----- .../builtin_plugins/hooks/auth/auth_ban.py | 9 ++++--- zhenxun/builtin_plugins/hooks/chkdsk_hook.py | 7 ++++- zhenxun/builtin_plugins/nickname.py | 4 ++- zhenxun/models/ban_console.py | 9 ++++++- 6 files changed, 61 insertions(+), 19 deletions(-) diff --git a/zhenxun/builtin_plugins/admin/ban/__init__.py b/zhenxun/builtin_plugins/admin/ban/__init__.py index 7c356bcc..9a8e2dd2 100644 --- a/zhenxun/builtin_plugins/admin/ban/__init__.py +++ b/zhenxun/builtin_plugins/admin/ban/__init__.py @@ -36,11 +36,12 @@ __plugin_meta__ = PluginMetadata( usage=""" 普通管理员 格式: - ban [At用户] ?[-t [时长(分钟)]] + ban [At用户] ?[-t [时长(分钟)]] ?[-r [理由]] 示例: ban @用户 : 永久拉黑用户 ban @用户 -t 100 : 拉黑用户100分钟 + ban @用户 -t 10 -r 坏 : 拉黑用户10分钟并携带理由 unban @用户 : 从小黑屋中拉出来 """.strip(), extra=PluginExtraData( @@ -50,7 +51,7 @@ __plugin_meta__ = PluginMetadata( superuser_help=""" 超级管理员额外命令 格式: - ban [At用户/用户Id] ?[-t [时长]] + ban [At用户/用户Id] ?[-t [时长]] ?[-r [理由]] unban --id [idx] : 通过id来进行unban操作 ban列表: 获取所有Ban数据 @@ -66,10 +67,13 @@ __plugin_meta__ = PluginMetadata( 私聊下: 示例: ban 123456789 : 永久拉黑用户123456789 + ban 123456789 -r 坏 : 永久拉黑用户123456789并携带理由 ban 123456789 -t 100 : 拉黑用户123456789 100分钟 ban -g 999999 : 拉黑群组为999999的群组 ban -g 999999 -t 100 : 拉黑群组为999999的群组 100分钟 + ban -g 999999 -r 坏 : 永久拉黑群组为999999的群组并携带理由 + unban 123456789 : 从小黑屋中拉出来 unban -g 999999 : 将群组9999999从小黑屋中拉出来 @@ -94,6 +98,9 @@ __plugin_meta__ = PluginMetadata( "user_id": AICallableProperties( type="string", description="用户的id" ), + "reason": AICallableProperties( + type="string", description="封禁理由" + ), "duration": AICallableProperties( type="integer", description="封禁时长(选择的值只能是1-360),单位为分钟,如果频繁触发,按情况增加", @@ -112,6 +119,7 @@ _ban_matcher = on_alconna( Alconna( "ban", Args["user?", [str, At]], + Option("-r|--reason", Args["reason", str]), Option("-g|--group", Args["group_id", str]), Option("-t|--time", Args["duration", int]), ), @@ -185,6 +193,7 @@ async def _( session: EventSession, arparma: Arparma, user: Match[str | At], + reason: Match[str], duration: Match[int], group_id: Match[str], ): @@ -200,13 +209,14 @@ async def _( user_id = user.result _duration = duration.result * 60 if duration.available else -1 _duration_text = f"{duration.result} 分钟" if duration.available else " 到世界湮灭" + ban_reason = reason.result if reason.available else None if (gid := session.id3 or session.id2) and not group_id.available: if not user_id or ( user_id == bot.self_id and session.id1 not in bot.config.superusers ): _duration = 0.5 await MessageUtils.build_message("倒反天罡,小小管理速速退下!").send() - await BanManage.ban(session.id1, gid, 30, session, True) + await BanManage.ban(session.id1, gid, ban_reason, 30, session, True) _duration_text = "半 分钟" logger.info( f"尝试ban {BotConfig.self_nickname} 反被拿下", @@ -222,7 +232,12 @@ async def _( ] ).finish(reply_to=True) await BanManage.ban( - user_id, gid, _duration, session, session.id1 in bot.config.superusers + user_id, + gid, + ban_reason, + _duration, + session, + session.id1 in bot.config.superusers, ) logger.info( "管理员Ban", @@ -244,7 +259,7 @@ async def _( ).finish(reply_to=True) elif session.id1 in bot.config.superusers: _group_id = group_id.result if group_id.available else None - await BanManage.ban(user_id, _group_id, _duration, session, True) + await BanManage.ban(user_id, _group_id, ban_reason, _duration, session, True) logger.info( "超级用户Ban", arparma.header_result, @@ -296,7 +311,7 @@ async def _( At(flag="user", target=user_id) if isinstance(user.result, At) else result - ), # type: ignore + ), " 从黑屋中拉了出来并急救了一下!", ] ).finish(reply_to=True) diff --git a/zhenxun/builtin_plugins/admin/ban/_data_source.py b/zhenxun/builtin_plugins/admin/ban/_data_source.py index 5b67191b..1f2d50e3 100644 --- a/zhenxun/builtin_plugins/admin/ban/_data_source.py +++ b/zhenxun/builtin_plugins/admin/ban/_data_source.py @@ -9,13 +9,13 @@ from zhenxun.services.log import logger from zhenxun.utils.image_utils import BuildImage, ImageTemplate -async def call_ban(user_id: str, duration: int = 1): +async def call_ban(user_id: str, reason: str | None = None, duration: int = 1): """调用ban 参数: user_id: 用户id """ - await BanConsole.ban(user_id, None, 9, duration * 60) + await BanConsole.ban(user_id, None, 9, reason, duration * 60) logger.info("被讨厌了,已将用户加入黑名单...", "ban", session=user_id) @@ -55,20 +55,23 @@ class BanManage: "用户ID", "群组ID", "BAN LEVEL", + "封禁原因", "剩余时长(分钟)", "操作员ID", ] row_data = [] for data in data_list: - duration = int((data.ban_time + data.duration - time.time()) / 60) - if data.duration < 0: + if data.duration == -1: duration = "∞" + else: + duration = int((data.ban_time + data.duration - time.time()) / 60) row_data.append( [ data.id, data.user_id, data.group_id, data.ban_level, + data.ban_reason, duration, data.operator, ] @@ -120,10 +123,15 @@ class BanManage: if ban_data.ban_level > user_level: return False, "unBan权限等级不足捏..." await ban_data.delete() - return True, str(ban_data.user_id or ban_data.group_id) + return ( + True, + f"用户 {ban_data.user_id}" + if ban_data.user_id + else f"群组 {ban_data.group_id}", + ) elif await BanConsole.check_ban_level(user_id, group_id, user_level): await BanConsole.unban(user_id, group_id) - return True, str(group_id) + return True, f"群组 {group_id}" return False, "该用户/群组不在黑名单中不足捏..." @classmethod @@ -131,6 +139,7 @@ class BanManage: cls, user_id: str | None, group_id: str | None, + reason: str | None, duration: int, session: EventSession, is_superuser: bool, @@ -140,6 +149,7 @@ class BanManage: 参数: user_id: 用户id group_id: 群组id + reason: 理由 duration: 时长,秒 session: Session is_superuser: 是否为超级用户操作 @@ -147,4 +157,4 @@ class BanManage: level = 9999 if not is_superuser and user_id and session.id1: level = await LevelUser.get_user_level(session.id1, group_id) - await BanConsole.ban(user_id, group_id, level, duration, session.id1) + await BanConsole.ban(user_id, group_id, level, reason, duration, session.id1) diff --git a/zhenxun/builtin_plugins/hooks/auth/auth_ban.py b/zhenxun/builtin_plugins/hooks/auth/auth_ban.py index 76b19370..b7663090 100644 --- a/zhenxun/builtin_plugins/hooks/auth/auth_ban.py +++ b/zhenxun/builtin_plugins/hooks/auth/auth_ban.py @@ -27,7 +27,7 @@ Config.add_plugin_config( ) -def calculate_ban_time(ban_record: BanConsole | None) -> int: +async def calculate_ban_time(ban_record: BanConsole | None) -> int: """根据ban记录计算剩余ban时间 参数: @@ -43,7 +43,10 @@ def calculate_ban_time(ban_record: BanConsole | None) -> int: return -1 _time = time.time() - (ban_record.ban_time + ban_record.duration) - return 0 if _time > 0 else int(abs(_time)) + if _time < 0: + return int(abs(_time)) + await ban_record.delete() + return 0 async def is_ban(user_id: str | None, group_id: str | None) -> int: @@ -113,7 +116,7 @@ async def is_ban(user_id: str | None, group_id: str | None) -> int: for result in results: if result.duration > 0 or result.duration == -1: # 直接计算ban时间,避免再次查询数据库 - ban_time = calculate_ban_time(result) + ban_time = await calculate_ban_time(result) if ban_time == -1 or ban_time > max_ban_time: max_ban_time = ban_time diff --git a/zhenxun/builtin_plugins/hooks/chkdsk_hook.py b/zhenxun/builtin_plugins/hooks/chkdsk_hook.py index ec5ccfed..30080281 100644 --- a/zhenxun/builtin_plugins/hooks/chkdsk_hook.py +++ b/zhenxun/builtin_plugins/hooks/chkdsk_hook.py @@ -92,7 +92,12 @@ async def _( if module: if _blmt.check(f"{user_id}__{module}"): await BanConsole.ban( - user_id, group_id, 9, malicious_ban_time * 60, bot.self_id + user_id, + group_id, + 9, + "恶意触发命令检测", + malicious_ban_time * 60, + bot.self_id, ) logger.info( f"触发了恶意触发检测: {matcher.plugin_name}", diff --git a/zhenxun/builtin_plugins/nickname.py b/zhenxun/builtin_plugins/nickname.py index 5cbc519e..ce3f9be8 100644 --- a/zhenxun/builtin_plugins/nickname.py +++ b/zhenxun/builtin_plugins/nickname.py @@ -275,7 +275,9 @@ async def _(bot: Bot, session: Uninfo): await GroupInfoUser.set_user_nickname(session.user.id, group_id, "") else: await FriendUser.set_user_nickname(session.user.id, "") - await BanConsole.ban(session.user.id, group_id, 9, 60, bot.self_id) + await BanConsole.ban( + session.user.id, group_id, 9, "用户昵称违规", 60, bot.self_id + ) return else: await MessageUtils.build_message("你在做梦吗?你没有昵称啊").finish( diff --git a/zhenxun/models/ban_console.py b/zhenxun/models/ban_console.py index a6f5c3bb..83b02cc3 100644 --- a/zhenxun/models/ban_console.py +++ b/zhenxun/models/ban_console.py @@ -21,6 +21,8 @@ class BanConsole(Model): """使用ban命令的用户等级""" ban_time = fields.BigIntField() """ban开始的时间""" + ban_reason = fields.TextField(null=True, default=None) + """ban的理由""" duration = fields.BigIntField() """ban时长""" operator = fields.CharField(255) @@ -107,7 +109,9 @@ class BanConsole(Model): if user.duration == -1: return -1 _time = time.time() - (user.ban_time + user.duration) - return 0 if _time > 0 else int(time.time() - user.ban_time - user.duration) + if _time < 0: + return int(time.time() - user.ban_time - user.duration) + await user.delete() return 0 @classmethod @@ -133,6 +137,7 @@ class BanConsole(Model): user_id: str | None, group_id: str | None, ban_level: int, + reason: str | None, duration: int, operator: str | None = None, ): @@ -157,6 +162,7 @@ class BanConsole(Model): group_id=group_id, ban_level=ban_level, ban_time=int(time.time()), + ban_reason=reason, duration=duration, operator=operator or 0, ) @@ -206,4 +212,5 @@ class BanConsole(Model): return [ "CREATE INDEX idx_ban_console_user_id ON ban_console(user_id);", "CREATE INDEX idx_ban_console_group_id ON ban_console(group_id);", + "ALTER TABLE ban_console ADD COLUMN ban_reason TEXT DEFAULT NULL;", ]