1. 为消息插件新增可配置的前台/后台分发模式,并在 robot 主链路中加入独立后台任务池,避免长任务长期占用前台 20 个消息处理槽位。 2. 放宽插件执行超时上限到 1800 秒,支持 200 秒以上长任务,同时保留熔断、统计和异常记录。 3. 为群聊总结和 AI 绘图启用后台执行配置,并将菜单插件默认输出改回文本模式。
134 lines
5.1 KiB
Python
134 lines
5.1 KiB
Python
from typing import Dict, Any, Tuple, Optional, List
|
||
from base.plugin_common.plugin_interface import PluginInterface
|
||
from wechat_ipad import WechatAPIClient
|
||
from utils.robot_cmd.robot_command import Feature
|
||
|
||
|
||
class MessagePluginInterface(PluginInterface):
|
||
"""消息处理插件接口"""
|
||
|
||
@staticmethod
|
||
def normalize_message_dispatch_mode(raw_mode: Any) -> str:
|
||
"""把插件声明的消息分发模式标准化为 `sync` 或 `background`。
|
||
|
||
设计说明:
|
||
1. `sync` 表示沿用当前主链路同步执行,插件会占用当前消息处理协程直到完成;
|
||
2. `background` 表示命中后立即转入后台任务池,主消息链路尽快释放,不再占用前台并发槽位;
|
||
3. 这里集中做别名兼容,后续插件只需要写 `background/async/queue` 这类语义值即可。
|
||
"""
|
||
mode = str(raw_mode or "").strip().lower()
|
||
if mode in {"background", "async", "queued", "queue", "detached"}:
|
||
return "background"
|
||
return "sync"
|
||
|
||
@property
|
||
def command_prefix(self) -> Optional[str]:
|
||
"""命令前缀,如 '/'"""
|
||
return None
|
||
|
||
@property
|
||
def commands(self) -> List[str]:
|
||
"""支持的命令列表"""
|
||
return []
|
||
|
||
@property
|
||
def feature_key(self) -> Optional[str]:
|
||
"""插件对应的功能权限键名"""
|
||
return None
|
||
|
||
@property
|
||
def feature_description(self) -> Optional[str]:
|
||
"""插件对应的功能权限描述"""
|
||
return None
|
||
|
||
def register_feature(self) -> Optional[Feature]:
|
||
"""注册插件功能权限
|
||
|
||
Returns:
|
||
Feature: 注册的功能权限枚举,如果不需要权限则返回None
|
||
"""
|
||
if self.feature_key and self.feature_description:
|
||
return Feature.register_feature(self.feature_key, self.feature_description)
|
||
return None
|
||
|
||
# 需要完成jobs 的业务,所以完成bot注入
|
||
def set_bot(self, bot: WechatAPIClient) -> None:
|
||
self.bot: WechatAPIClient = bot
|
||
|
||
def can_process(self, message: Dict[str, Any]) -> bool:
|
||
"""
|
||
检查插件是否可以处理该消息
|
||
|
||
Args:
|
||
message: 消息字典,包含消息的各种属性
|
||
|
||
Returns:
|
||
是否可以处理
|
||
"""
|
||
# 默认实现:检查是否是命令
|
||
if self.command_prefix and self.commands:
|
||
content = message.get("content", "")
|
||
if content.startswith(self.command_prefix):
|
||
command = content[len(self.command_prefix):].split()[0]
|
||
return command in self.commands
|
||
return False
|
||
|
||
def process_message(self, message: Dict[str, Any]) -> Tuple[bool, Optional[str]]:
|
||
"""
|
||
处理消息
|
||
|
||
Args:
|
||
message: 消息字典,包含消息的各种属性,以及发送消息所需的对象
|
||
- wcf: WcfAPI对象,可用于发送消息
|
||
- message_util: 消息工具类,提供更高级的消息处理功能
|
||
|
||
Returns:
|
||
(是否已处理, 处理结果)
|
||
"""
|
||
raise NotImplementedError("子类必须实现此方法")
|
||
|
||
def get_message_dispatch_mode(self, message: Dict[str, Any]) -> str:
|
||
"""返回当前消息应采用的执行模式。
|
||
|
||
默认行为:
|
||
1. 优先读取插件自身 `config.toml` 里的 `[runtime] message_dispatch_mode`;
|
||
2. 若未配置,则回退为 `sync`,保持历史行为不变;
|
||
3. 长任务插件如果需要“按命令动态切换前后台”,可以在子类中覆盖本方法。
|
||
"""
|
||
plugin_config = getattr(self, "_config", {}) or {}
|
||
runtime_config = plugin_config.get("runtime", {}) if isinstance(plugin_config, dict) else {}
|
||
runtime_config = runtime_config if isinstance(runtime_config, dict) else {}
|
||
raw_mode = runtime_config.get("message_dispatch_mode") or runtime_config.get("dispatch_mode") or "sync"
|
||
return self.normalize_message_dispatch_mode(raw_mode)
|
||
|
||
# ---------------- 插件定时调度能力(可选实现) ----------------
|
||
def get_schedule_actions(self) -> List[Dict[str, Any]]:
|
||
"""返回插件支持的可调度动作定义列表。
|
||
|
||
每项示例:
|
||
{
|
||
"action_key": "daily_push",
|
||
"name": "每日推送",
|
||
"description": "给目标群发送每日内容",
|
||
"trigger_type": "at_times",
|
||
"trigger_config": {"time_list": ["09:00"]},
|
||
"target_scope": "all_enabled_groups",
|
||
"target_config": {},
|
||
"payload": {},
|
||
"default_enabled": False
|
||
}
|
||
"""
|
||
return []
|
||
|
||
async def run_scheduled_action(self, action_key: str, context: Dict[str, Any]) -> Dict[str, Any]:
|
||
"""执行调度动作(插件可覆盖)。
|
||
|
||
Returns:
|
||
dict: {"success": bool, "summary": str, "detail": dict}
|
||
"""
|
||
return {
|
||
"success": False,
|
||
"summary": f"插件未实现调度动作: {action_key}",
|
||
"detail": {"action_key": action_key},
|
||
}
|