新增飞书机器人适配器全套功能,包括: - 基础适配器入口与工具导出 - 消息格式化、卡片渲染、回复调度逻辑 - 会话ID生成、模型覆盖策略 - 消息发送缓存、顺序队列管理 - 飞书签名验证、加解密webhook请求 - 审批权限校验、机器人菜单事件处理 - 文档评论、钉消息、语音转码处理 - 静态/动态目录管理、子代理生命周期管理 - 各类工具集:聊天、云盘、文档、知识库API封装
274 lines
8.2 KiB
Python
274 lines
8.2 KiB
Python
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", ""))
|