import { SlashCommandBuilder, EmbedBuilder, inlineCode, PermissionFlagsBits, MessageFlags } from "discord.js" import type { ChatInputCommandInteraction, AutocompleteInteraction, ApplicationCommandOptionChoiceData, Locale } from "discord.js" import * as AMP from "@/utils/amp" import type { Host, Instance, InstanceFields, InstanceResult, LoginSuccessData } from "@/types/amp" import type { ReturnMsgData } from "@/types" import type { GuildAmp } from "@/types/schemas" import dbGuild from "@/schemas/guild" import { t } from "@/utils/i18n" function returnMsg(result: ReturnMsgData, locale: Locale) { if (result.status === "fail") return `${t(locale, "common.failed")}\n${inlineCode(`${result.Title}: ${result.Message}`)}` if (result.status === "error") return `${t(locale, "common.error_occurred")}\n${inlineCode(`${result.error_code}`)}` } export const data = new SlashCommandBuilder() .setName("amp") .setDescription("Access my AMP gaming panel") .setDescriptionLocalizations({ fr: "Accède à mon panel de jeu AMP" }) .setDefaultMemberPermissions(PermissionFlagsBits.Administrator) .addSubcommand(subcommand => subcommand .setName("login") .setDescription("Log in before performing another command") .setNameLocalizations({ fr: "connexion" }) .setDescriptionLocalizations({ fr: "Connectez-vous avant d'effectuer une autre commande" }) .addStringOption(option => option .setName("username") .setDescription("Username") .setNameLocalizations({ fr: "nom_utilisateur" }) .setDescriptionLocalizations({ fr: "Nom d'utilisateur" }) .setRequired(true) ) .addStringOption(option => option .setName("password") .setDescription("Password") .setNameLocalizations({ fr: "mot_de_passe" }) .setDescriptionLocalizations({ fr: "Mot de passe" }) .setRequired(true) ) .addBooleanOption(option => option .setName("remember") .setDescription("Remember credentials") .setNameLocalizations({ fr: "memoriser" }) .setDescriptionLocalizations({ fr: "Mémoriser les identifiants" }) .setRequired(true) ) .addStringOption(option => option .setName("otp") .setDescription("Two-factor authentication code") .setNameLocalizations({ fr: "otp" }) .setDescriptionLocalizations({ fr: "Code d'authentification à 2 facteurs" }) ) ) .addSubcommandGroup(subcommandgroup => subcommandgroup .setName("instances") .setDescription("Interact with AMP instances") .setNameLocalizations({ fr: "instances" }) .setDescriptionLocalizations({ fr: "Intéragir avec les instances AMP" }) .addSubcommand(subcommand => subcommand .setName("list") .setDescription("List all available instances") .setNameLocalizations({ fr: "liste" }) .setDescriptionLocalizations({ fr: "Lister toutes les instances disponibles" }) ) .addSubcommand(subcommand => subcommand .setName("manage") .setDescription("Manage an instance") .setNameLocalizations({ fr: "gerer" }) .setDescriptionLocalizations({ fr: "Gérer une instance" }) .addStringOption(option => option .setName("instance") .setDescription("Instance name") .setNameLocalizations({ fr: "instance" }) .setDescriptionLocalizations({ fr: "Nom de l'instance" }) .setRequired(true) .setAutocomplete(true) ) ) .addSubcommand(subcommand => subcommand .setName("restart") .setDescription("Restart an instance") .setNameLocalizations({ fr: "redemarrer" }) .setDescriptionLocalizations({ fr: "Redémarrer une instance" }) .addStringOption(option => option .setName("name") .setDescription("Instance name") .setNameLocalizations({ fr: "nom" }) .setDescriptionLocalizations({ fr: "Nom de l'instance" }) .setRequired(true) ) ) ) export async function execute(interaction: ChatInputCommandInteraction) { const guildProfile = await dbGuild.findOne({ guildId: interaction.guild?.id }) if (!guildProfile) return interaction.reply({ content: t(interaction.locale, "common.database_not_found"), flags: MessageFlags.Ephemeral }) const dbData = guildProfile.get("guildAmp") as GuildAmp if (!dbData.enabled) return interaction.reply({ content: t(interaction.locale, "amp.module_disabled"), flags: MessageFlags.Ephemeral }) const host = dbData.host if (!host) return interaction.reply({ content: t(interaction.locale, "amp.host_not_configured"), flags: MessageFlags.Ephemeral }) let username = dbData.username let sessionID = dbData.sessionID let rememberMeToken = dbData.rememberMeToken const subcommandGroup = interaction.options.getSubcommandGroup(false) const subcommand = interaction.options.getSubcommand(true) if (subcommand == "login") { // Get a SessionID and a RememberMeToken if wanted await interaction.deferReply({ flags: MessageFlags.Ephemeral }) const details = { username: interaction.options.getString("username", true), password: interaction.options.getString("password", true), token: interaction.options.getString("otp") ?? "", rememberMe: interaction.options.getBoolean("remember", true) } const result = await AMP.Core.Login(host, details) if (result.status !== "success") return interaction.followUp({ content: returnMsg(result, interaction.locale), flags: MessageFlags.Ephemeral }) const loginData = result.data as LoginSuccessData username = dbData.username = loginData.userInfo.Username sessionID = dbData.sessionID = loginData.sessionID rememberMeToken = dbData.rememberMeToken = loginData.rememberMeToken guildProfile.set("guildAmp", dbData) guildProfile.markModified("guildAmp") await guildProfile.save().catch(console.error) return interaction.followUp({ content: t(interaction.locale, "amp.logged_in", { username }), flags: MessageFlags.Ephemeral }) } await interaction.deferReply() // Check if the SessionID is still valid if (!sessionID) return interaction.followUp({ content: t(interaction.locale, "amp.login_required"), flags: MessageFlags.Ephemeral }) const checkResult = await AMP.CheckSession(host, sessionID) if (checkResult.status === "fail") { if (rememberMeToken && username) { // Refresh the SessionID if the RememberMeToken is available const details = { username, password: "", token: rememberMeToken, rememberMe: true } const loginResult = await AMP.Core.Login(host, details) if (loginResult.status !== "success") return interaction.followUp({ content: returnMsg(loginResult, interaction.locale), flags: MessageFlags.Ephemeral }) const loginData = loginResult.data as LoginSuccessData sessionID = loginData.sessionID } else return interaction.followUp({ content: t(interaction.locale, "amp.login_required"), flags: MessageFlags.Ephemeral }) } else if (checkResult.status === "error") return interaction.followUp({ content: returnMsg(checkResult, interaction.locale), flags: MessageFlags.Ephemeral }) if (subcommandGroup == "instances") { if (subcommand == "list") { const result = (await AMP.ADSModule.GetInstances(host, sessionID)) as InstanceResult if (result.status !== "success") return interaction.followUp({ content: returnMsg(result, interaction.locale), flags: MessageFlags.Ephemeral }) await interaction.followUp({ content: t(interaction.locale, "amp.hosts_found", { count: result.data.length }) }) await Promise.all(result.data.map(async host => { const fields = [] as InstanceFields[] host.AvailableInstances.forEach((instance: Instance) => { fields.push({ name: instance.FriendlyName, value: `**${t(interaction.locale, "amp.running")}:** ${instance.Running}\n**${t(interaction.locale, "amp.port")}:** ${instance.Port}\n**${t(interaction.locale, "amp.module")}:** ${instance.Module}`, inline: true }) }) const embed = new EmbedBuilder() .setTitle(host.FriendlyName) .setDescription(t(interaction.locale, "amp.instance_list", { count: host.AvailableInstances.length })) .setColor(interaction.guild?.members.me?.displayColor ?? "#ffc370") .setTimestamp() .setFields(fields) return interaction.followUp({ embeds: [embed] }) })) } else if (subcommand == "manage") { const instanceID = interaction.options.getString("instance", true) const manageResult = await AMP.ADSModule.ManageInstance(host, sessionID, instanceID) if (manageResult.status !== "success") return interaction.followUp({ content: returnMsg(manageResult, interaction.locale), flags: MessageFlags.Ephemeral }) const serversResult = await AMP.ADSModule.Servers(host, sessionID, instanceID) if (serversResult.status !== "success") return interaction.followUp({ content: returnMsg(serversResult, interaction.locale), flags: MessageFlags.Ephemeral }) return interaction.followUp({ content: t(interaction.locale, "amp.manage_success") }) } else if (subcommand == "restart") { const query = interaction.options.getString("name", true) const restartResult = await AMP.ADSModule.RestartInstance(host, sessionID, query) if (restartResult.status !== "success") return interaction.followUp({ content: returnMsg(restartResult, interaction.locale), flags: MessageFlags.Ephemeral }) return interaction.followUp({ content: t(interaction.locale, "amp.restart_success") }) } } } export async function autocompleteRun(interaction: AutocompleteInteraction) { const query = interaction.options.getString("instance", true) const guildProfile = await dbGuild.findOne({ guildId: interaction.guild?.id }) if (!guildProfile) return interaction.respond([]) const dbData = guildProfile.get("guildAmp") as GuildAmp if (!dbData.enabled) return interaction.respond([]) const host = dbData.host if (!host) return interaction.respond([]) let sessionID = dbData.sessionID if (!sessionID) return interaction.respond([]) const username = dbData.username const rememberMeToken = dbData.rememberMeToken const checkResult = await AMP.CheckSession(host, sessionID) if (checkResult.status === "fail") { if (rememberMeToken && username) { // Refresh the SessionID if the RememberMeToken is available const details = { username, password: "", token: rememberMeToken, rememberMe: true } const loginResult = await AMP.Core.Login(host, details) if (loginResult.status !== "success") return interaction.respond([]) const loginData = loginResult.data as LoginSuccessData sessionID = loginData.sessionID } else return interaction.respond([]) } else if (checkResult.status === "error") return interaction.respond([]) const instancesResult = (await AMP.ADSModule.GetInstances(host, sessionID)) as InstanceResult if (instancesResult.status !== "success") return interaction.respond([]) const choices: ApplicationCommandOptionChoiceData[] = [] const hosts = instancesResult.data as Host[] hosts.forEach(host => { const instances = host.AvailableInstances instances.forEach(instance => { if (instance.FriendlyName.includes(query)) choices.push({ name: `${host.FriendlyName} - ${instance.FriendlyName}`, value: instance.InstanceID }) }) }) return interaction.respond(choices) }