Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(ApplicationCommandRegistries): add the right ids in the right place #716

Merged
merged 6 commits into from
Jan 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
/* eslint-disable @typescript-eslint/dot-notation */
import { container } from '@sapphire/pieces';
import type { RESTPostAPIApplicationCommandsJSONBody } from 'discord-api-types/v10';
import type { ApplicationCommandManager } from 'discord.js';
import { ApplicationCommandType, type ApplicationCommandManager } from 'discord.js';
import type { Command } from '../../structures/Command';
import type { CommandStore } from '../../structures/CommandStore';
import { RegisterBehavior } from '../../types/Enums';
import { InternalRegistryAPIType, RegisterBehavior } from '../../types/Enums';
import { Events } from '../../types/Events';
import { ApplicationCommandRegistry } from './ApplicationCommandRegistry';
import { getNeededRegistryParameters } from './getNeededParameters';
Expand Down Expand Up @@ -125,8 +126,17 @@ export async function handleBulkOverwrite(commandStore: CommandStore, applicatio
if (piece) {
const registry = piece.applicationCommandRegistry;

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

// idHints are useless, and any manually added id or names could end up not being valid any longer if you use bulk overwrites
// That said, this might be an issue, so we might need to do it like `handleAppendOrUpdate`
Expand Down Expand Up @@ -161,9 +171,18 @@ export async function handleBulkOverwrite(commandStore: CommandStore, applicatio

if (piece) {
const registry = piece.applicationCommandRegistry;
registry.guildCommandIds.set(guildId, id);

registry.addChatInputCommandIds(id);
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;
}
}

// 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`
Expand Down
140 changes: 98 additions & 42 deletions src/lib/utils/application-commands/ApplicationCommandRegistry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@ import {
type RESTPostAPIChatInputApplicationCommandsJSONBody,
type RESTPostAPIContextMenuApplicationCommandsJSONBody
} from 'discord-api-types/v10';
import type {
ApplicationCommand,
ApplicationCommandManager,
ChatInputApplicationCommandData,
import {
Collection,
MessageApplicationCommandData,
UserApplicationCommandData
type ApplicationCommand,
type ApplicationCommandManager,
type ChatInputApplicationCommandData,
type MessageApplicationCommandData,
type UserApplicationCommandData
} from 'discord.js';
import { InternalRegistryAPIType, RegisterBehavior } from '../../types/Enums';
import { allGuildIdsToFetchCommandsFor, getDefaultBehaviorWhenNotIdentical, getDefaultGuildIds } from './ApplicationCommandRegistries';
Expand All @@ -26,14 +26,61 @@ import { getCommandDifferences, getCommandDifferencesFast } from './computeDiffe
import { convertApplicationCommandToApiData, normalizeChatInputCommand, normalizeContextMenuCommand } from './normalizeInputs';

export class ApplicationCommandRegistry {
/**
* The piece this registry is for.
*/
public readonly commandName: string;

/**
* A set of all chat input command names and ids that point to this registry.
* You should not use this field directly, but instead use {@link ApplicationCommandRegistry.globalChatInputCommandIds}
*/
public readonly chatInputCommands = new Set<string>();

/**
* A set of all context menu command names and ids that point to this registry.
* You should not use this field directly, but instead use {@link ApplicationCommandRegistry.globalContextMenuCommandIds}
*/
public readonly contextMenuCommands = new Set<string>();

/**
* The guild ids that we need to fetch the commands for.
*/
public readonly guildIdsToFetch = new Set<string>();

/**
* The global slash command id for this command.
* @deprecated This field will only show the first global command id registered for this registry.
* Use {@link ApplicationCommandRegistry.globalChatInputCommandIds} instead.
*/
public globalCommandId: string | null = null;
public readonly guildCommandIds = new Map<string, string>();

/**
* A set of all registered and valid global chat input command ids that point to this registry.
*/
public readonly globalChatInputCommandIds = new Set<string>();

/**
* A set of all registered and valid global context menu command ids that point to this registry.
*/
public readonly globalContextMenuCommandIds = new Set<string>();

/**
* The guild command ids for this command.
* @deprecated This field will only show the first guild command id registered for this registry per guild.
* Use {@link ApplicationCommandRegistry.guildIdToChatInputCommandIds} and {@link ApplicationCommandRegistry.guildIdToContextMenuCommandIds} instead.
*/
public readonly guildCommandIds = new Collection<string, string>();

/**
* A map of guild ids to a set of registered and valid chat input command ids that point to this registry.
*/
public readonly guildIdToChatInputCommandIds = new Collection<string, Set<string>>();

/**
* A map of guild ids to a set of registered and valid context menu command ids that point to this registry.
*/
public readonly guildIdToContextMenuCommandIds = new Collection<string, Set<string>>();

private readonly apiCalls: InternalAPICall[] = [];

Expand Down Expand Up @@ -225,6 +272,42 @@ export class ApplicationCommandRegistry {
}
}

protected handleIdAddition(type: InternalRegistryAPIType, id: string, guildId?: string | null) {
switch (type) {
case InternalRegistryAPIType.ChatInput: {
this.addChatInputCommandIds(id);

if (guildId) {
this.guildIdToChatInputCommandIds.ensure(guildId, () => new Set()).add(id);
} else {
this.globalChatInputCommandIds.add(id);
}
break;
}
case InternalRegistryAPIType.ContextMenu: {
this.addContextMenuCommandIds(id);

if (guildId) {
this.guildIdToContextMenuCommandIds.ensure(guildId, () => new Set()).add(id);
} else {
this.globalContextMenuCommandIds.add(id);
}
break;
}
}

// Old field handling
if (guildId) {
// Old, wrongly typed field (thx kyra for spotting >_>)
if (!this.guildCommandIds.has(guildId)) {
this.guildCommandIds.set(guildId, id);
}
} else {
// First come, first serve (thx kyra for spotting >_>)
this.globalCommandId ??= id;
}
}

private getGuildIdsToRegister(options?: ApplicationCommandRegistryRegisterOptions) {
let guildIdsToRegister: ApplicationCommandRegistry.RegisterOptions['guildIds'] = undefined;

Expand Down Expand Up @@ -298,16 +381,8 @@ export class ApplicationCommandRegistry {
const globalCommand = globalCommands.find(findCallback);

if (globalCommand) {
switch (apiCall.type) {
case InternalRegistryAPIType.ChatInput:
this.addChatInputCommandIds(globalCommand.id);
break;
case InternalRegistryAPIType.ContextMenu:
this.addContextMenuCommandIds(globalCommand.id);
break;
}

this.debug(`Checking if command "${commandName}" is identical with global ${type} command with id "${globalCommand.id}"`);
this.handleIdAddition(apiCall.type, globalCommand.id);
await this.handleCommandPresent(globalCommand, builtData, behaviorIfNotEqual, null);
} else if (registerOptions.registerCommandIfMissing ?? true) {
this.debug(`Creating new global ${type} command with name "${commandName}"`);
Expand All @@ -332,16 +407,7 @@ export class ApplicationCommandRegistry {

if (existingGuildCommand) {
this.debug(`Checking if guild ${type} command "${commandName}" is identical to command "${existingGuildCommand.id}"`);

switch (apiCall.type) {
case InternalRegistryAPIType.ChatInput:
this.addChatInputCommandIds(existingGuildCommand.id);
break;
case InternalRegistryAPIType.ContextMenu:
this.addContextMenuCommandIds(existingGuildCommand.id);
break;
}

this.handleIdAddition(apiCall.type, existingGuildCommand.id, guildId);
await this.handleCommandPresent(existingGuildCommand, builtData, behaviorIfNotEqual, guildId);
} else if (registerOptions.registerCommandIfMissing ?? true) {
this.debug(`Creating new guild ${type} command with name "${commandName}" for guild "${guildId}"`);
Expand All @@ -358,12 +424,6 @@ export class ApplicationCommandRegistry {
behaviorIfNotEqual: RegisterBehavior,
guildId: string | null
) {
if (guildId) {
this.guildCommandIds.set(guildId, applicationCommand.id);
} else {
this.globalCommandId = applicationCommand.id;
}

if (behaviorIfNotEqual === RegisterBehavior.BulkOverwrite) {
this.debug(
`Command "${this.commandName}" has the behaviorIfNotEqual set to "BulkOverwrite" which is invalid. Using defaultBehaviorWhenNotIdentical instead`
Expand Down Expand Up @@ -467,12 +527,6 @@ export class ApplicationCommandRegistry {
try {
const result = await commandsManager.create(apiData, guildId);

if (guildId) {
this.guildCommandIds.set(guildId, result.id);
} else {
this.globalCommandId = result.id;
}

this.info(
`Successfully created ${type}${guildId ? ' guild' : ''} command "${apiData.name}" with id "${
result.id
Expand All @@ -481,13 +535,15 @@ export class ApplicationCommandRegistry {

switch (apiData.type) {
case undefined:
case ApplicationCommandType.ChatInput:
this.addChatInputCommandIds(result.id);
case ApplicationCommandType.ChatInput: {
this.handleIdAddition(InternalRegistryAPIType.ChatInput, result.id, guildId);
break;
}
case ApplicationCommandType.Message:
case ApplicationCommandType.User:
this.addContextMenuCommandIds(result.id);
case ApplicationCommandType.User: {
this.handleIdAddition(InternalRegistryAPIType.ContextMenu, result.id, guildId);
break;
}
}
} catch (err) {
this.error(
Expand Down