ForcePilot/backend/package/yuxi/channel/channels/feishu/outbound.py
Kris 9a8a27bf36 feat(channel): 新增渠道网关模块完整实现
本次提交新增了完整的多渠道消息网关系统,包括:
1. 支持飞书、钉钉、Web、Hook 四种渠道的适配器与配置
2. 领域模型层:消息、会话、绑定、出箱等核心实体
3. 应用服务层:管道、中间件、DTO 与业务逻辑
4. 基础设施层:持久化、过滤器、队列等端口实现
5. 接口层:REST API、SSE、WebSocket 通信端点
6. 前端页面与路由配置,添加渠道管理菜单
7. 新增相关依赖包与 docker-compose 部署配置
2026-05-30 21:53:09 +08:00

76 lines
2.3 KiB
Python

from __future__ import annotations
import asyncio
import json
import logging
import lark_oapi as lark
from lark_oapi.api.im.v1 import CreateMessageRequest, CreateMessageRequestBody
from yuxi.channel.domain.model.shared.channel_capabilities import ChannelCapabilities
from yuxi.channel.domain.model.shared.content_chunker import chunk_content
logger = logging.getLogger(__name__)
FEISHU_CAPABILITIES = ChannelCapabilities(
media=False,
group=True,
dm=True,
streaming=False,
typing=False,
reaction=True,
thread=True,
max_text_length=4096,
)
class FeishuOutbound:
def __init__(self, *, app_id: str, app_secret: str) -> None:
self._app_id = app_id
self._app_secret = app_secret
self._client: lark.Client | None = None
@property
def app_id(self) -> str:
return self._app_id
async def start(self) -> None:
self._client = lark.Client.builder().app_id(self._app_id).app_secret(self._app_secret).build()
async def send_text(self, receive_id: str, text: str) -> bool:
if not self._client:
logger.warning("feishu lark client not initialized, skipping send")
return False
chunks = chunk_content(text, FEISHU_CAPABILITIES.max_text_length)
for chunk in chunks:
if not await self._send_via_sdk(self._client, receive_id, chunk):
return False
return True
async def _send_via_sdk(self, client: lark.Client, receive_id: str, text: str) -> bool:
def _sync_send() -> bool:
body = (
CreateMessageRequestBody.builder()
.receive_id(receive_id)
.msg_type("text")
.content(json.dumps({"text": text}))
.build()
)
request = CreateMessageRequest.builder().receive_id_type("chat_id").request_body(body).build()
response = client.im.v1.message.create(request)
if not response.success():
logger.error(
"feishu send failed: code=%s, msg=%s",
response.code,
response.msg,
)
return False
return True
try:
return await asyncio.to_thread(_sync_send)
except Exception:
logger.exception("feishu send error")
return False