新增Twitch IRC协议相关的全套实现,包括: 1. 基础工具类:令牌处理、消息格式化、速率限制、消息去重 2. 核心适配器组件:IRC解析器、消息归一化、外发消息处理 3. API客户端:Helix API封装、认证提供者 4. 配置与部署:配置校验、设置向导 5. 辅助功能:配对管理、健康检查、目标解析等
105 lines
3.6 KiB
Python
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",
|
|
],
|
|
}
|