Skip to content

Commit

Permalink
feat(cmd): convert language to slash command
Browse files Browse the repository at this point in the history
  • Loading branch information
c43721 committed Nov 27, 2024
1 parent 16fd50e commit b534d0e
Show file tree
Hide file tree
Showing 4 changed files with 127 additions and 144 deletions.
174 changes: 51 additions & 123 deletions src/commands/Admin/language.ts
Original file line number Diff line number Diff line change
@@ -1,147 +1,75 @@
import { ApplyOptions, RequiresGuildContext, RequiresUserPermissions } from "@sapphire/decorators";
import { Message, EmbedBuilder } from "discord.js";
import { send } from "@sapphire/plugin-editable-commands";
import { ApplyOptions } from "@sapphire/decorators";
import { EmbedBuilder, InteractionContextType, StringSelectMenuBuilder, ActionRowBuilder } from "discord.js";
import PayloadColors from "#utils/colors";
import { inlineCode } from "@discordjs/builders";
import { LanguageKeys } from "#lib/i18n/all";
import { Subcommand, type SubcommandMappingArray } from "@sapphire/plugin-subcommands";
import { Args, CommandOptionsRunTypeEnum } from "@sapphire/framework";
import { fetchT } from "@sapphire/plugin-i18next";
import { Subcommand } from "@sapphire/plugin-subcommands";
import { Command, CommandOptionsRunTypeEnum } from "@sapphire/framework";
import { fetchT, getLocalizedData } from "@sapphire/plugin-i18next";
import { PermissionFlagsBits } from "discord-api-types/v10";
import { guild } from "#root/drizzle/schema";
import { eq } from "drizzle-orm";
import { isNullishOrEmpty } from "@sapphire/utilities";
import { PayloadCommand } from "#lib/structs/commands/PayloadCommand";

@ApplyOptions<Subcommand.Options>({
description: LanguageKeys.Commands.Language.Description,
detailedDescription: LanguageKeys.Commands.Language.DetailedDescription,
runIn: [CommandOptionsRunTypeEnum.GuildText],
})
export class UserCommand extends Subcommand {
private readonly database = this.container.database;
private readonly t = async (msg: Message) => await fetchT(msg);

readonly subcommandMappings: SubcommandMappingArray = [
{
name: "view",
type: "method",
messageRun: msg => this.view(msg),
default: true,
},
{
name: "set",
type: "method",
messageRun: (msg, args) => this.set(msg, args),
},
{
name: "update",
type: "method",
messageRun: (msg, args) => this.set(msg, args),
},
{
name: "delete",
type: "method",
messageRun: msg => this.delete(msg),
},
{
name: "remove",
type: "method",
messageRun: msg => this.delete(msg),
},
];

@RequiresGuildContext()
async view(msg: Message) {
const [g] = await this.database.select({ language: guild.language }).from(guild).where(eq(guild.id, msg.guildId));

const t = await this.t(msg);

const content = t(LanguageKeys.Commands.Language.CurrentLanguage, {
language: inlineCode(g?.language ?? "en-US"),
});

return await send(msg, content);
}

@RequiresGuildContext()
@RequiresUserPermissions([PermissionFlagsBits.Administrator])
async set(msg: Message, args: Args) {
const [g] = await this.database.select({ language: guild.language }).from(guild).where(eq(guild.id, msg.guildId));

const language = await args
.pick("enum", { enum: ["en-US", "es-ES", "fi-FI", "pl-PL", "ru-RU", "de-DE"] })
.catch(() => null);

const t = await this.t(msg);

if (isNullishOrEmpty(language)) {
return await send(msg, t(LanguageKeys.Commands.Language.SetNeedsArgs));
}

if (g?.language === language) {
return await send(msg, t(LanguageKeys.Commands.Language.SetSameLanguage));
}

await this.database
.update(guild)
.set({
language,
})
.where(eq(guild.id, msg.guildId));
export class UserCommand extends PayloadCommand {
async chatInputRun(interaction: Command.ChatInputCommandInteraction) {
const t = await fetchT(interaction);

const [g] = await this.database
.select({ language: guild.language })
.from(guild)
.where(eq(guild.id, interaction.guildId));

const languageSelector = new ActionRowBuilder<StringSelectMenuBuilder>().addComponents(
new StringSelectMenuBuilder()
.addOptions(
{ label: t(LanguageKeys.Commands.Language.English), value: "en-US" },
{ label: t(LanguageKeys.Commands.Language.Spanish), value: "es-ES" },
{ label: t(LanguageKeys.Commands.Language.German), value: "de" },
{ label: t(LanguageKeys.Commands.Language.Finnish), value: "fi" },
{ label: t(LanguageKeys.Commands.Language.French), value: "fr" },
{ label: t(LanguageKeys.Commands.Language.Russian), value: "ru" },
{ label: t(LanguageKeys.Commands.Language.Polish), value: "pl" },
)
.setCustomId("set-guild-language")
.setPlaceholder(t(LanguageKeys.Commands.Language.SelectLanguage)),
);

const embed = new EmbedBuilder({
author: {
name: msg.author.tag,
iconURL: msg.author.displayAvatarURL(),
},
title: t(LanguageKeys.Commands.Language.SetLanguageEmbedTitle, {
user: msg.author.tag,
}),
description: t(LanguageKeys.Commands.Language.SetLanguageEmbedDesc, {
old: inlineCode(g?.language ?? "en-US"),
new: inlineCode(language),
title: t(LanguageKeys.Commands.Language.SelectLanguage),
description: t(LanguageKeys.Commands.Language.CurrentLanguage, {
language: inlineCode(g?.language ?? "en-US"),
}),
timestamp: new Date(),
color: PayloadColors.Admin,
});

return await send(msg, { embeds: [embed] });
}

@RequiresGuildContext()
@RequiresUserPermissions([PermissionFlagsBits.Administrator])
async delete(msg: Message) {
const [g] = await this.database.select({ language: guild.language }).from(guild).where(eq(guild.id, msg.guildId));

const t = await this.t(msg);

if (g == null || g.language === "en-US") {
return await send(msg, t(LanguageKeys.Commands.Language.DeleteAlreadyDefault));
}

const embed = new EmbedBuilder({
author: {
name: msg.author.tag,
iconURL: msg.author.displayAvatarURL(),
},
title: t(LanguageKeys.Commands.Language.SetLanguageEmbedTitle, {
user: msg.author.tag,
}),
description: t(LanguageKeys.Commands.Language.SetLanguageEmbedDesc, {
old: inlineCode(g.language ?? "en-US"),
new: inlineCode("en-US"),
}),
timestamp: new Date(),
color: PayloadColors.Admin,
await interaction.reply({
embeds: [embed],
components: [languageSelector],
ephemeral: true,
});

await this.database
.update(guild)
.set({
language: "en-US",
})
.where(eq(guild.id, msg.guildId));
return;
}

return await send(msg, { embeds: [embed] });
public override registerApplicationCommands(registry: Command.Registry) {
const rootNameLocalizations = getLocalizedData(LanguageKeys.Commands.Language.Name);
const rootDescriptionLocalizations = getLocalizedData(this.description);

registry.registerChatInputCommand(builder =>
builder
.setName(this.name)
.setDescription(rootDescriptionLocalizations.localizations["en-US"])
.setContexts(InteractionContextType.Guild)
.setDefaultMemberPermissions(PermissionFlagsBits.Administrator)
.setDescriptionLocalizations(rootDescriptionLocalizations.localizations)
.setNameLocalizations(rootNameLocalizations.localizations),
);
}
}
50 changes: 50 additions & 0 deletions src/interaction-handlers/setGuildLanguage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { LanguageKeys } from "#lib/i18n/all/index";
import { guild } from "#root/drizzle/schema";
import { InteractionHandler, InteractionHandlerTypes } from "@sapphire/framework";
import { fetchT } from "@sapphire/plugin-i18next";
import { isNullOrUndefinedOrEmpty } from "@sapphire/utilities";
import { inlineCode, type ButtonInteraction, type StringSelectMenuInteraction } from "discord.js";

export class ButtonHandler extends InteractionHandler {
public constructor(ctx: InteractionHandler.LoaderContext, options: InteractionHandler.Options) {
super(ctx, {
...options,
interactionHandlerType: InteractionHandlerTypes.SelectMenu,
});
}

public override parse(interaction: ButtonInteraction) {
if (interaction.customId !== "set-guild-language") {
return this.none();
}

return this.some();
}

public async run(interaction: StringSelectMenuInteraction) {
const t = await fetchT(interaction);
const value = interaction.values[0];

if (isNullOrUndefinedOrEmpty(value)) {
return;
}

await this.container.database
.insert(guild)
.values({
id: interaction.guildId,
language: value,
})
.onConflictDoUpdate({
set: {
language: value,
},
target: guild.id,
});

await interaction.reply({
content: t(LanguageKeys.Commands.Language.SetLanguage, { language: inlineCode(value) }),
ephemeral: true,
});
}
}
28 changes: 13 additions & 15 deletions src/languages/en-US/commands/language.json
Original file line number Diff line number Diff line change
@@ -1,20 +1,18 @@
{
"description": "Manages your language for this server",
"name": "language",
"description": "Manage the language for the server",
"detailedDescription": {
"usages": [
"",
"set <new language>",
"update <new language>",
"delete",
"reset",
"view"
],
"details": "Manage your server's language. You may reset it at anytime using the `delete` or `reset` arguments."
"usages": [],
"details": "Manage your server's language"
},
"currentLanguage": "The current language is: {{ language }}",
"setNeedsArgs": "Please specify a new language!",
"setSameLanguage": "Your new language is the same as the old language!",
"setLanguageEmbedTitle": "Language updated by {{ user }}",
"setLanguageEmbedDesc": "Language updated from {{ old }} to {{ new }}",
"deleteAlreadyDefault": "Your language is already the default!"
"selectLanguage": "Select a language",
"setLanguage": "New language set: {{ language }}",
"english": "English",
"spanish": "Spanish",
"german": "German",
"finnish": "Finnish",
"french": "French",
"polish": "Polish",
"russian": "Russian"
}
19 changes: 13 additions & 6 deletions src/lib/i18n/all/keys/commands/language.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
import { FT, T } from "#lib/types/index";

export const Name = T<string>("commands/language:name");
export const Description = T<string>("commands/language:description");
export const DetailedDescription = T<string>("commands/language:detailedDescription");
export const CurrentLanguage = FT<{ language: string }, string>("commands/language:currentLanguage");
export const SetNeedsArgs = T<string>("commands/language:setNeedsArgs");
export const SetSameLanguage = T<string>("commands/language:setSamelanguage");
export const SetLanguageEmbedTitle = FT<{ user: string }, string>("commands/language:setLanguageEmbedTitle");
export const SetLanguageEmbedDesc = FT<{ old: string; new: string }, string>("commands/language:setLanguageEmbedDesc");
export const DeleteAlreadyDefault = T<string>("commands/language:deleteAlreadyDefault");

export const SelectLanguage = T<string>("commands/language:selectLanguage");
export const SetLanguage = FT<string, { language: string }>("commands/language:setLanguage");
export const CurrentLanguage = FT<string, { language: string }>("commands/language:currentLanguage");

export const English = T<string>("commands/language:english");
export const Spanish = T<string>("commands/language:spanish");
export const German = T<string>("commands/language:german");
export const Finnish = T<string>("commands/language:finnish");
export const French = T<string>("commands/language:french");
export const Polish = T<string>("commands/language:polish");
export const Russian = T<string>("commands/language:russian");

0 comments on commit b534d0e

Please sign in to comment.