from __future__ import annotations from typing import Any FEISHU_MESSAGE_ACTIONS: dict[str, dict[str, Any]] = { "send": { "status": "implemented", "description": "?????????????????????," "api": "im/v1/messages", }, "sendAttachment": { "status": "implemented", "description": "??????/??????/?????????????????????," "api": "im/v1/messages + content", }, "sendCard": { "status": "implemented", "description": "???????????????????????????", "api": "im/v1/messages + card", }, "sendPoll": { "status": "unsupported", "description": "?????????????????????????????????," "api": "N/A", }, "sendSticker": { "status": "planned", "description": "??????????????????," "api": "im/v1/stickers", }, "reply": { "status": "implemented", "description": "??????????????????????????? root_id???," "api": "im/v1/messages/reply", }, "edit": { "status": "implemented", "description": "????????????????????????", "api": "im/v1/messages/{message_id} PATCH", }, "delete": { "status": "implemented", "description": "??????/????????????", "api": "im/v1/messages/{message_id} DELETE", }, "read": { "status": "implemented", "description": "??????????????????", "api": "im/v1/messages/{message_id}/read", }, "react": { "status": "implemented", "description": "????????????????????????," "api": "im/v1/messages/{message_id}/reactions", }, "listReactions": { "status": "implemented", "description": "????????????????????????", "api": "im/v1/messages/{message_id}/reactions GET", }, "pin": { "status": "implemented", "description": "????????????", "api": "im/v1/pins", }, "unpin": { "status": "implemented", "description": "??????????????????", "api": "im/v1/pins/{message_id} DELETE", }, "listPins": { "status": "implemented", "description": "????????????????????????", "api": "im/v1/chats/{chat_id}/pins GET", }, "broadcast": { "status": "unsupported", "description": "???????????????????????????API", "api": "N/A", }, "typing": { "status": "implemented", "description": "通过 Reactions API 实现 typing indicator(含退避/年龄限制)", "api": "im/v1/reactions", }, "thread": { "status": "implemented", "description": "????????????/????????????", "api": "im/v1/threads", }, "streamChunk": { "status": "implemented", "description": "?????????????????????????????????????????????," "api": "im/v1/messages/{message_id} PATCH (streaming)", }, "kick": { "status": "unsupported", "description": "?????? Bot ???????????????????????????," "api": "N/A", }, "ban": { "status": "unsupported", "description": "?????? Bot ?????????????????????," "api": "N/A", }, "mute": { "status": "unsupported", "description": "?????? Bot ?????????????????????", "api": "N/A", }, "leave": { "status": "unsupported", "description": "Bot ???????????????????????????", "api": "N/A", }, "addParticipant": { "status": "planned", "description": "?????????????????????," "api": "im/v1/chats/{chat_id}/members POST", }, "removeParticipant": { "status": "planned", "description": "?????????????????????," "api": "im/v1/chats/{chat_id}/members/{member_id} DELETE", }, "renameGroup": { "status": "planned", "description": "??????????????????", "api": "im/v1/chats/{chat_id} PATCH", }, "setGroupIcon": { "status": "unsupported", "description": "????????????????????? API ???????????????," "api": "N/A", }, "channelInfo": { "status": "implemented", "description": "????????????/????????????", "api": "im/v1/chats/{chat_id} GET", }, "channelList": { "status": "implemented", "description": "????????????????????????????????????", "api": "im/v1/chats GET", }, "channelCreate": { "status": "planned", "description": "????????????", "api": "im/v1/chats POST", }, "channelEdit": { "status": "planned", "description": "??????????????????", "api": "im/v1/chats/{chat_id} PATCH", }, "memberInfo": { "status": "implemented", "description": "????????????/????????????", "api": "contact/v3/users/{user_id} GET", }, "roleInfo": { "status": "unsupported", "description": "?????????????????????API", "api": "N/A", }, "downloadFile": { "status": "implemented", "description": "????????????/??????", "api": "im/v1/messages/{message_id}/resources/{file_key} GET", }, "uploadFile": { "status": "implemented", "description": "????????????", "api": "im/v1/files POST ???im/v1/images POST", }, } _FEISHU_ACTION_MAPPING: dict[str, str] = { "send": "send", "sendAttachment": "send_attachment", "sendPoll": "create_poll", "sendSticker": "sticker", "reply": "reply", "edit": "edit", "delete": "delete", "read": "read", "react": "react", "listReactions": "list_reactions", "pin": "pin", "unpin": "unpin", "listPins": "list_pins", "typing": "typing", "broadcast": "broadcast", "thread": "thread_create", "streamChunk": "stream", "kick": "kick", "ban": "ban", "mute": "mute", "leave": "leave_group", "addParticipant": "add_participant", "removeParticipant": "remove_participant", "renameGroup": "rename_group", "setGroupIcon": "set_group_icon", "channelInfo": "channel_info", "channelList": "channel_list", "channelCreate": "channel_create", "channelEdit": "channel_edit", "memberInfo": "member_info", "roleInfo": "role_info", "downloadFile": "download_file", "uploadFile": "upload_file", } def register_feishu_actions() -> None: from yuxi.channels.message_actions import ActionDeclaration, ActionRegistry, ActionStatus, MessageAction declarations: dict[MessageAction, ActionDeclaration] = {} for action_name, info in FEISHU_MESSAGE_ACTIONS.items(): mapped = _FEISHU_ACTION_MAPPING.get(action_name) if mapped is None: continue try: action_enum = MessageAction(mapped) except ValueError: continue status_str = info.get("status", "unsupported") status = { "implemented": ActionStatus.SUPPORTED, "planned": ActionStatus.PARTIAL, "unsupported": ActionStatus.UNSUPPORTED, }.get(status_str, ActionStatus.UNSUPPORTED) declarations[action_enum] = ActionDeclaration( action=action_enum, status=status, reason=info.get("description", ""), impl=info.get("api", ""), ) ActionRegistry.register_channel_actions("feishu", declarations) def resolve_feishu_action_target(params: dict[str, Any]) -> str | None: chat_id = params.get("chatId", params.get("chat_id", "")) open_id = params.get("openId", params.get("open_id", "")) msg_id = params.get("messageId", params.get("message_id", "")) if msg_id: return msg_id if chat_id: return chat_id if open_id: return open_id return None def resolve_feishu_chat_id(params: dict[str, Any]) -> str: chat_id = params.get("chatId", params.get("chat_id", "")) if chat_id: if chat_id.startswith("oc_"): return f"feishu:group:{chat_id}" return chat_id return "" def resolve_feishu_member_id(params: dict[str, Any]) -> str: open_id = params.get("openId", params.get("open_id", "")) member_id = params.get("memberId", params.get("member_id", "")) return open_id or member_id or "" def resolve_feishu_message_id(params: dict[str, Any]) -> str: return params.get("messageId", params.get("message_id", ""))