Skip to content

Commit

Permalink
Adds support for global commands and makes /help a global command
Browse files Browse the repository at this point in the history
Fixes #66
  • Loading branch information
PolariTOON committed May 26, 2024
1 parent 3fde5b8 commit 4782306
Show file tree
Hide file tree
Showing 11 changed files with 79 additions and 38 deletions.
4 changes: 3 additions & 1 deletion src/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@ import trackerCommand from "./commands/tracker.js";
import trailerCommand from "./commands/trailer.js";
import updateCommand from "./commands/update.js";
import verifyCommand from "./commands/verify.js";
type ApplicationCommandData = ChatInputApplicationCommandData | MessageApplicationCommandData;
type ApplicationCommandData = (ChatInputApplicationCommandData | MessageApplicationCommandData) & {
global?: boolean;
};
type ApplicationUserInteraction = AutocompleteInteraction<"cached"> | ChatInputCommandInteraction<"cached"> | MessageContextMenuCommandInteraction<"cached">;
type Command = {
register(): ApplicationCommandData;
Expand Down
1 change: 0 additions & 1 deletion src/commands/apply.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ const applyCommand: Command = {
description: commandDescription["en-US"],
descriptionLocalizations: commandDescription,
defaultMemberPermissions: [],
dmPermission: false,
};
},
async interact(interaction: ApplicationUserInteraction): Promise<void> {
Expand Down
1 change: 0 additions & 1 deletion src/commands/approve.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ const approveCommand: Command = {
name: commandName,
nameLocalizations: commandDescription,
defaultMemberPermissions: [],
dmPermission: false,
};
},
async interact(interaction: ApplicationUserInteraction): Promise<void> {
Expand Down
1 change: 0 additions & 1 deletion src/commands/chat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,6 @@ const chatCommand: Command = {
},
],
defaultMemberPermissions: [],
dmPermission: false,
};
},
async interact(interaction: ApplicationUserInteraction): Promise<void> {
Expand Down
1 change: 0 additions & 1 deletion src/commands/emoji.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,6 @@ const emojiCommand: Command = {
}).flat<ApplicationCommandOptionData[][]>(),
],
defaultMemberPermissions: [],
dmPermission: false,
};
},
async interact(interaction: ApplicationUserInteraction): Promise<void> {
Expand Down
1 change: 0 additions & 1 deletion src/commands/gate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,6 @@ const gateCommand: Command = {
},
],
defaultMemberPermissions: [],
dmPermission: false,
};
},
async interact(interaction: ApplicationUserInteraction): Promise<void> {
Expand Down
70 changes: 47 additions & 23 deletions src/commands/help.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,10 +81,12 @@ const features: Feature[] = [
};
}),
];
let globalFetchedTimestamp: number = Number.NEGATIVE_INFINITY;
let globalApplicationCommandPermissions: Collection<string, ApplicationCommandPermissions[]> | null = null;
const guildFetchedTimestamps: Collection<Snowflake, number> = new Collection<Snowflake, number>();
const guildApplicationCommandPermissions: Collection<Snowflake, Collection<string, ApplicationCommandPermissions[]> | undefined> = new Collection<Snowflake, Collection<string, ApplicationCommandPermissions[]> | undefined>();
const guildWebhooks: Collection<Snowflake, Collection<Snowflake, Webhook> | undefined> = new Collection<Snowflake, Collection<Snowflake, Webhook> | undefined>();
const guildAutoModerationRules: Collection<Snowflake, Collection<Snowflake, AutoModerationRule> | undefined> = new Collection<Snowflake, Collection<Snowflake, AutoModerationRule> | undefined>();
const guildApplicationCommandPermissions: Collection<Snowflake, Collection<string, ApplicationCommandPermissions[]>> = new Collection<Snowflake, Collection<string, ApplicationCommandPermissions[]>>();
const guildWebhooks: Collection<Snowflake, Collection<Snowflake, Webhook>> = new Collection<Snowflake, Collection<Snowflake, Webhook>>();
const guildAutoModerationRules: Collection<Snowflake, Collection<Snowflake, AutoModerationRule>> = new Collection<Snowflake, Collection<Snowflake, AutoModerationRule>>();
function naiveStream(content: string): string[] {
content = content.replace(/^\n+|\n+$/g, "").replace(/\n+/g, "\n");
if (content.length === 0) {
Expand Down Expand Up @@ -237,19 +239,43 @@ const helpCommand: Command = {
autocomplete: true,
},
],
dmPermission: false,
global: true,
};
},
async interact(interaction: ApplicationUserInteraction): Promise<void> {
const {client, createdTimestamp, member, guild}: ApplicationUserInteraction = interaction;
const fetchedTimestamp: number = guildFetchedTimestamps.get(guild.id) ?? Number.NEGATIVE_INFINITY;
const elapsedTime: number = createdTimestamp - fetchedTimestamp;
const forceFetch: boolean = elapsedTime >= 60000;
if (forceFetch) {
const globalElapsedTime: number = createdTimestamp - globalFetchedTimestamp;
const globalForceFetch: boolean = globalElapsedTime >= 60000;
if (globalForceFetch) {
globalFetchedTimestamp = createdTimestamp;
}
const guildFetchedTimestamp: number = guildFetchedTimestamps.get(guild.id) ?? Number.NEGATIVE_INFINITY;
const guildElapsedTime: number = createdTimestamp - guildFetchedTimestamp;
const guildForceFetch: boolean = guildElapsedTime >= 60000;
if (guildForceFetch) {
guildFetchedTimestamps.set(guild.id, createdTimestamp);
}
const applicationCommands: Collection<string, ApplicationCommand> = guild.commands.cache;
const permissions: Collection<string, ApplicationCommandPermissions[]> | undefined = await (async (): Promise<Collection<string, ApplicationCommandPermissions[]> | undefined> => {
if (forceFetch) {
const globalApplicationCommands: Collection<string, ApplicationCommand> = client.application.commands.cache;
const guildApplicationCommands: Collection<string, ApplicationCommand> = guild.commands.cache;
const applicationCommands: Collection<string, ApplicationCommand> = globalApplicationCommands.concat(guildApplicationCommands);
const globalPermissions: Collection<string, ApplicationCommandPermissions[]> | null = await (async (): Promise<Collection<string, ApplicationCommandPermissions[]> | null> => {
if (globalForceFetch) {
try {
globalApplicationCommandPermissions = await client.application.commands.permissions.fetch({
guild,
});
} catch {
globalApplicationCommandPermissions = null;
}
}
return globalApplicationCommandPermissions;
})();
if (globalPermissions == null) {
return;
}
const guildPermissions: Collection<string, ApplicationCommandPermissions[]> | undefined = await (async (): Promise<Collection<string, ApplicationCommandPermissions[]> | undefined> => {
if (guildForceFetch) {
try {
guildApplicationCommandPermissions.set(guild.id, await guild.commands.permissions.fetch({}));
} catch {
Expand All @@ -258,11 +284,12 @@ const helpCommand: Command = {
}
return guildApplicationCommandPermissions.get(guild.id);
})();
if (permissions == null) {
if (guildPermissions == null) {
return;
}
const permissions: Collection<string, ApplicationCommandPermissions[]> = globalPermissions.concat(guildPermissions);
const webhooks: Collection<string, Webhook> | undefined = await (async (): Promise<Collection<string, Webhook> | undefined> => {
if (forceFetch) {
if (guildForceFetch) {
try {
guildWebhooks.set(guild.id, await guild.fetchWebhooks());
} catch {
Expand All @@ -275,7 +302,7 @@ const helpCommand: Command = {
return;
}
const autoModerationRules: Collection<string, AutoModerationRule> | undefined = await (async (): Promise<Collection<string, AutoModerationRule> | undefined> => {
if (forceFetch) {
if (guildForceFetch) {
try {
guildAutoModerationRules.set(guild.id, await guild.autoModerationRules.fetch());
} catch {
Expand Down Expand Up @@ -309,7 +336,7 @@ const helpCommand: Command = {
await interaction.respond([]);
return;
}
const results: Feature[] = nearest<Feature>(value.toLocaleLowerCase(resolvedLocale), features, 7, (feature: Feature): string => {
const results: Feature[] = nearest<Feature>(value.replace(/ \((command|hook|rule)\)$/u, "").toLocaleLowerCase(resolvedLocale), features, 7, (feature: Feature): string => {
const {name}: Feature = feature;
return name.toLocaleLowerCase(resolvedLocale);
});
Expand All @@ -325,9 +352,6 @@ const helpCommand: Command = {
if (applicationCommand == null) {
return null;
}
if (applicationCommand.guild == null) {
return null;
}
if (applicationCommand.type !== ApplicationCommandType.ChatInput && applicationCommand.type !== ApplicationCommandType.Message) {
return null;
}
Expand Down Expand Up @@ -423,9 +447,6 @@ const helpCommand: Command = {
if (applicationCommand == null) {
return null;
}
if (applicationCommand.guild == null) {
return null;
}
if (applicationCommand.type !== ApplicationCommandType.ChatInput && applicationCommand.type !== ApplicationCommandType.Message) {
return null;
}
Expand Down Expand Up @@ -578,9 +599,6 @@ const helpCommand: Command = {
if (applicationCommand == null) {
return null;
}
if (applicationCommand.guild == null) {
return null;
}
if (applicationCommand.type !== ApplicationCommandType.ChatInput && applicationCommand.type !== ApplicationCommandType.Message) {
return null;
}
Expand Down Expand Up @@ -664,13 +682,19 @@ const helpCommand: Command = {
}
await interaction.reply({
content: formatMessage("en-US"),
allowedMentions: {
users: [],
},
});
if (resolvedLocale === "en-US") {
return;
}
await interaction.followUp({
content: formatMessage(resolvedLocale),
ephemeral: true,
allowedMentions: {
users: [],
},
});
return;
}
Expand Down
1 change: 0 additions & 1 deletion src/commands/patch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ const patchCommand: Command = {
name: commandName,
nameLocalizations: commandDescription,
defaultMemberPermissions: [],
dmPermission: false,
};
},
async interact(interaction: ApplicationUserInteraction): Promise<void> {
Expand Down
1 change: 0 additions & 1 deletion src/commands/refuse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ const refuseCommand: Command = {
name: commandName,
nameLocalizations: commandDescription,
defaultMemberPermissions: [],
dmPermission: false,
};
},
async interact(interaction: ApplicationUserInteraction): Promise<void> {
Expand Down
1 change: 0 additions & 1 deletion src/commands/verify.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ const verifyCommand: Command = {
description: commandDescription["en-US"],
descriptionLocalizations: commandDescription,
defaultMemberPermissions: [],
dmPermission: false,
};
},
async interact(interaction: ApplicationUserInteraction): Promise<void> {
Expand Down
35 changes: 29 additions & 6 deletions src/shicka.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import type {
ApplicationCommandDataResolvable,
AutoModerationActionMetadataOptions,
AutoModerationActionOptions,
AutoModerationRuleCreateOptions,
ClientApplication,
ClientEvents,
Collection,
ForumChannel,
Expand Down Expand Up @@ -35,16 +37,26 @@ import schedule from "node-schedule";
import * as commands from "./commands.js";
import * as hooks from "./hooks.js";
import * as rules from "./rules.js";
type ApplicationCommandCreateOptionsResolvable = ApplicationCommandData;
type GlobalApplicationCommandCreateOptionsResolvable = ApplicationCommandCreateOptionsResolvable & {global: true};
type GuildApplicationCommandCreateOptionsResolvable = ApplicationCommandCreateOptionsResolvable & {global?: false};
type WebhookCreateOptionsResolvable = WebhookData["hookOptions"];
type WebjobEvent = WebjobInvocation["event"];
type AutoModerationRuleCreateOptionsResolvable = AutoModerationRuleData;
const {
SHICKA_DISCORD_TOKEN,
}: NodeJS.ProcessEnv = process.env;
const discordToken: string = SHICKA_DISCORD_TOKEN ?? "";
async function submitGuildCommands(guild: Guild, commandRegistry: ApplicationCommandData[]): Promise<boolean> {
async function submitCommands(applicationOrGuild: ClientApplication | Guild, globalOrGuildCommandRegistry: ApplicationCommandCreateOptionsResolvable[]): Promise<boolean> {
const commandRegistry: ApplicationCommandDataResolvable[] = globalOrGuildCommandRegistry.map<ApplicationCommandDataResolvable>((commandOptionsResolvable: ApplicationCommandCreateOptionsResolvable): ApplicationCommandDataResolvable => {
const {
global,
...commandOptions
}: ApplicationCommandCreateOptionsResolvable = commandOptionsResolvable;
return commandOptions;
})
try {
await guild.commands.set(commandRegistry);
await applicationOrGuild.commands.set(commandRegistry);
} catch (error: unknown) {
console.warn(error);
return false;
Expand Down Expand Up @@ -267,9 +279,23 @@ client.once("ready", async (client: Client<true>): Promise<void> => {
const command: Command = commands[commandName as keyof typeof commands];
return command.register();
});
const globalCommandRegistry: GlobalApplicationCommandCreateOptionsResolvable[] = commandRegistry.filter<GlobalApplicationCommandCreateOptionsResolvable>((commandData: ApplicationCommandData): commandData is GlobalApplicationCommandCreateOptionsResolvable => {
return commandData.global ?? false;
});
try {
const submitted: boolean = await submitCommands(client.application, globalCommandRegistry);
if (submitted == false) {
throw new Error();
}
} catch (error: unknown) {
console.error(error);
}
const guildCommandRegistry: GuildApplicationCommandCreateOptionsResolvable[] = commandRegistry.filter<GuildApplicationCommandCreateOptionsResolvable>((commandData: ApplicationCommandData): commandData is GuildApplicationCommandCreateOptionsResolvable => {
return !(commandData.global ?? false);
});
for (const guild of client.guilds.cache.values()) {
try {
const submitted: boolean = await submitGuildCommands(guild, commandRegistry);
const submitted: boolean = await submitCommands(guild, guildCommandRegistry);
if (submitted == false) {
throw new Error();
}
Expand Down Expand Up @@ -433,9 +459,6 @@ client.on("interactionCreate", async (interaction: Interaction): Promise<void> =
if (command == null) {
return;
}
if (command.guild == null) {
return;
}
if (command.type !== ApplicationCommandType.ChatInput && command.type !== ApplicationCommandType.Message) {
return;
}
Expand Down

0 comments on commit 4782306

Please sign in to comment.