diff --git a/src/plugins/built_in/napcat_adapter_plugin/plugin.py b/src/plugins/built_in/napcat_adapter_plugin/plugin.py index 4f7c1ee8e..39082d0f3 100644 --- a/src/plugins/built_in/napcat_adapter_plugin/plugin.py +++ b/src/plugins/built_in/napcat_adapter_plugin/plugin.py @@ -18,7 +18,6 @@ from .src.recv_handler.meta_event_handler import meta_event_handler from .src.recv_handler.notice_handler import notice_handler from .src.recv_handler.message_sending import message_send_instance from .src.send_handler import send_handler -from .src.config.migrate_features import auto_migrate_features from .src.mmc_com_layer import mmc_start_com, router, mmc_stop_com from .src.response_pool import put_response, check_timeout_response from .src.websocket_manager import websocket_manager @@ -221,16 +220,10 @@ class LauchNapcatAdapterHandler(BaseEventHandler): init_subscribe = [EventType.ON_START] async def execute(self, kwargs): - # 执行功能配置迁移(如果需要) - logger.info("检查功能配置迁移...") - auto_migrate_features() - # 启动消息重组器的清理任务 logger.info("启动消息重组器...") await reassembler.start_cleanup_task() - # 功能管理器已迁移到插件系统配置 - logger.info("功能配置已迁移到插件系统") logger.info("开始启动Napcat Adapter") message_send_instance.maibot_router = router # 创建单独的异步任务,防止阻塞主线程 @@ -270,7 +263,7 @@ class NapcatAdapterPlugin(BasePlugin): "plugin": { "name": ConfigField(type=str, default="napcat_adapter_plugin", description="插件名称"), "version": ConfigField(type=str, default="1.0.0", description="插件版本"), - "config_version": ConfigField(type=str, default="1.2.0", description="配置文件版本"), + "config_version": ConfigField(type=str, default="1.3.0", description="配置文件版本"), "enabled": ConfigField(type=bool, default=False, description="是否启用插件"), }, "inner": { @@ -301,6 +294,37 @@ class NapcatAdapterPlugin(BasePlugin): }, "debug": { "level": ConfigField(type=str, default="INFO", description="日志等级(DEBUG, INFO, WARNING, ERROR, CRITICAL)", choices=["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]), + }, + "features": { + # 权限设置 + "group_list_type": ConfigField(type=str, default="blacklist", description="群聊列表类型:whitelist(白名单)或 blacklist(黑名单)", choices=["whitelist", "blacklist"]), + "group_list": ConfigField(type=list, default=[], description="群聊ID列表"), + "private_list_type": ConfigField(type=str, default="blacklist", description="私聊列表类型:whitelist(白名单)或 blacklist(黑名单)", choices=["whitelist", "blacklist"]), + "private_list": ConfigField(type=list, default=[], description="用户ID列表"), + "ban_user_id": ConfigField(type=list, default=[], description="全局禁止用户ID列表,这些用户无法在任何地方使用机器人"), + "ban_qq_bot": ConfigField(type=bool, default=False, description="是否屏蔽QQ官方机器人消息"), + + # 聊天功能设置 + "enable_poke": ConfigField(type=bool, default=True, description="是否启用戳一戳功能"), + "ignore_non_self_poke": ConfigField(type=bool, default=False, description="是否无视不是针对自己的戳一戳"), + "poke_debounce_seconds": ConfigField(type=int, default=3, description="戳一戳防抖时间(秒),在指定时间内第二次针对机器人的戳一戳将被忽略"), + "enable_reply_at": ConfigField(type=bool, default=True, description="是否启用引用回复时艾特用户的功能"), + "reply_at_rate": ConfigField(type=float, default=0.5, description="引用回复时艾特用户的几率 (0.0 ~ 1.0)"), + + # 视频处理设置 + "enable_video_analysis": ConfigField(type=bool, default=True, description="是否启用视频识别功能"), + "max_video_size_mb": ConfigField(type=int, default=100, description="视频文件最大大小限制(MB)"), + "download_timeout": ConfigField(type=int, default=60, description="视频下载超时时间(秒)"), + "supported_formats": ConfigField(type=list, default=["mp4", "avi", "mov", "mkv", "flv", "wmv", "webm"], description="支持的视频格式"), + + # 消息缓冲设置 + "enable_message_buffer": ConfigField(type=bool, default=True, description="是否启用消息缓冲合并功能"), + "message_buffer_enable_group": ConfigField(type=bool, default=True, description="是否启用群聊消息缓冲合并"), + "message_buffer_enable_private": ConfigField(type=bool, default=True, description="是否启用私聊消息缓冲合并"), + "message_buffer_interval": ConfigField(type=float, default=3.0, description="消息合并间隔时间(秒),在此时间内的连续消息将被合并"), + "message_buffer_initial_delay": ConfigField(type=float, default=0.5, description="消息缓冲初始延迟(秒),收到第一条消息后等待此时间开始合并"), + "message_buffer_max_components": ConfigField(type=int, default=50, description="单个会话最大缓冲消息组件数量,超过此数量将强制合并"), + "message_buffer_block_prefixes": ConfigField(type=list, default=["/", "!", "!", ".", "。", "#", "%"], description="消息缓冲屏蔽前缀,以这些前缀开头的消息不会被缓冲"), } } @@ -313,7 +337,8 @@ class NapcatAdapterPlugin(BasePlugin): "maibot_server": "连接麦麦的ws服务设置", "voice": "发送语音设置", "slicing": "WebSocket消息切片设置", - "debug": "调试设置" + "debug": "调试设置", + "features": "功能设置(权限控制、聊天功能、视频处理、消息缓冲等)" } def register_events(self): diff --git a/src/plugins/built_in/napcat_adapter_plugin/src/config/__init__.py b/src/plugins/built_in/napcat_adapter_plugin/src/config/__init__.py deleted file mode 100644 index 99c6f490c..000000000 --- a/src/plugins/built_in/napcat_adapter_plugin/src/config/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -# 配置已迁移到插件系统,此文件不再需要 -# 所有配置访问应通过插件系统的 config_api 进行 diff --git a/src/plugins/built_in/napcat_adapter_plugin/src/config/config_base.py b/src/plugins/built_in/napcat_adapter_plugin/src/config/config_base.py deleted file mode 100644 index 87cb079d2..000000000 --- a/src/plugins/built_in/napcat_adapter_plugin/src/config/config_base.py +++ /dev/null @@ -1,136 +0,0 @@ -from dataclasses import dataclass, fields, MISSING -from typing import TypeVar, Type, Any, get_origin, get_args, Literal, Dict, Union - -T = TypeVar("T", bound="ConfigBase") - -TOML_DICT_TYPE = { - int, - float, - str, - bool, - list, - dict, -} - - -@dataclass -class ConfigBase: - """配置类的基类""" - - @classmethod - def from_dict(cls: Type[T], data: Dict[str, Any]) -> T: - """从字典加载配置字段""" - if not isinstance(data, dict): - raise TypeError(f"Expected a dictionary, got {type(data).__name__}") - - init_args: Dict[str, Any] = {} - - for f in fields(cls): - field_name = f.name - field_type = f.type - if field_name.startswith("_"): - # 跳过以 _ 开头的字段 - continue - - if field_name not in data: - if f.default is not MISSING or f.default_factory is not MISSING: - # 跳过未提供且有默认值/默认构造方法的字段 - continue - else: - raise ValueError(f"Missing required field: '{field_name}'") - - value = data[field_name] - try: - init_args[field_name] = cls._convert_field(value, field_type) - except TypeError as e: - raise TypeError(f"字段 '{field_name}' 出现类型错误: {e}") from e - except Exception as e: - raise RuntimeError(f"无法将字段 '{field_name}' 转换为目标类型,出现错误: {e}") from e - - return cls(**init_args) - - @classmethod - def _convert_field(cls, value: Any, field_type: Type[Any]) -> Any: - """ - 转换字段值为指定类型 - - 1. 对于嵌套的 dataclass,递归调用相应的 from_dict 方法 - 2. 对于泛型集合类型(list, set, tuple),递归转换每个元素 - 3. 对于基础类型(int, str, float, bool),直接转换 - 4. 对于其他类型,尝试直接转换,如果失败则抛出异常 - """ - # 如果是嵌套的 dataclass,递归调用 from_dict 方法 - if isinstance(field_type, type) and issubclass(field_type, ConfigBase): - return field_type.from_dict(value) - - field_origin_type = get_origin(field_type) - field_args_type = get_args(field_type) - - # 处理泛型集合类型(list, set, tuple) - if field_origin_type in {list, set, tuple}: - # 检查提供的value是否为list - if not isinstance(value, list): - raise TypeError(f"Expected an list for {field_type.__name__}, got {type(value).__name__}") - - if field_origin_type is list: - return [cls._convert_field(item, field_args_type[0]) for item in value] - if field_origin_type is set: - return {cls._convert_field(item, field_args_type[0]) for item in value} - if field_origin_type is tuple: - # 检查提供的value长度是否与类型参数一致 - if len(value) != len(field_args_type): - raise TypeError( - f"Expected {len(field_args_type)} items for {field_type.__name__}, got {len(value)}" - ) - return tuple(cls._convert_field(item, arg_type) for item, arg_type in zip(value, field_args_type)) - - if field_origin_type is dict: - # 检查提供的value是否为dict - if not isinstance(value, dict): - raise TypeError(f"Expected a dictionary for {field_type.__name__}, got {type(value).__name__}") - - # 检查字典的键值类型 - if len(field_args_type) != 2: - raise TypeError(f"Expected a dictionary with two type arguments for {field_type.__name__}") - key_type, value_type = field_args_type - - return {cls._convert_field(k, key_type): cls._convert_field(v, value_type) for k, v in value.items()} - - # 处理Optional类型 - if field_origin_type is Union: # assert get_origin(Optional[Any]) is Union - if value is None: - return None - # 如果有数据,检查实际类型 - if type(value) not in field_args_type: - raise TypeError(f"Expected {field_args_type} for {field_type.__name__}, got {type(value).__name__}") - return cls._convert_field(value, field_args_type[0]) - - # 处理int, str, float, bool等基础类型 - if field_origin_type is None: - if isinstance(value, field_type): - return field_type(value) - else: - raise TypeError(f"Expected {field_type.__name__}, got {type(value).__name__}") - - # 处理Literal类型 - if field_origin_type is Literal: - # 获取Literal的允许值 - allowed_values = get_args(field_type) - if value in allowed_values: - return value - else: - raise TypeError(f"Value '{value}' is not in allowed values {allowed_values} for Literal type") - - # 处理其他类型 - if field_type is Any: - return value - - # 其他类型直接转换 - try: - return field_type(value) - except (ValueError, TypeError) as e: - raise TypeError(f"无法将 {type(value).__name__} 转换为 {field_type.__name__}") from e - - def __str__(self): - """返回配置类的字符串表示""" - return f"{self.__class__.__name__}({', '.join(f'{f.name}={getattr(self, f.name)}' for f in fields(self))})" diff --git a/src/plugins/built_in/napcat_adapter_plugin/src/config/config_utils.py b/src/plugins/built_in/napcat_adapter_plugin/src/config/config_utils.py deleted file mode 100644 index a275b3078..000000000 --- a/src/plugins/built_in/napcat_adapter_plugin/src/config/config_utils.py +++ /dev/null @@ -1,145 +0,0 @@ -""" -配置文件工具模块 -提供统一的配置文件生成和管理功能 -""" - -import os -import shutil -from pathlib import Path -from datetime import datetime -from typing import Optional - -from src.common.logger import get_logger - -logger = get_logger("napcat_adapter") - - -def ensure_config_directories(): - """确保配置目录存在""" - os.makedirs("config", exist_ok=True) - os.makedirs("config/old", exist_ok=True) - - -def create_config_from_template( - config_path: str, template_path: str, config_name: str = "配置文件", should_exit: bool = True -) -> bool: - """ - 从模板创建配置文件的统一函数 - - Args: - config_path: 配置文件路径 - template_path: 模板文件路径 - config_name: 配置文件名称(用于日志显示) - should_exit: 创建后是否退出程序 - - Returns: - bool: 是否成功创建配置文件 - """ - try: - # 确保配置目录存在 - ensure_config_directories() - - config_path_obj = Path(config_path) - template_path_obj = Path(template_path) - - # 检查配置文件是否存在 - if config_path_obj.exists(): - return False # 配置文件已存在,无需创建 - - logger.info(f"{config_name}不存在,从模板创建新配置") - - # 检查模板文件是否存在 - if not template_path_obj.exists(): - logger.error(f"模板文件不存在: {template_path}") - if should_exit: - logger.critical("无法创建配置文件,程序退出") - quit(1) - return False - - # 确保配置文件目录存在 - config_path_obj.parent.mkdir(parents=True, exist_ok=True) - - # 复制模板文件到配置目录 - shutil.copy2(template_path_obj, config_path_obj) - logger.info(f"已创建新{config_name}: {config_path}") - - if should_exit: - logger.info("程序将退出,请检查配置文件后重启") - quit(0) - - return True - - except Exception as e: - logger.error(f"创建{config_name}失败: {e}") - if should_exit: - logger.critical("无法创建配置文件,程序退出") - quit(1) - return False - - -def create_default_config_dict(default_values: dict, config_path: str, config_name: str = "配置文件") -> bool: - """ - 创建默认配置文件(使用字典数据) - - Args: - default_values: 默认配置值字典 - config_path: 配置文件路径 - config_name: 配置文件名称(用于日志显示) - - Returns: - bool: 是否成功创建配置文件 - """ - try: - import tomlkit - - config_path_obj = Path(config_path) - - # 确保配置文件目录存在 - config_path_obj.parent.mkdir(parents=True, exist_ok=True) - - # 写入默认配置 - with open(config_path_obj, "w", encoding="utf-8") as f: - tomlkit.dump(default_values, f) - - logger.info(f"已创建默认{config_name}: {config_path}") - return True - - except Exception as e: - logger.error(f"创建默认{config_name}失败: {e}") - return False - - -def backup_config_file(config_path: str, backup_dir: str = "config/old") -> Optional[str]: - """ - 备份配置文件 - - Args: - config_path: 要备份的配置文件路径 - backup_dir: 备份目录 - - Returns: - Optional[str]: 备份文件路径,失败时返回None - """ - try: - config_path_obj = Path(config_path) - if not config_path_obj.exists(): - return None - - # 确保备份目录存在 - backup_dir_obj = Path(backup_dir) - backup_dir_obj.mkdir(parents=True, exist_ok=True) - - # 创建备份文件名 - timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") - backup_filename = f"{config_path_obj.stem}.toml.bak.{timestamp}" - backup_path = backup_dir_obj / backup_filename - - # 备份文件 - shutil.copy2(config_path_obj, backup_path) - logger.info(f"已备份配置文件到: {backup_path}") - - return str(backup_path) - - except Exception as e: - logger.error(f"备份配置文件失败: {e}") - return None diff --git a/src/plugins/built_in/napcat_adapter_plugin/src/config/migrate_features.py b/src/plugins/built_in/napcat_adapter_plugin/src/config/migrate_features.py deleted file mode 100644 index e721029c0..000000000 --- a/src/plugins/built_in/napcat_adapter_plugin/src/config/migrate_features.py +++ /dev/null @@ -1,215 +0,0 @@ -""" -功能配置迁移脚本 -用于将旧的配置文件中的聊天、权限、视频处理等设置迁移到新的独立功能配置文件 -""" - -import os -import shutil -from pathlib import Path -import tomlkit -from src.common.logger import get_logger - -logger = get_logger("napcat_adapter") - - -def migrate_features_from_config( - old_config_path: str = "plugins/napcat_adapter_plugin/config/config.toml", - new_features_path: str = "plugins/napcat_adapter_plugin/config/features.toml", - template_path: str = "plugins/napcat_adapter_plugin/template/features_template.toml", -): - """ - 从旧配置文件迁移功能设置到新的功能配置文件 - - Args: - old_config_path: 旧配置文件路径 - new_features_path: 新功能配置文件路径 - template_path: 功能配置模板路径 - """ - try: - # 检查旧配置文件是否存在 - if not os.path.exists(old_config_path): - logger.warning(f"旧配置文件不存在: {old_config_path}") - return False - - # 读取旧配置文件 - with open(old_config_path, "r", encoding="utf-8") as f: - old_config = tomlkit.load(f) - - # 检查是否有chat配置段和video配置段 - chat_config = old_config.get("chat", {}) - video_config = old_config.get("video", {}) - - # 检查是否有权限相关配置 - permission_keys = [ - "group_list_type", - "group_list", - "private_list_type", - "private_list", - "ban_user_id", - "ban_qq_bot", - "enable_poke", - "ignore_non_self_poke", - "poke_debounce_seconds", - ] - video_keys = ["enable_video_analysis", "max_video_size_mb", "download_timeout", "supported_formats"] - - has_permission_config = any(key in chat_config for key in permission_keys) - has_video_config = any(key in video_config for key in video_keys) - - if not has_permission_config and not has_video_config: - logger.info("旧配置文件中没有找到功能相关配置,无需迁移") - return False - - # 确保新功能配置目录存在 - new_features_dir = Path(new_features_path).parent - new_features_dir.mkdir(parents=True, exist_ok=True) - - # 如果新功能配置文件已存在,先备份 - if os.path.exists(new_features_path): - backup_path = f"{new_features_path}.backup" - shutil.copy2(new_features_path, backup_path) - logger.info(f"已备份现有功能配置文件到: {backup_path}") - - # 创建新的功能配置 - new_features_config = { - "group_list_type": chat_config.get("group_list_type", "whitelist"), - "group_list": chat_config.get("group_list", []), - "private_list_type": chat_config.get("private_list_type", "whitelist"), - "private_list": chat_config.get("private_list", []), - "ban_user_id": chat_config.get("ban_user_id", []), - "ban_qq_bot": chat_config.get("ban_qq_bot", False), - "enable_poke": chat_config.get("enable_poke", True), - "ignore_non_self_poke": chat_config.get("ignore_non_self_poke", False), - "poke_debounce_seconds": chat_config.get("poke_debounce_seconds", 3), - "enable_video_analysis": video_config.get("enable_video_analysis", True), - "max_video_size_mb": video_config.get("max_video_size_mb", 100), - "download_timeout": video_config.get("download_timeout", 60), - "supported_formats": video_config.get( - "supported_formats", ["mp4", "avi", "mov", "mkv", "flv", "wmv", "webm"] - ), - } - - # 写入新的功能配置文件 - with open(new_features_path, "w", encoding="utf-8") as f: - tomlkit.dump(new_features_config, f) - - logger.info(f"功能配置已成功迁移到: {new_features_path}") - - # 显示迁移的配置内容 - logger.info("迁移的配置内容:") - for key, value in new_features_config.items(): - logger.info(f" {key}: {value}") - - return True - - except Exception as e: - logger.error(f"功能配置迁移失败: {e}") - return False - - -def remove_features_from_old_config(config_path: str = "plugins/napcat_adapter_plugin/config/config.toml"): - """ - 从旧配置文件中移除功能相关配置,并将旧配置移动到 config/old/ 目录 - - Args: - config_path: 配置文件路径 - """ - try: - if not os.path.exists(config_path): - logger.warning(f"配置文件不存在: {config_path}") - return False - - # 确保 config/old 目录存在 - old_config_dir = "plugins/napcat_adapter_plugin/config/old" - os.makedirs(old_config_dir, exist_ok=True) - - # 备份原配置文件到 config/old 目录 - old_config_path = os.path.join(old_config_dir, "config_with_features.toml") - shutil.copy2(config_path, old_config_path) - logger.info(f"已备份包含功能配置的原文件到: {old_config_path}") - - # 读取配置文件 - with open(config_path, "r", encoding="utf-8") as f: - config = tomlkit.load(f) - - # 移除chat段中的功能相关配置 - removed_keys = [] - if "chat" in config: - chat_config = config["chat"] - permission_keys = [ - "group_list_type", - "group_list", - "private_list_type", - "private_list", - "ban_user_id", - "ban_qq_bot", - "enable_poke", - "ignore_non_self_poke", - "poke_debounce_seconds", - ] - - for key in permission_keys: - if key in chat_config: - del chat_config[key] - removed_keys.append(key) - - if removed_keys: - logger.info(f"已从chat配置段中移除功能相关配置: {removed_keys}") - - # 移除video段中的配置 - if "video" in config: - video_config = config["video"] - video_keys = ["enable_video_analysis", "max_video_size_mb", "download_timeout", "supported_formats"] - - video_removed_keys = [] - for key in video_keys: - if key in video_config: - del video_config[key] - video_removed_keys.append(key) - - if video_removed_keys: - logger.info(f"已从video配置段中移除配置: {video_removed_keys}") - removed_keys.extend(video_removed_keys) - - # 如果video段为空,则删除整个段 - if not video_config: - del config["video"] - logger.info("已删除空的video配置段") - - if removed_keys: - logger.info(f"总共移除的配置项: {removed_keys}") - - # 写回配置文件 - with open(config_path, "w", encoding="utf-8") as f: - f.write(tomlkit.dumps(config)) - - logger.info(f"已更新配置文件: {config_path}") - return True - - except Exception as e: - logger.error(f"移除功能配置失败: {e}") - return False - - -def auto_migrate_features(): - """ - 自动执行功能配置迁移 - """ - logger.info("开始自动功能配置迁移...") - - # 执行迁移 - if migrate_features_from_config(): - logger.info("功能配置迁移成功") - - # 询问是否要从旧配置文件中移除功能配置 - logger.info("功能配置已迁移到独立文件,建议从主配置文件中移除相关配置") - # 在实际使用中,这里可以添加用户确认逻辑 - # 为了自动化,这里直接执行移除 - remove_features_from_old_config() - - else: - logger.info("功能配置迁移跳过或失败") - - -if __name__ == "__main__": - auto_migrate_features() diff --git a/src/plugins/built_in/napcat_adapter_plugin/src/config/official_configs.py b/src/plugins/built_in/napcat_adapter_plugin/src/config/official_configs.py deleted file mode 100644 index 35bac1d57..000000000 --- a/src/plugins/built_in/napcat_adapter_plugin/src/config/official_configs.py +++ /dev/null @@ -1,74 +0,0 @@ -from dataclasses import dataclass, field -from typing import Literal - -from .config_base import ConfigBase - -""" -须知: -1. 本文件中记录了所有的配置项 -2. 所有新增的class都需要继承自ConfigBase -3. 所有新增的class都应在config.py中的Config类中添加字段 -4. 对于新增的字段,若为可选项,则应在其后添加field()并设置default_factory或default -""" - -ADAPTER_PLATFORM = "qq" - - -@dataclass -class NicknameConfig(ConfigBase): - nickname: str - """机器人昵称""" - - -@dataclass -class NapcatServerConfig(ConfigBase): - mode: Literal["reverse", "forward"] = "reverse" - """连接模式:reverse=反向连接(作为服务器), forward=正向连接(作为客户端)""" - - host: str = "localhost" - """主机地址""" - - port: int = 8095 - """端口号""" - - url: str = "" - """正向连接时的完整WebSocket URL,如 ws://localhost:8080/ws""" - - access_token: str = "" - """WebSocket 连接的访问令牌,用于身份验证""" - - heartbeat_interval: int = 30 - """心跳间隔时间,单位为秒""" - - -@dataclass -class MaiBotServerConfig(ConfigBase): - platform_name: str = field(default=ADAPTER_PLATFORM, init=False) - """平台名称,“qq”""" - - host: str = "localhost" - """MaiMCore的主机地址""" - - port: int = 8000 - """MaiMCore的端口号""" - - -@dataclass -class VoiceConfig(ConfigBase): - use_tts: bool = False - """是否启用TTS功能""" - - -@dataclass -class SlicingConfig(ConfigBase): - max_frame_size: int = 64 - """WebSocket帧的最大大小,单位为字节,默认64KB""" - - delay_ms: int = 10 - """切片发送间隔时间,单位为毫秒""" - - -@dataclass -class DebugConfig(ConfigBase): - level: Literal["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"] = "INFO" - """日志级别,默认为INFO""" diff --git a/src/plugins/built_in/napcat_adapter_plugin/src/recv_handler/message_handler.py b/src/plugins/built_in/napcat_adapter_plugin/src/recv_handler/message_handler.py index f761dc33f..7f60fe9da 100644 --- a/src/plugins/built_in/napcat_adapter_plugin/src/recv_handler/message_handler.py +++ b/src/plugins/built_in/napcat_adapter_plugin/src/recv_handler/message_handler.py @@ -97,21 +97,41 @@ class MessageHandler: # 使用新的权限管理器检查权限 if group_id: - if not config_api.get_plugin_config(self.plugin_config, f"features.group_allowed.{group_id}", True): - logger.warning("群聊不在聊天权限范围内,消息被丢弃") - return False + # 检查群聊黑白名单 + group_list_type = config_api.get_plugin_config(self.plugin_config, "features.group_list_type", "blacklist") + group_list = config_api.get_plugin_config(self.plugin_config, "features.group_list", []) + + if group_list_type == "whitelist": + if group_id not in group_list: + logger.warning("群聊不在白名单中,消息被丢弃") + return False + else: # blacklist + if group_id in group_list: + logger.warning("群聊在黑名单中,消息被丢弃") + return False else: - if not config_api.get_plugin_config(self.plugin_config, f"features.private_allowed.{user_id}", True): - logger.warning("私聊不在聊天权限范围内,消息被丢弃") - return False + # 检查私聊黑白名单 + private_list_type = config_api.get_plugin_config(self.plugin_config, "features.private_list_type", "blacklist") + private_list = config_api.get_plugin_config(self.plugin_config, "features.private_list", []) + + if private_list_type == "whitelist": + if user_id not in private_list: + logger.warning("私聊不在白名单中,消息被丢弃") + return False + else: # blacklist + if user_id in private_list: + logger.warning("私聊在黑名单中,消息被丢弃") + return False # 检查全局禁止名单 - if not ignore_global_list and config_api.get_plugin_config(self.plugin_config, f"features.user_banned.{user_id}", False): + ban_user_id = config_api.get_plugin_config(self.plugin_config, "features.ban_user_id", []) + if not ignore_global_list and user_id in ban_user_id: logger.warning("用户在全局黑名单中,消息被丢弃") return False # 检查QQ官方机器人 - if config_api.get_plugin_config(self.plugin_config, "features.qq_bot_banned", False) and group_id and not ignore_bot: + ban_qq_bot = config_api.get_plugin_config(self.plugin_config, "features.ban_qq_bot", False) + if ban_qq_bot and group_id and not ignore_bot: logger.debug("开始判断是否为机器人") member_info = await get_member_info(self.get_server_connection(), group_id, user_id) if member_info: @@ -267,14 +287,15 @@ class MessageHandler: return None # 检查是否需要使用消息缓冲 - if config_api.get_plugin_config(self.plugin_config, "features.message_buffer_enabled", False): + enable_message_buffer = config_api.get_plugin_config(self.plugin_config, "features.enable_message_buffer", True) + if enable_message_buffer: # 检查消息类型是否启用缓冲 message_type = raw_message.get("message_type") should_use_buffer = False - if message_type == "group" and config_api.get_plugin_config(self.plugin_config, "features.message_buffer_group_enabled", False): + if message_type == "group" and config_api.get_plugin_config(self.plugin_config, "features.message_buffer_enable_group", True): should_use_buffer = True - elif message_type == "private" and config_api.get_plugin_config(self.plugin_config, "features.message_buffer_private_enabled", False): + elif message_type == "private" and config_api.get_plugin_config(self.plugin_config, "features.message_buffer_enable_private", True): should_use_buffer = True if should_use_buffer: