diff --git a/provisioner-rules.yaml b/provisioner-rules.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..c1fdb6eba67b8282617241e6667bb2d3262c65fa
--- /dev/null
+++ b/provisioner-rules.yaml
@@ -0,0 +1,15 @@
+# This rule checks the memberlist of a room to determine if it will let
+# the bridge create a link to the room. This is useful for avoiding conflicts
+# with other bridges.
+userIds:
+    # Anyone in this set will be ALWAYS exempt from the conflicts rule.
+    # Here anyone who's localpart starts with nice is exempt.
+    exempt:
+        - "@nice+.:example.com"
+        - "@mr.e:localhost"
+    # This is a regex that will exclude anyone who has "guy" at the end of their localpart.
+    # evilbloke is also exempt.
+    conflict:
+        - "@+.guy:example.com"
+        - "@evilbloke:example.com"
+        - "@mr.*:localhost"
diff --git a/src/bot.ts b/src/bot.ts
index 47fddc3b9e3ce2ecf0143bbe20c3740471363307..eee7eb5424b3a83060dfa8979b4a7eb08de4d4de 100644
--- a/src/bot.ts
+++ b/src/bot.ts
@@ -63,11 +63,8 @@ interface IThirdPartyLookup {
 }
 
 export class DiscordBot {
-    private config: DiscordBridgeConfig;
     private clientFactory: DiscordClientFactory;
-    private store: DiscordStore;
     private bot: Discord.Client;
-    private bridge: Bridge;
     private presenceInterval: number;
     private sentMessages: string[];
     private lastEventIds: { [channelId: string]: string };
@@ -77,35 +74,38 @@ export class DiscordBot {
     private userSync: UserSyncroniser;
     private channelSync: ChannelSyncroniser;
     private roomHandler: MatrixRoomHandler;
+    private provisioner: Provisioner;
     /* Caches */
     private roomIdsForGuildCache: Map<string, {roomIds: string[], ts: number}>;
 
     /* Handles messages queued up to be sent to discord. */
     private discordMessageQueue: { [channelId: string]: Promise<void> };
 
-    constructor(config: DiscordBridgeConfig, store: DiscordStore, private provisioner: Provisioner) {
-        this.config = config;
-        this.store = store;
-        this.sentMessages = [];
+    constructor(
+        private botUserId: string,
+        private config: DiscordBridgeConfig,
+        private bridge: Bridge,
+        private store: DiscordStore,
+    ) {
+
+        // create handlers
+        this.provisioner = new Provisioner(store.roomStore);
         this.clientFactory = new DiscordClientFactory(store, config.auth);
         this.discordMsgProcessor = new DiscordMessageProcessor(
-            new DiscordMessageProcessorOpts(this.config.bridge.domain, this),
+            new DiscordMessageProcessorOpts(config.bridge.domain, this),
         );
         this.presenceHandler = new PresenceHandler(this);
-        this.discordMessageQueue = {};
-        this.lastEventIds = {};
-    }
-
-    public setBridge(bridge: Bridge) {
-        this.bridge = bridge;
+        this.roomHandler = new MatrixRoomHandler(this, config, this.provisioner, bridge, store.roomStore);
         this.mxEventProcessor = new MatrixEventProcessor(
-            new MatrixEventProcessorOpts(this.config, bridge, this),
+            new MatrixEventProcessorOpts(config, bridge, this),
         );
-        this.channelSync = new ChannelSyncroniser(this.bridge, this.config, this, this.store.roomStore);
-    }
+        this.channelSync = new ChannelSyncroniser(bridge, config, this, store.roomStore);
+        this.userSync = new UserSyncroniser(bridge, config, this);
 
-    public setRoomHandler(roomHandler: MatrixRoomHandler) {
-        this.roomHandler = roomHandler;
+        // init vars
+        this.sentMessages = [];
+        this.discordMessageQueue = {};
+        this.lastEventIds = {};
     }
 
     get ClientFactory(): DiscordClientFactory {
@@ -120,6 +120,14 @@ export class DiscordBot {
         return this.channelSync;
     }
 
+    get BotUserId(): string {
+        return this.botUserId;
+    }
+
+    get RoomHandler(): MatrixRoomHandler {
+        return this.roomHandler;
+    }
+
     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_
@@ -130,8 +138,11 @@ export class DiscordBot {
         return this.bridge.getIntentFromLocalpart(`_discord_${member.id}`);
     }
 
-    public async run(): Promise<void> {
+    public async init(): Promise<void> {
         await this.clientFactory.init();
+    }
+
+    public async run(): Promise<void> {
         const client = await this.clientFactory.getClient();
 
         if (!this.config.bridge.disableTypingNotifications) {
@@ -224,7 +235,6 @@ export class DiscordBot {
         });
         const jsLog = new Log("discord.js");
 
-        this.userSync = new UserSyncroniser(this.bridge, this.config, this);
         client.on("userUpdate", async (_, user) => {
             try {
                 await this.userSync.OnUpdateUser(user);
@@ -831,7 +841,7 @@ export class DiscordBot {
                     await afterSend(res);
                 } catch (e) {
                     if (e.errcode !== "M_FORBIDDEN" && e.errcode !==  "M_GUEST_ACCESS_FORBIDDEN") {
-                        log.error("DiscordBot", "Failed to send message into room.", e);
+                        log.error("Failed to send message into room.", e);
                         return;
                     }
                     if (msg.member) {
diff --git a/src/clientfactory.ts b/src/clientfactory.ts
index 0d584c2f8080b4c2b4f58abb8b02848cae10b2b5..72a5b57006a512f2051c3a43f34fd48887ba3124 100644
--- a/src/clientfactory.ts
+++ b/src/clientfactory.ts
@@ -22,7 +22,7 @@ import { Log } from "./log";
 
 const log = new Log("ClientFactory");
 
-const READY_TIMEOUT = 5000;
+const READY_TIMEOUT = 30000;
 
 export class DiscordClientFactory {
     private config: DiscordBridgeConfigAuth;
diff --git a/src/discordas.ts b/src/discordas.ts
index c445547842dd95daf5bc7abe500cb93bfdf0ea41..9115b3d6a5a5b11aa8958f5add2d4a6939372ca5 100644
--- a/src/discordas.ts
+++ b/src/discordas.ts
@@ -20,9 +20,7 @@ import * as yaml from "js-yaml";
 import * as fs from "fs";
 import { DiscordBridgeConfig } from "./config";
 import { DiscordBot } from "./bot";
-import { MatrixRoomHandler } from "./matrixroomhandler";
 import { DiscordStore } from "./store";
-import { Provisioner } from "./provisioner";
 import { Log } from "./log";
 import "source-map-support/register";
 
@@ -57,6 +55,9 @@ function generateRegistration(reg, callback)  {
     callback(reg);
 }
 
+// tslint:disable-next-line no-any
+type callbackFn = (...args: any[]) => Promise<any>;
+
 async function run(port: number, fileConfig: DiscordBridgeConfig) {
     const config = new DiscordBridgeConfig();
     config.ApplyConfig(fileConfig);
@@ -74,10 +75,9 @@ async function run(port: number, fileConfig: DiscordBridgeConfig) {
         token: registration.as_token,
         url: config.bridge.homeserverUrl,
     });
-    const provisioner = new Provisioner();
-    const discordstore = new DiscordStore(config.database);
-    const discordbot = new DiscordBot(config, discordstore, provisioner);
-    const roomhandler = new MatrixRoomHandler(discordbot, config, botUserId, provisioner);
+    const store = new DiscordStore(config.database);
+
+    const callbacks: { [id: string]: callbackFn; } = {};
 
     const bridge = new Bridge({
         clientFactory,
@@ -85,26 +85,30 @@ async function run(port: number, fileConfig: DiscordBridgeConfig) {
             // onUserQuery: userQuery,
             onAliasQueried: async (alias: string, roomId: string) => {
                 try {
-                    return await roomhandler.OnAliasQueried.bind(roomhandler)(alias, roomId);
+                    return await callbacks.onAliasQueried(alias, roomId);
                 } catch (err) { log.error("Exception thrown while handling \"onAliasQueried\" event", err); }
             },
             onAliasQuery: async (alias: string, aliasLocalpart: string) => {
                 try {
-                    return await roomhandler.OnAliasQuery.bind(roomhandler)(alias, aliasLocalpart);
+                    return await callbacks.onAliasQuery(alias, aliasLocalpart);
                 } catch (err) { log.error("Exception thrown while handling \"onAliasQuery\" event", err); }
             },
             onEvent: async (request) => {
                 try {
                     // Build our own context.
+                    if (!store.roomStore) {
+                        log.warn("Discord store not ready yet, dropping message");
+                        return;
+                    }
                     const roomId = request.getData().room_id;
                     let context = {};
                     if (roomId) {
-                        const entries  = await discordstore.roomStore.getEntriesByMatrixId(request.getData().room_id);
+                        const entries  = await store.roomStore.getEntriesByMatrixId(request.getData().room_id);
                         context = {
                             rooms: entries[0],
                         };
                     }
-                    await request.outcomeFrom(Bluebird.resolve(roomhandler.OnEvent(request, context)));
+                    await request.outcomeFrom(Bluebird.resolve(callbacks.OnEvent(request, context)));
                 } catch (err) {
                     log.error("Exception thrown while handling \"onEvent\" event", err);
                     await request.outcomeFrom(Bluebird.reject("Failed to handle"));
@@ -113,7 +117,13 @@ async function run(port: number, fileConfig: DiscordBridgeConfig) {
             onLog: (line, isError) => {
                 log.verbose("matrix-appservice-bridge", line);
             },
-            thirdPartyLookup: roomhandler.ThirdPartyLookup,
+            thirdPartyLookup: async () => {
+                try {
+                    return await callbacks.thirdPartyLookup();
+                } catch (err) {
+                    log.error("Exception thrown while handling \"thirdPartyLookup\" event", err);
+                }
+            },
         },
         disableContext: true,
         domain: config.bridge.domain,
@@ -131,23 +141,35 @@ async function run(port: number, fileConfig: DiscordBridgeConfig) {
         registration,
         userStore: config.database.userStorePath,
     });
-    log.info("Initing bridge.");
-    log.info(`Started listening on port ${port}.`);
+    // Warn and deprecate old config options.
+    const discordbot = new DiscordBot(botUserId, config, bridge, store);
+    const roomhandler = discordbot.RoomHandler;
+
+    try {
+        callbacks.onAliasQueried = roomhandler.OnAliasQueried.bind(roomhandler);
+        callbacks.onAliasQuery = roomhandler.OnAliasQuery.bind(roomhandler);
+        callbacks.onEvent = roomhandler.OnEvent.bind(roomhandler);
+        callbacks.thirdPartyLookup = async () => {
+            return roomhandler.ThirdPartyLookup;
+        };
+    } catch (err) {
+        log.error("Failed to register callbacks. Exiting.", err);
+        process.exit(1);
+    }
+
+    log.info("Initing bridge");
 
     try {
         await bridge.run(port, config);
-        log.info("Initing store.");
-        await discordstore.init(0, bridge.getRoomStore());
-        log.info("Initing bot.");
-        provisioner.setStore(discordstore.roomStore);
-        roomhandler.setBridge(bridge, discordstore.roomStore);
-        discordbot.setBridge(bridge);
-        discordbot.setRoomHandler(roomhandler);
+        log.info(`Started listening on port ${port}`);
+        await store.init(undefined, bridge.getRoomStore());
+        log.info("Initing bot");
+        await discordbot.init();
         await discordbot.run();
-        log.info("Discordbot started successfully.");
+        log.info("Discordbot started successfully");
     } catch (err) {
         log.error(err);
-        log.error("Failure during startup. Exiting.");
+        log.error("Failure during startup. Exiting");
         process.exit(1);
     }
 }
diff --git a/src/matrixmessageprocessor.ts b/src/matrixmessageprocessor.ts
index 562d20a30729cb5281bce536d4d87d4eca1ff434..e47f3efccf226cd8a6412ff1f903aa5af8587477 100644
--- a/src/matrixmessageprocessor.ts
+++ b/src/matrixmessageprocessor.ts
@@ -94,7 +94,7 @@ export class MatrixMessageProcessor {
                 msg = msg.replace(/@room/g, "@here");
             }
         }
-        const escapeChars = ["\\", "*", "_", "~", "`"];
+        const escapeChars = ["\\", "*", "_", "~", "`", "|"];
         msg = msg.split(" ").map((s) => {
             if (s.match(/^https?:\/\//)) {
                 return s;
diff --git a/src/matrixroomhandler.ts b/src/matrixroomhandler.ts
index 75a9e1bb95111ae39c4094bbd7a4a6e14f0bab0f..032a77fb8ee1b556b363953ad15dc6aaf5e299ce 100644
--- a/src/matrixroomhandler.ts
+++ b/src/matrixroomhandler.ts
@@ -59,19 +59,16 @@ const JOIN_ROOM_SCHEDULE = [
 /* tslint:enable:no-magic-numbers */
 
 export class MatrixRoomHandler {
-    private bridge: Bridge;
-    private roomStore: DbRoomStore;
+    private botUserId: string;
     private botJoinedRooms: Set<string>; // roomids
     private botJoinedRoomsCacheUpdatedAt = 0;
     constructor(
         private discord: DiscordBot,
         private config: DiscordBridgeConfig,
-        private botUserId: string,
         private provisioner: Provisioner,
-    ) {
-        this.discord = discord;
-        this.config = config;
-        this.botUserId = botUserId;
+        private bridge: Bridge,
+        private roomStore: DbRoomStore) {
+        this.botUserId = this.discord.BotUserId;
         this.botJoinedRooms = new Set();
     }
 
@@ -86,11 +83,6 @@ export class MatrixRoomHandler {
         };
     }
 
-    public setBridge(bridge: Bridge, roomStore: DbRoomStore) {
-        this.bridge = bridge;
-        this.roomStore = roomStore;
-    }
-
     public async OnAliasQueried(alias: string, roomId: string) {
         log.verbose(`Got OnAliasQueried for ${alias} ${roomId}`);
         let channel: Discord.GuildChannel;
diff --git a/src/presencehandler.ts b/src/presencehandler.ts
index 9f1c1e2195891844ff469a8234d4bec7b381a667..24e6e7f7bf293a61e689c7fcb38d5fdc4ba412f6 100644
--- a/src/presencehandler.ts
+++ b/src/presencehandler.ts
@@ -32,11 +32,9 @@ interface IMatrixPresence {
 }
 
 export class PresenceHandler {
-    private readonly bot: DiscordBot;
     private presenceQueue: User[];
     private interval: NodeJS.Timeout | null;
-    constructor(bot: DiscordBot) {
-        this.bot = bot;
+    constructor(private bot: DiscordBot) {
         this.presenceQueue = [];
     }
 
diff --git a/src/provisioner.ts b/src/provisioner.ts
index 4f11e990752448678c6679f63183e0410d7e40bc..9a871a1750fae09eb2f3592b24d1ba1488ccc55d 100644
--- a/src/provisioner.ts
+++ b/src/provisioner.ts
@@ -27,11 +27,8 @@ const PERMISSION_REQUEST_TIMEOUT = 300000; // 5 minutes
 export class Provisioner {
 
     private pendingRequests: Map<string, (approved: boolean) => void> = new Map(); // [channelId]: resolver fn
-    private roomStore: DbRoomStore;
 
-    constructor(roomStore: DbRoomStore) {
-        this.roomStore = roomStore;
-    }
+    constructor(private roomStore: DbRoomStore) { }
 
     public async BridgeMatrixRoom(channel: Discord.TextChannel, roomId: string) {
         const remote = new RemoteRoom(`discord_${channel.guild.id}_${channel.id}_bridged`);
diff --git a/src/store.ts b/src/store.ts
index 4e5dd9d0ec96c19faf8e440005550c838048c196..f17bf143a6c454755d93d389545d97afacaba0df 100644
--- a/src/store.ts
+++ b/src/store.ts
@@ -37,7 +37,7 @@ export class DiscordStore {
     private version: number;
     private config: DiscordBridgeConfigDatabase;
     private pRoomStore: DbRoomStore;
-    constructor(private configOrFile: DiscordBridgeConfigDatabase|string) {
+    constructor(configOrFile: DiscordBridgeConfigDatabase|string) {
         if (typeof(configOrFile) === "string") {
             this.config = new DiscordBridgeConfigDatabase();
             this.config.filename = configOrFile;
diff --git a/test/db/test_roomstore.ts b/test/db/test_roomstore.ts
index 443794572e066ffb7e4a5cfc9866f701710c89ac..c94a796aba105b1f5bc7bb361f84cc139e559461 100644
--- a/test/db/test_roomstore.ts
+++ b/test/db/test_roomstore.ts
@@ -123,20 +123,20 @@ describe("RoomStore", () => {
             const EXPECTED_ROOMS = 2;
             await store.roomStore.upsertEntry({
                 id: "test4_1",
-                matrix: new MatrixStoreRoom("test4_m1"),
+                matrix: new MatrixStoreRoom("!test_mOne:eggs.com"),
                 remote: new RemoteStoreRoom("test4_r", {discord_guild: "five", discord_channel: "five"}),
             });
             await store.roomStore.upsertEntry({
                 id: "test4_2",
-                matrix: new MatrixStoreRoom("test4_m2"),
+                matrix: new MatrixStoreRoom("!test_mTwo:eggs.com"),
                 remote: new RemoteStoreRoom("test4_r", {discord_guild: "nine", discord_channel: "nine"}),
             });
-            const entries = await store.roomStore.getEntriesByMatrixIds(["test4_m1", "test4_m2"]);
+            const entries = await store.roomStore.getEntriesByMatrixIds(["!test_mOne:eggs.com", "!test_mTwo:eggs.com"]);
             expect(entries).to.have.lengthOf(EXPECTED_ROOMS);
             expect(entries[0].id).to.equal("test4_1");
-            expect(entries[0].matrix!.roomId).to.equal("test4_m1");
+            expect(entries[0].matrix!.roomId).to.equal("!test_mOne:eggs.com");
             expect(entries[1].id).to.equal("test4_2");
-            expect(entries[1].matrix!.roomId).to.equal("test4_m2");
+            expect(entries[1].matrix!.roomId).to.equal("!test_mTwo:eggs.com");
         });
     });
     describe("linkRooms", () => {
diff --git a/test/test_discordbot.ts b/test/test_discordbot.ts
index d757a73a1545092c04e881f5d9d347a1c9ca8877..d6dcd1b3a2c1d9484bf60e5daced3b600cf04e14 100644
--- a/test/test_discordbot.ts
+++ b/test/test_discordbot.ts
@@ -89,10 +89,11 @@ describe("DiscordBot", () => {
     describe("run()", () => {
         it("should resolve when ready.", async () => {
             discordBot = new modDiscordBot.DiscordBot(
+                "",
                 config,
-                { },
+                mockBridge,
+                {},
             );
-            discordBot.setBridge(mockBridge);
             await discordBot.run();
         });
     });
@@ -100,10 +101,11 @@ describe("DiscordBot", () => {
     describe("LookupRoom()", () => {
         beforeEach( async () => {
             discordBot = new modDiscordBot.DiscordBot(
+                "",
                 config,
-                { },
+                mockBridge,
+                {},
             );
-            discordBot.setBridge(mockBridge);
             await discordBot.run();
         });
         it("should reject a missing guild.", async () => {
@@ -141,8 +143,10 @@ describe("DiscordBot", () => {
             ATTACHMENT = {};
             MSGTYPE = "";
             const discord = new modDiscordBot.DiscordBot(
+                "",
                 config,
                 mockBridge,
+                {},
             );
             discord.bot = { user: { id: "654" } };
             discord.provisioner = {
@@ -294,8 +298,10 @@ describe("DiscordBot", () => {
     describe("OnMessageUpdate()", () => {
         it("should return on an unchanged message", async () => {
             discordBot = new modDiscordBot.DiscordBot(
+                "",
                 config,
                 mockBridge,
+                {},
             );
 
             const guild: any = new MockGuild("123", []);
@@ -319,8 +325,10 @@ describe("DiscordBot", () => {
         });
         it("should send a matrix message on an edited discord message", async () => {
             discordBot = new modDiscordBot.DiscordBot(
+                "",
                 config,
                 mockBridge,
+                {},
             );
             discordBot.store.Get = (a, b) => null;
 
@@ -345,8 +353,10 @@ describe("DiscordBot", () => {
         });
         it("should delete and re-send if it is the newest message", async () => {
             discordBot = new modDiscordBot.DiscordBot(
+                "",
                 config,
                 mockBridge,
+                {},
             );
             discordBot.store.Get = (a, b) => { return {
                 MatrixId: "$event:localhost;!room:localhost",
@@ -380,8 +390,10 @@ describe("DiscordBot", () => {
     describe("event:message", () => {
         it("should delay messages so they arrive in order", async () => {
             discordBot = new modDiscordBot.DiscordBot(
+                "",
                 config,
                 mockBridge,
+                {},
             );
             let expected = 0;
             discordBot.OnMessage = async (msg: any) => {
@@ -389,7 +401,6 @@ describe("DiscordBot", () => {
                 expected++;
             };
             const client: MockDiscordClient = (await discordBot.ClientFactory.getClient()) as MockDiscordClient;
-            discordBot.setBridge(mockBridge);
             await discordBot.run();
             const ITERATIONS = 25;
             const CHANID = 123;
@@ -401,8 +412,10 @@ describe("DiscordBot", () => {
         });
         it("should handle messages that reject in the queue", async () => {
             discordBot = new modDiscordBot.DiscordBot(
+                "",
                 config,
                 mockBridge,
+                {},
             );
             let expected = 0;
             const THROW_EVERY = 5;
@@ -415,7 +428,6 @@ describe("DiscordBot", () => {
                 return Promise.resolve();
             };
             const client: MockDiscordClient = (await discordBot.ClientFactory.getClient()) as MockDiscordClient;
-            discordBot.setBridge(mockBridge);
             await discordBot.run();
             const ITERATIONS = 25;
             const CHANID = 123;
@@ -444,7 +456,6 @@ describe("DiscordBot", () => {
     //   const discordBot = new modDiscordBot.DiscordBot(
     //     config,
     //   );
-    //   discordBot.setBridge(mockBridge);
     //   discordBot.run();
     //   it("should reject an unknown room.", () => {
     //     return assert.isRejected(discordBot.OnTyping( {id: "512"}, {id: "12345"}, true));
diff --git a/test/test_matrixmessageprocessor.ts b/test/test_matrixmessageprocessor.ts
index a823c651c285be8f3407472d442a31fa29be22ca..66fced8ee3fe2e238c8ea451dd0aa92924981316 100644
--- a/test/test_matrixmessageprocessor.ts
+++ b/test/test_matrixmessageprocessor.ts
@@ -98,6 +98,13 @@ describe("MatrixMessageProcessor", () => {
             const result = await mp.FormatMessage(msg, guild as any);
             expect(result).is.equal("wow \\\\\\*this\\\\\\* is cool");
         });
+        it("escapes ALL the stuff", async () => {
+            const mp = new MatrixMessageProcessor(bot);
+            const guild = new MockGuild("1234");
+            const msg = getPlainMessage("\\ * _ ~ ` |");
+            const result = await mp.FormatMessage(msg, guild as any);
+            expect(result).is.equal("\\\\ \\* \\_ \\~ \\` \\|");
+        });
     });
     describe("FormatMessage / formatted_body / simple", () => {
         it("leaves blank stuff untouched", async () => {
diff --git a/test/test_matrixroomhandler.ts b/test/test_matrixroomhandler.ts
index 39ce63e026229866d34ec1a05ceaf1aa41c1776b..1aa82ad4d68709af01549310db8aad1134f91c6f 100644
--- a/test/test_matrixroomhandler.ts
+++ b/test/test_matrixroomhandler.ts
@@ -17,15 +17,9 @@ limitations under the License.
 import * as Chai from "chai";
 import * as Proxyquire from "proxyquire";
 import {DiscordBridgeConfig} from "../src/config";
-import {MockDiscordClient} from "./mocks/discordclient";
-import {PresenceHandler} from "../src/presencehandler";
-import {DiscordBot} from "../src/bot";
-import {MatrixRoomHandler} from "../src/matrixroomhandler";
 import {MockChannel} from "./mocks/channel";
 import {MockMember} from "./mocks/member";
-import * as Bluebird from "bluebird";
 import {MockGuild} from "./mocks/guild";
-import {Guild} from "discord.js";
 import { Util } from "../src/util";
 
 // we are a test file and thus need those
@@ -33,10 +27,6 @@ import { Util } from "../src/util";
 
 const expect = Chai.expect;
 
-// const DiscordClientFactory = Proxyquire("../src/clientfactory", {
-//     "discord.js": { Client: require("./mocks/discordclient").MockDiscordClient },
-// }).DiscordClientFactory;
-
 const RoomHandler = (Proxyquire("../src/matrixroomhandler", {
     "./util": {
         Util: {
@@ -112,6 +102,7 @@ function createRH(opts: any = {}) {
         OnUpdate: async () => { },
     };
     const bot = {
+        BotUserId: "@botuser:localhost",
         ChannelSyncroniser: cs,
         GetBotId: () => "bot12345",
         GetChannelFromRoomId: async (roomid: string) => {
@@ -194,8 +185,7 @@ function createRH(opts: any = {}) {
             }
         },
     };
-    const handler = new RoomHandler(bot as any, config, "@botuser:localhost", provisioner as any);
-    handler.setBridge(bridge, {
+    const store = {
         getEntriesByMatrixId: (matrixId) => {
             return [{
                 matrix: {},
@@ -208,7 +198,8 @@ function createRH(opts: any = {}) {
         removeEntriesByMatrixRoomId: () => {
 
         },
-    });
+    }
+    const handler = new RoomHandler(bot as any, config, provisioner as any, bridge as any, store);
     return handler;
 }
 
diff --git a/test/test_provisioner.ts b/test/test_provisioner.ts
index 2d9b9161cd030ece325f6baf1c50ac164013a42b..79f8f71734c2f3da382bd6a8c2093c4f7c90f235 100644
--- a/test/test_provisioner.ts
+++ b/test/test_provisioner.ts
@@ -29,7 +29,7 @@ const TIMEOUT_MS = 1000;
 describe("Provisioner", () => {
     describe("AskBridgePermission", () => {
         it("should fail to bridge a room that timed out", async () => {
-            const p = new Provisioner();
+            const p = new Provisioner({} as any);
             const startAt = Date.now();
             try {
                 await p.AskBridgePermission(
@@ -47,7 +47,7 @@ describe("Provisioner", () => {
             }
         });
         it("should fail to bridge a room that was declined", async () => {
-            const p = new Provisioner();
+            const p = new Provisioner({} as any);
             const promise = p.AskBridgePermission(
                 new MockChannel("foo", "bar") as any,
                 "Mark",
@@ -63,7 +63,7 @@ describe("Provisioner", () => {
 
         });
         it("should bridge a room that was approved", async () => {
-            const p = new Provisioner();
+            const p = new Provisioner({} as any);
             const promise = p.AskBridgePermission(
                 new MockChannel("foo", "bar") as any,
                 "Mark",
diff --git a/tools/chanfix.ts b/tools/chanfix.ts
index ac39a9787f35118838f10abec7f3e64f06d73996..b37b0fe9a35fdc892ed37118e19108497c359612 100644
--- a/tools/chanfix.ts
+++ b/tools/chanfix.ts
@@ -80,9 +80,8 @@ const clientFactory = new ClientFactory({
     token: registration.as_token,
     url: config.bridge.homeserverUrl,
 });
-const provisioner = new Provisioner();
 const discordstore = new DiscordStore(config.database ? config.database.filename : "discord.db");
-const discordbot = new DiscordBot(config, discordstore, provisioner);
+const discordbot = new DiscordBot("", config, null, discordstore);
 
 const bridge = new Bridge({
     clientFactory,
@@ -101,11 +100,9 @@ const bridge = new Bridge({
     userStore: config.database.userStorePath,
 });
 
-discordbot.setBridge(bridge);
-
 async function run() {
+    await bridge.loadDatabases();
     await discordstore.init();
-    provisioner.setStore(discordstore.roomStore);
     bridge._clientFactory = clientFactory;
     bridge._botClient = bridge._clientFactory.getClientAs();
     bridge._botIntent = new Intent(bridge._botClient, bridge._botClient, { registered: true });
@@ -113,23 +110,22 @@ async function run() {
     const client = await discordbot.ClientFactory.getClient();
 
     // first set update_icon to true if needed
-    // first set update_icon to true if needed
-    const mxRoomEntries = await discordstore.roomStore.getEntriesByRemoteRoomData({
+    const mxRoomEntries = await bridge.getRoomStore().getEntriesByRemoteRoomData({
         update_name: true,
         update_topic: true,
     });
 
     const promiseList: Promise<void>[] = [];
     mxRoomEntries.forEach((entry) => {
-        if (entry.remote && entry.remote.get("plumbed")) {
+        if (entry.remote.get("plumbed")) {
             return; // skipping plumbed rooms
         }
-        const updateIcon = entry.remote!.get("update_icon");
+        const updateIcon = entry.remote.get("update_icon");
         if (updateIcon !== undefined && updateIcon !== null) {
             return; // skipping because something was set manually
         }
-        entry.remote!.set("update_icon", true);
-        promiseList.push(discordstore.roomStore.upsertEntry(entry));
+        entry.remote.set("update_icon", true);
+        promiseList.push(bridge.getRoomStore().upsertEntry(entry));
     });
     await Promise.all(promiseList);
 
diff --git a/tools/ghostfix.ts b/tools/ghostfix.ts
index 2da601b3246da84cd53d531daeee9737c0d23786..3e9a4ced371aba06b27820ba0b2c945b7ae85499 100644
--- a/tools/ghostfix.ts
+++ b/tools/ghostfix.ts
@@ -20,15 +20,11 @@ import * as fs from "fs";
 import * as args from "command-line-args";
 import * as usage from "command-line-usage";
 import * as Bluebird from "bluebird";
-import { ChannelSyncroniser } from "../src/channelsyncroniser";
 import { DiscordBridgeConfig } from "../src/config";
-import { DiscordBot } from "../src/bot";
-import { DiscordStore } from "../src/store";
-import { Provisioner } from "../src/provisioner";
-import { UserSyncroniser } from "../src/usersyncroniser";
 import { Log } from "../src/log";
 import { Util } from "../src/util";
-import { TextChannel } from "discord.js";
+import { DiscordBot } from "../src/bot";
+import { DiscordStore } from "../src/store";
 
 const log = new Log("GhostFix");
 
@@ -93,9 +89,6 @@ const clientFactory = new ClientFactory({
     token: registration.as_token,
     url: config.bridge.homeserverUrl,
 });
-const provisioner = new Provisioner();
-const discordstore = new DiscordStore(config.database ? config.database.filename : "discord.db");
-const discordbot = new DiscordBot(config, discordstore, provisioner);
 
 const bridge = new Bridge({
     clientFactory,
@@ -113,14 +106,13 @@ const bridge = new Bridge({
     roomStore: config.database.roomStorePath,
     userStore: config.database.userStorePath,
 });
-discordbot.setBridge(bridge);
 
 async function run() {
-    await discordstore.init();
-    provisioner.setStore(discordstore.roomStore);
-    const userSync = new UserSyncroniser(bridge, config, discordbot);
+    await bridge.loadDatabases();
+    const store = new DiscordStore(config.database);
+    const discordbot = new DiscordBot(botUserId, config, bridge, store);
+    await discordbot.init();
     bridge._clientFactory = clientFactory;
-    await discordbot.ClientFactory.init();
     const client = await discordbot.ClientFactory.getClient();
 
     const promiseList: Promise<void>[] = [];
@@ -136,7 +128,7 @@ async function run() {
                     let currentSchedule = JOIN_ROOM_SCHEDULE[0];
                     const doJoin = async () => {
                         await Util.DelayedPromise(currentSchedule);
-                        await userSync.OnUpdateGuildMember(member, true);
+                        await discordbot.UserSyncroniser.OnUpdateGuildMember(member, true);
                     };
                     const errorHandler = async (err) => {
                         log.error(`Error joining rooms for ${member.id}`);