diff --git a/src/bot.ts b/src/bot.ts
index daaceb10d245fb2fd9f88ccd453f313c510424a0..107fad21f993ca86f0925e29abab2b77b0171dde 100644
--- a/src/bot.ts
+++ b/src/bot.ts
@@ -446,19 +446,23 @@ export class DiscordBot {
         }
     }
 
-    public async GetChannelFromRoomId(roomId: string): Promise<Discord.Channel> {
+    public async GetChannelFromRoomId(roomId: string, client?: Discord.Client): Promise<Discord.Channel> {
         const entries = await this.bridge.getRoomStore().getEntriesByMatrixId(
             roomId,
         );
 
+        if (!client) {
+            client = this.bot;
+        }
+
         if (entries.length === 0) {
             log.verbose(`Couldn"t find channel for roomId ${roomId}.`);
             throw Error("Room(s) not found.");
         }
         const entry = entries[0];
-        const guild = this.bot.guilds.get(entry.remote.get("discord_guild"));
+        const guild = client.guilds.get(entry.remote.get("discord_guild"));
         if (guild) {
-            const channel = this.bot.channels.get(entry.remote.get("discord_channel"));
+            const channel = client.channels.get(entry.remote.get("discord_channel"));
             if (channel) {
                 return channel;
             }
@@ -499,6 +503,60 @@ export class DiscordBot {
         return rooms.map((room) => room.matrix.getId());
     }
 
+    public async handleMatrixKickBan(roomId: string, kickeeUserId: string, kicker: string, kickban: "kick"|"ban", reason?: string) {
+        const kickeeUser = (await this.GetDiscordUserOrMember(
+            new MatrixUser(kickeeUserId.replace("@", "")).localpart.substring("_discord".length)
+        ))!;
+        if (!kickeeUser || kickeeUser instanceof Discord.User) {
+            log.error("Could not find discord user for", kicker);
+            return;
+        }
+        const kickee = kickeeUser as Discord.GuildMember;
+        const client = await this.clientFactory.getClient(kicker);
+        let channel: Discord.Channel;
+        try {
+            channel = await this.GetChannelFromRoomId(roomId, client);
+        } catch (ex) {
+            log.error("Failed to get channel for ", roomId, ex);
+            return;
+        }
+        if (channel.type !== "text") {
+            log.warn("Channel was not a text channel");
+            return;
+        }
+        const tchan = (channel as Discord.TextChannel);
+        const existingPerms = tchan.memberPermissions(kickee);
+        if (existingPerms && existingPerms.has(Discord.Permissions.FLAGS.VIEW_CHANNEL as number) === false ) {
+            log.warn("User isn't allowed to read anyway.");
+            return;
+        }
+        await tchan.send(
+            `${kickee} was ${kickban === "ban" ? "banned" : "kicked"} from this channel by ${kickeeUserId}.`
+            + (reason ? ` Reason: ${reason}` : "")
+        );
+        log.info(`${kickban === "ban" ? "Banning" : "Kicking"} ${kickee}`);
+
+        await tchan.overwritePermissions(kickee,
+            {
+              VIEW_CHANNEL: false,
+              SEND_MESSAGES: false,
+            },
+            `Matrix user was ${kickban} by ${kicker}`);
+        if (kickban === "kick") {
+            // Kicks will let the user back in after ~30 seconds.
+            setTimeout(async () => {
+                log.info(`Kick was lifted for ${kickee.displayName}`);
+                await tchan.overwritePermissions(kickee,
+                    {
+                      VIEW_CHANNEL: null,
+                      SEND_MESSAGES: null,
+                  } as any, // XXX: Discord.js typings are wrong.
+                    `Lifting kick for since duration expired.`);
+            }, this.config.room.kickFor);
+        }
+
+    }
+
     private async SendMatrixMessage(matrixMsg: MessageProcessorMatrixResult, chan: Discord.Channel,
                                     guild: Discord.Guild, author: Discord.User,
                                     msgID: string): Promise<boolean> {
diff --git a/src/clientfactory.ts b/src/clientfactory.ts
index b3b9a907447397b054bdbfd6e01ef65c62c96e26..fe4828254917734d12b4db8fb63feeb45983e317 100644
--- a/src/clientfactory.ts
+++ b/src/clientfactory.ts
@@ -3,7 +3,6 @@ import { DiscordStore } from "./store";
 import { Client as DiscordClient } from "discord.js";
 import * as Bluebird from "bluebird";
 import { Log } from "./log";
-import { Client as MatrixClient } from "matrix-js-sdk";
 
 const log = new Log("ClientFactory");
 
@@ -12,8 +11,8 @@ const READY_TIMEOUT = 5000;
 export class DiscordClientFactory {
     private config: DiscordBridgeConfigAuth;
     private store: DiscordStore;
-    private botClient: MatrixClient;
-    private clients: Map<string, MatrixClient>;
+    private botClient: DiscordClient;
+    private clients: Map<string, DiscordClient>;
     constructor(store: DiscordStore, config?: DiscordBridgeConfigAuth) {
         this.config = config!;
         this.clients = new Map();
@@ -62,13 +61,13 @@ export class DiscordClientFactory {
         });
     }
 
-    public async getClient(userId: string | null = null): Promise<MatrixClient> {
+    public async getClient(userId: string | null = null): Promise<DiscordClient> {
         if (userId === null) {
             return this.botClient;
         }
         if (this.clients.has(userId)) {
             log.verbose("Returning cached user client for", userId);
-            return this.clients.get(userId);
+            return this.clients.get(userId) as DiscordClient;
         }
         const discordIds = await this.store.get_user_discord_ids(userId);
         if (discordIds.length === 0) {
@@ -76,11 +75,11 @@ export class DiscordClientFactory {
         }
         // TODO: Select a profile based on preference, not the first one.
         const token = await this.store.get_token(discordIds[0]);
-        const client = Bluebird.promisifyAll(new DiscordClient({
+        const client = new DiscordClient({
             fetchAllMembers: true,
             messageCacheLifetime: 5,
             sync: true,
-        }));
+        });
         const jsLog = new Log("discord.js-ppt");
         client.on("debug", (msg) => { jsLog.verbose(msg); });
         client.on("error", (msg) => { jsLog.error(msg); });
diff --git a/src/config.ts b/src/config.ts
index c5dfb88b5bba1b70d81cd647da0e8f8923381211..e9cec8ac0ef78f63d2e545e5beeec64e2141de2a 100644
--- a/src/config.ts
+++ b/src/config.ts
@@ -59,6 +59,7 @@ export class DiscordBridgeConfigLogging {
 
 class DiscordBridgeConfigRoom {
     public defaultVisibility: string;
+    public kickFor:number = 30000;
 }
 
 class DiscordBridgeConfigChannel {
diff --git a/src/matrixroomhandler.ts b/src/matrixroomhandler.ts
index 885c687b1c86a816ecf2a5b8153ee6d12fc662f0..8df1d5f4b4d364955f5171f92e45e6cd8f3881bf 100644
--- a/src/matrixroomhandler.ts
+++ b/src/matrixroomhandler.ts
@@ -123,11 +123,17 @@ export class MatrixRoomHandler {
         if (event.type === "m.room.member" && event.content!.membership === "invite") {
             await this.HandleInvite(event);
             return;
-        } else if (event.type === "m.room.member" && event.content!.membership === "join") {
-            if (this.bridge.getBot().isRemoteUser(event.state_key)) {
+        } else if (event.type === "m.room.member" && this.bridge.getBot().isRemoteUser(event.state_key)) {
+            if (event.content!.membership !== undefined && event.content!.membership === "join") {
                 await this.discord.UserSyncroniser.OnMemberState(event, USERSYNC_STATE_DELAY_MS);
-            } else {
-                await this.discord.ProcessMatrixStateEvent(event);
+            } else if (["kick", "ban"].includes(event.content!.membership!)) {
+                // Kick/Ban handling
+                await this.discord.handleMatrixKickBan(
+                    event.room_id,
+                    event.state_key,
+                    event.sender,
+                    event.content!.membership as "kick"|"ban",
+                );
             }
             return;
         } else if (["m.room.member", "m.room.name", "m.room.topic"].includes(event.type)) {
diff --git a/src/matrixtypes.ts b/src/matrixtypes.ts
index 4efbf8395084b61ef852d59886b6eb5e9cb5058e..3e678dcd477b2a7668b72761f5fd7409bd341885 100644
--- a/src/matrixtypes.ts
+++ b/src/matrixtypes.ts
@@ -7,6 +7,7 @@ export interface IMatrixEventContent {
     msgtype?: string;
     url?: string;
     displayname?: string;
+    reason?: string;
     "m.relates_to"?: any; // tslint:disable-line no-any
 }