From 57746f7fb481d2674308c431b78f278798a88df3 Mon Sep 17 00:00:00 2001 From: David Teller Date: Tue, 21 Dec 2021 15:10:25 +0100 Subject: [PATCH] Wrap MatrixClient into something that displays nicer error messages --- src/Mjolnir.ts | 6 ++-- src/utils.ts | 54 ++++++++++++++++++++++++++++++++ test/integration/clientHelper.ts | 3 +- 3 files changed, 60 insertions(+), 3 deletions(-) diff --git a/src/Mjolnir.ts b/src/Mjolnir.ts index 97afbc0..602f0dc 100644 --- a/src/Mjolnir.ts +++ b/src/Mjolnir.ts @@ -42,6 +42,7 @@ import { EventRedactionQueue, RedactUserInRoom } from "./queues/EventRedactionQu import * as htmlEscape from "escape-html"; import { ReportManager } from "./report/ReportManager"; import { WebAPIs } from "./webapis/WebAPIs"; +import { makeClientWithSanerExceptions } from "./utils"; export const STATE_NOT_STARTED = "not_started"; export const STATE_CHECKING_PERMISSIONS = "checking_permissions"; @@ -54,7 +55,7 @@ const PROTECTED_ROOMS_EVENT_TYPE = "org.matrix.mjolnir.protected_rooms"; const WARN_UNPROTECTED_ROOM_EVENT_PREFIX = "org.matrix.mjolnir.unprotected_room_warning.for."; export class Mjolnir { - + public readonly client: MatrixClient; private displayName: string; private localpart: string; private currentState: string = STATE_NOT_STARTED; @@ -153,11 +154,12 @@ export class Mjolnir { } constructor( - public readonly client: MatrixClient, + client: MatrixClient, public readonly protectedRooms: { [roomId: string]: string }, private banLists: BanList[], ) { this.explicitlyProtectedRoomIds = Object.keys(this.protectedRooms); + this.client = makeClientWithSanerExceptions(client); for (const reason of config.automaticallyRedactForReasons) { this.automaticRedactionReasons.push(new MatrixGlob(reason.toLowerCase())); diff --git a/src/utils.ts b/src/utils.ts index 83c05bd..ca227d2 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -28,6 +28,7 @@ import { import { logMessage } from "./LogProxy"; import config from "./config"; import * as htmlEscape from "escape-html"; +import { ClientRequest, IncomingMessage } from "http"; export function setToArray(set: Set): T[] { const arr: T[] = []; @@ -201,3 +202,56 @@ export async function replaceRoomIdsWithPills(client: MatrixClient, text: string return content; } + +export function makeClientWithSanerExceptions(client: MatrixClient): MatrixClient { + let result = new Proxy(client, { + get: function (obj, key) { + let value = obj[key]; + if (!(typeof value == "function")) { + return value; + } + return function (...args) { + let result = value.apply(client, args); + if (!(result instanceof Promise)) { + // We're only interested in watching async code. + return result; + } + return result.catch(reason => { + if (!(reason instanceof IncomingMessage)) { + // In most cases, we're happy with the result. + throw reason; + } + // However, MatrixClient has a tendency of throwing + // instances of `IncomingMessage` instead of instances + // of `Error`. The former take ~800 lines of log and + // provide no stack trace, which makes them typically + // useless. + let method: string | null = null; + let path: string = ''; + let body: string | null = null; + if (reason.method) { + method = reason.method; + } + if (reason.url) { + path = reason.url; + } + if ("req" in reason && (reason as any).req instanceof ClientRequest) { + if (!method) { + method = (reason as any).req.method; + } + if (!path) { + path = (reason as any).req.path; + } + } + if ("body" in reason) { + body = JSON.stringify((reason as any).body); + } + let error = new Error(`Error during MatrixClient request ${method} ${path}: ${reason.statusCode} ${reason.statusMessage} -- ${body}`); + //(error as any).message = reason; + throw error; + }); + } + } + }); + return result; +} \ No newline at end of file diff --git a/test/integration/clientHelper.ts b/test/integration/clientHelper.ts index b83d413..169a1ee 100644 --- a/test/integration/clientHelper.ts +++ b/test/integration/clientHelper.ts @@ -2,6 +2,7 @@ import axios from "axios"; import { HmacSHA1 } from "crypto-js"; import { LogService, MatrixClient, MemoryStorageProvider, PantalaimonClient } from "matrix-bot-sdk"; import config from "../../src/config"; +import { makeClientWithSanerExceptions } from "../../src/utils"; /** * Register a user using the synapse admin api that requires the use of a registration secret rather than an admin user. @@ -67,7 +68,7 @@ export async function registerNewTestUser(isAdmin: boolean, label: string = "") export async function newTestUser(isAdmin: boolean = false, label: string = ""): Promise { const username = await registerNewTestUser(isAdmin, label); const pantalaimon = new PantalaimonClient(config.homeserverUrl, new MemoryStorageProvider()); - return await pantalaimon.createClientWithCredentials(username, username); + return makeClientWithSanerExceptions(await pantalaimon.createClientWithCredentials(username, username)); } /**