Skip to content

🐛 parse received rich text and replied messages - #181

Merged
StarHeartHunt merged 2 commits into
masterfrom
fix/received-post-and-reply
Nov 11, 2026
Merged

StarHeartHunt merged 2 commits into
masterfrom
fix/received-post-and-reply

Conversation

@StarHeartHunt

Copy link
Copy Markdown
Member

Received rich text (post) and replied messages were hard to use:

  • A received post stayed one Post segment holding the raw content. Its at nodes carry mention keys such as @_user_1 rather than open_ids, as the documentation shows. _check_at_me never saw the leading @, so @bot /cmd sent as rich text, for example with an image attached, never matched a command, and extract_plain_text() returned @@_user_1 /cmd …. Plugins could not see the text, mentions or images inside either.
  • event.reply only had the raw JSON in body.content. Message.deserialize could not parse it with its mentions, because the get message API returns each mention id as a single string, while events give a UserId object.

Changes

Received rich text

deserialize now expands a received post into the same segments as other messages:

Node Segment
text text, with style kept when set
at at, with user_id resolved through mentions; a key without a mention is kept as it is
img image
media media
Others (a, code_block, emotion, hr, …) A generic segment with the node's fields

Paragraphs are joined with \n, and the title becomes the first line. Adjacent plain text is merged, and styled text stays separate. Empty style lists, which Feishu sends on most nodes, are dropped.

As a result:

  • _check_at_me strips the leading @ and sets to_me, as it does for text messages.
  • a and code_block count as text, so extract_plain_text() includes link text and code.
  • Message.serialize() already combines these segments into a post, so a received message can be sent again. Mentions are sent with the resolved open_id instead of the @_user_1 key, which only has a meaning inside the received message.

Behavior change: a received post is no longer a single Post segment. Code that read event.get_message()[0].data["content"] has to use the segments, or the raw event.event.message.content. Sending a Post built with MessageSegment.post() is unchanged.

Replied messages

  • Message.deserialize accepts both Mention from events and ReplyMention from the get message API.
  • Reply.get_message() parses event.reply by its msg_type. Mentions resolve to the ids that the API returned. These are open_ids, because _check_reply does not pass user_id_type.

Testing

  • 9 tests added, and all of them fail without this change. The post structure follows the documentation linked above: no locale wrapper, at nodes with @_user_N keys, and empty style lists.
  • test_check_tome_in_post runs _check_at_me on a @bot /say … post with an image.
  • pytest passes on Python 3.11 and 3.15, with 149 tests. Ruff passes, and Pyright reports nothing new.
  • In a bot that uses nonebot-plugin-alconna 0.62.1, against a local fake Feishu, a received @bot /cmd … post with an image now becomes Text and Image instead of a single Hyper.

This was not tested with messages from a real Feishu client. The received structures come from the documentation only.

A follow-up in nonebot-plugin-alconna can switch its Feishu builder to event.reply.get_message(), instead of passing body.content as the reply message.

A received `post` was kept as one `Post` segment holding the raw content.
Its `at` nodes carry mention keys such as `@_user_1` instead of open_ids, so
`_check_at_me` never found the leading @, commands in rich text never matched,
and plugins could not see the text, mentions or images inside.

Received rich text now becomes the same segments as other messages: `text`,
`at` (resolved to the open_id through `mentions`), `image` and `media`, with
other nodes such as `a`, `code_block` and `emotion` kept as generic segments.
Paragraphs are joined with newlines and the title becomes the first line.
`a` and `code_block` count as text, so their text is in the plain text.
`Message.serialize` already turns these segments back into a post.
`event.reply` only gave the raw JSON in `body.content`, so the replied message
had to be parsed by hand. `Message.deserialize` could not help, because the
get message API returns mentions whose `id` is a single string, while it only
accepted the `UserId` object from events.

`deserialize` now accepts both kinds of mentions, and `Reply.get_message()`
parses the replied message by its type, with mentions resolved like in events.
@StarHeartHunt StarHeartHunt added the bug Something isn't working label Nov 11, 2026
@github-actions

Copy link
Copy Markdown
Contributor

@codecov

codecov Bot commented Nov 11, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 90.07%. Comparing base (fcdfd73) to head (e453209).
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@            Coverage Diff             @@
##           master     #181      +/-   ##
==========================================
+ Coverage   89.19%   90.07%   +0.87%     
==========================================
  Files          11       11              
  Lines        1499     1531      +32     
==========================================
+ Hits         1337     1379      +42     
+ Misses        162      152      -10     
Flag Coverage Δ
unittests 90.07% <100.00%> (+0.87%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@StarHeartHunt
StarHeartHunt merged commit 6de90c1 into master Nov 11, 2026
21 checks passed
@StarHeartHunt
StarHeartHunt deleted the fix/received-post-and-reply branch November 11, 2026 15:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant