-
Notifications
You must be signed in to change notification settings - Fork 13.1k
fix(native-federation): thread messages also being treated as quotes #37077
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
Looks like this PR is not ready to merge, because of the following issues:
Please fix the issues and try again If you have any trouble, please check the PR guidelines |
|
WalkthroughRefactors Matrix message relation handling to explicitly guard relation presence, distinguish threads, rich replies, and edits, extract quote/thread IDs accordingly, and adjust error logging argument order; also bumps @rocket.chat/federation-sdk from 0.1.9 to 0.1.10 in two EE package.json files. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant MX as Matrix Event
participant H as Message Handler
participant Logger as Logger
MX->>H: receive message event (content)
alt content has `m.relates_to`
H->>H: set hasRelation = true
alt relation.rel_type == "m.thread"
H->>H: mark as thread message\nextract threadRootEventId
else relation.rel_type == "m.replace"
H->>H: mark as edit\nuse relation.event_id to find original
else
H->>H: other relation types (no thread/edit)
end
else no `m.relates_to`
alt content has `m.in_reply_to`
H->>H: mark as rich reply\nextract quoteMessageEventId
else
H->>H: regular message
end
end
H-->>MX: proceed with mapped metadata (thread/quote/edit)
opt on error
H->>Logger: logger.error(error, "Error processing Matrix message:")
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Jira integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (1)
yarn.lockis excluded by!**/yarn.lock,!**/*.lock
📒 Files selected for processing (3)
ee/apps/federation-service/package.json(1 hunks)ee/packages/federation-matrix/package.json(1 hunks)ee/packages/federation-matrix/src/events/message.ts(2 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
ee/packages/federation-matrix/src/events/message.ts (1)
packages/core-typings/src/IMessage/IMessage.ts (2)
isThreadMessage(317-317)isEditedMessage(264-271)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
- GitHub Check: 📦 Build Packages
- GitHub Check: CodeQL-Build
- GitHub Check: CodeQL-Build
| const hasRelation = relation && 'rel_type' in relation; | ||
|
|
||
| const isThreadMessage = hasRelation && relation.rel_type === 'm.thread'; | ||
|
|
||
| const threadRootEventId = isThreadMessage && relation.event_id; | ||
|
|
||
| const quoteMessageEventId = relation && 'm.in_reply_to' in relation && relation['m.in_reply_to']?.event_id; | ||
| // SPEC: Though rich replies form a relationship to another event, they do not use rel_type to create this relationship. | ||
| // Instead, a subkey named m.in_reply_to is used to describe the reply’s relationship, | ||
| const isRichReply = relation && !('rel_type' in relation) && 'm.in_reply_to' in relation; | ||
|
|
||
| const quoteMessageEventId = isRichReply && relation['m.in_reply_to']?.event_id; | ||
|
|
||
| const thread = threadRootEventId ? await getThreadMessageId(threadRootEventId) : undefined; | ||
|
|
||
| const isEditedMessage = relation?.rel_type === 'm.replace'; | ||
| if (isEditedMessage && relation?.event_id && data.content['m.new_content']) { | ||
| const isEditedMessage = hasRelation && relation.rel_type === 'm.replace'; | ||
| if (isEditedMessage && relation.event_id && data.content['m.new_content']) { | ||
| logger.debug('Received edited message from Matrix, updating existing message'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Restore quote detection when rel_type is present on replies.
Element and other Matrix homeservers routinely send rich replies with m.relates_to.rel_type = "m.reference" (per MSC2677). By requiring the property to be absent (!('rel_type' in relation)), we now skip quote handling for those events, so legitimate replies regress to plain messages. Please loosen the guard to treat replies as quotes whenever m.in_reply_to exists, except for the explicit thread/edit types.
- const hasRelation = relation && 'rel_type' in relation;
-
- const isThreadMessage = hasRelation && relation.rel_type === 'm.thread';
+ const hasRelation = relation && 'rel_type' in relation;
+ const relationRelType = hasRelation ? relation.rel_type : undefined;
+
+ const isThreadMessage = relationRelType === 'm.thread';
@@
- const isRichReply = relation && !('rel_type' in relation) && 'm.in_reply_to' in relation;
+ const isRichReply =
+ !!relation &&
+ 'm.in_reply_to' in relation &&
+ relationRelType !== 'm.thread' &&
+ relationRelType !== 'm.replace';
@@
- const isEditedMessage = hasRelation && relation.rel_type === 'm.replace';
+ const isEditedMessage = relationRelType === 'm.replace';📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const hasRelation = relation && 'rel_type' in relation; | |
| const isThreadMessage = hasRelation && relation.rel_type === 'm.thread'; | |
| const threadRootEventId = isThreadMessage && relation.event_id; | |
| const quoteMessageEventId = relation && 'm.in_reply_to' in relation && relation['m.in_reply_to']?.event_id; | |
| // SPEC: Though rich replies form a relationship to another event, they do not use rel_type to create this relationship. | |
| // Instead, a subkey named m.in_reply_to is used to describe the reply’s relationship, | |
| const isRichReply = relation && !('rel_type' in relation) && 'm.in_reply_to' in relation; | |
| const quoteMessageEventId = isRichReply && relation['m.in_reply_to']?.event_id; | |
| const thread = threadRootEventId ? await getThreadMessageId(threadRootEventId) : undefined; | |
| const isEditedMessage = relation?.rel_type === 'm.replace'; | |
| if (isEditedMessage && relation?.event_id && data.content['m.new_content']) { | |
| const isEditedMessage = hasRelation && relation.rel_type === 'm.replace'; | |
| if (isEditedMessage && relation.event_id && data.content['m.new_content']) { | |
| logger.debug('Received edited message from Matrix, updating existing message'); | |
| const hasRelation = relation && 'rel_type' in relation; | |
| const relationRelType = hasRelation ? relation.rel_type : undefined; | |
| const isThreadMessage = relationRelType === 'm.thread'; | |
| const threadRootEventId = isThreadMessage && relation.event_id; | |
| // SPEC: Though rich replies form a relationship to another event, they do not use rel_type to create this relationship. | |
| // Instead, a subkey named m.in_reply_to is used to describe the reply’s relationship, | |
| const isRichReply = | |
| !!relation && | |
| 'm.in_reply_to' in relation && | |
| relationRelType !== 'm.thread' && | |
| relationRelType !== 'm.replace'; | |
| const quoteMessageEventId = isRichReply && relation['m.in_reply_to']?.event_id; | |
| const thread = threadRootEventId ? await getThreadMessageId(threadRootEventId) : undefined; | |
| const isEditedMessage = relationRelType === 'm.replace'; | |
| if (isEditedMessage && relation.event_id && data.content['m.new_content']) { | |
| logger.debug('Received edited message from Matrix, updating existing message'); |
🤖 Prompt for AI Agents
In ee/packages/federation-matrix/src/events/message.ts around lines 140 to 156,
the current check treats a reply as a rich reply only when rel_type is absent,
which skips quote handling for replies that include rel_type (e.g.
"m.reference"); update the logic so that a reply is considered a rich reply
whenever relation['m.in_reply_to'] exists (and relation is defined), but still
exclude events that are explicit threads (rel_type === 'm.thread') or edits
(rel_type === 'm.replace'). Concretely, compute hasRelation safely, keep
isThreadMessage and isEditedMessage as-is, then set isRichReply = relation &&
relation['m.in_reply_to'] && !isThreadMessage && !isEditedMessage so
quoteMessageEventId is derived when m.in_reply_to.event_id exists; ensure
threadRootEventId and edits remain unaffected.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## develop #37077 +/- ##
===========================================
+ Coverage 67.39% 67.40% +0.01%
===========================================
Files 3328 3328
Lines 113342 113342
Branches 20563 20565 +2
===========================================
+ Hits 76386 76401 +15
+ Misses 34356 34338 -18
- Partials 2600 2603 +3
Flags with carried forward coverage won't be shown. Click here to find out more. 🚀 New features to boost your workflow:
|
Proposed changes (including videos or screenshots)
Issue(s)
Steps to test or reproduce
Further comments
FDR-165
RocketChat/homeserver#231
Summary by CodeRabbit
New Features
Bug Fixes
Stability
Chores