Merge pull request #788 from itohatweb/loop-loggers

added(eventHandlers): debug loop functions
This commit is contained in:
Skillz4Killz
2021-04-09 15:38:58 -04:00
committed by GitHub
26 changed files with 233 additions and 13 deletions

View File

@@ -32,6 +32,10 @@ export async function handleChannelDelete(data: DiscordGatewayPayload) {
await cacheHandlers.delete("channels", payload.id);
cacheHandlers.forEach("messages", (message) => {
eventHandlers.debug(
"loop",
`Running forEach messages loop in CHANNEL_DELTE file.`,
);
if (message.channelId === payload.id) {
cacheHandlers.delete("messages", message.id);
}

View File

@@ -27,18 +27,30 @@ export async function handleGuildDelete(
}
cacheHandlers.forEach("messages", (message) => {
eventHandlers.debug(
"loop",
`1. Running forEach messages loop in CHANNEL_DELTE file.`,
);
if (message.guildId === payload.id) {
cacheHandlers.delete("messages", message.id);
}
});
cacheHandlers.forEach("channels", (channel) => {
eventHandlers.debug(
"loop",
`2. Running forEach channels loop in CHANNEL_DELTE file.`,
);
if (channel.guildId === payload.id) {
cacheHandlers.delete("channels", channel.id);
}
});
cacheHandlers.forEach("members", (member) => {
eventHandlers.debug(
"loop",
`3. Running forEach members loop in CHANNEL_DELTE file.`,
);
if (!member.guilds.has(payload.id)) return;
member.guilds.delete(payload.id);

View File

@@ -46,12 +46,20 @@ export async function handleGuildMemberUpdate(data: DiscordGatewayPayload) {
const roleIds = guildMember.roles || [];
roleIds.forEach((id) => {
eventHandlers.debug(
"loop",
`1. Running forEach loop in GUILD_MEMBER_UPDATE file.`,
);
if (!payload.roles.includes(id)) {
eventHandlers.roleLost?.(guild, memberStruct, id);
}
});
payload.roles.forEach((id) => {
eventHandlers.debug(
"loop",
`2. Running forEach loop in GUILD_MEMBER_UPDATE file.`,
);
if (!roleIds.includes(id)) {
eventHandlers.roleGained?.(guild, memberStruct, id);
}

View File

@@ -32,7 +32,13 @@ export async function handleReady(
shard.unavailableGuildIds = new Set(payload.guilds.map((g) => g.id));
// Start ready check in 2 seconds
setTimeout(async () => await checkReady(payload, shardId, now), 2000);
setTimeout(async () => {
eventHandlers.debug(
"loop",
`1. Running setTimeout in READY file.`,
);
await checkReady(payload, shardId, now);
}, 2000);
// Wait 5 seconds to spawn next shard
await delay(5000);
@@ -53,7 +59,13 @@ async function checkReady(payload: DiscordReady, shardId: number, now: number) {
await loaded(shardId);
} else {
// Not all guilds were loaded but 10 seconds haven't passed so check again
setTimeout(async () => await checkReady(payload, shardId, now), 2000);
setTimeout(async () => {
eventHandlers.debug(
"loop",
`2. Running setTimeout in READY file.`,
);
await checkReady(payload, shardId, now);
}, 2000);
}
} else {
// All guilds were loaded
@@ -71,13 +83,23 @@ async function loaded(shardId: number) {
if (shardId === ws.lastShardId - 1) {
// Still some shards are loading so wait another 2 seconds for them
if (ws.shards.some((shard) => !shard.ready)) {
setTimeout(async () => await loaded(shardId), 2000);
setTimeout(async () => {
eventHandlers.debug(
"loop",
`3. Running setTimeout in CHANNEL_DELTE file.`,
);
await 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()) {
eventHandlers.debug(
"loop",
"Running for of loop in READY file for loading members.",
);
await Promise.allSettled(
members.map(async (member) => {
const memberStruct = await structures.createMemberStruct(

View File

@@ -10,6 +10,10 @@ export async function handleUserUpdate(data: DiscordGatewayPayload) {
if (!member) return;
Object.entries(userData).forEach(([key, value]) => {
eventHandlers.debug(
"loop",
`Running forEach loop in USER_UPDATE file.`,
);
// @ts-ignore index signatures
if (member[key] !== value) return member[key] = value;
});

View File

@@ -15,10 +15,18 @@ export async function handleGuildRoleDelete(data: DiscordGatewayPayload) {
// For bots without GUILD_MEMBERS member.roles is never updated breaking permissions checking.
cacheHandlers.forEach("members", (member) => {
eventHandlers.debug(
"loop",
`1. Running forEach members loop in GUILD_ROLE_DELETE file.`,
);
// Not in the relevant guild so just skip.
if (!member.guilds.has(guild.id)) return;
member.guilds.forEach((g) => {
eventHandlers.debug(
"loop",
`2. Running forEach loop in CHANNEL_DELTE file.`,
);
// Member does not have this role
if (!g.roles.includes(payload.role_id)) return;
// Remove this role from the members cache

View File

@@ -1,3 +1,4 @@
import { eventHandlers } from "../../bot.ts";
import { cacheHandlers } from "../../cache.ts";
import { rest } from "../../rest/rest.ts";
import { structures } from "../../structures/mod.ts";
@@ -19,6 +20,10 @@ export async function createChannel(
const requiredPerms: Set<PermissionStrings> = new Set(["MANAGE_CHANNELS"]);
options?.permissionOverwrites?.forEach((overwrite) => {
eventHandlers.debug(
"loop",
`Running forEach loop in create_channel file.`,
);
overwrite.allow.forEach(requiredPerms.add, requiredPerms);
overwrite.deny.forEach(requiredPerms.add, requiredPerms);
});

View File

@@ -1,3 +1,4 @@
import { eventHandlers } from "../../bot.ts";
import { rest } from "../../rest/rest.ts";
import { ModifyChannel } from "../../types/channels/modify_channel.ts";
import { endpoints } from "../../util/constants.ts";
@@ -77,6 +78,10 @@ function processEditChannelQueue() {
const now = Date.now();
editChannelNameTopicQueue.forEach((request) => {
eventHandlers.debug(
"loop",
`Running forEach loop in edit_channel file.`,
);
if (now > request.timestamp) return;
// 10 minutes have passed so we can reset this channel again
if (!request.items.length) {
@@ -96,7 +101,13 @@ function processEditChannelQueue() {
});
if (editChannelNameTopicQueue.size) {
setTimeout(() => processEditChannelQueue(), 600000);
setTimeout(() => {
eventHandlers.debug(
"loop",
`Running setTimeout in EDIT_CHANNEL file.`,
);
processEditChannelQueue();
}, 600000);
} else {
editChannelProcessing = false;
}

View File

@@ -1,4 +1,4 @@
import { applicationId } from "../../bot.ts";
import { applicationId, eventHandlers } from "../../bot.ts";
import { cache } from "../../cache.ts";
import { rest } from "../../rest/rest.ts";
import { DiscordenoInteractionResponse } from "../../types/discordeno/interaction_response.ts";
@@ -25,7 +25,13 @@ export async function sendInteractionResponse(
// Expire in 15 minutes
cache.executedSlashCommands.set(token, id);
setTimeout(
() => cache.executedSlashCommands.delete(token),
() => {
eventHandlers.debug(
"loop",
`Running setTimeout in send_interaction_response file.`,
);
cache.executedSlashCommands.delete(token);
},
900000,
);

View File

@@ -1,3 +1,4 @@
import { eventHandlers } from "../../bot.ts";
import { cacheHandlers } from "../../cache.ts";
import { rest } from "../../rest/rest.ts";
import { Emoji } from "../../types/emojis/emoji.ts";
@@ -17,7 +18,13 @@ export async function getEmojis(guildId: string, addToCache = true) {
const guild = await cacheHandlers.get("guilds", guildId);
if (!guild) throw new Error(Errors.GUILD_NOT_FOUND);
result.forEach((emoji) => guild.emojis.set(emoji.id!, emoji));
result.forEach((emoji) => {
eventHandlers.debug(
"loop",
`Running forEach loop in get_emojis file.`,
);
guild.emojis.set(emoji.id!, emoji);
});
cacheHandlers.set("guilds", guildId, guild);
}

View File

@@ -1,4 +1,4 @@
import { identifyPayload } from "../../bot.ts";
import { eventHandlers, identifyPayload } from "../../bot.ts";
import { cacheHandlers } from "../../cache.ts";
import { rest } from "../../rest/rest.ts";
import { Member, structures } from "../../structures/mod.ts";
@@ -32,6 +32,8 @@ export async function getMembers(guildId: string, options?: GetMemberOptions) {
(options?.limit ?? guild.memberCount) > members.size &&
membersLeft > 0
) {
eventHandlers.debug("loop", "Running while loop in getMembers function.");
if (options?.limit && options.limit > 1000) {
console.log(
`Paginating get members from REST. #${loops} / ${
@@ -64,7 +66,13 @@ export async function getMembers(guildId: string, options?: GetMemberOptions) {
if (!memberStructures.length) break;
memberStructures.forEach((member) => members.set(member.id, member));
memberStructures.forEach((member) => {
eventHandlers.debug(
"loop",
`Running forEach loop in get_members file.`,
);
members.set(member.id, member);
});
options = {
limit: options?.limit,

View File

@@ -1,3 +1,4 @@
import { eventHandlers } from "../../bot.ts";
import { addReaction } from "./add_reaction.ts";
/** Adds multiple reactions to a message. If `ordered` is true(default is false), it will add the reactions one at a time in the order provided. Note: Reaction takes the form of **name:id** for custom guild emoji, or Unicode characters. Requires READ_MESSAGE_HISTORY and ADD_REACTIONS */
@@ -13,6 +14,10 @@ export async function addReactions(
);
} else {
for (const reaction of reactions) {
eventHandlers.debug(
"loop",
"Running for of loop in addReactions function.",
);
await addReaction(channelId, messageId, reaction);
}
}

View File

@@ -3,6 +3,10 @@ import { rest } from "./rest.ts";
/** Cleans up the queues by checking if there is nothing left and removing it. */
export function cleanupQueues() {
for (const [key, queue] of rest.pathQueues) {
rest.eventHandlers.debug(
"loop",
"Running for of loop in cleanupQueues function.",
);
if (queue.length) continue;
// REMOVE IT FROM CACHE
rest.pathQueues.delete(key);

View File

@@ -1,3 +1,4 @@
import { eventHandlers } from "../bot.ts";
import { DiscordHTTPResponseCodes } from "../types/codes/http_response_codes.ts";
import { delay } from "../util/utils.ts";
import { rest } from "./rest.ts";
@@ -8,9 +9,19 @@ export async function processQueue(id: string) {
if (!queue) return;
while (queue.length) {
rest.eventHandlers.debug(
"loop",
"Running while loop in processQueue function.",
);
// IF THE BOT IS GLOBALLY RATELIMITED TRY AGAIN
if (rest.globallyRateLimited) {
setTimeout(() => processQueue(id), 1000);
setTimeout(() => {
eventHandlers.debug(
"loop",
`Running setTimeout in processQueue function.`,
);
processQueue(id);
}, 1000);
break;
}

View File

@@ -1,3 +1,4 @@
import { eventHandlers } from "../bot.ts";
import { rest } from "./rest.ts";
/** This will create a infinite loop running in 1 seconds using tail recursion to keep rate limits clean. When a rate limit resets, this will remove it so the queue can proceed. */
@@ -5,6 +6,10 @@ export function processRateLimitedPaths() {
const now = Date.now();
rest.ratelimitedPaths.forEach((value, key) => {
rest.eventHandlers.debug(
"loop",
`Running forEach loop in process_rate_limited_paths file.`,
);
// IF THE TIME HAS NOT REACHED CANCEL
if (value.resetTimestamp > now) return;
// RATE LIMIT IS OVER, DELETE THE RATE LIMITER
@@ -20,6 +25,12 @@ export function processRateLimitedPaths() {
} else {
rest.processingRateLimitedPaths = true;
// RECHECK IN 1 SECOND
setTimeout(() => processRateLimitedPaths(), 1000);
setTimeout(() => {
eventHandlers.debug(
"loop",
`Running setTimeout in processRateLimitedPaths function.`,
);
processRateLimitedPaths();
}, 1000);
}
}

View File

@@ -1,3 +1,4 @@
import { eventHandlers } from "../bot.ts";
import { cache } from "../cache.ts";
import { channelOverwriteHasPermission } from "../helpers/channels/channel_overwrite_has_permission.ts";
import { deleteChannel } from "../helpers/channels/delete_channel.ts";
@@ -81,6 +82,10 @@ export async function createChannelStruct(
const props: Record<string, PropertyDescriptor> = {};
Object.keys(rest).forEach((key) => {
eventHandlers.debug(
"loop",
`Running forEach loop in createChannelStruct function.`,
);
// @ts-ignore index signature
props[key] = createNewProp(rest[key]);
});

View File

@@ -1,4 +1,4 @@
import { botId } from "../bot.ts";
import { botId, eventHandlers } from "../bot.ts";
import { cache, cacheHandlers } from "../cache.ts";
import { deleteServer } from "../helpers/guilds/delete_server.ts";
import { editGuild } from "../helpers/guilds/edit_guild.ts";
@@ -147,6 +147,10 @@ export async function createGuildStruct(
const props: Record<string, ReturnType<typeof createNewProp>> = {};
for (const key of Object.keys(rest)) {
eventHandlers.debug(
"loop",
`Running for of loop in createGuildStruct function.`,
);
// @ts-ignore index signature
props[key] = createNewProp(rest[key]);
}

View File

@@ -1,3 +1,4 @@
import { eventHandlers } from "../bot.ts";
import { cache, cacheHandlers } from "../cache.ts";
import { avatarURL } from "../helpers/members/avatar_url.ts";
import { banMember } from "../helpers/members/ban_member.ts";
@@ -87,11 +88,19 @@ export async function createMemberStruct(
const props: Record<string, ReturnType<typeof createNewProp>> = {};
for (const key of Object.keys(rest)) {
eventHandlers.debug(
"loop",
`Running for of loop for Object.keys(rest) in createMemberStruct function.`,
);
// @ts-ignore index signature
props[key] = createNewProp(rest[key]);
}
for (const key of Object.keys(user)) {
eventHandlers.debug(
"loop",
`Running for of for Object.keys(user) loop in createMemberStruct function.`,
);
// @ts-ignore index signature
props[key] = createNewProp(user[key]);
}
@@ -105,6 +114,10 @@ export async function createMemberStruct(
const cached = await cacheHandlers.get("members", user.id);
if (cached) {
for (const [id, guild] of cached.guilds.entries()) {
eventHandlers.debug(
"loop",
`Running for of for cached.guilds.entries() loop in createMemberStruct function.`,
);
member.guilds.set(id, guild);
}
}

View File

@@ -1,3 +1,4 @@
import { eventHandlers } from "../bot.ts";
import { cache, cacheHandlers } from "../cache.ts";
import { sendDirectMessage } from "../helpers/members/send_direct_message.ts";
import { addReaction } from "../helpers/messages/add_reaction.ts";
@@ -129,6 +130,10 @@ export async function createMessageStruct(data: DiscordMessage) {
const props: Record<string, ReturnType<typeof createNewProp>> = {};
for (const key of Object.keys(rest)) {
eventHandlers.debug(
"loop",
`Running for of loop in createMessageStruct function.`,
);
// @ts-ignore index signature
props[key] = createNewProp(rest[key]);
}

View File

@@ -1,3 +1,4 @@
import { eventHandlers } from "../bot.ts";
import { cache } from "../cache.ts";
import { deleteRole } from "../helpers/roles/delete_role.ts";
import { editRole } from "../helpers/roles/edit_role.ts";
@@ -74,6 +75,10 @@ export async function createRoleStruct(data: DiscordGuildRoleCreate) {
const props: Record<string, ReturnType<typeof createNewProp>> = {};
for (const key of Object.keys(rest)) {
eventHandlers.debug(
"loop",
`Running for of loop in createRoleStruct function.`,
);
// @ts-ignore index signature
props[key] = createNewProp(rest[key]);
}

View File

@@ -1,3 +1,4 @@
import { eventHandlers } from "../bot.ts";
import { cache } from "../cache.ts";
import { createNewProp } from "../util/utils.ts";
@@ -25,6 +26,10 @@ export function createTemplateStruct(
const restProps: Record<string, Partial<PropertyDescriptor>> = {};
for (const key of Object.keys(rest)) {
eventHandlers.debug(
"loop",
`Running for of loop in createTemplateStruct function.`,
);
// @ts-ignore index signature
restProps[key] = createNewProp(rest[key]);
}

View File

@@ -1,4 +1,5 @@
import { encode } from "../../deps.ts";
import { eventHandlers } from "../bot.ts";
import { DiscordGatewayOpcodes } from "../types/codes/gateway_opcodes.ts";
import { Errors } from "../types/misc/errors.ts";
import { DiscordImageFormat } from "../types/misc/image_format.ts";
@@ -10,10 +11,15 @@ export const sleep = (timeout: number) => {
return new Promise((resolve) => setTimeout(resolve, timeout));
};
// TODO: move this function to helpers
export function editBotStatus(
data: Pick<GatewayStatusUpdatePayload, "activities" | "status">,
) {
ws.shards.forEach((shard) => {
eventHandlers.debug(
"loop",
`Running forEach loop in editBotStatus function.`,
);
shard.ws.send(
JSON.stringify({
op: DiscordGatewayOpcodes.StatusUpdate,
@@ -83,6 +89,10 @@ export function camelKeysToSnakeCase<T>(
const convertedObject: Record<string, any> = {};
Object.keys(obj).forEach((key) => {
eventHandlers.debug(
"loop",
`Running forEach loop in camelKeysToSnakeCase function.`,
);
convertedObject[camelToSnakeCase(key)] = camelKeysToSnakeCase(
(obj as Record<string, any>)[key],
);
@@ -105,6 +115,10 @@ export function snakeKeysToCamelCase<T>(
const convertedObject: Record<string, any> = {};
Object.keys(obj).forEach((key) => {
eventHandlers.debug(
"loop",
`Running forEach loop in snakeKeysToCamelCase function.`,
);
convertedObject[snakeToCamelCase(key)] = snakeKeysToCamelCase(
(obj as Record<string, any>)[key],
);
@@ -124,6 +138,10 @@ function validateSlashOptionChoices(
optionType: SlashCommandOptionType,
) {
for (const choice of choices) {
eventHandlers.debug(
"loop",
`Running for of loop in validateSlashOptionChoices function.`,
);
if ([...choice.name].length < 1 || [...choice.name].length > 100) {
throw new Error(Errors.INVALID_SLASH_OPTIONS_CHOICES);
}
@@ -144,6 +162,10 @@ function validateSlashOptionChoices(
/** @private */
function validateSlashOptions(options: SlashCommandOption[]) {
for (const option of options) {
eventHandlers.debug(
"loop",
`Running for of loop in validateSlashOptions function.`,
);
if (
option.choices?.length &&
(option.choices.length > 25 ||
@@ -174,6 +196,10 @@ export function validateSlashCommands(
create = false,
) {
for (const command of commands) {
eventHandlers.debug(
"loop",
`Running for of loop in validateSlashCommands function.`,
);
if (
(command.name && !SLASH_COMMANDS_NAME_REGEX.test(command.name)) ||
(create && !command.name)

View File

@@ -4,8 +4,16 @@ import { ws } from "./ws.ts";
/** The handler to clean up shards that identified but never received a READY. */
export async function cleanupLoadingShards() {
while (ws.loadingShards.size) {
ws.log(
"DEBUG",
"Running while loop in cleanupLoadingShards function.",
);
const now = Date.now();
ws.loadingShards.forEach((loadingShard) => {
ws.log(
"DEBUG",
`Running forEach loop in cleanupLoadingShards function.`,
);
// Not a minute yet. Max should be few seconds but do a minute to be safe.
if (now < loadingShard.startedAt + 60000) return;

View File

@@ -42,6 +42,7 @@ export function log(type: "RAW", data: Record<string, unknown>): unknown;
export function log(type: "RECONNECT", data: { shardId: number }): unknown;
export function log(type: "RESUMED", data: { shardId: number }): unknown;
export function log(type: "RESUMING", data: { shardId: number }): unknown;
export function log(type: "DEBUG", data: unknown): unknown;
export function log(
type:
| "CLOSED"
@@ -56,7 +57,8 @@ export function log(
| "RAW"
| "RECONNECT"
| "RESUMED"
| "RESUMING",
| "RESUMING"
| "DEBUG",
data: unknown,
) {
console.log(type, data);

View File

@@ -15,6 +15,10 @@ export function heartbeat(shardId: number, interval: number) {
shard.heartbeat.interval = interval;
shard.heartbeat.intervalId = setInterval(() => {
ws.log(
"DEBUG",
`Running setInterval in heartbeat file.`,
);
const currentShard = ws.shards.get(shardId);
if (!currentShard) return;

View File

@@ -13,8 +13,16 @@ export function spawnShards(firstShardId = 0) {
index < ws.botGatewayData.sessionStartLimit.maxConcurrency;
index++
) {
ws.log(
"DEBUG",
`1. Running for loop in spawnShards function.`,
);
// ORGANIZE ALL SHARDS INTO THEIR OWN BUCKETS
for (let i = 0; i < maxShards; i++) {
ws.log(
"DEBUG",
`2. Running for loop in spawnShards function.`,
);
const bucketId = i % ws.botGatewayData.sessionStartLimit.maxConcurrency;
const bucket = buckets.get(bucketId);
@@ -40,10 +48,19 @@ export function spawnShards(firstShardId = 0) {
// SPREAD THIS OUT TO DIFFERENT CLUSTERS TO BEGIN STARTING UP
buckets.forEach(async (bucket, bucketId) => {
ws.log(
"DEBUG",
`3. Running forEach loop in spawnShards function.`,
);
for (const [clusterId, ...queue] of bucket) {
ws.log(
"DEBUG",
`4. Running for of loop in spawnShards function.`,
);
let shardId = queue.shift();
while (shardId !== undefined) {
ws.log("DEBUG", "Running while loop in getMembers function.");
await ws.tellClusterToIdentify(clusterId as number, shardId, bucketId);
shardId = queue.shift();
}