Skip to content

Commit

Permalink
feat: add option to retry bulk overwrites (#741)
Browse files Browse the repository at this point in the history
* feat: add option to retry bulk overwrites
  • Loading branch information
favna committed Apr 6, 2024
1 parent 65a91c7 commit f8a6ad5
Show file tree
Hide file tree
Showing 2 changed files with 90 additions and 45 deletions.
4 changes: 4 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import {
acquire,
getBulkOverwriteRetries,
getDefaultBehaviorWhenNotIdentical,
getDefaultGuildIds,
registries,
setBulkOverwriteRetries,
setDefaultBehaviorWhenNotIdentical,
setDefaultGuildIds
} from './lib/utils/application-commands/ApplicationCommandRegistries';
Expand All @@ -27,8 +29,10 @@ const ApplicationCommandRegistries = {
acquire,
setDefaultBehaviorWhenNotIdentical,
setDefaultGuildIds,
setBulkOverwriteRetries,
getDefaultGuildIds,
getDefaultBehaviorWhenNotIdentical,
getBulkOverwriteRetries,
get registries(): ReadonlyMap<string, ApplicationCommandRegistry> {
return registries;
}
Expand Down
131 changes: 86 additions & 45 deletions src/lib/utils/application-commands/ApplicationCommandRegistries.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/* eslint-disable @typescript-eslint/dot-notation */
import { container } from '@sapphire/pieces';
import { retry } from '@sapphire/utilities';
import type { RESTPostAPIApplicationCommandsJSONBody } from 'discord-api-types/v10';
import { ApplicationCommandType, type ApplicationCommandManager } from 'discord.js';
import type { Command } from '../../structures/Command';
Expand All @@ -13,6 +14,7 @@ import { bulkOverwriteDebug, bulkOverwriteInfo, bulkOverwriteWarn } from './regi

export let defaultBehaviorWhenNotIdentical = RegisterBehavior.Overwrite;
export let defaultGuildIds: ApplicationCommandRegistry.RegisterOptions['guildIds'] = undefined;
let bulkOVerwriteRetries = 1;

export const registries = new Map<string, ApplicationCommandRegistry>();

Expand Down Expand Up @@ -62,6 +64,24 @@ export function getDefaultGuildIds() {
return defaultGuildIds;
}

/**
* Sets the amount of retries for when registering commands, only applies when {@link defaultBehaviorWhenNotIdentical}
* is set to {@link RegisterBehavior.BulkOverwrite}. This is used if registering the commands times out.
* The default value is `1`, which means no retries are performed.
* @param newAmountOfRetries The new amount of retries to set. Set this to `null` to reset it to the default
*/
export function setBulkOverwriteRetries(newAmountOfRetries: number | null) {
newAmountOfRetries ??= 1;

if (newAmountOfRetries <= 0) throw new RangeError('The amount of retries must be greater than 0');

bulkOVerwriteRetries = newAmountOfRetries;
}

export function getBulkOverwriteRetries() {
return bulkOVerwriteRetries;
}

export async function handleRegistryAPICalls() {
container.client.emit(Events.ApplicationCommandRegistriesInitialising);

Expand Down Expand Up @@ -115,6 +135,21 @@ export async function handleBulkOverwrite(commandStore: CommandStore, applicatio
}

// Handle global commands
await retry(() => handleBulkOverwriteGlobalCommands(commandStore, applicationCommands, foundGlobalCommands), bulkOVerwriteRetries);

// Handle guild commands
for (const [guildId, guildCommands] of Object.entries(foundGuildCommands)) {
await retry(() => handleBulkOverwriteGuildCommands(commandStore, applicationCommands, guildId, guildCommands), bulkOVerwriteRetries);
}

container.client.emit(Events.ApplicationCommandRegistriesRegistered, registries, Date.now() - now);
}

async function handleBulkOverwriteGlobalCommands(
commandStore: CommandStore,
applicationCommands: ApplicationCommandManager,
foundGlobalCommands: BulkOverwriteData[]
) {
try {
bulkOverwriteDebug(`Overwriting global application commands, now at ${foundGlobalCommands.length} commands`);
const result = await applicationCommands.set(foundGlobalCommands.map((x) => x.data));
Expand Down Expand Up @@ -150,59 +185,65 @@ export async function handleBulkOverwrite(commandStore: CommandStore, applicatio

bulkOverwriteInfo(`Successfully overwrote global application commands. The application now has ${result.size} global commands`);
} catch (error) {
if (error instanceof Error && error.name === 'AbortError') throw error;

emitBulkOverwriteError(error, null);
}
}

// Handle guild commands
for (const [guildId, guildCommands] of Object.entries(foundGuildCommands)) {
try {
bulkOverwriteDebug(`Overwriting guild application commands for guild ${guildId}, now at ${guildCommands.length} commands`);
const result = await applicationCommands.set(
guildCommands.map((x) => x.data),
guildId
);

// Go through each registered command, find its piece and alias it
for (const [id, guildCommand] of result.entries()) {
// I really hope nobody has a guild command with the same name as another command -.-
// Not like they could anyways as Discord would throw an error for duplicate names
// But yknow... If you're reading this and you did this... Why?
const piece = guildCommands.find((x) => x.data.name === guildCommand.name)?.piece;

if (piece) {
const registry = piece.applicationCommandRegistry;

switch (guildCommand.type) {
case ApplicationCommandType.ChatInput: {
registry['handleIdAddition'](InternalRegistryAPIType.ChatInput, id, guildId);
break;
}
case ApplicationCommandType.User:
case ApplicationCommandType.Message: {
registry['handleIdAddition'](InternalRegistryAPIType.ContextMenu, id, guildId);
break;
}
}
async function handleBulkOverwriteGuildCommands(
commandStore: CommandStore,
applicationCommands: ApplicationCommandManager,
guildId: string,
guildCommands: BulkOverwriteData[]
) {
try {
bulkOverwriteDebug(`Overwriting guild application commands for guild ${guildId}, now at ${guildCommands.length} commands`);
const result = await applicationCommands.set(
guildCommands.map((x) => x.data),
guildId
);

// Go through each registered command, find its piece and alias it
for (const [id, guildCommand] of result.entries()) {
// I really hope nobody has a guild command with the same name as another command -.-
// Not like they could anyways as Discord would throw an error for duplicate names
// But yknow... If you're reading this and you did this... Why?
const piece = guildCommands.find((x) => x.data.name === guildCommand.name)?.piece;

// idHints are useless, and any manually added ids or names could no longer be valid if you use bulk overwrites.
// That said, this might be an issue, so we might need to do it like `handleAppendOrUpdate`
commandStore.aliases.set(id, piece);
} else {
bulkOverwriteWarn(
`Registered guild command "${guildCommand.name}" (${id}) but failed to find the piece in the command store. This should not happen`
);
if (piece) {
const registry = piece.applicationCommandRegistry;

switch (guildCommand.type) {
case ApplicationCommandType.ChatInput: {
registry['handleIdAddition'](InternalRegistryAPIType.ChatInput, id, guildId);
break;
}
case ApplicationCommandType.User:
case ApplicationCommandType.Message: {
registry['handleIdAddition'](InternalRegistryAPIType.ContextMenu, id, guildId);
break;
}
}
}

bulkOverwriteInfo(
`Successfully overwrote guild application commands for guild ${guildId}. The application now has ${result.size} guild commands for guild ${guildId}`
);
} catch (error) {
emitBulkOverwriteError(error, guildId);
// idHints are useless, and any manually added ids or names could no longer be valid if you use bulk overwrites.
// That said, this might be an issue, so we might need to do it like `handleAppendOrUpdate`
commandStore.aliases.set(id, piece);
} else {
bulkOverwriteWarn(
`Registered guild command "${guildCommand.name}" (${id}) but failed to find the piece in the command store. This should not happen`
);
}
}
}

container.client.emit(Events.ApplicationCommandRegistriesRegistered, registries, Date.now() - now);
bulkOverwriteInfo(
`Successfully overwrote guild application commands for guild ${guildId}. The application now has ${result.size} guild commands for guild ${guildId}`
);
} catch (error) {
if (error instanceof Error && error.name === 'AbortError') throw error;

emitBulkOverwriteError(error, guildId);
}
}

async function handleAppendOrUpdate(
Expand Down

0 comments on commit f8a6ad5

Please sign in to comment.