diff --git a/src/channelsyncroniser.ts b/src/channelsyncroniser.ts index c948f35da71df85004efa18ee4fbb802d0014d85..e2aa65ec7dc6a7b8323e186c268fd9ea4773a821 100644 --- a/src/channelsyncroniser.ts +++ b/src/channelsyncroniser.ts @@ -17,7 +17,7 @@ limitations under the License. import * as Discord from "discord.js"; import { DiscordBot } from "./bot"; import { Util } from "./util"; -import { DiscordBridgeConfig } from "./config"; +import { DiscordBridgeConfig, DiscordBridgeConfigChannelDeleteOptions } from "./config"; import { Bridge } from "matrix-appservice-bridge"; import { Log } from "./log"; import { DbRoomStore, IRoomStoreEntry } from "./db/roomstore"; @@ -105,6 +105,20 @@ export class ChannelSyncroniser { } } + public async OnUnbridge(channel: Discord.Channel, roomId: string) { + try { + const entry = (await this.roomStore.getEntriesByMatrixId(roomId))[0]; + const opts = new DiscordBridgeConfigChannelDeleteOptions(); + opts.namePrefix = null; + opts.topicPrefix = null; + opts.ghostsLeave = true; + await this.handleChannelDeletionForRoom(channel as Discord.TextChannel, roomId, entry); + log.info(`Channel ${channel.id} has been unbridged.`); + } catch (e) { + log.error(`Failed to unbridge channel from room: ${e}`); + } + } + public async OnDelete(channel: Discord.Channel) { if (channel.type !== "text") { log.info(`Channel ${channel.id} was deleted but isn't a text channel, so ignoring.`); @@ -269,22 +283,24 @@ export class ChannelSyncroniser { private async handleChannelDeletionForRoom( channel: Discord.TextChannel, roomId: string, - entry: IRoomStoreEntry): Promise<void> { + entry: IRoomStoreEntry, + overrideOptions?: DiscordBridgeConfigChannelDeleteOptions): Promise<void> { log.info(`Deleting ${channel.id} from ${roomId}.`); const intent = await this.bridge.getIntent(); - const options = this.config.channel.deleteOptions; + const options = overrideOptions || this.config.channel.deleteOptions; const plumbed = entry.remote!.get("plumbed"); + // tslint:disable-next-line: no-any await this.roomStore.upsertEntry(entry); if (options.ghostsLeave) { for (const member of channel.members.array()) { - try { - const mIntent = await this.bot.GetIntentFromDiscordMember(member); - mIntent.leave(roomId); - log.info(`${member.id} left ${roomId}.`); - } catch (e) { - log.warn(`Failed to make ${member.id} leave `); - } + const mIntent = await this.bot.GetIntentFromDiscordMember(member); + // Not awaiting this because we want to do this in the background. + mIntent.leave(roomId).then(() => { + log.verbose(`${member.id} left ${roomId}.`); + }).catch(() => { + log.warn(`Failed to make ${member.id} leave.`); + }); } } if (options.namePrefix) { @@ -347,7 +363,6 @@ export class ChannelSyncroniser { } } } - // Unlist // Remove entry await this.roomStore.removeEntriesByMatrixRoomId(roomId); diff --git a/src/config.ts b/src/config.ts index 637bc555fb28450a2b2feccb5eb584eaa2c114fe..166604317accbb842d349afe1ca26eb5c4500247 100644 --- a/src/config.ts +++ b/src/config.ts @@ -86,7 +86,7 @@ class DiscordBridgeConfigChannel { public deleteOptions = new DiscordBridgeConfigChannelDeleteOptions(); } -class DiscordBridgeConfigChannelDeleteOptions { +export class DiscordBridgeConfigChannelDeleteOptions { public namePrefix: string | null = null; public topicPrefix: string | null = null; public disableMessaging: boolean = false; diff --git a/src/discordcommandhandler.ts b/src/discordcommandhandler.ts index 99dfc6269b6d475072c8e11891845557caa162ef..04401b423968f1d0c5ee49f5b5c965af48c33fb0 100644 --- a/src/discordcommandhandler.ts +++ b/src/discordcommandhandler.ts @@ -20,7 +20,7 @@ import { Util, ICommandActions, ICommandParameters, CommandPermissonCheck } from import { Bridge } from "matrix-appservice-bridge"; import { Log } from "./log"; -const log = new Log("MatrixCommandHandler"); +const log = new Log("DiscordCommandHandler"); export class DiscordCommandHandler { constructor( diff --git a/src/matrixcommandhandler.ts b/src/matrixcommandhandler.ts index c91ccbc6fb41f5f57d41657a4db5ad08a96f8e6f..d289b7a74be8338bd04d23d602b98c230e8d4b7e 100644 --- a/src/matrixcommandhandler.ts +++ b/src/matrixcommandhandler.ts @@ -134,7 +134,7 @@ export class MatrixCommandHandler { remoteRoom.data.discord_channel, ); try { - await this.provisioner.UnbridgeChannel(res.channel); + await this.provisioner.UnbridgeChannel(res.channel, event.room_id); return "This room has been unbridged"; } catch (err) { log.error("Error while unbridging room " + event.room_id); diff --git a/src/provisioner.ts b/src/provisioner.ts index 0b5ee7420889e2a93ceed49ce730ba5bf6c343e2..4f92ca7ab2c540d9a9777683a72d4ac559efd83e 100644 --- a/src/provisioner.ts +++ b/src/provisioner.ts @@ -17,9 +17,12 @@ limitations under the License. import * as Discord from "discord.js"; import { DbRoomStore, RemoteStoreRoom, MatrixStoreRoom } from "./db/roomstore"; import { ChannelSyncroniser } from "./channelsyncroniser"; +import { Log } from "./log"; const PERMISSION_REQUEST_TIMEOUT = 300000; // 5 minutes +const log = new Log("Provisioner"); + export class Provisioner { private pendingRequests: Map<string, (approved: boolean) => void> = new Map(); // [channelId]: resolver fn @@ -38,7 +41,7 @@ export class Provisioner { return this.roomStore.linkRooms(local, remote); } - public async UnbridgeChannel(channel: Discord.TextChannel) { + public async UnbridgeChannel(channel: Discord.TextChannel, rId?: string) { const roomsRes = await this.roomStore.getEntriesByRemoteRoomData({ discord_channel: channel.id, discord_guild: channel.guild.id, @@ -48,6 +51,18 @@ export class Provisioner { throw Error("Channel is not bridged"); } const remoteRoom = roomsRes[0].remote as RemoteStoreRoom; + let roomsToUnbridge: string[] = []; + if (rId) { + roomsToUnbridge = [rId]; + } else { + // Kill em all. + roomsToUnbridge = roomsRes.map((entry) => entry.matrix!.roomId); + } + await Promise.all(roomsToUnbridge.map( async (roomId) => { + return this.channelSync.OnUnbridge(channel, roomId).catch((err) => { + log.error(`Failed to cleanly unbridge ${channel.id} ${channel.guild} from ${roomId}`); + }); + })); await this.roomStore.removeEntriesByRemoteRoomId(remoteRoom.getId()); }