Skip to content

Commit

Permalink
fix: fixed the infinite difference found for commands (#499)
Browse files Browse the repository at this point in the history
* chore: hopefully fix the infinite difference found for commands

* chore: scream

* chore: hmmmmm

* chore: well how about that
  • Loading branch information
vladfrangu authored Aug 5, 2022
1 parent b264c3e commit 1e11f53
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 12 deletions.
10 changes: 4 additions & 6 deletions src/lib/utils/application-commands/ApplicationCommandRegistry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,9 @@ import type {
MessageApplicationCommandData,
UserApplicationCommandData
} from 'discord.js';
import objectHash from 'object-hash';
import { InternalRegistryAPIType, RegisterBehavior } from '../../types/Enums';
import { getDefaultBehaviorWhenNotIdentical } from './ApplicationCommandRegistries';
import { CommandDifference, getCommandDifferences } from './computeDifferences';
import { CommandDifference, getCommandDifferences, getCommandDifferencesFast } from './computeDifferences';
import { convertApplicationCommandToApiData, normalizeChatInputCommand, normalizeContextMenuCommand } from './normalizeInputs';

export class ApplicationCommandRegistry {
Expand Down Expand Up @@ -350,14 +349,13 @@ export class ApplicationCommandRegistry {
const now = Date.now();

// Step 0: compute differences
const oldHash = objectHash(convertApplicationCommandToApiData(applicationCommand));
const newHash = objectHash(apiData);
const areThereDifferences = getCommandDifferencesFast(convertApplicationCommandToApiData(applicationCommand), apiData);

const later = Date.now() - now;
this.debug(`Took ${later}ms to process differences via object hashing`);
this.debug(`Took ${later}ms to process differences via fast compute differences`);

// Step 1: if there are no differences, return
if (oldHash === newHash) {
if (!areThereDifferences) {
this.debug(
`${guildId ? 'Guild command' : 'Command'} "${apiData.name}" is identical to command "${applicationCommand.name}" (${
applicationCommand.id
Expand Down
12 changes: 12 additions & 0 deletions src/lib/utils/application-commands/computeDifferences.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,18 @@ type APIApplicationCommandSubcommandTypes = APIApplicationCommandSubcommandOptio
type APIApplicationCommandNumericTypes = APIApplicationCommandIntegerOption | APIApplicationCommandNumberOption;
type APIApplicationCommandChoosableAndAutocompletableTypes = APIApplicationCommandNumericTypes | APIApplicationCommandStringOption;

/**
* @returns `true` if there are differences, `false` otherwise
*/
export function getCommandDifferencesFast(existingCommand: RESTPostAPIApplicationCommandsJSONBody, apiData: InternalAPICall['builtData']) {
for (const _ of getCommandDifferences(existingCommand, apiData)) {
// Return immediately on first difference found (also means we skip all other checks)
return true;
}

return false;
}

export function getCommandDifferences(existingCommand: RESTPostAPIApplicationCommandsJSONBody, apiData: InternalAPICall['builtData']) {
const differences: CommandDifference[] = [];

Expand Down
35 changes: 29 additions & 6 deletions src/lib/utils/application-commands/normalizeInputs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,29 @@ function isBuilder(
return command instanceof SlashCommandBuilder;
}

function addDefaultsToChatInputJSON(data: RESTPostAPIChatInputApplicationCommandsJSONBody): RESTPostAPIChatInputApplicationCommandsJSONBody {
data.default_permission ??= true;
data.dm_permission ??= true;
data.type ??= ApplicationCommandType.ChatInput;

// Localizations default to null from d.js
data.name_localizations ??= null;
data.description_localizations ??= null;

return data;
}

function addDefaultsToContextMenuJSON(data: RESTPostAPIContextMenuApplicationCommandsJSONBody): RESTPostAPIContextMenuApplicationCommandsJSONBody {
data.default_permission ??= true;
data.dm_permission ??= true;

// Localizations default to null from d.js
data.name_localizations ??= null;
data.description_localizations ??= null;

return data;
}

export function normalizeChatInputCommand(
command:
| ChatInputApplicationCommandData
Expand All @@ -42,11 +65,11 @@ export function normalizeChatInputCommand(
if (isFunction(command)) {
const builder = new SlashCommandBuilder();
command(builder);
return builder.toJSON() as RESTPostAPIChatInputApplicationCommandsJSONBody;
return addDefaultsToChatInputJSON(builder.toJSON() as RESTPostAPIChatInputApplicationCommandsJSONBody);
}

if (isBuilder(command)) {
return command.toJSON() as RESTPostAPIChatInputApplicationCommandsJSONBody;
return addDefaultsToChatInputJSON(command.toJSON() as RESTPostAPIChatInputApplicationCommandsJSONBody);
}

const finalObject: RESTPostAPIChatInputApplicationCommandsJSONBody = {
Expand All @@ -67,7 +90,7 @@ export function normalizeChatInputCommand(
finalObject.options = command.options.map((option) => ApplicationCommand['transformOption'](option) as APIApplicationCommandOption);
}

return finalObject;
return addDefaultsToChatInputJSON(finalObject);
}

export function normalizeContextMenuCommand(
Expand All @@ -80,11 +103,11 @@ export function normalizeContextMenuCommand(
if (isFunction(command)) {
const builder = new ContextMenuCommandBuilder();
command(builder);
return builder.toJSON() as RESTPostAPIContextMenuApplicationCommandsJSONBody;
return addDefaultsToContextMenuJSON(builder.toJSON() as RESTPostAPIContextMenuApplicationCommandsJSONBody);
}

if (command instanceof ContextMenuCommandBuilder) {
return command.toJSON() as RESTPostAPIContextMenuApplicationCommandsJSONBody;
return addDefaultsToContextMenuJSON(command.toJSON() as RESTPostAPIContextMenuApplicationCommandsJSONBody);
}

let type: ApplicationCommandType;
Expand Down Expand Up @@ -115,7 +138,7 @@ export function normalizeContextMenuCommand(
finalObject.default_member_permissions = String(command.defaultMemberPermissions);
}

return finalObject;
return addDefaultsToContextMenuJSON(finalObject);
}

export function convertApplicationCommandToApiData(command: ApplicationCommand): RESTPostAPIApplicationCommandsJSONBody {
Expand Down

0 comments on commit 1e11f53

Please sign in to comment.