Réécriture complète 4.0
	
		
			
	
		
	
	
		
	
		
			Some checks failed
		
		
	
	
		
			
				
	
				Build and Push Docker Image / build-and-push (push) Failing after 6m16s
				
			
		
		
	
	
				
					
				
			
		
			Some checks failed
		
		
	
	Build and Push Docker Image / build-and-push (push) Failing after 6m16s
				
			This commit is contained in:
		| @@ -1,210 +1,241 @@ | ||||
| import { SlashCommandBuilder, ChatInputCommandInteraction, AutocompleteInteraction, ApplicationCommandOptionChoiceData, EmbedBuilder, inlineCode, PermissionFlagsBits } from 'discord.js' | ||||
| import dbGuild from '../../schemas/guild' | ||||
| import * as AMP from '../../utils/amp' | ||||
|  | ||||
| interface InstanceFields { | ||||
| 	name: string | ||||
| 	value: string | ||||
| 	inline: boolean | ||||
| } | ||||
| interface InstanceResult { | ||||
| 	status: string | ||||
| 	data:  [ | ||||
| 		Host | ||||
| 	] | ||||
| } | ||||
| interface Host { | ||||
| 	AvailableInstances: Instance[] | ||||
| 	FriendlyName: string | ||||
| } | ||||
| interface Instance { | ||||
| 	InstanceID: string | ||||
| 	FriendlyName: string | ||||
| 	Running: boolean | ||||
| 	Module: string | ||||
| 	Port: number | ||||
| } | ||||
| interface FailMsgData { | ||||
|     Title: string | ||||
|     Message: string | ||||
| } | ||||
| interface ErrorMsgData { | ||||
|     error_code: string | ||||
| } | ||||
|  | ||||
| function failMsg(data: FailMsgData) { return `La commande a échouée !\n${inlineCode(`${data.Title}: ${data.Message}`)}` } | ||||
| function errorMsg(data: ErrorMsgData) { return `Y'a eu une erreur !\n${inlineCode(`${data.error_code}`)}` } | ||||
|  | ||||
| export default { | ||||
| 	data: new SlashCommandBuilder() | ||||
| 	.setName('amp') | ||||
| 	.setDescription('Accède à mon panel de jeu AMP !') | ||||
| 	.setDefaultMemberPermissions(PermissionFlagsBits.Administrator) | ||||
| 	.addSubcommand(subcommand => subcommand.setName('login').setDescription("Connectez-vous avant d'effectuer une autre commande !") | ||||
| 		.addStringOption(option => option.setName('username').setDescription("Nom d'Utilisateur").setRequired(true)) | ||||
| 		.addStringOption(option => option.setName('password').setDescription('Mot de Passe').setRequired(true)) | ||||
| 		.addBooleanOption(option => option.setName('remember').setDescription('Mémoriser les identifiants').setRequired(true)) | ||||
| 		.addStringOption(option => option.setName('otp').setDescription('Code de double authentification')))		 | ||||
| 	.addSubcommandGroup(subcommandgroup => subcommandgroup.setName('instances').setDescription('Intéragir avec les instances AMP.') | ||||
| 		.addSubcommand(subcommand => subcommand.setName('list').setDescription('Liste toutes les instances disponibles.')) | ||||
| 		.addSubcommand(subcommand => subcommand.setName('manage').setDescription('Gérer une instance.') | ||||
| 			.addStringOption(option => option.setName('instance').setDescription("Nom de l'instance").setRequired(true).setAutocomplete(true))) | ||||
| 		.addSubcommand(subcommand => subcommand.setName('restart').setDescription('Redémarre une instance.') | ||||
| 			.addStringOption(option => option.setName('name').setDescription("Nom de l'instance").setRequired(true))) | ||||
| 	), | ||||
| 	async autocompleteRun(interaction: AutocompleteInteraction) { | ||||
| 		let query = interaction.options.getString('instance', true) | ||||
| 		 | ||||
| 		let guildProfile = await dbGuild.findOne({ guildId: interaction?.guild?.id }) | ||||
| 		if (!guildProfile) return await interaction.respond([]) | ||||
|  | ||||
| 		let dbData = guildProfile.get('guildAmp') | ||||
| 		if (!dbData?.enabled) return await interaction.respond([]) | ||||
|  | ||||
| 		let host = dbData.host as string | ||||
| 		let username = dbData.username as string | ||||
| 		let sessionID = dbData.sessionID as string | ||||
| 		let rememberMeToken = dbData.rememberMeToken as string | ||||
| 		 | ||||
| 		// Check if the SessionID is still valid | ||||
| 		let session = await AMP.CheckSession(host, sessionID) | ||||
| 		if (session.status === 'fail') { | ||||
| 			if (rememberMeToken) { | ||||
| 				// Refresh the SessionID if the RememberMeToken is available | ||||
| 				let details = { username, password: '', token: rememberMeToken, rememberMe: true } | ||||
| 				let result = await AMP.Core.Login(host, details) | ||||
|  | ||||
| 				if (result.status === 'success') sessionID = result.data.sessionID | ||||
| 				else if (result.status === 'fail') return interaction.respond([]) | ||||
| 				else if (result.status === 'error') return interaction.respond([]) | ||||
| 			} else return await interaction.respond([]) | ||||
| 		} | ||||
| 		else if (session.status === 'error') return interaction.respond([]) | ||||
| 		 | ||||
| 		let choices: ApplicationCommandOptionChoiceData[] = [] | ||||
| 		let result = await AMP.ADSModule.GetInstances(host, sessionID) | ||||
| 		if (result.status === 'success') { | ||||
| 			let hosts = result.data.result as Host[] | ||||
| 			hosts.forEach(host => { | ||||
| 				let instances = host.AvailableInstances as Instance[] | ||||
| 				instances.forEach(instance => { | ||||
| 					if (instance.FriendlyName.includes(query)) choices.push({ name: `${host.FriendlyName} - ${instance.FriendlyName}`, value: instance.InstanceID }) | ||||
| 				}) | ||||
| 			}) | ||||
| 		} | ||||
| 		else if (result.status === 'fail') return interaction.respond([]) | ||||
| 		else if (result.status === 'error') return interaction.respond([]) | ||||
| 		 | ||||
| 		return interaction.respond(choices) | ||||
| 	}, | ||||
| 	async execute(interaction: ChatInputCommandInteraction) { | ||||
| 		let guildProfile = await dbGuild.findOne({ guildId: interaction?.guild?.id }) | ||||
| 		if (!guildProfile) return interaction.reply({ content: `Database data for **${interaction.guild?.name}** does not exist, please initialize with \`/database init\` !` }) | ||||
| 		 | ||||
| 		let dbData = guildProfile.get('guildAmp') | ||||
| 		if (!dbData?.enabled) return interaction.reply({ content: `AMP module is disabled for **${interaction.guild?.name}**, please activate with \`/database edit guildAmp.enabled True\` !` }) | ||||
|  | ||||
| 		let host = dbData.host as string | ||||
| 		let username = dbData.username as string | ||||
| 		let sessionID = dbData.sessionID as string | ||||
| 		let rememberMeToken = dbData.rememberMeToken as string | ||||
|  | ||||
| 		// Let the user login | ||||
| 		if (interaction.options.getSubcommand() == 'login') { | ||||
| 			// Get a SessionID and a RememberMeToken if wanted | ||||
| 			await interaction.deferReply({ ephemeral: true }) | ||||
|  | ||||
| 			let details = { | ||||
| 				username: interaction.options.getString('username') || '', | ||||
| 				password: interaction.options.getString('password') || '', | ||||
| 				rememberMe: interaction.options.getBoolean('remember') || '', | ||||
| 				token: interaction.options.getString('otp') || '' | ||||
| 			} | ||||
|  | ||||
| 			let result = await AMP.Core.Login(host, details) | ||||
| 			if (result.status === 'success') { | ||||
| 				username = dbData['username'] = result.data.userInfo.Username | ||||
| 				sessionID = dbData['sessionID'] = result.data.sessionID | ||||
| 				rememberMeToken = dbData['rememberMeToken'] = result.data.rememberMeToken | ||||
|  | ||||
| 				guildProfile.set('guildAmp', dbData) | ||||
| 				guildProfile.markModified('guildAmp') | ||||
| 				await guildProfile.save().catch(console.error) | ||||
|  | ||||
| 				return await interaction.followUp(`Tu es connecté au panel sous **${username}** !`) | ||||
| 			} | ||||
| 			else if (result.status === 'fail') return await interaction.followUp(failMsg(result.data)) | ||||
| 			else if (result.status === 'error') return await interaction.followUp(errorMsg(result.data)) | ||||
| 		} | ||||
| 		await interaction.deferReply() | ||||
| 		 | ||||
| 		// Check if the SessionID is still valid | ||||
| 		let session = await AMP.CheckSession(host, sessionID) | ||||
| 		if (session.status === 'fail') { | ||||
| 			if (rememberMeToken) { | ||||
| 				// Refresh the SessionID if the RememberMeToken is available | ||||
| 				let details = { username, password: '', token: rememberMeToken, rememberMe: true } | ||||
| 				let result = await AMP.Core.Login(host, details) | ||||
|  | ||||
| 				if (result.status === 'success') sessionID = result.data.sessionID | ||||
| 				else if (result.status === 'fail') return await interaction.followUp(failMsg(result.data)) | ||||
| 				else if (result.status === 'error') return await interaction.followUp(errorMsg(result.data)) | ||||
| 			} | ||||
| 			else return await interaction.followUp(`Tu dois te connecter avant d'effectuer une autre commande !`) | ||||
| 		} | ||||
| 		else if (session.status === 'error') return await interaction.followUp(errorMsg(session.data)) | ||||
|  | ||||
| 		if (interaction.options.getSubcommandGroup() == 'instances') { | ||||
| 			if (interaction.options.getSubcommand() == 'list') { | ||||
| 				let result = await AMP.ADSModule.GetInstances(host, sessionID) as InstanceResult | ||||
|  | ||||
| 				if (result.status === 'success') { | ||||
| 					await interaction.followUp({ content: `${result.data.length} hôte(s) trouvé(s) !` }) | ||||
| 					result.data.forEach(async host => { | ||||
| 						let fields = [] as InstanceFields[] | ||||
| 						host.AvailableInstances.forEach((instance: Instance) => { | ||||
| 							fields.push({ | ||||
| 								name: instance.FriendlyName, | ||||
| 								value: `**Running:** ${instance.Running}\n**Port:** ${instance.Port}\n**Module:** ${instance.Module}`, | ||||
| 								inline: true | ||||
| 							}) | ||||
| 						}) | ||||
| 						let embed = new EmbedBuilder() | ||||
| 							.setTitle(host.FriendlyName) | ||||
| 							.setDescription(`Liste des ${host.AvailableInstances.length} instances :`) | ||||
| 							.setColor(interaction.guild?.members.me?.displayColor || '#ffc370') | ||||
| 							.setTimestamp() | ||||
| 							.setFields(fields) | ||||
| 						return await interaction.followUp({ embeds: [embed] }) | ||||
| 					}) | ||||
| 				} | ||||
| 				else if (result.status === 'fail') return await interaction.followUp(failMsg(result.data as unknown as FailMsgData)) | ||||
| 				else if (result.status === 'error') return await interaction.followUp(errorMsg(result.data as unknown as ErrorMsgData)) | ||||
| 			} | ||||
| 			else if (interaction.options.getSubcommand() == 'manage') { | ||||
| 				let instanceID = interaction.options.getString('instance', true) | ||||
| 				let result = await AMP.ADSModule.ManageInstance(host, sessionID, instanceID) | ||||
|  | ||||
| 				if (result.status === 'success') { | ||||
| 					let server = await AMP.ADSModule.Servers(host, sessionID, instanceID) | ||||
|  | ||||
| 					if (server.status === 'success') return await interaction.followUp(`Ok !`) | ||||
| 					else if (server.status === 'fail') return await interaction.followUp(failMsg(server.data)) | ||||
| 					else if (server.status === 'error') return await interaction.followUp(errorMsg(server.data)) | ||||
| 				} | ||||
| 				else if (result.status === 'fail') return await interaction.followUp(failMsg(result.data)) | ||||
| 				else if (result.status === 'error') return await interaction.followUp(errorMsg(result.data)) | ||||
| 			} | ||||
| 			else if (interaction.options.getSubcommand() == 'restart') { | ||||
| 				let query = interaction.options.getString('name') | ||||
| 				if (!query) return | ||||
| 				 | ||||
| 				let result = await AMP.ADSModule.RestartInstance(host, sessionID, query) | ||||
|  | ||||
| 				if (result.status === 'success') return await interaction.followUp(`Ok !`) | ||||
| 				else if (result.status === 'fail') return await interaction.followUp(failMsg(result.data)) | ||||
| 				else if (result.status === 'error') return await interaction.followUp(errorMsg(result.data)) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 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) | ||||
| } | ||||
|   | ||||
| @@ -1,37 +1,39 @@ | ||||
| import { SlashCommandBuilder, EmbedBuilder, ChatInputCommandInteraction, TextChannel, PermissionFlagsBits } from 'discord.js' | ||||
| import { SlashCommandBuilder, EmbedBuilder, PermissionFlagsBits, ChannelType } from "discord.js" | ||||
| import type { ChatInputCommandInteraction } from "discord.js" | ||||
| import { t } from "@/utils/i18n" | ||||
| import { logConsole } from "@/utils/console" | ||||
|  | ||||
| module.exports = { | ||||
| 	data: new SlashCommandBuilder() | ||||
| 		.setName('boost') | ||||
| 		.setDescription('Tester le boost du serveur !') | ||||
| 		.setDefaultMemberPermissions(PermissionFlagsBits.Administrator), | ||||
| 	async execute(interaction: ChatInputCommandInteraction) { | ||||
| 		if (interaction.guild?.id !== '796327643783626782') return interaction.reply({ content: 'Non !' })// Jujul Community | ||||
| 		let member = interaction.member | ||||
| 		if (!member) return console.log(`\u001b[1;31m Aucun membre trouvé !`) | ||||
| export const data = new SlashCommandBuilder() | ||||
| 	.setName("boost") | ||||
| 	.setDescription("Test the server boost") | ||||
| 	.setDescriptionLocalizations({ fr: "Tester le boost du serveur" }) | ||||
| 	.setDefaultMemberPermissions(PermissionFlagsBits.Administrator) | ||||
|  | ||||
| 		let guild = interaction.guild | ||||
| 		if (!guild) return console.log(`\u001b[1;31m Aucun serveur trouvé !`) | ||||
| export async function execute(interaction: ChatInputCommandInteraction) { | ||||
| 	if (interaction.guild?.id !== "796327643783626782") return interaction.reply({ content: t(interaction.locale, "boost.not_authorized") }) // Jujul Community | ||||
|  | ||||
| 		let channel = guild.channels.cache.get('924353449930412153') as TextChannel | ||||
| 		if (!channel) return console.log(`\u001b[1;31m Aucun channel trouvé avec l'id "924353449930412153" !`) | ||||
| 	const member = interaction.member | ||||
| 	if (!member) { logConsole('discordjs', 'boost.no_member'); return } | ||||
|  | ||||
| 		let boostRole = guild.roles.premiumSubscriberRole | ||||
| 		if (!boostRole) return console.log(`\u001b[1;31m Aucun rôle de boost trouvé !`) | ||||
| 	const guild = interaction.guild | ||||
| 	if (!guild.members.me) { logConsole('discordjs', 'boost.not_in_guild'); return } | ||||
|  | ||||
| 		if (!guild.members.me) return console.log(`\u001b[1;31m Je ne suis pas sur le serveur !`) | ||||
|  | ||||
| 		let embed = new EmbedBuilder() | ||||
| 			.setColor(guild.members.me.displayHexColor) | ||||
| 			.setTitle(`Nouveau boost de ${member.user.username} !`) | ||||
| 			.setDescription(` | ||||
| 				Merci à toi pour ce boost.\n | ||||
| 				Grâce à toi, on a atteint ${guild.premiumSubscriptionCount} boosts ! | ||||
| 			`) | ||||
| 			.setThumbnail(member.user.avatar) | ||||
| 			.setTimestamp(new Date()) | ||||
|  | ||||
| 		await channel.send({ embeds: [embed] }) | ||||
| 		await interaction.reply({ content: 'Va voir dans <#924353449930412153> !' }) | ||||
| 	const channel = await guild.channels.fetch("924353449930412153") | ||||
| 	if (!channel || (channel.type !== ChannelType.GuildText && channel.type !== ChannelType.GuildAnnouncement)) { | ||||
| 		logConsole('discordjs', 'boost.no_channel', { channelId: "924353449930412153" }) | ||||
| 		return | ||||
| 	} | ||||
| } | ||||
|  | ||||
| 	const boostRole = guild.roles.premiumSubscriberRole | ||||
| 	if (!boostRole) { logConsole('discordjs', 'boost.no_boost_role'); return } | ||||
|  | ||||
| 	const embed = new EmbedBuilder() | ||||
| 		.setColor(guild.members.me.displayHexColor) | ||||
| 		.setTitle(t(interaction.locale, "boost.new_boost_title", { username: member.user.username })) | ||||
| 		.setDescription(t(interaction.locale, "boost.new_boost_description", { count: (guild.premiumSubscriptionCount ?? 0).toString() })) | ||||
| 		.setThumbnail(member.user.avatar) | ||||
| 		.setTimestamp(new Date()) | ||||
|  | ||||
| 	await channel.send({ embeds: [embed] }) | ||||
| 	return interaction.reply({ content: t(interaction.locale, "boost.check_channel", { channelId: "924353449930412153" }) }) | ||||
| } | ||||
|   | ||||
| @@ -1,76 +1,108 @@ | ||||
| import { SlashCommandBuilder, ChatInputCommandInteraction, EmbedBuilder, APIEmbedField, PermissionFlagsBits } from 'discord.js' | ||||
|  | ||||
| import dbGuildInit from '../../utils/dbGuildInit' | ||||
| import dbGuild from '../../schemas/guild' | ||||
|  | ||||
| const parseObject = (obj: object, prefix = ''): { name: string, value: object | string | boolean }[] => { | ||||
| 	let fields: { name: string, value: object | string | boolean }[] = [] | ||||
| 	 | ||||
| 	for (let [key, value] of Object.entries(obj)) { | ||||
| 		if (typeof value === 'object') fields.push(...parseObject(value, `${prefix}${key}.`)) | ||||
| 		else { | ||||
| 			if (typeof value === 'boolean') value = value ? 'True' : 'False' | ||||
| 			else if (!value) value = 'None' | ||||
| 			else value = value.toString() | ||||
|  | ||||
| 			fields.push({ name: `${prefix}${key}`, value }) | ||||
| 		} | ||||
| 	} | ||||
| 	return fields | ||||
| } | ||||
|  | ||||
| export default { | ||||
| 	data: new SlashCommandBuilder() | ||||
| 		.setName('database') | ||||
| 		.setDescription('Communicate with the database') | ||||
| 		.setDefaultMemberPermissions(PermissionFlagsBits.Administrator) | ||||
| 		.addSubcommand(subcommand => subcommand.setName('info').setDescription('Returns information about the current guild')) | ||||
| 		.addSubcommand(subcommand => subcommand.setName('init').setDescription('Force initialize an entry for the current guild in the database')) | ||||
| 		.addSubcommand(subcommand => subcommand.setName('edit').setDescription('Modify parameters for the current guild') | ||||
| 			.addStringOption(option => option.setName('key').setDescription('Key to modify').setRequired(true)) | ||||
| 			.addStringOption(option => option.setName('value').setDescription('Value to set').setRequired(true))), | ||||
| 	async execute(interaction: ChatInputCommandInteraction) { | ||||
| 		let guild = interaction.guild | ||||
| 		if (!guild) return await interaction.reply({ content: 'This command must be used in a server.', ephemeral: true }) | ||||
| 		 | ||||
| 		let guildProfile = await dbGuild.findOne({ guildId: guild.id }) | ||||
|  | ||||
| 		if (interaction.options.getSubcommand() === 'info') { | ||||
| 			if (!guildProfile) return await interaction.reply({ content: `Database data for **${guild.name}** does not exist !` }) | ||||
|  | ||||
| 			let fields = parseObject(guildProfile.toObject()) | ||||
|  | ||||
| 			let embed = new EmbedBuilder() | ||||
| 				.setTitle('Database Information') | ||||
| 				.setDescription(`Guild **${guildProfile.guildName}** (ID: ${guildProfile.guildId})`) | ||||
| 				.setThumbnail(guildProfile.guildIcon as string) | ||||
| 				.setTimestamp() | ||||
| 				//.addFields(fields as APIEmbedField[]) | ||||
| 				// Limit the number of fields to 25 | ||||
| 				.addFields(fields.slice(0, 25) as APIEmbedField[]) | ||||
| 			return await interaction.reply({ embeds: [embed] }) | ||||
|  | ||||
| 		} else if (interaction.options.getSubcommand() === 'init') { | ||||
| 			if (guildProfile) return await interaction.reply({ content: `Database data for **${guildProfile.guildName}** already exists !` }) | ||||
| 			 | ||||
| 			guildProfile = await dbGuildInit(guild) | ||||
| 			if (!guildProfile) return await interaction.reply({ content: `An error occured while initializing database data for **${guild.name}** !` }) | ||||
| 			 | ||||
| 			return await interaction.reply({ content: `Database data for **${guildProfile.guildName}** successfully initialized !` }) | ||||
| 			 | ||||
| 		} else if (interaction.options.getSubcommand() === 'edit') { | ||||
| 			if (!guildProfile) return await interaction.reply({ content: `Database data for **${guild.name}** does not exist, please init with \`/database init\` !` }) | ||||
|  | ||||
| 			let key = interaction.options.getString('key', true) | ||||
| 			let value = interaction.options.getString('value', true) | ||||
|  | ||||
| 			let oldValue = guildProfile.get(key) | ||||
| 			if (!oldValue) oldValue = 'None' | ||||
|  | ||||
| 			guildProfile.set(key, value) | ||||
| 			await guildProfile.save().catch(console.error) | ||||
|  | ||||
| 			return await interaction.reply({ content: `Database data for **${guildProfile.guildName}** successfully updated !\n**${key}**: ${oldValue} -> ${value}` }) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| import { SlashCommandBuilder, EmbedBuilder, PermissionFlagsBits, MessageFlags } from "discord.js" | ||||
| import type { ChatInputCommandInteraction, APIEmbedField } from "discord.js" | ||||
| import dbGuildInit from "@/utils/dbGuildInit" | ||||
| import dbGuild from "@/schemas/guild" | ||||
| import { t } from "@/utils/i18n" | ||||
|  | ||||
| const parseObject = (obj: object, prefix = ""): { name: string, value: object | string | boolean }[] => { | ||||
| 	const fields: { name: string, value: object | string | boolean }[] = [] | ||||
|  | ||||
| 	for (const [key, value] of Object.entries(obj)) { | ||||
| 		if (value !== null && typeof value === "object") fields.push(...parseObject(value as object, `${prefix}${key}.`)) | ||||
| 		else { | ||||
| 			let newValue: string | ||||
| 			if (typeof value === "boolean") newValue = value ? "True" : "False" | ||||
| 			else if (value === null || value === undefined) newValue = "None" | ||||
| 			else newValue = String(value) | ||||
|  | ||||
| 			fields.push({ name: `${prefix}${key}`, value: newValue }) | ||||
| 		} | ||||
| 	} | ||||
| 	return fields | ||||
| } | ||||
|  | ||||
| export const data = new SlashCommandBuilder() | ||||
| 	.setName("database") | ||||
| 	.setDescription("Communicate with the database") | ||||
| 	.setDescriptionLocalizations({ fr: "Communiquer avec la base de données" }) | ||||
| 	.setDefaultMemberPermissions(PermissionFlagsBits.Administrator) | ||||
| 	.addSubcommand(subcommand => subcommand | ||||
| 		.setName("info") | ||||
| 		.setDescription("Returns information about the current guild") | ||||
| 		.setNameLocalizations({ fr: "info" }) | ||||
| 		.setDescriptionLocalizations({ fr: "Retourne les informations sur le serveur actuel" }) | ||||
| 	) | ||||
| 	.addSubcommand(subcommand => subcommand | ||||
| 		.setName("init") | ||||
| 		.setDescription("Force initialize an entry for the current guild in the database") | ||||
| 		.setNameLocalizations({ fr: "init" }) | ||||
| 		.setDescriptionLocalizations({ fr: "Initialiser de force une entrée pour le serveur actuel dans la base de données" }) | ||||
| 	) | ||||
| 	.addSubcommand(subcommand => subcommand | ||||
| 		.setName("edit") | ||||
| 		.setDescription("Modify parameters for the current guild") | ||||
| 		.setNameLocalizations({ fr: "modifier" }) | ||||
| 		.setDescriptionLocalizations({ fr: "Modifier les paramètres pour le serveur actuel" }) | ||||
| 		.addStringOption(option => option | ||||
| 			.setName("key") | ||||
| 			.setDescription("Key to modify") | ||||
| 			.setNameLocalizations({ fr: "cle" }) | ||||
| 			.setDescriptionLocalizations({ fr: "Clé à modifier" }) | ||||
| 			.setRequired(true) | ||||
| 		) | ||||
| 		.addStringOption(option => option | ||||
| 			.setName("value") | ||||
| 			.setDescription("Value to set") | ||||
| 			.setNameLocalizations({ fr: "valeur" }) | ||||
| 			.setDescriptionLocalizations({ fr: "Valeur à définir" }) | ||||
| 			.setRequired(true) | ||||
| 		) | ||||
| 	) | ||||
|  | ||||
| export async function execute(interaction: ChatInputCommandInteraction) { | ||||
| 	if (interaction.user !== interaction.client.application.owner) return interaction.reply({ content: t(interaction.locale, "database.owner_only"), flags: MessageFlags.Ephemeral }) | ||||
|  | ||||
| 	const guild = interaction.guild | ||||
| 	if (!guild) return interaction.reply({ content: t(interaction.locale, "database.server_only"), flags: MessageFlags.Ephemeral }) | ||||
|  | ||||
| 	let guildProfile = await dbGuild.findOne({ guildId: guild.id }) | ||||
|  | ||||
| 	const subcommand = interaction.options.getSubcommand(true) | ||||
| 	if (subcommand === "info") { | ||||
| 		if (!guildProfile) return interaction.reply({ content: t(interaction.locale, "common.database_not_found"), flags: MessageFlags.Ephemeral }) | ||||
|  | ||||
| 		const fields = parseObject(guildProfile.toObject()) | ||||
| 		const embed = new EmbedBuilder() | ||||
| 			.setTitle(t(interaction.locale, "database.info_title")) | ||||
| 			.setDescription(t(interaction.locale, "database.guild_info", { name: guildProfile.guildName, id: guildProfile.guildId })) | ||||
| 			.setThumbnail(guildProfile.guildIcon) | ||||
| 			.setTimestamp() | ||||
| 			//.addFields(fields as APIEmbedField[]) | ||||
| 			// Limit the number of fields to 25 | ||||
| 			.addFields(fields.slice(0, 25) as APIEmbedField[]) | ||||
|  | ||||
| 		return interaction.reply({ embeds: [embed] }) | ||||
| 	} | ||||
| 	else if (subcommand === "init") { | ||||
| 		if (guildProfile) return interaction.reply({ content: t(interaction.locale, "database.already_exists", { name: guildProfile.guildName }), flags: MessageFlags.Ephemeral }) | ||||
|  | ||||
| 		guildProfile = await dbGuildInit(guild) | ||||
|  | ||||
| 		return interaction.reply({ content: t(interaction.locale, "database.initialized", { name: guildProfile.guildName }), flags: MessageFlags.Ephemeral }) | ||||
| 	} | ||||
| 	else if (subcommand === "edit") { | ||||
| 		if (!guildProfile) return interaction.reply({ content: t(interaction.locale, "common.database_not_found"), flags: MessageFlags.Ephemeral }) | ||||
|  | ||||
| 		const key = interaction.options.getString("key", true) | ||||
| 		const value = interaction.options.getString("value", true) | ||||
|  | ||||
| 		let oldValue: string = guildProfile.get(key) as string | ||||
| 		if (!oldValue) oldValue = t(interaction.locale, "common.none") | ||||
|  | ||||
| 		guildProfile.set(key, value) | ||||
| 		guildProfile.markModified(key) | ||||
| 		await guildProfile.save().catch(console.error) | ||||
|  | ||||
| 		return interaction.reply({ content: t(interaction.locale, "database.updated", { name: guildProfile.guildName, key, oldValue, value }), flags: MessageFlags.Ephemeral }) | ||||
| 	} | ||||
| } | ||||
|   | ||||
							
								
								
									
										353
									
								
								src/commands/global/freebox.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										353
									
								
								src/commands/global/freebox.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,353 @@ | ||||
| import { SlashCommandBuilder, EmbedBuilder, MessageFlags, ButtonBuilder, ButtonStyle, ActionRowBuilder, inlineCode } from "discord.js" | ||||
| import type { ChatInputCommandInteraction } from "discord.js" | ||||
| import crypto from "crypto" | ||||
| import * as Freebox from "@/utils/freebox" | ||||
| import type { | ||||
| 	APIResponseData, APIResponseDataError, APIResponseDataVersion, | ||||
| 	ConnectionStatus, GetChallenge, LcdConfig, OpenSession, RequestAuthorization, TrackAuthorizationProgress | ||||
| } from "@/types/freebox" | ||||
| import type { GuildFbx } from "@/types/schemas" | ||||
| import dbGuild from "@/schemas/guild" | ||||
| import { t } from "@/utils/i18n" | ||||
|  | ||||
| export const data = new SlashCommandBuilder() | ||||
| 	.setName("freebox") | ||||
| 	.setDescription("Access FreeboxOS API") | ||||
| 	.setDescriptionLocalizations({ fr: "Accéder à l'API FreeboxOS" }) | ||||
| 	.addSubcommand(subcommand => subcommand | ||||
| 		.setName("status") | ||||
| 		.setDescription("Display Freebox configuration and status") | ||||
| 		.setDescriptionLocalizations({ fr: "Afficher la configuration et l'état de la Freebox" }) | ||||
| 	) | ||||
| 	.addSubcommand(subcommand => subcommand | ||||
| 		.setName("setup") | ||||
| 		.setDescription("Configure Freebox settings easily") | ||||
| 		.setDescriptionLocalizations({ fr: "Configurer facilement les paramètres Freebox" }) | ||||
| 		.addStringOption(option => option | ||||
| 			.setName("host") | ||||
| 			.setDescription("Freebox host (IP address)") | ||||
| 			.setNameLocalizations({ fr: "hote" }) | ||||
| 			.setDescriptionLocalizations({ fr: "Hôte Freebox (adresse IP)" }) | ||||
| 			.setRequired(false) | ||||
| 		) | ||||
| 		.addIntegerOption(option => option | ||||
| 			.setName("version") | ||||
| 			.setDescription("Freebox API version") | ||||
| 			.setNameLocalizations({ fr: "version" }) | ||||
| 			.setDescriptionLocalizations({ fr: "Version de l'API Freebox" }) | ||||
| 			.setRequired(false) | ||||
| 		) | ||||
| 		.addBooleanOption(option => option | ||||
| 			.setName("enabled") | ||||
| 			.setDescription("Enable or disable the Freebox module") | ||||
| 			.setNameLocalizations({ fr: "active" }) | ||||
| 			.setDescriptionLocalizations({ fr: "Activer ou désactiver le module Freebox" }) | ||||
| 			.setRequired(false) | ||||
| 		) | ||||
| 	) | ||||
| 	.addSubcommand(subcommand => subcommand | ||||
| 		.setName("init") | ||||
| 		.setDescription("Create an app on the Freebox to authenticate") | ||||
| 		.setDescriptionLocalizations({ fr: "Créer une app sur la Freebox pour s'authentifier" }) | ||||
| 		.addStringOption(option => option | ||||
| 			.setName("host") | ||||
| 			.setDescription("Freebox host (IP or domain)") | ||||
| 			.setNameLocalizations({ fr: "hote" }) | ||||
| 			.setDescriptionLocalizations({ fr: "Hôte Freebox (IP ou domaine)" }) | ||||
| 			.setRequired(true) | ||||
| 		) | ||||
| 	) | ||||
| 	.addSubcommandGroup(subcommandGroup => subcommandGroup | ||||
| 		.setName("get") | ||||
| 		.setDescription("Retrieve data") | ||||
| 		.setNameLocalizations({ fr: "recuperer" }) | ||||
| 		.setDescriptionLocalizations({ fr: "Récupérer des données" }) | ||||
| 		.addSubcommand(subcommand => subcommand | ||||
| 			.setName("version") | ||||
| 			.setDescription("Display API version") | ||||
| 			.setDescriptionLocalizations({ fr: "Afficher la version de l'API" }) | ||||
| 		) | ||||
| 		.addSubcommand(subcommand => subcommand | ||||
| 			.setName("connection") | ||||
| 			.setDescription("Retrieve connection information") | ||||
| 			.setNameLocalizations({ fr: "connexion" }) | ||||
| 			.setDescriptionLocalizations({ fr: "Récupérer les informations de connexion" }) | ||||
| 		) | ||||
| 		.addSubcommand(subcommand => subcommand | ||||
| 			.setName("lcd") | ||||
| 			.setDescription("Retrieve LCD configuration") | ||||
| 			.setDescriptionLocalizations({ fr: "Récupérer la configuration de l'écran LCD" }) | ||||
| 		) | ||||
| 	) | ||||
| 	.addSubcommandGroup(subcommandGroup => subcommandGroup | ||||
| 		.setName("lcd") | ||||
| 		.setDescription("Control LCD features") | ||||
| 		.setDescriptionLocalizations({ fr: "Contrôler les fonctionnalités LCD" }) | ||||
| 		.addSubcommand(subcommand => subcommand | ||||
| 			.setName("leds") | ||||
| 			.setDescription("Toggle LED strip on/off") | ||||
| 			.setNameLocalizations({ fr: "leds" }) | ||||
| 			.setDescriptionLocalizations({ fr: "Allumer/éteindre le bandeau LED" }) | ||||
| 			.addBooleanOption(option => option | ||||
| 				.setName("enabled") | ||||
| 				.setDescription("Enable or disable LED strip") | ||||
| 				.setNameLocalizations({ fr: "active" }) | ||||
| 				.setDescriptionLocalizations({ fr: "Activer ou désactiver le bandeau LED" }) | ||||
| 				.setRequired(true) | ||||
| 			) | ||||
| 		) | ||||
| 		.addSubcommand(subcommand => subcommand | ||||
| 			.setName("timer") | ||||
| 			.setDescription("Setup automatic LED timer") | ||||
| 			.setNameLocalizations({ fr: "minuteur" }) | ||||
| 			.setDescriptionLocalizations({ fr: "Configurer le minuteur automatique des LEDs" }) | ||||
| 			.addStringOption(option => option | ||||
| 				.setName("action") | ||||
| 				.setDescription("Timer action") | ||||
| 				.setNameLocalizations({ fr: "action" }) | ||||
| 				.setDescriptionLocalizations({ fr: "Action du minuteur" }) | ||||
| 				.setRequired(true) | ||||
| 				.addChoices( | ||||
| 					{ name: "Enable timer", value: "enable", name_localizations: { fr: "Activer minuteur" } }, | ||||
| 					{ name: "Disable timer", value: "disable", name_localizations: { fr: "Désactiver minuteur" } }, | ||||
| 					{ name: "Status", value: "status", name_localizations: { fr: "Statut" } } | ||||
| 				) | ||||
| 			) | ||||
| 			.addStringOption(option => option | ||||
| 				.setName("morning_time") | ||||
| 				.setDescription("Morning time (HH:MM format, 24h)") | ||||
| 				.setNameLocalizations({ fr: "heure_matin" }) | ||||
| 				.setDescriptionLocalizations({ fr: "Heure du matin (format HH:MM, 24h)" }) | ||||
| 				.setRequired(false) | ||||
| 			) | ||||
| 			.addStringOption(option => option | ||||
| 				.setName("night_time") | ||||
| 				.setDescription("Night time (HH:MM format, 24h)") | ||||
| 				.setNameLocalizations({ fr: "heure_nuit" }) | ||||
| 				.setDescriptionLocalizations({ fr: "Heure du soir (format HH:MM, 24h)" }) | ||||
| 				.setRequired(false) | ||||
| 			) | ||||
| 		) | ||||
| 	) | ||||
|  | ||||
| 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("guildFbx") as GuildFbx | ||||
| 	let host = dbData.host | ||||
| 	let appToken = dbData.appToken | ||||
|  | ||||
| 	const subcommandGroup = interaction.options.getSubcommandGroup(false) | ||||
| 	const subcommand = interaction.options.getSubcommand(true) | ||||
| 	 | ||||
| 	if (subcommand === "status") { | ||||
| 		// Construire l'embed de statut | ||||
| 		const embed = new EmbedBuilder() | ||||
| 			.setTitle(t(interaction.locale, "freebox.status.title")) | ||||
| 			.setColor(dbData.enabled ? 0x00ff00 : 0xff0000) | ||||
| 			.addFields({ | ||||
| 				name: t(interaction.locale, "freebox.status.config_section"), | ||||
| 				value: [ | ||||
| 					t(interaction.locale, "freebox.status.module_field", { status: dbData.enabled ? t(interaction.locale, "freebox.common.enabled") : t(interaction.locale, "freebox.common.disabled") }), | ||||
| 					t(interaction.locale, "freebox.status.host_field", { value: host ? `\`${host}\`` : t(interaction.locale, "freebox.status.host_not_configured") }), | ||||
| 					t(interaction.locale, "freebox.status.token_field", { status: appToken ? t(interaction.locale, "freebox.status.token_configured") : t(interaction.locale, "freebox.status.token_not_configured") }) | ||||
| 				].join('\n'), | ||||
| 				inline: false | ||||
| 			}) | ||||
|  | ||||
| 		// Informations LCD si disponibles | ||||
| 		if (dbData.lcd) { | ||||
| 			const lcdStatus = dbData.lcd.enabled ? t(interaction.locale, "freebox.status.timer_enabled") : t(interaction.locale, "freebox.status.timer_disabled") | ||||
| 			const botManaged = dbData.lcd.botId ? `<@${dbData.lcd.botId}>` : t(interaction.locale, "freebox.status.timer_no_manager") | ||||
| 			const morningTime = dbData.lcd.morningTime ?? t(interaction.locale, "freebox.status.timer_not_configured") | ||||
| 			const nightTime = dbData.lcd.nightTime ?? t(interaction.locale, "freebox.status.timer_not_configured") | ||||
| 			 | ||||
| 			embed.addFields({ | ||||
| 				name: t(interaction.locale, "freebox.status.timer_section"), | ||||
| 				value: [ | ||||
| 					t(interaction.locale, "freebox.timer.status_field", { status: lcdStatus }), | ||||
| 					t(interaction.locale, "freebox.timer.managed_by", { manager: botManaged }), | ||||
| 					t(interaction.locale, "freebox.timer.morning", { time: morningTime }), | ||||
| 					t(interaction.locale, "freebox.timer.night", { time: nightTime }) | ||||
| 				].join('\n'), | ||||
| 				inline: false | ||||
| 			}) | ||||
| 		} | ||||
|  | ||||
| 		// Boutons d'action | ||||
| 		const buttons = new ActionRowBuilder<ButtonBuilder>().addComponents( | ||||
| 			new ButtonBuilder() | ||||
| 				.setCustomId("freebox_test_connection") | ||||
| 				.setLabel(t(interaction.locale, "freebox.buttons.test_connection")) | ||||
| 				.setEmoji("🔌") | ||||
| 				.setStyle(ButtonStyle.Primary) | ||||
| 				.setDisabled(!appToken), | ||||
| 			new ButtonBuilder() | ||||
| 				.setCustomId("freebox_lcd_status") | ||||
| 				.setLabel(t(interaction.locale, "freebox.buttons.lcd_status")) | ||||
| 				.setEmoji("💡") | ||||
| 				.setStyle(ButtonStyle.Secondary) | ||||
| 				.setDisabled(!appToken), | ||||
| 			new ButtonBuilder() | ||||
| 				.setCustomId("freebox_refresh_status") | ||||
| 				.setLabel(t(interaction.locale, "freebox.buttons.refresh_status")) | ||||
| 				.setEmoji("🔄") | ||||
| 				.setStyle(ButtonStyle.Success) | ||||
| 		) | ||||
|  | ||||
| 		return interaction.reply({ embeds: [embed], components: [buttons], flags: MessageFlags.Ephemeral }) | ||||
| 	} | ||||
|  | ||||
| 	if (!dbData.enabled) return interaction.reply({ content: t(interaction.locale, "common.module_disabled"), flags: MessageFlags.Ephemeral }) | ||||
|  | ||||
| 	if (subcommand === "init") { | ||||
| 		if (appToken) return interaction.reply({ content: t(interaction.locale, "freebox.auth.app_token_already_set"), flags: MessageFlags.Ephemeral }) | ||||
|  | ||||
| 		host = interaction.options.getString("host", true) | ||||
| 		if (host === "mafreebox.freebox.fr") return interaction.reply({ content: t(interaction.locale, "freebox.auth.host_not_allowed"), flags: MessageFlags.Ephemeral }) | ||||
|  | ||||
| 		const initData = await Freebox.Core.Init(host) as APIResponseData<RequestAuthorization> | ||||
| 		if (!initData.success) return Freebox.handleError(initData as APIResponseDataError, interaction) | ||||
|  | ||||
| 		appToken = initData.result.app_token | ||||
| 		const trackId = initData.result.track_id | ||||
| 		if (!trackId) return interaction.reply({ content: t(interaction.locale, "freebox.auth.track_id_failed"), flags: MessageFlags.Ephemeral }) | ||||
|  | ||||
| 		// Si l'utilisateur n'a pas encore autorisé l'application, on lui demande de le faire | ||||
| 		await interaction.reply({ content: t(interaction.locale, "freebox.auth.init_in_progress", { trackId }), flags: MessageFlags.Ephemeral }) | ||||
|  | ||||
| 		// eslint-disable-next-line @typescript-eslint/no-misused-promises | ||||
| 		const initCheck = setInterval(async () => { | ||||
| 			if (!host || !trackId) { clearInterval(initCheck); return } | ||||
| 	 | ||||
| 			const trackData = await Freebox.Core.Init(host, trackId) as APIResponseData<TrackAuthorizationProgress> | ||||
| 			if (!trackData.success) return Freebox.handleError(trackData as APIResponseDataError, interaction, false) | ||||
|  | ||||
| 			const status = trackData.result.status | ||||
| 			if (status === "granted") { | ||||
| 				clearInterval(initCheck) | ||||
| 				dbData.appToken = appToken | ||||
|  | ||||
| 				guildProfile.set("guildFbx", dbData) | ||||
| 				guildProfile.markModified("guildFbx") | ||||
| 				await guildProfile.save().catch(console.error) | ||||
|  | ||||
| 				return interaction.followUp({ content: t(interaction.locale, "common.success"), flags: MessageFlags.Ephemeral }) | ||||
| 			} else if (status === "denied") { | ||||
| 				clearInterval(initCheck) | ||||
|  | ||||
| 				return interaction.followUp({ content: t(interaction.locale, "freebox.auth.user_denied_access"), flags: MessageFlags.Ephemeral }) | ||||
| 			} else if (status === "pending") { console.log("Freebox authorization pending...") } | ||||
| 		}, 2000) | ||||
| 	} | ||||
| 	else { | ||||
| 		if (!host) return interaction.reply({ content: t(interaction.locale, "freebox.general.host_not_set"), flags: MessageFlags.Ephemeral }) | ||||
|  | ||||
| 		if (subcommand === "version") { | ||||
| 			const versionData = await Freebox.Core.Version(host) as APIResponseDataVersion | ||||
|  | ||||
| 			const embed = new EmbedBuilder() | ||||
| 				.setTitle("FreeboxOS API Version") | ||||
| 				.setDescription(`Version: ${versionData.api_version || "Unknown"}`) | ||||
|  | ||||
| 			return interaction.reply({ embeds: [embed] }) | ||||
| 		} | ||||
|  | ||||
| 		if (!appToken) return interaction.reply({ content: t(interaction.locale, "freebox.general.app_token_not_set"), flags: MessageFlags.Ephemeral }) | ||||
|  | ||||
| 		const challengeData = await Freebox.Login.Challenge(host) as APIResponseData<GetChallenge> | ||||
| 		if (!challengeData.success) return Freebox.handleError(challengeData as APIResponseDataError, interaction) | ||||
|  | ||||
| 		const password = crypto.createHmac("sha1", appToken).update(challengeData.result.challenge).digest("hex") | ||||
| 		const sessionData = await Freebox.Login.Session(host, password) as APIResponseData<OpenSession> | ||||
| 		if (!sessionData.success) return Freebox.handleError(sessionData as APIResponseDataError, interaction) | ||||
|  | ||||
| 		const sessionToken = sessionData.result.session_token | ||||
| 		if (!sessionToken) return interaction.reply({ content: t(interaction.locale, "freebox.auth.session_token_failed"), flags: MessageFlags.Ephemeral }) | ||||
|  | ||||
| 		if (subcommandGroup === "get") { | ||||
| 			if (subcommand === "connection") { | ||||
| 				const connectionData = await Freebox.Get.Connection(host, sessionToken) as APIResponseData<ConnectionStatus> | ||||
| 				if (!connectionData.success) return Freebox.handleError(connectionData as APIResponseDataError, interaction) | ||||
|  | ||||
| 				return interaction.reply({ content: t(interaction.locale, "freebox.api.connection_details", { details: inlineCode(JSON.stringify(connectionData.result)) }), flags: MessageFlags.Ephemeral }) | ||||
| 			} | ||||
| 			else if (subcommand === "lcd") { | ||||
| 				const lcdData = await Freebox.Get.LcdConfig(host, sessionToken) as APIResponseData<LcdConfig> | ||||
| 				if (!lcdData.success) return Freebox.handleError(lcdData as APIResponseDataError, interaction) | ||||
|  | ||||
| 				return interaction.reply({ content: t(interaction.locale, "freebox.api.lcd_details", { details: inlineCode(JSON.stringify(lcdData.result)) }), flags: MessageFlags.Ephemeral }) | ||||
| 			} | ||||
| 		} | ||||
| 		else if (subcommandGroup === "lcd") { | ||||
| 			// Initialiser l'objet LCD s'il n'existe pas | ||||
| 			dbData.lcd ??= { enabled: false } | ||||
|  | ||||
| 			// Vérifier si le bot est autorisé pour ce serveur | ||||
| 			if (dbData.lcd.enabled && dbData.lcd.botId && dbData.lcd.botId !== interaction.client.user.id) { | ||||
| 				return interaction.reply({ content: t(interaction.locale, "freebox.lcd.managed_by_other_bot"), flags: MessageFlags.Ephemeral }) | ||||
| 			} | ||||
|  | ||||
| 			if (subcommand === "leds") { | ||||
| 				const enabled = interaction.options.getBoolean("enabled", true) | ||||
| 				const lcdData = await Freebox.Set.LcdConfig(host, sessionToken, { led_strip_enabled: enabled }) as APIResponseData<LcdConfig> | ||||
| 				if (!lcdData.success) return Freebox.handleError(lcdData as APIResponseDataError, interaction) | ||||
|  | ||||
| 				return interaction.reply({ content: t(interaction.locale, "freebox.lcd.leds_success", { status: enabled ? t(interaction.locale, "freebox.common.enabled") : t(interaction.locale, "freebox.common.disabled") }), flags: MessageFlags.Ephemeral }) | ||||
| 			} | ||||
| 			else if (subcommand === "timer") { | ||||
| 				const action = interaction.options.getString("action") | ||||
| 				if (!action) return interaction.reply({ content: t(interaction.locale, "freebox.general.invalid_action"), flags: MessageFlags.Ephemeral }) | ||||
|  | ||||
| 				if (action === "status") { | ||||
| 					const status = dbData.lcd.enabled ? t(interaction.locale, "freebox.common.enabled") : t(interaction.locale, "freebox.common.disabled") | ||||
| 					const managedBy = dbData.lcd.botId ? `<@${dbData.lcd.botId}>` : t(interaction.locale, "common.none") | ||||
| 					 | ||||
| 					return interaction.reply({ content: t(interaction.locale, "freebox.timer.status_display", { status, managedBy }), flags: MessageFlags.Ephemeral }) | ||||
| 				} | ||||
| 				else if (action === "enable") { | ||||
| 					const morningTime = interaction.options.getString("morning_time") | ||||
| 					const nightTime = interaction.options.getString("night_time") | ||||
| 					 | ||||
| 					if (!morningTime || !nightTime) return interaction.reply({ content: t(interaction.locale, "freebox.timer.times_required"), flags: MessageFlags.Ephemeral }) | ||||
|  | ||||
| 					// Valider le format HH:MM | ||||
| 					const timeRegex = /^([01]?[0-9]|2[0-3]):[0-5][0-9]$/ | ||||
| 					if (!timeRegex.test(morningTime) || !timeRegex.test(nightTime)) return interaction.reply({ content: t(interaction.locale, "freebox.general.invalid_time_format"), flags: MessageFlags.Ephemeral }) | ||||
|  | ||||
| 					// Activer le timer et enregistrer ce bot comme responsable | ||||
| 					dbData.lcd.enabled = true | ||||
| 					dbData.lcd.botId = interaction.client.user.id | ||||
| 					dbData.lcd.morningTime = morningTime | ||||
| 					dbData.lcd.nightTime = nightTime | ||||
|  | ||||
| 					guildProfile.set("guildFbx", dbData) | ||||
| 					guildProfile.markModified("guildFbx") | ||||
| 					await guildProfile.save().catch(console.error) | ||||
|  | ||||
| 					// Démarrer les timers automatiques | ||||
| 					if (interaction.guildId) Freebox.Timer.schedule(interaction.client, interaction.guildId, dbData) | ||||
|  | ||||
| 					return interaction.reply({ content: t(interaction.locale, "freebox.timer.enabled", { morningTime, nightTime }), flags: MessageFlags.Ephemeral }) | ||||
| 				} | ||||
| 				else if (action === "disable") { | ||||
| 					// Arrêter les timers actifs avant de désactiver | ||||
| 					if (interaction.guildId) Freebox.Timer.clear(interaction.guildId) | ||||
|  | ||||
| 					// Désactiver le timer | ||||
| 					dbData.lcd.enabled = false | ||||
| 					dbData.lcd.botId = "" | ||||
| 					dbData.lcd.morningTime = "" | ||||
| 					dbData.lcd.nightTime = "" | ||||
|  | ||||
| 					guildProfile.set("guildFbx", dbData) | ||||
| 					guildProfile.markModified("guildFbx") | ||||
| 					await guildProfile.save().catch(console.error) | ||||
|  | ||||
| 					return interaction.reply({ content: t(interaction.locale, "freebox.timer.disabled"), flags: MessageFlags.Ephemeral }) | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										17
									
								
								src/commands/global/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								src/commands/global/index.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | ||||
| import * as amp from "./amp" | ||||
| import * as boost from "./boost" | ||||
| import * as database from "./database" | ||||
| import * as freebox from "./freebox" | ||||
| import * as ping from "./ping" | ||||
| import * as twitch from "./twitch" | ||||
|  | ||||
| import type { Command } from "@/types" | ||||
|  | ||||
| export default [ | ||||
| 	amp, | ||||
| 	boost, | ||||
| 	database, | ||||
| 	freebox, | ||||
| 	ping, | ||||
| 	twitch | ||||
| ] as Command[] | ||||
| @@ -1,11 +1,17 @@ | ||||
| import { SlashCommandBuilder, ChatInputCommandInteraction } from 'discord.js' | ||||
|  | ||||
| export default { | ||||
| 	data: new SlashCommandBuilder() | ||||
| 		.setName('ping') | ||||
| 		.setDescription('Check the latency of the bot'), | ||||
| 	async execute(interaction: ChatInputCommandInteraction) { | ||||
| 		let sent = await interaction.reply({ content: 'Pinging...', fetchReply: true }) | ||||
| 		interaction.editReply(`Websocket heartbeat: ${interaction.client.ws.ping}ms.\nRoundtrip latency: ${sent.createdTimestamp - interaction.createdTimestamp}ms`) | ||||
| 	} | ||||
| } | ||||
| import { SlashCommandBuilder } from "discord.js" | ||||
| import type { ChatInputCommandInteraction } from "discord.js" | ||||
| import { t } from "@/utils/i18n" | ||||
|  | ||||
| export const data = new SlashCommandBuilder() | ||||
| 	.setName("ping") | ||||
| 	.setDescription("Check the latency of the bot") | ||||
| 	.setDescriptionLocalizations({ fr: "Vérifier la latence du bot" }) | ||||
|  | ||||
| export async function execute(interaction: ChatInputCommandInteraction) { | ||||
| 	await interaction.reply({ content: t(interaction.locale, "ping.pinging") }) | ||||
| 	const sent = await interaction.fetchReply() | ||||
| 	return interaction.editReply(t(interaction.locale, "ping.response", {  | ||||
| 		heartbeat: interaction.client.ws.ping.toString(), | ||||
| 		latency: (sent.createdTimestamp - interaction.createdTimestamp).toString() | ||||
| 	})) | ||||
| } | ||||
|   | ||||
							
								
								
									
										198
									
								
								src/commands/global/twitch.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										198
									
								
								src/commands/global/twitch.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,198 @@ | ||||
| import { SlashCommandBuilder, ChannelType, MessageFlags, PermissionFlagsBits } from "discord.js" | ||||
| import type { ChatInputCommandInteraction, AutocompleteInteraction, ApplicationCommandOptionChoiceData } from "discord.js" | ||||
| import chalk from "chalk" | ||||
| import { twitchClient, listener, onlineSub, offlineSub, generateTwitchEmbed } from "@/utils/twitch" | ||||
| import type { GuildTwitch } from "@/types/schemas" | ||||
| import dbGuild from "@/schemas/guild" | ||||
| import { t } from "@/utils/i18n" | ||||
|  | ||||
| export const data = new SlashCommandBuilder() | ||||
| 	.setName("twitch") | ||||
| 	.setDescription("Manage streamers notifications") | ||||
| 	.setDescriptionLocalizations({ fr: "Gérer les notifications des streameurs" }) | ||||
| 	.setDefaultMemberPermissions(PermissionFlagsBits.ManageGuild) | ||||
| 	.addSubcommand(subcommand => subcommand | ||||
| 		.setName("status") | ||||
| 		.setDescription("Display Twitch module status") | ||||
| 		.setNameLocalizations({ fr: "statut" }) | ||||
| 		.setDescriptionLocalizations({ fr: "Afficher le statut du module Twitch" }) | ||||
| 	) | ||||
| 	.addSubcommand(subcommand => subcommand | ||||
| 		.setName("channel") | ||||
| 		.setDescription("Set the channel to send notifications") | ||||
| 		.setNameLocalizations({ fr: "canal" }) | ||||
| 		.setDescriptionLocalizations({ fr: "Définir le canal pour envoyer les notifications" }) | ||||
| 		.addChannelOption(option => option | ||||
| 			.setName("channel") | ||||
| 			.setDescription("The channel to send notifications") | ||||
| 			.setNameLocalizations({ fr: "canal" }) | ||||
| 			.setDescriptionLocalizations({ fr: "Le canal pour envoyer les notifications" }) | ||||
| 			.setRequired(true) | ||||
| 		) | ||||
| 	) | ||||
| 	.addSubcommandGroup(subcommandgroup => subcommandgroup | ||||
| 		.setName("streamer") | ||||
| 		.setDescription("Manage streamers") | ||||
| 		.setNameLocalizations({ fr: "streameur" }) | ||||
| 		.setDescriptionLocalizations({ fr: "Gérer les streameurs" }) | ||||
| 		.addSubcommand(subcommand => subcommand | ||||
| 			.setName("list") | ||||
| 			.setDescription("List all streamers") | ||||
| 			.setNameLocalizations({ fr: "liste" }) | ||||
| 			.setDescriptionLocalizations({ fr: "Lister tous les streameurs" }) | ||||
| 		) | ||||
| 		.addSubcommand(subcommand => subcommand | ||||
| 			.setName("add") | ||||
| 			.setDescription("Add a streamer") | ||||
| 			.setNameLocalizations({ fr: "ajouter" }) | ||||
| 			.setDescriptionLocalizations({ fr: "Ajouter un streameur" }) | ||||
| 			.addStringOption(option => option | ||||
| 				.setName("username") | ||||
| 				.setDescription("The username of the streamer to add") | ||||
| 				.setNameLocalizations({ fr: "nom_utilisateur" }) | ||||
| 				.setDescriptionLocalizations({ fr: "Le nom d'utilisateur du streameur à ajouter" }) | ||||
| 				.setRequired(true) | ||||
| 				.setAutocomplete(true) | ||||
| 			) | ||||
| 			.addUserOption(option => option | ||||
| 				.setName("member") | ||||
| 				.setDescription("The member on the guild to mention") | ||||
| 				.setNameLocalizations({ fr: "membre" }) | ||||
| 				.setDescriptionLocalizations({ fr: "Le membre sur le serveur à mentionner" }) | ||||
| 				.setRequired(false) | ||||
| 			) | ||||
| 		) | ||||
| 		.addSubcommand(subcommand => subcommand | ||||
| 			.setName("remove") | ||||
| 			.setDescription("Remove a streamer") | ||||
| 			.setNameLocalizations({ fr: "supprimer" }) | ||||
| 			.setDescriptionLocalizations({ fr: "Supprimer un streameur" }) | ||||
| 			.addStringOption(option => option | ||||
| 				.setName("username") | ||||
| 				.setDescription("The username of the streamer to remove") | ||||
| 				.setNameLocalizations({ fr: "nom_utilisateur" }) | ||||
| 				.setDescriptionLocalizations({ fr: "Le nom d'utilisateur du streameur à supprimer" }) | ||||
| 				.setRequired(true) | ||||
| 				.setAutocomplete(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("guildTwitch") as GuildTwitch | ||||
|  | ||||
| 	const subcommandGroup = interaction.options.getSubcommandGroup(false) | ||||
| 	const subcommand = interaction.options.getSubcommand(true) | ||||
| 	if (subcommand == "status") { | ||||
| 		// Utiliser la fonction utilitaire pour générer l'embed et les composants | ||||
| 		const { embed, components } = generateTwitchEmbed(dbData, interaction.client, interaction.guild?.id ?? "", interaction.locale) | ||||
|  | ||||
| 		return interaction.reply({ embeds: [embed], components: components, flags: MessageFlags.Ephemeral }) | ||||
| 	} | ||||
|  | ||||
| 	if (!dbData.enabled) return interaction.reply({ content: t(interaction.locale, "twitch.module_disabled_activate"), flags: MessageFlags.Ephemeral }) | ||||
|  | ||||
| 	if (subcommand == "channel") { | ||||
| 		const channel = interaction.options.getChannel("channel", true) | ||||
| 		if (channel.type !== ChannelType.GuildText && channel.type !== ChannelType.GuildAnnouncement) | ||||
| 			return interaction.reply({ content: t(interaction.locale, "common.invalid_text_channel"), flags: MessageFlags.Ephemeral }) | ||||
|  | ||||
| 		dbData.channelId = channel.id | ||||
| 		guildProfile.set("guildTwitch", dbData) | ||||
| 		guildProfile.markModified("guildTwitch") | ||||
| 		await guildProfile.save().catch(console.error) | ||||
|  | ||||
| 		return interaction.reply({ content: t(interaction.locale, "twitch.notifications_channel_set", { channel: channel.name ?? channel.id }), flags: MessageFlags.Ephemeral }) | ||||
| 	} | ||||
| 	else if (subcommandGroup == "streamer") { | ||||
| 		if (!dbData.channelId) return interaction.reply({ content: t(interaction.locale, "twitch.configure_channel_first"), flags: MessageFlags.Ephemeral }) | ||||
|  | ||||
| 		if (subcommand == "list") { | ||||
| 			if (!dbData.streamers.length) return interaction.reply({ content: t(interaction.locale, "twitch.no_streamers_list"), flags: MessageFlags.Ephemeral }) | ||||
|  | ||||
| 			const streamers = [] as string[] | ||||
| 			await Promise.all(dbData.streamers.map(async streamer => { | ||||
| 				try { | ||||
| 					const user = await twitchClient.users.getUserById(streamer.twitchUserId) | ||||
| 					if (user) streamers.push(`- ${user.displayName} (${streamer.twitchUserId})`) | ||||
| 					else streamers.push(`- ${t(interaction.locale, "twitch.user_not_found_id", { id: streamer.twitchUserId })}`) | ||||
| 				} catch (error) { | ||||
| 					console.log(chalk.magenta(`[Twitch] Error fetching user for ID ${streamer.twitchUserId}`)) | ||||
| 					console.error(error) | ||||
| 				} | ||||
| 			})) | ||||
| 			const streamerList = streamers.length > 0 ? streamers.join("\n") : t(interaction.locale, "twitch.no_streamers") | ||||
|  | ||||
| 			return interaction.reply({ content: `${t(interaction.locale, "twitch.list.title")}:\n${streamerList}`, flags: MessageFlags.Ephemeral }) | ||||
| 		} | ||||
| 		else if (subcommand == "add") { | ||||
| 			const username = interaction.options.getString("username", true) | ||||
| 			const member = interaction.options.getUser("member", false) | ||||
|  | ||||
| 			const user = await twitchClient.users.getUserByName(username) | ||||
| 			if (!user) return interaction.reply({ content: t(interaction.locale, "twitch.streamer_not_found", { username }), flags: MessageFlags.Ephemeral }) | ||||
|  | ||||
| 			if (dbData.streamers.some(s => s.twitchUserId === user.id)) return interaction.reply({ content: t(interaction.locale, "twitch.streamer_already_added", { username }), flags: MessageFlags.Ephemeral }) | ||||
| 			dbData.streamers.push({ twitchUserId: user.id, discordUserId: member?.id ?? "", messageId: "" }) | ||||
|  | ||||
| 			guildProfile.set("guildTwitch", dbData) | ||||
| 			guildProfile.markModified("guildTwitch") | ||||
| 			await guildProfile.save().catch(console.error) | ||||
|  | ||||
| 			const userSubs = await twitchClient.eventSub.getSubscriptionsForUser(user.id) | ||||
| 			// eslint-disable-next-line @typescript-eslint/no-misused-promises | ||||
| 			if (!userSubs.data.find(sub => sub.transportMethod === "webhook" && sub.type === "stream.online")) listener.onStreamOnline(user.id, onlineSub) | ||||
| 			// eslint-disable-next-line @typescript-eslint/no-misused-promises | ||||
| 			if (!userSubs.data.find(sub => sub.transportMethod === "webhook" && sub.type === "stream.offline")) listener.onStreamOffline(user.id, offlineSub) | ||||
|  | ||||
| 			return interaction.reply({ content: t(interaction.locale, "twitch.streamer_added", { username, id: user.id }), flags: MessageFlags.Ephemeral }) | ||||
| 		} | ||||
| 		else if (subcommand == "remove") { | ||||
| 			const username = interaction.options.getString("username", true) | ||||
|  | ||||
| 			const user = await twitchClient.users.getUserByName(username) | ||||
| 			if (!user) return interaction.reply({ content: t(interaction.locale, "twitch.streamer_not_found", { username }), flags: MessageFlags.Ephemeral }) | ||||
|  | ||||
| 			const streamerIndex = dbData.streamers.findIndex(s => s.twitchUserId === user.id) | ||||
| 			if (streamerIndex === -1)return interaction.reply({ content: t(interaction.locale, "twitch.streamer_not_in_list", { username }), flags: MessageFlags.Ephemeral }) | ||||
|  | ||||
| 			dbData.streamers.splice(streamerIndex, 1) | ||||
| 			guildProfile.set("guildTwitch", dbData) | ||||
| 			guildProfile.markModified("guildTwitch") | ||||
| 			await guildProfile.save().catch(console.error) | ||||
|  | ||||
| 			if (!await dbGuild.exists({ "guildTwitch.streamers.twitchUserId": user.id })) { | ||||
| 				const userSubs = await twitchClient.eventSub.getSubscriptionsForUser(user.id) | ||||
| 				await Promise.all(userSubs.data.map(async sub => { if (sub.transportMethod === "webhook" && (sub.type === "stream.online" || sub.type === "stream.offline")) await sub.unsubscribe() })) | ||||
| 				console.log(chalk.magenta(`[Twitch] Listener removed for ${user.displayName} (ID ${user.id})`)) | ||||
| 			} | ||||
|  | ||||
| 			return interaction.reply({ content: t(interaction.locale, "twitch.streamer_removed", { username }), flags: MessageFlags.Ephemeral }) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| export async function autocompleteRun(interaction: AutocompleteInteraction) { | ||||
| 	const query = interaction.options.getString("username", true) | ||||
| 	if (!query || query.length < 3) return interaction.respond([]) | ||||
|  | ||||
| 	const guildProfile = await dbGuild.findOne({ guildId: interaction.guild?.id }) | ||||
| 	if (!guildProfile) return interaction.respond([]) | ||||
|  | ||||
| 	const dbData = guildProfile.get("guildTwitch") as GuildTwitch | ||||
| 	if (!dbData.enabled) return interaction.respond([]) | ||||
|  | ||||
| 	const choices: ApplicationCommandOptionChoiceData[] = [] | ||||
| 	const searchResult = await twitchClient.search.searchChannels(query) | ||||
| 	if (searchResult.data.length === 0) return interaction.respond([]) | ||||
|  | ||||
| 	searchResult.data.forEach(streamerResult => { | ||||
| 		if (dbData.streamers.some(s => s.twitchUserId === streamerResult.id)) return | ||||
| 		choices.push({ name: streamerResult.displayName, value: streamerResult.name }) | ||||
| 	}) | ||||
|  | ||||
| 	return interaction.respond(choices) | ||||
| } | ||||
		Reference in New Issue
	
	Block a user