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
1 change: 1 addition & 0 deletions lark_oapi/channel/channel.py
Original file line number Diff line number Diff line change
Expand Up @@ -1156,6 +1156,7 @@ async def _handle_message_event(self, data: Any) -> None:
event_id=event_id,
message_event=message,
sender=sender,
bot_open_id=self._bot_open_id,
)
if inbound is None:
return
Expand Down
6 changes: 5 additions & 1 deletion lark_oapi/channel/normalize/pipeline.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ async def process(
event_id: Optional[str],
message_event: Any,
sender: Any,
bot_open_id: Optional[str] = None,
) -> Optional[InboundMessage]:
"""Return InboundMessage or None if the event was deduped / filtered."""
msg = _message_to_dict(message_event)
Expand Down Expand Up @@ -159,8 +160,10 @@ async def process(
content = parse_message_content(message_type, msg.get("content"))

# Process mentions for text / post (node-aligned: extract → resolve).
# ``bot_open_id`` lets extract_mentions flag self-mentions so
# ``InboundMessage.mentioned_bot`` is correct (fixes #134).
raw_mentions = msg.get("mentions") or []
ext = extract_mentions(raw_mentions)
ext = extract_mentions(raw_mentions, bot_open_id=bot_open_id)
mentions: List[Mention] = list(ext.mention_list)
mentioned_all = ext.mentioned_all
if isinstance(content, TextContent):
Expand Down Expand Up @@ -257,6 +260,7 @@ async def process(
sender=sender_identity,
mentions=mentions,
mentioned_all=mentioned_all,
mentioned_bot=ext.mentioned_bot,
reply=reply,
content=content,
raw=msg if isinstance(msg, dict) else {},
Expand Down
51 changes: 51 additions & 0 deletions lark_oapi/channel/tests/test_pipeline.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,3 +186,54 @@ async def fetch_message(mid: str):
inbound = await p.process(event_id="e", message_event=msg, sender=_sender())
assert isinstance(inbound.content, InteractiveContent)
assert inbound.content.card_version == "v2"


# ---------------------------------------------------------------------------
# mentioned_bot (#134): the field must be set when the bot is in mentions[]
# ---------------------------------------------------------------------------


@pytest.mark.asyncio
async def test_mentioned_bot_true_when_bot_in_mentions():
msg = _msg(
mentions=[
{"key": "@_user_1", "id": {"open_id": "ou_user"}, "name": "Alice"},
{"key": "@_user_2", "id": {"open_id": "ou_bot"}, "name": "my-bot"},
]
)
p = InboundPipeline(PipelineConfig(), PipelineDeps())
inbound = await p.process(
event_id="e", message_event=msg, sender=_sender(), bot_open_id="ou_bot"
)
assert inbound is not None
assert inbound.mentioned_bot is True
# the bot self-mention is excluded from the public mention list
assert [m.open_id for m in inbound.mentions] == ["ou_user"]


@pytest.mark.asyncio
async def test_mentioned_bot_false_without_bot_open_id():
msg = _msg(
mentions=[{"key": "@_user_1", "id": {"open_id": "ou_user"}, "name": "Alice"}]
)
p = InboundPipeline(PipelineConfig(), PipelineDeps())
inbound = await p.process(event_id="e", message_event=msg, sender=_sender())
assert inbound is not None
assert inbound.mentioned_bot is False


@pytest.mark.asyncio
async def test_mentioned_bot_false_when_bot_not_mentioned():
msg = _msg(
mentions=[
{"key": "@_user_1", "id": {"open_id": "ou_user"}, "name": "Alice"},
{"key": "@_user_2", "id": {"open_id": "ou_other"}, "name": "Bob"},
]
)
p = InboundPipeline(PipelineConfig(), PipelineDeps())
inbound = await p.process(
event_id="e", message_event=msg, sender=_sender(), bot_open_id="ou_bot"
)
assert inbound is not None
assert inbound.mentioned_bot is False
assert len(inbound.mentions) == 2