Skip to content
10 changes: 5 additions & 5 deletions packages/federation-sdk/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
import 'reflect-metadata';

import type { Emitter } from '@rocket.chat/emitter';
import type {
EventStagingStore,
Membership,
MessageType,
} from '@rocket.chat/federation-core';
import type { EventStagingStore } from '@rocket.chat/federation-core';
import type {
EventID,
EventStore,
Expand Down Expand Up @@ -91,6 +87,10 @@ export type HomeserverEventSignatures = {
event_id: EventID;
event: PduForType<'m.room.encrypted'>;
};
'homeserver.matrix.room.create': {
event: PduForType<'m.room.create'>;
event_id: EventID;
};
'homeserver.matrix.message': {
event_id: EventID;
event: PduForType<'m.room.message'>;
Expand Down
24 changes: 23 additions & 1 deletion packages/federation-sdk/src/services/invite.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,14 @@ import {
import { singleton } from 'tsyringe';
import { ConfigService } from './config.service';
import { EventAuthorizationService } from './event-authorization.service';
import { EventEmitterService } from './event-emitter.service';
import { EventService } from './event.service';
import { FederationService } from './federation.service';
import { StateService, UnknownRoomError } from './state.service';
import {
RoomInfoNotReadyError,
StateService,
UnknownRoomError,
} from './state.service';
// TODO: Have better (detailed/specific) event input type
export type ProcessInviteEvent = {
event: EventBase;
Expand All @@ -37,6 +43,7 @@ export class InviteService {
private readonly stateService: StateService,
private readonly configService: ConfigService,
private readonly eventAuthorizationService: EventAuthorizationService,
private readonly emitterService: EventEmitterService,
) {}

/**
Expand Down Expand Up @@ -103,6 +110,11 @@ export class InviteService {
// without it join events will not be processed if /event/{eventId} causes problems
void federationService.sendEventToAllServersInRoom(inviteEvent);

this.emitterService.emit('homeserver.matrix.membership', {
event_id: inviteEvent.eventId,
event: inviteEvent.event,
});

return {
event_id: inviteEvent.eventId,
event: PersistentEventFactory.createFromRawEvent(
Expand Down Expand Up @@ -133,6 +145,11 @@ export class InviteService {
// let everyone know
void federationService.sendEventToAllServersInRoom(inviteEvent);

this.emitterService.emit('homeserver.matrix.membership', {
event_id: inviteEvent.eventId,
event: inviteEvent.event,
});

return {
event_id: inviteEvent.eventId,
event: PersistentEventFactory.createFromRawEvent(
Expand Down Expand Up @@ -226,6 +243,11 @@ export class InviteService {

// we do not send transaction here
// the asking server will handle the transactions

this.emitterService.emit('homeserver.matrix.membership', {
event_id: inviteEvent.eventId,
event: inviteEvent.event,
});
}

// we are not the host of the server
Expand Down
16 changes: 14 additions & 2 deletions packages/federation-sdk/src/services/room.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import {
roomPowerLevelsEvent,
} from '@rocket.chat/federation-core';
import { delay, inject, singleton } from 'tsyringe';
import { FederationService } from './federation.service';

import {
ForbiddenError,
Expand All @@ -34,11 +33,17 @@ import { EventStagingRepository } from '../repositories/event-staging.repository
import { EventRepository } from '../repositories/event.repository';
import { RoomRepository } from '../repositories/room.repository';
import { ConfigService } from './config.service';
import { EventAuthorizationService } from './event-authorization.service';
import { EventEmitterService } from './event-emitter.service';
import { EventFetcherService } from './event-fetcher.service';
import { EventService } from './event.service';
import { FederationService } from './federation.service';
import { InviteService } from './invite.service';
import { StateService, UnknownRoomError } from './state.service';
import {
RoomInfoNotReadyError,
StateService,
UnknownRoomError,
} from './state.service';

@singleton()
export class RoomService {
Expand All @@ -57,6 +62,7 @@ export class RoomService {
private readonly eventRepository: EventRepository,
@inject(delay(() => EventStagingRepository))
private readonly eventStagingRepository: EventStagingRepository,
private readonly emitterService: EventEmitterService,
) {}

private validatePowerLevelChange(
Expand Down Expand Up @@ -890,6 +896,11 @@ export class RoomService {

void federationService.sendEventToAllServersInRoom(membershipEvent);

this.emitterService.emit('homeserver.matrix.membership', {
event_id: membershipEvent.eventId,
event: membershipEvent.event,
});

return membershipEvent.eventId;
}

Expand Down Expand Up @@ -1268,6 +1279,7 @@ export class RoomService {

await this.roomRepository.markRoomAsDeleted(roomId, event.eventId);

// TODO: check if all sendEventToAllServersInRoom should be followed by an emitter
void this.federationService.sendEventToAllServersInRoom(event);
Comment on lines +1282 to 1283
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Track the TODO for consistent event emission pattern.

The TODO comment suggests that the pattern of emitting events after sendEventToAllServersInRoom may need to be applied consistently throughout the codebase. Consider creating an issue to track this refactoring work.

Do you want me to scan the codebase for other occurrences of sendEventToAllServersInRoom that might need the same emission pattern?

🤖 Prompt for AI Agents
In packages/federation-sdk/src/services/room.service.ts around lines 1282-1283,
the TODO notes inconsistency about whether sendEventToAllServersInRoom should be
followed by an emitter; create a tracked fix: open an issue in the repo (include
context, affected method, and proposed change to always emit after
sendEventToAllServersInRoom), replace the inline TODO with a brief reference to
that issue (e.g., "see issue #NNN") and, as part of the issue or a follow-up PR,
scan the codebase for all usages of sendEventToAllServersInRoom and update each
callsite to consistently emit the event (or document justified exceptions), and
add unit/integration tests to validate the emission behavior where applicable.


logger.info(`Successfully marked room ${roomId} as tombstone`);
Expand Down
13 changes: 9 additions & 4 deletions packages/federation-sdk/src/services/staging-area.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -268,12 +268,17 @@ export class StagingAreaService {
private async processNotificationStage(event: EventStagingStore) {
this.logger.debug(`Notifying clients about event ${event._id}`);

const {
_id: eventId,
event: { room_id: roomId },
} = event;
const { _id: eventId, roomId } = event;

switch (true) {
case event.event.type === 'm.room.create':
{
this.eventEmitterService.emit('homeserver.matrix.room.create', {
event_id: eventId,
event: event.event,
});
}
break;
case event.event.type === 'm.room.message':
this.eventEmitterService.emit('homeserver.matrix.message', {
event_id: eventId,
Expand Down
11 changes: 10 additions & 1 deletion packages/federation-sdk/src/services/state.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,13 @@ export class PartialStateResolutionError extends Error {
export class UnknownRoomError extends Error {
constructor(roomId: RoomID) {
super(`Room ${roomId} does not exist`);
this.name = 'UnknownRoomError';
}
}
export class RoomInfoNotReadyError extends Error {
constructor(message: string) {
super(message);
this.name = 'RoomInfoNotReadyError';
}
}

Expand All @@ -82,7 +89,9 @@ export class StateService {
'm.room.create',
)) ?? {};
if (event?.type !== 'm.room.create') {
throw new Error('Create event mapping not found for room information');
throw new RoomInfoNotReadyError(
'Create event mapping not found for room information',
);
}

if (!stateId) {
Expand Down