diff --git a/package-lock.json b/package-lock.json index 30e5132..5763b18 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "workers-discord", - "version": "0.0.8", + "version": "0.0.9", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "workers-discord", - "version": "0.0.8", + "version": "0.0.9", "license": "Apache-2.0", "dependencies": { "deep-equal": "^2.2.3" @@ -14,14 +14,14 @@ "devDependencies": { "@cloudflare/workers-types": "^4.20231025.0", "@types/deep-equal": "^1.0.4", - "discord-api-types": "^0.37.63", + "discord-api-types": "^0.37.84", "toucan-js": "^3.3.1", "tsup": "^7.2.0", "typescript": "^5.2.2" }, "peerDependencies": { "@cloudflare/workers-types": ">=4.0.0, <5.0.0", - "discord-api-types": ">=0.37.0, <0.38.0", + "discord-api-types": ">=0.37.84, <0.38.0", "toucan-js": ">=3.0.0, <4.0.0" }, "peerDependenciesMeta": { @@ -797,9 +797,9 @@ } }, "node_modules/discord-api-types": { - "version": "0.37.63", - "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.63.tgz", - "integrity": "sha512-WbEDWj/1JGCIC1oCMIC4z9XbYY8PrWpV5eqFFQymJhJlHMqgIjqoYbU812X5oj5cwbRrEh6Va4LNLumB2Nt6IQ==", + "version": "0.37.93", + "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.93.tgz", + "integrity": "sha512-M5jn0x3bcXk8EI2c6F6V6LeOWq10B/cJf+YJSyqNmg7z4bdXK+Z7g9zGJwHS0h9Bfgs0nun2LQISFOzwck7G9A==", "dev": true }, "node_modules/es-get-iterator": { diff --git a/package.json b/package.json index 853bb99..30d172b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "workers-discord", - "version": "0.0.8", + "version": "0.0.9", "description": "Some wrappers for Discord applications in Workers", "type": "module", "main": "dist/index.js", @@ -26,7 +26,7 @@ }, "peerDependencies": { "@cloudflare/workers-types": ">=4.0.0, <5.0.0", - "discord-api-types": ">=0.37.0, <0.38.0", + "discord-api-types": ">=0.37.84, <0.38.0", "toucan-js": ">=3.0.0, <4.0.0" }, "peerDependenciesMeta": { @@ -40,7 +40,7 @@ "devDependencies": { "@cloudflare/workers-types": "^4.20231025.0", "@types/deep-equal": "^1.0.4", - "discord-api-types": "^0.37.63", + "discord-api-types": "^0.37.84", "toucan-js": "^3.3.1", "tsup": "^7.2.0", "typescript": "^5.2.2" diff --git a/src/register.ts b/src/register.ts index 6978294..54e09e0 100644 --- a/src/register.ts +++ b/src/register.ts @@ -1,7 +1,11 @@ import type { APIApplicationCommand, APIApplicationCommandOption, + ApplicationCommandType, + ApplicationIntegrationType, + InteractionContextType, } from 'discord-api-types/payloads'; +import type { RESTPatchAPIApplicationCommandJSONBody } from 'discord-api-types/rest'; import equal from 'deep-equal'; import type { Toucan } from 'toucan-js'; @@ -45,26 +49,42 @@ const consistentCommandOption = (obj: APIApplicationCommandOption): Option => ({ }); /** - * Check which properties of a command have changed + * Ensure an array of context types is consistent + * + * Useful when doing deep-equal checks for context type equality */ -const updatedCommandProps = (oldCmd: CommandMeta, newCmd: CommandMeta) => ({ - name: oldCmd.name !== newCmd.name, - description: oldCmd.description !== newCmd.description, - options: !equal( - oldCmd.options && oldCmd.options.map(consistentCommandOption), - newCmd.options && newCmd.options.map(consistentCommandOption), - ), -}); - -type Filtered = Pick; +const consistentContexts = (arr: number[] | undefined) => arr && [ ...new Set(arr) ].sort(); /** - * Filter an object to only include properties in a given diff + * Get the patch required to update a command */ -const objectPatch = , Diff extends Record>(obj: Obj, diff: Diff) => Object.entries(obj) - .reduce((acc, [key, value]) => diff[key] ? { ...acc, [key]: value } : acc, {}) as Pick, keyof Obj>>; +const updatedCommandProps = (oldCmd: CommandMeta, newCmd: CommandMeta) => { + type Patch = Partial<{ + name: string; + description: string; + options: APIApplicationCommandOption[]; + integration_types: ApplicationIntegrationType[]; + contexts: InteractionContextType[]; + }>; + const patch: Patch = {}; + + if (oldCmd.name !== newCmd.name) patch.name = newCmd.name; + if (oldCmd.description !== newCmd.description) patch.description = newCmd.description; + if (!equal( + oldCmd.options?.map(consistentCommandOption), + newCmd.options?.map(consistentCommandOption), + )) patch.options = newCmd.options; + if (!equal( + consistentContexts(oldCmd.contexts?.installation), + consistentContexts(newCmd.contexts?.installation), + )) patch.integration_types = newCmd.contexts?.installation; + if (!equal( + consistentContexts(oldCmd.contexts?.interaction), + consistentContexts(newCmd.contexts?.interaction), + )) patch.contexts = newCmd.contexts?.interaction; + + return patch; +}; /** * Register or update commands with Discord @@ -89,30 +109,37 @@ const registerCommands = async & APIApplicationCommand)[] = []; + const commandData: (Command & { discord: APIApplicationCommand })[] = []; // Patch any commands that already exist in Discord const toPatch = cmds.reduce((arr, command) => { const discord = discordCommands.find(c => c.name === command.name); if (!discord) return arr; - const diff = updatedCommandProps(discord, command); - if (!Object.values(diff).includes(true)) { - commandData.push({ ...discord, ...command }); + const diff = updatedCommandProps({ + name: discord.name, + description: discord.description, + options: discord.options, + contexts: { + installation: discord.integration_types, + interaction: discord.contexts ?? undefined, + } + }, command); + if (!Object.keys(diff).length) { + commandData.push({ ...command, discord }); return arr; } return [ ...arr, { command, discord, diff } ]; - }, [] as { command: Command; discord: APIApplicationCommand; diff: ReturnType }[]); + }, [] as { command: Command; discord: APIApplicationCommand; diff: Partial }[]); for (let i = 0; i < toPatch.length; i++) { // Naive avoidance of rate limits if (i >= 5) await new Promise(resolve => setTimeout(resolve, ratelimit)); // Get the props to patch and do the update const { command, discord, diff } = toPatch[i]; - const cmdPatch = objectPatch(command, diff); - const data = await updateCommand(clientId, token, discord.id, cmdPatch, guildId); - commandData.push({ ...command, ...data }); + const data = await updateCommand(clientId, token, discord.id, diff, guildId); + commandData.push({ ...command, discord: { ...discord, ...data } }); } // Register any commands that're new in the code @@ -123,8 +150,14 @@ const registerCommands = async extends CommandMeta {