diff --git a/src/controllers/channels.ts b/src/controllers/channels.ts index 38db306da..5e8f1b487 100644 --- a/src/controllers/channels.ts +++ b/src/controllers/channels.ts @@ -2,41 +2,37 @@ import { cache } from "../utils/cache.ts"; import { ChannelCreatePayload, ChannelTypes } from "../types/channel.ts"; import { eventHandlers } from "../module/client.ts"; import { structures } from "../structures/mod.ts"; +import { DiscordPayload } from "../types/discord.ts"; -export const handleInternalChannelCreate = (data: ChannelCreatePayload) => { - const channel = structures.createChannel(data); +export function handleInternalChannelCreate(data: DiscordPayload) { + if (data.t !== "CHANNEL_CREATE") return; + + const payload = data.d as ChannelCreatePayload; + const channel = structures.createChannel(payload); cache.channels.set(channel.id, channel); + if (channel.guildID) { const guild = cache.guilds.get(channel.guildID); guild?.channels.set(channel.id, channel); } + eventHandlers.channelCreate?.(channel); -}; +} -export const handleInternalChannelUpdate = (data: ChannelCreatePayload) => { - const cachedChannel = cache.channels.get(data.id); - const channel = structures.createChannel(data); - cache.channels.set(channel.id, channel); +export function handleInternalChannelDelete(data: DiscordPayload) { + if (data.t !== "CHANNEL_DELETE") return; + + const payload = data.d as ChannelCreatePayload; + + const cachedChannel = cache.channels.get(payload.id); if (!cachedChannel) return; - if (channel.guildID) { - const guild = cache.guilds.get(channel.guildID); - guild?.channels.set(channel.id, channel); - } - - eventHandlers.channelUpdate?.(channel, cachedChannel); -}; - -export const handleInternalChannelDelete = (data: ChannelCreatePayload) => { - const cachedChannel = cache.channels.get(data.id); - if (!cachedChannel) return; - - if (cachedChannel.type === ChannelTypes.GUILD_VOICE && data.guild_id) { - const guild = cache.guilds.get(data.guild_id); + if (cachedChannel.type === ChannelTypes.GUILD_VOICE && payload.guild_id) { + const guild = cache.guilds.get(payload.guild_id); if (guild) { guild.voiceStates.forEach((vs, key) => { - if (vs.channelID !== data.id) return; + if (vs.channelID !== payload.id) return; // Since this channel was deleted all voice states for this channel should be deleted guild.voiceStates.delete(key); @@ -48,12 +44,29 @@ export const handleInternalChannelDelete = (data: ChannelCreatePayload) => { }); } - guild?.channels.delete(data.id); + guild?.channels.delete(payload.id); } - cache.channels.delete(data.id); + cache.channels.delete(payload.id); cache.messages.forEach((message) => { - if (message.channelID === data.id) cache.messages.delete(message.id); + if (message.channelID === payload.id) cache.messages.delete(message.id); }); eventHandlers.channelDelete?.(cachedChannel); -}; +} + +export function handleInternalChannelUpdate(data: DiscordPayload) { + if (data.t !== "CHANNEL_UPDATE") return; + + const payload = data.d as ChannelCreatePayload; + const cachedChannel = cache.channels.get(payload.id); + const channel = structures.createChannel(payload); + cache.channels.set(channel.id, channel); + if (!cachedChannel) return; + + if (channel.guildID) { + const guild = cache.guilds.get(channel.guildID); + guild?.channels.set(channel.id, channel); + } + + eventHandlers.channelUpdate?.(channel, cachedChannel); +} diff --git a/src/controllers/misc.ts b/src/controllers/misc.ts new file mode 100644 index 000000000..ed4caeecc --- /dev/null +++ b/src/controllers/misc.ts @@ -0,0 +1,25 @@ +import { delay } from "https://deno.land/std@0.67.0/async/delay.ts"; +import { eventHandlers, setBotID } from "../module/client.ts"; +import { allowNextShard } from "../module/shardingManager.ts"; +import { DiscordPayload, ReadyPayload } from "../types/discord.ts"; +import { cache } from "../utils/cache.ts"; + +export async function handleInternalReady(data: DiscordPayload, shardID: number) { + if (data.t !== "READY") return; + + const payload = data.d as ReadyPayload; + setBotID(payload.user.id); + + // Triggered on each shard + eventHandlers.shardReady?.(shardID); + if (payload.shard && shardID === payload.shard[1] - 1) { + // Wait 10 seconds to allow all guild create events to be processed + await delay(10000); + cache.isReady = true; + eventHandlers.ready?.(); + } + + // Wait 5 seconds to spawn next shard + await delay(5000); + allowNextShard() +} diff --git a/src/controllers/mod.ts b/src/controllers/mod.ts new file mode 100644 index 000000000..5c184510f --- /dev/null +++ b/src/controllers/mod.ts @@ -0,0 +1,21 @@ +import { + handleInternalChannelCreate, + handleInternalChannelDelete, + handleInternalChannelUpdate, +} from "./channels.ts"; +import { + handleInternalGuildCreate, + handleInternalGuildDelete, + handleInternalGuildUpdate, +} from "./guilds.ts"; +import { handleInternalReady } from "./misc.ts"; + +export let controllers = { + READY: handleInternalReady, + CHANNEL_CREATE: handleInternalChannelCreate, + CHANNEL_DELETE: handleInternalChannelDelete, + CHANNEL_UPDATE: handleInternalChannelUpdate, + GUILD_CREATE: handleInternalGuildCreate, + GUILD_DELETE: handleInternalGuildDelete, + GUILD_UPDATE: handleInternalGuildUpdate, +}; diff --git a/src/module/client.ts b/src/module/client.ts index 4dfeb4c5c..a05d4f261 100644 --- a/src/module/client.ts +++ b/src/module/client.ts @@ -61,5 +61,5 @@ export function updateEventHandlers(newEventHandlers: EventHandlers) { } export function setBotID(id: string) { - botID = id; + if (botID !== id) botID = id; } diff --git a/src/module/shardingManager.ts b/src/module/shardingManager.ts index 879d73bbe..b0af2d6c3 100644 --- a/src/module/shardingManager.ts +++ b/src/module/shardingManager.ts @@ -59,6 +59,7 @@ import { } from "./basicShard.ts"; import { BotStatusRequest } from "../utils/utils.ts"; import { structures } from "../structures/mod.ts"; +import { controllers } from "../controllers/mod.ts"; let shardCounter = 0; let basicSharding = false; @@ -76,6 +77,11 @@ const fetchAllMembersProcessingRequests = new Map< const shards: Worker[] = []; let createNextShard = true; +/** This function is meant to be used on the ready event to alert the library to start the next shard. */ +export function allowNextShard(enabled = true) { + createNextShard = enabled; +} + export function createShardWorker(shardID?: number) { const path = new URL("./shard.ts", import.meta.url).toString(); const shard = new Worker(path, { type: "module", deno: true }); @@ -140,32 +146,9 @@ export async function handleDiscordPayload( // Incase the user wants to listen to heartbeat responses return eventHandlers.heartbeat?.(); case GatewayOpcode.Dispatch: - if (data.t === "READY") { - const payload = data.d as ReadyPayload; - setBotID(payload.user.id); - // Triggered on each shard - eventHandlers.shardReady?.(shardID); - if (payload.shard && shardID === payload.shard[1] - 1) { - // Wait 10 seconds to allow all guild create events to be processed - await delay(10000); - cache.isReady = true; - eventHandlers.ready?.(); - } - // Wait 5 seconds to spawn next shard - await delay(5000); - createNextShard = true; - } - - if (data.t === "CHANNEL_CREATE") { - return handleInternalChannelCreate(data.d as ChannelCreatePayload); - } - - if (data.t === "CHANNEL_UPDATE") { - return handleInternalChannelUpdate(data.d as ChannelCreatePayload); - } - if (data.t === "CHANNEL_DELETE") { - return handleInternalChannelDelete(data.d as ChannelCreatePayload); - } + if (!data.t) return; + // Run the appropriate controller for this event. + controllers[data.t]?.(data, shardID); if (data.t === "GUILD_CREATE") { const options = data.d as CreateGuildPayload; diff --git a/src/types/discord.ts b/src/types/discord.ts index 20227b50a..6e11f2cfd 100644 --- a/src/types/discord.ts +++ b/src/types/discord.ts @@ -11,7 +11,14 @@ export interface DiscordPayload { /** The sequence number, used for resuming sessions and heartbeats. ONLY for OPCode 0 */ s?: number; /** The event name for this payload. ONLY for OPCode 0 */ - t?: string; + t?: + | "READY" + | "CHANNEL_CREATE" + | "CHANNEL_UPDATE" + | "CHANNEL_DELETE" + | "GUILD_CREATE" + | "GUILD_DELETE" + | "GUILD_UPDATE"; } export interface DiscordBotGatewayData {