diff --git a/src/bot.ts b/src/bot.ts
index c2375e695c2f8a6f165848f340229c794053d4c3..59e4099de4f1d4c00d961e873aa017599ad1f66b 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 PRESENCE_UPDATE_DELAY = 3000;
 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.ProcessMember(newMember); });
       }
       client.on("userUpdate", (_, newUser) => { this.UpdateUser(newUser); });
       client.on("channelUpdate", (_, newChannel) => { this.UpdateRooms(newChannel); });
@@ -74,14 +81,12 @@ 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.BulkPresenceUpdate();
-        return null;
+        this.bot.guilds.forEach((guild) =>{
+            guild.members.forEach((member) => {
+                this.presenceHandler.EnqueueMember(member);
+            })
+        })
+        this.presenceHandler.Start(PRESENCE_UPDATE_DELAY);
       }
     });
   }
@@ -291,7 +296,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(() => {
@@ -441,47 +446,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);
@@ -489,14 +453,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) => {
+        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}`);
@@ -517,7 +482,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);
       }));
@@ -538,7 +503,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.");
@@ -548,7 +512,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) => {
@@ -607,7 +571,7 @@ export class DiscordBot {
         }
         while (storeEvent.Next()) {
           log.info("DiscordBot", `Deleting discord msg ${storeEvent.DiscordId}`);
-          const client = this.bridge.getIntent().getClient();
+          const client = this.GetIntentFromDiscordMember(msg.author);
           const matrixIds = storeEvent.MatrixId.split(";");
           await client.redactEvent(matrixIds[1], matrixIds[0]);
         }
diff --git a/src/presencehandler.ts b/src/presencehandler.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c8286988d279eae7fa587f571e8225f86d939c73
--- /dev/null
+++ b/src/presencehandler.ts
@@ -0,0 +1,102 @@
+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();
+    }
+
+    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(member);
+        if(index !== -1) {
+            this.presenceQueue = this.presenceQueue.splice(this.presenceQueue.findIndex(member));
+        } 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 item = this.presenceQueue.shift();
+        if (item) {
+            if(!this.ProcessMember(item)) {
+                this.presenceQueue.push(item);
+            } 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 = "Do not disturb | " + status.StatusMsg ? status.StatusMsg : "";
+        } else if (presence.status === "offline") {
+            status.Presence = "offline";
+            status.ShouldDrop = true; // Drop until we recieve an update.
+        } else { // idle or dnd
+            status.Presence = "unavailable";
+        }
+        return status;
+    }
+
+    private setMatrixPresence(guildMember: Discord.GuildMember, status: PresenceHandlerStatus) {
+        const intent = this.bot.GetIntentFromDiscordMember(guildMember);
+        intent.getClient().setPresence(status).catch((ex) => {
+            log.warn("PresenceHandler", `Could not update Matrix presence for ${guildMember.id}`);
+        });
+    }
+}