> Daily Analysis
-- msg: {{message_count}},
- users: {{participant_count}}
- } -
diff --git a/README.md b/README.md index a11e0d6..23f7329 100644 --- a/README.md +++ b/README.md @@ -77,7 +77,7 @@ _✨ 一个基于 AstrBot 的智能群聊分析插件,支持 **QQ (OneBot)** ### 📊 可视化报告 - **多种格式**: 支持图片和文本输出格式 - **精美图片**: 生成美观的可视化报告 - - **PDF报告**: 生成专业的PDF格式分析报告(需配置) + - **HTML报告**: 生成清晰的HTML格式分析报告,可以进一步配置为外链形式发送 - **QQ群**: 支持上传到群相册和群文件,查阅黑历史友好 - **详细数据**: 包含消息统计、时间分布、关键词、金句等 diff --git a/_conf_schema.json b/_conf_schema.json index 5a77417..e629cb1 100644 --- a/_conf_schema.json +++ b/_conf_schema.json @@ -61,7 +61,7 @@ "type": "string", "description": "输出格式", "default": "image", - "hint": "分析报告的输出格式:image(图片)、text(文本)、pdf(PDF文件)、html(HTML文件)。使用 PDF 需要额外配置,根据文件中的 PDF_功能说明.md 进行配置" + "hint": "分析报告的输出格式:image(图片)、text(文本)、html(HTML文件)。" }, "report_template": { "type": "string", @@ -350,31 +350,6 @@ } } }, - "pdf": { - "description": "PDF 设置", - "type": "object", - "hint": "PDF 报告输出相关配置,包括输出目录、浏览器路径和文件名格式", - "items": { - "pdf_output_dir": { - "type": "string", - "description": "PDF输出目录", - "default": "", - "hint": "PDF报告文件的保存目录。留空则自动使用插件数据目录下的 reports 目录。" - }, - "browser_path": { - "type": "string", - "description": "自定义浏览器路径", - "default": "", - "hint": "要填写的话请你清楚自己在干什么。自定义浏览器的可执行文件路径(如 Chrome 或 Edge 的 .exe 文件)。提示:如果是在网页后台设置,则直接输入普通路径即可(如 C:\\Program Files\\...);如果是手动编辑 config.json 文件,请务必使用双反斜杠 '\\\\' 分隔路径。" - }, - "pdf_filename_format": { - "type": "string", - "description": "PDF文件名格式", - "default": "群聊分析报告_${group_id}_${date}_${ulid}.pdf", - "hint": "PDF文件名格式,支持变量:${group_id}(群号)、${date}(日期)、${ulid}(时间排序ID),支持子目录/层级。" - } - } - }, "html": { "description": "HTML 设置", "type": "object", diff --git a/docs/pdf_feature_guide.md b/docs/pdf_feature_guide.md deleted file mode 100644 index b10c77e..0000000 --- a/docs/pdf_feature_guide.md +++ /dev/null @@ -1,90 +0,0 @@ -# 群日常分析插件 - PDF 功能说明 - -## 概述 - -本插件现已支持生成 PDF 格式的群聊分析报告,提供更专业的报告输出格式。PDF 引擎已迁移至 Playwright,提供更好的兼容性。 - -## 功能特性 - -- 📄 **PDF 报告生成**: 将群聊分析结果导出为精美的 PDF 文档 -- 🎨 **专业排版**: 针对 PDF 打印优化的样式设计 -- 📊 **完整内容**: 包含所有分析数据(统计信息、话题分析、用户称号、群聊金句) -- 🔧 **灵活配置**: 可自定义输出目录、文件名格式以及**自定义浏览器路径** - -## 安装依赖 - -使用 PDF 功能前,需要安装 playwright 库。有以下几种安装方式: - -### 方式一:使用命令安装(推荐) - -在群聊中直接使用安装命令: - -``` -/安装PDF -``` - -此命令会自动: -- 检查 playwright 是否已安装 -- 如未安装,自动安装 playwright 库 -- 检查是否需要下载 Chromium 浏览器(已配置自定义路径则跳过) -- 测试 PDF 功能是否正常 - -### 方式二:手动安装 - -```bash -pip install playwright -playwright install chromium -``` - -## 使用方法 - -### 1. 设置输出格式 - -使用新增的 `/设置格式` 命令来切换输出格式: - -``` -/设置格式 pdf -``` - -### 2. 生成 PDF 报告 - -设置为 PDF 格式后,使用常规的分析命令: - -``` -/群分析 -``` - -### 3. 高级配置:自定义浏览器路径 - -如果您希望使用系统中已有的 Chrome 或 Edge,或者在特定的路径下运行浏览器,可以在配置中设置 `browser_path`: - -```json -{ - "browser_path": "C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe" -} -``` - -## 故障排除 - -### 1. Playwright 安装失败 - -请确保您的系统网络能够访问官网下载浏览器内核。如果是在受限环境下,建议使用系统的 Chrome/Edge 并配置 `browser_path`。 - -### 2. PDF 生成失败 - -- 检查 `browser_path` 是否正确且可执行。 -- 确保系统安装了必要的字体。 - -## 更新日志 - -### v1.2.0 -- 🚀 **Playwright 迁移**: 替换了不稳定的 pyppeteer。 -- 🛠️ **自定义浏览器**: 支持手动指定 Chrome/Edge 路径。 -- ⚡ **安装优化**: 自动检测系统浏览器并减少不必要的下载。 - -## 技术实现 - -PDF 生成基于以下技术: -- **Playwright**: 现代无头浏览器控制 -- **HTML/CSS**: 报告模板和样式 -- **异步处理**: 非阻塞的 PDF 生成过程 diff --git a/main.py b/main.py index 220785a..b404982 100644 --- a/main.py +++ b/main.py @@ -47,7 +47,6 @@ from .src.infrastructure.reporting.generators import ReportGenerator from .src.infrastructure.scheduler.auto_scheduler import AutoScheduler from .src.shared.trace_context import TraceContext, TraceLogFilter from .src.utils.logger import logger -from .src.utils.pdf_utils import PDFInstaller from .src.utils.resilience import GlobalRateLimiter @@ -621,21 +620,6 @@ class GroupDailyAnalysis(Star): await adapter.send_text_report(group_id, text_report) return - elif output_format == "pdf": - pdf_path = await self.report_generator.generate_pdf_report( - analysis_result, - group_id, - avatar_getter=avatar_url_getter, - nickname_getter=nickname_getter, - ) - if pdf_path: - if not await adapter.send_file(group_id, pdf_path): - yield event.chain_result( - [File(name=Path(pdf_path).name, file=pdf_path)] - ) - else: - yield event.plain_result("⚠️ PDF 生成失败。") - elif output_format == "html": html_path, json_path = await self.report_generator.generate_html_report( analysis_result, @@ -689,29 +673,19 @@ class GroupDailyAnalysis(Star): if not format_type: current_format = self.config_manager.get_output_format() - pdf_status = ( - "✅" - if self.config_manager.playwright_available - else "❌ (需安装 Playwright)" - ) yield event.plain_result(f"""📊 当前输出格式: {current_format} 可用格式: • image - 图片格式 (默认) • text - 文本格式 -• pdf - PDF 格式 {pdf_status} • html - HTML 格式 用法: /设置格式 [格式名称]""") return format_type = format_type.lower() - if format_type not in ["image", "text", "pdf", "html"]: - yield event.plain_result("❌ 无效的格式类型,支持: image, text, pdf, html") - return - - if format_type == "pdf" and not self.config_manager.playwright_available: - yield event.plain_result("❌ PDF 格式不可用,请使用 /安装PDF 命令安装依赖") + if format_type not in ["image", "text", "html"]: + yield event.plain_result("❌ 无效的格式类型,支持: image, text, html") return self.config_manager.set_output_format(format_type) @@ -807,37 +781,6 @@ class GroupDailyAnalysis(Star): ) yield event.chain_result([preview_nodes]) - @filter.command("安装PDF", alias={"install_pdf"}) - @filter.permission_type(PermissionType.ADMIN) - async def install_pdf_deps(self, event: AstrMessageEvent): - """ - 安装 PDF 功能依赖(跨平台支持) - 用法: /安装PDF - """ - if self._terminating: - return - - current_task = asyncio.current_task() - if current_task: - self._background_tasks.add(current_task) - - try: - yield event.plain_result("🔄 开始安装 PDF 功能依赖,请稍候...") - - result = await PDFInstaller.install_playwright( - self.config_manager, task_registry=self._background_tasks - ) - yield event.plain_result(result) - - except asyncio.CancelledError: - logger.info("PDF 安装任务被取消") - except Exception as e: - logger.error(f"安装 PDF 依赖失败: {e}", exc_info=True) - yield event.plain_result(f"❌ 安装过程中出现错误: {str(e)}") - finally: - if current_task: - self._background_tasks.discard(current_task) - @filter.command("分析设置", alias={"analysis_settings"}) @filter.permission_type(PermissionType.ADMIN) async def analysis_settings(self, event: AstrMessageEvent, action: str = "status"): @@ -915,7 +858,6 @@ class GroupDailyAnalysis(Star): ) auto_time = self.config_manager.get_auto_analysis_time() - 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() @@ -941,12 +883,11 @@ class GroupDailyAnalysis(Star): • 增量分析: {incremental_status_text} • 调试模式: {debug_status} (增量立即报告) • 输出格式: {output_format} -• PDF 功能: {pdf_status} • 最小消息数: {min_threshold} 💡 可用命令: enable, disable, status, reload, test, incremental_debug -💡 支持的输出格式: image, text, pdf (图片和PDF包含活跃度可视化) -💡 其他命令: /设置格式, /安装PDF, /增量状态""") +💡 支持的输出格式: image, text (图片包含活跃度可视化) +💡 其他命令: /设置格式, /增量状态""") @filter.command("增量状态", alias={"incremental_status"}) @filter.permission_type(PermissionType.ADMIN) diff --git a/requirements.txt b/requirements.txt index 2d64e91..cccf4e6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,2 @@ -playwright>=1.40.0 diskcache ulid-py \ No newline at end of file diff --git a/src/domain/repositories/report_repository.py b/src/domain/repositories/report_repository.py index f457be6..0c02399 100644 --- a/src/domain/repositories/report_repository.py +++ b/src/domain/repositories/report_repository.py @@ -25,17 +25,22 @@ class IReportGenerator(ABC): pass @abstractmethod - async def generate_pdf_report( + async def generate_html_report( self, analysis_result: dict, group_id: str, - avatar_getter: Any = None, + avatar_url_getter: Any = None, nickname_getter: Any = None, - ) -> str | None: - """生成 PDF 报告""" + ) -> tuple[str | None, str | None]: + """生成 HTML 报告""" pass @abstractmethod def generate_text_report(self, analysis_result: dict) -> str: """生成文本报告""" pass + + @abstractmethod + async def close(self): + """释放资源""" + pass diff --git a/src/infrastructure/config/config_manager.py b/src/infrastructure/config/config_manager.py index beb1ac6..502e361 100644 --- a/src/infrastructure/config/config_manager.py +++ b/src/infrastructure/config/config_manager.py @@ -1,10 +1,8 @@ """ 配置管理模块 - 基础设施层 -负责处理插件配置和PDF依赖检查 +负责处理插件配置 """ -import sys - from astrbot.api import AstrBotConfig from astrbot.api.star import StarTools @@ -21,15 +19,11 @@ class ConfigManager: - llm: LLM 设置 - analysis_features: 分析功能开关 - incremental: 增量分析设置 - - pdf: PDF 设置 - prompts: 提示词模板 """ def __init__(self, config: AstrBotConfig): self.config = config - self._playwright_available = False - self._playwright_version = None - self._check_playwright_availability() def _get_group(self, group: str) -> dict: """获取指定分组的配置字典,不存在时返回空字典""" @@ -240,26 +234,6 @@ class ConfigManager: "plugin_specific_persona_id", "" ) - def get_pdf_output_dir(self) -> str: - """获取PDF输出目录""" - from pathlib import Path - - from astrbot.core.utils.astrbot_path import get_astrbot_data_path - - try: - default_path = StarTools.get_data_dir() / "reports" - val = self._get_group("pdf").get("pdf_output_dir") - return val if val else str(default_path) - except Exception: - val = self._get_group("pdf").get("pdf_output_dir") - fallback_path = ( - Path(get_astrbot_data_path()) - / "plugin_data" - / "astrbot_plugin_qq_group_daily_analysis" - / "reports" - ) - return val if val else str(fallback_path) - def get_bot_self_ids(self) -> list: """获取机器人自身的 ID 列表 (兼容 bot_qq_ids)""" basic = self._get_group("basic") @@ -268,12 +242,6 @@ class ConfigManager: ids = basic.get("bot_qq_ids", []) return ids - def get_pdf_filename_format(self) -> str: - """获取PDF文件名格式""" - return self._get_group("pdf").get( - "pdf_filename_format", "群聊分析报告_{group_id}_{date}.pdf" - ) - def get_html_output_dir(self) -> str: """获取HTML输出目录""" from pathlib import Path @@ -407,11 +375,6 @@ class ConfigManager: ) # 2. 文件名格式升级 - modified |= self._upgrade_config_item( - "pdf", - "pdf_filename_format", - self.set_pdf_filename_format, - ) modified |= self._upgrade_config_item( "html", "html_filename_format", @@ -616,16 +579,6 @@ class ConfigManager: self._ensure_group("analysis_features")["max_golden_quotes"] = count self.config.save_config() - def set_pdf_output_dir(self, directory: str): - """设置PDF输出目录""" - self._ensure_group("pdf")["pdf_output_dir"] = directory - self.config.save_config() - - def set_pdf_filename_format(self, format_str: str): - """设置PDF文件名格式""" - self._ensure_group("pdf")["pdf_filename_format"] = format_str - self.config.save_config() - def set_html_filename_format(self, format_str: str): """设置HTML文件名格式""" self._ensure_group("html")["html_filename_format"] = format_str @@ -754,94 +707,6 @@ class ConfigManager: """获取多群增量分析的交错间隔(秒),避免 API 压力""" return self._get_group("incremental").get("incremental_stagger_seconds", 30) - @property - def playwright_available(self) -> bool: - """检查playwright是否可用""" - return self._playwright_available - - @property - def playwright_version(self) -> str | None: - """获取playwright版本""" - return self._playwright_version - - def _check_playwright_availability(self): - """检查 playwright 可用性""" - try: - import importlib.util - - if importlib.util.find_spec("playwright") is None: - raise ImportError - - import playwright - from playwright.async_api import async_playwright # noqa: F401 - - self._playwright_available = True - - try: - self._playwright_version = playwright.__version__ - logger.info(f"使用 playwright {self._playwright_version} 作为 PDF 引擎") - except AttributeError: - self._playwright_version = "unknown" - logger.info("使用 playwright (版本未知) 作为 PDF 引擎") - - except ImportError: - self._playwright_available = False - self._playwright_version = None - logger.warning( - "playwright 未安装,PDF 功能将不可用。请使用 pip install playwright 安装,并运行 playwright install chromium" - ) - - def get_browser_path(self) -> str: - """获取自定义浏览器路径""" - return self._get_group("pdf").get("browser_path", "") - - def set_browser_path(self, path: str): - """设置自定义浏览器路径""" - self._ensure_group("pdf")["browser_path"] = path - self.config.save_config() - - def reload_playwright(self) -> bool: - """重新加载 playwright 模块""" - try: - logger.info("开始重新加载 playwright 模块...") - - modules_to_remove = [ - mod for mod in sys.modules.keys() if mod.startswith("playwright") - ] - logger.info(f"移除模块: {modules_to_remove}") - for mod in modules_to_remove: - del sys.modules[mod] - - try: - import playwright - - self._playwright_available = True - try: - self._playwright_version = playwright.__version__ - logger.info( - f"重新加载成功,playwright 版本: {self._playwright_version}" - ) - except AttributeError: - self._playwright_version = "unknown" - logger.info("重新加载成功,playwright 版本未知") - - return True - - except ImportError: - logger.info("playwright 重新导入可能需要重启 AstrBot") - self._playwright_available = False - self._playwright_version = None - return False - except Exception: - logger.info("playwright 重新导入失败") - self._playwright_available = False - self._playwright_version = None - return False - - except Exception as e: - logger.error(f"重新加载 playwright 时出错: {e}") - return False - def save_config(self): """保存配置到AstrBot配置系统""" try: diff --git a/src/infrastructure/reporting/__init__.py b/src/infrastructure/reporting/__init__.py index 6ff4113..52a016a 100644 --- a/src/infrastructure/reporting/__init__.py +++ b/src/infrastructure/reporting/__init__.py @@ -1,6 +1,6 @@ """ 报告生成模块 -包含HTML、PDF、文本报告生成功能 +包含HTML、图片、文本报告生成功能 """ from .generators import ReportGenerator diff --git a/src/infrastructure/reporting/dispatcher.py b/src/infrastructure/reporting/dispatcher.py index e168437..0326b78 100644 --- a/src/infrastructure/reporting/dispatcher.py +++ b/src/infrastructure/reporting/dispatcher.py @@ -48,8 +48,6 @@ class ReportDispatcher: success = False if output_format == "image": success = await self._dispatch_image(group_id, analysis_result, platform_id) - elif output_format == "pdf": - success = await self._dispatch_pdf(group_id, analysis_result, platform_id) elif output_format == "html": success = await self._dispatch_html(group_id, analysis_result, platform_id) else: @@ -113,43 +111,6 @@ class ReportDispatcher: ) return await self._dispatch_text(group_id, analysis_result, platform_id) - async def _dispatch_pdf( - self, group_id: str, analysis_result: dict[str, Any], platform_id: str | None - ) -> bool: - trace_id = TraceContext.get() - # 1. 检查 Playwright - if not self.config_manager.playwright_available: - logger.warning( - f"[{trace_id}] Playwright not available, falling back to text." - ) - return await self._dispatch_text(group_id, analysis_result, platform_id) - - # 2. 生成 PDF - pdf_path = None - try: - pdf_path = await self.report_generator.generate_pdf_report( - analysis_result, group_id - ) - except Exception as e: - logger.error(f"[{trace_id}] Failed to generate PDF report: {e}") - - # 3. 发送 PDF - if pdf_path: - sent = await self.message_sender.send_file( - group_id, - pdf_path, - caption="📊 每日群聊分析报告已生成:", - platform_id=platform_id, - ) - if sent: - return True - - # 4. 回退:文本报告 - logger.warning( - f"[{trace_id}] PDF dispatch failed, falling back to text report." - ) - return await self._dispatch_text(group_id, analysis_result, platform_id) - async def _dispatch_html( self, group_id: str, analysis_result: dict[str, Any], platform_id: str | None ) -> bool: diff --git a/src/infrastructure/reporting/generators.py b/src/infrastructure/reporting/generators.py index 9ef599b..39ecbba 100644 --- a/src/infrastructure/reporting/generators.py +++ b/src/infrastructure/reporting/generators.py @@ -282,61 +282,6 @@ class ReportGenerator(IReportGenerator): await self._avatar_session.close() self._avatar_session = None - async def generate_pdf_report( - self, - analysis_result: dict, - group_id: str, - avatar_getter=None, - nickname_getter=None, - ) -> str | None: - """生成PDF格式的分析报告""" - try: - # 确保输出目录存在(使用 asyncio.to_thread 避免阻塞) - output_dir = Path(self.config_manager.get_pdf_output_dir()) - await asyncio.to_thread(output_dir.mkdir, parents=True, exist_ok=True) - - # 生成文件路径,支持 {group_id}/{date}/{ulid} 自定义子目录 - current_date = datetime.now().strftime("%Y%m%d") - pdf_path = self._build_safe_report_path( - output_dir, - self.config_manager.get_pdf_filename_format(), - group_id=group_id, - date=current_date, - ) - - # 准备渲染数据 - render_data = await self._prepare_render_data( - analysis_result, - chart_template="activity_chart_pdf.html", - avatar_url_getter=avatar_getter, - nickname_getter=nickname_getter, - ) - logger.info(f"PDF 渲染数据准备完成,包含 {len(render_data)} 个字段") - - # 生成 HTML 内容(使用 Jinja2 渲染器以支持逻辑标签) - html_content = self.html_templates.render_template( - "pdf_template.html", **render_data - ) - - # 检查HTML内容是否有效 - if not html_content: - logger.error("PDF报告HTML渲染失败:返回空内容") - return None - - logger.info(f"HTML 内容生成完成,长度: {len(html_content)} 字符") - - # 转换为 PDF - success = await self._html_to_pdf(html_content, str(pdf_path)) - - if success: - return str(pdf_path.absolute()) - else: - return None - - except Exception as e: - logger.error(f"生成 PDF 报告失败: {e}") - return None - async def generate_html_report( self, analysis_result: dict, @@ -920,171 +865,3 @@ class ReportGenerator(IReportGenerator): logger.debug("头像缓存已关闭") except Exception as e: logger.warning(f"关闭头像缓存失败: {e}") - - async def _html_to_pdf(self, html_content: str, output_path: str) -> bool: - """将 HTML 内容转换为 PDF 文件""" - try: - # 动态导入 playwright - try: - from playwright.async_api import async_playwright # type: ignore - except ImportError: - logger.error("playwright 未安装,无法生成 PDF") - logger.info("💡 请尝试运行: pip install playwright") - return False - - import os - import sys - - logger.info("启动浏览器进行 PDF 转换 (使用 Playwright)") - - async with async_playwright() as p: - browser = None - - executable_path = None - - # 0. 优先检查配置的自定义路径 - custom_browser_path = self.config_manager.get_browser_path() - if custom_browser_path: - if Path(custom_browser_path).exists(): - logger.info( - f"使用配置的自定义浏览器路径: {custom_browser_path}" - ) - executable_path = custom_browser_path - else: - logger.warning( - f"配置的浏览器路径不存在: {custom_browser_path},尝试自动检测..." - ) - - # 1. 如果没有自定义路径,尝试自动检测系统浏览器 - if not executable_path: - system_browser_paths = [] - if sys.platform.startswith("win"): - username = os.environ.get("USERNAME", "") - local_app_data = os.environ.get( - "LOCALAPPDATA", rf"C:\Users\{username}\AppData\Local" - ) - program_files = os.environ.get( - "ProgramFiles", r"C:\Program Files" - ) - program_files_x86 = os.environ.get( - "ProgramFiles(x86)", r"C:\Program Files (x86)" - ) - - system_browser_paths = [ - os.path.join( - program_files, r"Google\Chrome\Application\chrome.exe" - ), - os.path.join( - program_files_x86, - r"Google\Chrome\Application\chrome.exe", - ), - os.path.join( - local_app_data, r"Google\Chrome\Application\chrome.exe" - ), - os.path.join( - program_files_x86, - r"Microsoft\Edge\Application\msedge.exe", - ), - os.path.join( - program_files, r"Microsoft\Edge\Application\msedge.exe" - ), - ] - elif sys.platform.startswith("linux"): - system_browser_paths = [ - "/usr/bin/google-chrome", - "/usr/bin/google-chrome-stable", - "/usr/bin/chromium", - "/usr/bin/chromium-browser", - "/snap/bin/chromium", - ] - elif sys.platform.startswith("darwin"): - system_browser_paths = [ - "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome", - "/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge", - "/Applications/Chromium.app/Contents/MacOS/Chromium", - ] - - # 尝试找到可用的系统浏览器 - for path in system_browser_paths: - if Path(path).exists(): - executable_path = path - logger.info(f"使用系统浏览器: {path}") - break - - # 定义默认启动参数 - launch_kwargs = { - "headless": True, - "args": [ - "--no-sandbox", - "--disable-setuid-sandbox", - "--font-render-hinting=none", - ], - } - - if executable_path: - launch_kwargs["executable_path"] = executable_path - launch_kwargs["channel"] = ( - "chrome" if "chrome" in executable_path.lower() else "msedge" - ) - - try: - if executable_path: - # 如果指定了路径,通常使用 chromium 启动 - browser = await p.chromium.launch(**launch_kwargs) - else: - # 尝试直接启动,依赖 playwright install - logger.info("尝试启动 Playwright 托管的浏览器...") - browser = await p.chromium.launch( - headless=True, args=launch_kwargs["args"] - ) - - except Exception as e: - logger.warning(f"浏览器启动失败: {e}") - if "Executable doesn't exist" in str(e) or "executable at" in str( - e - ): - logger.error("未找到可用的浏览器。") - logger.info( - "💡 请确保已安装 Playwright 浏览器: playwright install chromium" - ) - logger.info("💡 或者安装 Google Chrome / Microsoft Edge") - return False - - if not browser: - return False - - try: - context = await browser.new_context(device_scale_factor=1) - page = await context.new_page() - - # 设置页面内容 - await page.set_content( - html_content, wait_until="networkidle", timeout=60000 - ) - - # 生成 PDF - logger.info("开始生成 PDF...") - await page.pdf( - path=output_path, - format="A4", - print_background=True, - margin={ - "top": "10mm", - "right": "10mm", - "bottom": "10mm", - "left": "10mm", - }, - ) - logger.info(f"PDF 生成成功: {output_path}") - return True - - except Exception as e: - logger.error(f"PDF 生成过程出错: {e}") - return False - finally: - if browser: - await browser.close() - - except Exception as e: - logger.error(f"Playwright 运行出错: {e}") - return False diff --git a/src/infrastructure/reporting/templates.py b/src/infrastructure/reporting/templates.py index d277ee5..fae62ce 100644 --- a/src/infrastructure/reporting/templates.py +++ b/src/infrastructure/reporting/templates.py @@ -91,29 +91,6 @@ class HTMLTemplates: 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 "" - def render_template(self, template_name: str, **kwargs) -> str: """渲染指定的模板文件 diff --git a/src/infrastructure/reporting/templates/ATRI/pdf_template.html b/src/infrastructure/reporting/templates/ATRI/pdf_template.html deleted file mode 100644 index 8ada117..0000000 --- a/src/infrastructure/reporting/templates/ATRI/pdf_template.html +++ /dev/null @@ -1,577 +0,0 @@ - - -
- - -{{current_datetime}}
-- Activity analysis report, generated based on message statistics and AI analysis -
- -消息数: {{message_count}}
-参与人数: {{participant_count}}
-总字符数: {{total_characters}}
-表情数: {{emoji_count}}
-最活跃时段: {{most_active_period}}
-