[v4.6.2] 新增图片报告重试机制并优化调度稳定性

This commit is contained in:
SXP-Simon
2026-01-17 23:54:39 +08:00
parent e78c827857
commit 7e70a6532c
6 changed files with 97 additions and 23 deletions
+8 -3
View File
@@ -1,15 +1,20 @@
# 更新日志 (CHANGELOG)
## [v4.6.1] - Linux 下的 `/安装PDF` 指令简易修复,纠正头像获取的处理;PDF 格式在 Linux 环境下占用过大,原因还在排查中,暂时不推荐使用。
* **🐛 Bug 修复**: 修复 generators.py 中 _get_user_avatar 的 TypeError。将 aiohttp 请求重构为异步上下文模式,确保 response.read() 被正确等待,并增加了网络连接失败处理。
* **🐛 Bug 修复**: Linux 环境下的安装 PDF 命令修复,调整 PDF 格式排版情况。PDF 格式在 Linux 环境下占用过大,原因还在排查中,暂时不推荐使用。
## [v4.6.2] - 新增图片报告重试机制并优化调度稳定性
* **✨ 新功能**: 新增图片报告重试机制并优化调度稳定性,实现延迟队列与死信队列,支持指数退避 + 随机抖动 (Jitter) 重试策略。
---
<details>
<summary>📋 点击查看历史更新日志</summary>
## [v4.6.1] - Linux 下的 `/安装PDF` 指令简易修复,纠正头像获取的处理;PDF 格式在 Linux 环境下占用过大,原因还在排查中,暂时不推荐使用。
* **🐛 Bug 修复**: 修复 generators.py 中 _get_user_avatar 的 TypeError。将 aiohttp 请求重构为异步上下文模式,确保 response.read() 被正确等待,并增加了网络连接失败处理。
* **🐛 Bug 修复**: Linux 环境下的安装 PDF 命令修复,调整 PDF 格式排版情况。PDF 格式在 Linux 环境下占用过大,原因还在排查中,暂时不推荐使用。
## [v4.6.0] - 通过 AstrBot 配置文件来判断账号启用插件
* **🐛 Bug 修复**: 检查该平台是否包含该插件的配置文件 plugin_set,通过 AstrBot 配置文件来判断账号启用插件。
+1 -1
View File
@@ -3,7 +3,7 @@
# QQ群日常分析插件
[![Plugin Version](https://img.shields.io/badge/Latest_Version-v4.6.1-blue.svg?style=for-the-badge&color=76bad9)](https://github.com/SXP-Simon/astrbot-qq-group-daily-analysis)
[![Plugin Version](https://img.shields.io/badge/Latest_Version-v4.6.2-blue.svg?style=for-the-badge&color=76bad9)](https://github.com/SXP-Simon/astrbot-qq-group-daily-analysis)
[![AstrBot](https://img.shields.io/badge/AstrBot-Plugin-ff69b4?style=for-the-badge)](https://github.com/AstrBotDevs/AstrBot)
[![License](https://img.shields.io/badge/License-MIT-green.svg?style=for-the-badge)](LICENSE)
[![](https://img.shields.io/badge/五彩斑斓的Bug群-Bug反馈群&水群-white?style=for-the-badge&color=76bad9&logo=qq&logoColor=76bad9)](https://qm.qq.com/q/oTzIrdDBIc)
+4 -2
View File
@@ -40,7 +40,9 @@ class QQGroupDailyAnalysis(Star):
context, self.config_manager, self.bot_manager
)
self.report_generator = ReportGenerator(self.config_manager)
self.retry_manager = RetryManager(self.bot_manager, self.html_render)
self.retry_manager = RetryManager(
self.bot_manager, self.html_render, self.report_generator
)
self.auto_scheduler = AutoScheduler(
self.config_manager,
self.message_analyzer.message_handler,
@@ -213,7 +215,7 @@ class QQGroupDailyAnalysis(Star):
group_id
)
await self.retry_manager.add_task(
html_content, group_id, platform_id
html_content, analysis_result, group_id, platform_id
)
else:
# 如果图片生成失败且无HTML,回退到文本报告
+1 -1
View File
@@ -14,6 +14,6 @@ help: | # 插件的帮助信息
命令:
/群分析 [天数] - 分析群聊活动
/分析设置 [操作] - 管理设置(enable/disable/status/test
version: v4.6.1 # 插件版本号。格式:v1.1.1 或者 v1.1
version: v4.6.2 # 插件版本号。格式:v1.1.1 或者 v1.1
author: SXP-Simon # 作者
repo: https://github.com/SXP-Simon/astrbot_plugin_qq_group_daily_analysis # 插件的仓库地址
+1 -1
View File
@@ -565,7 +565,7 @@ class AutoScheduler:
f"{group_id} 图片生成失败,已静默加入重试队列"
)
await self.retry_manager.add_task(
html_content, group_id, platform_id
html_content, analysis_result, group_id, platform_id
)
else:
logger.error(
+82 -15
View File
@@ -1,8 +1,8 @@
import asyncio
import random
import time
from dataclasses import dataclass
from collections.abc import Callable
from dataclasses import dataclass
from astrbot.api import logger
@@ -12,6 +12,7 @@ class RetryTask:
"""重试任务数据类"""
html_content: str
analysis_result: dict # 保存原始分析结果,用于文本回退
group_id: str
platform_id: str # 需要保存 platform_id 以便找回 Bot
retry_count: int = 0
@@ -34,9 +35,10 @@ class RetryManager:
4. 超过最大重试次数放入死信队列
"""
def __init__(self, bot_manager, html_render_func: Callable):
def __init__(self, bot_manager, html_render_func: Callable, report_generator=None):
self.bot_manager = bot_manager
self.html_render_func = html_render_func
self.report_generator = report_generator # 用于生成文本报告
self.queue = asyncio.Queue()
self.running = False
self.worker_task = None
@@ -69,7 +71,9 @@ class RetryManager:
logger.info("[RetryManager] 图片重试管理器已停止")
async def add_task(self, html_content: str, group_id: str, platform_id: str):
async def add_task(
self, html_content: str, analysis_result: dict, group_id: str, platform_id: str
):
"""添加重试任务"""
if not self.running:
logger.warning(
@@ -79,6 +83,7 @@ class RetryManager:
task = RetryTask(
html_content=html_content,
analysis_result=analysis_result,
group_id=group_id,
platform_id=platform_id,
created_at=time.time(),
@@ -115,10 +120,12 @@ class RetryManager:
self.queue.task_done()
else:
logger.error(
f"[RetryManager] 群 {task.group_id} 超过最大重试次数,移入死信队列"
f"[RetryManager] 群 {task.group_id} 超过最大重试次数,移入死信队列并尝试文本回退"
)
self._dlq.append(task)
self.queue.task_done()
# 尝试发送文本回退
await self._send_fallback_text(task)
await self._notify_failure(task)
except asyncio.CancelledError:
@@ -189,7 +196,7 @@ class RetryManager:
return True
elif retcode == 1200:
logger.warning(
f"[RetryManager] 发送失败 (retcode=1200): 可能是Bot被禁言或不在群内,稍后重试"
"[RetryManager] 发送失败 (retcode=1200): 可能是Bot被禁言或不在群内,稍后重试"
)
return False
else:
@@ -224,15 +231,75 @@ class RetryManager:
logger.error(f"[RetryManager] 处理任务时发生意外错误: {e}", exc_info=True)
return False
async def _notify_failure(self, task: RetryTask):
"""通知最终失败"""
try:
bot = self.bot_manager.get_bot_instance(task.platform_id)
if bot and hasattr(bot, "api") and hasattr(bot.api, "call_action"):
await bot.api.call_action(
"send_group_msg",
group_id=int(task.group_id),
message=f"[AstrBot QQ群日常分析总结插件] 报告生成/发送多次失败 (Group: {task.group_id}),请检查服务器日志。",
)
except Exception:
pass
async def _send_fallback_text(self, task: RetryTask):
"""发送文本回退报告(使用合并转发)"""
if not self.report_generator:
logger.warning("[RetryManager] 未配置 ReportGenerator,无法发送文本回退")
return
try:
logger.info(f"[RetryManager] 正在为群 {task.group_id} 生成文本回退报告...")
text_report = self.report_generator.generate_text_report(
task.analysis_result
)
bot = self.bot_manager.get_bot_instance(task.platform_id)
if not bot:
return
# 构造合并转发节点
# 注意:这里需要构造符合 OneBot v11 标准的节点列表
# 即使没有 self_id,我们也可以尝试发送
# 获取 bot self_id (如果能获取到)
bot_id = "10000" # fallback id
if hasattr(bot, "self_id"):
bot_id = str(bot.self_id)
nickname = "AstrBot日常分析"
nodes = [
{
"type": "node",
"data": {
"name": nickname,
"uin": bot_id,
"content": "⚠️ 图片报告多次生成失败,为您呈现文本版报告:",
},
},
{
"type": "node",
"data": {"name": nickname, "uin": bot_id, "content": text_report},
},
]
if hasattr(bot, "api") and hasattr(bot.api, "call_action"):
# 尝试发送群合并转发消息
# 一般使用 send_group_forward_msg 或 send_group_msg (带 nodes)
try:
await bot.api.call_action(
"send_group_forward_msg",
group_id=int(task.group_id),
messages=nodes,
)
logger.info(
f"[RetryManager] 群 {task.group_id} 文本回退报告发送成功 (合并转发)"
)
except Exception as e:
logger.warning(
f"[RetryManager] 合并转发失败,尝试直接发送文本: {e}"
)
# 回退到直接发送宽文本
await bot.api.call_action(
"send_group_msg",
group_id=int(task.group_id),
message=f"⚠️ 图片报告生成失败,文本报告:\n{text_report}"[
:4500
], # 截断防止过长
)
except Exception as e:
logger.error(f"[RetryManager] 文本回退发送失败: {e}", exc_info=True)