Skip to content

Commit ee7c1a7

Browse files
committed
Working command deploy script
1 parent b8a7358 commit ee7c1a7

File tree

2 files changed

+81
-16
lines changed

2 files changed

+81
-16
lines changed

scripts/deploy-commands.ts

+79-16
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,26 @@ import {
33
SlashCommandBuilder,
44
} from "@discordjs/builders";
55
import { REST } from "@discordjs/rest";
6-
import { ApplicationCommandType, Routes } from "discord-api-types/v9";
6+
import {
7+
APIApplicationCommand,
8+
ApplicationCommandType,
9+
Routes,
10+
} from "discord-api-types/v9";
711
import { applicationId, discordToken, guildId } from "../src/constants";
812
import { logger } from "../src/features/log";
13+
import { difference } from "../src/helpers/sets";
914

10-
const cmd = {
11-
name: "test action",
12-
description: "¡some kind of test action!",
13-
handler: () => {},
14-
};
15-
const cmds = [cmd];
15+
// TODO: make this a global command in production
16+
const upsertUrl = () => Routes.applicationGuildCommands(applicationId, guildId);
17+
const deleteUrl = (commandId: string) =>
18+
Routes.applicationGuildCommand(applicationId, guildId, commandId);
19+
20+
interface CommandConfig {
21+
name: string;
22+
description: string;
23+
type: ApplicationCommandType;
24+
}
25+
const cmds: CommandConfig[] = [];
1626

1727
const commands = [
1828
...cmds
@@ -44,15 +54,68 @@ const commands = [
4454
.toJSON(),
4555
),
4656
];
57+
const names = new Set(commands.map((c) => c.name));
4758

4859
const rest = new REST({ version: "9" }).setToken(discordToken);
60+
const deploy = async () => {
61+
const remoteCommands = (await rest.get(
62+
upsertUrl(),
63+
)) as APIApplicationCommand[];
64+
65+
// Take the list of names to delete and swap it out for IDs to delete
66+
const remoteNames = new Set(remoteCommands.map((c) => c.name));
67+
const deleteNames = [...difference(remoteNames, names)];
68+
const toDelete = deleteNames
69+
.map((x) => remoteCommands.find((y) => y.name === x)?.id)
70+
.filter((x): x is string => Boolean(x));
4971

50-
rest
51-
// TODO: make this a global command in production
52-
.put(Routes.applicationGuildCommands(applicationId, guildId), {
53-
body: commands,
54-
})
55-
.then(() =>
56-
logger.log("DEPLOY", "Successfully registered application commands."),
57-
)
58-
.catch((e) => logger.log("DEPLOY", e));
72+
logger.log(
73+
"DEPLOY",
74+
`Removing ${toDelete.length} commands: [${deleteNames.join(",")}]`,
75+
);
76+
await Promise.allSettled(
77+
toDelete.map((commandId) => rest.delete(deleteUrl(commandId))),
78+
);
79+
80+
// Grab a list of commands that need to be updated
81+
const toUpdate = remoteCommands.filter(
82+
(c) =>
83+
// Check all necessary fields to see if any changed. User and Message
84+
// commands don't have a description.
85+
!commands.find((x) => {
86+
const {
87+
type = ApplicationCommandType.ChatInput,
88+
name,
89+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
90+
// @ts-expect-error Unions are weird
91+
description = "",
92+
} = x;
93+
switch (type as ApplicationCommandType) {
94+
case ApplicationCommandType.User:
95+
case ApplicationCommandType.Message:
96+
return name === c.name && type === c.type;
97+
case ApplicationCommandType.ChatInput:
98+
default:
99+
return (
100+
name === c.name &&
101+
type === c.type &&
102+
description === c.description
103+
);
104+
}
105+
}),
106+
);
107+
108+
logger.log(
109+
"DEPLOY",
110+
`Updating ${toUpdate.length} commands: [${toUpdate
111+
.map((x) => x.name)
112+
.join(",")}]`,
113+
);
114+
115+
await rest.put(upsertUrl(), { body: commands });
116+
};
117+
try {
118+
deploy();
119+
} catch (e) {
120+
logger.log("DEPLOY EXCEPTION", e as string);
121+
}

src/helpers/sets.ts

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export const difference = <T>(a: Set<T>, b: Set<T>) =>
2+
new Set(Array.from(a).filter((x) => !b.has(x)));

0 commit comments

Comments
 (0)