-
Notifications
You must be signed in to change notification settings - Fork 13.1k
feat: federation message reactions #36420
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
Merged
Merged
Changes from all commits
Commits
Show all changes
17 commits
Select commit
Hold shift + click to select a range
0d69114
chore: adds bridged messages collection support
ricardogarim 0bd3770
feat: adds set and unset reactions capabilities
ricardogarim cff527b
chore: move hooks to ee folder
ricardogarim 160132e
chore: reset files from feat/federation
ricardogarim 6d40cc6
chore: adds emojione lib
ricardogarim e8e93bb
feat: adds set and unset reactions capabilities
ricardogarim adc869d
chore: move hooks to ee folder
ricardogarim 17c1cfd
chore: reset files from feat/federation
ricardogarim 63efdaa
chore: adjusts reactions to new state events approach
ricardogarim 51ab538
chore: removes bridged messages collection
ricardogarim 949bc05
chore: use common matrixUserId on sendMessage
ricardogarim 4afe8c9
xxx
ricardogarim 688377a
chore: simplifies configs
ricardogarim 812a06d
chore: adjusts unseting reactions from rc
ricardogarim 422235d
fix: fixes hook loop on reactions
ricardogarim 26c5686
enables back beforeReacted with federation version validation
ricardogarim 0c3568f
Merge branch 'feat/federation' into feat/federation-reactions
ricardogarim File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,30 @@ | ||
| // import { FederationMatrix } from '@rocket.chat/core-services'; | ||
| import { FederationMatrix } from '@rocket.chat/core-services'; | ||
| import type { IMessage, IUser } from '@rocket.chat/core-typings'; | ||
|
|
||
| // import { callbacks } from '../../../../lib/callbacks'; | ||
| import { callbacks } from '../../../../lib/callbacks'; | ||
|
|
||
| // callbacks.add('federation-event-example', async () => FederationMatrix.handleExample(), callbacks.priority.MEDIUM, 'federation-event-example-handler'); | ||
| callbacks.add( | ||
| 'afterSetReaction', | ||
| async (message: IMessage, params: { user: IUser; reaction: string }): Promise<void> => { | ||
| // Don't federate reactions that came from Matrix | ||
| if (params.user.username?.includes(':')) { | ||
| return; | ||
| } | ||
| await FederationMatrix.sendReaction(message._id, params.reaction, params.user); | ||
| }, | ||
| callbacks.priority.HIGH, | ||
| 'federation-matrix-after-set-reaction', | ||
| ); | ||
|
|
||
| callbacks.add( | ||
| 'afterUnsetReaction', | ||
| async (_message: IMessage, params: { user: IUser; reaction: string; oldMessage: IMessage }): Promise<void> => { | ||
| // Don't federate reactions that came from Matrix | ||
| if (params.user.username?.includes(':')) { | ||
| return; | ||
| } | ||
| await FederationMatrix.removeReaction(params.oldMessage._id, params.reaction, params.user, params.oldMessage); | ||
| }, | ||
| callbacks.priority.HIGH, | ||
| 'federation-matrix-after-unset-reaction', | ||
| ); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,91 @@ | ||
| import type { HomeserverEventSignatures } from '@hs/federation-sdk'; | ||
| import { Message, FederationMatrix } from '@rocket.chat/core-services'; | ||
| import type { Emitter } from '@rocket.chat/emitter'; | ||
| import { Logger } from '@rocket.chat/logger'; | ||
| import { Users, Messages } from '@rocket.chat/models'; // Rooms | ||
| import emojione from 'emojione'; | ||
|
|
||
| const logger = new Logger('federation-matrix:reaction'); | ||
|
|
||
| export function reaction(emitter: Emitter<HomeserverEventSignatures>) { | ||
| emitter.on('homeserver.matrix.reaction', async (data) => { | ||
| try { | ||
| const isSetReaction = data.content?.['m.relates_to']; | ||
|
|
||
| const reactionTargetEventId = isSetReaction?.event_id; | ||
| const reactionKey = isSetReaction?.key; | ||
|
|
||
| const [userPart, domain] = data.sender.split(':'); | ||
| if (!userPart || !domain) { | ||
| logger.error('Invalid Matrix sender ID format:', data.sender); | ||
| return; | ||
| } | ||
|
|
||
| const user = await Users.findOneByUsername(data.sender); | ||
| if (!user) { | ||
| logger.error(`No RC user mapping found for Matrix event ${reactionTargetEventId} ${data.sender}`); | ||
| return; | ||
| } | ||
|
|
||
| if (!isSetReaction) { | ||
| logger.debug(`No relates_to content in reaction event`); | ||
| return; | ||
| } | ||
|
|
||
| const rcMessage = await Messages.findOneByFederationId(reactionTargetEventId); | ||
| if (!rcMessage) { | ||
| logger.debug(`No RC message mapping found for Matrix event ${reactionTargetEventId}`); | ||
| return; | ||
| } | ||
|
|
||
| const reactionEmoji = emojione.toShort(reactionKey); | ||
| await Message.reactToMessage(user._id, reactionEmoji, rcMessage._id, true); | ||
| await Messages.setFederationReactionEventId(data.sender, rcMessage._id, reactionEmoji, data.event_id); | ||
| } catch (error) { | ||
| logger.error('Failed to process Matrix reaction:', error); | ||
| } | ||
| }); | ||
|
|
||
| emitter.on('homeserver.matrix.redaction', async (data) => { | ||
ricardogarim marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| try { | ||
| const redactedEventId = data.redacts; | ||
| if (!redactedEventId) { | ||
| logger.debug('No redacts field in redaction event'); | ||
| return; | ||
| } | ||
|
|
||
| const reactionEvent = await FederationMatrix.getEventById(redactedEventId); | ||
| if (!reactionEvent || reactionEvent.type !== 'm.reaction') { | ||
| logger.debug(`Event ${redactedEventId} is not a reaction event`); | ||
| return; | ||
| } | ||
|
|
||
| const reactionContent = reactionEvent.content?.['m.relates_to']; | ||
| if (!reactionContent) { | ||
| logger.debug('No relates_to content in reaction event'); | ||
| return; | ||
| } | ||
|
|
||
| const targetMessageEventId = reactionContent.event_id; | ||
| const reactionKey = reactionContent.key; | ||
|
|
||
| const rcMessage = await Messages.findOneByFederationId(targetMessageEventId); | ||
| if (!rcMessage) { | ||
| logger.debug(`No RC message found for event ${targetMessageEventId}`); | ||
| return; | ||
| } | ||
|
|
||
| const user = await Users.findOneByUsername(data.sender); | ||
| if (!user) { | ||
| logger.debug(`User not found: ${data.sender}`); | ||
| return; | ||
| } | ||
|
|
||
| const reactionEmoji = emojione.toShort(reactionKey); | ||
| await Message.reactToMessage(user._id, reactionEmoji, rcMessage._id, false); | ||
| await Messages.unsetFederationReactionEventId(redactedEventId, rcMessage._id, reactionEmoji); | ||
| } catch (error) { | ||
| logger.error('Failed to process Matrix reaction redaction:', error); | ||
| } | ||
| }); | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| import type { IMessage, IUser } from '@rocket.chat/core-typings'; | ||
|
|
||
| export interface ICallbackPriority { | ||
| HIGH: number; | ||
| MEDIUM: number; | ||
| LOW: number; | ||
| } | ||
|
|
||
| export interface ICallbacks { | ||
| priority: ICallbackPriority; | ||
| add(hook: string, callback: (...args: any[]) => any, priority?: number, id?: string): void; | ||
| remove(hook: string, id: string): void; | ||
| } | ||
|
|
||
| export interface IFederationCallbackHandlers { | ||
| afterSetReaction?: (message: IMessage, params: { user: IUser; reaction: string }) => Promise<void>; | ||
| afterUnsetReaction?: (message: IMessage, params: { user: IUser; reaction: string; oldMessage: IMessage }) => Promise<void>; | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.