继续尝试迁移,但是组件获取插件配置存在问题

This commit is contained in:
Windpicker-owo
2025-09-06 05:45:00 +08:00
parent 6c042cc73f
commit 41dc58d4fb
11 changed files with 94 additions and 584 deletions

View File

@@ -23,17 +23,20 @@ class BaseEventHandler(ABC):
"""是否拦截消息,默认为否"""
init_subscribe: List[Union[EventType, str]] = [EventType.UNKNOWN]
"""初始化时订阅的事件名称"""
plugin_name = None
def __init__(self):
self.log_prefix = "[EventHandler]"
"""对应插件名"""
self.plugin_config: Optional[Dict] = None
"""插件配置字典"""
self.subscribed_events = []
"""订阅的事件列表"""
if EventType.UNKNOWN in self.init_subscribe:
raise NotImplementedError("事件处理器必须指定 event_type")
from src.plugin_system.core.component_registry import component_registry
self.plugin_config = component_registry.get_plugin_config(self.plugin_name)
@abstractmethod
async def execute(self, kwargs: dict | None) -> Tuple[bool, bool, Optional[str]]:
"""执行事件处理的抽象方法,子类必须实现
@@ -90,14 +93,6 @@ class BaseEventHandler(ABC):
intercept_message=cls.intercept_message,
)
def set_plugin_config(self, plugin_config: Dict) -> None:
"""设置插件配置
Args:
plugin_config (dict): 插件配置字典
"""
self.plugin_config = plugin_config
def set_plugin_name(self, plugin_name: str) -> None:
"""设置插件名称

View File

@@ -248,6 +248,7 @@ class ComponentRegistry:
logger.error(f"注册失败: {handler_name} 不是有效的EventHandler")
return False
handler_class.plugin_name = handler_info.plugin_name
self._event_handler_registry[handler_name] = handler_class
if not handler_info.enabled:

View File

@@ -145,11 +145,12 @@ class EventManager:
logger.info(f"事件 {event_name} 已禁用")
return True
def register_event_handler(self, handler_class: Type[BaseEventHandler]) -> bool:
def register_event_handler(self, handler_class: Type[BaseEventHandler], plugin_config: Optional[dict] = None) -> bool:
"""注册事件处理器
Args:
handler_class (Type[BaseEventHandler]): 事件处理器类
plugin_config (Optional[dict]): 插件配置字典默认为None
Returns:
bool: 注册成功返回True已存在返回False
@@ -163,7 +164,12 @@ class EventManager:
logger.warning(f"事件处理器 {handler_name} 已存在,跳过注册")
return False
self._event_handlers[handler_name] = handler_class()
# 创建事件处理器实例,传递插件配置
handler_instance = handler_class()
if plugin_config is not None and hasattr(handler_instance, 'set_plugin_config'):
handler_instance.set_plugin_config(plugin_config)
self._event_handlers[handler_name] = handler_instance
# 处理init_subscribe缓存失败的订阅
if self._event_handlers[handler_name].init_subscribe:

View File

@@ -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.features_config import features_manager
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
@@ -158,11 +157,7 @@ async def graceful_shutdown():
except Exception as e:
logger.warning(f"停止消息重组器清理任务时出错: {e}")
# 停止功能管理器文件监控
try:
await features_manager.stop_file_watcher()
except Exception as e:
logger.warning(f"停止功能管理器文件监控时出错: {e}")
# 停止功能管理器文件监控(已迁移到插件系统配置,无需操作)
# 关闭消息处理器(包括消息缓冲器)
try:
@@ -234,11 +229,8 @@ class LauchNapcatAdapterHandler(BaseEventHandler):
logger.info("启动消息重组器...")
await reassembler.start_cleanup_task()
# 初始化功能管理器
logger.info("正在初始化功能管理器...")
features_manager.load_config()
await features_manager.start_file_watcher(check_interval=2.0)
logger.info("功能管理器初始化完成")
# 功能管理器已迁移到插件系统配置
logger.info("功能配置已迁移到插件系统")
logger.info("开始启动Napcat Adapter")
message_send_instance.maibot_router = router
# 设置插件配置
@@ -250,6 +242,12 @@ class LauchNapcatAdapterHandler(BaseEventHandler):
set_response_pool_config(self.plugin_config)
# 设置send_handler的插件配置
send_handler.set_plugin_config(self.plugin_config)
# 设置message_handler的插件配置
message_handler.set_plugin_config(self.plugin_config)
# 设置notice_handler的插件配置
notice_handler.set_plugin_config(self.plugin_config)
# 设置meta_event_handler的插件配置
meta_event_handler.set_plugin_config(self.plugin_config)
# 创建单独的异步任务,防止阻塞主线程
asyncio.create_task(napcat_server(self.plugin_config))
asyncio.create_task(mmc_start_com(self.plugin_config))
@@ -287,6 +285,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="配置文件版本"),
"enabled": ConfigField(type=bool, default=False, description="是否启用插件"),
},
"inner": {

View File

@@ -1,151 +0,0 @@
import os
from dataclasses import dataclass
from datetime import datetime
import tomlkit
import shutil
from tomlkit import TOMLDocument
from tomlkit.items import Table
from src.common.logger import get_logger
logger = get_logger("napcat_adapter")
from rich.traceback import install
from .config_base import ConfigBase
from .official_configs import (
DebugConfig,
MaiBotServerConfig,
NapcatServerConfig,
NicknameConfig,
SlicingConfig,
VoiceConfig,
)
install(extra_lines=3)
TEMPLATE_DIR = "plugins/napcat_adapter_plugin/template"
CONFIG_DIR = "plugins/napcat_adapter_plugin/config"
OLD_CONFIG_DIR = "plugins/napcat_adapter_plugin/config/old"
def ensure_config_directories():
"""确保配置目录存在"""
os.makedirs(CONFIG_DIR, exist_ok=True)
os.makedirs(OLD_CONFIG_DIR, exist_ok=True)
def update_config():
"""更新配置文件,统一使用 config/old 目录进行备份"""
# 确保目录存在
ensure_config_directories()
# 定义文件路径
template_path = f"{TEMPLATE_DIR}/template_config.toml"
config_path = f"{CONFIG_DIR}/config.toml"
# 检查配置文件是否存在
if not os.path.exists(config_path):
logger.info("主配置文件不存在,从模板创建新配置")
shutil.copy2(template_path, config_path)
logger.info(f"已创建新配置文件: {config_path}")
logger.info("程序将退出,请检查配置文件后重启")
# 读取配置文件和模板文件
with open(config_path, "r", encoding="utf-8") as f:
old_config = tomlkit.load(f)
with open(template_path, "r", encoding="utf-8") as f:
new_config = tomlkit.load(f)
# 检查version是否相同
if old_config and "inner" in old_config and "inner" in new_config:
old_version = old_config["inner"].get("version")
new_version = new_config["inner"].get("version")
if old_version and new_version and old_version == new_version:
logger.info(f"检测到配置文件版本号相同 (v{old_version}),跳过更新")
return
else:
logger.info(f"检测到版本号不同: 旧版本 v{old_version} -> 新版本 v{new_version}")
else:
logger.info("已有配置文件未检测到版本号,可能是旧版本。将进行更新")
# 创建备份文件
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
backup_path = os.path.join(OLD_CONFIG_DIR, f"config.toml.bak.{timestamp}")
# 备份旧配置文件
shutil.copy2(config_path, backup_path)
logger.info(f"已备份旧配置文件到: {backup_path}")
# 复制模板文件到配置目录
shutil.copy2(template_path, config_path)
logger.info(f"已创建新配置文件: {config_path}")
def update_dict(target: TOMLDocument | dict, source: TOMLDocument | dict):
"""将source字典的值更新到target字典中如果target中存在相同的键"""
for key, value in source.items():
# 跳过version字段的更新
if key == "version":
continue
if key in target:
if isinstance(value, dict) and isinstance(target[key], (dict, Table)):
update_dict(target[key], value)
else:
try:
# 对数组类型进行特殊处理
if isinstance(value, list):
# 如果是空数组,确保它保持为空数组
target[key] = tomlkit.array(str(value)) if value else tomlkit.array()
else:
# 其他类型使用item方法创建新值
target[key] = tomlkit.item(value)
except (TypeError, ValueError):
# 如果转换失败,直接赋值
target[key] = value
# 将旧配置的值更新到新配置中
logger.info("开始合并新旧配置...")
update_dict(new_config, old_config)
# 保存更新后的配置(保留注释和格式)
with open(config_path, "w", encoding="utf-8") as f:
f.write(tomlkit.dumps(new_config))
logger.info("配置文件更新完成,建议检查新配置文件中的内容,以免丢失重要信息")
@dataclass
class Config(ConfigBase):
"""总配置类"""
nickname: NicknameConfig
napcat_server: NapcatServerConfig
maibot_server: MaiBotServerConfig
voice: VoiceConfig
slicing: SlicingConfig
debug: DebugConfig
def load_config(config_path: str) -> Config:
"""
加载配置文件
:param config_path: 配置文件路径
:return: Config对象
"""
# 读取配置文件
with open(config_path, "r", encoding="utf-8") as f:
config_data = tomlkit.load(f)
# 创建Config对象
try:
return Config.from_dict(config_data)
except Exception as e:
logger.critical("配置文件解析失败")
raise e
# 更新配置
update_config()
logger.info("正在品鉴配置文件...")
global_config = load_config(config_path=f"{CONFIG_DIR}/config.toml")
logger.info("非常的新鲜,非常的美味!")

View File

@@ -1,359 +0,0 @@
import asyncio
from dataclasses import dataclass, field
from typing import Literal, Optional
from pathlib import Path
import tomlkit
from src.common.logger import get_logger
logger = get_logger("napcat_adapter")
from .config_base import ConfigBase
from .config_utils import create_config_from_template, create_default_config_dict
@dataclass
class FeaturesConfig(ConfigBase):
"""功能配置类"""
group_list_type: Literal["whitelist", "blacklist"] = "whitelist"
"""群聊列表类型 白名单/黑名单"""
group_list: list[int] = field(default_factory=list)
"""群聊列表"""
private_list_type: Literal["whitelist", "blacklist"] = "whitelist"
"""私聊列表类型 白名单/黑名单"""
private_list: list[int] = field(default_factory=list)
"""私聊列表"""
ban_user_id: list[int] = field(default_factory=list)
"""被封禁的用户ID列表封禁后将无法与其进行交互"""
ban_qq_bot: bool = False
"""是否屏蔽QQ官方机器人若为True则所有QQ官方机器人将无法与MaiMCore进行交互"""
enable_poke: bool = True
"""是否启用戳一戳功能"""
ignore_non_self_poke: bool = False
"""是否无视不是针对自己的戳一戳"""
poke_debounce_seconds: int = 3
"""戳一戳防抖时间(秒),在指定时间内第二次针对机器人的戳一戳将被忽略"""
enable_reply_at: bool = True
"""是否启用引用回复时艾特用户的功能"""
reply_at_rate: float = 0.5
"""引用回复时艾特用户的几率 (0.0 ~ 1.0)"""
enable_video_analysis: bool = True
"""是否启用视频识别功能"""
max_video_size_mb: int = 100
"""视频文件最大大小限制MB"""
download_timeout: int = 60
"""视频下载超时时间(秒)"""
supported_formats: list[str] = field(default_factory=lambda: ["mp4", "avi", "mov", "mkv", "flv", "wmv", "webm"])
"""支持的视频格式"""
# 消息缓冲配置
enable_message_buffer: bool = True
"""是否启用消息缓冲合并功能"""
message_buffer_enable_group: bool = True
"""是否启用群消息缓冲合并"""
message_buffer_enable_private: bool = True
"""是否启用私聊消息缓冲合并"""
message_buffer_interval: float = 3.0
"""消息合并间隔时间(秒),在此时间内的连续消息将被合并"""
message_buffer_initial_delay: float = 0.5
"""消息缓冲初始延迟(秒),收到第一条消息后等待此时间开始合并"""
message_buffer_max_components: int = 50
"""单个会话最大缓冲消息组件数量,超过此数量将强制合并"""
message_buffer_block_prefixes: list[str] = field(default_factory=lambda: ["/", "!", "", ".", "", "#", "%"])
"""消息缓冲屏蔽前缀,以这些前缀开头的消息不会被缓冲"""
class FeaturesManager:
"""功能管理器,支持热重载"""
def __init__(self, config_path: str = "plugins/napcat_adapter_plugin/config/features.toml"):
self.config_path = Path(config_path)
self.config: Optional[FeaturesConfig] = None
self._file_watcher_task: Optional[asyncio.Task] = None
self._last_modified: Optional[float] = None
self._callbacks: list = []
def add_reload_callback(self, callback):
"""添加配置重载回调函数"""
self._callbacks.append(callback)
def remove_reload_callback(self, callback):
"""移除配置重载回调函数"""
if callback in self._callbacks:
self._callbacks.remove(callback)
async def _notify_callbacks(self):
"""通知所有回调函数配置已重载"""
for callback in self._callbacks:
try:
if asyncio.iscoroutinefunction(callback):
await callback(self.config)
else:
callback(self.config)
except Exception as e:
logger.error(f"配置重载回调执行失败: {e}")
def load_config(self) -> FeaturesConfig:
"""加载功能配置文件"""
try:
# 检查配置文件是否存在,如果不存在则创建并退出程序
if not self.config_path.exists():
logger.info(f"功能配置文件不存在: {self.config_path}")
self._create_default_config()
# 配置文件创建后程序应该退出,让用户检查配置
logger.info("程序将退出,请检查功能配置文件后重启")
quit(0)
with open(self.config_path, "r", encoding="utf-8") as f:
config_data = tomlkit.load(f)
self.config = FeaturesConfig.from_dict(config_data)
self._last_modified = self.config_path.stat().st_mtime
logger.info(f"功能配置加载成功: {self.config_path}")
return self.config
except Exception as e:
logger.error(f"功能配置加载失败: {e}")
logger.critical("无法加载功能配置文件,程序退出")
quit(1)
def _create_default_config(self):
"""创建默认功能配置文件"""
template_path = "template/features_template.toml"
# 尝试从模板创建配置文件
if create_config_from_template(
str(self.config_path),
template_path,
"功能配置文件",
should_exit=False, # 不在这里退出,由调用方决定
):
return
# 如果模板文件不存在,创建基本配置
logger.info("模板文件不存在,创建基本功能配置")
default_config = {
"group_list_type": "whitelist",
"group_list": [],
"private_list_type": "whitelist",
"private_list": [],
"ban_user_id": [],
"ban_qq_bot": False,
"enable_poke": True,
"ignore_non_self_poke": False,
"poke_debounce_seconds": 3,
"enable_reply_at": True,
"reply_at_rate": 0.5,
"enable_video_analysis": True,
"max_video_size_mb": 100,
"download_timeout": 60,
"supported_formats": ["mp4", "avi", "mov", "mkv", "flv", "wmv", "webm"],
# 消息缓冲配置
"enable_message_buffer": True,
"message_buffer_enable_group": True,
"message_buffer_enable_private": True,
"message_buffer_interval": 3.0,
"message_buffer_initial_delay": 0.5,
"message_buffer_max_components": 50,
"message_buffer_block_prefixes": ["/", "!", "", ".", "", "#", "%"],
}
if not create_default_config_dict(default_config, str(self.config_path), "功能配置文件"):
logger.critical("无法创建功能配置文件")
quit(1)
async def reload_config(self) -> bool:
"""重新加载配置文件"""
try:
if not self.config_path.exists():
logger.warning(f"功能配置文件不存在,无法重载: {self.config_path}")
return False
current_modified = self.config_path.stat().st_mtime
if self._last_modified and current_modified <= self._last_modified:
return False # 文件未修改
old_config = self.config
new_config = self.load_config()
# 检查配置是否真的发生了变化
if old_config and self._configs_equal(old_config, new_config):
return False
logger.info("功能配置已重载")
await self._notify_callbacks()
return True
except Exception as e:
logger.error(f"功能配置重载失败: {e}")
return False
def _configs_equal(self, config1: FeaturesConfig, config2: FeaturesConfig) -> bool:
"""比较两个配置是否相等"""
return (
config1.group_list_type == config2.group_list_type
and set(config1.group_list) == set(config2.group_list)
and config1.private_list_type == config2.private_list_type
and set(config1.private_list) == set(config2.private_list)
and set(config1.ban_user_id) == set(config2.ban_user_id)
and config1.ban_qq_bot == config2.ban_qq_bot
and config1.enable_poke == config2.enable_poke
and config1.ignore_non_self_poke == config2.ignore_non_self_poke
and config1.poke_debounce_seconds == config2.poke_debounce_seconds
and config1.enable_reply_at == config2.enable_reply_at
and config1.reply_at_rate == config2.reply_at_rate
and config1.enable_video_analysis == config2.enable_video_analysis
and config1.max_video_size_mb == config2.max_video_size_mb
and config1.download_timeout == config2.download_timeout
and set(config1.supported_formats) == set(config2.supported_formats)
and
# 消息缓冲配置比较
config1.enable_message_buffer == config2.enable_message_buffer
and config1.message_buffer_enable_group == config2.message_buffer_enable_group
and config1.message_buffer_enable_private == config2.message_buffer_enable_private
and config1.message_buffer_interval == config2.message_buffer_interval
and config1.message_buffer_initial_delay == config2.message_buffer_initial_delay
and config1.message_buffer_max_components == config2.message_buffer_max_components
and set(config1.message_buffer_block_prefixes) == set(config2.message_buffer_block_prefixes)
)
async def start_file_watcher(self, check_interval: float = 1.0):
"""启动文件监控,定期检查配置文件变化"""
if self._file_watcher_task and not self._file_watcher_task.done():
logger.warning("文件监控已在运行")
return
self._file_watcher_task = asyncio.create_task(self._file_watcher_loop(check_interval))
logger.info(f"功能配置文件监控已启动,检查间隔: {check_interval}")
async def stop_file_watcher(self):
"""停止文件监控"""
if self._file_watcher_task and not self._file_watcher_task.done():
self._file_watcher_task.cancel()
try:
await self._file_watcher_task
except asyncio.CancelledError:
pass
logger.info("功能配置文件监控已停止")
async def _file_watcher_loop(self, check_interval: float):
"""文件监控循环"""
while True:
try:
await asyncio.sleep(check_interval)
await self.reload_config()
except asyncio.CancelledError:
break
except Exception as e:
logger.error(f"文件监控循环出错: {e}")
await asyncio.sleep(check_interval)
def get_config(self) -> FeaturesConfig:
"""获取当前功能配置"""
if self.config is None:
return self.load_config()
return self.config
def is_group_allowed(self, group_id: int) -> bool:
"""检查群聊是否被允许"""
config = self.get_config()
if config.group_list_type == "whitelist":
return group_id in config.group_list
else: # blacklist
return group_id not in config.group_list
def is_private_allowed(self, user_id: int) -> bool:
"""检查私聊是否被允许"""
config = self.get_config()
if config.private_list_type == "whitelist":
return user_id in config.private_list
else: # blacklist
return user_id not in config.private_list
def is_user_banned(self, user_id: int) -> bool:
"""检查用户是否被全局禁止"""
config = self.get_config()
return user_id in config.ban_user_id
def is_qq_bot_banned(self) -> bool:
"""检查是否禁止QQ官方机器人"""
config = self.get_config()
return config.ban_qq_bot
def is_poke_enabled(self) -> bool:
"""检查戳一戳功能是否启用"""
config = self.get_config()
return config.enable_poke
def is_non_self_poke_ignored(self) -> bool:
"""检查是否忽略非自己戳一戳"""
config = self.get_config()
return config.ignore_non_self_poke
def is_message_buffer_enabled(self) -> bool:
"""检查消息缓冲功能是否启用"""
config = self.get_config()
return config.enable_message_buffer
def is_message_buffer_group_enabled(self) -> bool:
"""检查群消息缓冲是否启用"""
config = self.get_config()
return config.message_buffer_enable_group
def is_message_buffer_private_enabled(self) -> bool:
"""检查私聊消息缓冲是否启用"""
config = self.get_config()
return config.message_buffer_enable_private
def get_message_buffer_interval(self) -> float:
"""获取消息缓冲间隔时间"""
config = self.get_config()
return config.message_buffer_interval
def get_message_buffer_initial_delay(self) -> float:
"""获取消息缓冲初始延迟"""
config = self.get_config()
return config.message_buffer_initial_delay
def get_message_buffer_max_components(self) -> int:
"""获取消息缓冲最大组件数量"""
config = self.get_config()
return config.message_buffer_max_components
def is_message_buffer_group_enabled(self) -> bool:
"""检查是否启用群聊消息缓冲"""
config = self.get_config()
return config.message_buffer_enable_group
def is_message_buffer_private_enabled(self) -> bool:
"""检查是否启用私聊消息缓冲"""
config = self.get_config()
return config.message_buffer_enable_private
def get_message_buffer_block_prefixes(self) -> list[str]:
"""获取消息缓冲屏蔽前缀列表"""
config = self.get_config()
return config.message_buffer_block_prefixes
# 全局功能管理器实例
features_manager = FeaturesManager()

View File

@@ -7,7 +7,7 @@ from src.common.logger import get_logger
logger = get_logger("napcat_adapter")
from .config.features_config import features_manager
from src.plugin_system.apis import config_api
from .recv_handler import RealMessageType
@@ -43,6 +43,11 @@ class SimpleMessageBuffer:
self.lock = asyncio.Lock()
self.merge_callback = merge_callback
self._shutdown = False
self.plugin_config = None
def set_plugin_config(self, plugin_config: dict):
"""设置插件配置"""
self.plugin_config = plugin_config
def get_session_id(self, event_data: Dict[str, Any]) -> str:
"""根据事件数据生成会话ID"""
@@ -97,8 +102,7 @@ class SimpleMessageBuffer:
return True
# 检查屏蔽前缀
config = features_manager.get_config()
block_prefixes = tuple(config.message_buffer_block_prefixes)
block_prefixes = tuple(config_api.get_plugin_config(self.plugin_config, "features.message_buffer_block_prefixes", []))
text = text.strip()
if text.startswith(block_prefixes):
@@ -124,15 +128,15 @@ class SimpleMessageBuffer:
if self._shutdown:
return False
config = features_manager.get_config()
if not config.enable_message_buffer:
# 检查是否启用消息缓冲
if not config_api.get_plugin_config(self.plugin_config, "features.enable_message_buffer", False):
return False
# 检查是否启用对应类型的缓冲
message_type = event_data.get("message_type", "")
if message_type == "group" and not config.message_buffer_enable_group:
if message_type == "group" and not config_api.get_plugin_config(self.plugin_config, "features.message_buffer_enable_group", False):
return False
elif message_type == "private" and not config.message_buffer_enable_private:
elif message_type == "private" and not config_api.get_plugin_config(self.plugin_config, "features.message_buffer_enable_private", False):
return False
# 提取文本
@@ -154,7 +158,7 @@ class SimpleMessageBuffer:
session = self.buffer_pool[session_id]
# 检查是否超过最大组件数量
if len(session.messages) >= config.message_buffer_max_components:
if len(session.messages) >= config_api.get_plugin_config(self.plugin_config, "features.message_buffer_max_components", 5):
logger.info(f"会话 {session_id} 消息数量达到上限,强制合并")
asyncio.create_task(self._force_merge_session(session_id))
self.buffer_pool[session_id] = BufferedSession(session_id=session_id, original_event=original_event)
@@ -187,8 +191,8 @@ class SimpleMessageBuffer:
async def _wait_and_start_merge(self, session_id: str):
"""等待初始延迟后开始合并定时器"""
config = features_manager.get_config()
await asyncio.sleep(config.message_buffer_initial_delay)
initial_delay = config_api.get_plugin_config(self.plugin_config, "features.message_buffer_initial_delay", 0.5)
await asyncio.sleep(initial_delay)
async with self.lock:
session = self.buffer_pool.get(session_id)
@@ -206,8 +210,8 @@ class SimpleMessageBuffer:
async def _wait_and_merge(self, session_id: str):
"""等待合并间隔后执行合并"""
config = features_manager.get_config()
await asyncio.sleep(config.message_buffer_interval)
interval = config_api.get_plugin_config(self.plugin_config, "features.message_buffer_interval", 2.0)
await asyncio.sleep(interval)
await self._merge_session(session_id)
async def _force_merge_session(self, session_id: str):

View File

@@ -5,7 +5,7 @@ from ...CONSTS import PLUGIN_NAME
logger = get_logger("napcat_adapter")
from src.plugin_system.core.config_manager import config_api
from src.plugin_system.apis import config_api
from ..message_buffer import SimpleMessageBuffer
from ..utils import (
get_group_info,
@@ -47,9 +47,17 @@ class MessageHandler:
def __init__(self):
self.server_connection: Server.ServerConnection = None
self.bot_id_list: Dict[int, bool] = {}
self.plugin_config = None
# 初始化简化消息缓冲器,传入回调函数
self.message_buffer = SimpleMessageBuffer(merge_callback=self._send_buffered_message)
def set_plugin_config(self, plugin_config: dict):
"""设置插件配置"""
self.plugin_config = plugin_config
# 将配置传递给消息缓冲器
if self.message_buffer:
self.message_buffer.set_plugin_config(plugin_config)
async def shutdown(self):
"""关闭消息处理器,清理资源"""
if self.message_buffer:
@@ -89,21 +97,21 @@ class MessageHandler:
# 使用新的权限管理器检查权限
if group_id:
if not config_api.get_plugin_config(PLUGIN_NAME, f"features.group_allowed.{group_id}", True):
if not config_api.get_plugin_config(self.plugin_config, f"features.group_allowed.{group_id}", True):
logger.warning("群聊不在聊天权限范围内,消息被丢弃")
return False
else:
if not config_api.get_plugin_config(PLUGIN_NAME, f"features.private_allowed.{user_id}", True):
if not config_api.get_plugin_config(self.plugin_config, f"features.private_allowed.{user_id}", True):
logger.warning("私聊不在聊天权限范围内,消息被丢弃")
return False
# 检查全局禁止名单
if not ignore_global_list and config_api.get_plugin_config(PLUGIN_NAME, f"features.user_banned.{user_id}", False):
if not ignore_global_list and config_api.get_plugin_config(self.plugin_config, f"features.user_banned.{user_id}", False):
logger.warning("用户在全局黑名单中,消息被丢弃")
return False
# 检查QQ官方机器人
if config_api.get_plugin_config(PLUGIN_NAME, "features.qq_bot_banned", False) and group_id and not ignore_bot:
if config_api.get_plugin_config(self.plugin_config, "features.qq_bot_banned", False) 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:
@@ -148,7 +156,7 @@ class MessageHandler:
# 发送者用户信息
user_info: UserInfo = UserInfo(
platform=config_api.get_plugin_config(PLUGIN_NAME, "maibot_server.platform_name"),
platform=config_api.get_plugin_config(self.plugin_config, "maibot_server.platform_name"),
user_id=sender_info.get("user_id"),
user_nickname=sender_info.get("nickname"),
user_cardname=sender_info.get("card"),
@@ -174,7 +182,7 @@ class MessageHandler:
nickname = fetched_member_info.get("nickname") if fetched_member_info else None
# 发送者用户信息
user_info: UserInfo = UserInfo(
platform=config_api.get_plugin_config(PLUGIN_NAME, "maibot_server.platform_name"),
platform=config_api.get_plugin_config(self.plugin_config, "maibot_server.platform_name"),
user_id=sender_info.get("user_id"),
user_nickname=nickname,
user_cardname=None,
@@ -191,7 +199,7 @@ class MessageHandler:
group_name = fetched_group_info.get("group_name")
group_info: GroupInfo = GroupInfo(
platform=config_api.get_plugin_config(PLUGIN_NAME, "maibot_server.platform_name"),
platform=config_api.get_plugin_config(self.plugin_config, "maibot_server.platform_name"),
group_id=raw_message.get("group_id"),
group_name=group_name,
)
@@ -209,7 +217,7 @@ class MessageHandler:
# 发送者用户信息
user_info: UserInfo = UserInfo(
platform=config_api.get_plugin_config(PLUGIN_NAME, "maibot_server.platform_name"),
platform=config_api.get_plugin_config(self.plugin_config, "maibot_server.platform_name"),
user_id=sender_info.get("user_id"),
user_nickname=sender_info.get("nickname"),
user_cardname=sender_info.get("card"),
@@ -222,7 +230,7 @@ class MessageHandler:
group_name = fetched_group_info.get("group_name")
group_info: GroupInfo = GroupInfo(
platform=config_api.get_plugin_config(PLUGIN_NAME, "maibot_server.platform_name"),
platform=config_api.get_plugin_config(self.plugin_config, "maibot_server.platform_name"),
group_id=raw_message.get("group_id"),
group_name=group_name,
)
@@ -232,12 +240,12 @@ class MessageHandler:
return None
additional_config: dict = {}
if config_api.get_plugin_config(PLUGIN_NAME, "voice.use_tts"):
if config_api.get_plugin_config(self.plugin_config, "voice.use_tts"):
additional_config["allow_tts"] = True
# 消息信息
message_info: BaseMessageInfo = BaseMessageInfo(
platform=config_api.get_plugin_config(PLUGIN_NAME, "maibot_server.platform_name"),
platform=config_api.get_plugin_config(self.plugin_config, "maibot_server.platform_name"),
message_id=message_id,
time=message_time,
user_info=user_info,
@@ -259,14 +267,14 @@ class MessageHandler:
return None
# 检查是否需要使用消息缓冲
if config_api.get_plugin_config(PLUGIN_NAME, "features.message_buffer_enabled", False):
if config_api.get_plugin_config(self.plugin_config, "features.message_buffer_enabled", False):
# 检查消息类型是否启用缓冲
message_type = raw_message.get("message_type")
should_use_buffer = False
if message_type == "group" and config_api.get_plugin_config(PLUGIN_NAME, "features.message_buffer_group_enabled", False):
if message_type == "group" and config_api.get_plugin_config(self.plugin_config, "features.message_buffer_group_enabled", False):
should_use_buffer = True
elif message_type == "private" and config_api.get_plugin_config(PLUGIN_NAME, "features.message_buffer_private_enabled", False):
elif message_type == "private" and config_api.get_plugin_config(self.plugin_config, "features.message_buffer_private_enabled", False):
should_use_buffer = True
if should_use_buffer:

View File

@@ -1,7 +1,7 @@
from src.common.logger import get_logger
logger = get_logger("napcat_adapter")
from ..config import global_config
from src.plugin_system.apis import config_api
import time
import asyncio
@@ -14,8 +14,15 @@ class MetaEventHandler:
"""
def __init__(self):
self.interval = global_config.napcat_server.heartbeat_interval
self.interval = 5.0 # 默认值稍后通过set_plugin_config设置
self._interval_checking = False
self.plugin_config = None
def set_plugin_config(self, plugin_config: dict):
"""设置插件配置"""
self.plugin_config = plugin_config
# 更新interval值
self.interval = config_api.get_plugin_config(self.plugin_config, "napcat_server.heartbeat_interval", 5000) / 1000
async def handle_meta_event(self, message: dict) -> None:
event_type = message.get("meta_event_type")

View File

@@ -8,8 +8,7 @@ from src.common.logger import get_logger
logger = get_logger("napcat_adapter")
from ..config import global_config
from ..config.features_config import features_manager
from src.plugin_system.apis import config_api
from ..database import BanUser, db_manager, is_identical
from . import NoticeType, ACCEPT_FORMAT
from .message_sending import message_send_instance
@@ -38,6 +37,11 @@ class NoticeHandler:
def __init__(self):
self.server_connection: Server.ServerConnection | None = None
self.last_poke_time: float = 0.0 # 记录最后一次针对机器人的戳一戳时间
self.plugin_config = None
def set_plugin_config(self, plugin_config: dict):
"""设置插件配置"""
self.plugin_config = plugin_config
async def set_server_connection(self, server_connection: Server.ServerConnection) -> None:
"""设置Napcat连接"""
@@ -112,7 +116,7 @@ class NoticeHandler:
sub_type = raw_message.get("sub_type")
match sub_type:
case NoticeType.Notify.poke:
if features_manager.is_poke_enabled() and await message_handler.check_allow_to_chat(
if config_api.get_plugin_config(self.plugin_config, "features.poke_enabled", True) and await message_handler.check_allow_to_chat(
user_id, group_id, False, False
):
logger.info("处理戳一戳消息")
@@ -159,13 +163,13 @@ class NoticeHandler:
else:
logger.warning("无法获取notice消息所在群的名称")
group_info = GroupInfo(
platform=global_config.maibot_server.platform_name,
platform=config_api.get_plugin_config(self.plugin_config, "maibot_server.platform_name", "qq"),
group_id=group_id,
group_name=group_name,
)
message_info: BaseMessageInfo = BaseMessageInfo(
platform=global_config.maibot_server.platform_name,
platform=config_api.get_plugin_config(self.plugin_config, "maibot_server.platform_name", "qq"),
message_id="notice",
time=message_time,
user_info=user_info,
@@ -206,7 +210,7 @@ class NoticeHandler:
# 防抖检查:如果是针对机器人的戳一戳,检查防抖时间
if self_id == target_id:
current_time = time.time()
debounce_seconds = features_manager.get_config().poke_debounce_seconds
debounce_seconds = config_api.get_plugin_config(self.plugin_config, "features.poke_debounce_seconds", 2.0)
if self.last_poke_time > 0:
time_diff = current_time - self.last_poke_time
@@ -243,7 +247,7 @@ class NoticeHandler:
else:
# 如果配置为忽略不是针对自己的戳一戳则直接返回None
if features_manager.is_non_self_poke_ignored():
if config_api.get_plugin_config(self.plugin_config, "features.non_self_poke_ignored", False):
logger.info("忽略不是针对自己的戳一戳消息")
return None, None
@@ -268,7 +272,7 @@ class NoticeHandler:
logger.warning(f"解析戳一戳消息失败: {str(e)},将使用默认文本")
user_info: UserInfo = UserInfo(
platform=global_config.maibot_server.platform_name,
platform=config_api.get_plugin_config(self.plugin_config, "maibot_server.platform_name", "qq"),
user_id=user_id,
user_nickname=user_name,
user_cardname=user_cardname,
@@ -299,7 +303,7 @@ class NoticeHandler:
operator_nickname = "QQ用户"
operator_info: UserInfo = UserInfo(
platform=global_config.maibot_server.platform_name,
platform=config_api.get_plugin_config(self.plugin_config, "maibot_server.platform_name", "qq"),
user_id=operator_id,
user_nickname=operator_nickname,
user_cardname=operator_cardname,
@@ -328,7 +332,7 @@ class NoticeHandler:
user_nickname = fetched_member_info.get("nickname")
user_cardname = fetched_member_info.get("card")
banned_user_info: UserInfo = UserInfo(
platform=global_config.maibot_server.platform_name,
platform=config_api.get_plugin_config(self.plugin_config, "maibot_server.platform_name", "qq"),
user_id=user_id,
user_nickname=user_nickname,
user_cardname=user_cardname,
@@ -367,7 +371,7 @@ class NoticeHandler:
operator_nickname = "QQ用户"
operator_info: UserInfo = UserInfo(
platform=global_config.maibot_server.platform_name,
platform=config_api.get_plugin_config(self.plugin_config, "maibot_server.platform_name", "qq"),
user_id=operator_id,
user_nickname=operator_nickname,
user_cardname=operator_cardname,
@@ -393,7 +397,7 @@ class NoticeHandler:
else:
logger.warning("无法获取解除禁言消息发送者的昵称,消息可能会无效")
lifted_user_info: UserInfo = UserInfo(
platform=global_config.maibot_server.platform_name,
platform=config_api.get_plugin_config(self.plugin_config, "maibot_server.platform_name", "qq"),
user_id=user_id,
user_nickname=user_nickname,
user_cardname=user_cardname,
@@ -436,13 +440,13 @@ class NoticeHandler:
else:
logger.warning("无法获取notice消息所在群的名称")
group_info = GroupInfo(
platform=global_config.maibot_server.platform_name,
platform=config_api.get_plugin_config(self.plugin_config, "maibot_server.platform_name", "qq"),
group_id=group_id,
group_name=group_name,
)
message_info: BaseMessageInfo = BaseMessageInfo(
platform=global_config.maibot_server.platform_name,
platform=config_api.get_plugin_config(self.plugin_config, "maibot_server.platform_name", "qq"),
message_id="notice",
time=time.time(),
user_info=None, # 自然解除禁言没有操作者
@@ -493,7 +497,7 @@ class NoticeHandler:
user_cardname = fetched_member_info.get("card")
lifted_user_info: UserInfo = UserInfo(
platform=global_config.maibot_server.platform_name,
platform=config_api.get_plugin_config(self.plugin_config, "maibot_server.platform_name", "qq"),
user_id=user_id,
user_nickname=user_nickname,
user_cardname=user_cardname,

View File

@@ -22,7 +22,6 @@ logger = get_logger("napcat_adapter")
from .utils import get_image_format, convert_image_to_gif
from .recv_handler.message_sending import message_send_instance
from .websocket_manager import websocket_manager
from .config.features_config import features_manager
class SendHandler:
@@ -292,11 +291,8 @@ class SendHandler:
"""处理回复消息"""
reply_seg = {"type": "reply", "data": {"id": id}}
# 获取功能配置
ft_config = features_manager.get_config()
# 检查是否启用引用艾特功能
if not ft_config.enable_reply_at:
if not config_api.get_plugin_config(self.plugin_config, "features.enable_reply_at", False):
return reply_seg
try:
@@ -315,7 +311,7 @@ class SendHandler:
return reply_seg
# 根据概率决定是否艾特用户
if random.random() < ft_config.reply_at_rate:
if random.random() < config_api.get_plugin_config(self.plugin_config, "features.reply_at_rate", 0.5):
at_seg = {"type": "at", "data": {"qq": str(replied_user_id)}}
# 在艾特后面添加一个空格
text_seg = {"type": "text", "data": {"text": " "}}