from __future__ import annotations from typing import Any from yuxi.channels.adapters.line.card_command import resolve_card_command from yuxi.channels.models import DeliveryResult class LINEActionsAdapter: """LINE Message Actions 适配器,提供 send/reply/broadcast/react/poll 等 actions 注册。""" def __init__(self, adapter): self._adapter = adapter def available_actions(self) -> list[str]: return [ "send", "reply", "broadcast", "multicast", "send_attachment", "sticker", "react", "poll", "download_file", "mark_as_read", "card", ] async def execute(self, action_name: str, params: dict[str, Any]) -> DeliveryResult: actions_map = { "send": self._action_send, "reply": self._action_reply, "broadcast": self._action_broadcast, "multicast": self._action_multicast, "send_attachment": self._action_send_attachment, "sticker": self._action_sticker, "react": self._action_react, "poll": self._action_poll, "download_file": self._action_download_file, "mark_as_read": self._action_mark_as_read, "card": self._action_card, } handler = actions_map.get(action_name) if handler is None: return DeliveryResult(success=False, error=f"Unknown action: {action_name}") return await handler(params) async def _action_send(self, params: dict) -> DeliveryResult: from yuxi.channels.models import ChannelIdentity, ChannelResponse chat_id = params.get("chat_id", params.get("to", "")) content = params.get("content", params.get("text", "")) identity = ChannelIdentity( channel_id=self._adapter.channel_id, channel_type=self._adapter.channel_type, channel_user_id=chat_id, channel_chat_id=chat_id, ) response = ChannelResponse(identity=identity, content=content) return await self._adapter.send(response) async def _action_reply(self, params: dict) -> DeliveryResult: from yuxi.channels.models import ChannelIdentity, ChannelResponse chat_id = params.get("chat_id", params.get("to", "")) content = params.get("content", params.get("text", "")) reply_token = params.get("reply_token", "") identity = ChannelIdentity( channel_id=self._adapter.channel_id, channel_type=self._adapter.channel_type, channel_user_id=chat_id, channel_chat_id=chat_id, ) response = ChannelResponse(identity=identity, content=content, metadata={"reply_token": reply_token}) return await self._adapter.send(response) async def _action_broadcast(self, params: dict) -> DeliveryResult: from yuxi.channels.models import ChannelIdentity, ChannelResponse content = params.get("content", params.get("text", "")) identity = ChannelIdentity( channel_id=self._adapter.channel_id, channel_type=self._adapter.channel_type, channel_user_id="broadcast", channel_chat_id="broadcast", ) response = ChannelResponse(identity=identity, content=content) return await self._adapter.send_broadcast(response) async def _action_multicast(self, params: dict) -> DeliveryResult: from yuxi.channels.models import ChannelIdentity, ChannelResponse user_ids = params.get("user_ids", params.get("to", [])) content = params.get("content", params.get("text", "")) identity = ChannelIdentity( channel_id=self._adapter.channel_id, channel_type=self._adapter.channel_type, channel_user_id="multicast", channel_chat_id="multicast", ) response = ChannelResponse(identity=identity, content=content) return await self._adapter.send_multicast(user_ids, response) async def _action_send_attachment(self, params: dict) -> DeliveryResult: chat_id = params.get("chat_id", params.get("to", "")) media_type = params.get("media_type", params.get("type", "image")) url = params.get("url", params.get("data", "")) return await self._adapter.send_media(chat_id, media_type, url, **params) async def _action_sticker(self, params: dict) -> DeliveryResult: chat_id = params.get("chat_id", params.get("to", "")) package_id = str(params.get("package_id", "1")) sticker_id = str(params.get("sticker_id", "1")) reply_token = params.get("reply_token") return await self._adapter.send_sticker(chat_id, package_id, sticker_id, reply_token) async def _action_react(self, params: dict) -> DeliveryResult: chat_id = params.get("chat_id", params.get("to", "")) msg_id = params.get("msg_id", params.get("message_id", "")) emoji = params.get("emoji", "👍") return await self._adapter.send_reaction(chat_id, msg_id, emoji) async def _action_poll(self, params: dict) -> DeliveryResult: chat_id = params.get("chat_id", params.get("to", "")) question = params.get("question", "") options = params.get("options", []) anonymous = params.get("anonymous", False) duration = params.get("duration_seconds", 0) allow_multiple = params.get("allow_multiple", False) return await self._adapter.create_poll(chat_id, question, options, anonymous, duration, allow_multiple) async def _action_download_file(self, params: dict) -> DeliveryResult: file_id = params.get("file_id", params.get("message_id", "")) try: await self._adapter.download_media(file_id) return DeliveryResult(success=True, message_id=file_id) except Exception as e: return DeliveryResult(success=False, error=str(e)) async def _action_mark_as_read(self, params: dict) -> DeliveryResult: chat_id = params.get("chat_id", params.get("to", "")) ok = await self._adapter.mark_as_read(chat_id) return DeliveryResult(success=ok) async def _action_card(self, params: dict) -> DeliveryResult: from yuxi.channels.models import ChannelIdentity, ChannelResponse chat_id = params.get("chat_id", params.get("to", "")) card_type = params.get("card_type", "") card_data = params.get("card_data", {}) card_msg = resolve_card_command(f"[[card:{card_type}:{import_json_dumps(card_data)}]]") if card_msg is None: return DeliveryResult(success=False, error=f"Failed to build card: {card_type}") identity = ChannelIdentity( channel_id=self._adapter.channel_id, channel_type=self._adapter.channel_type, channel_user_id=chat_id, channel_chat_id=chat_id, ) response = ChannelResponse( identity=identity, content="", metadata={ "line_message_type": "flex", "flex_contents": card_msg.get("contents", {}), "alt_text": card_msg.get("altText", "Card"), }, ) return await self._adapter.send(response) def import_json_dumps(obj): import json return json.dumps(obj, ensure_ascii=False)