diff --git a/src/bot.ts b/src/bot.ts index d2e13e14883faa3d0abe19b3bc5dd1b4fff477b4..e482314ed6dfe148da99ccfc845ea6cc0f4c034b 100644 --- a/src/bot.ts +++ b/src/bot.ts @@ -126,6 +126,14 @@ export class DiscordBot { return this.roomHandler; } + get MxEventProcessor(): MatrixEventProcessor { + return this.mxEventProcessor; + } + + get Provisioner(): Provisioner { + return this.provisioner; + } + public GetIntentFromDiscordMember(member: Discord.GuildMember | Discord.User, webhookID?: string): Intent { if (webhookID) { // webhookID and user IDs are the same, they are unique, so no need to prefix _webhook_ diff --git a/src/discordas.ts b/src/discordas.ts index e395fef41d577bcc3e57c3062c768465bc9ca230..c4a33a34e6faf18412dd05f6aa7e6ebc9f89327b 100644 --- a/src/discordas.ts +++ b/src/discordas.ts @@ -166,11 +166,12 @@ async function run(port: number, fileConfig: DiscordBridgeConfig) { const discordbot = new DiscordBot(botUserId, config, bridge, store); const roomhandler = discordbot.RoomHandler; + const eventProcessor = discordbot.MxEventProcessor; try { callbacks.onAliasQueried = roomhandler.OnAliasQueried.bind(roomhandler); callbacks.onAliasQuery = roomhandler.OnAliasQuery.bind(roomhandler); - callbacks.onEvent = roomhandler.OnEvent.bind(roomhandler); + callbacks.onEvent = eventProcessor.OnEvent.bind(roomhandler); callbacks.thirdPartyLookup = async () => { return roomhandler.ThirdPartyLookup; }; diff --git a/src/matrixcommandhandler.ts b/src/matrixcommandhandler.ts new file mode 100644 index 0000000000000000000000000000000000000000..a116d6f5f127220f2cbb347e5a6906a024b4e1ce --- /dev/null +++ b/src/matrixcommandhandler.ts @@ -0,0 +1,225 @@ +import { DiscordBot } from "./bot"; +import { Log } from "./log"; +import { DiscordBridgeConfig } from "./config"; +import { Bridge, BridgeContext } from "matrix-appservice-bridge"; +import { IMatrixEvent } from "./matrixtypes"; +import { Provisioner } from "./provisioner"; +import { Util } from "./util"; +import * as Discord from "discord.js"; +const log = new Log("MatrixCommandHandler"); + +/* tslint:disable:no-magic-numbers */ +const PROVISIONING_DEFAULT_POWER_LEVEL = 50; +const PROVISIONING_DEFAULT_USER_POWER_LEVEL = 0; +const ROOM_CACHE_MAXAGE_MS = 15 * 60 * 1000; +/* tslint:enable:no-magic-numbers */ + +export class MatrixCommandHandler { + private botJoinedRooms: Set<string> = new Set(); // roomids + private botJoinedRoomsCacheUpdatedAt = 0; + private provisioner: Provisioner; + constructor( + private discord: DiscordBot, + private bridge: Bridge, + private config: DiscordBridgeConfig, + ) { + this.provisioner = this.discord.Provisioner; + } + + public async HandleInvite(event: IMatrixEvent) { + log.info(`Received invite for ${event.state_key} in room ${event.room_id}`); + if (event.state_key === this.discord.GetBotId()) { + log.info("Accepting invite for bridge bot"); + await this.bridge.getIntent().joinRoom(event.room_id); + this.botJoinedRooms.add(event.room_id); + } + } + + public async ProcessCommand(event: IMatrixEvent, context: BridgeContext) { + const intent = this.bridge.getIntent(); + if (!(await this.isBotInRoom(event.room_id))) { + log.warn(`Bot is not in ${event.room_id}. Ignoring command`); + return; + } + + if (!this.config.bridge.enableSelfServiceBridging) { + // We can do this here because the only commands we support are self-service bridging + return this.bridge.getIntent().sendMessage(event.room_id, { + body: "The owner of this bridge does not permit self-service bridging.", + msgtype: "m.notice", + }); + } + + // Check to make sure the user has permission to do anything in the room. We can do this here + // because the only commands we support are self-service commands (which therefore require some + // level of permissions) + const plEvent = await this.bridge.getIntent().getClient() + .getStateEvent(event.room_id, "m.room.power_levels", ""); + let userLevel = PROVISIONING_DEFAULT_USER_POWER_LEVEL; + let requiredLevel = PROVISIONING_DEFAULT_POWER_LEVEL; + if (plEvent && plEvent.state_default) { + requiredLevel = plEvent.state_default; + } + if (plEvent && plEvent.users_default) { + userLevel = plEvent.users_default; + } + if (plEvent && plEvent.users && plEvent.users[event.sender]) { + userLevel = plEvent.users[event.sender]; + } + + if (userLevel < requiredLevel) { + return this.bridge.getIntent().sendMessage(event.room_id, { + body: "You do not have the required power level in this room to create a bridge to a Discord channel.", + msgtype: "m.notice", + }); + } + + const {command, args} = Util.MsgToArgs(event.content!.body as string, "!discord"); + + if (command === "help" && args[0] === "bridge") { + const link = Util.GetBotLink(this.config); + // tslint:disable prefer-template + return this.bridge.getIntent().sendMessage(event.room_id, { + body: "How to bridge a Discord guild:\n" + + "1. Invite the bot to your Discord guild using this link: " + link + "\n" + + "2. Invite me to the matrix room you'd like to bridge\n" + + "3. Open the Discord channel you'd like to bridge in a web browser\n" + + "4. In the matrix room, send the message `!discord bridge <guild id> <channel id>` " + + "(without the backticks)\n" + + " Note: The Guild ID and Channel ID can be retrieved from the URL in your web browser.\n" + + " The URL is formatted as https://discordapp.com/channels/GUILD_ID/CHANNEL_ID\n" + + "5. Enjoy your new bridge!", + msgtype: "m.notice", + }); + // tslint:enable prefer-template + } else if (command === "bridge") { + if (context.rooms.remote) { + return this.bridge.getIntent().sendMessage(event.room_id, { + body: "This room is already bridged to a Discord guild.", + msgtype: "m.notice", + }); + } + + const MAXARGS = 2; + if (args.length > MAXARGS || args.length < 1) { + return this.bridge.getIntent().sendMessage(event.room_id, { + body: "Invalid syntax. For more information try !discord help bridge", + msgtype: "m.notice", + }); + } + + let guildId: string; + let channelId: string; + + const AMOUNT_OF_IDS_DISCORD_IDENTIFIES_ROOMS_BY = 2; + + if (args.length === AMOUNT_OF_IDS_DISCORD_IDENTIFIES_ROOMS_BY) { // "x y" syntax + guildId = args[0]; + channelId = args[1]; + } else if (args.length === 1 && args[0].includes("/")) { // "x/y" syntax + const split = args[0].split("/"); + guildId = split[0]; + channelId = split[1]; + } else { + return this.bridge.getIntent().sendMessage(event.room_id, { + body: "Invalid syntax: See `!discord help`", + formatted_body: "Invalid syntax: See <code>!discord help</code>", + msgtype: "m.notice", + }); + } + + try { + const discordResult = await this.discord.LookupRoom(guildId, channelId); + const channel = discordResult.channel as Discord.TextChannel; + + log.info(`Bridging matrix room ${event.room_id} to ${guildId}/${channelId}`); + this.bridge.getIntent().sendMessage(event.room_id, { + body: "I'm asking permission from the guild administrators to make this bridge.", + msgtype: "m.notice", + }); + + await this.provisioner.AskBridgePermission(channel, event.sender); + await this.provisioner.BridgeMatrixRoom(channel, event.room_id); + return this.bridge.getIntent().sendMessage(event.room_id, { + body: "I have bridged this room to your channel", + msgtype: "m.notice", + }); + } catch (err) { + if (err.message === "Timed out waiting for a response from the Discord owners" + || err.message === "The bridge has been declined by the Discord guild") { + return this.bridge.getIntent().sendMessage(event.room_id, { + body: err.message, + msgtype: "m.notice", + }); + } + + log.error(`Error bridging ${event.room_id} to ${guildId}/${channelId}`); + log.error(err); + return this.bridge.getIntent().sendMessage(event.room_id, { + body: "There was a problem bridging that channel - has the guild owner approved the bridge?", + msgtype: "m.notice", + }); + } + } else if (command === "unbridge") { + const remoteRoom = context.rooms.remote; + + if (!remoteRoom) { + return this.bridge.getIntent().sendMessage(event.room_id, { + body: "This room is not bridged.", + msgtype: "m.notice", + }); + } + + if (!remoteRoom.data.plumbed) { + return this.bridge.getIntent().sendMessage(event.room_id, { + body: "This room cannot be unbridged.", + msgtype: "m.notice", + }); + } + + try { + await this.provisioner.UnbridgeRoom(remoteRoom); + return this.bridge.getIntent().sendMessage(event.room_id, { + body: "This room has been unbridged", + msgtype: "m.notice", + }); + } catch (err) { + log.error("Error while unbridging room " + event.room_id); + log.error(err); + return this.bridge.getIntent().sendMessage(event.room_id, { + body: "There was an error unbridging this room. " + + "Please try again later or contact the bridge operator.", + msgtype: "m.notice", + }); + } + } else if (command === "help") { + // Unknown command or no command given to get help on, so we'll just give them the help + // tslint:disable prefer-template + return this.bridge.getIntent().sendMessage(event.room_id, { + body: "Available commands:\n" + + "!discord bridge <guild id> <channel id> - Bridges this room to a Discord channel\n" + + "!discord unbridge - Unbridges a Discord channel from this room\n" + + "!discord help <command> - Help menu for another command. Eg: !discord help bridge\n", + msgtype: "m.notice", + }); + // tslint:enable prefer-template + } + } + + private async isBotInRoom(roomId: string): Promise<boolean> { + // Update the room cache, if not done already. + if (Date.now () - this.botJoinedRoomsCacheUpdatedAt > ROOM_CACHE_MAXAGE_MS) { + log.verbose("Updating room cache for bot..."); + try { + log.verbose("Got new room cache for bot"); + this.botJoinedRoomsCacheUpdatedAt = Date.now(); + const rooms = (await this.bridge.getBot().getJoinedRooms()) as string[]; + this.botJoinedRooms = new Set(rooms); + } catch (e) { + log.error("Failed to get room cache for bot, ", e); + return false; + } + } + return this.botJoinedRooms.has(roomId); + } +} diff --git a/src/matrixeventprocessor.ts b/src/matrixeventprocessor.ts index 2b23992eff3ce72f73f4ca0726cde6bbc7d3e921..dfab2e6488d995112de5a8008111d42eea42f609 100644 --- a/src/matrixeventprocessor.ts +++ b/src/matrixeventprocessor.ts @@ -21,10 +21,11 @@ import * as escapeStringRegexp from "escape-string-regexp"; import { Util } from "./util"; import * as path from "path"; import * as mime from "mime"; -import { MatrixUser, Bridge } from "matrix-appservice-bridge"; +import { MatrixUser, Bridge, BridgeContext } from "matrix-appservice-bridge"; import { Client as MatrixClient } from "matrix-js-sdk"; import { IMatrixEvent, IMatrixEventContent, IMatrixMessage } from "./matrixtypes"; import { MatrixMessageProcessor, IMatrixMessageProcessorParams } from "./matrixmessageprocessor"; +import { MatrixCommandHandler } from "./matrixcommandhandler"; import { Log } from "./log"; const log = new Log("MatrixEventProcessor"); @@ -34,6 +35,8 @@ const MIN_NAME_LENGTH = 2; const MAX_NAME_LENGTH = 32; const DISCORD_AVATAR_WIDTH = 128; const DISCORD_AVATAR_HEIGHT = 128; +const ROOM_NAME_PARTS = 2; +const AGE_LIMIT = 900000; // 15 * 60 * 1000 export class MatrixEventProcessorOpts { constructor( @@ -55,12 +58,101 @@ export class MatrixEventProcessor { private bridge: Bridge; private discord: DiscordBot; private matrixMsgProcessor: MatrixMessageProcessor; + private mxCommandHandler: MatrixCommandHandler; constructor(opts: MatrixEventProcessorOpts) { this.config = opts.config; this.bridge = opts.bridge; this.discord = opts.discord; this.matrixMsgProcessor = new MatrixMessageProcessor(this.discord); + this.mxCommandHandler = new MatrixCommandHandler(this.discord, this.bridge, this.config); + } + + public async OnEvent(request, context: BridgeContext): Promise<void> { + const event = request.getData() as IMatrixEvent; + if (event.unsigned.age > AGE_LIMIT) { + log.warn(`Skipping event due to age ${event.unsigned.age} > ${AGE_LIMIT}`); + return; + } + if ( + event.type === "m.room.member" && + event.content!.membership === "invite" && + event.state_key === this.discord.GetBotId() + ) { + await this.mxCommandHandler.HandleInvite(event); + return; + } else if (event.type === "m.room.member" && this.bridge.getBot().isRemoteUser(event.state_key)) { + if (["leave", "ban"].includes(event.content!.membership!) && event.sender !== event.state_key) { + // Kick/Ban handling + let prevMembership = ""; + if (event.content!.membership === "leave") { + const intent = this.bridge.getIntent(); + prevMembership = (await intent.getEvent(event.room_id, event.replaces_state)).content.membership; + } + await this.discord.HandleMatrixKickBan( + event.room_id, + event.state_key, + event.sender, + event.content!.membership as "leave"|"ban", + prevMembership, + event.content!.reason, + ); + } + return; + } else if (["m.room.member", "m.room.name", "m.room.topic"].includes(event.type)) { + await this.discord.ProcessMatrixStateEvent(event); + return; + } else if (event.type === "m.room.redaction" && context.rooms.remote) { + await this.discord.ProcessMatrixRedact(event); + return; + } else if (event.type === "m.room.message" || event.type === "m.sticker") { + log.verbose(`Got ${event.type} event`); + const isBotCommand = event.type === "m.room.message" && + event.content!.body && + event.content!.body!.startsWith("!discord"); + if (isBotCommand) { + await this.mxCommandHandler.ProcessCommand(event, context); + return; + } else if (context.rooms.remote) { + const srvChanPair = context.rooms.remote.roomId.substr("_discord".length).split("_", ROOM_NAME_PARTS); + try { + await this.discord.ProcessMatrixMsgEvent(event, srvChanPair[0], srvChanPair[1]); + return; + } catch (err) { + log.warn("There was an error sending a matrix event", err); + return; + } + } + } else if (event.type === "m.room.encryption" && context.rooms.remote) { + try { + await this.HandleEncryptionWarning(event.room_id); + return; + } catch (err) { + throw new Error(`Failed to handle encrypted room, ${err}`); + } + } else { + log.verbose("Got non m.room.message event"); + } + log.verbose("Event not processed by bridge"); + } + + public async HandleEncryptionWarning(roomId: string): Promise<void> { + const intent = this.bridge.getIntent(); + log.info(`User has turned on encryption in ${roomId}, so leaving.`); + /* N.B 'status' is not specced but https://github.com/matrix-org/matrix-doc/pull/828 + has been open for over a year with no resolution. */ + const sendPromise = intent.sendMessage(roomId, { + body: "You have turned on encryption in this room, so the service will not bridge any new messages.", + msgtype: "m.notice", + status: "critical", + }); + const channel = await this.discord.GetChannelFromRoomId(roomId); + await (channel as Discord.TextChannel).send( + "Someone on Matrix has turned on encryption in this room, so the service will not bridge any new messages", + ); + await sendPromise; + await intent.leave(roomId); + await this.bridge.getRoomStore().removeEntriesByMatrixRoomId(roomId); } public StateEventToMessage(event: IMatrixEvent, channel: Discord.TextChannel): string | undefined { diff --git a/src/matrixroomhandler.ts b/src/matrixroomhandler.ts index 109a5f4b2f7a9b1d6b2796c4dbb99abb2dd7600b..3a38e7a6bdda11648ce0d65c18bda8186bec10f2 100644 --- a/src/matrixroomhandler.ts +++ b/src/matrixroomhandler.ts @@ -22,7 +22,6 @@ import { thirdPartyProtocolResult, thirdPartyUserResult, thirdPartyLocationResult, - BridgeContext, ProvisionedRoom, Intent, } from "matrix-appservice-bridge"; @@ -41,7 +40,6 @@ const ICON_URL = "https://matrix.org/_matrix/media/r0/download/matrix.org/mlxoES /* tslint:disable:no-magic-numbers */ const HTTP_UNSUPPORTED = 501; const ROOM_NAME_PARTS = 2; -const AGE_LIMIT = 900000; // 15 * 60 * 1000 const PROVISIONING_DEFAULT_POWER_LEVEL = 50; const PROVISIONING_DEFAULT_USER_POWER_LEVEL = 0; const USERSYNC_STATE_DELAY_MS = 5000; @@ -139,271 +137,6 @@ export class MatrixRoomHandler { await Promise.all(promiseList); } - public async OnEvent(request, context: BridgeContext): Promise<void> { - const event = request.getData() as IMatrixEvent; - if (event.unsigned.age > AGE_LIMIT) { - log.warn(`Skipping event due to age ${event.unsigned.age} > ${AGE_LIMIT}`); - return; - } - if (event.type === "m.room.member" && event.content!.membership === "invite") { - await this.HandleInvite(event); - return; - } else if (event.type === "m.room.member" && this.bridge.getBot().isRemoteUser(event.state_key)) { - if (["leave", "ban"].includes(event.content!.membership!) && event.sender !== event.state_key) { - // Kick/Ban handling - let prevMembership = ""; - if (event.content!.membership === "leave") { - const intent = this.bridge.getIntent(); - prevMembership = (await intent.getEvent(event.room_id, event.replaces_state)).content.membership; - } - await this.discord.HandleMatrixKickBan( - event.room_id, - event.state_key, - event.sender, - event.content!.membership as "leave"|"ban", - prevMembership, - event.content!.reason, - ); - } - return; - } else if (["m.room.member", "m.room.name", "m.room.topic"].includes(event.type)) { - await this.discord.ProcessMatrixStateEvent(event); - return; - } else if (event.type === "m.room.redaction" && context.rooms.remote) { - await this.discord.ProcessMatrixRedact(event); - return; - } else if (event.type === "m.room.message" || event.type === "m.sticker") { - log.verbose(`Got ${event.type} event`); - const isBotCommand = event.type === "m.room.message" && - event.content!.body && - event.content!.body!.startsWith("!discord"); - if (isBotCommand) { - await this.ProcessCommand(event, context); - return; - } else if (context.rooms.remote) { - const srvChanPair = context.rooms.remote.roomId.substr("_discord".length).split("_", ROOM_NAME_PARTS); - try { - await this.discord.ProcessMatrixMsgEvent(event, srvChanPair[0], srvChanPair[1]); - return; - } catch (err) { - log.warn("There was an error sending a matrix event", err); - return; - } - } - } else if (event.type === "m.room.encryption" && context.rooms.remote) { - try { - await this.HandleEncryptionWarning(event.room_id); - return; - } catch (err) { - throw new Error(`Failed to handle encrypted room, ${err}`); - } - } else { - log.verbose("Got non m.room.message event"); - } - log.verbose("Event not processed by bridge"); - } - - public async HandleEncryptionWarning(roomId: string): Promise<void> { - const intent = this.bridge.getIntent(); - log.info(`User has turned on encryption in ${roomId}, so leaving.`); - /* N.B 'status' is not specced but https://github.com/matrix-org/matrix-doc/pull/828 - has been open for over a year with no resolution. */ - const sendPromise = intent.sendMessage(roomId, { - body: "You have turned on encryption in this room, so the service will not bridge any new messages.", - msgtype: "m.notice", - status: "critical", - }); - const channel = await this.discord.GetChannelFromRoomId(roomId); - await (channel as Discord.TextChannel).send( - "Someone on Matrix has turned on encryption in this room, so the service will not bridge any new messages", - ); - await sendPromise; - await intent.leave(roomId); - await this.roomStore.removeEntriesByMatrixRoomId(roomId); - } - - public async HandleInvite(event: IMatrixEvent) { - log.info(`Received invite for ${event.state_key} in room ${event.room_id}`); - if (event.state_key === this.botUserId) { - log.info("Accepting invite for bridge bot"); - await this.joinRoom(this.bridge.getIntent(), event.room_id); - this.botJoinedRooms.add(event.room_id); - } else { - await this.discord.ProcessMatrixStateEvent(event); - } - } - - public async ProcessCommand(event: IMatrixEvent, context: BridgeContext) { - const intent = this.bridge.getIntent(); - if (!(await this.isBotInRoom(event.room_id))) { - log.warn(`Bot is not in ${event.room_id}. Ignoring command`); - return; - } - - if (!this.config.bridge.enableSelfServiceBridging) { - // We can do this here because the only commands we support are self-service bridging - return this.bridge.getIntent().sendMessage(event.room_id, { - body: "The owner of this bridge does not permit self-service bridging.", - msgtype: "m.notice", - }); - } - - // Check to make sure the user has permission to do anything in the room. We can do this here - // because the only commands we support are self-service commands (which therefore require some - // level of permissions) - const plEvent = await this.bridge.getIntent().getClient() - .getStateEvent(event.room_id, "m.room.power_levels", ""); - let userLevel = PROVISIONING_DEFAULT_USER_POWER_LEVEL; - let requiredLevel = PROVISIONING_DEFAULT_POWER_LEVEL; - if (plEvent && plEvent.state_default) { - requiredLevel = plEvent.state_default; - } - if (plEvent && plEvent.users_default) { - userLevel = plEvent.users_default; - } - if (plEvent && plEvent.users && plEvent.users[event.sender]) { - userLevel = plEvent.users[event.sender]; - } - - if (userLevel < requiredLevel) { - return this.bridge.getIntent().sendMessage(event.room_id, { - body: "You do not have the required power level in this room to create a bridge to a Discord channel.", - msgtype: "m.notice", - }); - } - - const {command, args} = Util.MsgToArgs(event.content!.body as string, "!discord"); - - if (command === "help" && args[0] === "bridge") { - const link = Util.GetBotLink(this.config); - // tslint:disable prefer-template - return this.bridge.getIntent().sendMessage(event.room_id, { - body: "How to bridge a Discord guild:\n" + - "1. Invite the bot to your Discord guild using this link: " + link + "\n" + - "2. Invite me to the matrix room you'd like to bridge\n" + - "3. Open the Discord channel you'd like to bridge in a web browser\n" + - "4. In the matrix room, send the message `!discord bridge <guild id> <channel id>` " + - "(without the backticks)\n" + - " Note: The Guild ID and Channel ID can be retrieved from the URL in your web browser.\n" + - " The URL is formatted as https://discordapp.com/channels/GUILD_ID/CHANNEL_ID\n" + - "5. Enjoy your new bridge!", - msgtype: "m.notice", - }); - // tslint:enable prefer-template - } else if (command === "bridge") { - if (context.rooms.remote) { - return this.bridge.getIntent().sendMessage(event.room_id, { - body: "This room is already bridged to a Discord guild.", - msgtype: "m.notice", - }); - } - - const MAXARGS = 2; - if (args.length > MAXARGS || args.length < 1) { - return this.bridge.getIntent().sendMessage(event.room_id, { - body: "Invalid syntax. For more information try !discord help bridge", - msgtype: "m.notice", - }); - } - - let guildId: string; - let channelId: string; - - const AMOUNT_OF_IDS_DISCORD_IDENTIFIES_ROOMS_BY = 2; - - if (args.length === AMOUNT_OF_IDS_DISCORD_IDENTIFIES_ROOMS_BY) { // "x y" syntax - guildId = args[0]; - channelId = args[1]; - } else if (args.length === 1 && args[0].includes("/")) { // "x/y" syntax - const split = args[0].split("/"); - guildId = split[0]; - channelId = split[1]; - } else { - return this.bridge.getIntent().sendMessage(event.room_id, { - body: "Invalid syntax: See `!discord help`", - formatted_body: "Invalid syntax: See <code>!discord help</code>", - msgtype: "m.notice", - }); - } - - try { - const discordResult = await this.discord.LookupRoom(guildId, channelId); - const channel = discordResult.channel as Discord.TextChannel; - - log.info(`Bridging matrix room ${event.room_id} to ${guildId}/${channelId}`); - this.bridge.getIntent().sendMessage(event.room_id, { - body: "I'm asking permission from the guild administrators to make this bridge.", - msgtype: "m.notice", - }); - - await this.provisioner.AskBridgePermission(channel, event.sender); - await this.provisioner.BridgeMatrixRoom(channel, event.room_id); - return this.bridge.getIntent().sendMessage(event.room_id, { - body: "I have bridged this room to your channel", - msgtype: "m.notice", - }); - } catch (err) { - if (err.message === "Timed out waiting for a response from the Discord owners" - || err.message === "The bridge has been declined by the Discord guild") { - return this.bridge.getIntent().sendMessage(event.room_id, { - body: err.message, - msgtype: "m.notice", - }); - } - - log.error(`Error bridging ${event.room_id} to ${guildId}/${channelId}`); - log.error(err); - return this.bridge.getIntent().sendMessage(event.room_id, { - body: "There was a problem bridging that channel - has the guild owner approved the bridge?", - msgtype: "m.notice", - }); - } - } else if (command === "unbridge") { - const remoteRoom = context.rooms.remote; - - if (!remoteRoom) { - return this.bridge.getIntent().sendMessage(event.room_id, { - body: "This room is not bridged.", - msgtype: "m.notice", - }); - } - - if (!remoteRoom.data.plumbed) { - return this.bridge.getIntent().sendMessage(event.room_id, { - body: "This room cannot be unbridged.", - msgtype: "m.notice", - }); - } - - try { - await this.provisioner.UnbridgeRoom(remoteRoom); - return this.bridge.getIntent().sendMessage(event.room_id, { - body: "This room has been unbridged", - msgtype: "m.notice", - }); - } catch (err) { - log.error("Error while unbridging room " + event.room_id); - log.error(err); - return this.bridge.getIntent().sendMessage(event.room_id, { - body: "There was an error unbridging this room. " + - "Please try again later or contact the bridge operator.", - msgtype: "m.notice", - }); - } - } else if (command === "help") { - // Unknown command or no command given to get help on, so we'll just give them the help - // tslint:disable prefer-template - return this.bridge.getIntent().sendMessage(event.room_id, { - body: "Available commands:\n" + - "!discord bridge <guild id> <channel id> - Bridges this room to a Discord channel\n" + - "!discord unbridge - Unbridges a Discord channel from this room\n" + - "!discord help <command> - Help menu for another command. Eg: !discord help bridge\n", - msgtype: "m.notice", - }); - // tslint:enable prefer-template - } - } - public async OnAliasQuery(alias: string, aliasLocalpart: string): Promise<ProvisionedRoom> { log.info("Got request for #", aliasLocalpart); const srvChanPair = aliasLocalpart.substr("_discord_".length).split("_", ROOM_NAME_PARTS); @@ -663,21 +396,4 @@ export class MatrixRoomHandler { creationOpts, } as ProvisionedRoom; } - - private async isBotInRoom(roomId: string): Promise<boolean> { - // Update the room cache, if not done already. - if (Date.now () - this.botJoinedRoomsCacheUpdatedAt > ROOM_CACHE_MAXAGE_MS) { - log.verbose("Updating room cache for bot..."); - try { - log.verbose("Got new room cache for bot"); - this.botJoinedRoomsCacheUpdatedAt = Date.now(); - const rooms = (await this.bridge.getBot().getJoinedRooms()) as string[]; - this.botJoinedRooms = new Set(rooms); - } catch (e) { - log.error("Failed to get room cache for bot, ", e); - return false; - } - } - return this.botJoinedRooms.has(roomId); - } }