diff --git a/src/DraupnirBotMode.ts b/src/DraupnirBotMode.ts index 654c4617..31a9ad50 100644 --- a/src/DraupnirBotMode.ts +++ b/src/DraupnirBotMode.ts @@ -39,8 +39,9 @@ import { StringUserID, MatrixRoomID, } from "@the-draupnir-project/matrix-basic-types"; -import { Result, isError } from "@gnuxie/typescript-result"; +import { Err, Ok, Result, isError } from "@gnuxie/typescript-result"; import { + DraupnirRestartError, SafeModeToggle, SafeModeToggleOptions, } from "./safemode/SafeModeToggle"; @@ -70,6 +71,10 @@ interface BotModeTogle extends SafeModeToggle { startFromScratch( options?: SafeModeToggleOptions ): Promise>; + maybeRecoverWithSafeMode( + error: ResultError, + options?: SafeModeToggleOptions + ): Promise>; } export class DraupnirBotModeToggle implements BotModeTogle { @@ -168,12 +173,13 @@ export class DraupnirBotModeToggle implements BotModeTogle { } public async switchToDraupnir( options?: SafeModeToggleOptions - ): Promise> { + ): Promise> { if (this.draupnir !== null) { return ResultError.Result( `There is a draupnir for ${this.clientUserID} already running` ); } + this.stopSafeModeDraupnir(); const draupnirResult = await this.draupnirFactory.makeDraupnir( this.clientUserID, this.managementRoom, @@ -181,7 +187,18 @@ export class DraupnirBotModeToggle implements BotModeTogle { this ); if (isError(draupnirResult)) { - return draupnirResult; + const safeModeResult = await this.maybeRecoverWithSafeMode( + draupnirResult.error, + options + ); + if (isError(safeModeResult)) { + return safeModeResult; + } else { + return DraupnirRestartError.Result( + "Draupnir failed to start, switching to safe mode.", + { safeModeDraupnir: safeModeResult.ok } + ); + } } this.draupnir = draupnirResult.ok; this.draupnir.start(); @@ -203,7 +220,6 @@ export class DraupnirBotModeToggle implements BotModeTogle { } } } - this.stopSafeModeDraupnir(); return draupnirResult; } public async switchToSafeMode( @@ -239,18 +255,8 @@ export class DraupnirBotModeToggle implements BotModeTogle { ): Promise> { const draupnirResult = await this.switchToDraupnir(options ?? {}); if (isError(draupnirResult)) { - if (this.config.safeMode?.bootIntoOnStartupFailure) { - log.error( - "Failed to start draupnir, switching to safe mode as configured", - draupnirResult.error - ); - return await this.switchToSafeMode( - { - reason: SafeModeReason.InitializationError, - error: draupnirResult.error, - }, - options ?? {} - ); + if (draupnirResult.error instanceof DraupnirRestartError) { + return Ok(draupnirResult.error.safeModeDraupnir); } else { return draupnirResult; } @@ -258,6 +264,27 @@ export class DraupnirBotModeToggle implements BotModeTogle { return draupnirResult; } + public async maybeRecoverWithSafeMode( + error: ResultError, + options?: SafeModeToggleOptions | undefined + ): Promise> { + if (this.config.safeMode?.bootIntoOnStartupFailure) { + log.error( + "Failed to start draupnir, switching to safe mode as configured", + error + ); + return await this.switchToSafeMode( + { + reason: SafeModeReason.InitializationError, + error: error, + }, + options ?? {} + ); + } else { + return Err(error); + } + } + public async encryptionInitialized(): Promise { if (this.draupnir !== null) { try { diff --git a/src/safemode/SafeModeToggle.ts b/src/safemode/SafeModeToggle.ts index ddc831fc..f5913bbd 100644 --- a/src/safemode/SafeModeToggle.ts +++ b/src/safemode/SafeModeToggle.ts @@ -2,13 +2,29 @@ // // SPDX-License-Identifier: AFL-3.0 -import { Result } from "@gnuxie/typescript-result"; +import { Err, Result, ResultError } from "@gnuxie/typescript-result"; import { Draupnir } from "../Draupnir"; import { SafeModeDraupnir } from "./DraupnirSafeMode"; import { SafeModeCause } from "./SafeModeCause"; export type SafeModeToggleOptions = { sendStatusOnStart?: boolean }; +export class DraupnirRestartError extends ResultError { + constructor( + message: string, + public readonly safeModeDraupnir: SafeModeDraupnir + ) { + super(message); + } + + public static Result( + message: string, + options: { safeModeDraupnir: SafeModeDraupnir } + ): Result { + return Err(new DraupnirRestartError(message, options.safeModeDraupnir)); + } +} + export interface SafeModeToggle { /** * Switch the bot to Draupnir mode. @@ -17,7 +33,9 @@ export interface SafeModeToggle { * That means that by the command responds with ticks and crosses, * draupnir will be running or we will still be in safe mode. */ - switchToDraupnir(options?: SafeModeToggleOptions): Promise>; + switchToDraupnir( + options?: SafeModeToggleOptions + ): Promise>; switchToSafeMode( cause: SafeModeCause, options?: SafeModeToggleOptions