diff --git a/config/config.sample.yaml b/config/config.sample.yaml
index 40dff3da70e471a86b9d017b06d1538e92296521..5f8afd9c6d82ec142044da5c0b55642f38f26f1a 100644
--- a/config/config.sample.yaml
+++ b/config/config.sample.yaml
@@ -1,6 +1,7 @@
 bridge:
   domain: "localhost"
   homeserverUrl: "http://localhost:8008"
+  presenceInterval: 500,
   disablePresence: false
   disableTypingNotifications: false
   disableDiscordMentions: false
diff --git a/config/config.schema.yaml b/config/config.schema.yaml
index 0276b025231dc86bec9cf17305f1ed8391b6f0bf..bdb2bb5cc5c1bf24ccf82adeb82037c8800dc4e2 100644
--- a/config/config.schema.yaml
+++ b/config/config.schema.yaml
@@ -10,6 +10,8 @@ properties:
             type: "string"
           homeserverUrl:
             type: "string"
+          presenceInterval:
+            type: "number"
           disablePresence:
             type: "boolean"
           disableTypingNotifications:
diff --git a/src/bot.ts b/src/bot.ts
index 748f4edc2d52b693c466f84b378d2e5656c62689..f1b9bf7717cf1b1dd5c6bf11aa6123cc0afddbf7 100644
--- a/src/bot.ts
+++ b/src/bot.ts
@@ -6,6 +6,7 @@ import { DbEvent } from "./db/dbdataevent";
 import { MatrixUser, RemoteUser, Bridge, Entry } from "matrix-appservice-bridge";
 import { Util } from "./util";
 import { MessageProcessor, MessageProcessorOpts } from "./messageprocessor";
+import { PresenceHandler } from "./presencehandler";
 import * as Discord from "discord.js";
 import * as log from "npmlog";
 import * as Bluebird from "bluebird";
@@ -15,7 +16,7 @@ import * as path from "path";
 // Due to messages often arriving before we get a response from the send call,
 // messages get delayed from discord.
 const MSG_PROCESS_DELAY = 750;
-const PRESENCE_UPDATE_DELAY = 55000; // Synapse updates in 55 second intervals.
+const MIN_PRESENCE_UPDATE_DELAY = 250;
 class ChannelLookupResult {
   public channel: Discord.TextChannel;
   public botUser: boolean;
@@ -30,6 +31,7 @@ export class DiscordBot {
   private presenceInterval: any;
   private sentMessages: string[];
   private msgProcessor: MessageProcessor;
+  private presenceHandler: PresenceHandler;
   constructor(config: DiscordBridgeConfig, store: DiscordStore) {
     this.config = config;
     this.store = store;
@@ -39,6 +41,7 @@ export class DiscordBot {
       new MessageProcessorOpts(this.config.bridge.domain),
       this,
     );
+    this.presenceHandler = new PresenceHandler(this);
   }
 
   public setBridge(bridge: Bridge) {
@@ -49,6 +52,10 @@ export class DiscordBot {
      return this.clientFactory;
   }
 
+  public GetIntentFromDiscordMember(member: Discord.GuildMember | Discord.User): any {
+      return this.bridge.getIntentFromLocalpart(`_discord_${member.id}`);
+  }
+
   public run (): Promise<null> {
     return this.clientFactory.init().then(() => {
       return this.clientFactory.getClient();
@@ -58,7 +65,7 @@ export class DiscordBot {
         client.on("typingStop", (c, u) => { this.OnTyping(c, u, false);  });
       }
       if (!this.config.bridge.disablePresence) {
-        client.on("presenceUpdate", (_, newMember) => { this.UpdatePresence(newMember); });
+        client.on("presenceUpdate", (_, newMember) => { this.presenceHandler.EnqueueMember(newMember); });
       }
       client.on("userUpdate", (_, newUser) => { this.UpdateUser(newUser); });
       client.on("channelUpdate", (_, newChannel) => { this.UpdateRooms(newChannel); });
@@ -74,14 +81,14 @@ export class DiscordBot {
       this.bot = client;
 
       if (!this.config.bridge.disablePresence) {
-        /* Currently synapse sadly times out presence after a minute.
-         * This will set the presence for each user who is not offline */
-        this.presenceInterval = setInterval(
-          this.BulkPresenceUpdate.bind(this),
-          PRESENCE_UPDATE_DELAY,
+        this.bot.guilds.forEach((guild) => {
+            guild.members.forEach((member) => {
+                this.presenceHandler.EnqueueMember(member);
+            });
+        });
+        this.presenceHandler.Start(
+            Math.max(this.config.bridge.presenceInterval, MIN_PRESENCE_UPDATE_DELAY),
         );
-        this.BulkPresenceUpdate();
-        return null;
       }
     });
   }
@@ -236,7 +243,7 @@ export class DiscordBot {
 
   public async ProcessMatrixRedact(event: any) {
     if (this.config.bridge.disableDeletionForwarding) {
-      return
+      return;
     }
     log.info("DiscordBot", `Got redact request for ${event.redacts}`);
     log.verbose("DiscordBot", `Event:`, event);
@@ -294,7 +301,7 @@ export class DiscordBot {
   }
 
   public InitJoinUser(member: Discord.GuildMember, roomIds: string[]): Promise<any> {
-    const intent = this.bridge.getIntentFromLocalpart(`_discord_${member.id}`);
+    const intent = this.GetIntentFromDiscordMember(member);
     return this.UpdateUser(member.user).then(() => {
       return Bluebird.each(roomIds, (roomId) => intent.join(roomId));
     }).then(() => {
@@ -444,47 +451,6 @@ export class DiscordBot {
     });
   }
 
-  private BulkPresenceUpdate() {
-    if (this.config.bridge.disablePresence) {
-      return; // skip if there's nothing to do
-    }
-
-    log.verbose("DiscordBot", "Bulk presence update");
-    const members = [];
-    for (const guild of this.bot.guilds.values()) {
-      for (const member of guild.members.array().filter((m) => members.indexOf(m.id) === -1)) {
-        /* We ignore offline because they are likely to have been set
-         * by a 'presenceUpdate' event or will timeout. This saves
-         * some work on the HS */
-        if (member.presence.status !== "offline") {
-          this.UpdatePresence(member);
-        }
-        members.push(member.id);
-      }
-    }
-  }
-
-  private UpdatePresence(guildMember: Discord.GuildMember) {
-    if (this.config.bridge.disablePresence) {
-      return; // skip if there's nothing to do
-    }
-
-    const intent = this.bridge.getIntentFromLocalpart(`_discord_${guildMember.id}`);
-    try {
-      const presence: any = {};
-      presence.presence = guildMember.presence.status;
-      if (presence.presence === "idle" || presence.presence === "dnd") {
-        presence.presence = "unavailable";
-      }
-      if (guildMember.presence.game) {
-        presence.status_msg = "Playing " + guildMember.presence.game.name;
-      }
-      intent.getClient().setPresence(presence);
-    } catch (err) {
-      log.info("DiscordBot", "Couldn't set presence ", err);
-    }
-  }
-
   private AddGuildMember(guildMember: Discord.GuildMember) {
     return this.GetRoomIdsFromGuild(guildMember.guild.id).then((roomIds) => {
       return this.InitJoinUser(guildMember, roomIds);
@@ -492,14 +458,15 @@ export class DiscordBot {
   }
 
   private RemoveGuildMember(guildMember: Discord.GuildMember) {
-    const intent = this.bridge.getIntentFromLocalpart(`_discord_${guildMember.id}`);
+    const intent = this.GetIntentFromDiscordMember(guildMember);
     return Bluebird.each(this.GetRoomIdsFromGuild(guildMember.guild.id), (roomId) => {
-      return intent.leave(roomId);
+        this.presenceHandler.DequeueMember(guildMember);
+        return intent.leave(roomId);
     });
   }
 
   private UpdateGuildMember(guildMember: Discord.GuildMember, roomIds?: string[]) {
-    const client = this.bridge.getIntentFromLocalpart(`_discord_${guildMember.id}`).getClient();
+    const client = this.GetIntentFromDiscordMember(guildMember).getClient();
     const userId = client.credentials.userId;
     let avatar = null;
     log.info(`Updating nick for ${guildMember.user.username}`);
@@ -520,7 +487,7 @@ export class DiscordBot {
 
   private OnTyping(channel: Discord.Channel, user: Discord.User, isTyping: boolean) {
     this.GetRoomIdsFromChannel(channel).then((rooms) => {
-      const intent = this.bridge.getIntentFromLocalpart(`_discord_${user.id}`);
+      const intent = this.GetIntentFromDiscordMember(user);
       return Promise.all(rooms.map((room) => {
         return intent.sendTyping(room, isTyping);
       }));
@@ -541,7 +508,6 @@ export class DiscordBot {
       return;
     }
     // Update presence because sometimes discord misses people.
-    this.UpdatePresence(msg.member);
     this.UpdateUser(msg.author).then(() => {
       return this.GetRoomIdsFromChannel(msg.channel).catch((err) => {
         log.verbose("DiscordBot", "No bridged rooms to send message to. Oh well.");
@@ -551,7 +517,7 @@ export class DiscordBot {
       if (rooms === null) {
         return null;
       }
-      const intent = this.bridge.getIntentFromLocalpart(`_discord_${msg.author.id}`);
+      const intent = this.GetIntentFromDiscordMember(msg.author);
       // Check Attachements
       msg.attachments.forEach((attachment) => {
         Util.UploadContentFromUrl(attachment.url, intent, attachment.filename).then((content) => {
@@ -601,21 +567,18 @@ export class DiscordBot {
     });
   }
 
-  private async DeleteDiscordMessage(msg: Discord.Message) {
-    if (this.config.bridge.disableDeletionForwarding) {
-      return;
-    }
-    log.info("DiscordBot", `Got delete event for ${msg.id}`);
-    const storeEvent = await this.store.Get(DbEvent, {discord_id: msg.id});
-    if (!storeEvent.Result) {
-      log.warn("DiscordBot", `Could not redact because the event was in the store.`);
-      return;
-    }
-    while (storeEvent.Next()) {
-      log.info("DiscordBot", `Deleting discord msg ${storeEvent.DiscordId}`);
-      const client = this.bridge.getIntent().getClient();
-      const matrixIds = storeEvent.MatrixId.split(";");
-      await client.redactEvent(matrixIds[1], matrixIds[0]);
+    private async DeleteDiscordMessage(msg: Discord.Message) {
+        log.info("DiscordBot", `Got delete event for ${msg.id}`);
+        const storeEvent = await this.store.Get(DbEvent, {discord_id: msg.id});
+        if (!storeEvent.Result) {
+          log.warn("DiscordBot", `Could not redact because the event was in the store.`);
+          return;
+        }
+        while (storeEvent.Next()) {
+          log.info("DiscordBot", `Deleting discord msg ${storeEvent.DiscordId}`);
+          const client = this.GetIntentFromDiscordMember(msg.author);
+          const matrixIds = storeEvent.MatrixId.split(";");
+          await client.redactEvent(matrixIds[1], matrixIds[0]);
+        }
     }
   }
-}
diff --git a/src/config.ts b/src/config.ts
index 6bb0025689b308be5bea0e7b62aab635873aa396..cf144ca32abef2f57339e72c4430e56bde908079 100644
--- a/src/config.ts
+++ b/src/config.ts
@@ -11,6 +11,7 @@ export class DiscordBridgeConfig {
 class DiscordBridgeConfigBridge {
   public domain: string;
   public homeserverUrl: string;
+  public presenceInterval: number = 500;
   public disablePresence: boolean;
   public disableTypingNotifications: boolean;
   public disableDiscordMentions: boolean;
diff --git a/src/presencehandler.ts b/src/presencehandler.ts
new file mode 100644
index 0000000000000000000000000000000000000000..4db081439521a13d95ac8b2ce9de7523a490b13e
--- /dev/null
+++ b/src/presencehandler.ts
@@ -0,0 +1,115 @@
+import * as Discord from "discord.js";
+import * as log from "npmlog";
+import { DiscordBot } from "./bot";
+
+export class PresenceHandlerStatus {
+    /* One of: ["online", "offline", "unavailable"] */
+    public Presence: string;
+    public StatusMsg: string;
+    public ShouldDrop: boolean = false;
+}
+
+export class PresenceHandler {
+    private readonly bot: DiscordBot;
+    private presenceQueue: Discord.GuildMember[];
+    private interval: number;
+    constructor (bot: DiscordBot) {
+        this.bot = bot;
+        this.presenceQueue = new Array();
+    }
+
+    get QueueCount (): number {
+        return this.presenceQueue.length;
+    }
+
+    public Start(intervalTime: number) {
+        if (this.interval) {
+            log.info("PresenceHandler", "Restarting presence handler...");
+            this.Stop();
+        }
+        log.info("PresenceHandler", `Starting presence handler with new interval ${intervalTime}ms`);
+        this.interval = setInterval(this.processIntervalThread.bind(this), intervalTime);
+    }
+
+    public Stop() {
+        if (!this.interval) {
+            log.info("PresenceHandler", "Can not stop interval, not running.");
+        }
+        log.info("PresenceHandler", "Stopping presence handler");
+        clearInterval(this.interval);
+        this.interval = null;
+    }
+
+    public EnqueueMember(member: Discord.GuildMember) {
+        if (!this.presenceQueue.includes(member)) {
+            log.info("PresenceHandler", `Adding ${member.id} (${member.user.username}) to the presence queue`);
+            this.presenceQueue.push(member);
+        }
+    }
+
+    public DequeueMember(member: Discord.GuildMember) {
+        const index = this.presenceQueue.findIndex((item) => {
+            return member === item;
+        });
+        if (index !== -1) {
+            this.presenceQueue.splice(index, 1);
+        } else {
+            log.warn(
+                "PresenceHandler",
+                `Tried to remove ${member.id} from the presence queue but it could not be found`,
+            );
+        }
+    }
+
+    public ProcessMember(member: Discord.GuildMember): boolean {
+        const status = this.getUserPresence(member.presence);
+        this.setMatrixPresence(member, status);
+        return status.ShouldDrop;
+    }
+
+    private processIntervalThread() {
+        const member = this.presenceQueue.shift();
+        if (member) {
+            if (!this.ProcessMember(member)) {
+                this.presenceQueue.push(member);
+            } else {
+                log.info("PresenceHandler", `Dropping ${member.id} from the presence queue.`);
+            }
+        }
+    }
+
+    private getUserPresence(presence: Discord.Presence): PresenceHandlerStatus {
+        const status = new PresenceHandlerStatus();
+
+        if (presence.game) {
+            status.StatusMsg = `${presence.game.streaming ? "Streaming" : "Playing"} ${presence.game.name}`;
+            if (presence.game.url) {
+                status.StatusMsg += ` | ${presence.game.url}`;
+            }
+        }
+
+        if (presence.status === "online") {
+            status.Presence = "online";
+        } else if (presence.status === "dnd") {
+            status.Presence = "online";
+            status.StatusMsg = status.StatusMsg ? "Do not disturb | " + status.StatusMsg : "Do not disturb";
+        } else if (presence.status === "offline") {
+            status.Presence = "offline";
+            status.ShouldDrop = true; // Drop until we recieve an update.
+        } else { // idle
+            status.Presence = "unavailable";
+        }
+        return status;
+    }
+
+    private setMatrixPresence(guildMember: Discord.GuildMember, status: PresenceHandlerStatus) {
+        const intent = this.bot.GetIntentFromDiscordMember(guildMember);
+        const statusObj: any = {presence: status.Presence};
+        if (status.StatusMsg) {
+            statusObj.status_msg = status.StatusMsg;
+        }
+        intent.getClient().setPresence(statusObj).catch((ex) => {
+            log.warn("PresenceHandler", `Could not update Matrix presence for ${guildMember.id}`);
+        });
+    }
+}
diff --git a/test/mocks/member.ts b/test/mocks/member.ts
index c4f4b442edd0c02d73e3089ba0c741cf4471c2e1..b614c9650032effe5f9b855550bcaddace0942f4 100644
--- a/test/mocks/member.ts
+++ b/test/mocks/member.ts
@@ -1,11 +1,17 @@
 import {MockUser} from "./user";
+import * as Discord from "discord.js";
 
 export class MockMember {
   public id = "";
-  public presence = {status: "offline"}; // TODO: Mock this
+  public presence: Discord.Presence;
   public user: MockUser;
   constructor(id: string, username: string) {
     this.id = id;
+    this.presence = new Discord.Presence({});
     this.user = new MockUser(this.id, username);
   }
+
+  public MockSetPresence(presence: Discord.Presence) {
+      this.presence = presence;
+  }
 }
diff --git a/test/test_presencehandler.ts b/test/test_presencehandler.ts
new file mode 100644
index 0000000000000000000000000000000000000000..963397f998e091ad867eabd5fbf3bb1ab025e59c
--- /dev/null
+++ b/test/test_presencehandler.ts
@@ -0,0 +1,172 @@
+import * as Chai from "chai";
+import * as ChaiAsPromised from "chai-as-promised";
+import * as log from "npmlog";
+import * as Discord from "discord.js";
+import * as Proxyquire from "proxyquire";
+
+// import * as Proxyquire from "proxyquire";
+import { PresenceHandler } from "../src/presencehandler";
+import { DiscordBot } from "../src/bot";
+import { MockGuild } from "./mocks/guild";
+import { MockMember } from "./mocks/member";
+
+Chai.use(ChaiAsPromised);
+const expect = Chai.expect;
+const INTERVAL = 250;
+let lastStatus = null;
+// const assert = Chai.assert;
+const bot = {
+    GetIntentFromDiscordMember: (member) => {
+        return {
+            getClient: () => {
+                return {
+                    setPresence: (status) => {
+                        lastStatus = status;
+                        return Promise.resolve();
+                    },
+                };
+            },
+        };
+    },
+};
+
+describe("PresenceHandler", () => {
+    describe("init", () => {
+        it("constructor", () => {
+            const handler = new PresenceHandler(<DiscordBot> bot);
+        });
+    });
+    describe("Start", () => {
+        it("should start without errors", () => {
+            const handler = new PresenceHandler(<DiscordBot> bot);
+            handler.Start(INTERVAL);
+        });
+    });
+    describe("Stop", () => {
+        it("should stop without errors", () => {
+            const handler = new PresenceHandler(<DiscordBot> bot);
+            handler.Start(INTERVAL);
+            handler.Stop();
+        });
+    });
+    describe("EnqueueMember", () => {
+        it("adds a user properly", () => {
+            const handler = new PresenceHandler(<DiscordBot> bot);
+            const COUNT = 2;
+            handler.EnqueueMember(<any> new MockMember("abc", "def"));
+            handler.EnqueueMember(<any> new MockMember("abc", "ghi"));
+            Chai.assert.equal(handler.QueueCount, COUNT);
+        });
+        it("does not add duplicate users", () => {
+            const handler = new PresenceHandler(<DiscordBot> bot);
+            const member = <any> new MockMember("abc", "def");
+            handler.EnqueueMember(member);
+            handler.EnqueueMember(member);
+            Chai.assert.equal(handler.QueueCount, 1);
+        });
+    });
+    describe("DequeueMember", () => {
+        it("removes users properly", () => {
+            const handler = new PresenceHandler(<DiscordBot> bot);
+            const members = [
+                <any> new MockMember("abc", "def"),
+                <any> new MockMember("abc", "ghi"),
+                <any> new MockMember("abc", "wew"),
+            ];
+            handler.EnqueueMember(members[0]);
+            handler.EnqueueMember(members[1]);
+            handler.EnqueueMember(members[members.length - 1]);
+
+            handler.DequeueMember(members[members.length - 1]);
+            Chai.assert.equal(handler.QueueCount, members.length - 1);
+            handler.DequeueMember(members[1]);
+            Chai.assert.equal(handler.QueueCount, 1);
+            handler.DequeueMember(members[0]);
+            Chai.assert.equal(handler.QueueCount, 0);
+        });
+    });
+    describe("ProcessMember", () => {
+        it("processes an online user", () => {
+            lastStatus = null;
+            const handler = new PresenceHandler(<DiscordBot> bot);
+            const member = <any> new MockMember("abc", "def");
+            member.MockSetPresence(new Discord.Presence({
+                status: "online",
+            }));
+            handler.ProcessMember(member);
+            Chai.assert.deepEqual(lastStatus, {
+                presence: "online",
+            });
+        });
+        it("processes an offline user", () => {
+            lastStatus = null;
+            const handler = new PresenceHandler(<DiscordBot> bot);
+            const member = <any> new MockMember("abc", "def");
+            member.MockSetPresence(new Discord.Presence({
+                status: "offline",
+            }));
+            handler.ProcessMember(member);
+            Chai.assert.deepEqual(lastStatus, {
+                presence: "offline",
+            });
+
+        });
+        it("processes an idle user", () => {
+            lastStatus = null;
+            const handler = new PresenceHandler(<DiscordBot> bot);
+            const member = <any> new MockMember("abc", "def");
+            member.MockSetPresence(new Discord.Presence({
+                status: "idle",
+            }));
+            handler.ProcessMember(member);
+            Chai.assert.deepEqual(lastStatus, {
+                presence: "unavailable",
+            });
+        });
+        it("processes an dnd user", () => {
+            lastStatus = null;
+            const handler = new PresenceHandler(<DiscordBot> bot);
+            const member = <any> new MockMember("abc", "def");
+            member.MockSetPresence(new Discord.Presence({
+                status: "dnd",
+            }));
+            handler.ProcessMember(member);
+            Chai.assert.deepEqual(lastStatus, {
+                presence: "online",
+                status_msg: "Do not disturb",
+            });
+            member.MockSetPresence(new Discord.Presence({
+                status: "dnd",
+                game: new Discord.Game({name: "Test Game"}),
+            }));
+            handler.ProcessMember(member);
+            Chai.assert.deepEqual(lastStatus, {
+                presence: "online",
+                status_msg: "Do not disturb | Playing Test Game",
+            });
+        });
+        it("processes a user playing games", () => {
+            lastStatus = null;
+            const handler = new PresenceHandler(<DiscordBot> bot);
+            const member = <any> new MockMember("abc", "def");
+            member.MockSetPresence(new Discord.Presence({
+                status: "online",
+                game: new Discord.Game({name: "Test Game"}),
+            }));
+            handler.ProcessMember(member);
+            Chai.assert.deepEqual(lastStatus, {
+                presence: "online",
+                status_msg: "Playing Test Game",
+            });
+            member.MockSetPresence(new Discord.Presence({
+                status: "online",
+                game: new Discord.Game({name: "Test Game", type: 1}),
+            }));
+            handler.ProcessMember(member);
+            Chai.assert.deepEqual(lastStatus, {
+                presence: "online",
+                status_msg: "Streaming Test Game",
+            });
+        });
+    });
+});
diff --git a/tsconfig.json b/tsconfig.json
index 9a10f43fdd90e5b671faf50168667ad0c5dcb23d..acfe5f9eac7b63efed68e5a5cbbe729b692dce3f 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -2,7 +2,7 @@
     "compilerOptions": {
         "module": "commonjs",
         "moduleResolution": "node",
-        "target": "ES6",
+        "target": "es2016",
         "noImplicitAny": false,
         "sourceMap": false,
         "outDir": "./build",