From 00818f1db189426470d20b6ad0df667c5b08cabb Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Wed, 16 Feb 2022 12:54:50 +0000 Subject: [PATCH 1/2] Improve request typings + --- src/components/request-factory.ts | 4 ++-- src/components/request.ts | 28 ++++++++++++++-------------- src/utils/promiseutil.ts | 2 +- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/components/request-factory.ts b/src/components/request-factory.ts index ca0d1b60..fc531bda 100644 --- a/src/components/request-factory.ts +++ b/src/components/request-factory.ts @@ -33,8 +33,8 @@ export class RequestFactory { * @param opts The options to pass to the Request constructor, if any. * @return A new request object */ - public newRequest(opts?: RequestOpts) { - const req = new Request(opts || {data: null}); + public newRequest(opts?: RequestOpts): Request { + const req = new Request(opts); req.getPromise().then((res) => { this._resolves.forEach((resolveFn) => { resolveFn(req, res); diff --git a/src/components/request.ts b/src/components/request.ts index 5d4ee7e8..51a89ceb 100644 --- a/src/components/request.ts +++ b/src/components/request.ts @@ -21,14 +21,15 @@ function generateRequestId() { export interface RequestOpts { id?: string; - data: T; + data?: T; } -export class Request { +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export class Request { private id: string; - private data: T; + private data: T|null; private startTs: number; - private defer: Defer; + private defer: Defer; private pending: boolean; public get isPending(): boolean { @@ -42,10 +43,9 @@ export class Request { * generated if this is not provided. * @param opts.data Optional data to associate with this request. */ - constructor(opts: RequestOpts) { - opts = opts || {}; + constructor(opts: RequestOpts = { }) { this.id = opts.id || generateRequestId(); - this.data = opts.data; + this.data = opts.data || null; this.startTs = Date.now(); this.defer = defer(); this.pending = true; @@ -56,7 +56,7 @@ export class Request { * Get any optional data set on this request. * @return The data */ - public getData() { + public getData(): T|null { return this.data; } @@ -64,7 +64,7 @@ export class Request { * Get this request's ID. * @return The ID. */ - public getId() { + public getId(): string { return this.id; } @@ -72,7 +72,7 @@ export class Request { * Get the number of elapsed milliseconds since this request was created. * @return The number of milliseconds since this request was made. */ - public getDuration() { + public getDuration(): number { return Date.now() - this.startTs; } @@ -81,7 +81,7 @@ export class Request { * respective methods are called on this Request. * @return {Promise} A promise */ - public getPromise() { + public getPromise(): Promise { return this.defer.promise; } @@ -91,7 +91,7 @@ export class Request { * through, e.g. suppressing AS virtual users' messages is still a success. * @param msg The thing to resolve with. */ - public resolve(msg: unknown) { + public resolve(msg: R): void { this.pending = false; this.defer.resolve(msg); } @@ -101,7 +101,7 @@ export class Request { * processed correctly. * @param msg The thing to reject with. */ - public reject(msg: unknown) { + public reject(msg: unknown): void { this.pending = false; this.defer.reject(msg); } @@ -111,7 +111,7 @@ export class Request { * @param promise The promise whose resolution determines the outcome of this * request. */ - public outcomeFrom(promise: Promise) { + public outcomeFrom(promise: Promise): Promise { return promise.then(this.resolve.bind(this), this.reject.bind(this)); } } diff --git a/src/utils/promiseutil.ts b/src/utils/promiseutil.ts index 8873f3ee..0520264f 100644 --- a/src/utils/promiseutil.ts +++ b/src/utils/promiseutil.ts @@ -34,6 +34,6 @@ export function defer(): Defer { }; } -export function delay(delayMs: number) { +export function delay(delayMs: number): Promise { return new Promise((r) => setTimeout(r, delayMs)); } From 5df61804af871ca95130e44ce7f0bd1d4ccb1d6b Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Wed, 16 Feb 2022 12:55:00 +0000 Subject: [PATCH 2/2] Support extensible events --- package.json | 1 + src/bridge.ts | 47 +++++++++++++++++++++++++++++++++++++++++++---- yarn.lock | 5 +++++ 3 files changed, 49 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index b1891534..ec915864 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ "js-yaml": "^4.0.0", "matrix-appservice": "^0.10.0", "matrix-bot-sdk": "^0.6.0-beta.2", + "matrix-events-sdk": "^0.0.1-beta.6", "matrix-js-sdk": "^12.4.1", "nedb": "^1.8.0", "nopt": "^5.0.0", diff --git a/src/bridge.ts b/src/bridge.ts index d84f8e79..7c8ce76b 100644 --- a/src/bridge.ts +++ b/src/bridge.ts @@ -56,7 +56,7 @@ import { EphemeralEvent, PresenceEvent, ReadReceiptEvent, TypingEvent, WeakEvent import * as BotSDK from "matrix-bot-sdk"; import { ActivityTracker, ActivityTrackerOpts } from "./components/activity-tracker"; import { EncryptedIntent, EncryptedIntentOpts } from "./components/encrypted-intent"; -import e = require("express"); +import { ExtensibleEvent, ExtensibleEvents, NoticeEvent } from "matrix-events-sdk"; const log = logging.get("bridge"); @@ -74,8 +74,27 @@ const RECEIPT_CUTOFF_TIME_MS = 60000; export interface BridgeController { /** * The bridge will invoke when an event has been received from the HS. + * + * When using `unstableOnExtensibleEvent`, events that are parsed as + * extensible events will not be emitted from this callback. */ onEvent: (request: Request, context?: BridgeContext) => void; + + /** + * The bridge will invoke this an event has been received from the HS. The event + * object will be an extensible event. + * + * If an event is successfully parsed as an extensible event, it will be + * emitted through this callback. If it is not parsed, it will be emitted + * through `onEvent`. + * + * This callback is unstable and may break at any time. + * + * @see https://github.com/matrix-org/matrix-events-sdk + * @see https://github.com/matrix-org/matrix-doc/pull/1767 + */ + unstableOnExtensibleEvent?: ( + request: Request, context?: BridgeContext) => void; /** * The bridge will invoke this when a typing, read reciept or presence event * is received from the HS. **This will only work with the `bridgeEncryption` @@ -774,6 +793,8 @@ export class Bridge { }); this.appservice.onUserQuery = (userId) => this.onUserQuery(userId); this.appservice.onAliasQuery = this.onAliasQuery.bind(this); + + this.appservice.on("event", async (event) => { let passthrough = true; const weakEvent = event as WeakEvent; @@ -1432,7 +1453,16 @@ export class Bridge { } } - const request = this.requestFactory.newRequest({ data: event }); + // Extensible events + let request: Request; + const extensibleEvent = this.opts.controller.unstableOnExtensibleEvent && ExtensibleEvents.parse(event); + if (extensibleEvent) { + request = this.requestFactory.newRequest({ data: extensibleEvent }); + } + else { + request = this.requestFactory.newRequest({ data: event }); + } + const contextReady = this.getBridgeContext(event); const dataReady = contextReady.then(context => ({ request, context })); @@ -1483,14 +1513,23 @@ export class Bridge { return promise; } - private onConsume(err: Error|null, data: { request: Request, context?: BridgeContext}) { + private onConsume(err: Error|null, data: { request: Request, context?: BridgeContext}) { if (err) { // The data for the event could not be retrieved. this.onLog("onEvent failure: " + err, true); return; } + const { onEvent, unstableOnExtensibleEvent } = this.opts.controller; + const evt = data.request.getData(); + // unstableOnExtensibleEvent is ALWAYS defined if evt is ExtensibleEvent, because + // we only parse EE when unstableOnExtensibleEvent is truthy. + if (unstableOnExtensibleEvent && evt instanceof ExtensibleEvent) { + unstableOnExtensibleEvent(data.request as unknown as Request, data.context); + } + else { + onEvent(data.request as unknown as Request, data.context); + } - this.opts.controller.onEvent(data.request, data.context); } // eslint-disable-next-line camelcase diff --git a/yarn.lock b/yarn.lock index 989beddb..9181f354 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2428,6 +2428,11 @@ matrix-bot-sdk@^0.6.0-beta.2: optionalDependencies: better-sqlite3 "^7.4.3" +matrix-events-sdk@^0.0.1-beta.6: + version "0.0.1-beta.6" + resolved "https://registry.yarnpkg.com/matrix-events-sdk/-/matrix-events-sdk-0.0.1-beta.6.tgz#9001090ed2e2bf29efc113d6b29871bcc6520749" + integrity sha512-VMqPXe3Bg4R9yC9PNqGv6bDFwWlVYadYxp0Ke1ihhXUCpGcx7e28kOYcqK2T3RxLXK4KK7VH4JRbY53Do3r+Fw== + matrix-js-sdk@^12.4.1: version "12.4.1" resolved "https://registry.yarnpkg.com/matrix-js-sdk/-/matrix-js-sdk-12.4.1.tgz#966f506b4146e4fafffa5bbe80f5c53515e1bc78"