ForcePilot/backend/package/yuxi/channels/adapters/twitch/actions.py
Kris 59cd13cf84 feat(twitch): 实现完整的Twitch聊天适配器模块
新增Twitch IRC协议相关的全套实现,包括:
1. 基础工具类:令牌处理、消息格式化、速率限制、消息去重
2. 核心适配器组件:IRC解析器、消息归一化、外发消息处理
3. API客户端:Helix API封装、认证提供者
4. 配置与部署:配置校验、设置向导
5. 辅助功能:配对管理、健康检查、目标解析等
2026-05-12 00:50:10 +08:00

105 lines
3.6 KiB
Python

from __future__ import annotations
from typing import Any
from yuxi.channels.models import ChannelIdentity, ChannelResponse, DeliveryResult
from yuxi.channels.protocols.actions import ActionContext
_ACTION_HANDLERS: dict[str, str] = {}
def _build_response(ctx: ActionContext, adapter: Any, content_key: str = "content") -> ChannelResponse | None:
content = ctx.get(content_key, ctx.get("media_url", ""))
if not ctx.chat_id or not content:
return None
identity = ChannelIdentity(
channel_id="twitch",
channel_type=adapter.channel_type,
channel_user_id="",
channel_chat_id=ctx.chat_id,
)
return ChannelResponse(identity=identity, content=content)
async def handle_action(ctx: ActionContext, adapter: Any) -> DeliveryResult:
if ctx.action == "send":
response = _build_response(ctx, adapter, "content")
if response is None:
return DeliveryResult(success=False, error="missing chat_id or content")
return await adapter.send(response)
if ctx.action == "send_media":
response = _build_response(ctx, adapter, "media_url")
if response is None:
return DeliveryResult(success=False, error="missing chat_id or media_url")
return await adapter.send_media(ctx.chat_id, "url", response.content)
unsupported_hints: dict[str, str] = {
"reply": "Twitch IRC does not support message replies",
"edit": "Twitch IRC does not support message editing",
"unsend": "Twitch IRC does not support message deletion",
"delete_message": "Twitch IRC does not support message deletion",
"edit_message": "Twitch IRC does not support message editing",
"reactions": "Twitch IRC does not support reactions",
"send_reaction": "Twitch IRC does not support reactions",
"polls": "Twitch IRC does not support polls",
"native_commands": "Twitch IRC does not support native commands",
"pin": "Twitch IRC does not support message pinning",
"unpin": "Twitch IRC does not support message unpinning",
}
hint = unsupported_hints.get(ctx.action, f"unknown action: {ctx.action}")
return DeliveryResult(success=False, error=hint)
def supports_action(action: str) -> bool:
return action in {"send", "send_media"}
def describe_message_tool() -> dict[str, Any]:
return {
"send": {
"description": "Send a text message to a Twitch channel",
"parameters": {
"chat_id": {"type": "string", "description": "Channel name (e.g. #channel)"},
"content": {"type": "string", "description": "Text content to send"},
},
},
"send_media": {
"description": "Send a media URL to a Twitch channel (sent as text)",
"parameters": {
"chat_id": {"type": "string", "description": "Channel name (e.g. #channel)"},
"media_url": {"type": "string", "description": "Media URL to send"},
},
},
}
def extract_target_from_args(args: dict[str, Any]) -> dict | None:
chat_id = args.get("chat_id")
if chat_id:
return {"chat_id": chat_id}
return None
def resolve_execution_mode(action: str) -> str:
return "send"
def get_action_stats() -> dict[str, list[str]]:
return {
"implemented": ["send", "send_media"],
"planned": [],
"unsupported": [
"reply",
"edit",
"unsend",
"reactions",
"polls",
"native_commands",
"pin",
"unpin",
"send_reaction",
"delete_message",
"edit_message",
],
}