From abc3f9e2fa8d3e1c788186857bdbffbbdeca4e89 Mon Sep 17 00:00:00 2001 From: hydrabolt Date: Sun, 17 Apr 2016 18:20:57 +0100 Subject: [PATCH] Presence tracking --- src/client/websocket/WebSocketManager.js | 1 + .../packets/WebSocketPacketManager.js | 23 ++++++ .../websocket/packets/handlers/GuildDelete.js | 1 + .../packets/handlers/PresenceUpdate.js | 77 +++++++++++++++++++ src/structures/Guild.js | 24 +++++- src/structures/User.js | 2 + src/util/Constants.js | 2 + test/random.js | 4 + 8 files changed, 130 insertions(+), 4 deletions(-) create mode 100644 src/client/websocket/packets/handlers/PresenceUpdate.js diff --git a/src/client/websocket/WebSocketManager.js b/src/client/websocket/WebSocketManager.js index ae9b8c550..41c7ea407 100644 --- a/src/client/websocket/WebSocketManager.js +++ b/src/client/websocket/WebSocketManager.js @@ -71,6 +71,7 @@ class WebSocketManager { if (unavailableCount === 0) { this.client.emit(Constants.Events.READY); this.emittedReady = true; + this.packetManager.handleQueue(); } } } diff --git a/src/client/websocket/packets/WebSocketPacketManager.js b/src/client/websocket/packets/WebSocketPacketManager.js index 2cf3c2149..a64a7bac7 100644 --- a/src/client/websocket/packets/WebSocketPacketManager.js +++ b/src/client/websocket/packets/WebSocketPacketManager.js @@ -2,11 +2,18 @@ const Constants = require('../../../util/Constants'); +const BeforeReadyWhitelist = [ + Constants.WSEvents.READY, + Constants.WSEvents.GUILD_CREATE, + Constants.WSEvents.GUILD_DELETE, +]; + class WebSocketPacketManager { constructor(websocketManager) { this.ws = websocketManager; this.handlers = {}; + this.queue = []; this.register(Constants.WSEvents.READY, 'Ready'); this.register(Constants.WSEvents.GUILD_CREATE, 'GuildCreate'); @@ -23,6 +30,7 @@ class WebSocketPacketManager { this.register(Constants.WSEvents.CHANNEL_CREATE, 'ChannelCreate'); this.register(Constants.WSEvents.CHANNEL_DELETE, 'ChannelDelete'); this.register(Constants.WSEvents.CHANNEL_UPDATE, 'ChannelUpdate'); + this.register(Constants.WSEvents.PRESENCE_UPDATE, 'PresenceUpdate'); } get client() { @@ -34,7 +42,22 @@ class WebSocketPacketManager { this.handlers[event] = new Handler(this); } + handleQueue() { + for (let packetIndex in this.queue) { + this.handle(this.queue[packetIndex]); + this.queue.splice(packetIndex, 1); + } + } + handle(packet) { + + if (!this.ws.emittedReady) { + if (BeforeReadyWhitelist.indexOf(packet.t) === -1) { + this.queue.push(packet); + return; + } + } + if (this.handlers[packet.t]) { return this.handlers[packet.t].handle(packet); } diff --git a/src/client/websocket/packets/handlers/GuildDelete.js b/src/client/websocket/packets/handlers/GuildDelete.js index 550628ac0..350c27b1c 100644 --- a/src/client/websocket/packets/handlers/GuildDelete.js +++ b/src/client/websocket/packets/handlers/GuildDelete.js @@ -28,6 +28,7 @@ class GuildDeleteHandler extends AbstractHandler { } else { // delete guild client.store.KillGuild(guild); + this.packetManager.ws.checkIfReady(); } } else { // it's not there! :( diff --git a/src/client/websocket/packets/handlers/PresenceUpdate.js b/src/client/websocket/packets/handlers/PresenceUpdate.js new file mode 100644 index 000000000..c472a413c --- /dev/null +++ b/src/client/websocket/packets/handlers/PresenceUpdate.js @@ -0,0 +1,77 @@ +'use strict'; + +const AbstractHandler = require('./AbstractHandler'); +const Structure = name => require(`../../../../structures/${name}`); +const Constants = require('../../../../util/Constants'); +const CloneObject = require('../../../../util/CloneObject'); + +const Role = Structure('User'); + +class PresenceUpdateHandler extends AbstractHandler { + + constructor(packetManager) { + super(packetManager); + } + + handle(packet) { + let data = packet.d; + let client = this.packetManager.client; + let user = client.store.get('users', data.user.id); + let guild = client.store.get('guilds', data.guild_id); + + function makeUser(user) { + return client.store.NewUser(user); + } + + // step 1 + if (!user) { + if (data.user.username) { + user = makeUser(data.user); + }else { + return; + } + } + + if (guild) { + let memberInGuild = guild.store.get('members', user.id); + if (!memberInGuild) { + let member = guild._addMember({ + user, + roles: data.roles, + deaf: false, + mute: false, + }, true); + client.emit(Constants.Events.GUILD_MEMBER_AVAILABLE, guild, member); + } + } + + data.user.username = data.user.username || user.username; + data.user.id = data.user.id || user.id; + data.user.discriminator = data.user.discriminator || user.discriminator; + data.user.avatar = data.user.avatar || user.avatar; + data.user.status = data.status || user.status; + data.user.game = data.game; + + let same = ( + data.user.username === user.username && + data.user.id === user.id && + data.user.discriminator === user.discriminator && + data.user.avatar === user.avatar && + data.user.status === user.status && + !( + (data.user.game && !user.game) || + (!data.user.game && user.game) || + (data.user.game && user.game && data.user.game.name !== user.game.name) + ) + ); + + if (!same) { + let oldUser = CloneObject(user); + user.setup(data.user); + client.emit(Constants.Events.PRESENCE_UPDATE, oldUser, user); + } + } + +}; + +module.exports = PresenceUpdateHandler; diff --git a/src/structures/Guild.js b/src/structures/Guild.js index 2fb6627a2..94b5d82b0 100644 --- a/src/structures/Guild.js +++ b/src/structures/Guild.js @@ -26,12 +26,18 @@ class Guild { } } - _addMember(guildUser) { - guildUser.user = this.client.store.NewUser(guildUser.user); + _addMember(guildUser, noEvent) { + if (!(guildUser.user instanceof User)) { + guildUser.user = this.client.store.NewUser(guildUser.user); + } + + guildUser.joined_at = guildUser.joined_at || 0; let member = this.store.add('members', new GuildMember(this, guildUser)); - if (this.client.ws.emittedReady) { + if (this.client.ws.emittedReady && !noEvent) { this.client.emit(Constants.Events.GUILD_MEMBER_ADD, this, member); } + + return member; } _updateMember(member, data) { @@ -89,7 +95,17 @@ class Guild { for (let role of data.roles) { this.store.add('roles', new Role(this, role)); } - }; + } + + if (data.presences) { + for (let presence of data.presences) { + let user = this.client.store.get('users', presence.user.id); + if (user) { + user.status = presence.status; + user.game = presence.game; + } + } + } } } diff --git a/src/structures/User.js b/src/structures/User.js index 50487077f..dcab3f561 100644 --- a/src/structures/User.js +++ b/src/structures/User.js @@ -14,6 +14,8 @@ class User { this.discriminator = data.discriminator; this.avatar = data.avatar; this.bot = Boolean(data.bot); + this.status = data.status || 'offline'; + this.game = data.game; } } diff --git a/src/util/Constants.js b/src/util/Constants.js index ccd415687..3f2a8d64c 100644 --- a/src/util/Constants.js +++ b/src/util/Constants.js @@ -87,9 +87,11 @@ const Events = exports.Events = { GUILD_ROLE_CREATE: 'guildRoleCreate', GUILD_ROLE_DELETE: 'guildRoleDelete', GUILD_ROLE_UPDATE: 'guildRoleUpdate', + GUILD_MEMBER_AVAILABLE: 'guildMemberAvailable', CHANNEL_CREATE: 'channelCreate', CHANNEL_DELETE: 'channelDelete', CHANNEL_UPDATE: 'channelUpdate', + PRESENCE_UPDATE: 'presenceUpdate', WARN: 'warn', }; diff --git a/test/random.js b/test/random.js index 0adff7a8e..569b40b55 100644 --- a/test/random.js +++ b/test/random.js @@ -48,3 +48,7 @@ client.on('guildRoleDelete', (guild, role) => { client.on('guildRoleUpdate', (guild, old, newRole) => { console.log('updated role', old.name, 'to', newRole.name, 'in', guild.name); }); + +client.on('presenceUpdate', (oldUser, newUser) => { + console.log('presence from', oldUser.username, 'to', newUser.username); +});