UNPKG

detritus-client

Version:

A Typescript NodeJS library to interact with Discord's API, both Rest and Gateway.

1,204 lines 72.5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.GatewayDispatchHandler = exports.GatewayHandler = void 0; const basecollection_1 = require("../collections/basecollection"); const baseset_1 = require("../collections/baseset"); const constants_1 = require("../constants"); const errors_1 = require("../errors"); const structures_1 = require("../structures"); const componenthandler_1 = require("./componenthandler"); /** * Gateway Handler * @category Handler */ class GatewayHandler { constructor(client, options = {}) { this._chunksWaiting = new basecollection_1.BaseCollection(); this._componentHandler = new componenthandler_1.ComponentHandler(); this.loadAllMembers = false; // I've witnessed some duplicate events happening with almost a second in between // Some member add/remove events might not even happen due to "State Repair" this.duplicateMemberEventsCache = new basecollection_1.BaseCollection({ expire: 2000 }); this.client = client; this.client.gateway.on('killed', this.onKilled.bind(this)); this.client.gateway.on('packet', this.onPacket.bind(this)); this.dispatchHandler = new GatewayDispatchHandler(this); this.disabledEvents = new baseset_1.BaseSet((options.disabledEvents || []).map((v) => { return v.toUpperCase(); })); this.loadAllMembers = !!options.loadAllMembers; if (options.whitelistedEvents) { this.disabledEvents.clear(); for (let event of Object.values(constants_1.GatewayDispatchEvents)) { this.disabledEvents.add(event); } for (let event of options.whitelistedEvents) { this.disabledEvents.delete(event.toUpperCase()); } } this.disabledEvents.delete(constants_1.GatewayDispatchEvents.READY); } get shouldLoadAllMembers() { return this.loadAllMembers && this.client.gateway.guildSubscriptions; } onKilled(payload) { this.client.kill(payload.error); } onPacket(packet) { if (packet.op !== constants_1.GatewayOpCodes.DISPATCH) { return; } const { d: data, t: name } = packet; if (this.client.hasEventListener(constants_1.ClientEvents.RAW)) { this.client.emit(constants_1.ClientEvents.RAW, packet); } if (!this.disabledEvents.has(name)) { if (name in this.dispatchHandler) { try { this.dispatchHandler[name](data); } catch (error) { this.client.emit(constants_1.ClientEvents.WARN, { error: new errors_1.GatewayError(error, packet) }); } } else { this.client.emit(constants_1.ClientEvents.UNKNOWN, packet); } } } } exports.GatewayHandler = GatewayHandler; /** * Gateway Dispatch Handler * @category Handlers */ class GatewayDispatchHandler { constructor(handler) { this.handler = handler; } get client() { return this.handler.client; } /* Dispatch Events */ async [constants_1.GatewayDispatchEvents.READY](data) { this.client.reset(false); for (let [listenerId, listener] of this.handler._componentHandler.listeners) { if (!listener.timeout) { this.handler._componentHandler.listeners.delete(listenerId); } } for (let [nonce, cache] of this.handler._chunksWaiting) { cache.promise.reject(new Error('Gateway re-identified before a result came.')); } this.handler._chunksWaiting.clear(); let me; if (this.client.user) { me = this.client.user; me.merge(data['user']); } else { me = new structures_1.UserMe(this.client, data['user']); this.client.user = me; } this.client.users.insert(me); // since we reset the cache Object.defineProperty(this.client, '_isBot', { value: me.bot }); const authType = (this.client.isBot) ? constants_1.AuthTypes.BOT : constants_1.AuthTypes.USER; this.client.rest.setAuthType(authType); // data['analytics_token'] if (this.client.connectedAccounts.enabled && data['connected_accounts']) { for (let raw of data['connected_accounts']) { const account = new structures_1.ConnectedAccount(this.client, raw); this.client.connectedAccounts.insert(account); } } if (this.client.guilds.enabled) { for (let raw of data['guilds']) { let guild; if (this.client.guilds.has(raw.id)) { guild = this.client.guilds.get(raw.id); guild.merge(raw); } else { guild = new structures_1.Guild(this.client, raw); this.client.guilds.insert(guild); } guild.isReady = guild.memberCount === guild.members.length; if (guild.isReady) { // emit guild ready } else { if (this.handler.shouldLoadAllMembers) { this.client.gateway.requestGuildMembers(guild.id, { limit: 0, presences: true, query: '', }); } } } } if (this.client.notes.enabled && data['notes']) { for (let userId in data['notes']) { this.client.notes.insert(userId, data['notes'][userId]); } } if (this.client.presences.enabled && data['presences']) { for (let raw of data['presences']) { this.client.presences.insert(raw); } } if (this.client.channels.enabled && data['private_channels']) { for (let raw of data['private_channels']) { if (this.client.channels.has(raw.id)) { this.client.channels.get(raw.id).merge(raw); } else { this.client.channels.insert(structures_1.createChannelFromData(this.client, raw)); } } } if (this.client.relationships.enabled && data['relationships']) { for (let raw of data['relationships']) { if (this.client.relationships.has(raw.id)) { this.client.relationships.get(raw.id).merge(raw); } else { this.client.relationships.insert(new structures_1.Relationship(this.client, raw)); } } } if (this.client.sessions.enabled && data['sessions']) { for (let raw of data['sessions']) { this.client.sessions.insert(new structures_1.Session(this.client, raw)); } } if (data['user_settings']) { } if (this.client.isBot) { try { if (this.client.cluster) { await this.client.cluster.fillOauth2Application(); } else { await this.client.rest.fetchOauth2Application(); } } catch (error) { const payload = { error: new errors_1.GatewayHTTPError('Failed to fetch OAuth2 Application Information', error) }; this.client.emit(constants_1.ClientEvents.WARN, payload); } } else { this.client.owners.set(me.id, me); this.client.requiredAction = data['required_action']; } try { if (this.client.cluster) { await this.client.cluster.fillApplications(); } else { await this.client.applications.fill(); } } catch (error) { const payload = { error: new errors_1.GatewayHTTPError('Failed to fetch Applications', error) }; this.client.emit(constants_1.ClientEvents.WARN, payload); } const payload = { raw: data }; this.client.emit(constants_1.ClientEvents.GATEWAY_READY, payload); } [constants_1.GatewayDispatchEvents.RESUMED](data) { this.client.gateway.discordTrace = data['_trace']; const payload = { raw: data }; this.client.emit(constants_1.ClientEvents.GATEWAY_RESUMED, payload); } [constants_1.GatewayDispatchEvents.ACTIVITY_JOIN_INVITE](data) { } [constants_1.GatewayDispatchEvents.ACTIVITY_JOIN_REQUEST](data) { } [constants_1.GatewayDispatchEvents.ACTIVITY_START](data) { } [constants_1.GatewayDispatchEvents.APPLICATION_COMMAND_CREATE](data) { const command = new structures_1.ApplicationCommand(this.client, data); const payload = { _raw: data, command }; this.client.emit(constants_1.ClientEvents.APPLICATION_COMMAND_CREATE, payload); } [constants_1.GatewayDispatchEvents.APPLICATION_COMMAND_DELETE](data) { const command = new structures_1.ApplicationCommand(this.client, data); const payload = { _raw: data, command }; this.client.emit(constants_1.ClientEvents.APPLICATION_COMMAND_DELETE, payload); } [constants_1.GatewayDispatchEvents.APPLICATION_COMMAND_UPDATE](data) { const command = new structures_1.ApplicationCommand(this.client, data); const payload = { _raw: data, command }; this.client.emit(constants_1.ClientEvents.APPLICATION_COMMAND_UPDATE, payload); } [constants_1.GatewayDispatchEvents.BRAINTREE_POPUP_BRIDGE_CALLBACK](data) { } [constants_1.GatewayDispatchEvents.CALL_CREATE](data) { let call; if (this.client.voiceCalls.has(data['channel_id'])) { call = this.client.voiceCalls.get(data['channel_id']); call.merge(data); } else { call = new structures_1.VoiceCall(this.client, data); this.client.voiceCalls.insert(call); } const payload = { call }; this.client.emit(constants_1.ClientEvents.CALL_CREATE, payload); } [constants_1.GatewayDispatchEvents.CALL_DELETE](data) { let channelId = data['channel_id']; if (this.client.voiceCalls.has(channelId)) { const call = this.client.voiceCalls.get(channelId); call.kill(); } const payload = { channelId }; this.client.emit(constants_1.ClientEvents.CALL_DELETE, payload); } [constants_1.GatewayDispatchEvents.CALL_UPDATE](data) { let call; let channelId = data['channel_id']; let differences = null; if (this.client.voiceCalls.has(data['channel_id'])) { call = this.client.voiceCalls.get(data['channel_id']); if (this.client.hasEventListener(constants_1.ClientEvents.CALL_UPDATE)) { differences = call.differences(data); } call.merge(data); } else { call = new structures_1.VoiceCall(this.client, data); this.client.voiceCalls.insert(call); } const payload = { call, channelId, differences }; this.client.emit(constants_1.ClientEvents.CALL_UPDATE, payload); } [constants_1.GatewayDispatchEvents.CHANNEL_CREATE](data) { let channel; if (this.client.channels.has(data['id'])) { channel = this.client.channels.get(data['id']); channel.merge(data); } else { channel = structures_1.createChannelFromData(this.client, data); this.client.channels.insert(channel); } const guild = channel.guild; if (guild) { guild._channelIds.add(channel.id); } const payload = { channel }; this.client.emit(constants_1.ClientEvents.CHANNEL_CREATE, payload); } [constants_1.GatewayDispatchEvents.CHANNEL_DELETE](data) { let channel; if (this.client.channels.has(data['id'])) { channel = this.client.channels.get(data['id']); this.client.channels.delete(data['id']); } else { channel = structures_1.createChannelFromData(this.client, data); } channel.deleted = true; if (channel.isText) { for (let [messageId, message] of this.client.messages) { if (message.channelId === channel.id) { message.deleted = true; this.client.messages.delete(messageId); } this.handler._componentHandler.delete(messageId); } } const guild = channel.guild; if (guild) { guild.channels.delete(channel.id); } const payload = { channel }; this.client.emit(constants_1.ClientEvents.CHANNEL_DELETE, payload); } [constants_1.GatewayDispatchEvents.CHANNEL_PINS_ACK](data) { } [constants_1.GatewayDispatchEvents.CHANNEL_PINS_UPDATE](data) { let channel = null; if (this.client.channels.has(data['channel_id'])) { channel = this.client.channels.get(data['channel_id']); channel.merge({ last_pin_timestamp: data['last_pin_timestamp'], }); } const payload = { channel, channelId: data['channel_id'], guildId: data['guild_id'], lastPinTimestamp: data['last_pin_timestamp'], }; this.client.emit(constants_1.ClientEvents.CHANNEL_PINS_UPDATE, payload); } [constants_1.GatewayDispatchEvents.CHANNEL_UPDATE](data) { let channel; let differences = null; let old = null; const isListening = this.client.hasEventListener(constants_1.ClientEvents.CHANNEL_UPDATE); if (this.client.channels.has(data['id'])) { channel = this.client.channels.get(data['id']); if (isListening) { differences = channel.differences(data); old = channel.clone(); } channel.merge(data); } else { channel = structures_1.createChannelFromData(this.client, data); this.client.channels.insert(channel); } const payload = { channel, differences, old }; this.client.emit(constants_1.ClientEvents.CHANNEL_UPDATE, payload); } [constants_1.GatewayDispatchEvents.CHANNEL_RECIPIENT_ADD](data) { let channel = null; const channelId = data['channel_id']; const nick = data['nick'] || null; let user; if (this.client.users.has(data['user']['id'])) { user = this.client.users.get(data['user']['id']); user.merge(data); } else { user = new structures_1.User(this.client, data); this.client.users.insert(user); } if (this.client.channels.has(channelId)) { channel = this.client.channels.get(channelId); channel.recipients.set(user.id, user); if (nick) { channel.nicks.set(user.id, nick); } else { channel.nicks.delete(user.id); } } const payload = { channel, channelId, nick, user, }; this.client.emit(constants_1.ClientEvents.CHANNEL_RECIPIENT_ADD, payload); } [constants_1.GatewayDispatchEvents.CHANNEL_RECIPIENT_REMOVE](data) { let channel = null; const channelId = data['channel_id']; const nick = data['nick'] || null; let user; if (this.client.users.has(data['user']['id'])) { user = this.client.users.get(data['user']['id']); user.merge(data); } else { user = new structures_1.User(this.client, data); this.client.users.insert(user); } if (this.client.channels.has(channelId)) { channel = this.client.channels.get(channelId); channel.recipients.delete(user.id); channel.nicks.delete(user.id); } const payload = { channel, channelId, nick, user, }; this.client.emit(constants_1.ClientEvents.CHANNEL_RECIPIENT_REMOVE, payload); } [constants_1.GatewayDispatchEvents.ENTITLEMENT_CREATE](data) { } [constants_1.GatewayDispatchEvents.ENTITLEMENT_DELETE](data) { } [constants_1.GatewayDispatchEvents.ENTITLEMENT_UPDATE](data) { } [constants_1.GatewayDispatchEvents.FRIEND_SUGGESTION_CREATE](data) { this.client.emit(constants_1.ClientEvents.FRIEND_SUGGESTION_CREATE, { reasons: data.reasons.map((reason) => { return { name: reason['name'], platformType: reason['platform_type'] }; }), user: new structures_1.User(this.client, data['suggested_user']), }); } [constants_1.GatewayDispatchEvents.FRIEND_SUGGESTION_DELETE](data) { this.client.emit(constants_1.ClientEvents.FRIEND_SUGGESTION_DELETE, { suggestedUserId: data['suggested_user_id'], }); } [constants_1.GatewayDispatchEvents.GIFT_CODE_UPDATE](data) { this.client.emit(constants_1.ClientEvents.GIFT_CODE_UPDATE, { code: data['code'], uses: data['uses'], }); } [constants_1.GatewayDispatchEvents.GUILD_BAN_ADD](data) { const guild = this.client.guilds.get(data['guild_id']); const guildId = data['guild_id']; let user; if (this.client.users.has(data['user']['id'])) { user = this.client.users.get(data['user']['id']); user.merge(data['user']); } else { user = new structures_1.User(this.client, data['user']); } this.client.emit(constants_1.ClientEvents.GUILD_BAN_ADD, { guild, guildId, user, }); } [constants_1.GatewayDispatchEvents.GUILD_BAN_REMOVE](data) { const guild = this.client.guilds.get(data['guild_id']); const guildId = data['guild_id']; let user; if (this.client.users.has(data['user']['id'])) { user = this.client.users.get(data['user']['id']); user.merge(data['user']); } else { user = new structures_1.User(this.client, data['user']); } this.client.emit(constants_1.ClientEvents.GUILD_BAN_REMOVE, { guild, guildId, user, }); } [constants_1.GatewayDispatchEvents.GUILD_CREATE](data) { let fromUnavailable = false; let guild; if (this.client.guilds.has(data['id'])) { guild = this.client.guilds.get(data['id']); fromUnavailable = guild.unavailable; guild.merge(data); } else { guild = new structures_1.Guild(this.client, data); this.client.guilds.insert(guild); } guild.isReady = guild.memberCount === guild.members.length; if (guild.isReady) { // emit GUILD_READY } else { if (this.handler.shouldLoadAllMembers) { this.client.gateway.requestGuildMembers(guild.id, { limit: 0, presences: true, query: '', }); } } const payload = { fromUnavailable, guild }; this.client.emit(constants_1.ClientEvents.GUILD_CREATE, payload); } [constants_1.GatewayDispatchEvents.GUILD_DELETE](data) { let channels = null; let guild = null; const guildId = data['id']; const isUnavailable = !!data['unavailable']; let isNew; if (this.client.guilds.has(data['id'])) { guild = this.client.guilds.get(data['id']); guild.merge(data); isNew = false; } else { guild = new structures_1.Guild(this.client, data); this.client.guilds.insert(guild); isNew = true; } if (!isUnavailable) { guild.left = true; const me = guild.me; if (me) { me.left = true; } for (let [interactionId, interaction] of this.client.interactions) { if (interaction.guildId === guildId) { Object.defineProperty(interaction, '_deleted', { value: true }); this.client.interactions.delete(interactionId); } } } if (!isNew || !this.client.guilds.enabled) { if (this.client.hasEventListener(constants_1.ClientEvents.GUILD_DELETE)) { channels = new basecollection_1.BaseCollection(); } for (let [channelId, channel] of this.client.channels) { if (channel.guildId === guildId) { if (channels) { channels.set(channel.id, channel); } this.client.channels.delete(channelId); if (channel.isText) { if (this.client.typings.get(channelId)) { const typings = this.client.typings.get(channelId); for (let [userId, typing] of typings) { typing.timeout.stop(); typings.delete(userId); } typings.clear(); } } } } for (let [messageId, message] of this.client.messages) { if (message.guildId === guildId) { message.deleted = true; this.client.messages.delete(messageId); this.handler._componentHandler.delete(messageId); } } this.client.presences.clearGuildId(guildId); this.client.voiceStates.delete(guildId); guild.emojis.clear(); guild.members.clear(); // should we check each member and see if we should clear the user obj from cache too? guild.roles.clear(); guild.isReady = false; } if (!isUnavailable) { this.client.guilds.delete(guildId); } const payload = { channels, guild, guildId, isUnavailable }; this.client.emit(constants_1.ClientEvents.GUILD_DELETE, payload); } [constants_1.GatewayDispatchEvents.GUILD_EMOJIS_UPDATE](data) { let differences = null; let emojis; let guild = null; const guildId = data['guild_id']; if (this.client.guilds.has(guildId)) { guild = this.client.guilds.get(guildId); if (this.client.hasEventListener(constants_1.ClientEvents.GUILD_EMOJIS_UPDATE)) { differences = { created: new basecollection_1.BaseCollection(), deleted: new basecollection_1.BaseCollection(), updated: new basecollection_1.BaseCollection(), }; const unchanged = new baseset_1.BaseSet(); // go through each one for (let raw of data['emojis']) { Object.assign(raw, { guild_id: guildId }); const emojiId = raw.id; if (guild.emojis.has(emojiId)) { // updated const emoji = guild.emojis.get(emojiId); if (emoji.hasDifferences(raw)) { differences.updated.set(emojiId, { emoji, old: emoji.clone(), }); emoji.merge(raw); } else { unchanged.add(emojiId); } } else { // created differences.created.set(emojiId, new structures_1.Emoji(this.client, raw)); } } for (let [emojiId, emoji] of guild.emojis) { if (!unchanged.has(emojiId) && !differences.updated.has(emojiId)) { differences.deleted.set(emojiId, emoji); guild.emojis.delete(emojiId); } } for (let [emojiId, emoji] of differences.created) { guild.emojis.set(emojiId, emoji); } } else { guild.merge({ emojis: data['emojis'] }); } emojis = guild.emojis; } else { emojis = new basecollection_1.BaseCollection(); for (let raw of data['emojis']) { Object.assign(raw, { guild_id: guildId }); const emojiId = raw.id; const emoji = new structures_1.Emoji(this.client, raw); emojis.set(emojiId, emoji); } } const payload = { differences, emojis, guild, guildId }; this.client.emit(constants_1.ClientEvents.GUILD_EMOJIS_UPDATE, payload); } [constants_1.GatewayDispatchEvents.GUILD_INTEGRATIONS_UPDATE](data) { this.client.emit(constants_1.ClientEvents.GUILD_INTEGRATIONS_UPDATE, { guildId: data['guild_id'], }); } [constants_1.GatewayDispatchEvents.GUILD_MEMBER_ADD](data) { const guildId = data['guild_id']; let isDuplicate = false; let member; const userId = data['user']['id']; if (this.client.members.has(guildId, userId)) { member = this.client.members.get(guildId, userId); member.merge(data); } else { member = new structures_1.Member(this.client, data); this.client.members.insert(member); } // Discord can send us a duplicate `GUILD_MEMBER_ADD` event sometimes, like during a guild raid const isListening = this.client.hasEventListener(constants_1.ClientEvents.GUILD_MEMBER_ADD); if (isListening || this.client.guilds.has(guildId)) { const key = `${guildId}.${userId}`; const event = this.handler.duplicateMemberEventsCache.get(key); if (event === constants_1.GatewayDispatchEvents.GUILD_MEMBER_ADD) { isDuplicate = true; } else { if (this.client.guilds.has(guildId)) { const guild = this.client.guilds.get(guildId); guild.memberCount++; } } this.handler.duplicateMemberEventsCache.set(key, constants_1.GatewayDispatchEvents.GUILD_MEMBER_ADD); } const payload = { guildId, isDuplicate, member, userId }; this.client.emit(constants_1.ClientEvents.GUILD_MEMBER_ADD, payload); } [constants_1.GatewayDispatchEvents.GUILD_MEMBER_LIST_UPDATE](data) { this.client.emit(constants_1.ClientEvents.GUILD_MEMBER_LIST_UPDATE, { raw: data, }); } [constants_1.GatewayDispatchEvents.GUILD_MEMBER_REMOVE](data) { const guildId = data['guild_id']; let isDuplicate = false; let member = null; let user; const userId = data['user']['id']; if (this.client.users.has(userId)) { user = this.client.users.get(userId); user.merge(data['user']); } else { user = new structures_1.User(this.client, data['user']); } if (this.client.members.has(guildId, userId)) { member = this.client.members.get(guildId, userId); member.left = true; } this.client.members.delete(guildId, userId); // Discord can send us a duplicate `GUILD_MEMBER_ADD` event sometimes, just in case check _REMOVE too const isListening = this.client.hasEventListener(constants_1.ClientEvents.GUILD_MEMBER_REMOVE); if (isListening || this.client.guilds.has(guildId)) { const key = `${guildId}.${userId}`; const event = this.handler.duplicateMemberEventsCache.get(key); if (event === constants_1.GatewayDispatchEvents.GUILD_MEMBER_REMOVE) { isDuplicate = true; } else { if (this.client.guilds.has(guildId)) { const guild = this.client.guilds.get(guildId); guild.memberCount--; } } this.handler.duplicateMemberEventsCache.set(key, constants_1.GatewayDispatchEvents.GUILD_MEMBER_REMOVE); } if (this.client.presences.has(userId)) { const presence = this.client.presences.get(userId); presence._deleteGuildId(guildId); if (!presence.guildIds.length) { this.client.presences.delete(userId); } } for (let [cacheId, cache] of this.client.typings.caches) { if (cache.has(userId)) { const typing = cache.get(userId); typing._stop(false); } } this.client.voiceStates.delete(guildId, userId); // do a guild sweep for mutual guilds const sharesGuilds = this.client.guilds.some((guild) => guild.members.has(userId)); if (!sharesGuilds) { // do a channel sweep for mutual dms const sharesDms = this.client.channels.some((channel) => channel.recipients.has(userId)); if (!sharesDms) { // relationship check if (!this.client.relationships.has(userId)) { this.client.users.delete(userId); } } } const payload = { guildId, isDuplicate, member, user, userId }; this.client.emit(constants_1.ClientEvents.GUILD_MEMBER_REMOVE, payload); } [constants_1.GatewayDispatchEvents.GUILD_MEMBER_UPDATE](data) { let differences = null; let member; let old = null; let oldUser = null; const guildId = data['guild_id']; const userId = data['user']['id']; if (this.client.hasEventListener(constants_1.ClientEvents.USERS_UPDATE)) { if (this.client.users.has(userId)) { const user = this.client.users.get(userId); const userDifferences = user.differences(data['user']); if (userDifferences) { differences = { user: userDifferences }; oldUser = user.clone(); } } } const isListening = this.client.hasEventListener(constants_1.ClientEvents.GUILD_MEMBER_UPDATE); if (this.client.members.has(guildId, userId)) { member = this.client.members.get(guildId, userId); if (isListening) { const memberDifferences = member.differences(data); if (differences) { Object.assign(differences, memberDifferences); } else { differences = memberDifferences; } old = member.clone(); } member.merge(data); } else { member = new structures_1.Member(this.client, data); this.client.members.insert(member); } if (differences && differences.user) { const payload = { differences: differences.user, from: constants_1.ClientEvents.GUILD_MEMBER_UPDATE, old: oldUser, user: member.user, }; this.client.emit(constants_1.ClientEvents.USERS_UPDATE, payload); } const payload = { differences, guildId, member, old, userId }; this.client.emit(constants_1.ClientEvents.GUILD_MEMBER_UPDATE, payload); } [constants_1.GatewayDispatchEvents.GUILD_MEMBERS_CHUNK](data) { const chunkCount = data['chunk_count']; const chunkIndex = data['chunk_index']; const guildId = data['guild_id']; const nonce = data['nonce'] || null; let guild = this.client.guilds.get(guildId) || null; let members = null; let notFound = null; let presences = null; const isListening = (this.client.hasEventListener(constants_1.ClientEvents.GUILD_MEMBERS_CHUNK) || !!(nonce && this.handler._chunksWaiting.has(nonce))); let cache = nonce && this.handler._chunksWaiting.get(nonce); // do presences first since the members cache might depend on it (storeOffline = false) if (data['presences']) { presences = new basecollection_1.BaseCollection(); if (this.client.presences.enabled || isListening) { for (let value of data['presences']) { value.guild_id = guildId; const presence = this.client.presences.insert(value); if (isListening) { presences.set(presence.user.id, presence); } if (cache) { cache.presences.set(presence.user.id, presence); } } } } if (data['members']) { // we (the bot user) won't be in the chunk anyways, right? if (this.client.members.enabled || isListening) { members = new basecollection_1.BaseCollection(); for (let value of data['members']) { let rawUser = value.user; let member; if (this.client.members.has(guildId, rawUser.id)) { member = this.client.members.get(guildId, rawUser.id); member.merge(value); } else { member = new structures_1.Member(this.client, Object.assign(value, { guild_id: guildId })); this.client.members.insert(member); } if (isListening) { members.set(member.id, member); } if (cache) { cache.members.set(member.id, member); } } } else if (this.client.presences.enabled || this.client.users.enabled) { for (let value of data['members']) { let raw = value.user; let user; if (this.client.users.has(raw.id)) { user = this.client.users.get(raw.id); user.merge(raw); } else { user = new structures_1.User(this.client, raw); this.client.users.insert(user); } } } } if (data['not_found']) { // user ids // if the userId is not a big int, it'll be an integer.. notFound = data['not_found'].map((userId) => String(userId)); if (cache) { for (let userId of notFound) { cache.notFound.add(userId); } } } if (guild && !guild.isReady && guild.memberCount === guild.members.length) { guild.isReady = true; const payload = { guild }; this.client.emit(constants_1.ClientEvents.GUILD_READY, payload); } if (cache && chunkIndex + 1 === chunkCount) { cache.promise.resolve(); } const payload = { chunkCount, chunkIndex, guild, guildId, members, nonce, notFound, presences, }; this.client.emit(constants_1.ClientEvents.GUILD_MEMBERS_CHUNK, payload); } [constants_1.GatewayDispatchEvents.GUILD_ROLE_CREATE](data) { let guild = null; const guildId = data['guild_id']; let role; if (this.client.guilds.has(guildId)) { guild = this.client.guilds.get(guildId); if (guild.roles.has(data['role']['id'])) { role = guild.roles.get(data['role']['id']); role.merge(data['role']); } else { data['role']['guild_id'] = guildId; role = new structures_1.Role(this.client, data['role']); guild.roles.set(role.id, role); } } else { data['role']['guild_id'] = guildId; role = new structures_1.Role(this.client, data['role']); } const payload = { guild, guildId, role }; this.client.emit(constants_1.ClientEvents.GUILD_ROLE_CREATE, payload); } [constants_1.GatewayDispatchEvents.GUILD_ROLE_DELETE](data) { let guild = null; const guildId = data['guild_id']; let role = null; const roleId = data['role_id']; if (this.client.guilds.has(guildId)) { guild = this.client.guilds.get(guildId); if (guild.roles.has(roleId)) { role = guild.roles.get(roleId); guild.roles.delete(roleId); } for (let [userId, member] of guild.members) { if (member._roles) { const index = member._roles.indexOf(roleId); if (index !== -1) { member._roles.splice(index, 1); } } } } const payload = { guild, guildId, role, roleId }; this.client.emit(constants_1.ClientEvents.GUILD_ROLE_DELETE, payload); } [constants_1.GatewayDispatchEvents.GUILD_ROLE_UPDATE](data) { let differences = null; let guild = null; let old = null; let role; const guildId = data['guild_id']; const roleId = data['role']['id']; const isListening = this.client.hasEventListener(constants_1.ClientEvents.GUILD_ROLE_UPDATE); if (this.client.guilds.has(guildId)) { guild = this.client.guilds.get(guildId); if (guild.roles.has(roleId)) { role = guild.roles.get(roleId); if (isListening) { differences = role.differences(data['role']); old = role.clone(); } role.merge(data['role']); } else { data['role']['guild_id'] = guildId; role = new structures_1.Role(this.client, data['role']); guild.roles.set(role.id, role); } } else { data['role']['guild_id'] = guildId; role = new structures_1.Role(this.client, data['role']); } const payload = { differences, guild, guildId, old, role }; this.client.emit(constants_1.ClientEvents.GUILD_ROLE_UPDATE, payload); } [constants_1.GatewayDispatchEvents.GUILD_STICKERS_UPDATE](data) { let differences = null; let stickers; let guild = null; const guildId = data['guild_id']; if (this.client.guilds.has(guildId)) { guild = this.client.guilds.get(guildId); if (this.client.hasEventListener(constants_1.ClientEvents.GUILD_STICKERS_UPDATE)) { differences = { created: new basecollection_1.BaseCollection(), deleted: new basecollection_1.BaseCollection(), updated: new basecollection_1.BaseCollection(), }; const unchanged = new baseset_1.BaseSet(); // go through each one for (let raw of data['stickers']) { if (guild.stickers.has(raw.id)) { // updated const sticker = guild.stickers.get(raw.id); if (sticker.hasDifferences(raw)) { differences.updated.set(sticker.id, { sticker, old: sticker.clone(), }); sticker.merge(raw); } else { unchanged.add(sticker.id); } } else { // created differences.created.set(raw.id, new structures_1.Sticker(this.client, raw)); } } for (let [stickerId, sticker] of guild.stickers) { if (!unchanged.has(stickerId) && !differences.updated.has(stickerId)) { differences.deleted.set(stickerId, sticker); guild.stickers.delete(stickerId); } } for (let [stickerId, sticker] of differences.created) { guild.stickers.set(stickerId, sticker); } } else { guild.merge({ stickers: data['stickers'] }); } stickers = guild.stickers; } else { stickers = new basecollection_1.BaseCollection(); for (let raw of data['stickers']) { const sticker = new structures_1.Sticker(this.client, raw); stickers.set(sticker.id, sticker); } } const payload = { differences, stickers, guild, guildId }; this.client.emit(constants_1.ClientEvents.GUILD_STICKERS_UPDATE, payload); } [constants_1.GatewayDispatchEvents.GUILD_UPDATE](data) { let differences = null; let guild; let old = null; const isListening = this.client.hasEventListener(constants_1.ClientEvents.GUILD_UPDATE); if (this.client.guilds.has(data['id'])) { guild = this.client.guilds.get(data['id']); if (isListening) { differences = guild.differences(data); old = guild.clone(); } guild.merge(data); } else { guild = new structures_1.Guild(this.client, data); this.client.guilds.insert(guild); } guild.hasMetadata = true; const payload = { differences, guild, old }; this.client.emit(constants_1.ClientEvents.GUILD_UPDATE, payload); } [constants_1.GatewayDispatchEvents.INTERACTION_CREATE](data) { const interaction = new structures_1.Interaction(this.client, data); this.client.interactions.insert(interaction); if (interaction.message && interaction.message.interaction) { this.handler._componentHandler.replaceId(interaction.message.interaction.id, interaction.message.id); } const payload = { _raw: data, interaction }; this.client.emit(constants_1.ClientEvents.INTERACTION_CREATE, payload); if (interaction.isFromMessageComponent) { this.handler._componentHandler.execute(interaction); } } [constants_1.GatewayDispatchEvents.INVITE_CREATE](data) { const channelId = data['channel_id']; const guildId = data['guild_id']; const invite = new structures_1.Invite(this.client, data); const payload = { channelId, guildId, invite }; this.client.emit(constants_1.ClientEvents.INVITE_CREATE, payload); } [constants_1.GatewayDispatchEvents.INVITE_DELETE](data) { const channelId = data['channel_id']; const code = data['code']; const guildId = data['guild_id']; const payload = { channelId, code, guildId }; this.client.emit(constants_1.ClientEvents.INVITE_DELETE, payload); } [constants_1.GatewayDispatchEvents.LIBRARY_APPLICATION_UPDATE](data) { } [constants_1.GatewayDispatchEvents.LOBBY_CREATE](data) { } [constants_1.GatewayDispatchEvents.LOBBY_DELETE](data) { } [constants_1.GatewayDispatchEvents.LOBBY_UPDATE](data) { } [constants_1.GatewayDispatchEvents.LOBBY_MEMBER_CONNECT](data) { } [constants_1.GatewayDispatchEvents.LOBBY_MEMBER_DISCONNECT](data) { } [constants_1.GatewayDispatchEvents.LOBBY_MEMBER_UPDATE](data) { } [constants_1.GatewayDispatchEvents.LOBBY_MESSAGE](data) { } [constants_1.GatewayDispatchEvents.LOBBY_VOICE_SERVER_UPDATE](data) { } [constants_1.GatewayDispatchEvents.LOBBY_VOICE_STATE_UPDATE](data) { } [constants_1.GatewayDispatchEvents.MESSAGE_ACK](data) { } [constants_1.GatewayDispatchEvents.MESSAGE_CREATE](data) { let message; let typing = null; if (this.client.messages.has(data['id'])) { message = this.client.messages.get(data['id']); message.merge(data); } else { message = new structures_1.Message(this.client, data); this.client.messages.insert(message); } if (this.client.channels.has(message.channelId)) { const channel = this.client.channels.get(message.channelId); channel.merge({ last_message_id: message.id }); } if (this.client.typings.has(message.channelId)) { const typings = this.client.typings.get(message.channelId); if (typings.has(message.author.id)) { typing = typings.get(message.author.id); typing._stop(); } } if (message.interaction) { if (this.client.interactions.has(message.interaction.id)) { const interaction = this.client.interactions.get(message.interaction.id); interaction.responseId = message.id; } this.handler._componentHandler.replaceId(message.interaction.id, message.id); } const payload = { message, typing }; this.client.emit(constants_1.ClientEvents.MESSAGE_CREATE, payload); } [constants_1.GatewayDispatchEvents.MESSAGE_DELETE](data) { const channelId = data['channel_id']; const guildId = data['guild_id']; let message = null; const messageId = data['id']; if (this.client.messages.has(messageId)) { message = this.client.messages.get(messageId); message.deleted = true; this.client.messages.delete(messageId); if (message.interaction && this.client.interactions.has(message.interaction.id)) { const interaction = this.client.interactions.get(message.interaction.id); interaction.responseDeleted = true; interaction.responseId = messageId; } } else { for (let [interactionId, interaction] of this.client.interactions) { if (interaction.responseId === messageId) { interaction.responseDeleted = true; break; } } } this.handler._componentHandler.delete(messageId); const payload = { channelId, guildId, message, messageId, raw: data }; this.client.emit(constants_1.ClientEvents.MESSAGE_DELETE, payload); } [constants_1.GatewayDispatchEvents.MESSAGE_DELETE_BULK](data) { const amount = data['ids'].length; const channelId = data['channel_id']; const guildId = data['guild_id']; const messages = new basecollection_1.BaseCollection(); for (let messageId of data['ids']) { if (this.client.messages.has(messageId)) { const message = this.client.messages.get(messageId); message.deleted = true; messages.set(messageId, message); this.client.messages.delete(messageId); } else { messages.set(messageId, null); } this.handler._componentHandler.delete(messageId); } const payload = { amount, channelId, guildId, messages, raw: data }; this.client.emit(constants_1.ClientEvents.MESSAGE_DELETE_BULK, payload); } [constants_1.GatewayDispatchEvents.MESSAGE_REACTION_ADD](data) { const channelId = data['channel_id']; const guildId = data['guild_id']; let member = null; let message = null; const messageId = data['message_id']; let reaction = null; let user = null; const userId = data['user_id']; if (this.client.users.has(userId)) { user = this.client.users.get(userId); } if (data.member) { if (this.client.members.has(guildId, userId)) { member = this.client.members.get(guildId, userId); member.merge(data.member); } else { member = new structures_1.Member(this.client, data.member); this.client.members.insert(member); } } const emojiId = data.emoji.id || data.emoji.name; if (this.client.messages.has(messageId)) { message = this.client.messages.get(messageId); if (message._reactions && message._reactions.has(emojiId)) { reaction = message._reactions.get(emojiId); } } if (!reaction) { // https://github.com/discordapp/discord-api-docs/issues/812 Object.assign(data, { is_partial: true }); reaction = new s