Skip to content
Open
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
4 changes: 4 additions & 0 deletions gateway/platforms/feishu.py
Original file line number Diff line number Diff line change
Expand Up @@ -2760,8 +2760,12 @@ async def _process_inbound_message(
reply_to_message_id = (
getattr(message, "parent_id", None)
or getattr(message, "upper_message_id", None)
# Some Lark reply/topic payloads omit parent_id but still carry root_id.
or getattr(message, "root_id", None)
or None
)
if reply_to_message_id == message_id:
reply_to_message_id = None
reply_to_text = await self._fetch_message_text(reply_to_message_id) if reply_to_message_id else None

sender_primary = (
Expand Down
1 change: 1 addition & 0 deletions scripts/release.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@
# Slack ephemeral slash-ack salvage (May 2026)
"probepark@users.noreply.github.com": "probepark",
# Slack batch salvage (May 2026)
"me@promplate.dev": "CNSeniorious000",
"280484231+prive-fe-bot@users.noreply.github.com": "priveperfumes",
"amr@ghanem.sa": "amroessam",
"paperlantern.agent@gmail.com": "Hinotoi-agent",
Expand Down
55 changes: 35 additions & 20 deletions tests/gateway/test_feishu.py
Original file line number Diff line number Diff line change
Expand Up @@ -1819,29 +1819,26 @@ def test_process_inbound_group_message_keeps_group_type_when_chat_lookup_falls_b
event = adapter._dispatch_inbound_event.await_args.args[0]
self.assertEqual(event.source.chat_type, "group")

@patch.dict(os.environ, {}, clear=True)
def test_process_inbound_message_fetches_reply_to_text(self):
def _process_feishu_text_message(self, *, fetched_text="引用消息内容", **message_overrides):
from gateway.config import PlatformConfig
from gateway.platforms.feishu import FeishuAdapter

adapter = FeishuAdapter(PlatformConfig())
adapter._dispatch_inbound_event = AsyncMock()
adapter.get_chat_info = AsyncMock(
return_value={"chat_id": "oc_chat", "name": "Feishu DM", "type": "dm"}
)
adapter._resolve_sender_profile = AsyncMock(
return_value={"user_id": "ou_user", "user_name": "张三", "user_id_alt": None}
)
adapter._fetch_message_text = AsyncMock(return_value="父消息内容")
message = SimpleNamespace(
chat_id="oc_chat",
thread_id=None,
parent_id="om_parent",
upper_message_id=None,
message_type="text",
content='{"text":"reply"}',
message_id="om_reply",
)
adapter.get_chat_info = AsyncMock(return_value={"chat_id": "oc_chat", "name": "Feishu DM", "type": "dm"})
adapter._resolve_sender_profile = AsyncMock(return_value={"user_id": "ou_user", "user_name": "张三", "user_id_alt": None})
adapter._fetch_message_text = AsyncMock(return_value=fetched_text)
message_fields = {
"chat_id": "oc_chat",
"thread_id": None,
"parent_id": None,
"upper_message_id": None,
"message_type": "text",
"content": '{"text":"reply"}',
"message_id": "om_reply",
}
message_fields.update(message_overrides)
message = SimpleNamespace(**message_fields)

asyncio.run(
adapter._process_inbound_message(
Expand All @@ -1850,13 +1847,31 @@ def test_process_inbound_message_fetches_reply_to_text(self):
sender_id=SimpleNamespace(open_id="ou_user", user_id=None, union_id=None),
is_bot=False,
chat_type="p2p",
message_id="om_reply",
message_id=message.message_id,
)
)
return adapter, adapter._dispatch_inbound_event.await_args.args[0]

event = adapter._dispatch_inbound_event.await_args.args[0]
@patch.dict(os.environ, {}, clear=True)
def test_process_inbound_message_fetches_reply_to_text(self):
adapter, event = self._process_feishu_text_message(parent_id="om_parent", fetched_text="父消息内容")
self.assertEqual(event.reply_to_message_id, "om_parent")
self.assertEqual(event.reply_to_text, "父消息内容")
adapter._fetch_message_text.assert_awaited_once_with("om_parent")

@patch.dict(os.environ, {}, clear=True)
def test_process_inbound_message_fetches_reply_to_text_from_root_id(self):
adapter, event = self._process_feishu_text_message(root_id="om_root", fetched_text="根消息内容")
self.assertEqual(event.reply_to_message_id, "om_root")
self.assertEqual(event.reply_to_text, "根消息内容")
adapter._fetch_message_text.assert_awaited_once_with("om_root")

@patch.dict(os.environ, {}, clear=True)
def test_process_inbound_message_ignores_self_root_id(self):
adapter, event = self._process_feishu_text_message(root_id="om_reply")
self.assertIsNone(event.reply_to_message_id)
self.assertIsNone(event.reply_to_text)
adapter._fetch_message_text.assert_not_awaited()

@patch.dict(os.environ, {}, clear=True)
def test_send_replies_in_thread_when_thread_metadata_present(self):
Expand Down