mirror of
https://github.com/Nezumi-2711/astrbot_plugin_qq_group_daily_analysis.git
synced 2026-09-22 20:01:04 +00:00
[fix] (bot-instance) 尝试采用手动配置QQ号的方式,用于定时自动分析
This commit is contained in:
@@ -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输出目录",
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
@@ -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
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
"""完整的消息分析流程"""
|
||||
|
||||
Reference in New Issue
Block a user