import asyncio from beaver.foundation.events import OutboundMessage from beaver.interfaces.channels.platforms.feishu import FeishuAdapter class FakeSink: def __init__(self) -> None: self.messages = [] async def accept_inbound(self, message): self.messages.append(message) class FakeFeishuClient: def __init__(self) -> None: self.sent = [] async def send_text(self, *, receive_id_type: str, receive_id: str, text: str): self.sent.append({"receive_id_type": receive_id_type, "receive_id": receive_id, "text": text}) def test_feishu_normalizes_direct_text_event() -> None: async def run() -> None: sink = FakeSink() adapter = FeishuAdapter( channel_id="feishu-main", kind="feishu", mode="websocket", account_id="tenant-main", display_name=None, inbound_sink=sink, secrets={"appId": "app", "appSecret": "secret"}, config={}, client=FakeFeishuClient(), ) await adapter.handle_event_payload( { "event": { "message": { "message_id": "m1", "chat_id": "oc_chat", "chat_type": "p2p", "message_type": "text", "content": "{\"text\":\"hello\"}", }, "sender": {"sender_id": {"open_id": "ou_user"}}, } } ) message = sink.messages[0] assert message.content == "hello" assert message.session_id == "feishu-main:tenant-main:oc_chat" assert message.channel_identity.peer_type == "dm" assert message.channel_identity.user_id == "ou_user" asyncio.run(run()) def test_feishu_group_mention_gate() -> None: async def run() -> None: sink = FakeSink() adapter = FeishuAdapter( channel_id="feishu-main", kind="feishu", mode="websocket", account_id="tenant-main", display_name=None, inbound_sink=sink, secrets={"appId": "app", "appSecret": "secret"}, config={"requireMentionInGroups": True, "botOpenId": "ou_bot"}, client=FakeFeishuClient(), ) await adapter.handle_event_payload( { "event": { "message": { "message_id": "m1", "chat_id": "oc_group", "chat_type": "group", "message_type": "text", "content": "{\"text\":\"hello\"}", "mentions": [], }, "sender": {"sender_id": {"open_id": "ou_user"}}, } } ) await adapter.handle_event_payload( { "event": { "message": { "message_id": "m2", "chat_id": "oc_group", "chat_type": "group", "message_type": "text", "content": "{\"text\":\"hello\"}", "mentions": [{"id": {"open_id": "ou_bot"}}], }, "sender": {"sender_id": {"open_id": "ou_user"}}, } } ) assert len(sink.messages) == 1 asyncio.run(run()) def test_feishu_sends_text_to_chat_id() -> None: async def run() -> None: sink = FakeSink() client = FakeFeishuClient() adapter = FeishuAdapter( channel_id="feishu-main", kind="feishu", mode="websocket", account_id="tenant-main", display_name=None, inbound_sink=sink, secrets={"appId": "app", "appSecret": "secret"}, config={}, client=client, ) await adapter.handle_event_payload( { "event": { "message": { "message_id": "m1", "chat_id": "oc_chat", "chat_type": "p2p", "message_type": "text", "content": "{\"text\":\"hello\"}", }, "sender": {"sender_id": {"open_id": "ou_user"}}, } } ) await adapter.send( OutboundMessage( channel="feishu-main", content="ok", session_id=sink.messages[0].session_id, finish_reason="stop", channel_identity=sink.messages[0].channel_identity, ) ) assert client.sent == [{"receive_id_type": "chat_id", "receive_id": "oc_chat", "text": "ok"}] asyncio.run(run())