Move Mjolnir setup and configuration to a sensible place.

This commit is contained in:
gnuxie
2021-09-27 16:04:01 +01:00
parent c29cfe370a
commit 400af48e6e
4 changed files with 87 additions and 99 deletions
+83
View File
@@ -21,6 +21,7 @@ import {
LogService,
MatrixClient,
MatrixGlob,
MembershipEvent,
Permalinks,
UserID
} from "matrix-bot-sdk";
@@ -37,6 +38,7 @@ import { PROTECTIONS } from "./protections/protections";
import { UnlistedUserRedactionQueue } from "./queues/UnlistedUserRedactionQueue";
import { Healthz } from "./health/healthz";
import { EventRedactionQueue, RedactUserInRoom } from "./queues/EventRedactionQueue";
import * as htmlEscape from "escape-html";
export const STATE_NOT_STARTED = "not_started";
export const STATE_CHECKING_PERMISSIONS = "checking_permissions";
@@ -69,6 +71,84 @@ export class Mjolnir {
private explicitlyProtectedRoomIds: string[] = [];
private knownUnprotectedRooms: string[] = [];
/**
* Adds a listener to the client that will automatically accept invitations.
* @param {MatrixClient} client
* @param options By default accepts invites from anyone.
* @param {string} options.managementRoom The room to report ignored invitations to if `recordIgnoredInvites` is true.
* @param {boolean} options.recordIgnoredInvites Whether to report invites that will be ignored to the `managementRoom`.
* @param {boolean} options.autojoinOnlyIfManager Whether to only accept an invitation by a user present in the `managementRoom`.
* @param {string} options.acceptInvitesFromGroup A group of users to accept invites from, ignores invites form users not in this group.
*/
private static addJoinOnInviteListener(client: MatrixClient, options) {
client.on("room.invite", async (roomId: string, inviteEvent: any) => {
const membershipEvent = new MembershipEvent(inviteEvent);
const reportInvite = async () => {
if (!options.recordIgnoredInvites) return; // Nothing to do
await client.sendMessage(options.managementRoom, {
msgtype: "m.text",
body: `${membershipEvent.sender} has invited me to ${roomId} but the config prevents me from accepting the invitation. `
+ `If you would like this room protected, use "!mjolnir rooms add ${roomId}" so I can accept the invite.`,
format: "org.matrix.custom.html",
formatted_body: `${htmlEscape(membershipEvent.sender)} has invited me to ${htmlEscape(roomId)} but the config prevents me from `
+ `accepting the invitation. If you would like this room protected, use <code>!mjolnir rooms add ${htmlEscape(roomId)}</code> `
+ `so I can accept the invite.`,
});
};
if (options.autojoinOnlyIfManager) {
const managers = await client.getJoinedRoomMembers(options.managementRoom);
if (!managers.includes(membershipEvent.sender)) return reportInvite(); // ignore invite
} else {
const groupMembers = await client.unstableApis.getGroupUsers(options.acceptInvitesFromGroup);
const userIds = groupMembers.map(m => m.user_id);
if (!userIds.includes(membershipEvent.sender)) return reportInvite(); // ignore invite
}
return client.joinRoom(roomId);
});
}
/**
* Create a new Mjolnir instance from a client and the options in the configuration file, ready to be started.
* @param {MatrixClient} client The client for Mjolnir to use.
* @returns A new Mjolnir instance that can be started without further setup.
*/
static async setupMjolnirFromConfig(client: MatrixClient): Promise<Mjolnir> {
Mjolnir.addJoinOnInviteListener(client, config);
const banLists: BanList[] = [];
const protectedRooms: { [roomId: string]: string } = {};
const joinedRooms = await client.getJoinedRooms();
// Ensure we're also joined to the rooms we're protecting
LogService.info("index", "Resolving protected rooms...");
for (const roomRef of config.protectedRooms) {
const permalink = Permalinks.parseUrl(roomRef);
if (!permalink.roomIdOrAlias) continue;
let roomId = await client.resolveRoom(permalink.roomIdOrAlias);
if (!joinedRooms.includes(roomId)) {
roomId = await client.joinRoom(permalink.roomIdOrAlias, permalink.viaServers);
}
protectedRooms[roomId] = roomRef;
}
// Ensure we're also in the management room
LogService.info("index", "Resolving management room...");
const managementRoomId = await client.resolveRoom(config.managementRoom);
if (!joinedRooms.includes(managementRoomId)) {
config.managementRoom = await client.joinRoom(config.managementRoom);
} else {
config.managementRoom = managementRoomId;
}
await logMessage(LogLevel.INFO, "index", "Mjolnir is starting up. Use !mjolnir to query status.");
return new Mjolnir(client, protectedRooms, banLists);
}
constructor(
public readonly client: MatrixClient,
public readonly protectedRooms: { [roomId: string]: string },
@@ -209,6 +289,9 @@ export class Mjolnir {
});
}
/**
* Stop Mjolnir from syncing and processing commands.
*/
public stop() {
this.client.stop();
}
+2 -2
View File
@@ -26,7 +26,7 @@ import {
import config from "./config";
import { logMessage } from "./LogProxy";
import { Healthz } from "./health/healthz";
import { setupMjolnir } from "./setup";
import { Mjolnir } from "./Mjolnir";
config.RUNTIME = {};
@@ -54,7 +54,7 @@ if (config.health.healthz.enabled) {
config.RUNTIME.client = client;
let bot = await setupMjolnir(client, config);
let bot = await Mjolnir.setupMjolnirFromConfig(client);
await bot.start();
})().catch(err => {
logMessage(LogLevel.ERROR, "index", err);
-95
View File
@@ -1,95 +0,0 @@
/*
Copyright 2019-2021 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import { LogLevel, LogService, MatrixClient, Permalinks } from "matrix-bot-sdk";
import { MembershipEvent } from "matrix-bot-sdk/lib/models/events/MembershipEvent";
import * as htmlEscape from "escape-html";
import BanList from "./models/BanList";
import { logMessage } from "./LogProxy";
import { Mjolnir } from "./Mjolnir";
/**
* Adds a listener to the client that will automatically accept invitations.
* @param {MatrixClient} client
* @param options By default accepts invites from anyone.
* @param {string} options.managementRoom The room to report ignored invitations to if `recordIgnoredInvites` is true.
* @param {boolean} options.recordIgnoredInvites Whether to report invites that will be ignored to the `managementRoom`.
* @param {boolean} options.autojoinOnlyIfManager Whether to only accept an invitation by a user present in the `managementRoom`.
* @param {string} options.acceptInvitesFromGroup A group of users to accept invites from, ignores invites form users not in this group.
*/
function addJoinOnInviteListener(client: MatrixClient, options) {
client.on("room.invite", async (roomId: string, inviteEvent: any) => {
const membershipEvent = new MembershipEvent(inviteEvent);
const reportInvite = async () => {
if (!options.recordIgnoredInvites) return; // Nothing to do
await client.sendMessage(options.managementRoom, {
msgtype: "m.text",
body: `${membershipEvent.sender} has invited me to ${roomId} but the config prevents me from accepting the invitation. `
+ `If you would like this room protected, use "!mjolnir rooms add ${roomId}" so I can accept the invite.`,
format: "org.matrix.custom.html",
formatted_body: `${htmlEscape(membershipEvent.sender)} has invited me to ${htmlEscape(roomId)} but the config prevents me from `
+ `accepting the invitation. If you would like this room protected, use <code>!mjolnir rooms add ${htmlEscape(roomId)}</code> `
+ `so I can accept the invite.`,
});
};
if (options.autojoinOnlyIfManager) {
const managers = await client.getJoinedRoomMembers(options.managementRoom);
if (!managers.includes(membershipEvent.sender)) return reportInvite(); // ignore invite
} else {
const groupMembers = await client.unstableApis.getGroupUsers(options.acceptInvitesFromGroup);
const userIds = groupMembers.map(m => m.user_id);
if (!userIds.includes(membershipEvent.sender)) return reportInvite(); // ignore invite
}
return client.joinRoom(roomId);
});
}
export async function setupMjolnir(client, config): Promise<Mjolnir> {
addJoinOnInviteListener(client, config);
const banLists: BanList[] = [];
const protectedRooms: { [roomId: string]: string } = {};
const joinedRooms = await client.getJoinedRooms();
// Ensure we're also joined to the rooms we're protecting
LogService.info("index", "Resolving protected rooms...");
for (const roomRef of config.protectedRooms) {
const permalink = Permalinks.parseUrl(roomRef);
if (!permalink.roomIdOrAlias) continue;
let roomId = await client.resolveRoom(permalink.roomIdOrAlias);
if (!joinedRooms.includes(roomId)) {
roomId = await client.joinRoom(permalink.roomIdOrAlias, permalink.viaServers);
}
protectedRooms[roomId] = roomRef;
}
// Ensure we're also in the management room
LogService.info("index", "Resolving management room...");
const managementRoomId = await client.resolveRoom(config.managementRoom);
if (!joinedRooms.includes(managementRoomId)) {
config.managementRoom = await client.joinRoom(config.managementRoom);
} else {
config.managementRoom = managementRoomId;
}
await logMessage(LogLevel.INFO, "index", "Mjolnir is starting up. Use !mjolnir to query status.");
return new Mjolnir(client, protectedRooms, banLists);
}
+2 -2
View File
@@ -18,10 +18,10 @@ import {
PantalaimonClient,
MemoryStorageProvider
} from "matrix-bot-sdk";
import { Mjolnir} from '../../src/Mjolnir';
import config from "../../src/config";
import * as path from 'path';
import * as fs from 'fs/promises';
import { setupMjolnir } from '../../src/setup';
import { registerUser } from "./clientHelper";
/**
@@ -61,7 +61,7 @@ export async function makeMjolnir() {
const pantalaimon = new PantalaimonClient(config.homeserverUrl, new MemoryStorageProvider());
const client = await pantalaimon.createClientWithCredentials(config.pantalaimon.username, config.pantalaimon.password);
await ensureAliasedRoomExists(client, config.managementRoom);
return await setupMjolnir(client, config);
return await Mjolnir.setupMjolnirFromConfig(client);
}
/**