From 91668c2986288e379f953dcaeb62725f6772ab1e Mon Sep 17 00:00:00 2001 From: Will Hunt Date: Fri, 7 Aug 2020 16:33:29 +0100 Subject: [PATCH 01/15] Update config --- config.schema.yml | 6 +++++- src/irc/IrcServer.ts | 7 +++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/config.schema.yml b/config.schema.yml index e9f68ac79..0b9ec722a 100644 --- a/config.schema.yml +++ b/config.schema.yml @@ -311,9 +311,13 @@ properties: type: "string" minItems: 1 uniqueItems: true + createRoom: + type: "boolean" key: type: "string" - required: ["roomIds"] + oneOf: + - required: ["roomIds"] + - required: ["createRoom"] # Legacy format - type: "array" items: diff --git a/src/irc/IrcServer.ts b/src/irc/IrcServer.ts index d2251c14b..99e73dc40 100644 --- a/src/irc/IrcServer.ts +++ b/src/irc/IrcServer.ts @@ -174,6 +174,12 @@ export class IrcServer { return Array.from(roomIds.keys()); } + public getAutoCreateMappings() { + return Object.entries(this.config.mappings) + .filter(([k,v]) => v.createRoom) + .map(([k, v]) => ({ channel: k, key: v.key })); + } + public getChannelKey(channel: string) { return this.config.mappings[channel]?.key; } @@ -661,6 +667,7 @@ export interface IrcServerConfig { mappings: { [channel: string]: { roomIds: string[]; + createRoom?: boolean; key?: string; }; }; From e74dfae8092e5b42c410fdf23a06237b8ded1e9c Mon Sep 17 00:00:00 2001 From: Will Hunt Date: Fri, 7 Aug 2020 16:33:42 +0100 Subject: [PATCH 02/15] Allow choosing visibility of channels --- src/bridge/RoomCreation.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/bridge/RoomCreation.ts b/src/bridge/RoomCreation.ts index c9cdf8895..84088a88e 100644 --- a/src/bridge/RoomCreation.ts +++ b/src/bridge/RoomCreation.ts @@ -12,6 +12,8 @@ interface TrackChannelOpts { origin: RoomOrigin; roomAliasName?: string; intent?: Intent; + roomVisibility?: "public"|"private"; + historyVisiblity?: "joined"|"invited"|"shared"|"world_readable"; } /** @@ -35,7 +37,7 @@ export async function trackChannelAndCreateRoom(ircBridge: IrcBridge, req: Bridg type: "m.room.history_visibility", state_key: "", content: { - history_visibility: "joined" + history_visibility: opts.historyVisiblity || "joined" } } ]; @@ -64,7 +66,7 @@ export async function trackChannelAndCreateRoom(ircBridge: IrcBridge, req: Bridg const response = await intent.createRoom({ options: { name: ircChannel, - visibility: "private", + visibility: opts.roomVisibility || "private", preset: "public_chat", creation_content: { "m.federate": server.shouldFederate() From 45f0ead9f6f9225bada0d78f232c44414c65f036 Mon Sep 17 00:00:00 2001 From: Will Hunt Date: Fri, 7 Aug 2020 16:34:39 +0100 Subject: [PATCH 03/15] Don't map rooms with the createRoom flag, and check roomIds exists --- src/datastore/postgres/PgDataStore.ts | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/datastore/postgres/PgDataStore.ts b/src/datastore/postgres/PgDataStore.ts index e7f4754b1..79503bb98 100644 --- a/src/datastore/postgres/PgDataStore.ts +++ b/src/datastore/postgres/PgDataStore.ts @@ -63,10 +63,17 @@ export class PgDataStore implements DataStore { public async setServerFromConfig(server: IrcServer, serverConfig: IrcServerConfig): Promise { this.serverMappings[server.domain] = server; - for (const channel of Object.keys(serverConfig.mappings)) { + for (const [channel, data] of Object.entries(serverConfig.mappings)) { + if (data.createRoom) { + // We don't want to map this. + return; + } + if (!data.roomIds) { + throw Error(`roomIds not given on ${channel} config entry`); + } const ircRoom = new IrcRoom(server, channel); ircRoom.set("type", "channel"); - for (const roomId of serverConfig.mappings[channel].roomIds) { + for (const roomId of data.roomIds) { const mxRoom = new MatrixRoom(roomId); await this.storeRoom(ircRoom, mxRoom, "config"); } From 2a1392269eed09cb5c2f334249ebab45be4bc736 Mon Sep 17 00:00:00 2001 From: Will Hunt Date: Fri, 7 Aug 2020 16:35:02 +0100 Subject: [PATCH 04/15] Automatically create rooms for channels --- src/bridge/IrcBridge.ts | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/bridge/IrcBridge.ts b/src/bridge/IrcBridge.ts index 28f776484..d65219ea9 100644 --- a/src/bridge/IrcBridge.ts +++ b/src/bridge/IrcBridge.ts @@ -38,6 +38,7 @@ import { BridgeStateSyncer } from "./BridgeStateSyncer"; import { Registry } from "prom-client"; import { spawnMetricsWorker } from "../workers/MetricsWorker"; import { getBridgeVersion } from "../util/PackageInfo"; +import { trackChannelAndCreateRoom } from "./RoomCreation"; const log = getLogger("IrcBridge"); const DEFAULT_PORT = 8090; @@ -505,6 +506,10 @@ export class IrcBridge { log.info("Connecting to IRC networks..."); await this.connectToIrcNetworks(); + + log.info("Creating rooms for mappings with no roomIds"); + await this.autocreateMappedChannels(); + promiseutil.allSettled(this.ircServers.map((server) => { // Call MODE on all known channels to get modes of all channels return Bluebird.cast(this.publicitySyncer.initModes(server)); @@ -613,6 +618,33 @@ export class IrcBridge { await promiseutil.allSettled(promises); } + private async autocreateMappedChannels() { + const req = new BridgeRequest(new Request()); + for (const server of this.ircServers) { + for (const ircChannel of server.getAutoCreateMappings()) { + const existingRooms = await this.dataStore.getMatrixRoomsForChannel(server, ircChannel.channel); + if (existingRooms.length > 0) { + // We don't autocreate for existing channels + continue; + } + // Create a fresh room! + try { + const roomAliasName = server.getAliasFromChannel(ircChannel.channel).split(":")[0].substring(1); + await trackChannelAndCreateRoom(this, req, { + server, + ircChannel: ircChannel.channel, + key: ircChannel.key, + origin: "alias", + roomAliasName: roomAliasName, + roomVisibility: "public", + }); + } catch (ex) { + log.warn("Failed to create and track room from config:", ex); + } + } + } + } + public async sendMatrixAction(room: MatrixRoom, from: MatrixUser, action: MatrixAction): Promise { const intent = this.bridge.getIntent(from.userId); if (action.msgType) { From a516514d329c45b3cbece367c0bf1df83fe2b1b6 Mon Sep 17 00:00:00 2001 From: Will Hunt Date: Fri, 7 Aug 2020 17:19:20 +0100 Subject: [PATCH 05/15] Try to develop a way for rooms to be removed if removed from the config --- src/bridge/IrcBridge.ts | 4 +--- src/datastore/DataStore.ts | 2 +- src/datastore/NedbDataStore.ts | 6 ++++++ src/datastore/postgres/PgDataStore.ts | 20 ++++++++++++++++++-- 4 files changed, 26 insertions(+), 6 deletions(-) diff --git a/src/bridge/IrcBridge.ts b/src/bridge/IrcBridge.ts index d65219ea9..9a08966eb 100644 --- a/src/bridge/IrcBridge.ts +++ b/src/bridge/IrcBridge.ts @@ -369,8 +369,6 @@ export class IrcBridge { throw Error("Incorrect database config"); } - await this.dataStore.removeConfigMappings(); - this.clientPool = new ClientPool(this, this.dataStore); if (this.config.ircService.debugApi.enabled) { @@ -634,7 +632,7 @@ export class IrcBridge { server, ircChannel: ircChannel.channel, key: ircChannel.key, - origin: "alias", + origin: "config", roomAliasName: roomAliasName, roomVisibility: "public", }); diff --git a/src/datastore/DataStore.ts b/src/datastore/DataStore.ts index 504dd862e..0a001fbb7 100644 --- a/src/datastore/DataStore.ts +++ b/src/datastore/DataStore.ts @@ -128,7 +128,7 @@ export interface DataStore { getRoomIdsFromConfig(): Promise; - removeConfigMappings(): Promise; + removeConfigMappings(server: IrcServer): Promise; getIpv6Counter(): Promise; diff --git a/src/datastore/NedbDataStore.ts b/src/datastore/NedbDataStore.ts index 44fe49736..1b28e2686 100644 --- a/src/datastore/NedbDataStore.ts +++ b/src/datastore/NedbDataStore.ts @@ -91,6 +91,12 @@ export class NeDBDataStore implements DataStore { public async setServerFromConfig(server: IrcServer, serverConfig: IrcServerConfig): Promise { this.serverMappings[server.domain] = server; + if (server.getAutoCreateMappings().length > 0) { + throw Error('Cannot use autocreate feature with NeDB'); + } + + await this.removeConfigMappings(); + for (const channel of Object.keys(serverConfig.mappings)) { const ircRoom = new IrcRoom(server, channel); for (const roomId of serverConfig.mappings[channel].roomIds) { diff --git a/src/datastore/postgres/PgDataStore.ts b/src/datastore/postgres/PgDataStore.ts index 79503bb98..5df05955c 100644 --- a/src/datastore/postgres/PgDataStore.ts +++ b/src/datastore/postgres/PgDataStore.ts @@ -63,6 +63,8 @@ export class PgDataStore implements DataStore { public async setServerFromConfig(server: IrcServer, serverConfig: IrcServerConfig): Promise { this.serverMappings[server.domain] = server; + await this.removeConfigMappings(server); + for (const [channel, data] of Object.entries(serverConfig.mappings)) { if (data.createRoom) { // We don't want to map this. @@ -364,8 +366,22 @@ export class PgDataStore implements DataStore { ).rows.map((e) => e.room_id); } - public async removeConfigMappings(): Promise { - await this.pgPool.query("DELETE FROM rooms WHERE origin = 'config'"); + public async removeConfigMappings(server: IrcServer): Promise { + // We need to remove config mappings on startup so we can ensure what's in the config + // matches what's in the database. The problem is that autogenerated rooms should not + // be deleted as they will not be regenerated on startup. + // + // However, we *should* delete any of them that are no longer in the config, hence the + // gnarly code below. + let exclusionQuerys = []; + console.log(server.getNetworkId(), server.getAutoCreateMappings()); + for (const mapping of server.getAutoCreateMappings()) { + exclusionQuerys.push(`'${mapping.channel}'`); + } + const query = `DELETE FROM rooms WHERE origin = 'config' AND irc_server = '${server.getNetworkId()}'` + + ` AND irc_channel NOT IN (${exclusionQuerys.join(",")})`; + console.log(query); + this.pgPool.query(query); } public async getIpv6Counter(): Promise { From 4917a9203f7d7c8888ee341410d0fd452a32339e Mon Sep 17 00:00:00 2001 From: Will Hunt Date: Fri, 7 Aug 2020 17:22:11 +0100 Subject: [PATCH 06/15] linting --- src/bridge/IrcBridge.ts | 3 ++- src/irc/IrcServer.ts | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/bridge/IrcBridge.ts b/src/bridge/IrcBridge.ts index 9a08966eb..0c36bd84e 100644 --- a/src/bridge/IrcBridge.ts +++ b/src/bridge/IrcBridge.ts @@ -636,7 +636,8 @@ export class IrcBridge { roomAliasName: roomAliasName, roomVisibility: "public", }); - } catch (ex) { + } + catch (ex) { log.warn("Failed to create and track room from config:", ex); } } diff --git a/src/irc/IrcServer.ts b/src/irc/IrcServer.ts index 99e73dc40..1c357b47a 100644 --- a/src/irc/IrcServer.ts +++ b/src/irc/IrcServer.ts @@ -176,7 +176,7 @@ export class IrcServer { public getAutoCreateMappings() { return Object.entries(this.config.mappings) - .filter(([k,v]) => v.createRoom) + .filter(([, v]) => v.createRoom) .map(([k, v]) => ({ channel: k, key: v.key })); } From adcec3e6fd8b7a861eaeed79d0d7403c89506d4f Mon Sep 17 00:00:00 2001 From: Will Hunt Date: Fri, 7 Aug 2020 17:26:47 +0100 Subject: [PATCH 07/15] changelog --- changelog.d/1099.bugfix | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/1099.bugfix diff --git a/changelog.d/1099.bugfix b/changelog.d/1099.bugfix new file mode 100644 index 000000000..717a3e8d1 --- /dev/null +++ b/changelog.d/1099.bugfix @@ -0,0 +1 @@ +Add the ability to create rooms automatically for mapped channels in the config. \ No newline at end of file From 64552768cd942ff3c05bb8baf47d5f4b02d260fe Mon Sep 17 00:00:00 2001 From: Will Hunt Date: Fri, 7 Aug 2020 17:29:43 +0100 Subject: [PATCH 08/15] only exclude channels sometimes --- src/datastore/postgres/PgDataStore.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/datastore/postgres/PgDataStore.ts b/src/datastore/postgres/PgDataStore.ts index 5df05955c..1155557fd 100644 --- a/src/datastore/postgres/PgDataStore.ts +++ b/src/datastore/postgres/PgDataStore.ts @@ -378,9 +378,10 @@ export class PgDataStore implements DataStore { for (const mapping of server.getAutoCreateMappings()) { exclusionQuerys.push(`'${mapping.channel}'`); } - const query = `DELETE FROM rooms WHERE origin = 'config' AND irc_server = '${server.getNetworkId()}'` - + ` AND irc_channel NOT IN (${exclusionQuerys.join(",")})`; - console.log(query); + let query = `DELETE FROM rooms WHERE origin = 'config' AND irc_server = '${server.getNetworkId()}'` + if (exclusionQuerys.length) { + query += ` AND irc_channel NOT IN (${exclusionQuerys.join(",")})`; + } this.pgPool.query(query); } From fc5bd836d8330e8ab61f6500ba0e139c568fa377 Mon Sep 17 00:00:00 2001 From: Will Hunt Date: Mon, 10 Aug 2020 10:31:19 +0100 Subject: [PATCH 09/15] Support autocreated channels on NeDB --- src/datastore/NedbDataStore.ts | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/datastore/NedbDataStore.ts b/src/datastore/NedbDataStore.ts index 1b28e2686..f8598a0ae 100644 --- a/src/datastore/NedbDataStore.ts +++ b/src/datastore/NedbDataStore.ts @@ -91,11 +91,7 @@ export class NeDBDataStore implements DataStore { public async setServerFromConfig(server: IrcServer, serverConfig: IrcServerConfig): Promise { this.serverMappings[server.domain] = server; - if (server.getAutoCreateMappings().length > 0) { - throw Error('Cannot use autocreate feature with NeDB'); - } - - await this.removeConfigMappings(); + await this.removeConfigMappings(server); for (const channel of Object.keys(serverConfig.mappings)) { const ircRoom = new IrcRoom(server, channel); @@ -419,13 +415,22 @@ export class NeDBDataStore implements DataStore { }).filter((e) => e !== ""); } - public async removeConfigMappings() { + public async removeConfigMappings(server: IrcServer) { await this.roomStore.removeEntriesByLinkData({ from_config: true // for backwards compatibility }); - await this.roomStore.removeEntriesByLinkData({ - origin: 'config' + // Filter for config entries which are from this network and are NOT autocreated. + let entries = await this.roomStore.getEntriesByLinkData({ + origin: 'config', }); + const notChannels = server.getAutoCreateMappings().map((c) => c.channel); + entries = (await entries).filter((e) => e.remote?.get("domain") === server.domain && + !notChannels.includes(e.remote?.get("channel") as string)); + + // Remove just these entries. + for (const entry of entries) { + await this.roomStore.removeEntriesByRemoteRoomId(entry.remote_id); + } } public async getIpv6Counter(): Promise { From 511f26ce3bf370b146d67f0ec88877767302f214 Mon Sep 17 00:00:00 2001 From: Will Hunt Date: Mon, 10 Aug 2020 11:29:19 +0100 Subject: [PATCH 10/15] Postgresql formattign --- src/datastore/postgres/PgDataStore.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/datastore/postgres/PgDataStore.ts b/src/datastore/postgres/PgDataStore.ts index 1155557fd..efb271af1 100644 --- a/src/datastore/postgres/PgDataStore.ts +++ b/src/datastore/postgres/PgDataStore.ts @@ -376,13 +376,15 @@ export class PgDataStore implements DataStore { let exclusionQuerys = []; console.log(server.getNetworkId(), server.getAutoCreateMappings()); for (const mapping of server.getAutoCreateMappings()) { - exclusionQuerys.push(`'${mapping.channel}'`); + exclusionQuerys.push(mapping.channel); } - let query = `DELETE FROM rooms WHERE origin = 'config' AND irc_server = '${server.getNetworkId()}'` + let query = `DELETE FROM rooms WHERE origin = 'config' AND irc_server = $1` if (exclusionQuerys.length) { - query += ` AND irc_channel NOT IN (${exclusionQuerys.join(",")})`; + query += ` AND irc_channel NOT IN ('${exclusionQuerys.join("','")}')`; } - this.pgPool.query(query); + this.pgPool.query(query, [ + server.getNetworkId(), + ]); } public async getIpv6Counter(): Promise { From 260e93eda4fcb81d707894f97109475069c1d4ca Mon Sep 17 00:00:00 2001 From: Will Hunt Date: Mon, 10 Aug 2020 12:47:02 +0100 Subject: [PATCH 11/15] Don't track autocreated rooms when starting up --- src/bridge/IrcBridge.ts | 5 ++++- src/bridge/RoomCreation.ts | 34 +++++++++++++++++++++++----------- 2 files changed, 27 insertions(+), 12 deletions(-) diff --git a/src/bridge/IrcBridge.ts b/src/bridge/IrcBridge.ts index 0c36bd84e..d11c8f97c 100644 --- a/src/bridge/IrcBridge.ts +++ b/src/bridge/IrcBridge.ts @@ -501,13 +501,15 @@ export class IrcBridge { const requestTimeoutSeconds = this.config.ircService.provisioning.requestTimeoutSeconds; this.provisioner = new Provisioner(this, provisioningEnabled, requestTimeoutSeconds); + + log.info("Connecting to IRC networks..."); await this.connectToIrcNetworks(); - log.info("Creating rooms for mappings with no roomIds"); await this.autocreateMappedChannels(); + promiseutil.allSettled(this.ircServers.map((server) => { // Call MODE on all known channels to get modes of all channels return Bluebird.cast(this.publicitySyncer.initModes(server)); @@ -635,6 +637,7 @@ export class IrcBridge { origin: "config", roomAliasName: roomAliasName, roomVisibility: "public", + shouldTrack: false, // wait for a user to join }); } catch (ex) { diff --git a/src/bridge/RoomCreation.ts b/src/bridge/RoomCreation.ts index 84088a88e..5848455f5 100644 --- a/src/bridge/RoomCreation.ts +++ b/src/bridge/RoomCreation.ts @@ -3,6 +3,7 @@ import { IrcBridge } from "./IrcBridge"; import { MatrixRoom, Intent } from "matrix-appservice-bridge"; import { BridgeRequest } from "../models/BridgeRequest"; import { RoomOrigin } from "../datastore/DataStore"; +import { IrcRoom } from "../models/IrcRoom"; interface TrackChannelOpts { server: IrcServer; @@ -14,6 +15,7 @@ interface TrackChannelOpts { intent?: Intent; roomVisibility?: "public"|"private"; historyVisiblity?: "joined"|"invited"|"shared"|"world_readable"; + shouldTrack?: boolean, } /** @@ -23,7 +25,11 @@ interface TrackChannelOpts { * @param opts Information about the room creation request. */ export async function trackChannelAndCreateRoom(ircBridge: IrcBridge, req: BridgeRequest, opts: TrackChannelOpts) { - const { server, ircChannel, key, inviteList, origin, roomAliasName } = opts; + if (opts.shouldTrack === undefined) { + opts.shouldTrack = true; + } + let ircRoom: IrcRoom; + const { server, ircChannel, key, inviteList, origin, roomAliasName, shouldTrack } = opts; const intent = opts.intent || ircBridge.getAppServiceBridge().getIntent(); const initialState: ({type: string; state_key: string; content: object})[] = [ { @@ -58,9 +64,13 @@ export async function trackChannelAndCreateRoom(ircBridge: IrcBridge, req: Bridg ) ) } - req.log.info("Going to track IRC channel %s", ircChannel); - const ircRoom = await ircBridge.trackChannel(server, ircChannel, key); - req.log.info("Bot is now tracking IRC channel."); + if (shouldTrack) { + req.log.info("Going to track IRC channel %s", ircChannel); + ircRoom = await ircBridge.trackChannel(server, ircChannel, key); + req.log.info("Bot is now tracking IRC channel."); + } else { + ircRoom = new IrcRoom(server, ircChannel); + } let roomId; try { const response = await intent.createRoom({ @@ -87,12 +97,14 @@ export async function trackChannelAndCreateRoom(ircBridge: IrcBridge, req: Bridg const mxRoom = new MatrixRoom(roomId); await ircBridge.getStore().storeRoom(ircRoom, mxRoom, origin); - // /mode the channel AFTER we have created the mapping so we process - // +s and +i correctly. This is done asyncronously. - ircBridge.publicitySyncer.initModeForChannel(server, ircChannel).catch(() => { - req.log.error( - `Could not init mode for channel ${ircChannel} on ${server.domain}` - ); - }); + if (shouldTrack) { + // /mode the channel AFTER we have created the mapping so we process + // +s and +i correctly. This is done asyncronously. + ircBridge.publicitySyncer.initModeForChannel(server, ircChannel).catch(() => { + req.log.error( + `Could not init mode for channel ${ircChannel} on ${server.domain}` + ); + }); + } return { ircRoom, mxRoom }; } From 518c21d6b2eb90895e2c44a8ca99644c6d126fef Mon Sep 17 00:00:00 2001 From: Will Hunt Date: Mon, 10 Aug 2020 12:47:18 +0100 Subject: [PATCH 12/15] Fix config room adding and deleting --- src/datastore/NedbDataStore.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/datastore/NedbDataStore.ts b/src/datastore/NedbDataStore.ts index f8598a0ae..bfd2c7be8 100644 --- a/src/datastore/NedbDataStore.ts +++ b/src/datastore/NedbDataStore.ts @@ -93,9 +93,13 @@ export class NeDBDataStore implements DataStore { await this.removeConfigMappings(server); - for (const channel of Object.keys(serverConfig.mappings)) { + for (const [channel, opts] of Object.entries(serverConfig.mappings)) { + if (opts.createRoom) { + return; + } + log.info(`Mapping channel ${channel} to ${opts.roomIds.join(",")}`); const ircRoom = new IrcRoom(server, channel); - for (const roomId of serverConfig.mappings[channel].roomIds) { + for (const roomId of opts.roomIds) { const mxRoom = new MatrixRoom(roomId); await this.storeRoom(ircRoom, mxRoom, "config"); } @@ -429,7 +433,7 @@ export class NeDBDataStore implements DataStore { // Remove just these entries. for (const entry of entries) { - await this.roomStore.removeEntriesByRemoteRoomId(entry.remote_id); + await this.roomStore.delete({id: entry.id, "data.origin": "config"}); } } From a4d6f3aad9d576a40733e9ae85bfc4516386ed05 Mon Sep 17 00:00:00 2001 From: Will Hunt Date: Mon, 10 Aug 2020 12:48:52 +0100 Subject: [PATCH 13/15] Add tests --- spec/integ/static-channels.spec.js | 84 ++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 spec/integ/static-channels.spec.js diff --git a/spec/integ/static-channels.spec.js b/spec/integ/static-channels.spec.js new file mode 100644 index 000000000..94ed21d52 --- /dev/null +++ b/spec/integ/static-channels.spec.js @@ -0,0 +1,84 @@ +const envBundle = require("../util/env-bundle"); + +describe("Static channels", function() { + const staticTestChannel = "#astaticchannel"; + const generatedTestChannel = "#ageneratedchannel"; + const generatedRoomId = "!gen:bar"; + const {env, config, roomMapping, botUserId, test} = envBundle(); + + beforeEach(async function() { + config.ircService.servers[roomMapping.server].mappings[staticTestChannel] = { + roomIds: ["!foo:bar"], + }; + + config.ircService.servers[roomMapping.server].mappings[generatedTestChannel] = { + createRoom: true, + }; + + await test.beforeEach(env); + + env.ircMock._autoConnectNetworks( + roomMapping.server, roomMapping.botNick, roomMapping.server + ); + + // Ensure rooms are created on startup + sdk = env.clientMock._client(botUserId); + sdk.createRoom.and.callFake(async function(opts) { + return { + room_id: generatedRoomId + }; + }); + + await test.initEnv(env, config); + }); + + afterEach(async function() { + await test.afterEach(env); + }); + + it("should insert static channel mappings to bridge store", async function () { + const store = await env.ircBridge.getStore(); + const server = await env.ircBridge.getServer(roomMapping.server); + const mappings = await store.getMappingsForChannelByOrigin(server, staticTestChannel, "config"); + expect(mappings.length).toEqual(1); + const entry = mappings[0]; + expect(entry.matrix.roomId).toEqual("!foo:bar"); + expect(entry.remote.data).toEqual({ + domain: roomMapping.server, + channel: staticTestChannel, + type: "channel", + }); + expect(entry.data.origin).toEqual("config"); + }); + + it("should clear static channel mappings from bridge store", async function () { + const store = await env.ircBridge.getStore(); + const server = await env.ircBridge.getServer(roomMapping.server); + await store.removeConfigMappings(server); + const mappings = await store.getMappingsForChannelByOrigin(server, staticTestChannel, "config"); + expect(mappings.length).toEqual(0); + }); + + it("should create a channel mapping for mappings with createRoom", async function () { + const store = await env.ircBridge.getStore(); + const server = await env.ircBridge.getServer(roomMapping.server); + const mappings = await store.getMappingsForChannelByOrigin(server, generatedTestChannel, "config"); + expect(mappings.length).toEqual(1); + const entry = mappings[0]; + expect(entry.remote.data).toEqual({ + domain: roomMapping.server, + channel: generatedTestChannel, + type: "channel", + }); + expect(entry.matrix.roomId).toEqual(generatedRoomId); + expect(entry.data.origin).toEqual("config"); + }); + + it("should NOT clear channel mappings for mappings with createRoom", async function () { + const store = await env.ircBridge.getStore(); + const server = await env.ircBridge.getServer(roomMapping.server); + await store.removeConfigMappings(server); + const mappings = await store.getMappingsForChannelByOrigin(server, generatedTestChannel, "config"); + expect(mappings.length).toEqual(1); + }); +}); \ No newline at end of file From 0c352176f325a80c2501af1864ecd9bcb9c72810 Mon Sep 17 00:00:00 2001 From: Will Hunt Date: Mon, 10 Aug 2020 12:55:37 +0100 Subject: [PATCH 14/15] use domain --- src/datastore/postgres/PgDataStore.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/datastore/postgres/PgDataStore.ts b/src/datastore/postgres/PgDataStore.ts index efb271af1..5bbc79dab 100644 --- a/src/datastore/postgres/PgDataStore.ts +++ b/src/datastore/postgres/PgDataStore.ts @@ -378,12 +378,12 @@ export class PgDataStore implements DataStore { for (const mapping of server.getAutoCreateMappings()) { exclusionQuerys.push(mapping.channel); } - let query = `DELETE FROM rooms WHERE origin = 'config' AND irc_server = $1` + let query = `DELETE FROM rooms WHERE origin = 'config' AND irc_domain = $1` if (exclusionQuerys.length) { query += ` AND irc_channel NOT IN ('${exclusionQuerys.join("','")}')`; } this.pgPool.query(query, [ - server.getNetworkId(), + server.domain, ]); } From 9582253c38c7c9771bb661d037f18ddb1be4adf9 Mon Sep 17 00:00:00 2001 From: Will Hunt Date: Mon, 10 Aug 2020 12:56:30 +0100 Subject: [PATCH 15/15] lint --- src/bridge/RoomCreation.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/bridge/RoomCreation.ts b/src/bridge/RoomCreation.ts index 5848455f5..3ada3e442 100644 --- a/src/bridge/RoomCreation.ts +++ b/src/bridge/RoomCreation.ts @@ -15,7 +15,7 @@ interface TrackChannelOpts { intent?: Intent; roomVisibility?: "public"|"private"; historyVisiblity?: "joined"|"invited"|"shared"|"world_readable"; - shouldTrack?: boolean, + shouldTrack?: boolean; } /** @@ -68,7 +68,8 @@ export async function trackChannelAndCreateRoom(ircBridge: IrcBridge, req: Bridg req.log.info("Going to track IRC channel %s", ircChannel); ircRoom = await ircBridge.trackChannel(server, ircChannel, key); req.log.info("Bot is now tracking IRC channel."); - } else { + } + else { ircRoom = new IrcRoom(server, ircChannel); } let roomId;