mirror of
https://github.com/the-draupnir-project/Draupnir.git
synced 2026-04-27 03:15:17 +00:00
Migrate bot mode to use the safe mode toggle, but only for draupnir.
This commit is contained in:
+137
-69
@@ -13,6 +13,7 @@ import {
|
||||
DefaultEventDecoder,
|
||||
setGlobalLoggerProvider,
|
||||
RoomStateBackingStore,
|
||||
ClientsInRoomMap,
|
||||
} from "matrix-protection-suite";
|
||||
import {
|
||||
BotSDKLogServiceLogger,
|
||||
@@ -20,7 +21,7 @@ import {
|
||||
MatrixSendClient,
|
||||
RoomStateManagerFactory,
|
||||
SafeMatrixEmitter,
|
||||
resolveRoomReferenceSafe,
|
||||
joinedRoomsSafe,
|
||||
} from "matrix-protection-suite-for-matrix-bot-sdk";
|
||||
import { IConfig } from "./config";
|
||||
import { Draupnir } from "./Draupnir";
|
||||
@@ -32,7 +33,13 @@ import {
|
||||
isStringRoomID,
|
||||
MatrixRoomReference,
|
||||
StringUserID,
|
||||
MatrixRoomID,
|
||||
} from "@the-draupnir-project/matrix-basic-types";
|
||||
import { Result, isError } from "@gnuxie/typescript-result";
|
||||
import { SafeModeToggle } from "./safemode/SafeModeToggle";
|
||||
import { SafeModeDraupnir } from "./safemode/DraupnirSafeMode";
|
||||
import { ResultError } from "@gnuxie/typescript-result";
|
||||
import { SafeModeCause } from "./safemode/SafeModeCause";
|
||||
|
||||
setGlobalLoggerProvider(new BotSDKLogServiceLogger());
|
||||
|
||||
@@ -40,77 +47,138 @@ export function constructWebAPIs(draupnir: Draupnir): WebAPIs {
|
||||
return new WebAPIs(draupnir.reportManager, draupnir.config);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a file for providing default concrete implementations
|
||||
* for all things to bootstrap Draupnir in 'bot mode'.
|
||||
* However, people should be encouraged to make their own when
|
||||
* APIs are stable as the protection-suite makes Draupnir
|
||||
* almost completely modular and customizable.
|
||||
*/
|
||||
export class DraupnirBotModeToggle implements SafeModeToggle {
|
||||
private draupnir: Draupnir | null = null;
|
||||
private safeModeDraupnir: SafeModeDraupnir | null = null;
|
||||
|
||||
export async function makeDraupnirBotModeFromConfig(
|
||||
client: MatrixSendClient,
|
||||
matrixEmitter: SafeMatrixEmitter,
|
||||
config: IConfig,
|
||||
backingStore?: RoomStateBackingStore
|
||||
): Promise<Draupnir> {
|
||||
const clientUserId = await client.getUserId();
|
||||
if (!isStringUserID(clientUserId)) {
|
||||
throw new TypeError(`${clientUserId} is not a valid mxid`);
|
||||
}
|
||||
if (
|
||||
!isStringRoomAlias(config.managementRoom) &&
|
||||
!isStringRoomID(config.managementRoom)
|
||||
private constructor(
|
||||
private readonly clientUserID: StringUserID,
|
||||
private readonly managementRoom: MatrixRoomID,
|
||||
private readonly clientsInRoomMap: ClientsInRoomMap,
|
||||
private readonly roomStateManagerFactory: RoomStateManagerFactory,
|
||||
private readonly draupnirFactory: DraupnirFactory,
|
||||
private readonly matrixEmitter: SafeMatrixEmitter,
|
||||
private readonly config: IConfig
|
||||
) {
|
||||
throw new TypeError(
|
||||
`${config.managementRoom} is not a valid room id or alias`
|
||||
this.matrixEmitter.on("room.invite", (roomID, event) => {
|
||||
this.clientsInRoomMap.handleTimelineEvent(roomID, event);
|
||||
});
|
||||
this.matrixEmitter.on("room.event", (roomID, event) => {
|
||||
this.roomStateManagerFactory.handleTimelineEvent(roomID, event);
|
||||
this.clientsInRoomMap.handleTimelineEvent(roomID, event);
|
||||
});
|
||||
}
|
||||
public static async create(
|
||||
client: MatrixSendClient,
|
||||
matrixEmitter: SafeMatrixEmitter,
|
||||
config: IConfig,
|
||||
backingStore?: RoomStateBackingStore
|
||||
): Promise<DraupnirBotModeToggle> {
|
||||
const clientUserID = await client.getUserId();
|
||||
if (!isStringUserID(clientUserID)) {
|
||||
throw new TypeError(`${clientUserID} is not a valid mxid`);
|
||||
}
|
||||
if (
|
||||
!isStringRoomAlias(config.managementRoom) &&
|
||||
!isStringRoomID(config.managementRoom)
|
||||
) {
|
||||
throw new TypeError(
|
||||
`${config.managementRoom} is not a valid room id or alias`
|
||||
);
|
||||
}
|
||||
const configManagementRoomReference = MatrixRoomReference.fromRoomIDOrAlias(
|
||||
config.managementRoom
|
||||
);
|
||||
const clientsInRoomMap = new StandardClientsInRoomMap();
|
||||
const clientCapabilityFactory = new ClientCapabilityFactory(
|
||||
clientsInRoomMap,
|
||||
DefaultEventDecoder
|
||||
);
|
||||
// needed to have accurate join infomration.
|
||||
(
|
||||
await clientsInRoomMap.makeClientRooms(clientUserID, async () =>
|
||||
joinedRoomsSafe(client)
|
||||
)
|
||||
).expect("Unable to create ClientRooms");
|
||||
const clientPlatform = clientCapabilityFactory.makeClientPlatform(
|
||||
clientUserID,
|
||||
client
|
||||
);
|
||||
const managementRoom = (
|
||||
await clientPlatform
|
||||
.toRoomResolver()
|
||||
.resolveRoom(configManagementRoomReference)
|
||||
).expect("Unable to resolve Draupnir's management room");
|
||||
(await clientPlatform.toRoomJoiner().joinRoom(managementRoom)).expect(
|
||||
"Unable to join Draupnir's management room"
|
||||
);
|
||||
const clientProvider = async (userID: StringUserID) => {
|
||||
if (userID !== clientUserID) {
|
||||
throw new TypeError(`Bot mode shouldn't be requesting any other mxids`);
|
||||
}
|
||||
return client;
|
||||
};
|
||||
const roomStateManagerFactory = new RoomStateManagerFactory(
|
||||
clientsInRoomMap,
|
||||
clientProvider,
|
||||
DefaultEventDecoder,
|
||||
backingStore
|
||||
);
|
||||
const draupnirFactory = new DraupnirFactory(
|
||||
clientsInRoomMap,
|
||||
clientCapabilityFactory,
|
||||
clientProvider,
|
||||
roomStateManagerFactory
|
||||
);
|
||||
return new DraupnirBotModeToggle(
|
||||
clientUserID,
|
||||
managementRoom,
|
||||
clientsInRoomMap,
|
||||
roomStateManagerFactory,
|
||||
draupnirFactory,
|
||||
matrixEmitter,
|
||||
config
|
||||
);
|
||||
}
|
||||
const configManagementRoomReference = MatrixRoomReference.fromRoomIDOrAlias(
|
||||
config.managementRoom
|
||||
);
|
||||
const managementRoom = (
|
||||
await resolveRoomReferenceSafe(client, configManagementRoomReference)
|
||||
).expect("Unable to resolve Draupnir's management room");
|
||||
|
||||
await client.joinRoom(
|
||||
managementRoom.toRoomIDOrAlias(),
|
||||
managementRoom.getViaServers()
|
||||
);
|
||||
const clientsInRoomMap = new StandardClientsInRoomMap();
|
||||
const clientProvider = async (userID: StringUserID) => {
|
||||
if (userID !== clientUserId) {
|
||||
throw new TypeError(`Bot mode shouldn't be requesting any other mxids`);
|
||||
public async switchToDraupnir(): Promise<Result<Draupnir>> {
|
||||
if (this.draupnir !== null) {
|
||||
return ResultError.Result(
|
||||
`There is a draupnir for ${this.clientUserID} already running`
|
||||
);
|
||||
}
|
||||
return client;
|
||||
};
|
||||
const roomStateManagerFactory = new RoomStateManagerFactory(
|
||||
clientsInRoomMap,
|
||||
clientProvider,
|
||||
DefaultEventDecoder,
|
||||
backingStore
|
||||
);
|
||||
const clientCapabilityFactory = new ClientCapabilityFactory(
|
||||
clientsInRoomMap,
|
||||
DefaultEventDecoder
|
||||
);
|
||||
const draupnirFactory = new DraupnirFactory(
|
||||
clientsInRoomMap,
|
||||
clientCapabilityFactory,
|
||||
clientProvider,
|
||||
roomStateManagerFactory
|
||||
);
|
||||
const draupnir = await draupnirFactory.makeDraupnir(
|
||||
clientUserId,
|
||||
managementRoom,
|
||||
config
|
||||
);
|
||||
matrixEmitter.on("room.invite", (roomID, event) => {
|
||||
clientsInRoomMap.handleTimelineEvent(roomID, event);
|
||||
});
|
||||
matrixEmitter.on("room.event", (roomID, event) => {
|
||||
roomStateManagerFactory.handleTimelineEvent(roomID, event);
|
||||
clientsInRoomMap.handleTimelineEvent(roomID, event);
|
||||
});
|
||||
return draupnir.expect("Unable to create Draupnir");
|
||||
const draupnirResult = await this.draupnirFactory.makeDraupnir(
|
||||
this.clientUserID,
|
||||
this.managementRoom,
|
||||
this.config
|
||||
);
|
||||
if (isError(draupnirResult)) {
|
||||
return draupnirResult;
|
||||
}
|
||||
this.safeModeDraupnir?.stop();
|
||||
this.safeModeDraupnir = null;
|
||||
this.draupnir = draupnirResult.ok;
|
||||
return draupnirResult;
|
||||
}
|
||||
public async switchToSafeMode(
|
||||
cause: SafeModeCause
|
||||
): Promise<Result<SafeModeDraupnir>> {
|
||||
if (this.safeModeDraupnir !== null) {
|
||||
return ResultError.Result(
|
||||
`There is a safe mode draupnir for ${this.clientUserID} already running`
|
||||
);
|
||||
}
|
||||
const safeModeResult = await this.draupnirFactory.makeSafeModeDraupnir(
|
||||
this.clientUserID,
|
||||
this.managementRoom,
|
||||
this.config,
|
||||
cause
|
||||
);
|
||||
if (isError(safeModeResult)) {
|
||||
return safeModeResult;
|
||||
}
|
||||
this.draupnir?.stop();
|
||||
this.draupnir = null;
|
||||
this.safeModeDraupnir = safeModeResult.ok;
|
||||
return safeModeResult;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,6 +89,9 @@ export interface IConfig {
|
||||
redactReason: string;
|
||||
};
|
||||
};
|
||||
safeMode?: {
|
||||
bootOnStartupFailure: boolean;
|
||||
};
|
||||
health: {
|
||||
healthz: {
|
||||
enabled: boolean;
|
||||
@@ -188,6 +191,9 @@ const defaultConfig: IConfig = {
|
||||
"You have mentioned too many users in this message, so we have had to redact it.",
|
||||
},
|
||||
},
|
||||
safeMode: {
|
||||
bootOnStartupFailure: false,
|
||||
},
|
||||
health: {
|
||||
healthz: {
|
||||
enabled: false,
|
||||
|
||||
+6
-5
@@ -24,10 +24,7 @@ import {
|
||||
import { StoreType } from "@matrix-org/matrix-sdk-crypto-nodejs";
|
||||
import { read as configRead } from "./config";
|
||||
import { initializeSentry, patchMatrixClient } from "./utils";
|
||||
import {
|
||||
constructWebAPIs,
|
||||
makeDraupnirBotModeFromConfig,
|
||||
} from "./DraupnirBotMode";
|
||||
import { DraupnirBotModeToggle, constructWebAPIs } from "./DraupnirBotMode";
|
||||
import { Draupnir } from "./Draupnir";
|
||||
import { SafeMatrixEmitterWrapper } from "matrix-protection-suite-for-matrix-bot-sdk";
|
||||
import { DefaultEventDecoder, Task } from "matrix-protection-suite";
|
||||
@@ -104,12 +101,16 @@ void (async function () {
|
||||
eventDecoder
|
||||
)
|
||||
: undefined;
|
||||
bot = await makeDraupnirBotModeFromConfig(
|
||||
const toggle = await DraupnirBotModeToggle.create(
|
||||
client,
|
||||
new SafeMatrixEmitterWrapper(client, eventDecoder),
|
||||
config,
|
||||
store
|
||||
);
|
||||
|
||||
bot = (await toggle.switchToDraupnir()).expect(
|
||||
"Failed to initialize Draupnir"
|
||||
);
|
||||
apis = constructWebAPIs(bot);
|
||||
} catch (err) {
|
||||
console.error(
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
// SPDX-FileCopyrightText: 2024 Gnuxie <Gnuxie@protonmail.com>
|
||||
//
|
||||
// SPDX-License-Identifier: AFL-3.0
|
||||
|
||||
import { Result } from "@gnuxie/typescript-result";
|
||||
import { Draupnir } from "../Draupnir";
|
||||
import { SafeModeDraupnir } from "./DraupnirSafeMode";
|
||||
import { SafeModeCause } from "./SafeModeCause";
|
||||
|
||||
export interface SafeModeToggle {
|
||||
/**
|
||||
* Switch the bot to Draupnir mode.
|
||||
* We expect that the result represents the entire conversion.
|
||||
* We expect that the same matrix client is shared between the bots.
|
||||
* That means that by the command responds with ticks and crosses,
|
||||
* draupnir will be running or we will still be in safe mode.
|
||||
*/
|
||||
switchToDraupnir(): Promise<Result<Draupnir>>;
|
||||
switchToSafeMode(cause: SafeModeCause): Promise<Result<SafeModeDraupnir>>;
|
||||
}
|
||||
@@ -20,7 +20,7 @@ import { overrideRatelimitForUser, registerUser } from "./clientHelper";
|
||||
import { initializeSentry, patchMatrixClient } from "../../src/utils";
|
||||
import { IConfig } from "../../src/config";
|
||||
import { Draupnir } from "../../src/Draupnir";
|
||||
import { makeDraupnirBotModeFromConfig } from "../../src/DraupnirBotMode";
|
||||
import { DraupnirBotModeToggle } from "../../src/DraupnirBotMode";
|
||||
import {
|
||||
SafeMatrixEmitter,
|
||||
SafeMatrixEmitterWrapper,
|
||||
@@ -140,12 +140,15 @@ export async function makeMjolnir(
|
||||
await client.getUserId()
|
||||
);
|
||||
await ensureAliasedRoomExists(client, config.managementRoom);
|
||||
const mj = await makeDraupnirBotModeFromConfig(
|
||||
const toggle = await DraupnirBotModeToggle.create(
|
||||
client,
|
||||
new SafeMatrixEmitterWrapper(client, DefaultEventDecoder),
|
||||
config,
|
||||
backingStore
|
||||
);
|
||||
const mj = (await toggle.switchToDraupnir()).expect(
|
||||
"Could not create Draupnir"
|
||||
);
|
||||
globalClient = client;
|
||||
globalMjolnir = mj;
|
||||
globalSafeEmitter = new SafeMatrixEmitterWrapper(client, DefaultEventDecoder);
|
||||
|
||||
Reference in New Issue
Block a user