mirror of
https://github.com/the-draupnir-project/Draupnir.git
synced 2026-08-29 07:08:50 +00:00
Move Mjolnir setup and configuration to a sensible place.
This commit is contained in:
@@ -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
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user