mirror of
https://github.com/Nezumi-2711/astrbot_plugin_qq_group_daily_analysis.git
synced 2026-09-22 13:38:43 +00:00
refactor: 全局变量重构,受保护成员访问修复,同步 I/O 操作异步化
This commit is contained in:
@@ -25,43 +25,31 @@ from .src.scheduler.auto_scheduler import AutoScheduler
|
||||
from .src.utils.helpers import MessageAnalyzer
|
||||
from .src.utils.pdf_utils import PDFInstaller
|
||||
|
||||
# 全局变量
|
||||
config_manager = None
|
||||
bot_manager = None
|
||||
message_analyzer = None
|
||||
report_generator = None
|
||||
auto_scheduler = None
|
||||
|
||||
|
||||
class QQGroupDailyAnalysis(Star):
|
||||
def __init__(self, context: Context, config: AstrBotConfig):
|
||||
super().__init__(context)
|
||||
self.config = config
|
||||
|
||||
# 初始化模块化组件
|
||||
global \
|
||||
config_manager, \
|
||||
bot_manager, \
|
||||
message_analyzer, \
|
||||
report_generator, \
|
||||
auto_scheduler
|
||||
|
||||
config_manager = ConfigManager(config)
|
||||
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.config_manager = ConfigManager(config)
|
||||
self.bot_manager = BotManager(self.config_manager)
|
||||
self.bot_manager.set_context(context)
|
||||
self.message_analyzer = MessageAnalyzer(
|
||||
context, self.config_manager, self.bot_manager
|
||||
)
|
||||
self.report_generator = ReportGenerator(self.config_manager)
|
||||
self.auto_scheduler = AutoScheduler(
|
||||
self.config_manager,
|
||||
self.message_analyzer.message_handler,
|
||||
self.message_analyzer,
|
||||
self.report_generator,
|
||||
self.bot_manager,
|
||||
self.html_render, # 传入html_render函数
|
||||
)
|
||||
|
||||
# 延迟启动自动调度器,给系统时间初始化
|
||||
if config_manager.get_enable_auto_analysis():
|
||||
if self.config_manager.get_enable_auto_analysis():
|
||||
asyncio.create_task(self._delayed_start_scheduler())
|
||||
|
||||
logger.info("QQ群日常分析插件已初始化(模块化版本)")
|
||||
@@ -69,11 +57,11 @@ class QQGroupDailyAnalysis(Star):
|
||||
async def _delayed_start_scheduler(self):
|
||||
"""延迟启动调度器,给系统时间初始化"""
|
||||
try:
|
||||
# 等待10秒让系统完全初始化
|
||||
# 等待30秒让系统完全初始化
|
||||
await asyncio.sleep(30)
|
||||
|
||||
# 初始化所有bot实例
|
||||
discovered = await bot_manager.initialize_from_config()
|
||||
discovered = await self.bot_manager.initialize_from_config()
|
||||
if discovered:
|
||||
platform_count = len(discovered)
|
||||
logger.info(f"Bot管理器初始化成功,发现 {platform_count} 个适配器")
|
||||
@@ -83,10 +71,10 @@ class QQGroupDailyAnalysis(Star):
|
||||
)
|
||||
|
||||
# 启动调度器
|
||||
await auto_scheduler.start_scheduler()
|
||||
await self.auto_scheduler.start_scheduler()
|
||||
else:
|
||||
logger.warning("Bot管理器初始化失败,未发现任何适配器")
|
||||
status = bot_manager.get_status_info()
|
||||
status = self.bot_manager.get_status_info()
|
||||
logger.info(f"Bot管理器状态: {status}")
|
||||
|
||||
except Exception as e:
|
||||
@@ -97,40 +85,18 @@ class QQGroupDailyAnalysis(Star):
|
||||
try:
|
||||
logger.info("开始清理QQ群日常分析插件资源...")
|
||||
|
||||
global \
|
||||
auto_scheduler, \
|
||||
bot_manager, \
|
||||
message_analyzer, \
|
||||
report_generator, \
|
||||
config_manager
|
||||
|
||||
# 停止自动调度器
|
||||
if auto_scheduler:
|
||||
if self.auto_scheduler:
|
||||
logger.info("正在停止自动调度器...")
|
||||
await auto_scheduler.stop_scheduler()
|
||||
await self.auto_scheduler.stop_scheduler()
|
||||
logger.info("自动调度器已停止")
|
||||
|
||||
# 清理bot管理器资源
|
||||
# if bot_manager:
|
||||
# logger.info("正在清理bot管理器资源...")
|
||||
# # 如果有其他需要清理的资源,可以在这里添加
|
||||
|
||||
# # 清理消息分析器资源
|
||||
# if message_analyzer:
|
||||
# logger.info("正在清理消息分析器资源...")
|
||||
# # 如果有其他需要清理的资源,可以在这里添加
|
||||
|
||||
# # 清理报告生成器资源
|
||||
# if report_generator:
|
||||
# logger.info("正在清理报告生成器资源...")
|
||||
# # 如果有其他需要清理的资源,可以在这里添加
|
||||
|
||||
# 重置全局变量
|
||||
auto_scheduler = None
|
||||
bot_manager = None
|
||||
message_analyzer = None
|
||||
report_generator = None
|
||||
config_manager = None
|
||||
# 重置实例属性
|
||||
self.auto_scheduler = None
|
||||
self.bot_manager = None
|
||||
self.message_analyzer = None
|
||||
self.report_generator = None
|
||||
self.config_manager = None
|
||||
|
||||
logger.info("QQ群日常分析插件资源清理完成")
|
||||
|
||||
@@ -156,27 +122,27 @@ class QQGroupDailyAnalysis(Star):
|
||||
return
|
||||
|
||||
# 更新bot实例(用于手动命令)
|
||||
bot_manager.update_from_event(event)
|
||||
self.bot_manager.update_from_event(event)
|
||||
|
||||
# 检查群组权限
|
||||
if not config_manager.is_group_allowed(group_id):
|
||||
if not self.config_manager.is_group_allowed(group_id):
|
||||
yield event.plain_result("❌ 此群未启用日常分析功能")
|
||||
return
|
||||
|
||||
# 设置分析天数
|
||||
analysis_days = (
|
||||
days if days and 1 <= days <= 7 else config_manager.get_analysis_days()
|
||||
days if days and 1 <= days <= 7 else self.config_manager.get_analysis_days()
|
||||
)
|
||||
|
||||
yield event.plain_result(f"🔍 开始分析群聊近{analysis_days}天的活动,请稍候...")
|
||||
|
||||
# 调试:输出当前配置
|
||||
logger.info(f"当前输出格式配置: {config_manager.get_output_format()}")
|
||||
logger.info(f"当前输出格式配置: {self.config_manager.get_output_format()}")
|
||||
|
||||
try:
|
||||
# 获取该群对应的平台ID和bot实例
|
||||
platform_id = await auto_scheduler._get_platform_id_for_group(group_id)
|
||||
bot_instance = bot_manager.get_bot_instance(platform_id)
|
||||
platform_id = await self.auto_scheduler.get_platform_id_for_group(group_id)
|
||||
bot_instance = self.bot_manager.get_bot_instance(platform_id)
|
||||
|
||||
if not bot_instance:
|
||||
yield event.plain_result(
|
||||
@@ -185,7 +151,7 @@ class QQGroupDailyAnalysis(Star):
|
||||
return
|
||||
|
||||
# 获取群聊消息
|
||||
messages = await message_analyzer.message_handler.fetch_group_messages(
|
||||
messages = await self.message_analyzer.message_handler.fetch_group_messages(
|
||||
bot_instance, group_id, analysis_days, platform_id
|
||||
)
|
||||
if not messages:
|
||||
@@ -195,7 +161,7 @@ class QQGroupDailyAnalysis(Star):
|
||||
return
|
||||
|
||||
# 检查消息数量是否足够分析
|
||||
min_threshold = config_manager.get_min_messages_threshold()
|
||||
min_threshold = self.config_manager.get_min_messages_threshold()
|
||||
if len(messages) < min_threshold:
|
||||
yield event.plain_result(
|
||||
f"❌ 消息数量不足({len(messages)}条),至少需要{min_threshold}条消息才能进行有效分析"
|
||||
@@ -207,7 +173,7 @@ class QQGroupDailyAnalysis(Star):
|
||||
)
|
||||
|
||||
# 进行分析 - 传递 unified_msg_origin 以获取正确的 LLM 提供商
|
||||
analysis_result = await message_analyzer.analyze_messages(
|
||||
analysis_result = await self.message_analyzer.analyze_messages(
|
||||
messages, group_id, event.unified_msg_origin
|
||||
)
|
||||
|
||||
@@ -217,9 +183,9 @@ class QQGroupDailyAnalysis(Star):
|
||||
return
|
||||
|
||||
# 生成报告
|
||||
output_format = config_manager.get_output_format()
|
||||
output_format = self.config_manager.get_output_format()
|
||||
if output_format == "image":
|
||||
image_url = await report_generator.generate_image_report(
|
||||
image_url = await self.report_generator.generate_image_report(
|
||||
analysis_result, group_id, self.html_render
|
||||
)
|
||||
if image_url:
|
||||
@@ -227,18 +193,20 @@ class QQGroupDailyAnalysis(Star):
|
||||
else:
|
||||
# 如果图片生成失败,回退到文本报告
|
||||
logger.warning("图片报告生成失败,回退到文本报告")
|
||||
text_report = report_generator.generate_text_report(analysis_result)
|
||||
text_report = self.report_generator.generate_text_report(
|
||||
analysis_result
|
||||
)
|
||||
yield event.plain_result(
|
||||
f"⚠️ 图片报告生成失败,以下是文本版本:\n\n{text_report}"
|
||||
)
|
||||
elif output_format == "pdf":
|
||||
if not config_manager.pyppeteer_available:
|
||||
if not self.config_manager.pyppeteer_available:
|
||||
yield event.plain_result(
|
||||
"❌ PDF 功能不可用,请使用 /安装PDF 命令安装 pyppeteer==1.0.2"
|
||||
)
|
||||
return
|
||||
|
||||
pdf_path = await report_generator.generate_pdf_report(
|
||||
pdf_path = await self.report_generator.generate_pdf_report(
|
||||
analysis_result, group_id
|
||||
)
|
||||
if pdf_path:
|
||||
@@ -259,12 +227,16 @@ class QQGroupDailyAnalysis(Star):
|
||||
|
||||
# 回退到文本报告
|
||||
logger.warning("PDF 报告生成失败,回退到文本报告")
|
||||
text_report = report_generator.generate_text_report(analysis_result)
|
||||
text_report = self.report_generator.generate_text_report(
|
||||
analysis_result
|
||||
)
|
||||
yield event.plain_result(
|
||||
f"\n📝 以下是文本版本的分析报告:\n\n{text_report}"
|
||||
)
|
||||
else:
|
||||
text_report = report_generator.generate_text_report(analysis_result)
|
||||
text_report = self.report_generator.generate_text_report(
|
||||
analysis_result
|
||||
)
|
||||
yield event.plain_result(text_report)
|
||||
|
||||
except Exception as e:
|
||||
@@ -292,9 +264,11 @@ class QQGroupDailyAnalysis(Star):
|
||||
return
|
||||
|
||||
if not format_type:
|
||||
current_format = config_manager.get_output_format()
|
||||
current_format = self.config_manager.get_output_format()
|
||||
pdf_status = (
|
||||
"✅" if config_manager.pyppeteer_available else "❌ (需安装 pyppeteer)"
|
||||
"✅"
|
||||
if self.config_manager.pyppeteer_available
|
||||
else "❌ (需安装 pyppeteer)"
|
||||
)
|
||||
yield event.plain_result(f"""📊 当前输出格式: {current_format}
|
||||
|
||||
@@ -311,13 +285,13 @@ class QQGroupDailyAnalysis(Star):
|
||||
yield event.plain_result("❌ 无效的格式类型,支持: image, text, pdf")
|
||||
return
|
||||
|
||||
if format_type == "pdf" and not config_manager.pyppeteer_available:
|
||||
if format_type == "pdf" and not self.config_manager.pyppeteer_available:
|
||||
yield event.plain_result(
|
||||
"❌ PDF 格式不可用,请使用 /安装PDF 命令安装 pyppeteer==1.0.2"
|
||||
)
|
||||
return
|
||||
|
||||
config_manager.set_output_format(format_type)
|
||||
self.config_manager.set_output_format(format_type)
|
||||
yield event.plain_result(f"✅ 输出格式已设置为: {format_type}")
|
||||
|
||||
@filter.command("设置模板")
|
||||
@@ -335,23 +309,27 @@ class QQGroupDailyAnalysis(Star):
|
||||
|
||||
import os
|
||||
|
||||
# 获取模板目录和可用模板列表
|
||||
# 获取模板目录和可用模板列表(使用 asyncio.to_thread 避免阻塞)
|
||||
template_base_dir = os.path.join(
|
||||
os.path.dirname(__file__), "src", "reports", "templates"
|
||||
)
|
||||
available_templates = []
|
||||
if os.path.exists(template_base_dir):
|
||||
available_templates = sorted(
|
||||
[
|
||||
d
|
||||
for d in os.listdir(template_base_dir)
|
||||
if os.path.isdir(os.path.join(template_base_dir, d))
|
||||
and not d.startswith("__")
|
||||
]
|
||||
)
|
||||
|
||||
def _list_templates_sync():
|
||||
if os.path.exists(template_base_dir):
|
||||
return sorted(
|
||||
[
|
||||
d
|
||||
for d in os.listdir(template_base_dir)
|
||||
if os.path.isdir(os.path.join(template_base_dir, d))
|
||||
and not d.startswith("__")
|
||||
]
|
||||
)
|
||||
return []
|
||||
|
||||
available_templates = await asyncio.to_thread(_list_templates_sync)
|
||||
|
||||
if not template_input:
|
||||
current_template = config_manager.get_report_template()
|
||||
current_template = self.config_manager.get_report_template()
|
||||
# 列出可用的模板(带序号)
|
||||
template_list_str = "\n".join(
|
||||
[f"【{i}】{t}" for i, t in enumerate(available_templates, start=1)]
|
||||
@@ -377,13 +355,14 @@ class QQGroupDailyAnalysis(Star):
|
||||
)
|
||||
return
|
||||
|
||||
# 检查模板是否存在
|
||||
# 检查模板是否存在(使用 asyncio.to_thread 避免阻塞)
|
||||
template_dir = os.path.join(template_base_dir, template_name)
|
||||
if not os.path.exists(template_dir):
|
||||
template_exists = await asyncio.to_thread(os.path.exists, template_dir)
|
||||
if not template_exists:
|
||||
yield event.plain_result(f"❌ 模板 '{template_name}' 不存在")
|
||||
return
|
||||
|
||||
config_manager.set_report_template(template_name)
|
||||
self.config_manager.set_report_template(template_name)
|
||||
yield event.plain_result(f"✅ 报告模板已设置为: {template_name}")
|
||||
|
||||
@filter.command("查看模板")
|
||||
@@ -406,24 +385,27 @@ class QQGroupDailyAnalysis(Star):
|
||||
)
|
||||
assets_dir = os.path.join(os.path.dirname(__file__), "assets")
|
||||
|
||||
# 获取可用模板列表
|
||||
available_templates = []
|
||||
if os.path.exists(template_dir):
|
||||
available_templates = sorted(
|
||||
[
|
||||
d
|
||||
for d in os.listdir(template_dir)
|
||||
if os.path.isdir(os.path.join(template_dir, d))
|
||||
and not d.startswith("__")
|
||||
]
|
||||
)
|
||||
# 获取可用模板列表(使用 asyncio.to_thread 避免阻塞)
|
||||
def _list_templates_sync():
|
||||
if os.path.exists(template_dir):
|
||||
return sorted(
|
||||
[
|
||||
d
|
||||
for d in os.listdir(template_dir)
|
||||
if os.path.isdir(os.path.join(template_dir, d))
|
||||
and not d.startswith("__")
|
||||
]
|
||||
)
|
||||
return []
|
||||
|
||||
available_templates = await asyncio.to_thread(_list_templates_sync)
|
||||
|
||||
if not available_templates:
|
||||
yield event.plain_result("❌ 未找到任何可用的报告模板")
|
||||
return
|
||||
|
||||
# 获取当前使用的模板
|
||||
current_template = config_manager.get_report_template()
|
||||
current_template = self.config_manager.get_report_template()
|
||||
|
||||
# 获取机器人信息用于合并转发消息
|
||||
bot_id = event.get_self_id()
|
||||
@@ -483,7 +465,7 @@ class QQGroupDailyAnalysis(Star):
|
||||
|
||||
try:
|
||||
# 安装 pyppeteer
|
||||
result = await PDFInstaller.install_pyppeteer(config_manager)
|
||||
result = await PDFInstaller.install_pyppeteer(self.config_manager)
|
||||
yield event.plain_result(result)
|
||||
|
||||
# 提供系统依赖安装指导
|
||||
@@ -518,50 +500,50 @@ class QQGroupDailyAnalysis(Star):
|
||||
return
|
||||
|
||||
if action == "enable":
|
||||
mode = config_manager.get_group_list_mode()
|
||||
mode = self.config_manager.get_group_list_mode()
|
||||
if mode == "whitelist":
|
||||
glist = config_manager.get_group_list()
|
||||
glist = self.config_manager.get_group_list()
|
||||
if group_id not in glist:
|
||||
glist.append(group_id)
|
||||
config_manager.set_group_list(glist)
|
||||
self.config_manager.set_group_list(glist)
|
||||
yield event.plain_result("✅ 已将当前群加入白名单")
|
||||
# 重新启动定时任务
|
||||
await auto_scheduler.restart_scheduler()
|
||||
await self.auto_scheduler.restart_scheduler()
|
||||
else:
|
||||
yield event.plain_result("ℹ️ 当前群已在白名单中")
|
||||
elif mode == "blacklist":
|
||||
glist = config_manager.get_group_list()
|
||||
glist = self.config_manager.get_group_list()
|
||||
if group_id in glist:
|
||||
glist.remove(group_id)
|
||||
config_manager.set_group_list(glist)
|
||||
self.config_manager.set_group_list(glist)
|
||||
yield event.plain_result("✅ 已将当前群从黑名单移除")
|
||||
# 重新启动定时任务
|
||||
await auto_scheduler.restart_scheduler()
|
||||
await self.auto_scheduler.restart_scheduler()
|
||||
else:
|
||||
yield event.plain_result("ℹ️ 当前群不在黑名单中")
|
||||
else:
|
||||
yield event.plain_result("ℹ️ 当前为无限制模式,所有群聊默认启用")
|
||||
|
||||
elif action == "disable":
|
||||
mode = config_manager.get_group_list_mode()
|
||||
mode = self.config_manager.get_group_list_mode()
|
||||
if mode == "whitelist":
|
||||
glist = config_manager.get_group_list()
|
||||
glist = self.config_manager.get_group_list()
|
||||
if group_id in glist:
|
||||
glist.remove(group_id)
|
||||
config_manager.set_group_list(glist)
|
||||
self.config_manager.set_group_list(glist)
|
||||
yield event.plain_result("✅ 已将当前群从白名单移除")
|
||||
# 重新启动定时任务
|
||||
await auto_scheduler.restart_scheduler()
|
||||
await self.auto_scheduler.restart_scheduler()
|
||||
else:
|
||||
yield event.plain_result("ℹ️ 当前群不在白名单中")
|
||||
elif mode == "blacklist":
|
||||
glist = config_manager.get_group_list()
|
||||
glist = self.config_manager.get_group_list()
|
||||
if group_id not in glist:
|
||||
glist.append(group_id)
|
||||
config_manager.set_group_list(glist)
|
||||
self.config_manager.set_group_list(glist)
|
||||
yield event.plain_result("✅ 已将当前群加入黑名单")
|
||||
# 重新启动定时任务
|
||||
await auto_scheduler.restart_scheduler()
|
||||
await self.auto_scheduler.restart_scheduler()
|
||||
else:
|
||||
yield event.plain_result("ℹ️ 当前群已在黑名单中")
|
||||
else:
|
||||
@@ -571,40 +553,40 @@ class QQGroupDailyAnalysis(Star):
|
||||
|
||||
elif action == "reload":
|
||||
# 重新启动定时任务
|
||||
await auto_scheduler.restart_scheduler()
|
||||
await self.auto_scheduler.restart_scheduler()
|
||||
yield event.plain_result("✅ 已重新加载配置并重启定时任务")
|
||||
|
||||
elif action == "test":
|
||||
# 测试自动分析功能
|
||||
if not config_manager.is_group_allowed(group_id):
|
||||
if not self.config_manager.is_group_allowed(group_id):
|
||||
yield event.plain_result("❌ 请先启用当前群的分析功能")
|
||||
return
|
||||
|
||||
yield event.plain_result("🧪 开始测试自动分析功能...")
|
||||
|
||||
# 更新bot实例(用于测试)
|
||||
bot_manager.update_from_event(event)
|
||||
self.bot_manager.update_from_event(event)
|
||||
|
||||
# 执行自动分析
|
||||
try:
|
||||
await auto_scheduler._perform_auto_analysis_for_group(group_id)
|
||||
await self.auto_scheduler._perform_auto_analysis_for_group(group_id)
|
||||
yield event.plain_result("✅ 自动分析测试完成,请查看群消息")
|
||||
except Exception as e:
|
||||
yield event.plain_result(f"❌ 自动分析测试失败: {str(e)}")
|
||||
|
||||
else: # status
|
||||
is_allowed = config_manager.is_group_allowed(group_id)
|
||||
is_allowed = self.config_manager.is_group_allowed(group_id)
|
||||
status = "已启用" if is_allowed else "未启用"
|
||||
mode = config_manager.get_group_list_mode()
|
||||
mode = self.config_manager.get_group_list_mode()
|
||||
|
||||
auto_status = (
|
||||
"已启用" if config_manager.get_enable_auto_analysis() else "未启用"
|
||||
"已启用" if self.config_manager.get_enable_auto_analysis() else "未启用"
|
||||
)
|
||||
auto_time = config_manager.get_auto_analysis_time()
|
||||
auto_time = self.config_manager.get_auto_analysis_time()
|
||||
|
||||
pdf_status = PDFInstaller.get_pdf_status(config_manager)
|
||||
output_format = config_manager.get_output_format()
|
||||
min_threshold = config_manager.get_min_messages_threshold()
|
||||
pdf_status = PDFInstaller.get_pdf_status(self.config_manager)
|
||||
output_format = self.config_manager.get_output_format()
|
||||
min_threshold = self.config_manager.get_min_messages_threshold()
|
||||
|
||||
yield event.plain_result(f"""📊 当前群分析功能状态:
|
||||
• 群分析功能: {status} (模式: {mode})
|
||||
|
||||
@@ -32,10 +32,9 @@ class ReportGenerator:
|
||||
# 准备渲染数据
|
||||
render_payload = await self._prepare_render_data(analysis_result)
|
||||
|
||||
# 先渲染HTML模板
|
||||
html_content = self._render_html_template(
|
||||
self.html_templates.get_image_template(), render_payload
|
||||
)
|
||||
# 先渲染HTML模板(使用异步方法)
|
||||
image_template = await self.html_templates.get_image_template_async()
|
||||
html_content = self._render_html_template(image_template, render_payload)
|
||||
|
||||
# 检查HTML内容是否有效
|
||||
if not html_content:
|
||||
@@ -88,9 +87,9 @@ class ReportGenerator:
|
||||
) -> str | None:
|
||||
"""生成PDF格式的分析报告"""
|
||||
try:
|
||||
# 确保输出目录存在
|
||||
# 确保输出目录存在(使用 asyncio.to_thread 避免阻塞)
|
||||
output_dir = Path(self.config_manager.get_pdf_output_dir())
|
||||
output_dir.mkdir(parents=True, exist_ok=True)
|
||||
await asyncio.to_thread(output_dir.mkdir, parents=True, exist_ok=True)
|
||||
|
||||
# 生成文件名
|
||||
current_date = datetime.now().strftime("%Y%m%d")
|
||||
@@ -103,10 +102,9 @@ class ReportGenerator:
|
||||
render_data = await self._prepare_render_data(analysis_result)
|
||||
logger.info(f"PDF 渲染数据准备完成,包含 {len(render_data)} 个字段")
|
||||
|
||||
# 生成 HTML 内容(PDF模板使用{{}}占位符)
|
||||
html_content = self._render_html_template(
|
||||
self.html_templates.get_pdf_template(), render_data
|
||||
)
|
||||
# 生成 HTML 内容(使用异步方法)
|
||||
pdf_template = await self.html_templates.get_pdf_template_async()
|
||||
html_content = self._render_html_template(pdf_template, render_data)
|
||||
|
||||
# 检查HTML内容是否有效
|
||||
if not html_content:
|
||||
|
||||
+48
-18
@@ -3,6 +3,7 @@ HTML模板模块
|
||||
使用Jinja2加载外部HTML模板文件
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import os
|
||||
|
||||
from jinja2 import Environment, FileSystemLoader, select_autoescape
|
||||
@@ -21,12 +22,11 @@ class HTMLTemplates:
|
||||
# 缓存不同模板的Jinja2环境
|
||||
self._envs = {}
|
||||
|
||||
def _get_env(self) -> Environment:
|
||||
"""获取当前配置的模板环境"""
|
||||
def _get_env_sync(self) -> Environment:
|
||||
"""获取当前配置的模板环境(同步版本,供 asyncio.to_thread 调用)"""
|
||||
template_name = self.config_manager.get_report_template()
|
||||
|
||||
# 如果环境已缓存且配置未变(这里简单假设配置变了会重新获取,或者我们可以每次都检查)
|
||||
# 为了响应配置热更,我们每次都检查一下或者简单地按需创建
|
||||
# 如果环境已缓存且配置未变
|
||||
if template_name in self._envs:
|
||||
return self._envs[template_name]
|
||||
|
||||
@@ -34,7 +34,6 @@ class HTMLTemplates:
|
||||
if not os.path.exists(template_dir):
|
||||
logger.warning(f"模板目录不存在: {template_dir},回退到 scrapbook")
|
||||
template_dir = os.path.join(self.base_dir, "scrapbook")
|
||||
# 如果 scrapbook 也不存在,那就有大问题了,不过这里假设 scrapbook 一定存在
|
||||
|
||||
env = Environment(
|
||||
loader=FileSystemLoader(template_dir),
|
||||
@@ -45,31 +44,62 @@ class HTMLTemplates:
|
||||
self._envs[template_name] = env
|
||||
return env
|
||||
|
||||
def get_image_template(self) -> str:
|
||||
"""获取图片报告的HTML模板(返回原始模板字符串)"""
|
||||
async def _get_env_async(self) -> Environment:
|
||||
"""获取当前配置的模板环境(异步版本)"""
|
||||
return await asyncio.to_thread(self._get_env_sync)
|
||||
|
||||
def _get_env(self) -> Environment:
|
||||
"""获取当前配置的模板环境(同步版本,向后兼容)"""
|
||||
return self._get_env_sync()
|
||||
|
||||
def _read_template_file_sync(self, filename: str) -> str:
|
||||
"""同步读取模板文件内容"""
|
||||
with open(filename, encoding="utf-8") as f:
|
||||
return f.read()
|
||||
|
||||
async def get_image_template_async(self) -> str:
|
||||
"""获取图片报告的HTML模板(异步版本,返回原始模板字符串)"""
|
||||
try:
|
||||
env = self._get_env()
|
||||
# 获取模板对象
|
||||
env = await self._get_env_async()
|
||||
template = env.get_template("image_template.html")
|
||||
# 读取原始模板文件内容,而不是渲染它
|
||||
with open(template.filename, encoding="utf-8") as f:
|
||||
return f.read()
|
||||
return await asyncio.to_thread(
|
||||
self._read_template_file_sync, template.filename
|
||||
)
|
||||
except Exception as e:
|
||||
# 如果加载失败,返回空字符串让调用者处理
|
||||
logger.error(f"加载图片模板失败: {e}")
|
||||
return ""
|
||||
|
||||
def get_pdf_template(self) -> str:
|
||||
"""获取PDF报告的HTML模板(返回原始模板字符串)"""
|
||||
def get_image_template(self) -> str:
|
||||
"""获取图片报告的HTML模板(同步版本,向后兼容)"""
|
||||
try:
|
||||
env = self._get_env()
|
||||
# 获取模板对象
|
||||
template = env.get_template("image_template.html")
|
||||
with open(template.filename, encoding="utf-8") as f:
|
||||
return f.read()
|
||||
except Exception as e:
|
||||
logger.error(f"加载图片模板失败: {e}")
|
||||
return ""
|
||||
|
||||
async def get_pdf_template_async(self) -> str:
|
||||
"""获取PDF报告的HTML模板(异步版本,返回原始模板字符串)"""
|
||||
try:
|
||||
env = await self._get_env_async()
|
||||
template = env.get_template("pdf_template.html")
|
||||
# 读取原始模板文件内容,而不是渲染它
|
||||
return await asyncio.to_thread(
|
||||
self._read_template_file_sync, template.filename
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"加载PDF模板失败: {e}")
|
||||
return ""
|
||||
|
||||
def get_pdf_template(self) -> str:
|
||||
"""获取PDF报告的HTML模板(同步版本,向后兼容)"""
|
||||
try:
|
||||
env = self._get_env()
|
||||
template = env.get_template("pdf_template.html")
|
||||
with open(template.filename, encoding="utf-8") as f:
|
||||
return f.read()
|
||||
except Exception as e:
|
||||
# 如果加载失败,返回空字符串让调用者处理
|
||||
logger.error(f"加载PDF模板失败: {e}")
|
||||
return ""
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ class AutoScheduler:
|
||||
elif bot_qq_ids:
|
||||
self.bot_manager.set_bot_qq_ids([bot_qq_ids])
|
||||
|
||||
async def _get_platform_id_for_group(self, group_id):
|
||||
async def get_platform_id_for_group(self, group_id):
|
||||
"""根据群ID获取对应的平台ID"""
|
||||
try:
|
||||
# 首先检查已注册的bot实例
|
||||
@@ -355,7 +355,7 @@ class AutoScheduler:
|
||||
else:
|
||||
# 回退到原来的逻辑(单个平台)
|
||||
logger.warning(f"群 {group_id} 没有多个平台可用,使用回退逻辑")
|
||||
platform_id = await self._get_platform_id_for_group(group_id)
|
||||
platform_id = await self.get_platform_id_for_group(group_id)
|
||||
|
||||
if not platform_id:
|
||||
logger.error(f"❌ 群 {group_id} 无法获取平台ID,跳过分析")
|
||||
@@ -621,7 +621,7 @@ class AutoScheduler:
|
||||
)
|
||||
else:
|
||||
logger.warning(f"群 {group_id} 没有多个平台可用,使用回退逻辑")
|
||||
platform_id = await self._get_platform_id_for_group(group_id)
|
||||
platform_id = await self.get_platform_id_for_group(group_id)
|
||||
if not platform_id:
|
||||
logger.error(f"❌ 群 {group_id} 无法获取平台ID,无法发送图片")
|
||||
return False
|
||||
@@ -794,7 +794,7 @@ class AutoScheduler:
|
||||
else:
|
||||
# 回退到原来的逻辑(单个平台)
|
||||
logger.warning(f"群 {group_id} 没有多个平台可用,使用回退逻辑")
|
||||
platform_id = await self._get_platform_id_for_group(group_id)
|
||||
platform_id = await self.get_platform_id_for_group(group_id)
|
||||
|
||||
if not platform_id:
|
||||
logger.error(f"❌ 群 {group_id} 无法获取平台ID,无法发送文本")
|
||||
@@ -874,7 +874,7 @@ class AutoScheduler:
|
||||
else:
|
||||
# 回退到原来的逻辑(单个平台)
|
||||
logger.warning(f"群 {group_id} 没有多个平台可用,使用回退逻辑")
|
||||
platform_id = await self._get_platform_id_for_group(group_id)
|
||||
platform_id = await self.get_platform_id_for_group(group_id)
|
||||
|
||||
if not platform_id:
|
||||
logger.error(f"❌ 群 {group_id} 无法获取平台ID,无法发送PDF")
|
||||
|
||||
Reference in New Issue
Block a user