mirror of
https://github.com/matrix-org/mjolnir.git
synced 2024-10-01 01:36:06 -04:00
Let's make sure that we can still check for errors with concise error handling
This commit is contained in:
parent
3f2039f6a7
commit
c48a1e8ffc
@ -10,9 +10,9 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "tsc",
|
"build": "tsc",
|
||||||
"lint": "tslint --project ./tsconfig.json -t stylish",
|
"lint": "tslint --project ./tsconfig.json -t stylish",
|
||||||
"start:dev": "yarn build && node lib/index.js",
|
"start:dev": "yarn build && node --async-stack-traces lib/index.js",
|
||||||
"test": "ts-mocha --project ./tsconfig.json test/commands/**/*.ts",
|
"test": "ts-mocha --project ./tsconfig.json test/commands/**/*.ts",
|
||||||
"test:integration": "NODE_ENV=harness ts-mocha --require test/integration/fixtures.ts --project ./tsconfig.json \"test/integration/**/*Test.ts\"",
|
"test:integration": "NODE_ENV=harness ts-mocha --async-stack-traces --require test/integration/fixtures.ts --project ./tsconfig.json \"test/integration/**/*Test.ts\"",
|
||||||
"test:manual": "NODE_ENV=harness ts-node test/integration/manualLaunchScript.ts"
|
"test:manual": "NODE_ENV=harness ts-node test/integration/manualLaunchScript.ts"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
@ -27,7 +27,7 @@ import config from "./config";
|
|||||||
import { logMessage } from "./LogProxy";
|
import { logMessage } from "./LogProxy";
|
||||||
import { Healthz } from "./health/healthz";
|
import { Healthz } from "./health/healthz";
|
||||||
import { Mjolnir } from "./Mjolnir";
|
import { Mjolnir } from "./Mjolnir";
|
||||||
import { patchMatrixClientForConciseExceptions } from "./utils";
|
import { patchMatrixClient } from "./utils";
|
||||||
|
|
||||||
config.RUNTIME = {};
|
config.RUNTIME = {};
|
||||||
|
|
||||||
@ -52,7 +52,7 @@ if (config.health.healthz.enabled) {
|
|||||||
} else {
|
} else {
|
||||||
client = new MatrixClient(config.homeserverUrl, config.accessToken, storage);
|
client = new MatrixClient(config.homeserverUrl, config.accessToken, storage);
|
||||||
}
|
}
|
||||||
patchMatrixClientForConciseExceptions();
|
patchMatrixClient();
|
||||||
config.RUNTIME.client = client;
|
config.RUNTIME.client = client;
|
||||||
|
|
||||||
let bot = await Mjolnir.setupMjolnirFromConfig(client);
|
let bot = await Mjolnir.setupMjolnirFromConfig(client);
|
||||||
|
@ -22,7 +22,6 @@ import { JSDOM } from 'jsdom';
|
|||||||
|
|
||||||
import config from "../config";
|
import config from "../config";
|
||||||
import { Mjolnir } from "../Mjolnir";
|
import { Mjolnir } from "../Mjolnir";
|
||||||
import { patchMatrixClientForConciseExceptions } from "../utils";
|
|
||||||
|
|
||||||
/// Regexp, used to extract the action label from an action reaction
|
/// Regexp, used to extract the action label from an action reaction
|
||||||
/// such as `⚽ Kick user @foobar:localhost from room [kick-user]`.
|
/// such as `⚽ Kick user @foobar:localhost from room [kick-user]`.
|
||||||
@ -876,7 +875,6 @@ class DisplayManager {
|
|||||||
if (!await action.canExecute(this.owner, report, moderationRoomId)) {
|
if (!await action.canExecute(this.owner, report, moderationRoomId)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
patchMatrixClientForConciseExceptions();
|
|
||||||
await this.owner.mjolnir.client.sendEvent(config.managementRoom, "m.reaction", {
|
await this.owner.mjolnir.client.sendEvent(config.managementRoom, "m.reaction", {
|
||||||
"m.relates_to": {
|
"m.relates_to": {
|
||||||
"rel_type": "m.annotation",
|
"rel_type": "m.annotation",
|
||||||
|
42
src/utils.ts
42
src/utils.ts
@ -220,14 +220,15 @@ let isMatrixClientPatchedForConciseExceptions = false;
|
|||||||
* This method configures `MatrixClient` to ensure that methods that may throw
|
* This method configures `MatrixClient` to ensure that methods that may throw
|
||||||
* instead throws more reasonable insetances of `Error`.
|
* instead throws more reasonable insetances of `Error`.
|
||||||
*/
|
*/
|
||||||
export function patchMatrixClientForConciseExceptions() {
|
function patchMatrixClientForConciseExceptions() {
|
||||||
if (isMatrixClientPatchedForConciseExceptions) {
|
if (isMatrixClientPatchedForConciseExceptions) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let originalRequestFn = getRequestFn();
|
let originalRequestFn = getRequestFn();
|
||||||
setRequestFn((params, cb) => {
|
setRequestFn((params, cb) => {
|
||||||
|
let stack = new Error().stack;
|
||||||
originalRequestFn(params, function conciseExceptionRequestFn(err, response, resBody) {
|
originalRequestFn(params, function conciseExceptionRequestFn(err, response, resBody) {
|
||||||
if (!err && (response.statusCode < 200 || response.statusCode >= 300)) {
|
if (!err && (response?.statusCode < 200 || response?.statusCode >= 300)) {
|
||||||
// Normally, converting HTTP Errors into rejections is done by the caller
|
// Normally, converting HTTP Errors into rejections is done by the caller
|
||||||
// of `requestFn` within matrix-bot-sdk. However, this always ends up rejecting
|
// of `requestFn` within matrix-bot-sdk. However, this always ends up rejecting
|
||||||
// with an `IncomingMessage` - exactly what we wish to avoid here.
|
// with an `IncomingMessage` - exactly what we wish to avoid here.
|
||||||
@ -277,12 +278,47 @@ export function patchMatrixClientForConciseExceptions() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ("body" in err) {
|
if ("body" in err) {
|
||||||
body = JSON.stringify((err as any).body);
|
body = (err as any).body;
|
||||||
}
|
}
|
||||||
let error = new Error(`Error during MatrixClient request ${method} ${path}: ${err.statusCode} ${err.statusMessage} -- ${body}`);
|
let error = new Error(`Error during MatrixClient request ${method} ${path}: ${err.statusCode} ${err.statusMessage} -- ${body}`);
|
||||||
|
error.stack = stack;
|
||||||
|
if (body) {
|
||||||
|
// Calling code may use `body` to check for errors, so let's
|
||||||
|
// make sure that we're providing it.
|
||||||
|
try {
|
||||||
|
body = JSON.parse(body);
|
||||||
|
} catch (ex) {
|
||||||
|
// Not JSON.
|
||||||
|
}
|
||||||
|
// Define the property but don't make it visible during logging.
|
||||||
|
Object.defineProperty(error, "body", {
|
||||||
|
value: body,
|
||||||
|
enumerable: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Calling code may use `statusCode` to check for errors, so let's
|
||||||
|
// make sure that we're providing it.
|
||||||
|
if ("statusCode" in err) {
|
||||||
|
Object.defineProperty(error, "statusCode", {
|
||||||
|
value: err.statusCode,
|
||||||
|
enumerable: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
return cb(error, response, resBody);
|
return cb(error, response, resBody);
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
isMatrixClientPatchedForConciseExceptions = true;
|
isMatrixClientPatchedForConciseExceptions = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform any patching deemed necessary to MatrixClient.
|
||||||
|
*/
|
||||||
|
export function patchMatrixClient() {
|
||||||
|
// Note that the order of patches is meaningful.
|
||||||
|
//
|
||||||
|
// - `patchMatrixClientForConciseExceptions` converts all `IncomingMessage`
|
||||||
|
// errors into instances of `Error` handled as errors;
|
||||||
|
// - `patchMatrixClientForRetry` expects that all errors are returned as
|
||||||
|
// errors.
|
||||||
|
patchMatrixClientForConciseExceptions();
|
||||||
|
}
|
||||||
|
@ -21,7 +21,7 @@ const REPORT_NOTICE_REGEXPS = {
|
|||||||
|
|
||||||
describe("Test: Reporting abuse", async () => {
|
describe("Test: Reporting abuse", async () => {
|
||||||
it('Mjölnir intercepts abuse reports', async function() {
|
it('Mjölnir intercepts abuse reports', async function() {
|
||||||
this.timeout(10000);
|
this.timeout(60000);
|
||||||
|
|
||||||
// Listen for any notices that show up.
|
// Listen for any notices that show up.
|
||||||
let notices = [];
|
let notices = [];
|
||||||
@ -216,7 +216,7 @@ describe("Test: Reporting abuse", async () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
it('The redact action works', async function() {
|
it('The redact action works', async function() {
|
||||||
this.timeout(10000);
|
this.timeout(60000);
|
||||||
|
|
||||||
// Listen for any notices that show up.
|
// Listen for any notices that show up.
|
||||||
let notices = [];
|
let notices = [];
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import { HmacSHA1 } from "crypto-js";
|
import { HmacSHA1 } from "crypto-js";
|
||||||
import { extractRequestError, LogService, MatrixClient, MemoryStorageProvider, PantalaimonClient } from "matrix-bot-sdk";
|
import { LogService, MatrixClient, MemoryStorageProvider, PantalaimonClient } from "matrix-bot-sdk";
|
||||||
import config from "../../src/config";
|
import config from "../../src/config";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -8,7 +8,7 @@ import { onReactionTo } from "./commandUtils";
|
|||||||
|
|
||||||
describe("Test: The redaction command", function () {
|
describe("Test: The redaction command", function () {
|
||||||
it('Mjölnir redacts all of the events sent by a spammer when instructed to by giving their id and a room id.', async function() {
|
it('Mjölnir redacts all of the events sent by a spammer when instructed to by giving their id and a room id.', async function() {
|
||||||
this.timeout(20000);
|
this.timeout(60000);
|
||||||
// Create a few users and a room.
|
// Create a few users and a room.
|
||||||
let badUser = await newTestUser(false, "spammer-needs-redacting");
|
let badUser = await newTestUser(false, "spammer-needs-redacting");
|
||||||
let badUserId = await badUser.getUserId();
|
let badUserId = await badUser.getUserId();
|
||||||
@ -52,7 +52,7 @@ import { onReactionTo } from "./commandUtils";
|
|||||||
});
|
});
|
||||||
})
|
})
|
||||||
it('Mjölnir redacts all of the events sent by a spammer when instructed to by giving their id in multiple rooms.', async function() {
|
it('Mjölnir redacts all of the events sent by a spammer when instructed to by giving their id in multiple rooms.', async function() {
|
||||||
this.timeout(20000);
|
this.timeout(60000);
|
||||||
// Create a few users and a room.
|
// Create a few users and a room.
|
||||||
let badUser = await newTestUser(false, "spammer-needs-redacting");
|
let badUser = await newTestUser(false, "spammer-needs-redacting");
|
||||||
let badUserId = await badUser.getUserId();
|
let badUserId = await badUser.getUserId();
|
||||||
@ -101,7 +101,7 @@ import { onReactionTo } from "./commandUtils";
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
it("Redacts a single event when instructed to.", async function () {
|
it("Redacts a single event when instructed to.", async function () {
|
||||||
this.timeout(20000);
|
this.timeout(60000);
|
||||||
// Create a few users and a room.
|
// Create a few users and a room.
|
||||||
let badUser = await newTestUser(false, "spammer-needs-redacting");
|
let badUser = await newTestUser(false, "spammer-needs-redacting");
|
||||||
const mjolnir = config.RUNTIME.client!
|
const mjolnir = config.RUNTIME.client!
|
||||||
|
@ -24,7 +24,7 @@ import {
|
|||||||
import { Mjolnir} from '../../src/Mjolnir';
|
import { Mjolnir} from '../../src/Mjolnir';
|
||||||
import config from "../../src/config";
|
import config from "../../src/config";
|
||||||
import { registerUser } from "./clientHelper";
|
import { registerUser } from "./clientHelper";
|
||||||
import { patchMatrixClientForConciseExceptions } from "../../src/utils";
|
import { patchMatrixClient } from "../../src/utils";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ensures that a room exists with the alias, if it does not exist we create it.
|
* Ensures that a room exists with the alias, if it does not exist we create it.
|
||||||
@ -33,8 +33,9 @@ import { patchMatrixClientForConciseExceptions } from "../../src/utils";
|
|||||||
* @returns The room ID of the aliased room.
|
* @returns The room ID of the aliased room.
|
||||||
*/
|
*/
|
||||||
export async function ensureAliasedRoomExists(client: MatrixClient, alias: string): Promise<string> {
|
export async function ensureAliasedRoomExists(client: MatrixClient, alias: string): Promise<string> {
|
||||||
return await client.resolveRoom(alias)
|
try {
|
||||||
.catch(async e => {
|
return await client.resolveRoom(alias);
|
||||||
|
} catch (e) {
|
||||||
if (e?.body?.errcode === 'M_NOT_FOUND') {
|
if (e?.body?.errcode === 'M_NOT_FOUND') {
|
||||||
console.info(`${alias} hasn't been created yet, so we're making it now.`)
|
console.info(`${alias} hasn't been created yet, so we're making it now.`)
|
||||||
let roomId = await client.createRoom({
|
let roomId = await client.createRoom({
|
||||||
@ -44,7 +45,7 @@ export async function ensureAliasedRoomExists(client: MatrixClient, alias: strin
|
|||||||
return roomId
|
return roomId
|
||||||
}
|
}
|
||||||
throw e;
|
throw e;
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function configureMjolnir() {
|
async function configureMjolnir() {
|
||||||
@ -81,7 +82,7 @@ export async function makeMjolnir(): Promise<Mjolnir> {
|
|||||||
LogService.info("test/mjolnirSetupUtils", "Starting bot...");
|
LogService.info("test/mjolnirSetupUtils", "Starting bot...");
|
||||||
const pantalaimon = new PantalaimonClient(config.homeserverUrl, new MemoryStorageProvider());
|
const pantalaimon = new PantalaimonClient(config.homeserverUrl, new MemoryStorageProvider());
|
||||||
const client = await pantalaimon.createClientWithCredentials(config.pantalaimon.username, config.pantalaimon.password);
|
const client = await pantalaimon.createClientWithCredentials(config.pantalaimon.username, config.pantalaimon.password);
|
||||||
patchMatrixClientForConciseExceptions();
|
patchMatrixClient();
|
||||||
await ensureAliasedRoomExists(client, config.managementRoom);
|
await ensureAliasedRoomExists(client, config.managementRoom);
|
||||||
let mjolnir = await Mjolnir.setupMjolnirFromConfig(client);
|
let mjolnir = await Mjolnir.setupMjolnirFromConfig(client);
|
||||||
globalClient = client;
|
globalClient = client;
|
||||||
|
@ -8,7 +8,7 @@ import { getMessagesByUserIn } from "../../src/utils";
|
|||||||
*/
|
*/
|
||||||
describe("Test: timeline pagination", function () {
|
describe("Test: timeline pagination", function () {
|
||||||
it('does not paginate across the entire room history while backfilling.', async function() {
|
it('does not paginate across the entire room history while backfilling.', async function() {
|
||||||
this.timeout(20000);
|
this.timeout(60000);
|
||||||
// Create a few users and a room.
|
// Create a few users and a room.
|
||||||
let badUser = await newTestUser(false, "spammer");
|
let badUser = await newTestUser(false, "spammer");
|
||||||
let badUserId = await badUser.getUserId();
|
let badUserId = await badUser.getUserId();
|
||||||
@ -38,7 +38,7 @@ describe("Test: timeline pagination", function () {
|
|||||||
assert.equal(eventCount, 7, "There shouldn't be any more events (1 member event and 6 messages), and they should all be from the same account.");
|
assert.equal(eventCount, 7, "There shouldn't be any more events (1 member event and 6 messages), and they should all be from the same account.");
|
||||||
})
|
})
|
||||||
it('does not call the callback with an empty array when there are no relevant events', async function() {
|
it('does not call the callback with an empty array when there are no relevant events', async function() {
|
||||||
this.timeout(20000);
|
this.timeout(60000);
|
||||||
let badUser = await newTestUser(false, "spammer");
|
let badUser = await newTestUser(false, "spammer");
|
||||||
let badUserId = await badUser.getUserId();
|
let badUserId = await badUser.getUserId();
|
||||||
let moderator = await newTestUser(false, "moderator");
|
let moderator = await newTestUser(false, "moderator");
|
||||||
@ -53,7 +53,7 @@ describe("Test: timeline pagination", function () {
|
|||||||
assert.equal(cbCount, 0, "The callback should never get called");
|
assert.equal(cbCount, 0, "The callback should never get called");
|
||||||
})
|
})
|
||||||
it("The limit provided is respected", async function() {
|
it("The limit provided is respected", async function() {
|
||||||
this.timeout(20000);
|
this.timeout(60000);
|
||||||
let badUser = await newTestUser(false, "spammer");
|
let badUser = await newTestUser(false, "spammer");
|
||||||
let badUserId = await badUser.getUserId();
|
let badUserId = await badUser.getUserId();
|
||||||
let moderator = await newTestUser(false, "moderator");
|
let moderator = await newTestUser(false, "moderator");
|
||||||
@ -82,7 +82,7 @@ describe("Test: timeline pagination", function () {
|
|||||||
assert.equal(cbCount, 1, "The callback should be called once with events matching the glob.");
|
assert.equal(cbCount, 1, "The callback should be called once with events matching the glob.");
|
||||||
});
|
});
|
||||||
it("Gives the events to the callback ordered by youngest first (even more important when the limit is reached halfway through a chunk).", async function() {
|
it("Gives the events to the callback ordered by youngest first (even more important when the limit is reached halfway through a chunk).", async function() {
|
||||||
this.timeout(20000);
|
this.timeout(60000);
|
||||||
let moderator = await newTestUser(false, "moderator");
|
let moderator = await newTestUser(false, "moderator");
|
||||||
let moderatorId = await moderator.getUserId();
|
let moderatorId = await moderator.getUserId();
|
||||||
let targetRoom = await moderator.createRoom();
|
let targetRoom = await moderator.createRoom();
|
||||||
|
Loading…
Reference in New Issue
Block a user