diff --git a/config/config.sample.yaml b/config/config.sample.yaml index edb0b2e6994a21f6a89afdd6996173cfd7512a19..090b5998a5684d50189d87df721572613873abe9 100644 --- a/config/config.sample.yaml +++ b/config/config.sample.yaml @@ -38,6 +38,8 @@ bridge: disableInviteNotifications: false # Auto-determine the language of code blocks (this can be CPU-intensive) determineCodeLanguage: false + # MXID of an admin user that will be PMd if the bridge experiences problems. Optional + adminMxid: '@admin:localhost' # Authentication configuration for the discord bot. auth: # This MUST be a string (wrapped in quotes) @@ -113,4 +115,4 @@ ghosts: metrics: enable: false port: 9001 - host: "127.0.0.1" \ No newline at end of file + host: "127.0.0.1" diff --git a/src/config.ts b/src/config.ts index 216d531eaa23879c7c74a0d2bfe4e3376624f2af..772e73092a66e77f324e375eb592b1139eccfdb4 100644 --- a/src/config.ts +++ b/src/config.ts @@ -100,6 +100,7 @@ class DiscordBridgeConfigBridge { public determineCodeLanguage: boolean = false; public activityTracker: UserActivityTrackerConfig = UserActivityTrackerConfig.DEFAULT; public userLimit: number|null = null; + public adminMxid: string|null = null; } export class DiscordBridgeConfigDatabase { diff --git a/src/discordas.ts b/src/discordas.ts index 182b5cda54906345b24e78652148f82c1fc8d978..49c65c1a4c5e638d810247a4b6ae99d57efc599c 100644 --- a/src/discordas.ts +++ b/src/discordas.ts @@ -13,7 +13,7 @@ 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 { Appservice, IAppserviceRegistration, LogService } from "matrix-bot-sdk"; +import { Appservice, IAppserviceRegistration, LogService, MatrixClient } from "matrix-bot-sdk"; import * as yaml from "js-yaml"; import * as fs from "fs"; import { DiscordBridgeConfig } from "./config"; @@ -30,6 +30,8 @@ import { Response } from "express"; const log = new Log("DiscordAS"); +const ADMIN_ROOMS_KEY = 'uk.half-shot.discord.admin-rooms'; + const commandOptions = [ { name: "config", alias: "c", type: String }, { name: "url", alias: "u", type: String }, @@ -215,7 +217,7 @@ async function run(): Promise<void> { roomhandler.bindThirdparty(); try { - await startDiscordBot(discordbot); + await startDiscordBot(discordbot, appservice.botClient, config.bridge.adminMxid); log.info("Discordbot started successfully"); } catch (err) { log.error(err); @@ -228,16 +230,49 @@ async function run(): Promise<void> { } -async function startDiscordBot(discordbot: DiscordBot, falloffSeconds = 5) { +interface AdminRooms { + [mxid: string]: string; +} + +async function notifyBridgeAdmin(client: MatrixClient, adminMxid: string, message: string) { + log.verbose(`Looking up admin room for ${adminMxid}`); + let adminRooms = await client.getAccountData<AdminRooms>(ADMIN_ROOMS_KEY).catch(() => null) || {}; + if (!adminRooms[adminMxid]) { + log.verbose(`Creating an admin room for ${adminMxid}`); + adminRooms[adminMxid] = await client.createRoom(); + await client.inviteUser(adminMxid, adminRooms[adminMxid]); + await client.setAccountData(ADMIN_ROOMS_KEY, adminRooms); + } + await client.sendText(adminRooms[adminMxid], message) +} + +let adminNotified = false; + +async function startDiscordBot( + discordbot: DiscordBot, + client: MatrixClient, + adminMxid: string|null, + falloffSeconds = 5 +) { try { await discordbot.init(); await discordbot.run(); } catch (err) { + if (err.code === 'TOKEN_INVALID' && adminMxid && !adminNotified) { + await notifyBridgeAdmin(client, adminMxid, `Your Discord bot token seems to be invalid, and the bridge cannot function. Please update it in your bridge settings and restart the bridge`); + adminNotified = true; + } + // no more than 5 minutes const newFalloffSeconds = Math.min(falloffSeconds * 2, 5 * 60); log.error(`Failed do start Discordbot: ${err.code}. Will try again in ${newFalloffSeconds} seconds`); await new Promise((r, _) => setTimeout(r, newFalloffSeconds * 1000)); - return startDiscordBot(discordbot, newFalloffSeconds); + return startDiscordBot(discordbot, client, adminMxid, newFalloffSeconds); + } + + if (adminMxid && adminNotified) { + await notifyBridgeAdmin(client, adminMxid, `The token situation is now resolved and the bridge is running correctly`); + adminNotified = false; } }