Skip to content
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

Support extensible events #393

Open
wants to merge 2 commits into
base: develop
Choose a base branch
from
Open
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
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
47 changes: 43 additions & 4 deletions src/bridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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");

Expand All @@ -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<WeakEvent>, 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<ExtensibleEvent>, 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`
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -1432,7 +1453,16 @@ export class Bridge {
}
}

const request = this.requestFactory.newRequest({ data: event });
// Extensible events
let request: Request<ExtensibleEvent|WeakEvent>;
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 }));

Expand Down Expand Up @@ -1483,14 +1513,23 @@ export class Bridge {
return promise;
}

private onConsume(err: Error|null, data: { request: Request<WeakEvent>, context?: BridgeContext}) {
private onConsume(err: Error|null, data: { request: Request<WeakEvent|ExtensibleEvent>, 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<ExtensibleEvent>, data.context);
}
else {
onEvent(data.request as unknown as Request<WeakEvent>, data.context);
}

this.opts.controller.onEvent(data.request, data.context);
}

// eslint-disable-next-line camelcase
Expand Down
4 changes: 2 additions & 2 deletions src/components/request-factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<T>(opts?: RequestOpts<T>) {
const req = new Request(opts || {data: null});
public newRequest<T, R extends unknown>(opts?: RequestOpts<T>): Request<T, R> {
Copy link
Contributor

Choose a reason for hiding this comment

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

Nitpick: does R extends unknown do anything more than a regular R?

const req = new Request(opts);
req.getPromise().then((res) => {
this._resolves.forEach((resolveFn) => {
resolveFn(req, res);
Expand Down
28 changes: 14 additions & 14 deletions src/components/request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,15 @@ function generateRequestId() {

export interface RequestOpts<T> {
id?: string;
data: T;
data?: T;
}

export class Request<T> {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export class Request<T, R extends any = any> {
private id: string;
private data: T;
private data: T|null;
private startTs: number;
private defer: Defer<unknown>;
private defer: Defer<R>;
private pending: boolean;

public get isPending(): boolean {
Expand All @@ -42,10 +43,9 @@ export class Request<T> {
* generated if this is not provided.
* @param opts.data Optional data to associate with this request.
*/
constructor(opts: RequestOpts<T>) {
opts = opts || {};
constructor(opts: RequestOpts<T> = { }) {
this.id = opts.id || generateRequestId();
this.data = opts.data;
this.data = opts.data || null;
this.startTs = Date.now();
this.defer = defer();
this.pending = true;
Expand All @@ -56,23 +56,23 @@ export class Request<T> {
* Get any optional data set on this request.
* @return The data
*/
public getData() {
public getData(): T|null {
return this.data;
}

/**
* Get this request's ID.
* @return The ID.
*/
public getId() {
public getId(): string {
return this.id;
}

/**
* 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;
}

Expand All @@ -81,7 +81,7 @@ export class Request<T> {
* respective methods are called on this Request.
* @return {Promise} A promise
*/
public getPromise() {
public getPromise(): Promise<R> {
return this.defer.promise;
}

Expand All @@ -91,7 +91,7 @@ export class Request<T> {
* 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);
}
Expand All @@ -101,7 +101,7 @@ export class Request<T> {
* processed correctly</i>.
* @param msg The thing to reject with.
*/
public reject(msg: unknown) {
public reject(msg: unknown): void {
this.pending = false;
this.defer.reject(msg);
}
Expand All @@ -111,7 +111,7 @@ export class Request<T> {
* @param promise The promise whose resolution determines the outcome of this
* request.
*/
public outcomeFrom(promise: Promise<unknown>) {
public outcomeFrom(promise: Promise<R>): Promise<unknown> {
return promise.then(this.resolve.bind(this), this.reject.bind(this));
}
}
2 changes: 1 addition & 1 deletion src/utils/promiseutil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,6 @@ export function defer<T>(): Defer<T> {
};
}

export function delay(delayMs: number) {
export function delay(delayMs: number): Promise<void> {
return new Promise((r) => setTimeout(r, delayMs));
}
5 changes: 5 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down