116 lines
3.8 KiB
Python
116 lines
3.8 KiB
Python
|
|
from __future__ import annotations
|
|||
|
|
|
|||
|
|
import logging
|
|||
|
|
from collections.abc import Callable, Awaitable
|
|||
|
|
from dataclasses import dataclass, field
|
|||
|
|
|
|||
|
|
from yuxi.channel.sdk.actions.names import MessageAction
|
|||
|
|
|
|||
|
|
logger = logging.getLogger(__name__)
|
|||
|
|
|
|||
|
|
type ActionHandler = Callable[..., Awaitable[dict]]
|
|||
|
|
|
|||
|
|
|
|||
|
|
@dataclass
|
|||
|
|
class ActionDescriptor:
|
|||
|
|
action: MessageAction
|
|||
|
|
description: str = ""
|
|||
|
|
parameters: list[dict] = field(default_factory=list)
|
|||
|
|
requires_trusted_sender: bool = False
|
|||
|
|
|
|||
|
|
def to_capability(self) -> dict:
|
|||
|
|
return {
|
|||
|
|
"action": self.action.value,
|
|||
|
|
"description": self.description,
|
|||
|
|
"parameters": self.parameters,
|
|||
|
|
"requires_trusted_sender": self.requires_trusted_sender,
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
class MessageActionRegistry:
|
|||
|
|
"""消息动作注册表。
|
|||
|
|
|
|||
|
|
渠道插件通过 register() 声明支持的动作及对应 handler。
|
|||
|
|
一次注册同时提供 supports_action() 查询和 execute_action() 执行能力。
|
|||
|
|
消除"声明了但没实现"或"实现了但没声明"的不一致。
|
|||
|
|
|
|||
|
|
对齐 OpenClaw ChannelMessageActionAdapter 的设计理念:
|
|||
|
|
- 注册即声明:register() 同时维护 handlers 和 descriptors
|
|||
|
|
- supports_action() 和 execute_action() 共用同一注册表
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
def __init__(self):
|
|||
|
|
self._handlers: dict[MessageAction, ActionHandler] = {}
|
|||
|
|
self._descriptors: dict[MessageAction, ActionDescriptor] = {}
|
|||
|
|
|
|||
|
|
def register(
|
|||
|
|
self,
|
|||
|
|
action: MessageAction,
|
|||
|
|
handler: ActionHandler,
|
|||
|
|
*,
|
|||
|
|
description: str = "",
|
|||
|
|
parameters: list[dict] | None = None,
|
|||
|
|
requires_trusted_sender: bool = False,
|
|||
|
|
) -> MessageActionRegistry:
|
|||
|
|
self._handlers[action] = handler
|
|||
|
|
self._descriptors[action] = ActionDescriptor(
|
|||
|
|
action=action,
|
|||
|
|
description=description,
|
|||
|
|
parameters=parameters or [],
|
|||
|
|
requires_trusted_sender=requires_trusted_sender,
|
|||
|
|
)
|
|||
|
|
return self
|
|||
|
|
|
|||
|
|
def supports_action(self, action: MessageAction | str) -> bool:
|
|||
|
|
if isinstance(action, str):
|
|||
|
|
try:
|
|||
|
|
action = MessageAction(action)
|
|||
|
|
except ValueError:
|
|||
|
|
return False
|
|||
|
|
return action in self._handlers
|
|||
|
|
|
|||
|
|
def describe_actions(self) -> list[ActionDescriptor]:
|
|||
|
|
return list(self._descriptors.values())
|
|||
|
|
|
|||
|
|
def list_actions(self) -> list[MessageAction]:
|
|||
|
|
return list(self._handlers.keys())
|
|||
|
|
|
|||
|
|
def requires_trusted_sender(self, action: MessageAction | str) -> bool:
|
|||
|
|
if isinstance(action, str):
|
|||
|
|
try:
|
|||
|
|
action = MessageAction(action)
|
|||
|
|
except ValueError:
|
|||
|
|
return False
|
|||
|
|
desc = self._descriptors.get(action)
|
|||
|
|
return desc.requires_trusted_sender if desc else False
|
|||
|
|
|
|||
|
|
async def execute_action(
|
|||
|
|
self,
|
|||
|
|
action: MessageAction | str,
|
|||
|
|
params: dict,
|
|||
|
|
context: dict | None = None,
|
|||
|
|
) -> dict:
|
|||
|
|
if isinstance(action, str):
|
|||
|
|
try:
|
|||
|
|
action = MessageAction(action)
|
|||
|
|
except ValueError:
|
|||
|
|
return {"success": False, "error": f"Unknown action: {action}"}
|
|||
|
|
|
|||
|
|
handler = self._handlers.get(action)
|
|||
|
|
if handler is None:
|
|||
|
|
return {
|
|||
|
|
"success": False,
|
|||
|
|
"error": f"Action '{action.value}' is not supported by this channel",
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
try:
|
|||
|
|
if context:
|
|||
|
|
return await handler(params=params, context=context)
|
|||
|
|
return await handler(**params)
|
|||
|
|
except Exception as e:
|
|||
|
|
logger.exception("Action '%s' execution failed", action.value)
|
|||
|
|
return {"success": False, "error": str(e)}
|
|||
|
|
|
|||
|
|
def get_message_actions(self) -> list[dict]:
|
|||
|
|
"""兼容 BaseChannelPlugin.get_message_actions() 返回格式。"""
|
|||
|
|
return [desc.to_capability() for desc in self.describe_actions()]
|