From 50c04f36251c02c13591cfe9caa4b207dcab12d3 Mon Sep 17 00:00:00 2001 From: webjoin111 <455457521@qq.com> Date: Sat, 15 Nov 2025 12:43:16 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20feat(tag):=20=E5=AE=9E=E7=8E=B0?= =?UTF-8?q?=E7=BE=A4=E7=BB=84=E6=A0=87=E7=AD=BE=E8=87=AA=E5=8A=A8=E6=B8=85?= =?UTF-8?q?=E7=90=86=E5=8F=8A=E6=89=8B=E5=8A=A8=E6=B8=85=E7=90=86=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../platform/qq/group_handle/__init__.py | 7 +++ .../scheduler/auto_update_group.py | 16 +++++++ .../builtin_plugins/superuser/tag_manage.py | 15 +++++++ zhenxun/services/tags/manager.py | 44 +++++++++++++++++++ 4 files changed, 82 insertions(+) diff --git a/zhenxun/builtin_plugins/platform/qq/group_handle/__init__.py b/zhenxun/builtin_plugins/platform/qq/group_handle/__init__.py index ff8a1158..4ddcc47e 100644 --- a/zhenxun/builtin_plugins/platform/qq/group_handle/__init__.py +++ b/zhenxun/builtin_plugins/platform/qq/group_handle/__init__.py @@ -17,6 +17,8 @@ from zhenxun.configs.utils import PluginExtraData, RegisterConfig, Task from zhenxun.models.event_log import EventLog from zhenxun.models.group_console import GroupConsole from zhenxun.services.cache import CacheRoot +from zhenxun.services.log import logger +from zhenxun.services.tags import tag_manager from zhenxun.utils.common_utils import CommonUtils from zhenxun.utils.enum import EventLogType, PluginType from zhenxun.utils.platform import PlatformUtils @@ -135,6 +137,11 @@ async def _( await EventLog.create( user_id=user_id, group_id=group_id, event_type=EventLogType.KICK_BOT ) + await tag_manager.remove_group_from_all_tags(group_id) + logger.info( + f"机器人被移出群聊,已自动从所有静态标签中移除群组 {group_id}", + "群组标签管理", + ) elif event.sub_type in ["leave", "kick"]: if event.sub_type == "leave": """主动退群""" diff --git a/zhenxun/builtin_plugins/scheduler/auto_update_group.py b/zhenxun/builtin_plugins/scheduler/auto_update_group.py index dda1f3e3..465fce79 100644 --- a/zhenxun/builtin_plugins/scheduler/auto_update_group.py +++ b/zhenxun/builtin_plugins/scheduler/auto_update_group.py @@ -2,6 +2,7 @@ import nonebot from nonebot_plugin_apscheduler import scheduler from zhenxun.services.log import logger +from zhenxun.services.tags import tag_manager from zhenxun.utils.platform import PlatformUtils @@ -37,3 +38,18 @@ async def _(): f"Bot: {bot.self_id} 自动更新好友信息错误", "自动更新好友", e=e ) logger.info("自动更新好友信息成功...") + + +# 自动清理静态标签中的无效群组 +@scheduler.scheduled_job( + "cron", + hour=23, + minute=30, +) +async def _prune_stale_tags(): + deleted_count = await tag_manager.prune_stale_group_links() + if deleted_count > 0: + logger.info(f"定时任务:成功清理了 {deleted_count} 个无效的群组标签" + f"关联。", "群组标签管理") + else: + logger.debug("定时任务:未发现无效的群组标签关联。", "群组标签管理") diff --git a/zhenxun/builtin_plugins/superuser/tag_manage.py b/zhenxun/builtin_plugins/superuser/tag_manage.py index 5a0c056b..dfbbdb77 100644 --- a/zhenxun/builtin_plugins/superuser/tag_manage.py +++ b/zhenxun/builtin_plugins/superuser/tag_manage.py @@ -176,6 +176,7 @@ tag_cmd = on_alconna( help_text="删除标签", ), Subcommand("clear", help_text="清空所有标签"), + Subcommand("prune", alias=["check", "清理"], help_text="清理无效的群组关联"), Subcommand( "clone", Args["source_name", str]["new_name", str], @@ -192,6 +193,13 @@ tag_cmd = on_alconna( block=True, ) +tag_cmd.shortcut( + "清理标签", + command="tag", + arguments=["prune"], + prefix=True, +) + @tag_cmd.assign("list") async def handle_list(): @@ -466,3 +474,10 @@ async def handle_clone( await MessageUtils.build_message(msg).finish() except (ValueError, IntegrityError) as e: await MessageUtils.build_message(f"克隆失败: {e}").finish() + + +@tag_cmd.assign("prune") +async def handle_prune(): + deleted_count = await tag_manager.prune_stale_group_links() + msg = f"清理完成!共移除了 {deleted_count} 个无效的群组关联。" + await MessageUtils.build_message(msg).finish() diff --git a/zhenxun/services/tags/manager.py b/zhenxun/services/tags/manager.py index e26d6b7e..79147a21 100644 --- a/zhenxun/services/tags/manager.py +++ b/zhenxun/services/tags/manager.py @@ -9,6 +9,7 @@ from typing import Any, ClassVar from aiocache import Cache, cached from arclet.alconna import Alconna, Args +import nonebot from nonebot.adapters import Bot from tortoise.exceptions import IntegrityError from tortoise.expressions import Q @@ -176,6 +177,49 @@ class TagManager: deleted_count = await GroupTag.filter(name=name).delete() return deleted_count > 0 + @invalidate_on_change + async def remove_group_from_all_tags(self, group_id: str) -> int: + """ + 从所有静态标签中移除一个指定的群组ID。 + 主要用于机器人退群时的实时清理。 + + 参数: + group_id: 要移除的群组ID。 + + 返回: + 被删除的关联数量。 + """ + deleted_count = await GroupTagLink.filter(group_id=group_id).delete() + if deleted_count > 0: + logger.info(f"已从 {deleted_count} 个标签中移除群组 {group_id} 的关联。") + return deleted_count + + @invalidate_on_change + async def prune_stale_group_links(self) -> int: + """ + 清理所有静态标签中无效的群组关联。 + 无效指的是机器人已不再任何一个已连接的Bot的群组列表中。 + + 返回: + 被清理的无效关联的总数。 + """ + all_bot_group_ids = set() + for bot in nonebot.get_bots().values(): + groups, _ = await PlatformUtils.get_group_list(bot) + all_bot_group_ids.update(g.group_id for g in groups if g.group_id) + + all_static_links = await GroupTagLink.filter(tag__tag_type="STATIC").all() + + stale_link_ids = [ + link.id + for link in all_static_links + if link.group_id not in all_bot_group_ids + ] + + if stale_link_ids: + return await GroupTagLink.filter(id__in=stale_link_ids).delete() + return 0 + @invalidate_on_change async def add_groups_to_tag(self, name: str, group_ids: list[str]) -> int: # type: ignore """