diff --git a/bot.py b/bot.py index 9dc5e509b..4cdb54ede 100644 --- a/bot.py +++ b/bot.py @@ -249,7 +249,7 @@ class ShutdownManager: return success except Exception as e: - logger.error(f"麦麦关闭失败: {e}") + logger.error(f"麦麦关闭失败: {e}", exc_info=True) return False @@ -602,12 +602,11 @@ class MaiBotMain: async def wait_for_user_input(): """等待用户输入(异步方式)""" try: - # 在非生产环境下,使用异步方式等待输入 if os.getenv("ENVIRONMENT") != "production": logger.info("程序执行完成,按 Ctrl+C 退出...") - # 使用 Event 替代 sleep 循环,避免阻塞事件循环 - shutdown_event = asyncio.Event() - await shutdown_event.wait() + # 使用非阻塞循环 + while True: + await asyncio.sleep(0.1) except KeyboardInterrupt: logger.info("用户中断程序") return True @@ -616,6 +615,7 @@ async def wait_for_user_input(): return False + async def main_async(): """主异步函数""" exit_code = 0 diff --git a/src/plugins/built_in/napcat_adapter/README.md b/src/plugins/built_in/napcat_adapter/README.md index b13bf78a4..30bbc5386 100644 --- a/src/plugins/built_in/napcat_adapter/README.md +++ b/src/plugins/built_in/napcat_adapter/README.md @@ -54,6 +54,100 @@ NEW_napcat_adapter/ 2. **启动插件**: 插件自动在系统启动时加载 3. **WebSocket连接**: 自动连接到 Napcat OneBot 11 服务器 +### 黑白名单过滤系统 + +本适配器内置了完整的黑白名单过滤系统,支持群聊和私聊消息的精细控制。 + +#### 🛡️ 过滤功能说明 + +1. **群聊过滤** + - **黑名单模式** (`blacklist`): 屏蔽指定群聊的消息 + - **白名单模式** (`whitelist`): 只接收指定群聊的消息 + +2. **私聊过滤** + - **黑名单模式** (`blacklist`): 屏蔽指定用户的私聊消息 + - **白名单模式** (`whitelist`): 只接收指定用户的私聊消息 + +3. **全局封禁** + - **用户封禁列表**: 无论在群聊还是私聊中都会被过滤 + - **机器人过滤**: 自动屏蔽其他QQ机器人的消息 + +#### 📝 配置示例 + +```toml +[features] +# 群聊配置:只允许特定群聊的消息 +group_list_type = "whitelist" +group_list = ["123456789", "987654321"] + +# 私聊配置:屏蔽特定用户的消息 +private_list_type = "blacklist" +private_list = ["111111111", "222222222"] + +# 全局封禁:这些用户的所有消息都会被过滤 +ban_user_id = ["333333333", "444444444"] + +# 屏蔽其他QQ机器人 +ban_qq_bot = true +``` + +#### 🎯 常见使用场景 + +1. **个人机器人**(只服务特定群组和用户): + ```toml + group_list_type = "whitelist" + private_list_type = "whitelist" + ``` + +2. **群管机器人**(屏蔽捣乱用户): + ```toml + group_list_type = "blacklist" + ban_user_id = ["捣乱用户1", "捣乱用户2"] + ``` + +3. **公开服务机器人**(无限制接收所有消息): + ```toml + # 保持默认的 blacklist 模式,名单为空即可 + group_list_type = "blacklist" + private_list_type = "blacklist" + ``` + +#### 📋 配置参数详解 + +| 参数 | 类型 | 默认值 | 说明 | +|------|------|--------|------| +| `group_list_type` | string | "blacklist" | 群聊过滤模式:"blacklist" 或 "whitelist" | +| `group_list` | array | [] | 群聊QQ号列表 | +| `private_list_type` | string | "blacklist" | 私聊过滤模式:"blacklist" 或 "whitelist" | +| `private_list` | array | [] | 用户QQ号列表 | +| `ban_user_id` | array | [] | 全局封禁的用户QQ号列表 | +| `ban_qq_bot` | boolean | false | 是否屏蔽其他QQ机器人消息 | + +#### ⚡ 过滤逻辑流程 + +1. **消息接收** → 检查全局封禁列表 +2. **通过** → 检查是否为机器人消息(如果启用 ban_qq_bot) +3. **通过** → 根据消息类型应用对应过滤规则: + - **群聊消息**: 应用群聊黑白名单规则 + - **私聊消息**: 应用私聊黑白名单规则 +4. **通过所有过滤** → 正常处理消息 +5. **任一过滤失败** → 丢弃消息,记录调试日志 + +#### 🔧 配置文件位置 + +- **示例配置**: `src/plugins/built_in/napcat_adapter/config.example.toml` +- **实际配置**: `config/plugins/NEW_napcat_adapter.toml` + +#### 📊 日志信息 + +被过滤的消息会在调试日志中记录: +``` +[DEBUG] napcat_adapter: 群聊 123456789 在黑名单中,消息被过滤 +[DEBUG] napcat_adapter: 私聊用户 111111111 在黑名单中,消息被过滤 +[DEBUG] napcat_adapter: 用户 333333333 在全局封禁列表中,消息被过滤 +[DEBUG] napcat_adapter: 检测到机器人消息 555555555,消息被过滤 +``` + ## 🔑 核心数据结构 ### MessageEnvelope (mofox-wire v2.x) diff --git a/src/plugins/built_in/napcat_adapter/src/handlers/to_core/message_handler.py b/src/plugins/built_in/napcat_adapter/src/handlers/to_core/message_handler.py index f3cd281d4..9fe87f68a 100644 --- a/src/plugins/built_in/napcat_adapter/src/handlers/to_core/message_handler.py +++ b/src/plugins/built_in/napcat_adapter/src/handlers/to_core/message_handler.py @@ -39,6 +39,79 @@ class MessageHandler: """设置插件配置""" self.plugin_config = config + def _should_process_message(self, raw: Dict[str, Any]) -> bool: + """ + 检查消息是否应该被处理(黑白名单过滤) + + Args: + raw: OneBot 原始消息数据 + + Returns: + bool: True表示应该处理,False表示应该过滤 + """ + if not self.plugin_config: + return True # 如果没有配置,默认处理所有消息 + + features_config = self.plugin_config.get("features", {}) + + # 获取消息基本信息 + message_type = raw.get("message_type") + sender_info = raw.get("sender", {}) + user_id = str(sender_info.get("user_id", "")) + + # 检查全局封禁用户列表 + ban_user_ids = features_config.get("ban_user_id", []) + if user_id in ban_user_ids: + logger.debug(f"用户 {user_id} 在全局封禁列表中,消息被过滤") + return False + + # 检查是否屏蔽其他QQ机器人 + if features_config.get("ban_qq_bot", False): + # 判断是否为机器人消息:通常通过sender中的role字段或其他标识 + role = sender_info.get("role", "") + if role == "admin" or "bot" in str(sender_info).lower(): + logger.debug(f"检测到机器人消息 {user_id},消息被过滤") + return False + + # 群聊消息处理 + if message_type == "group": + group_id = str(raw.get("group_id", "")) + + # 获取群聊配置 + group_list_type = features_config.get("group_list_type", "blacklist") + group_list = features_config.get("group_list", []) + + if group_list_type == "blacklist": + # 黑名单模式:如果在黑名单中就过滤 + if group_id in group_list: + logger.debug(f"群聊 {group_id} 在黑名单中,消息被过滤") + return False + else: # whitelist + # 白名单模式:如果不在白名单中就过滤 + if group_id not in group_list: + logger.debug(f"群聊 {group_id} 不在白名单中,消息被过滤") + return False + + # 私聊消息处理 + elif message_type == "private": + # 获取私聊配置 + private_list_type = features_config.get("private_list_type", "blacklist") + private_list = features_config.get("private_list", []) + + if private_list_type == "blacklist": + # 黑名单模式:如果在黑名单中就过滤 + if user_id in private_list: + logger.debug(f"私聊用户 {user_id} 在黑名单中,消息被过滤") + return False + else: # whitelist + # 白名单模式:如果不在白名单中就过滤 + if user_id not in private_list: + logger.debug(f"私聊用户 {user_id} 不在白名单中,消息被过滤") + return False + + # 通过所有过滤条件 + return True + async def handle_raw_message(self, raw: Dict[str, Any]): """ 处理原始消息并转换为 MessageEnvelope @@ -47,13 +120,18 @@ class MessageHandler: raw: OneBot 原始消息数据 Returns: - MessageEnvelope (dict) + MessageEnvelope (dict) or None (if message is filtered) """ message_type = raw.get("message_type") message_id = str(raw.get("message_id", "")) message_time = time.time() + # 黑白名单过滤 + if not self._should_process_message(raw): + logger.debug(f"消息被黑白名单过滤丢弃: message_id={message_id}") + return None + msg_builder = MessageBuilder() # 构造用户信息