Revert "调整插件执行模式并修复全球新闻后台线程"

This reverts commit adbf4471cf.
This commit is contained in:
Liu
2026-05-01 12:12:54 +08:00
parent 9d2054a83d
commit b59b61dade
10 changed files with 19 additions and 183 deletions

View File

@@ -591,20 +591,6 @@ class DouyuPlugin(MessagePluginInterface):
self._status_check_retry_count = 3
self._status_check_retry_delay_seconds = 1
self._daily_report_llm_client: Optional[UnifiedLLMClient] = None
# 斗鱼插件是典型“快命令 + 慢命令”混合体:
# 1. 订阅/列表类命令基本都是 Redis 读写,应该继续走前台同步,保证即时反馈;
# 2. 日报类命令会拉历史弹幕、调 LLM、渲染图片天然属于长任务
# 3. 因此这里把“慢命令名单”集中收口,供分发模式与超时策略共同复用。
self._background_report_commands = {
"#斗鱼弹幕日报",
"斗鱼弹幕日报",
"#强制斗鱼弹幕日报",
"强制斗鱼弹幕日报",
"#斗鱼粉丝日报",
"斗鱼粉丝日报",
"#强制斗鱼粉丝日报",
"强制斗鱼粉丝日报",
}
# 直播间语义画像:
# 1. 允许按房间号补充“主播职业生涯、圈内关系、常见梗来源”等背景;
# 2. 这些信息不会直接替代真实弹幕,只用于帮助 LLM 更准确理解圈内黑话;
@@ -638,36 +624,6 @@ class DouyuPlugin(MessagePluginInterface):
except Exception:
return False, day_text
@staticmethod
def _extract_command_token(message: Dict[str, Any]) -> str:
"""从消息里提取首个命令词。"""
content = str(message.get("content", "") or "").strip()
return content.split()[0] if content else ""
def get_message_dispatch_mode(self, message: Dict[str, Any]) -> str:
"""按命令决定斗鱼插件走前台还是后台。
设计说明:
1. 订阅、取消订阅、列表查询都很轻,继续前台执行能保证手感;
2. 日报命令一旦命中,后面会进入“查素材 -> 调模型 -> 渲染图片”的长链路;
3. 因此只有日报相关命令切后台,避免它们把前台 20 个消息槽位长期占住。
"""
command = self._extract_command_token(message)
if command in self._background_report_commands:
return self.normalize_message_dispatch_mode("background")
return super().get_message_dispatch_mode(message)
def get_message_process_timeout_seconds(self, message: Dict[str, Any]) -> Optional[int]:
"""只为日报命令放宽总超时,普通命令继续走默认保护值。"""
command = self._extract_command_token(message)
if command in self._background_report_commands:
# 用户已经明确存在 200 秒级长任务:
# 1. 斗鱼日报除了 LLM还包含素材整理与图片渲染
# 2. 这里放宽到 15 分钟,足够覆盖补发历史日报或高峰期模型排队;
# 3. 非日报命令不受影响,仍保持原有更紧的保护策略。
return 900
return None
@staticmethod
def _normalize_text_list(values: Any) -> List[str]:
"""

View File

@@ -59,11 +59,6 @@ class GameTaskPlugin(MessagePluginInterface):
self.LOG = logger
# 注册功能权限
self.feature = self.register_feature()
# 百科问答里只有“出题 / 判题”会真正走 LLM
# 1. `/t` 需要模型随机生成题目;
# 2. `/a` 需要模型判分并给理由;
# 3. `/s /r /l /h` 主要是本地 DB 读写,保持前台即可。
self._background_commands = {"/t", "/a"}
def initialize(self, context: Dict[str, Any]) -> bool:
"""初始化插件"""
@@ -123,22 +118,6 @@ class GameTaskPlugin(MessagePluginInterface):
return command in self._commands
def get_message_dispatch_mode(self, message: Dict[str, Any]) -> str:
"""只把 LLM 型命令切到后台,避免百科插件拖慢前台消息链。"""
content = str(message.get("content", "") or "").strip()
command = content.split()[0] if content else ""
if command in self._background_commands:
return self.normalize_message_dispatch_mode("background")
return super().get_message_dispatch_mode(message)
def get_message_process_timeout_seconds(self, message: Dict[str, Any]) -> Optional[int]:
"""为出题/判题命令单独放宽超时,兼容慢模型或排队场景。"""
content = str(message.get("content", "") or "").strip()
command = content.split()[0] if content else ""
if command in self._background_commands:
return 120
return None
def calculate_game_points(self, message: Dict[str, Any], success: bool, response: str) -> int:
"""计算游戏积分"""
if not success:

View File

@@ -1,6 +1,6 @@
import asyncio
import threading
import time
import time # 添加这一行
from typing import Dict, Any, List, Optional, Tuple
from base.plugin_common.message_plugin_interface import MessagePluginInterface
@@ -146,43 +146,31 @@ class GlobalNewsPlugin(MessagePluginInterface):
self._news_tasks[task_id] = thread
self.LOG.info(f"启动新闻获取任务: {task_id}")
def _fetch_news_thread(self, task_id: str, sender: str, roomid: str):
"""在单独线程里执行新闻抓取主流程。
这里必须保持为同步函数:
1. `threading.Thread(target=...)` 只能直接执行普通可调用对象;
2. 之前把协程函数直接塞给 `target`,线程里只会得到一个未执行的 coroutine任务实际上不会跑
3. 现在在线程内部显式创建事件循环,再把异步抓取和发消息协程跑完,才能真正脱离主链路执行。
"""
loop = asyncio.new_event_loop()
async def _fetch_news_thread(self, task_id: str, sender: str, roomid: str):
"""在单独线程中运行异步新闻获取任务"""
try:
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
news_result = loop.run_until_complete(self._fetch_news_async())
loop.close()
# 处理结果
receiver = roomid if roomid else sender
if news_result:
# 在线程自有事件循环里把图片和完成提示真正发出去,
# 避免这里只拿到 coroutine 对象却没有执行。
loop.run_until_complete(self.bot.send_image_message(receiver, news_result))
loop.run_until_complete(self.bot.send_text_message(receiver, "🌍全球新闻获取完成!", sender))
# 发送新闻图片
receiver = roomid if roomid else sender
await self.bot.send_image_message(receiver, news_result)
await self.bot.send_text_message("🌍全球新闻获取完成!", receiver, sender)
else:
loop.run_until_complete(self.bot.send_text_message(receiver, "❌获取新闻失败,请稍后再试", sender))
await self.bot.send_text_message(
(roomid if roomid else sender), "❌获取新闻失败,请稍后再试", sender)
except Exception as e:
self.LOG.error(f"新闻获取任务出错: {e}")
try:
receiver = roomid if roomid else sender
loop.run_until_complete(self.bot.send_text_message(receiver, f"❌获取新闻出错: {str(e)}", sender))
except Exception as send_error:
self.LOG.error(f"新闻获取失败后的通知发送异常: {send_error}")
await self.bot.send_text_message((roomid if roomid else sender), f"❌获取新闻出错: {str(e)}",
sender)
finally:
# 清理任务
if task_id in self._news_tasks:
del self._news_tasks[task_id]
try:
loop.close()
except Exception:
pass
async def _fetch_news_async(self) -> str:
"""异步获取所有新闻源的新闻"""

View File

@@ -48,11 +48,3 @@ history_group_summary_limit = 10
max_output_chars = 320
min_output_chars = 140
sharpness_level = "high"
[runtime]
# 成员锐评命令需要同时查画像、拉最近消息,再走一次 LLM 生成:
# 1. 这条链路比普通查询明显更重,而且用户已经接受“先提示处理中,再稍后出结果”的交互;
# 2. 默认切到后台后,就不会因为某次模型慢响应把前台消息槽位卡住;
# 3. 总超时放宽到 4 分钟,兼容群画像较大或模型排队的情况。
message_dispatch_mode = "background"
plugin_process_timeout_seconds = 240

View File

@@ -3,12 +3,4 @@ enable = true
commands = ["更新系统", "系统更新", "重启系统", "更新重启"]
wait_time = 5
# 设置管理员微信ID只有这些ID可以执行更新操作
shell_path= "/home/liuwei/abot/restart.sh"
[runtime]
# 系统更新属于典型后台维护任务:
# 1. 命令命中后会执行重启脚本,整个过程可能持续几十秒到数分钟;
# 2. 这类任务不应该长期占住前台消息并发槽位,否则会影响其他插件收消息;
# 3. 因此默认切到后台执行,并把总超时放宽到 10 分钟。
message_dispatch_mode = "background"
plugin_process_timeout_seconds = 600
shell_path= "/home/liuwei/abot/restart.sh"

View File

@@ -571,11 +571,6 @@ class ValueRankPlugin(MessagePluginInterface):
self.mention_batch_size = 200
self.mention_window_start_minutes = 20
self.mention_window_end_minutes = 10
# 身价排行里只有少数命令是真正的长任务:
# 1. `社交关系图` 需要拼 HTML 再截图渲染;
# 2. `重算身价` 会扫描整群候选成员并重写快照;
# 3. 其他榜单/说明类命令基本是读库拼文本,不值得全部切到后台。
self._background_commands = {"社交关系图", "重算身价"}
def initialize(self, context: Dict[str, Any]) -> bool:
"""初始化插件与配置。"""
@@ -644,26 +639,6 @@ class ValueRankPlugin(MessagePluginInterface):
command = content.split(" ")[0]
return command in self._commands
def get_message_dispatch_mode(self, message: Dict[str, Any]) -> str:
"""按命令决定是否切入后台任务池。"""
content = str(message.get("content", "") or "").strip()
command = content.split()[0] if content else ""
if command in self._background_commands:
# 这两个命令明显比普通查询重很多:
# 1. `社交关系图` 的瓶颈主要在模板渲染与截图;
# 2. `重算身价` 会遍历群成员并批量回写快照;
# 3. 改成后台后,轻量榜单查询就不会再被这类维护型命令拖住。
return self.normalize_message_dispatch_mode("background")
return super().get_message_dispatch_mode(message)
def get_message_process_timeout_seconds(self, message: Dict[str, Any]) -> Optional[int]:
"""为重渲染/重算命令单独放宽总超时。"""
content = str(message.get("content", "") or "").strip()
command = content.split()[0] if content else ""
if command in self._background_commands:
return 240
return None
@plugin_stats_decorator(plugin_name="身价排行")
async def process_message(self, message: Dict[str, Any]) -> Tuple[bool, Optional[str]]:
"""处理用户命令入口。"""

View File

@@ -4,12 +4,4 @@ command = ["黑丝视频", "黑丝", "来个黑丝", "搞个黑丝"]
command-format = """
🎬视频指令:
黑丝
"""
[runtime]
# 视频插件会经历“拉取接口 -> 下载文件 -> 抽首帧 -> 发送视频”整条链路:
# 1. 任一环节抖动都可能让处理时间明显长于普通文本命令;
# 2. 切到后台后,慢下载不会再卡住前台消息处理;
# 3. 总超时放宽到 4 分钟,兼容网络波动和大一点的视频文件。
message_dispatch_mode = "background"
plugin_process_timeout_seconds = 240
"""

View File

@@ -6,12 +6,4 @@ command-format = """
猛男
肌肉
帅哥
"""
[runtime]
# 猛男视频和普通视频插件的耗时结构基本一致:
# 1. 需要先拉接口,再下载视频文件,并额外抽首帧做封面;
# 2. 这些 IO/编解码步骤不适合长期占住前台并发槽位;
# 3. 因此同样默认走后台模式,并保留 4 分钟总超时。
message_dispatch_mode = "background"
plugin_process_timeout_seconds = 240
"""