[fix] (bot-instance) 尝试采用手动配置QQ号的方式,用于定时自动分析

This commit is contained in:
SXP-Simon
2025-09-15 22:05:52 +08:00
parent 1bd508cd69
commit 53a21f7642
7 changed files with 245 additions and 65 deletions
+6
View File
@@ -117,6 +117,12 @@
"default": "",
"hint": "自定义服务所使用的模型名称,例如 gpt-4 、deepseek/deepseek-r1:free 或自定义模型标识,由于自定义情况复杂,无法给出有效的参数参考,需要根据实际情况例如日志报错判断。留空则使用 Astrbot 统一内置提供商。"
},
"bot_qq_id": {
"type": "string",
"description": "机器人QQ号",
"default": "",
"hint": "用于自动分析的机器人QQ号,填写后可启用自动分析功能"
},
"pdf_output_dir": {
"type": "string",
"description": "PDF输出目录",
+21 -39
View File
@@ -18,6 +18,7 @@ from astrbot.core.star.filter.permission import PermissionType
# 导入重构后的模块
from .src.core.config import ConfigManager
from .src.core.bot_manager import BotManager
from .src.reports.generators import ReportGenerator
from .src.scheduler.auto_scheduler import AutoScheduler
from .src.utils.pdf_utils import PDFInstaller
@@ -26,6 +27,7 @@ from .src.utils.helpers import MessageAnalyzer
# 全局变量
config_manager = None
bot_manager = None
message_analyzer = None
report_generator = None
auto_scheduler = None
@@ -37,16 +39,19 @@ class QQGroupDailyAnalysis(Star):
self.config = config
# 初始化模块化组件
global config_manager, message_analyzer, report_generator, auto_scheduler
global config_manager, bot_manager, message_analyzer, report_generator, auto_scheduler
config_manager = ConfigManager(config)
message_analyzer = MessageAnalyzer(context, config_manager)
bot_manager = BotManager(config_manager)
bot_manager.set_context(context)
message_analyzer = MessageAnalyzer(context, config_manager, bot_manager)
report_generator = ReportGenerator(config_manager)
auto_scheduler = AutoScheduler(
config_manager,
message_analyzer.message_handler,
message_analyzer,
report_generator,
bot_manager,
self.html_render # 传入html_render函数
)
@@ -62,44 +67,22 @@ class QQGroupDailyAnalysis(Star):
# 等待10秒让系统完全初始化
await asyncio.sleep(10)
# 尝试获取bot实例
bot_instance = await self._get_bot_instance()
if bot_instance:
auto_scheduler.set_bot_instance(bot_instance)
logger.info("已为自动调度器设置bot实例")
else:
logger.info("暂时未获取到bot实例,定时任务仍会启动")
# 初始化bot管理器
if await bot_manager.initialize_from_config():
logger.info("Bot管理器初始化成功,启用自动分析功能")
# 启动调度器
await auto_scheduler.start_scheduler()
# 启动调度器
await auto_scheduler.start_scheduler()
else:
logger.warning("Bot管理器初始化失败,无法启用自动分析功能")
status = bot_manager.get_status_info()
logger.info(f"Bot管理器状态: {status}")
except Exception as e:
logger.error(f"延迟启动调度器失败: {e}")
async def _get_bot_instance(self):
"""从Context获取bot实例"""
try:
# 简化的获取逻辑,尝试常见的几种方式
if hasattr(self.context, 'get_platforms') and callable(self.context.get_platforms):
platforms = self.context.get_platforms()
for platform in platforms:
if hasattr(platform, 'bot') and platform.bot:
logger.info(f"从平台获取到bot实例")
return platform.bot
# 尝试从context的platforms属性获取
if hasattr(self.context, 'platforms') and self.context.platforms:
for platform in self.context.platforms:
if hasattr(platform, 'bot') and platform.bot:
logger.info(f"从平台列表获取到bot实例")
return platform.bot
logger.info("暂时无法获取bot实例")
return None
except Exception as e:
logger.error(f"获取bot实例失败: {e}")
return None
async def _reload_config_and_restart_scheduler(self):
"""重新加载配置并重启调度器"""
@@ -131,9 +114,8 @@ class QQGroupDailyAnalysis(Star):
yield event.plain_result("❌ 请在群聊中使用此命令")
return
# 设置bot实例
auto_scheduler.set_bot_instance(event.bot)
await message_analyzer.set_bot_instance(event.bot)
# 更新bot实例(用于手动命令)
bot_manager.update_from_event(event)
# 检查群组权限
enabled_groups = config_manager.get_enabled_groups()
@@ -151,7 +133,7 @@ class QQGroupDailyAnalysis(Star):
try:
# 获取群聊消息
messages = await message_analyzer.message_handler.fetch_group_messages(event.bot, group_id, analysis_days)
messages = await message_analyzer.message_handler.fetch_group_messages(bot_manager.get_bot_instance(), group_id, analysis_days)
if not messages:
yield event.plain_result("❌ 未找到足够的群聊记录,请确保群内有足够的消息历史")
return
@@ -338,8 +320,8 @@ class QQGroupDailyAnalysis(Star):
yield event.plain_result("🧪 开始测试自动分析功能...")
# 设置bot实例
auto_scheduler.set_bot_instance(event.bot)
# 更新bot实例(用于测试)
bot_manager.update_from_event(event)
# 执行自动分析
try:
+164
View File
@@ -0,0 +1,164 @@
"""
Bot实例管理模块
统一管理bot实例的获取、设置和使用
"""
from typing import Optional, Dict, Any
from astrbot.api import logger
class BotManager:
"""Bot实例管理器 - 统一管理所有bot相关操作"""
def __init__(self, config_manager):
self.config_manager = config_manager
self._bot_instance = None
self._bot_qq_id = None
self._context = None
self._is_initialized = False
def set_context(self, context):
"""设置AstrBot上下文"""
self._context = context
def set_bot_instance(self, bot_instance):
"""设置bot实例"""
if bot_instance:
self._bot_instance = bot_instance
try:
logger.info(f"Bot实例已设置: {type(bot_instance).__name__}")
except ImportError:
print(f"Bot实例已设置: {type(bot_instance).__name__}")
else:
try:
logger.warning("尝试设置空的bot实例")
except ImportError:
print("尝试设置空的bot实例")
def set_bot_qq_id(self, bot_qq_id: str):
"""设置bot QQ号"""
if bot_qq_id:
self._bot_qq_id = str(bot_qq_id)
try:
logger.info(f"Bot QQ号已设置: {self._bot_qq_id}")
except ImportError:
print(f"Bot QQ号已设置: {self._bot_qq_id}")
else:
try:
logger.warning("尝试设置空的bot QQ号")
except ImportError:
print("尝试设置空的bot QQ号")
def get_bot_instance(self):
"""获取当前bot实例"""
return self._bot_instance
def has_bot_instance(self) -> bool:
"""检查是否有可用的bot实例"""
return self._bot_instance is not None
def has_bot_qq_id(self) -> bool:
"""检查是否有配置的bot QQ号"""
return self._bot_qq_id is not None
def is_ready_for_auto_analysis(self) -> bool:
"""检查是否准备好进行自动分析"""
return self.has_bot_instance() and self.has_bot_qq_id()
def is_ready_for_manual_analysis(self) -> bool:
"""检查是否准备好进行手动分析"""
return self.has_bot_instance()
async def auto_discover_bot_instance(self) -> Optional[Any]:
"""自动发现可用的bot实例"""
try:
if not self._context:
logger.warning("未设置AstrBot上下文,无法自动发现bot实例")
return None
# 通过platform_manager获取平台实例
if hasattr(self._context, 'platform_manager') and hasattr(self._context.platform_manager, 'platform_insts'):
platforms = self._context.platform_manager.platform_insts
for platform in platforms:
# 对于aiocqhttp适配器,bot实例在get_client()方法中
if hasattr(platform, 'get_client'):
bot_client = platform.get_client()
if bot_client:
logger.info(f"自动发现bot实例: {type(bot_client).__name__}")
self.set_bot_instance(bot_client)
return bot_client
# 也检查是否直接有bot属性
elif hasattr(platform, 'bot') and platform.bot:
logger.info(f"自动发现bot实例: {type(platform.bot).__name__}")
self.set_bot_instance(platform.bot)
return platform.bot
logger.warning("未找到可用的bot实例")
return None
except Exception as e:
logger.error(f"自动发现bot实例失败: {e}")
return None
async def initialize_from_config(self) -> bool:
"""从配置初始化bot管理器"""
try:
# 获取配置的bot QQ号
bot_qq_id = self.config_manager.get_bot_qq_id()
if bot_qq_id:
self.set_bot_qq_id(bot_qq_id)
else:
logger.warning("配置中未找到bot QQ号")
# 自动发现bot实例
await self.auto_discover_bot_instance()
self._is_initialized = True
if self.is_ready_for_auto_analysis():
logger.info("Bot管理器初始化完成,可进行自动分析")
return True
elif self.has_bot_instance():
logger.info("Bot管理器初始化完成,可进行手动分析")
return True
else:
logger.warning("Bot管理器初始化完成,但功能受限")
return False
except Exception as e:
logger.error(f"Bot管理器初始化失败: {e}")
return False
def get_status_info(self) -> Dict[str, Any]:
"""获取bot管理器状态信息"""
return {
"has_bot_instance": self.has_bot_instance(),
"has_bot_qq_id": self.has_bot_qq_id(),
"bot_qq_id": self._bot_qq_id,
"bot_instance_type": type(self._bot_instance).__name__ if self._bot_instance else None,
"ready_for_auto_analysis": self.is_ready_for_auto_analysis(),
"ready_for_manual_analysis": self.is_ready_for_manual_analysis(),
"is_initialized": self._is_initialized
}
def update_from_event(self, event):
"""从事件更新bot实例(用于手动命令)"""
if hasattr(event, 'bot') and event.bot:
self.set_bot_instance(event.bot)
return True
return False
def validate_for_message_fetching(self, group_id: str) -> tuple[bool, str]:
"""验证是否可以进行消息获取"""
if not self.has_bot_instance():
return False, f"{group_id}: 没有可用的bot实例"
if not group_id:
return False, "无效的群组ID"
return True, "验证通过"
def should_filter_bot_message(self, sender_id: str) -> bool:
"""判断是否应该过滤bot自己的消息"""
if not self._bot_qq_id:
return False
return str(sender_id) == self._bot_qq_id
+4
View File
@@ -97,6 +97,10 @@ class ConfigManager:
def get_pdf_output_dir(self) -> str:
"""获取PDF输出目录"""
return self.config.get("pdf_output_dir", "data/plugins/astrbot-qq-group-daily-analysis/reports")
def get_bot_qq_id(self) -> str:
"""获取bot QQ号"""
return str(self.config.get("bot_qq_id", ""))
def get_pdf_filename_format(self) -> str:
"""获取PDF文件名格式"""
+26 -13
View File
@@ -15,27 +15,40 @@ from ...src.visualization.activity_charts import ActivityVisualizer
class MessageHandler:
"""消息处理器"""
def __init__(self, config_manager):
def __init__(self, config_manager, bot_manager=None):
self.config_manager = config_manager
self.activity_visualizer = ActivityVisualizer()
self.bot_qq_id = None
self.bot_manager = bot_manager
async def set_bot_qq_id(self, bot_instance):
"""设置机器人QQ号"""
async def set_bot_qq_id(self, bot_qq_id: str):
"""设置机器人QQ号(保持向后兼容)"""
try:
if bot_instance and not self.bot_qq_id:
login_info = await bot_instance.api.call_action("get_login_info")
self.bot_qq_id = str(login_info.get("user_id", ""))
logger.info(f"获取到机器人QQ号: {self.bot_qq_id}")
if self.bot_manager:
self.bot_manager.set_bot_qq_id(bot_qq_id)
logger.info(f"设置机器人QQ号: {bot_qq_id}")
except Exception as e:
logger.error(f"获取机器人QQ号失败: {e}")
logger.error(f"设置机器人QQ号失败: {e}")
def set_bot_manager(self, bot_manager):
"""设置bot管理器"""
self.bot_manager = bot_manager
async def fetch_group_messages(self, bot_instance, group_id: str, days: int) -> List[Dict]:
"""获取群聊消息记录"""
try:
if not bot_instance or not group_id:
logger.error(f"{group_id} 无效的客户端或群组ID")
return []
# 验证参数
if self.bot_manager:
is_valid, error_msg = self.bot_manager.validate_for_message_fetching(group_id)
if not is_valid:
logger.error(error_msg)
return []
else:
if not group_id:
logger.error(f"{group_id} 无效的群组ID")
return []
if not bot_instance:
logger.info(f"{group_id} 自动分析未获取到 bot 实例,跳过 Bot 消息获取")
return []
# 计算时间范围
end_time = datetime.now()
@@ -90,7 +103,7 @@ class MessageHandler:
# 过滤掉机器人自己的消息
sender_id = str(msg.get("sender", {}).get("user_id", ""))
if self.bot_qq_id and sender_id == self.bot_qq_id:
if self.bot_manager and self.bot_manager.should_filter_bot_message(sender_id):
continue
if msg_time >= start_time and msg_time <= end_time:
+16 -9
View File
@@ -12,21 +12,23 @@ from astrbot.api import logger
class AutoScheduler:
"""自动调度器"""
def __init__(self, config_manager, message_handler, analyzer, report_generator, html_render_func=None):
def __init__(self, config_manager, message_handler, analyzer, report_generator, bot_manager, html_render_func=None):
self.config_manager = config_manager
self.message_handler = message_handler
self.analyzer = analyzer
self.report_generator = report_generator
self.bot_manager = bot_manager
self.html_render_func = html_render_func
self.scheduler_task = None
self.bot_instance = None
self.last_execution_date = None # 记录上次执行日期,防止重复执行
def set_bot_instance(self, bot_instance):
"""设置bot实例"""
self.bot_instance = bot_instance
# 同时设置消息处理器的bot实例
asyncio.create_task(self.message_handler.set_bot_qq_id(bot_instance))
"""设置bot实例(保持向后兼容)"""
self.bot_manager.set_bot_instance(bot_instance)
def set_bot_qq_id(self, bot_qq_id: str):
"""设置bot QQ号(保持向后兼容)"""
self.bot_manager.set_bot_qq_id(bot_qq_id)
async def start_scheduler(self):
"""启动定时任务调度器"""
@@ -116,15 +118,20 @@ class AutoScheduler:
async def _perform_auto_analysis_for_group(self, group_id: str):
"""为指定群执行自动分析"""
try:
if not self.bot_instance:
logger.warning(f"{group_id} 自动分析跳过:未获取到bot实例")
# 检查bot管理器状态
if not self.bot_manager.is_ready_for_auto_analysis():
status = self.bot_manager.get_status_info()
logger.warning(f"{group_id} 自动分析跳过:bot管理器未就绪 - {status}")
return
logger.info(f"开始为群 {group_id} 执行自动分析")
# 获取群聊消息
analysis_days = self.config_manager.get_analysis_days()
messages = await self.message_handler.fetch_group_messages(self.bot_instance, group_id, analysis_days)
bot_instance = self.bot_manager.get_bot_instance()
messages = await self.message_handler.fetch_group_messages(bot_instance, group_id, analysis_days)
if not messages:
logger.warning(f"{group_id} 未获取到足够的消息记录")
return
+8 -4
View File
@@ -13,16 +13,20 @@ from ...src.analysis.statistics import UserAnalyzer
class MessageAnalyzer:
"""消息分析器 - 整合所有分析功能"""
def __init__(self, context, config_manager):
def __init__(self, context, config_manager, bot_manager=None):
self.context = context
self.config_manager = config_manager
self.message_handler = MessageHandler(config_manager)
self.bot_manager = bot_manager
self.message_handler = MessageHandler(config_manager, bot_manager)
self.llm_analyzer = LLMAnalyzer(context, config_manager)
self.user_analyzer = UserAnalyzer(config_manager)
async def set_bot_instance(self, bot_instance):
"""设置bot实例"""
await self.message_handler.set_bot_qq_id(bot_instance)
"""设置bot实例(保持向后兼容)"""
if self.bot_manager:
self.bot_manager.set_bot_instance(bot_instance)
else:
await self.message_handler.set_bot_qq_id(bot_instance)
async def analyze_messages(self, messages: List[Dict], group_id: str) -> Dict:
"""完整的消息分析流程"""