diff --git a/plugins/message_summary/config.toml b/plugins/message_summary/config.toml index fbaf3ad..f584b39 100644 --- a/plugins/message_summary/config.toml +++ b/plugins/message_summary/config.toml @@ -15,6 +15,6 @@ image_format = "png" # 图片渲染模式: # - template: 使用 HTML 模板渲染(模板样式稳定后再切换) # - markdown: 使用历史 md2image 样式 -summary_image_mode = "markdown" +summary_image_mode = "template" # 总结卡片模板路径(相对项目根目录) -summary_image_template_path = "plugins/message_summary/templates/summary_card.html" +summary_image_template_path = "plugins/message_summary/templates/gemini_summary_card.html" diff --git a/plugins/message_summary/main.py b/plugins/message_summary/main.py index ef7aef3..f1e07e7 100644 --- a/plugins/message_summary/main.py +++ b/plugins/message_summary/main.py @@ -9,6 +9,11 @@ from typing import Dict, Any, Tuple, Optional, List from loguru import logger from markupsafe import Markup +try: + # 优先使用 markdown 库做完整渲染(支持表格、代码块等)。 + import markdown as markdown_lib +except Exception: + markdown_lib = None from base.plugin_common.message_plugin_interface import MessagePluginInterface from base.plugin_common.plugin_interface import PluginStatus @@ -410,11 +415,76 @@ class MessageSummaryPlugin(MessagePluginInterface): return cleaned @staticmethod - def _summary_markdown_to_html(summary_text: str) -> str: - """把总结 Markdown 转为基础 HTML 片段(模板内部展示用)。""" - # 这里不依赖第三方 markdown 库,保证在最小运行环境也能稳定渲染。 - # 规则按“标题/列表/段落”三类做轻量转换,足够覆盖总结文本场景。 - lines = str(summary_text or "").splitlines() + def _sanitize_rendered_html(rendered_html: str) -> str: + """对渲染后的 HTML 做最小安全过滤。 + + 安全策略: + 1. 移除 script/style/iframe 等高风险标签,避免模板渲染执行脚本; + 2. 清除行内事件属性(onload/onerror/onclick...); + 3. 禁止 javascript: 协议链接。 + + 说明: + - 这里是“轻量过滤”,目标是平衡安全与展示完整度; + - 若后续需要更严格过滤,可接入专门的 HTML Sanitizer。 + """ + safe_html = str(rendered_html or "") + # 删除高风险标签及其内容。 + safe_html = re.sub( + r"<\s*(script|style|iframe|object|embed|form|link|meta)\b[^>]*>.*?<\s*/\s*\1\s*>", + "", + safe_html, + flags=re.IGNORECASE | re.DOTALL, + ) + # 删除自闭合高风险标签。 + safe_html = re.sub( + r"<\s*(script|style|iframe|object|embed|form|link|meta)\b[^>]*/\s*>", + "", + safe_html, + flags=re.IGNORECASE | re.DOTALL, + ) + # 删除行内事件处理器属性。 + safe_html = re.sub(r"\son[a-zA-Z]+\s*=\s*(['\"]).*?\1", "", safe_html, flags=re.IGNORECASE | re.DOTALL) + # 阻断 javascript: 链接。 + safe_html = re.sub( + r"""(href|src)\s*=\s*(['"])\s*javascript:[^'"]*\2""", + r'\1=\2#\2', + safe_html, + flags=re.IGNORECASE, + ) + return safe_html + + @classmethod + def _summary_markdown_to_html(cls, summary_text: str) -> str: + """把总结 Markdown 转为 HTML 片段(模板内部展示用)。 + + 升级点: + 1. 使用 markdown 库完整支持标题、列表、粗斜体、引用、代码块、表格等结构; + 2. 对 LLM 输出里常见的富标签 markdown(例如 ```、|表格|、> 引用)效果更好; + 3. 渲染后做一次轻量安全过滤,避免模板内注入脚本。 + """ + text = str(summary_text or "").strip() + if not text: + return "

暂无总结内容。

" + + # 兼容处理: + # 1. 环境安装了 markdown 库时,走完整渲染; + # 2. 未安装时自动降级到内置轻量转换,避免插件启动失败。 + if markdown_lib is not None: + rendered = markdown_lib.markdown( + text, + extensions=[ + "extra", # 综合扩展:支持表格、定义列表等 + "fenced_code", # 支持 ``` 代码块 + "tables", # 支持 Markdown 表格 + "sane_lists", # 更稳定的列表解析 + "nl2br", # 保留换行,提升聊天总结可读性 + ], + output_format="html5", + ) + return cls._sanitize_rendered_html(rendered) + + # 轻量回退实现(兼容无 markdown 三方包的运行环境)。 + lines = text.splitlines() html_parts: List[str] = [] list_items: List[str] = [] @@ -443,7 +513,8 @@ class MessageSummaryPlugin(MessagePluginInterface): flush_list() html_parts.append(f"

{html.escape(line)}

") flush_list() - return "".join(html_parts) + rendered = "".join(html_parts) + return cls._sanitize_rendered_html(rendered) def _render_summary_template_html(self, group_name: str, summary_text: str) -> str: """根据模板路径渲染总结图片 HTML。""" diff --git a/plugins/message_summary/templates/gemini-code-1776907723749.html b/plugins/message_summary/templates/gemini-code-1776907723749.html new file mode 100644 index 0000000..5b1948f --- /dev/null +++ b/plugins/message_summary/templates/gemini-code-1776907723749.html @@ -0,0 +1,199 @@ + + + + + Group_Digest_Mobile_V4 + + + + + + +
+ +
+
+
+

CHAT INSIGHTS REPORT

+

ID: 20260423-L-OPEN

+
+
+ Daily Archive +
+
+ +
+
+ Msgs + 1,402 +
+
+ Links + 12 +
+
+ Files + 5 +
+
+ Alerts + 3 +
+
+
+ +
+ # Personal Interest Radar +
+ Gemma 4 + RTX 5090 + Playwright + Nasdaq 100 + frp Tunnel +
+
+ +
+ # Key Discussions + +
+
+ 01. LLM 本地部署显存压测 +
+
+
+
+ Background +

群友在 Mac mini M1 (16G) 环境下运行 Gemma 4 26B,出现内存频繁交换导致系统卡顿。

+
+
+ Key Points +
    +
  • 4-bit 量化(GGUF/Q4_K_M)显存占用约 17.5GB。
  • +
  • 需通过脚本强制清理物理内存 Swap 以缓解抖动。
  • +
  • Ollama 推理时建议关闭 Electron 全家桶。
  • +
+
+
+ Conclusion +

“16G 内存是底线,追求速度建议 32G 或 24G 显存 GPU 运行。”

+
+
+
+ +
+
+ 02. Dota 2 击杀识别 OCR 算法 +
+
+
+ Analysis +

弃用 Tesseract 改用 PaddleOCR。在处理 Douyu 直播间 1080P 高码率画面时,识别准确率从 72% 提升至 91%。

+
+
+
+ +
+ # Shared Resources +
+
+
+ + ollama-python/v2.1-stable +
+ +
+
+
+ + RTX_5090_Whitepaper_Draft.pdf +
+ +
+
+
+ +
+
+ # Marketplace +
+
+ [出] 3060Ti + ¥1650 +
+
+ [求] TI 饰品 + 面议 +
+
+
+
+ # Unresolved Pool +
+

"Ubuntu 24.04 驱动掉线如何修复?"

+

"Nasdaq 100 ETF 最优定投点?"

+
+
+
+ +
+
+ # Core Knowledge Points +

+ FRP 配置项优化: 针对内网穿透超时,需将 tcp_mux 设置为 true,并检查服务端防火墙 MTU 限制。 +

+
+
+ + + +
+ +
+ Engine: Playwright 1.42 / Renderer: Webkit / User: Liuwei +
+ + + \ No newline at end of file diff --git a/plugins/message_summary/templates/gemini_summary_card.html b/plugins/message_summary/templates/gemini_summary_card.html new file mode 100644 index 0000000..9fe575c --- /dev/null +++ b/plugins/message_summary/templates/gemini_summary_card.html @@ -0,0 +1,298 @@ + + + + + + {{ title }} + + + +
+
+
+
+
CHAT INSIGHTS SUMMARY
+
{{ generated_at }}
+
+
Daily Archive
+
+
+
+ Type +
群总结
+
+
+ Mode +
Template
+
+
+ Render +
HTML
+
+
+ Engine +
LLM
+
+
+
+ +
+ # {{ title }} +
+ {{ summary_html }} +
+
+ 内容已按 Markdown 富标签样式渲染(标题、列表、表格、代码块、引用)。 +
+ +
+
+ + +