mirror of
https://github.com/discordeno/discordeno.git
synced 2026-06-16 19:28:17 +00:00
fix(controllers/READY): reimplement guild cache mechanism (#647)
* add lastShardID * fix ready event controller * forgot to push this file * move ready to its own file * some changes * Update READY.ts * some changes idk if they are good * Update options.ts * Update READY.ts * Update guilds.ts
This commit is contained in:
@@ -0,0 +1,104 @@
|
||||
import {
|
||||
eventHandlers,
|
||||
lastShardID,
|
||||
setApplicationID,
|
||||
setBotID,
|
||||
} from "../../bot.ts";
|
||||
import { DiscordPayload, ReadyPayload } from "../../types/discord.ts";
|
||||
import { cache } from "../../util/cache.ts";
|
||||
import { delay } from "../../util/utils.ts";
|
||||
import { allowNextShard, basicShards } from "../../ws/mod.ts";
|
||||
import { initialMemberLoadQueue } from "../structures/guild.ts";
|
||||
import { structures } from "../structures/mod.ts";
|
||||
import { cacheHandlers } from "./cache.ts";
|
||||
|
||||
/** This function is the internal handler for the ready event. Users can override this with controllers if desired. */
|
||||
export async function handleInternalReady(
|
||||
data: DiscordPayload,
|
||||
shardID: number,
|
||||
) {
|
||||
// The bot has already started, the last shard is resumed, however.
|
||||
if (cache.isReady) return;
|
||||
|
||||
const payload = data.d as ReadyPayload;
|
||||
setBotID(payload.user.id);
|
||||
setApplicationID(payload.application.id);
|
||||
|
||||
// Triggered on each shard
|
||||
eventHandlers.shardReady?.(shardID);
|
||||
// Save when the READY event was received to prevent infinite load loops
|
||||
const now = Date.now();
|
||||
|
||||
const shard = basicShards.get(shardID);
|
||||
if (!shard) return;
|
||||
|
||||
// Set ready to false just to go sure
|
||||
shard.ready = false;
|
||||
// All guilds are unavailable at first
|
||||
shard.unavailableGuildIDs = new Set(payload.guilds.map((g) => g.id));
|
||||
|
||||
// Start ready check in 2 seconds
|
||||
setTimeout(() => checkReady(payload, shardID, now), 2000);
|
||||
|
||||
// Wait 5 seconds to spawn next shard
|
||||
await delay(5000);
|
||||
allowNextShard();
|
||||
}
|
||||
|
||||
// Don't pass the shard itself because unavailableGuilds won't be updated by the GUILD_CREATE event
|
||||
/** This function checks if the shard is fully loaded */
|
||||
function checkReady(payload: ReadyPayload, shardID: number, now: number) {
|
||||
const shard = basicShards.get(shardID);
|
||||
if (!shard) return;
|
||||
|
||||
// Check if all guilds were loaded
|
||||
if (shard.unavailableGuildIDs.size) {
|
||||
if (Date.now() - now > 10000) {
|
||||
eventHandlers.shardFailedToLoad?.(shardID, shard.unavailableGuildIDs);
|
||||
// Force execute the loaded function to prevent infinite loop
|
||||
loaded(shardID);
|
||||
} else {
|
||||
// Not all guilds were loaded but 10 seconds haven't passed so check again
|
||||
setTimeout(() => checkReady(payload, shardID, now), 2000);
|
||||
}
|
||||
} else {
|
||||
// All guilds were loaded
|
||||
loaded(shardID);
|
||||
}
|
||||
}
|
||||
|
||||
async function loaded(shardID: number) {
|
||||
const shard = basicShards.get(shardID);
|
||||
if (!shard) return;
|
||||
|
||||
shard.ready = true;
|
||||
|
||||
// If it is the last shard we can go full ready
|
||||
if (shardID === lastShardID - 1) {
|
||||
// Still some shards are loading so wait another 2 seconds for them
|
||||
if (basicShards.some((shard) => !shard.ready)) {
|
||||
setTimeout(() => loaded(shardID), 2000);
|
||||
} else {
|
||||
cache.isReady = true;
|
||||
eventHandlers.ready?.();
|
||||
|
||||
// All the members that came in on guild creates should now be processed 1 by 1
|
||||
for (const [guildID, members] of initialMemberLoadQueue.entries()) {
|
||||
await Promise.allSettled(
|
||||
members.map(async (member) => {
|
||||
const memberStruct = await structures.createMemberStruct(
|
||||
member,
|
||||
guildID,
|
||||
);
|
||||
|
||||
return cacheHandlers.set(
|
||||
"members",
|
||||
memberStruct.id,
|
||||
memberStruct,
|
||||
);
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,8 @@ import {
|
||||
} from "../../types/mod.ts";
|
||||
import { cache } from "../../util/cache.ts";
|
||||
import { Collection } from "../../util/collection.ts";
|
||||
import { Member, structures } from "../structures/mod.ts";
|
||||
import { basicShards } from "../../ws/mod.ts";
|
||||
import { structures } from "../structures/mod.ts";
|
||||
import { cacheHandlers } from "./cache.ts";
|
||||
|
||||
export async function handleInternalGuildCreate(
|
||||
@@ -26,15 +27,22 @@ export async function handleInternalGuildCreate(
|
||||
);
|
||||
await cacheHandlers.set("guilds", guildStruct.id, guildStruct);
|
||||
|
||||
if (await cacheHandlers.has("unavailableGuilds", payload.id)) {
|
||||
const shard = basicShards.get(shardID);
|
||||
|
||||
if (shard?.unavailableGuildIDs.has(payload.id)) {
|
||||
await cacheHandlers.delete("unavailableGuilds", payload.id);
|
||||
|
||||
shard.unavailableGuildIDs.delete(payload.id);
|
||||
}
|
||||
|
||||
if (!cache.isReady) return eventHandlers.guildLoaded?.(guildStruct);
|
||||
eventHandlers.guildCreate?.(guildStruct);
|
||||
}
|
||||
|
||||
export async function handleInternalGuildDelete(data: DiscordPayload) {
|
||||
export async function handleInternalGuildDelete(
|
||||
data: DiscordPayload,
|
||||
shardID: number,
|
||||
) {
|
||||
const payload = data.d as GuildDeletePayload;
|
||||
cacheHandlers.forEach("messages", (message) => {
|
||||
if (message.guildID === payload.id) {
|
||||
@@ -62,6 +70,9 @@ export async function handleInternalGuildDelete(data: DiscordPayload) {
|
||||
});
|
||||
|
||||
if (payload.unavailable) {
|
||||
const shard = basicShards.get(shardID);
|
||||
if (shard) shard.unavailableGuildIDs.add(payload.id);
|
||||
|
||||
return cacheHandlers.set("unavailableGuilds", payload.id, Date.now());
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { eventHandlers, setApplicationID, setBotID } from "../../bot.ts";
|
||||
import { eventHandlers } from "../../bot.ts";
|
||||
import {
|
||||
DiscordPayload,
|
||||
IntegrationCreateUpdateEvent,
|
||||
@@ -6,76 +6,14 @@ import {
|
||||
InviteCreateEvent,
|
||||
InviteDeleteEvent,
|
||||
PresenceUpdatePayload,
|
||||
ReadyPayload,
|
||||
TypingStartPayload,
|
||||
UserPayload,
|
||||
VoiceStateUpdatePayload,
|
||||
WebhookUpdatePayload,
|
||||
} from "../../types/mod.ts";
|
||||
import { cache } from "../../util/cache.ts";
|
||||
import { delay } from "../../util/utils.ts";
|
||||
import { allowNextShard } from "../../ws/shard_manager.ts";
|
||||
import { initialMemberLoadQueue } from "../structures/guild.ts";
|
||||
import { structures } from "../structures/mod.ts";
|
||||
import { cacheHandlers } from "./cache.ts";
|
||||
|
||||
/** This function is the internal handler for the ready event. Users can override this with controllers if desired. */
|
||||
export async function handleInternalReady(
|
||||
data: DiscordPayload,
|
||||
shardID: number,
|
||||
) {
|
||||
const payload = data.d as ReadyPayload;
|
||||
setBotID(payload.user.id);
|
||||
setApplicationID(payload.application.id);
|
||||
|
||||
// Triggered on each shard
|
||||
eventHandlers.shardReady?.(shardID);
|
||||
if (payload.shard && shardID === payload.shard[1] - 1) {
|
||||
const loadedAllGuilds = async () => {
|
||||
const guildsMissing = async () => {
|
||||
for (const g of payload.guilds) {
|
||||
if (!(await cacheHandlers.has("guilds", g.id))) return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
if (await guildsMissing()) {
|
||||
setTimeout(loadedAllGuilds, 2000);
|
||||
} else {
|
||||
// The bot has already started, the last shard is resumed, however.
|
||||
if (cache.isReady) return;
|
||||
|
||||
cache.isReady = true;
|
||||
eventHandlers.ready?.();
|
||||
|
||||
// All the members that came in on guild creates should now be processed 1 by 1
|
||||
for (const [guildID, members] of initialMemberLoadQueue.entries()) {
|
||||
await Promise.all(
|
||||
members.map(async (member) => {
|
||||
const memberStruct = await structures.createMemberStruct(
|
||||
member,
|
||||
guildID,
|
||||
);
|
||||
|
||||
return cacheHandlers.set(
|
||||
"members",
|
||||
memberStruct.id,
|
||||
memberStruct,
|
||||
);
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
setTimeout(loadedAllGuilds, 2000);
|
||||
}
|
||||
|
||||
// Wait 5 seconds to spawn next shard
|
||||
await delay(5000);
|
||||
allowNextShard();
|
||||
}
|
||||
|
||||
/** This function is the internal handler for the presence update event. Users can override this with controllers if desired. */
|
||||
export async function handleInternalPresenceUpdate(data: DiscordPayload) {
|
||||
const payload = data.d as PresenceUpdatePayload;
|
||||
@@ -233,7 +171,7 @@ export function handleInternalIntegrationDelete(data: DiscordPayload) {
|
||||
|
||||
export function handleInternalInviteCreate(payload: DiscordPayload) {
|
||||
if (payload.t !== "INVITE_CREATE") return;
|
||||
|
||||
//TODO: replace with tocamelcase
|
||||
const {
|
||||
channel_id: channelID,
|
||||
created_at: createdAt,
|
||||
|
||||
@@ -40,7 +40,6 @@ import {
|
||||
handleInternalInviteCreate,
|
||||
handleInternalInviteDelete,
|
||||
handleInternalPresenceUpdate,
|
||||
handleInternalReady,
|
||||
handleInternalTypingStart,
|
||||
handleInternalUserUpdate,
|
||||
handleInternalVoiceStateUpdate,
|
||||
@@ -52,6 +51,7 @@ import {
|
||||
handleInternalMessageReactionRemoveAll,
|
||||
handleInternalMessageReactionRemoveEmoji,
|
||||
} from "./reactions.ts";
|
||||
import { handleInternalReady } from "./READY.ts";
|
||||
import {
|
||||
handleInternalGuildRoleCreate,
|
||||
handleInternalGuildRoleDelete,
|
||||
|
||||
Reference in New Issue
Block a user