Skip to content
Merged
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
82 changes: 50 additions & 32 deletions packages/federation-sdk/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,39 @@ export interface HomeserverServices {
emitter: EventEmitterService;
}

type RelatesTo =
| {
rel_type: 'm.replace';
event_id: EventID;
}
| {
rel_type: 'm.annotation';
event_id: EventID;
key: string;
}
| {
rel_type: 'm.thread';
event_id: EventID;
'm.in_reply_to'?: {
event_id: EventID;
room_id: string;
sender: string;
origin_server_ts: number;
};
is_falling_back?: boolean;
}
| {
// 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,

// rich {"body":"quote","m.mentions":{},"m.relates_to":{"is_falling_back":false,"m.in_reply_to":{"event_id":"$0vkvf2Ha_FdWe3zVaoDw3X15VCyZIZRYrHQXuoZDURQ"}},"msgtype":"m.text"}

'm.in_reply_to': {
event_id: EventID;
};
is_falling_back?: boolean;
};

export type HomeserverEventSignatures = {
'homeserver.ping': {
message: string;
Expand All @@ -130,6 +163,22 @@ export type HomeserverEventSignatures = {
last_active_ago?: number;
origin?: string;
};
'homeserver.matrix.encrypted': {
event_id: EventID;
event: PduForType<'m.room.encrypted'>;

room_id: string;
sender: string;
origin_server_ts: number;
content: {
algorithm: 'm.megolm.v1.aes-sha2';
ciphertext: string;
'm.relates_to'?: RelatesTo;
device_id?: string;
sender_key?: string;
session_id?: string;
};
};
'homeserver.matrix.message': {
event_id: EventID;
event: PduForType<'m.room.message'>;
Expand All @@ -141,38 +190,7 @@ export type HomeserverEventSignatures = {
body: string;
msgtype: MessageType;
url?: string;
'm.relates_to'?:
| {
rel_type: 'm.replace';
event_id: EventID;
}
| {
rel_type: 'm.annotation';
event_id: EventID;
key: string;
}
| {
rel_type: 'm.thread';
event_id: EventID;
'm.in_reply_to'?: {
event_id: EventID;
room_id: string;
sender: string;
origin_server_ts: number;
};
is_falling_back?: boolean;
}
| {
// 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,

// rich {"body":"quote","m.mentions":{},"m.relates_to":{"is_falling_back":false,"m.in_reply_to":{"event_id":"$0vkvf2Ha_FdWe3zVaoDw3X15VCyZIZRYrHQXuoZDURQ"}},"msgtype":"m.text"}

'm.in_reply_to': {
event_id: EventID;
};
is_falling_back?: boolean;
};
'm.relates_to'?: RelatesTo;
'm.new_content'?: {
body: string;
msgtype: MessageType;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ export class EventRepository {
case 'm.reaction':
case 'm.room.name':
case 'm.room.message':
case 'm.room.encrypted':
case 'm.room.member':
case 'm.room.power_levels':
case 'm.room.topic':
Expand Down
26 changes: 26 additions & 0 deletions packages/federation-sdk/src/services/staging-area.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,32 @@ export class StagingAreaService {
},
});
break;
case event.event.type === 'm.room.encrypted':
this.eventEmitterService.emit('homeserver.matrix.encrypted', {
event_id: eventId,
event: event.event,
room_id: roomId,
sender: event.event.sender,
origin_server_ts: event.event.origin_server_ts,
content: {
...event.event.content,
'm.relates_to': event.event.content?.['m.relates_to'] as
| {
rel_type: 'm.replace';
event_id: EventID;
}
| {
rel_type: 'm.annotation';
event_id: EventID;
key: string;
}
| {
rel_type: 'm.thread';
event_id: EventID;
},
},
});
break;
case event.event.type === 'm.reaction': {
this.eventEmitterService.emit('homeserver.matrix.reaction', {
event_id: eventId,
Expand Down
64 changes: 49 additions & 15 deletions packages/room/src/types/v3-11.ts
Original file line number Diff line number Diff line change
Expand Up @@ -366,21 +366,8 @@ export type PduRoomNameEventContent = z.infer<
typeof PduRoomNameEventContentSchema
>;

// Base message content schema
const BaseMessageContentSchema = z.object({
body: z.string().describe('The body of the message.'),
msgtype: z
.enum([
'm.text',
'm.image',
'm.file',
'm.audio',
'm.video',
'm.emote',
'm.notice',
'm.location',
])
.describe('The type of the message.'),
// Base timeline content schema
const BaseTimelineContentSchema = z.object({
// Optional fields for message edits and relations aka threads
'm.relates_to': z
.object({
Expand All @@ -406,6 +393,24 @@ const BaseMessageContentSchema = z.object({
})
.optional()
.describe('Relation information for edits, replies, reactions, etc.'),
});

// Base message content schema
const BaseMessageContentSchema = BaseTimelineContentSchema.extend({
body: z.string().describe('The body of the message.'),
msgtype: z
.enum([
'm.text',
'm.image',
'm.file',
'm.audio',
'm.video',
'm.emote',
'm.notice',
'm.location',
])
.describe('The type of the message.'),
// Optional fields for message edits and relations aka threads
format: z
.enum(['org.matrix.custom.html'])
.describe('The format of the message content.')
Expand Down Expand Up @@ -512,6 +517,26 @@ export const PduMessageEventContentSchema = z.union([
}),
]);

const EncryptedContentSchema = BaseTimelineContentSchema.extend({
algorithm: z
.enum(['m.megolm.v1.aes-sha2'])
.describe('The algorithm used to encrypt the content.'),
ciphertext: z.string().describe('The encrypted content.'),
// Optional fields for message edits and relations aka threads
device_id: z
.string()
.describe('The formatted body of the message.')
.optional(),
sender_key: z
.string()
.describe('The formatted body of the message.')
.optional(),
session_id: z
.string()
.describe('The formatted body of the message.')
.optional(),
});

export type PduMessageEventContent = z.infer<
typeof PduMessageEventContentSchema
>;
Expand Down Expand Up @@ -675,6 +700,12 @@ const EventPduTypeRoomTombstone = z.object({
content: PduRoomTombstoneEventContentSchema,
});

const EventPduTypeRoomEncrypted = z.object({
...PduNoContentTimelineEventSchema,
type: z.literal('m.room.encrypted'),
content: EncryptedContentSchema,
});

const EventPduTypeRoomMessage = z.object({
...PduNoContentTimelineEventSchema,
type: z.literal('m.room.message'),
Expand Down Expand Up @@ -723,6 +754,8 @@ export const PduStateEventSchema = z.discriminatedUnion('type', [
export const PduTimelineSchema = z.discriminatedUnion('type', [
EventPduTypeRoomMessage,

EventPduTypeRoomEncrypted,

EventPduTypeRoomReaction,

EventPduTypeRoomRedaction,
Expand All @@ -740,6 +773,7 @@ export type PduContent<T extends PduType = PduType> = PduForType<T>['content'];
export function isTimelineEventType(type: PduType) {
return (
type === 'm.room.message' ||
type === 'm.room.encrypted' ||
type === 'm.reaction' ||
type === 'm.room.redaction'
);
Expand Down