diff --git a/src/sliding-sync-sdk.ts b/src/sliding-sync-sdk.ts index 9080db1d1dc555414d4f88685fc2213ed7eea6ae..ed482008597cbe3ae33da9534cff574ba680ac92 100644 --- a/src/sliding-sync-sdk.ts +++ b/src/sliding-sync-sdk.ts @@ -28,7 +28,7 @@ import { defaultClientOpts, defaultSyncApiOpts, type SetPresence, - mapToDeviceEvent, + processToDeviceMessages, } from "./sync.ts"; import { type MatrixEvent } from "./models/event.ts"; import { @@ -150,51 +150,11 @@ class ExtensionToDevice implements Extension<ExtensionToDeviceRequest, Extension } public async onResponse(data: ExtensionToDeviceResponse): Promise<void> { - const cancelledKeyVerificationTxns: string[] = []; let events = data["events"] || []; if (events.length > 0 && this.cryptoCallbacks) { events = await this.cryptoCallbacks.preprocessToDeviceMessages(events); } - events - .map(mapToDeviceEvent) - .map((toDeviceEvent) => { - // map is a cheap inline forEach - // We want to flag m.key.verification.start events as cancelled - // if there's an accompanying m.key.verification.cancel event, so - // we pull out the transaction IDs from the cancellation events - // so we can flag the verification events as cancelled in the loop - // below. - if (toDeviceEvent.getType() === "m.key.verification.cancel") { - const txnId: string | undefined = toDeviceEvent.getContent()["transaction_id"]; - if (txnId) { - cancelledKeyVerificationTxns.push(txnId); - } - } - - // as mentioned above, .map is a cheap inline forEach, so return - // the unmodified event. - return toDeviceEvent; - }) - .forEach((toDeviceEvent) => { - const content = toDeviceEvent.getContent(); - if (toDeviceEvent.getType() == "m.room.message" && content.msgtype == "m.bad.encrypted") { - // the mapper already logged a warning. - logger.log("Ignoring undecryptable to-device event from " + toDeviceEvent.getSender()); - return; - } - - if ( - toDeviceEvent.getType() === "m.key.verification.start" || - toDeviceEvent.getType() === "m.key.verification.request" - ) { - const txnId = content["transaction_id"]; - if (cancelledKeyVerificationTxns.includes(txnId)) { - toDeviceEvent.flagCancelled(); - } - } - - this.client.emit(ClientEvent.ToDeviceEvent, toDeviceEvent); - }); + processToDeviceMessages(events, this.client); this.nextBatch = data.next_batch; } diff --git a/src/sync.ts b/src/sync.ts index 4c9a78fa64d1b8ded176c17ad34d06763d4be610..3fdbb8a22c47b098d2bf670051ea15e78c6d6d57 100644 --- a/src/sync.ts +++ b/src/sync.ts @@ -1150,47 +1150,7 @@ export class SyncApi { toDeviceMessages = await this.syncOpts.cryptoCallbacks.preprocessToDeviceMessages(toDeviceMessages); } - const cancelledKeyVerificationTxns: string[] = []; - toDeviceMessages - .map(mapToDeviceEvent) - .map((toDeviceEvent) => { - // map is a cheap inline forEach - // We want to flag m.key.verification.start events as cancelled - // if there's an accompanying m.key.verification.cancel event, so - // we pull out the transaction IDs from the cancellation events - // so we can flag the verification events as cancelled in the loop - // below. - if (toDeviceEvent.getType() === "m.key.verification.cancel") { - const txnId: string = toDeviceEvent.getContent()["transaction_id"]; - if (txnId) { - cancelledKeyVerificationTxns.push(txnId); - } - } - - // as mentioned above, .map is a cheap inline forEach, so return - // the unmodified event. - return toDeviceEvent; - }) - .forEach(function (toDeviceEvent) { - const content = toDeviceEvent.getContent(); - if (toDeviceEvent.getType() == "m.room.message" && content.msgtype == "m.bad.encrypted") { - // the mapper already logged a warning. - logger.log("Ignoring undecryptable to-device event from " + toDeviceEvent.getSender()); - return; - } - - if ( - toDeviceEvent.getType() === "m.key.verification.start" || - toDeviceEvent.getType() === "m.key.verification.request" - ) { - const txnId = content["transaction_id"]; - if (cancelledKeyVerificationTxns.includes(txnId)) { - toDeviceEvent.flagCancelled(); - } - } - - client.emit(ClientEvent.ToDeviceEvent, toDeviceEvent); - }); + processToDeviceMessages(toDeviceMessages, client); } else { // no more to-device events: we can stop polling with a short timeout. this.catchingUp = false; @@ -1946,7 +1906,56 @@ export function _createAndReEmitRoom(client: MatrixClient, roomId: string, opts: return room; } -export function mapToDeviceEvent(plainOldJsObject: Partial<IEvent>): MatrixEvent { +/** + * Process a list of (decrypted, where possible) received to-device events. + * + * Converts the events into `MatrixEvent`s, and emits appropriate {@link ClientEvent.ToDeviceEvent} events. + * */ +export function processToDeviceMessages(toDeviceMessages: IToDeviceEvent[], client: MatrixClient): void { + const cancelledKeyVerificationTxns: string[] = []; + toDeviceMessages + .map(mapToDeviceEvent) + .map((toDeviceEvent) => { + // map is a cheap inline forEach + // We want to flag m.key.verification.start events as cancelled + // if there's an accompanying m.key.verification.cancel event, so + // we pull out the transaction IDs from the cancellation events + // so we can flag the verification events as cancelled in the loop + // below. + if (toDeviceEvent.getType() === "m.key.verification.cancel") { + const txnId: string = toDeviceEvent.getContent()["transaction_id"]; + if (txnId) { + cancelledKeyVerificationTxns.push(txnId); + } + } + + // as mentioned above, .map is a cheap inline forEach, so return + // the unmodified event. + return toDeviceEvent; + }) + .forEach(function (toDeviceEvent) { + const content = toDeviceEvent.getContent(); + if (toDeviceEvent.getType() == "m.room.message" && content.msgtype == "m.bad.encrypted") { + // the mapper already logged a warning. + logger.log("Ignoring undecryptable to-device event from " + toDeviceEvent.getSender()); + return; + } + + if ( + toDeviceEvent.getType() === "m.key.verification.start" || + toDeviceEvent.getType() === "m.key.verification.request" + ) { + const txnId = content["transaction_id"]; + if (cancelledKeyVerificationTxns.includes(txnId)) { + toDeviceEvent.flagCancelled(); + } + } + + client.emit(ClientEvent.ToDeviceEvent, toDeviceEvent); + }); +} + +function mapToDeviceEvent(plainOldJsObject: Partial<IEvent>): MatrixEvent { // to-device events should not have a `room_id` property, but let's be sure delete plainOldJsObject.room_id; return new MatrixEvent(plainOldJsObject);