ForcePilot/backend/package/yuxi/channels/adapters/feishu/message_actions.py

274 lines
8.2 KiB
Python
Raw Normal View History

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", ""))