diff --git a/src/controllers/guilds.ts b/src/controllers/guilds.ts index fcabd6708..4a2d2506c 100644 --- a/src/controllers/guilds.ts +++ b/src/controllers/guilds.ts @@ -1,6 +1,11 @@ import { cache } from "../utils/cache.ts"; import { DiscordPayload } from "../types/discord.ts"; -import { CreateGuildPayload, GuildDeletePayload, UpdateGuildPayload } from "../types/guild.ts"; +import { + CreateGuildPayload, + GuildDeletePayload, + GuildEmojisUpdatePayload, + UpdateGuildPayload, +} from "../types/guild.ts"; import { structures } from "../structures/mod.ts"; import { eventHandlers } from "../module/client.ts"; import { GuildUpdateChange } from "../types/options.ts"; @@ -46,7 +51,6 @@ export function handleInternalGuildDelete(data: DiscordPayload) { return cache.unavailableGuilds.set(payload.id, Date.now()); } - const guild = cache.guilds.get(payload.id); if (!guild) return; return eventHandlers.guildDelete?.(guild); @@ -93,6 +97,21 @@ export function handleInternalGuildUpdate(data: DiscordPayload) { }).filter((change) => change) as GuildUpdateChange[]; return eventHandlers.guildUpdate?.(cachedGuild, changes); - - +} + +export function handleInternalGuildEmojisUpdate(data: DiscordPayload) { + if (data.t !== "GUILD_EMOJIS_UPDATE") return; + + const payload = data.d as GuildEmojisUpdatePayload; + const guild = cache.guilds.get(payload.guild_id); + if (!guild) return; + + const cachedEmojis = guild.emojis; + guild.emojis = payload.emojis; + + return eventHandlers.guildEmojisUpdate?.( + guild, + payload.emojis, + cachedEmojis, + ); } diff --git a/src/controllers/members.ts b/src/controllers/members.ts new file mode 100644 index 000000000..c66e03b6f --- /dev/null +++ b/src/controllers/members.ts @@ -0,0 +1,128 @@ +import { eventHandlers } from "../module/client.ts"; +import { structures } from "../structures/mod.ts"; +import { DiscordPayload } from "../types/discord.ts"; +import { + GuildBanPayload, + GuildMemberAddPayload, + GuildMemberChunkPayload, + GuildMemberUpdatePayload, +} from "../types/guild.ts"; +import { cache } from "../utils/cache.ts"; + +export function handleInternalGuildMemberAdd(data: DiscordPayload) { + if (data.t !== "GUILD_MEMBER_ADD") return; + + const payload = data.d as GuildMemberAddPayload; + const guild = cache.guilds.get(payload.guild_id); + if (!guild) return; + + guild.memberCount++; + const member = structures.createMember( + payload, + guild, + ); + guild.members.set(payload.user.id, member); + + eventHandlers.guildMemberAdd?.(guild, member); +} + +export function handleInternalGuildMemberRemove(data: DiscordPayload) { + if (data.t !== "GUILD_MEMBER_REMOVE") return; + + const payload = data.d as GuildBanPayload; + const guild = cache.guilds.get(payload.guild_id); + if (!guild) return; + + guild.memberCount--; + const member = guild.members.get(payload.user.id); + eventHandlers.guildMemberRemove?.( + guild, + member || payload.user, + ); + + eventHandlers.guildMemberRemove?.( + guild, + member || payload.user, + ); + + guild.members.delete(payload.user.id); +} + +export function handleInternalGuildMemberUpdate(data: DiscordPayload) { + if (data.t !== "GUILD_MEMBER_UPDATE") return; + + const payload = data.d as GuildMemberUpdatePayload; + const guild = cache.guilds.get(payload.guild_id); + if (!guild) return; + + const cachedMember = guild.members.get(payload.user.id); + + const newMemberData = { + ...payload, + premium_since: payload.premium_since || undefined, + joined_at: new Date(cachedMember?.joinedAt || Date.now()) + .toISOString(), + deaf: cachedMember?.deaf || false, + mute: cachedMember?.mute || false, + }; + const member = structures.createMember( + newMemberData, + guild, + ); + guild.members.set(payload.user.id, member); + + if (cachedMember?.nick !== payload.nick) { + eventHandlers.nicknameUpdate?.( + guild, + member, + payload.nick, + cachedMember?.nick, + ); + } + const roleIDs = cachedMember?.roles || []; + + roleIDs.forEach((id) => { + if (!payload.roles.includes(id)) { + eventHandlers.roleLost?.(guild, member, id); + } + }); + + payload.roles.forEach((id) => { + if (!roleIDs.includes(id)) { + eventHandlers.roleGained?.(guild, member, id); + } + }); + + eventHandlers.guildMemberUpdate?.(guild, member, cachedMember); +} + +export function handleInternalGuildMembersChunk(data: DiscordPayload) { + if (data.t !== "GUILD_MEMBERS_CHUNK") return; + + const payload = data.d as GuildMemberChunkPayload; + const guild = cache.guilds.get(payload.guild_id); + if (!guild) return; + + payload.members.forEach((member) => { + guild.members.set( + member.user.id, + structures.createMember( + member, + guild, + ), + ); + }); + + // Check if its necessary to resolve the fetchmembers promise for this chunk or if more chunks will be coming + if ( + payload.nonce + ) { + const resolve = cache.fetchAllMembersProcessingRequests.get(payload.nonce); + if (!resolve) return; + + if (payload.chunk_index + 1 === payload.chunk_count) { + cache.fetchAllMembersProcessingRequests.delete(payload.nonce); + resolve(guild.members); + } + } +} diff --git a/src/controllers/mod.ts b/src/controllers/mod.ts index dc88f18eb..353ebaecf 100644 --- a/src/controllers/mod.ts +++ b/src/controllers/mod.ts @@ -10,8 +10,15 @@ import { import { handleInternalGuildCreate, handleInternalGuildDelete, + handleInternalGuildEmojisUpdate, handleInternalGuildUpdate, } from "./guilds.ts"; +import { + handleInternalGuildMemberAdd, + handleInternalGuildMemberRemove, + handleInternalGuildMembersChunk, + handleInternalGuildMemberUpdate, +} from "./members.ts"; import { handleInternalReady } from "./misc.ts"; export let controllers = { @@ -24,4 +31,9 @@ export let controllers = { GUILD_UPDATE: handleInternalGuildUpdate, GUILD_BAN_ADD: handleInternalGuildBanAdd, GUILD_BAN_REMOVE: handleInternalGuildBanRemove, + GUILD_EMOJIS_UPDATE: handleInternalGuildEmojisUpdate, + GUILD_MEMBER_ADD: handleInternalGuildMemberAdd, + GUILD_MEMBER_REMOVE: handleInternalGuildMemberRemove, + GUILD_MEMBER_UPDATE: handleInternalGuildMemberUpdate, + GUILD_MEMBERS_CHUNK: handleInternalGuildMembersChunk, }; diff --git a/src/controllers/roles.ts b/src/controllers/roles.ts new file mode 100644 index 000000000..0b5fc6782 --- /dev/null +++ b/src/controllers/roles.ts @@ -0,0 +1,44 @@ +import { eventHandlers } from "../module/client.ts"; +import { structures } from "../structures/mod.ts"; +import { DiscordPayload } from "../types/discord.ts"; +import { GuildRoleDeletePayload, GuildRolePayload } from "../types/guild.ts"; +import { cache } from "../utils/cache.ts"; + +export function handleInternalGuildRoleCreate(data: DiscordPayload) { + if (data.t !== "GUILD_ROLE_CREATE") return; + + const payload = data.d as GuildRolePayload; + const guild = cache.guilds.get(payload.guild_id); + if (!guild) return; + + const role = structures.createRole(payload.role); + const roles = guild.roles.set(payload.role.id, role); + guild.roles = roles; + return eventHandlers.roleCreate?.(guild, role); +} + +export function handleInternalGuildRoleDelete(data: DiscordPayload) { + if (data.t !== "GUILD_ROLE_DELETE") return; + + const payload = data.d as GuildRoleDeletePayload; + const guild = cache.guilds.get(payload.guild_id); + if (!guild) return; + + const cachedRole = guild.roles.get(payload.role_id)!; + guild.roles.delete(payload.role_id); + eventHandlers.roleDelete?.(guild, cachedRole); +} + +export function handleInternalGuildRoleUpdate(data: DiscordPayload) { + if (data.t !== "GUILD_ROLE_UPDATE") return; + + const payload = data.d as GuildRolePayload; + const guild = cache.guilds.get(payload.guild_id); + if (!guild) return; + + const cachedRole = guild.roles.get(payload.role.id); + if (!cachedRole) return; + + const role = structures.createRole(payload.role); + eventHandlers.roleUpdate?.(guild, role, cachedRole); +} diff --git a/src/module/shardingManager.ts b/src/module/shardingManager.ts index 9d5c58ad1..0ebc598e9 100644 --- a/src/module/shardingManager.ts +++ b/src/module/shardingManager.ts @@ -17,11 +17,6 @@ import { import { delay } from "https://deno.land/std@0.67.0/async/delay.ts"; import { Guild } from "../structures/guild.ts"; import { - GuildBanPayload, - GuildEmojisUpdatePayload, - GuildMemberAddPayload, - GuildMemberUpdatePayload, - GuildMemberChunkPayload, GuildRolePayload, UserPayload, FetchMembersOptions, @@ -48,16 +43,6 @@ import { controllers } from "../controllers/mod.ts"; let shardCounter = 0; let basicSharding = false; -export interface FetchAllMembersRequest { - resolve: Function; - requestedMax: number; - receivedAmount: number; -} - -const fetchAllMembersProcessingRequests = new Map< - string, - Function ->(); const shards: Worker[] = []; let createNextShard = true; @@ -134,163 +119,6 @@ export async function handleDiscordPayload( // Run the appropriate controller for this event. controllers[data.t]?.(data, shardID); - if (data.t === "GUILD_EMOJIS_UPDATE") { - const options = data.d as GuildEmojisUpdatePayload; - const guild = cache.guilds.get(options.guild_id); - if (!guild) return; - - const cachedEmojis = guild.emojis; - guild.emojis = options.emojis; - - return eventHandlers.guildEmojisUpdate?.( - guild, - options.emojis, - cachedEmojis, - ); - } - - if (data.t === "GUILD_MEMBER_ADD") { - const options = data.d as GuildMemberAddPayload; - const guild = cache.guilds.get(options.guild_id); - if (!guild) return; - - const memberCount = guild.memberCount + 1; - guild.memberCount = memberCount; - const member = structures.createMember( - options, - guild, - ); - guild.members.set(options.user.id, member); - - return eventHandlers.guildMemberAdd?.(guild, member); - } - - if (data.t === "GUILD_MEMBER_REMOVE") { - const options = data.d as GuildBanPayload; - const guild = cache.guilds.get(options.guild_id); - if (!guild) return; - - const memberCount = guild.memberCount - 1; - guild.memberCount = memberCount; - - const member = guild.members.get(options.user.id); - eventHandlers.guildMemberRemove?.( - guild, - member || options.user, - ); - return guild.members.delete(options.user.id); - } - - if (data.t === "GUILD_MEMBER_UPDATE") { - const options = data.d as GuildMemberUpdatePayload; - const guild = cache.guilds.get(options.guild_id); - if (!guild) return; - - const cachedMember = guild.members.get(options.user.id); - - const newMemberData = { - ...options, - premium_since: options.premium_since || undefined, - joined_at: new Date(cachedMember?.joinedAt || Date.now()) - .toISOString(), - deaf: cachedMember?.deaf || false, - mute: cachedMember?.mute || false, - }; - const member = structures.createMember( - newMemberData, - guild, - ); - guild.members.set(options.user.id, member); - - if (cachedMember?.nick !== options.nick) { - eventHandlers.nicknameUpdate?.( - guild, - member, - options.nick, - cachedMember?.nick, - ); - } - const roleIDs = cachedMember?.roles || []; - - roleIDs.forEach((id) => { - if (!options.roles.includes(id)) { - eventHandlers.roleLost?.(guild, member, id); - } - }); - - options.roles.forEach((id) => { - if (!roleIDs.includes(id)) { - eventHandlers.roleGained?.(guild, member, id); - } - }); - - return eventHandlers.guildMemberUpdate?.(guild, member, cachedMember); - } - - if (data.t === "GUILD_MEMBERS_CHUNK") { - const options = data.d as GuildMemberChunkPayload; - const guild = cache.guilds.get(options.guild_id); - if (!guild) return; - - options.members.forEach((member) => { - guild.members.set( - member.user.id, - structures.createMember( - member, - guild, - ), - ); - }); - - // Check if its necessary to resolve the fetchmembers promise for this chunk or if more chunks will be coming - if ( - options.nonce - ) { - const resolve = fetchAllMembersProcessingRequests.get(options.nonce); - if (!resolve) return; - - if (options.chunk_index + 1 === options.chunk_count) { - fetchAllMembersProcessingRequests.delete(options.nonce); - resolve(guild.members); - } - } - } - - if (data.t === "GUILD_ROLE_DELETE") { - const options = data.d as GuildRoleDeletePayload; - const guild = cache.guilds.get(options.guild_id); - if (!guild) return; - - const cachedRole = guild.roles.get(options.role_id)!; - guild.roles.delete(options.role_id); - return eventHandlers.roleDelete?.(guild, cachedRole); - } - - if ( - data.t && - ["GUILD_ROLE_CREATE", "GUILD_ROLE_UPDATE"] - .includes(data.t) - ) { - const options = data.d as GuildRolePayload; - const guild = cache.guilds.get(options.guild_id); - if (!guild) return; - - if (data.t === "GUILD_ROLE_CREATE") { - const role = structures.createRole(options.role); - const roles = guild.roles.set(options.role.id, role); - guild.roles = roles; - return eventHandlers.roleCreate?.(guild, role); - } - - const cachedRole = guild.roles.get(options.role.id); - if (!cachedRole) return; - - if (data.t === "GUILD_ROLE_UPDATE") { - const role = structures.createRole(options.role); - return eventHandlers.roleUpdate?.(guild, role, cachedRole); - } - } - if (data.t === "MESSAGE_CREATE") { const options = data.d as MessageCreateOptions; const channel = cache.channels.get(options.channel_id); @@ -550,7 +378,7 @@ export async function requestAllMembers( options?: FetchMembersOptions, ) { const nonce = `${guild.id}-${Math.random().toString()}`; - fetchAllMembersProcessingRequests.set(nonce, resolve); + cache.fetchAllMembersProcessingRequests.set(nonce, resolve); if (basicSharding) { return requestGuildMembers(guild.id, guild.shardID, nonce, options); diff --git a/src/types/discord.ts b/src/types/discord.ts index 85e5ca87f..aabde1db7 100644 --- a/src/types/discord.ts +++ b/src/types/discord.ts @@ -14,13 +14,21 @@ export interface DiscordPayload { t?: | "READY" | "CHANNEL_CREATE" - | "CHANNEL_UPDATE" | "CHANNEL_DELETE" + | "CHANNEL_UPDATE" | "GUILD_CREATE" | "GUILD_DELETE" | "GUILD_UPDATE" | "GUILD_BAN_ADD" - | "GUILD_BAN_REMOVE"; + | "GUILD_BAN_REMOVE" + | "GUILD_EMOJIS_UPDATE" + | "GUILD_MEMBER_ADD" + | "GUILD_MEMBER_REMOVE" + | "GUILD_MEMBER_UPDATE" + | "GUILD_MEMBERS_CHUNK" + | "GUILD_ROLE_CREATE" + | "GUILD_ROLE_DELETE" + | "GUILD_ROLE_UPDATE"; } export interface DiscordBotGatewayData { diff --git a/src/utils/cache.ts b/src/utils/cache.ts index b15a46b65..7dc3fe88b 100644 --- a/src/utils/cache.ts +++ b/src/utils/cache.ts @@ -12,6 +12,7 @@ export interface CacheData { messages: Collection; unavailableGuilds: Collection; presences: Collection; + fetchAllMembersProcessingRequests: Collection; } export const cache: CacheData = { @@ -21,6 +22,7 @@ export const cache: CacheData = { messages: new Collection(), unavailableGuilds: new Collection(), presences: new Collection(), + fetchAllMembersProcessingRequests: new Collection(), }; async function cleanMessageCache() {