diff --git a/src/bot.ts b/src/bot.ts
index 5242f2cce2eed15b41b7cd41e35d0ea4ce655b4b..30a0455e35a17b7c80ae3c021365b1b60fbe00e2 100644
--- a/src/bot.ts
+++ b/src/bot.ts
@@ -101,7 +101,7 @@ export class DiscordBot {
             new MatrixEventProcessorOpts(config, bridge, this),
         );
         this.channelSync = new ChannelSyncroniser(bridge, config, this, store.roomStore);
-        this.discordCommandHandler = new DiscordCommandHandler(this);
+        this.discordCommandHandler = new DiscordCommandHandler(bridge, this);
         // init vars
         this.sentMessages = [];
         this.discordMessageQueue = {};
diff --git a/src/discordcommandhandler.ts b/src/discordcommandhandler.ts
index 1d76d544a7158210149bc5f70ad6948ccdc72c90..09d2fa48978fc694066afeb63933a7b990221235 100644
--- a/src/discordcommandhandler.ts
+++ b/src/discordcommandhandler.ts
@@ -1,9 +1,11 @@
 import { DiscordBot } from "./bot";
 import * as Discord from "discord.js";
-import { Uitl, ICommandActions, ICommandParameters } from "./util";
+import { Util, ICommandActions, ICommandParameters } from "./util";
+import { Bridge } from "matrix-appservice-bridge";
 export class DiscordCommandHandler {
     constructor(
-        private discord: DiscordBot;
+        private bridge: Bridge,
+        private discord: DiscordBot,
     ) { }
 
     public async Process(msg: Discord.Message) {
@@ -12,8 +14,6 @@ export class DiscordCommandHandler {
             return;
         }
 
-        const {command, args} = Util.MsgToArgs(msg.content, "!matrix");
-
         const intent = this.bridge.getIntent();
 
         const actions: ICommandActions = {
@@ -48,46 +48,12 @@ export class DiscordCommandHandler {
             },
         };
 
-        if (command === "help") {
-            let replyHelpMessage = "Available Commands:\n";
-            for (const actionKey of Object.keys(actions)) {
-                const action = actions[actionKey];
-                if (!msg.member.hasPermission(action.permission as Discord.PermissionResolvable)) {
-                    continue;
-                }
-                replyHelpMessage += " - `!matrix " + actionKey;
-                for (const param of action.params) {
-                    replyHelpMessage += ` <${param}>`;
-                }
-                replyHelpMessage += `\`: ${action.description}\n`;
-            }
-            replyHelpMessage += "\nParameters:\n";
-            for (const parameterKey of Object.keys(parameters)) {
-                const parameter = parameters[parameterKey];
-                replyHelpMessage += ` - \`<${parameterKey}>\`: ${parameter.description}\n`;
-            }
-            await msg.channel.send(replyHelpMessage);
-            return;
-        }
-
-        if (!actions[command]) {
-            await msg.channel.send("**Error:** unknown command. Try `!matrix help` to see all commands");
-            return;
-        }
-
-        if (!msg.member.hasPermission(actions[command].permission as Discord.PermissionResolvable)) {
-            await msg.channel.send("**ERROR:** insufficiant permissions to use this matrix command");
-            return;
-        }
-
-        let replyMessage = "";
-        try {
-            replyMessage = await Util.ParseCommand(actions[command], parameters, args);
-        } catch (e) {
-            replyMessage = "**ERROR:** " + e.message;
+        const permissionCheck: ICommandPermissonCheck = async (permission) => {
+            return msg.member.hasPermission(permission as Discord.PermissionResolvable);
         }
 
-        await msg.channel.send(replyMessage);
+        const reply = await Util.ParseCommand("!matrix", msg.content, actions, parameters, permissionCheck);
+        await msg.channel.send(reply);
     }
 
     private ModerationActionGenerator(discordChannel: Discord.TextChannel, funcKey: string, action: string) {
diff --git a/src/matrixcommandhandler.ts b/src/matrixcommandhandler.ts
index 36ed7d71c1cfed3f029fb0680e4073e8363555e0..ea1e8e72912265a53516698450ee5595d7be39e2 100644
--- a/src/matrixcommandhandler.ts
+++ b/src/matrixcommandhandler.ts
@@ -4,7 +4,7 @@ import { DiscordBridgeConfig } from "./config";
 import { Bridge, BridgeContext } from "matrix-appservice-bridge";
 import { IMatrixEvent } from "./matrixtypes";
 import { Provisioner } from "./provisioner";
-import { Util } from "./util";
+import { Util, ICommandActions, ICommandParameters, ICommandPermissonCheck } from "./util";
 import * as Discord from "discord.js";
 const log = new Log("MatrixCommandHandler");
 
@@ -42,6 +42,62 @@ export class MatrixCommandHandler {
             return;
         }
 
+        const {command, args} = Util.MsgToArgs(event.content!.body as string, "!discord");
+
+        const actions: ICommandActions = {
+            bridge: {
+                description: "Bridges this room to a Discord channel",
+                params: ["guildid", "channelId"],
+                permission: {
+                    level: PROVISIONING_DEFAULT_POWER_LEVEL,
+                    selfService: true,
+                },
+                run: async ({guildId, channelId}) => {
+                    // TODO: parse guildId/channelId
+
+                },
+            },
+            unbridge: {
+                description: "Unbridges a Discord channel from this room",
+                params: [],
+                permission: {
+                    level: PROVISIONING_DEFAULT_POWER_LEVEL,
+                    selfService: true,
+                },
+                run: async () => {
+
+                }
+            },
+        };
+
+        const parameters: ICommandParameters = {
+            guildId: {
+                description: "The ID of a guild/server on discord",
+            },
+            channelId: {
+                description: "The ID of a channel on discord",
+            },
+        };
+
+        const permissionCheck: ICommandPermissonCheck = async (permission) => {
+            if (permission.selfService && !this.config.bridge.enableSelfServiceBridging) {
+                return false;
+            }
+            const plEvent = await this.bridge.getIntent().getClient()
+                .getStateEvent(event.room_id, "m.room.power_levels", "");
+            let userLevel = PROVISIONING_DEFAULT_USER_POWER_LEVEL;
+            let requiredLevel = permission.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 (!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, {
diff --git a/src/matrixmessageprocessor.ts b/src/matrixmessageprocessor.ts
index 124978d7689fb22620c6ac60d9bf76e8d7217b70..91d7e5ad543f0b2bd4caa48c94972cc95cecd034 100644
--- a/src/matrixmessageprocessor.ts
+++ b/src/matrixmessageprocessor.ts
@@ -84,6 +84,7 @@ export class MatrixMessageProcessor {
         const res: IMatrixEvent = await this.params.mxClient.getStateEvent(
             this.params.roomId, "m.room.power_levels");
 
+        // TODO: utilize default values correctly
         // Some rooms may not have notifications.room set if the value hasn't
         // been changed from the default. If so, use our hardcoded power level.
         const requiredPowerLevel = res && res.notifications && res.notifications.room
diff --git a/src/util.ts b/src/util.ts
index aca4d651a2eb9780ff8d1a5666b389799edb10db..1d308bb04e26cf368627e01253e5a87a38b88f30 100644
--- a/src/util.ts
+++ b/src/util.ts
@@ -27,11 +27,14 @@ const HTTP_OK = 200;
 import { Log } from "./log";
 const log = new Log("Util");
 
+type PERMISSIONTYPES = any; // tslint:disable-line no-any
+
 export interface ICommandAction {
     params: string[];
     description?: string;
-    permission?: string;
+    permission?: PERMISSIONTYPES;
     run(params: any): Promise<any>; // tslint:disable-line no-any
+    help?: string;
 }
 
 export interface ICommandActions {
@@ -40,13 +43,17 @@ export interface ICommandActions {
 
 export interface ICommandParameter {
     description?: string;
-    get(param: string): Promise<any>; // tslint:disable-line no-any
+    get(param: string)?: Promise<any>; // tslint:disable-line no-any
 }
 
 export interface ICommandParameters {
     [index: string]: ICommandParameter;
 }
 
+export interface ICommandPermissonCheck {
+    (permission: PERMISSIONTYPES): Promise<bool>;
+}
+
 export interface IPatternMap {
     [index: string]: string;
 }
@@ -228,19 +235,93 @@ export class Util {
         return Object.keys(matrixUsers)[0];
     }
 
-    public static async ParseCommand(action: ICommandAction, parameters: ICommandParameters, args: string[]) {
+    public static async ParseHelpMessage(
+        prefix: string,
+        actions: ICommandActions,
+        parameters: ICommandParameters,
+        args: string[],
+        permissionCheck: ICommandPermissonCheck?,
+    ): string {
+        let reply = "";
+        if (args[0]) {
+            const actionKey = args[0];
+            const action = actions[actionKey];
+            if (!actions[actionKey]) {
+                return `**ERROR:** unknown command! Try \`${prefix} help\` to see all commands`;
+            }
+            if (action.permission !== undefined && permissionCheck && !(await permissionCheck(action.permission))) {
+                return `**ERROR:** permission denied! Try \`${prefix} help\` to see all available commands`;
+            }
+            reply += `\`${prefix} ${actionKey}`;
+            for (const param of action.params) {
+                reply += ` <${param}>`;
+            }
+            reply += `\`: ${action.description}\n`;
+            if (action.help) {
+                reply += action.help;
+            }
+            return reply;
+        }
+        reply += "Available Commands:\n";
+        for (const actionKey of Object.keys(actions)) {
+            const action = actions[actionKey];
+            if (action.permission !== undefined && permissionCheck && !(await permissionCheck(action.permission))) {
+                continue;
+            }
+            reply += ` - \`${prefix} ${actionKey}`;
+            for (const param of action.params) {
+                reply += ` <${param}>`;
+            }
+            reply += `\`: ${action.description}\n`;
+        }
+        reply += "\nParameters:\n";
+        for (const parameterKey of Object.keys(parameters)) {
+            const parameter = parameters[parameterKey];
+            reply += ` - \`<${parameterKey}>\`: ${parameter.description}\n`;
+        }
+        return reply;
+    }
+
+    public static async ParseCommand(
+        prefix: string,
+        msg: string,
+        actions: ICommandAction[],
+        parameters: ICommandParameters,
+        permissionCheck: ICommandPermissonCheck?,
+    ): string {
+        const {command, args} = Util.MsgToArgs(msg, prefix);
+
+        if (command === "help") {
+            return await Util.ParseHelpMessage(prefix, actions, parameters, args, permissionCheck);
+        }
+
+        if (!actions[command]) {
+            return `**ERROR:** unknown command. Try \`${prefix} help\` to see all commands`;
+        }
+        const action = actions[command];
+        if (action.permission !== undefined && permissionCheck && !permissionCheck(action.permission)) {
+            return `**ERROR:** insufficiant permissions to use this command`;
+        }
         if (action.params.length === 1) {
             args[0] = args.join(" ");
         }
-        const params = {};
-        let i = 0;
-        for (const param of action.params) {
-            params[param] = await parameters[param].get(args[i]);
-            i++;
-        }
+        try {
+            const params = {};
+            let i = 0;
+            for (const param of action.params) {
+                if (parameters[param].get) {
+                    params[param] = await parameters[param].get(args[i]);
+                } else {
+                    params[param] = args[i];
+                }
+                i++;
+            }
 
-        const retStr = await action.run(params);
-        return retStr;
+            const retStr = await action.run(params);
+            return retStr;
+        } catch (e) {
+            return `**ERROR:** ${e.message}`;
+        }
     }
 
     public static MsgToArgs(msg: string, prefix: string) {