Skip to content

Commit ecac66e

Browse files
authored
Merge pull request #536 from the-draupnir-project/gnuxie/interface-manager-rework
Extract the `@the-draupnir-project/interface-manager` system to its own library where it can be better tested and improved.
2 parents 5c2eebd + 0854aec commit ecac66e

File tree

77 files changed

+2615
-5787
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

77 files changed

+2615
-5787
lines changed

.github/workflows/mjolnir.yml

Lines changed: 46 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@ name: Tests
66

77
on:
88
push:
9-
branches: [ main ]
9+
branches: [main]
1010
pull_request:
11-
branches: [ main ]
11+
branches: [main]
1212
schedule:
13-
- cron: '20 20 * * *'
13+
- cron: "20 20 * * *"
1414
env:
1515
CARGO_TERM_COLOR: always
1616

@@ -19,68 +19,57 @@ jobs:
1919
name: Build & Lint
2020
runs-on: ubuntu-latest
2121
steps:
22-
- uses: actions/checkout@v4
22+
- uses: actions/checkout@v4
2323

24-
- name: Specifically use node 18 like in the readme.
25-
uses: actions/setup-node@v4
26-
with:
27-
node-version: '18'
28-
- run: corepack enable
29-
- run: corepack yarn install
30-
- run: corepack yarn build
31-
- run: corepack yarn lint
32-
unit:
33-
name: Unit tests
34-
runs-on: ubuntu-latest
35-
steps:
36-
- uses: actions/checkout@v4
37-
- name: Specifically use node 18 like in the readme.
38-
uses: actions/setup-node@v4
39-
with:
40-
node-version: '18'
41-
- run: corepack yarn install
42-
- run: corepack yarn test
24+
- name: Specifically use node 18 like in the readme.
25+
uses: actions/setup-node@v4
26+
with:
27+
node-version: "18"
28+
- run: corepack enable
29+
- run: corepack yarn install
30+
- run: corepack yarn build
31+
- run: corepack yarn lint
4332
integration:
4433
name: Integration tests
4534
runs-on: ubuntu-latest
4635
timeout-minutes: 60
4736
steps:
48-
- uses: actions/checkout@v4
49-
- uses: actions/setup-node@v4
50-
with:
51-
node-version: '18'
52-
- name: Fetch and build mx-tester (cached across runs)
53-
uses: baptiste0928/cargo-install@v3
54-
with:
55-
crate: mx-tester
56-
version: "0.3.3"
57-
- name: Setup image
58-
run: RUST_LOG=debug,hyper=info,rusttls=info mx-tester build up
59-
- name: Setup dependencies
60-
run: corepack yarn install
61-
- name: Run tests
62-
run: RUST_LOG=debug,hyper=info,rusttls=info mx-tester run
63-
- name: Cleanup
64-
run: mx-tester down
37+
- uses: actions/checkout@v4
38+
- uses: actions/setup-node@v4
39+
with:
40+
node-version: "18"
41+
- name: Fetch and build mx-tester (cached across runs)
42+
uses: baptiste0928/cargo-install@v3
43+
with:
44+
crate: mx-tester
45+
version: "0.3.3"
46+
- name: Setup image
47+
run: RUST_LOG=debug,hyper=info,rusttls=info mx-tester build up
48+
- name: Setup dependencies
49+
run: corepack yarn install
50+
- name: Run tests
51+
run: RUST_LOG=debug,hyper=info,rusttls=info mx-tester run
52+
- name: Cleanup
53+
run: mx-tester down
6554
appservice-integration:
6655
name: Application Service Integration tests
6756
runs-on: ubuntu-latest
6857
timeout-minutes: 30
6958
steps:
70-
- uses: actions/checkout@v4
71-
- uses: actions/setup-node@v4
72-
with:
73-
node-version: '18'
74-
- name: Fetch and build mx-tester (cached across runs)
75-
uses: baptiste0928/cargo-install@v3
76-
with:
77-
crate: mx-tester
78-
version: "0.3.3"
79-
- name: Setup image
80-
run: RUST_LOG=debug,hyper=info,rusttls=info mx-tester build up
81-
- name: Setup dependencies
82-
run: corepack yarn install
83-
- name: Run tests
84-
run: corepack yarn test:appservice:integration
85-
- name: Cleanup
86-
run: mx-tester down
59+
- uses: actions/checkout@v4
60+
- uses: actions/setup-node@v4
61+
with:
62+
node-version: "18"
63+
- name: Fetch and build mx-tester (cached across runs)
64+
uses: baptiste0928/cargo-install@v3
65+
with:
66+
crate: mx-tester
67+
version: "0.3.3"
68+
- name: Setup image
69+
run: RUST_LOG=debug,hyper=info,rusttls=info mx-tester build up
70+
- name: Setup dependencies
71+
run: corepack yarn install
72+
- name: Run tests
73+
run: corepack yarn test:appservice:integration
74+
- name: Cleanup
75+
run: mx-tester down

package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,11 @@
5050
"typescript-formatter": "^7.2"
5151
},
5252
"dependencies": {
53+
"@gnuxie/typescript-result": "^0.2.0",
5354
"@sentry/node": "^7.17.2",
5455
"@sentry/tracing": "^7.17.2",
5556
"@sinclair/typebox": "0.32.34",
57+
"@the-draupnir-project/interface-manager": "1.0.2",
5658
"@the-draupnir-project/matrix-basic-types": "^0.1.1",
5759
"await-lock": "^2.2.2",
5860
"better-sqlite3": "^9.4.3",
@@ -65,8 +67,8 @@
6567
"js-yaml": "^4.1.0",
6668
"jsdom": "^24.0.0",
6769
"matrix-appservice-bridge": "^9.0.1",
68-
"matrix-protection-suite": "npm:@gnuxie/matrix-protection-suite@1.0.0",
69-
"matrix-protection-suite-for-matrix-bot-sdk": "npm:@gnuxie/matrix-protection-suite-for-matrix-bot-sdk@1.0.0",
70+
"matrix-protection-suite": "npm:@gnuxie/matrix-protection-suite@1.1.0",
71+
"matrix-protection-suite-for-matrix-bot-sdk": "npm:@gnuxie/matrix-protection-suite-for-matrix-bot-sdk@1.1.0",
7072
"parse-duration": "^1.0.2",
7173
"pg": "^8.8.0",
7274
"shell-quote": "^1.7.3",

src/Draupnir.ts

Lines changed: 26 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ import {
3131
isError,
3232
} from "matrix-protection-suite";
3333
import { UnlistedUserRedactionQueue } from "./queues/UnlistedUserRedactionQueue";
34-
import { findCommandTable } from "./commands/interface-manager/InterfaceCommand";
3534
import { ThrottlingQueue } from "./queues/ThrottlingQueue";
3635
import ManagementRoomOutput from "./ManagementRoomOutput";
3736
import { ReportPoller } from "./report/ReportPoller";
@@ -42,12 +41,6 @@ import {
4241
SynapseAdminClient,
4342
} from "matrix-protection-suite-for-matrix-bot-sdk";
4443
import { IConfig } from "./config";
45-
import {
46-
COMMAND_PREFIX,
47-
DraupnirContext,
48-
extractCommandFromMessageBody,
49-
handleCommand,
50-
} from "./commands/CommandHandler";
5144
import { LogLevel } from "matrix-bot-sdk";
5245
import {
5346
ARGUMENT_PROMPT_LISTENER,
@@ -59,7 +52,6 @@ import { RendererMessageCollector } from "./capabilities/RendererMessageCollecto
5952
import { DraupnirRendererMessageCollector } from "./capabilities/DraupnirRendererMessageCollector";
6053
import { renderProtectionFailedToStart } from "./protections/ProtectedRoomsSetRenderers";
6154
import { draupnirStatusInfo, renderStatusInfo } from "./commands/StatusCommand";
62-
import { renderMatrixAndSend } from "./commands/interface-manager/DeadDocumentMatrix";
6355
import { isInvitationForUser } from "./protections/invitation/inviteCore";
6456
import {
6557
StringRoomID,
@@ -68,9 +60,13 @@ import {
6860
isStringRoomID,
6961
isStringRoomAlias,
7062
MatrixRoomReference,
71-
userLocalpart,
7263
userServerName,
7364
} from "@the-draupnir-project/matrix-basic-types";
65+
import {
66+
MatrixAdaptorContext,
67+
sendMatrixEventsFromDeadDocument,
68+
} from "./commands/interface-manager/MPSMatrixInterfaceAdaptor";
69+
import { makeDraupnirCommandDispatcher } from "./commands/DraupnirCommandDispatcher";
7470

7571
const log = new Logger("Draupnir");
7672

@@ -80,14 +76,14 @@ const log = new Logger("Draupnir");
8076
// to Mjolnir because it needs to be started after Mjolnir started and not before.
8177
// And giving it to the class was a dumb easy way of doing that.
8278

83-
export class Draupnir implements Client {
79+
export class Draupnir implements Client, MatrixAdaptorContext {
8480
/**
8581
* This is for users who are not listed on a watchlist,
8682
* but have been flagged by the automatic spam detection as suispicous
8783
*/
8884
public unlistedUserRedactionQueue = new UnlistedUserRedactionQueue();
8985

90-
private readonly commandTable = findCommandTable("draupnir");
86+
private readonly commandDispatcher = makeDraupnirCommandDispatcher(this);
9187
public taskQueue: ThrottlingQueue;
9288
/**
9389
* Reporting back to the management room.
@@ -106,8 +102,6 @@ export class Draupnir implements Client {
106102

107103
public readonly reactionHandler: MatrixReactionHandler;
108104

109-
public readonly commandContext: Omit<DraupnirContext, "event">;
110-
111105
private readonly timelineEventListener = this.handleTimelineEvent.bind(this);
112106

113107
public readonly capabilityMessageRenderer: RendererMessageCollector;
@@ -146,38 +140,16 @@ export class Draupnir implements Client {
146140
if (config.pollReports) {
147141
this.reportPoller = new ReportPoller(this, this.reportManager);
148142
}
149-
150-
this.commandContext = {
151-
draupnir: this,
152-
roomID: this.managementRoomID,
153-
client: this.client,
154-
reactionHandler: this.reactionHandler,
155-
clientPlatform: this.clientPlatform,
156-
};
157143
this.reactionHandler.on(
158144
ARGUMENT_PROMPT_LISTENER,
159-
makeListenerForArgumentPrompt(
160-
this.client,
161-
this.clientPlatform,
162-
this.managementRoomID,
163-
this.reactionHandler,
164-
this.commandTable,
165-
this.commandContext
166-
)
145+
makeListenerForArgumentPrompt(this.commandDispatcher)
167146
);
168147
this.reactionHandler.on(
169148
DEFAUILT_ARGUMENT_PROMPT_LISTENER,
170-
makeListenerForPromptDefault(
171-
this.client,
172-
this.clientPlatform,
173-
this.managementRoomID,
174-
this.reactionHandler,
175-
this.commandTable,
176-
this.commandContext
177-
)
149+
makeListenerForPromptDefault(this.commandDispatcher)
178150
);
179151
this.capabilityMessageRenderer = new DraupnirRendererMessageCollector(
180-
this.client,
152+
this.clientPlatform.toRoomMessageSender(),
181153
this.managementRoomID
182154
);
183155
}
@@ -256,7 +228,7 @@ export class Draupnir implements Client {
256228
draupnir,
257229
(error, protectionName, description) =>
258230
renderProtectionFailedToStart(
259-
client,
231+
clientPlatform.toRoomMessageSender(),
260232
managementRoom.toRoomIDOrAlias(),
261233
error,
262234
protectionName,
@@ -291,11 +263,11 @@ export class Draupnir implements Client {
291263
"Mjolnir@startup",
292264
"Startup complete. Now monitoring rooms."
293265
);
294-
await renderMatrixAndSend(
295-
renderStatusInfo(statusInfo),
266+
await sendMatrixEventsFromDeadDocument(
267+
this.clientPlatform.toRoomMessageSender(),
296268
this.managementRoomID,
297-
undefined,
298-
this.client
269+
renderStatusInfo(statusInfo),
270+
{}
299271
);
300272
} catch (ex) {
301273
log.error(`Caught an error when trying to show status at startup`, ex);
@@ -355,33 +327,12 @@ export class Draupnir implements Client {
355327
);
356328
return;
357329
}
358-
const commandBeingRun = extractCommandFromMessageBody(
359-
event.content.body,
330+
this.commandDispatcher.handleCommandMessageEvent(
360331
{
361-
prefix: COMMAND_PREFIX,
362-
localpart: userLocalpart(this.clientUserID),
363-
userId: this.clientUserID,
364-
additionalPrefixes: this.config.commands.additionalPrefixes,
365-
allowNoPrefix: this.config.commands.allowNoPrefix,
366-
}
367-
);
368-
if (commandBeingRun === undefined) {
369-
return;
370-
}
371-
log.info(`Command being run by ${event.sender}: ${commandBeingRun}`);
372-
void Task(
373-
this.client
374-
.sendReadReceipt(roomID, event.event_id)
375-
.then((_) => Ok(undefined))
376-
);
377-
void Task(
378-
handleCommand(
379-
roomID,
380332
event,
381-
commandBeingRun,
382-
this,
383-
this.commandTable
384-
).then((_) => Ok(undefined))
333+
roomID,
334+
},
335+
event.content.body
385336
);
386337
}
387338
this.reportManager.handleTimelineEvent(roomID, event);
@@ -414,4 +365,11 @@ export class Draupnir implements Client {
414365
public handleEventReport(report: EventReport): void {
415366
this.protectedRoomsSet.handleEventReport(report);
416367
}
368+
369+
/**
370+
* This is needed to implement the MatrixInterfaceAdaptor interface.
371+
*/
372+
public get commandRoomID() {
373+
return this.managementRoomID;
374+
}
417375
}

src/appservice/AppService.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ const log = new Logger("AppService");
5656
*/
5757
export class MjolnirAppService {
5858
private readonly api: Api;
59-
private readonly commands: AppserviceCommandHandler;
59+
public readonly commands: AppserviceCommandHandler;
6060

6161
/**
6262
* The constructor is private because we want to ensure intialization steps are followed,

0 commit comments

Comments
 (0)