Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion gateway/platforms/feishu.py
Original file line number Diff line number Diff line change
Expand Up @@ -3289,9 +3289,26 @@ async def _send_raw_message(
content=payload,
uuid_value=str(uuid.uuid4()),
)
request = self._build_create_message_request("chat_id", body)
receive_id_type = self._detect_receive_id_type(chat_id)
request = self._build_create_message_request(receive_id_type, body)
return await asyncio.to_thread(self._client.im.v1.message.create, request)

@staticmethod
def _detect_receive_id_type(receive_id: str) -> str:
"""Detect the correct receive_id_type based on ID prefix.

Feishu's im.message.create API requires different receive_id_type values:
- User open_id (prefix 'ou_') → 'open_id'
- Group chat_id (prefix 'oc_') → 'chat_id'
- Union ID (prefix 'on_') → 'union_id'
"""
if receive_id.startswith("ou_"):
return "open_id"
if receive_id.startswith("on_"):
return "union_id"
# Default to chat_id for group chats (oc_) and any other format
return "chat_id"

@staticmethod
def _response_succeeded(response: Any) -> bool:
return bool(response and getattr(response, "success", lambda: False)())
Expand Down
24 changes: 24 additions & 0 deletions tests/gateway/test_feishu.py
Original file line number Diff line number Diff line change
Expand Up @@ -2855,3 +2855,27 @@ async def _direct(func, *args, **kwargs):
result = asyncio.run(adapter._resolve_sender_name_from_api("ou_broken"))

self.assertIsNone(result)


class TestDetectReceiveIdType(unittest.TestCase):
"""Regression tests for #9980: receive_id_type detection based on ID prefix."""

def test_open_id_prefix(self):
from gateway.platforms.feishu import FeishuAdapter
self.assertEqual(FeishuAdapter._detect_receive_id_type("ou_245ad75b"), "open_id")

def test_chat_id_prefix(self):
from gateway.platforms.feishu import FeishuAdapter
self.assertEqual(FeishuAdapter._detect_receive_id_type("oc_1234567890"), "chat_id")

def test_union_id_prefix(self):
from gateway.platforms.feishu import FeishuAdapter
self.assertEqual(FeishuAdapter._detect_receive_id_type("on_abcdef"), "union_id")

def test_unknown_prefix_defaults_to_chat_id(self):
from gateway.platforms.feishu import FeishuAdapter
self.assertEqual(FeishuAdapter._detect_receive_id_type("some_unknown_id"), "chat_id")

def test_empty_string_defaults_to_chat_id(self):
from gateway.platforms.feishu import FeishuAdapter
self.assertEqual(FeishuAdapter._detect_receive_id_type(""), "chat_id")