diff --git a/IDs.ts b/IDs.ts new file mode 100644 index 0000000..74aae78 --- /dev/null +++ b/IDs.ts @@ -0,0 +1,10 @@ +export enum Serveur { + IUT_NC_DEP_INFO = "753171392450527282", + BETA_1 = "885296615089397790", + BETA_2 = "888005290333712384" +} + +export enum RoleBeta { + BETA_1 = "887875935175573545", + BETA_2 = "888005290333712385" +} \ No newline at end of file diff --git a/commands/Enseignant.ts b/commands/Enseignant.ts deleted file mode 100644 index f2e23ce..0000000 --- a/commands/Enseignant.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { CommandInteraction, VoiceChannel } from "discord.js"; -import { DefaultPermission, Discord, Guild, Permission, Slash, SlashOption } from "discordx"; -import { RoleIDs, ServerIDs } from "../enums/IDs"; - -@Discord() -@Guild(ServerIDs.MAIN) -@DefaultPermission(false) -@Permission({ id: RoleIDs.ADMIN, type: 'ROLE', permission: true }) -@Permission({ id: RoleIDs.STAR, type: 'ROLE', permission: true }) -@Permission({ id: RoleIDs.ENSEIGNANT, type: 'ROLE', permission: true }) -abstract class Enseignant { - - @Slash('groupevocal', { description: "Crée un ou plusieurs salons vocaux temporaire dans cette catégorie (1 salon pendant 60m par défaut)" }) - private async groupevocal( - @SlashOption('quantité', { description: "Nombre de salon à créer. (10 salon max)" }) - amount: number = 1, - @SlashOption('durée', { description: "Durée du salon avant sa suppression en minute. (240 minutes = 4 heures max)" }) - duration: number = 60, - interaction: CommandInteraction - ) { - if (amount <= 0) { interaction.reply({ content: "Désolé cependant vous ne pouvez créer une quantité négative ou nulle de salon.", ephemeral: true }); return; } - if (duration <= 0) { interaction.reply({ content: "Désolé cependant vous ne pouvez créer de salon pour une durée négative ou nulle.", ephemeral: true }); return; } - - if (amount > 10) { interaction.reply({ content: "Désolé cependant vous ne pouvez pas créer plus de 10 salons à la fois.", ephemeral: true }); return; } - if (duration > 240) { interaction.reply({ content: "Désolé cependant vous ne pouvez créer de salon durant plus de 4 heures.", ephemeral: true }); return; } - - if (interaction.channel?.type !== "GUILD_TEXT") { return; } - - const category = interaction.channel?.parent; - - if (!category) { interaction.reply({ content: "Désolé cependant ce salon n'a pas de catégorie.", ephemeral: true }); return; } - - const guild = interaction.guild; - - if (!guild) { interaction.reply({ content: `Une erreur est survenue (guild: ${guild})`, ephemeral: true }); return; } - - await interaction.deferReply(); - - let newSalonsPromise: Promise[] = []; - - for (let i = 0; i < amount; i++) { - newSalonsPromise.push(guild.channels.create(`💻・Groupe ${i + 1}`, { - type: "GUILD_VOICE", - parent: category, - reason: `Salon vocal de groupe temporaire créé par ${interaction.user.username}` - })); - } - - interaction.editReply({ content: "Votre demande est en cours de traitement..." }) - - const newChannels = await Promise.all(newSalonsPromise); - - newChannels.forEach(async salon => { - salon.permissionOverwrites.create(interaction.user, { 'MANAGE_CHANNELS': true }, { reason: "Permissions données au créateur du salon" }); - setTimeout((salon: VoiceChannel) => { - salon.delete(); - }, duration * 60000, salon); - }); - - interaction.editReply({ content: `Vos ${amount} salons ont été créé pour ${duration} minutes. \`${duration} minutes restantes\`` }); - - let minutesSpend = 0; - const intervalID = setInterval( - (interaction: CommandInteraction, minutesSpend: number) => { - interaction.editReply({ content: `Vos ${amount} salons ont été créé pour ${duration} minutes. \`${duration - minutesSpend} minutes restantes\`` }); - minutesSpend++ - }, 60000, interaction, ++minutesSpend); - - setTimeout((intervalID, interaction: CommandInteraction) => { clearInterval(intervalID); interaction.editReply({ content: `Temps écoulé ! Vos salons ont été supprimés.` }) }, duration * 60000, intervalID, interaction); - } -} \ No newline at end of file diff --git a/commands/Fun.ts b/commands/Fun.ts deleted file mode 100644 index aecbcb7..0000000 --- a/commands/Fun.ts +++ /dev/null @@ -1,147 +0,0 @@ -import { randomInt } from "crypto"; -import { ButtonInteraction, CommandInteraction, EmojiIdentifierResolvable, MessageActionRow, MessageButton, User } from "discord.js"; -import { ButtonComponent, DefaultPermission, Discord, Slash, SlashChoice, SlashGroup, SlashOption } from "discordx"; - -enum pfcChoix { - Pierre = "Pierre", - Feuille = "Feuille", - Ciseaux = "Ciseaux" -} - -enum pfcResultat { - WIN, - LOOSE, - DRAW -} - -class pfcProposition { - public static propositions = [ - new pfcProposition(pfcChoix.Pierre, '🪨', 'pfc-rock'), - new pfcProposition(pfcChoix.Feuille, '🧻', 'pfc-paper'), - new pfcProposition(pfcChoix.Ciseaux, '✂️', 'pfc-scissors') - ] - - public name: pfcChoix; - public emoji: EmojiIdentifierResolvable; - public buttonCustomID: "pfc-rock" | "pfc-paper" | "pfc-scissors"; - - constructor(nom: pfcChoix, emoji: EmojiIdentifierResolvable, buttonCustomID: "pfc-rock" | "pfc-paper" | "pfc-scissors") { - this.name = nom; - this.emoji = emoji; - this.buttonCustomID = buttonCustomID; - } - - public static nameToClass(nom: string) { - return this.propositions.find(proposition => nom === proposition.name); - } - - public static buttonCustomIDToClass(buttonCustomID: string) { - return this.propositions.find(proposition => buttonCustomID === proposition.buttonCustomID); - } -} - -@Discord() -@DefaultPermission(true) -@SlashGroup("fun", "Commandes orienté sur l'amusement") -abstract class Fun { - - @Slash("pfc", { description: "Quoi de plus amusant que jouer à Pierre Feuille Ciseaux avec un robot ?" }) - async pfc( - @SlashChoice(pfcChoix) - @SlashOption("Choix", { description: "Que voulez-vous jouer ?" }) - choice: pfcChoix, - interaction: CommandInteraction - ) { - - if (choice) { - await interaction.deferReply({ ephemeral: true }); - - const playerChoice = pfcProposition.nameToClass(choice); - - if (!playerChoice) { interaction.editReply({ content: `Erreur, ${choice} est un choix invalide.` }); return; } - - const botChoice = Fun.pfcPlayBot(); - const result = Fun.isWin(playerChoice, botChoice); - - interaction.editReply(Fun.pfcTraitementResultat(playerChoice, botChoice, result, interaction.user)); - } else { - await interaction.deferReply(); - - const rockButton = new MessageButton() - .setLabel("Pierre") - .setEmoji('🪨') - .setStyle('PRIMARY') - .setCustomId('pfc-rock'); - - const paperButton = new MessageButton() - .setLabel("Feuille") - .setEmoji('🧻') - .setStyle('PRIMARY') - .setCustomId('pfc-paper'); - - const scissorButton = new MessageButton() - .setLabel("Ciseaux") - .setEmoji('✂️') - .setStyle('PRIMARY') - .setCustomId('pfc-scissors'); - - const wellButton = new MessageButton() - .setLabel("Puit") - .setEmoji('❓') - .setStyle('PRIMARY') - .setCustomId('pfc-well') - .setDisabled(true); - - const buttonRow = new MessageActionRow() - .addComponents(rockButton, paperButton, scissorButton, wellButton); - - interaction.editReply({ content: "Ok let's go. 1v1 Pierre Feuille Ciseaux. Vas-y choisis !", components: [buttonRow] }); - } - } - - @ButtonComponent('pfc-rock') - @ButtonComponent('pfc-paper') - @ButtonComponent('pfc-scissors') - private async pfcButton(interaction: ButtonInteraction) { - const playerChoice = pfcProposition.buttonCustomIDToClass(interaction.customId); - - if (!playerChoice) { interaction.reply({ content: `Erreur, ${interaction.customId} est un choix invalide.`, ephemeral: true }); return; } - - const botChoice = Fun.pfcPlayBot(); - const result = Fun.isWin(playerChoice, botChoice); - - interaction.update(Fun.pfcTraitementResultat(playerChoice, botChoice, result, interaction.user)); - } - - private static isWin(playerChoice: pfcProposition, botChoice: pfcProposition): pfcResultat { - switch (playerChoice.name) { - case pfcChoix.Pierre: - if (botChoice.name === pfcChoix.Ciseaux) return pfcResultat.WIN; - if (botChoice.name === pfcChoix.Feuille) return pfcResultat.LOOSE; - return pfcResultat.DRAW; - case pfcChoix.Feuille: - if (botChoice.name === pfcChoix.Pierre) return pfcResultat.WIN; - if (botChoice.name === pfcChoix.Ciseaux) return pfcResultat.LOOSE; - return pfcResultat.DRAW; - case pfcChoix.Ciseaux: - if (botChoice.name === pfcChoix.Feuille) return pfcResultat.WIN; - if (botChoice.name === pfcChoix.Pierre) return pfcResultat.LOOSE; - return pfcResultat.DRAW; - } - } - - private static pfcPlayBot(): pfcProposition { - return pfcProposition.propositions[randomInt(pfcProposition.propositions.length)]; - } - - private static pfcTraitementResultat(choix: pfcProposition, choixBot: pfcProposition, resultat: pfcResultat, player: User) { - switch (resultat) { - case pfcResultat.WIN: - return { content: `(${player}) ${choixBot.emoji} ${choixBot.name} ! Well, noob ${choix.emoji} ${choix.name} need nerf plz...` }; - case pfcResultat.LOOSE: - return { content: `(${player}) ${choixBot.emoji} ${choixBot.name} ! GG NO RE, EZ !` }; - case pfcResultat.DRAW: - return { content: `(${player}) ${choixBot.emoji} ${choixBot.name} ! Ha... Égalité...` }; - } - } -} \ No newline at end of file diff --git a/commands/Info.ts b/commands/Info.ts deleted file mode 100644 index 2e373f5..0000000 --- a/commands/Info.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { CommandInteraction, MessageEmbed } from 'discord.js'; -import { Discord, Slash } from 'discordx'; -import { RoleIDs } from '../enums/IDs'; - -@Discord() -abstract class Info { - - @Slash('info', { description: "Affiche les informations du serveur" }) - private async info(interaction: CommandInteraction) { - if (interaction.channel?.type === "DM") { interaction.reply({ content: "❌ Désolé mais je ne peux pas effectuer cette commande en message privé.", ephemeral: true }) } - - await interaction.deferReply(); - - const guild = interaction.guild; - - const resEmbed = new MessageEmbed() - .setColor('#DD131E') - .setTitle('Informations du Serveur') - .addFields( - { - name: 'Membres', - value: ` - <@&${RoleIDs.ADMIN}> : \`${guild?.roles.resolve(RoleIDs.ADMIN)?.members.size}\` - <@&${RoleIDs.SERVER_BOOSTER}> : \`${guild?.roles.resolve(RoleIDs.SERVER_BOOSTER)?.members.size}\` - <@&${RoleIDs.ENSEIGNANT}> : \`${guild?.roles.resolve(RoleIDs.ENSEIGNANT)?.members.size}\` - <@&${RoleIDs.DÉLÉGUÉ}> : \`${guild?.roles.resolve(RoleIDs.DÉLÉGUÉ)?.members.size}\` - Ancien Étudiant : \`${(guild?.roles.resolve(RoleIDs.ANCIEN_DUT)?.members.size || 0) + (guild?.roles.resolve(RoleIDs.ANCIEN_LP_CIASIE)?.members.size || 0)}\` - <@&${RoleIDs.ÉTUDIANT}> : \`${guild?.roles.resolve(RoleIDs.ÉTUDIANT)?.members.size}\` - <@&${RoleIDs.MEMBRE_UL}> : \`${guild?.roles.resolve(RoleIDs.MEMBRE_UL)?.members.size}\` - <@&${RoleIDs.COMPTE_SECONDAIRE}> : \`${guild?.roles.resolve(RoleIDs.COMPTE_SECONDAIRE)?.members.size}\` - Total : \`${guild?.memberCount}\` - `, - inline: true - }, - { - name: 'Promos', - value: ` - <@&${RoleIDs.LP_ACORS}> : \`${guild?.roles.resolve(RoleIDs.LP_ACORS)?.members.size}\` - <@&${RoleIDs.LP_AFTER}> : \`${guild?.roles.resolve(RoleIDs.LP_AFTER)?.members.size}\` - <@&${RoleIDs.LP_CIASIE}> : \`${guild?.roles.resolve(RoleIDs.LP_CIASIE)?.members.size}\` - <@&${RoleIDs.BUT_1A}> : \`${guild?.roles.resolve(RoleIDs.BUT_1A)?.members.size}\` - <@&${RoleIDs.DUT_2A}> : \`${guild?.roles.resolve(RoleIDs.DUT_2A)?.members.size}\` - `, - inline: true - }, - { name: 'Nombre de salons', value: `${guild?.channels.cache.size}/500` } - ) - .setTimestamp(); - - interaction.editReply({ embeds: [resEmbed] }) - } -} \ No newline at end of file diff --git a/commands/Maintenance.ts b/commands/Maintenance.ts deleted file mode 100644 index 1bbb929..0000000 --- a/commands/Maintenance.ts +++ /dev/null @@ -1,222 +0,0 @@ -import { AwaitMessagesOptions, CategoryChannel, Channel, Collection, CommandInteraction, DMChannel, GuildChannel, Message, MessageActionRow, MessageButton, MessageEmbed, Snowflake } from "discord.js"; -import { DefaultPermission, Discord, Guild, Permission, Slash, SlashGroup, SlashOption } from "discordx"; -import { RoleIDs, ServerIDs } from "../enums/IDs"; - -@Discord() -@Guild(ServerIDs.MAIN) -@DefaultPermission(false) -@Permission({ id: RoleIDs.ADMIN, type: 'ROLE', permission: true }) -@Permission({ id: RoleIDs.STAR, type: 'ROLE', permission: true }) -@SlashGroup("maintenance", "Commandes de maintenance du serveur") -abstract class Maintenance { - - @Slash("setupCategorieScolaire", { description: "Créé les salons basiques communs des catégories scolaires" }) - async setupCategorieScolaire( - @SlashOption("categorie", { description: "ID de la catégorie à affecter", required: true, type: "CHANNEL" }) - categoryParam: Channel, - @SlashOption("createAnnonces", { description: "Voulez-vous créer le salon '📢・annonces' ?" }) - createAnnonces: boolean = false, - @SlashOption("createDocuments", { description: "Voulez-vous créer le salon '📚・documents' ?" }) - createDocuments: boolean = false, - @SlashOption("createOffreDeStage", { description: "Voulez-vous créer le salon '📬・offres-de-stage' ?" }) - createOffreDeStage: boolean = false, - interaction: CommandInteraction - ) { - await interaction.deferReply({ ephemeral: true }); - - if (interaction.channel?.type === "DM") { interaction.editReply({ content: "❌ Désolé mais je ne peux pas effectuer cette commande en message privé." }); return; } - - if (categoryParam?.type !== "GUILD_CATEGORY") { interaction.editReply({ content: "❌ Cela ne s'agit pas d'une catégorie." }); return; } - - interaction.editReply({ content: `Salons en cours de création...` }); - - const guild = interaction.guild; - const category = categoryParam; - - if (createAnnonces) { - const annoncementChannel = await guild?.channels.create("📢・annonces", { - type: "GUILD_TEXT", - topic: "Annonces scolaires concernant la classe ou la promo", - parent: category, - reason: `Création du salon demandé par ${interaction.user.username} via la commande 'setupCategorieScolaire'` - }); - - await annoncementChannel?.permissionOverwrites.edit(RoleIDs.EVERYONE, { "SEND_MESSAGES": false }); - await annoncementChannel?.permissionOverwrites.edit(RoleIDs.DÉLÉGUÉ, { "SEND_MESSAGES": true }); - await annoncementChannel?.permissionOverwrites.edit(RoleIDs.ENSEIGNANT, { "SEND_MESSAGES": true }); - } - - if (createDocuments) { - const documentChannel = await guild?.channels.create("📚・documents", { - type: "GUILD_TEXT", - topic: "Documents scolaires concernant la classe ou la promo", - parent: category, - reason: `Création du salon demandé par ${interaction.user.username} via la commande 'setupCategorieScolaire'` - }); - - await documentChannel?.permissionOverwrites.edit(RoleIDs.EVERYONE, { "SEND_MESSAGES": false }); - await documentChannel?.permissionOverwrites.edit(RoleIDs.DÉLÉGUÉ, { "SEND_MESSAGES": true }); - await documentChannel?.permissionOverwrites.edit(RoleIDs.ENSEIGNANT, { "SEND_MESSAGES": true }); - } - - if (createOffreDeStage) { - const offreDeStageChannel = await guild?.channels.create("📬・offres-de-stage", { - type: "GUILD_TEXT", - topic: "Offre de stages trouvées par des enseignants ou publiés par des professionnels", - parent: category, - reason: `Création du salon demandé par ${interaction.user.username} via la commande 'setupCategorieScolaire'` - }); - - await offreDeStageChannel?.permissionOverwrites.edit(RoleIDs.EVERYONE, { "SEND_MESSAGES": false }); - await offreDeStageChannel?.permissionOverwrites.edit(RoleIDs.DÉLÉGUÉ, { "SEND_MESSAGES": true }); - await offreDeStageChannel?.permissionOverwrites.edit(RoleIDs.ENSEIGNANT, { "SEND_MESSAGES": true }); - } - - const discussionsChannel = await guild?.channels.create("💬・discussions", { - type: "GUILD_TEXT", - topic: "Salon de discussions de la classe ou de la promo. N'hésitez pas à démarrer des fils de discussions s'il s'agit d'un sujet spécifique comme un cours en particulier !", - parent: category, - reason: `Création du salon demandé par ${interaction.user.username} via la commande 'setupCategorieScolaire'` - }); - - discussionsChannel?.edit({ - defaultAutoArchiveDuration: - (guild?.features.includes("SEVEN_DAY_THREAD_ARCHIVE") ? 10080 : - (guild?.features.includes("THREE_DAY_THREAD_ARCHIVE") ? 4320 : - 1440) - ) - }); - - const amphiChannel = await guild?.channels.create("🎤・Amphithéâtre", { - type: "GUILD_STAGE_VOICE", - topic: "Conférence réservé à la classe ou à la promo. Les partages d'écrans ne sont pour l'instant pas disponibles", - parent: category, - reason: `Création du salon demandé par ${interaction.user.username} via la commande 'setupCategorieScolaire'` - }); - - await amphiChannel?.permissionOverwrites.edit(RoleIDs.ENSEIGNANT, { "MANAGE_CHANNELS": true, "MUTE_MEMBERS": true, "MOVE_MEMBERS": true }); - await amphiChannel?.permissionOverwrites.edit(RoleIDs.DÉLÉGUÉ, { "MANAGE_CHANNELS": true, "MUTE_MEMBERS": true, "MOVE_MEMBERS": true }); - - await guild?.channels.create("💻・Vocal #1", { - type: "GUILD_VOICE", - topic: "Salon vocal réservé à la classe ou à la promo", - parent: category, - reason: `Création du salon demandé par ${interaction.user.username} via la commande 'setupCategorieScolaire'` - }); - - await guild?.channels.create("💻・Vocal #2", { - type: "GUILD_VOICE", - topic: "Salon vocal réservé à la classe ou à la promo", - parent: category, - reason: `Création du salon demandé par ${interaction.user.username} via la commande 'setupCategorieScolaire'` - }); - - interaction.editReply({ content: `Les salons ont été créés !` }); - } - - @Slash('deleteMessages', { description: "Supprime les derniers messages envoyés il y a moins de 2 semaines. Supprime 100 messages par défaut" }) - async deleteMessages( - @SlashOption('nombre', { description: "Nombre de message à effacer" }) - amount: number, - @SlashOption('jours', { description: "Ancienneté des messages à supprimer en jours" }) - days: number = 0, - @SlashOption('heures', { description: "Ancienneté des messages à supprimer en heures" }) - hours: number = 0, - @SlashOption('minutes', { description: "Ancienneté des messages à supprimer en minutes" }) - minutes: number = 0, - interaction: CommandInteraction - ) { - await interaction.deferReply({ ephemeral: true }) - - const channel = interaction.channel; - - if (!channel) { interaction.editReply({ content: "Erreur, channel: " + channel }); return; } - - if (channel?.type === "DM") { interaction.editReply({ content: "❌ Désolé mais je ne peux pas effectuer cette commande en message privé." }); return; } - - let messageList: Collection = new Collection(); - - if (days || hours || minutes) { - const time: number = days * 8.64e+7 + hours * 3.6e+6 + minutes * 60000; - - let Options: AwaitMessagesOptions = { - time - } - - if (amount) - Options.max = amount - - const maxTimestamp = new Date(Date.now() - time); - - messageList = (await channel.messages.fetch()).filter(message => message.createdAt >= maxTimestamp); - } - - const deletedMessages = await channel.bulkDelete((messageList || amount || 100), true); - const notDeletedMessageAmount = (messageList?.size || amount || 100) - deletedMessages.size; - - let replyMessage: string = `${deletedMessages.size} ${deletedMessages.size > 1 ? "messages ont été supprimés" : "message a été supprimé"}.` - - if (notDeletedMessageAmount !== 0) - replyMessage += ` ${notDeletedMessageAmount} ${notDeletedMessageAmount > 1 ? "messages n'ont pas pu être supprimés" : "message n'a pas pu être supprimé"}. Cela est peut être dû à la limitation de 2 semaines de Discord (Ou qu'il n'y avait pas autant de message dans le salon). Pour supprimer tout un salon vous pouvez utiliser \`/purgechannel\`.`; - - interaction.reply({ content: replyMessage }) - } - - @Slash('purgeChannel', { description: "Clone et supprime le salon afin de supprimer son contenu" }) - async purgeChannel( - @SlashOption("channel", { description: "Salon à purger. Salon actuel par défaut", type: "CHANNEL", required: true }) - channel: GuildChannel | DMChannel, - interaction: CommandInteraction - ) { - await interaction.deferReply({ ephemeral: true }); - - if (channel.isThread()) { interaction.editReply({ content: "❌ Désolé mais je ne peux pas effectuer cette commande sur un Fil" }); return; } - if (channel.type === "DM") { interaction.editReply({ content: "❌ Désolé mais je ne peux pas effectuer cette commande en message privé sans indiquer un salon précis." }); return; } - - const newChannel = await channel.clone({ reason: `Purge du salon demandé par ${interaction.user.username}` }); - await channel.delete(); - - if (channel !== interaction.channel) - interaction.editReply({ content: `Salon purgé ! ${newChannel}` }); - } - - @Slash('webhook', { description: "Retourne le lien d'un webhook créé par le bot" }) - async webhook( - @SlashOption("Salon", { type: "CHANNEL", required: true }) - channel: GuildChannel | DMChannel, - interaction: CommandInteraction - ) { - await interaction.deferReply({ ephemeral: true }); - - if (channel.isThread()) { interaction.editReply({ content: "❌ Désolé mais je ne peux pas effectuer cette commande sur un Fil" }); return; } - if (channel.type === "DM") { interaction.editReply({ content: "❌ Désolé mais je ne peux pas effectuer cette commande en message privé sans indiquer un salon précis." }); return; } - if (!channel.isText()) { interaction.editReply({ content: "❌ Désolé mais je ne peux pas effectuer cette commande sur un salon non-textuel." }); return; } - - const webhooks = await channel.fetchWebhooks(); - - let webhook = webhooks.find(webhook => { - if (webhook.owner === null || interaction.client === null || interaction.client.user === null) return false; - if (!webhook.owner.bot) return false; - if (webhook.owner.id !== interaction.client.user.id) return false; - return true; - }); - - if (!webhook) { - webhook = await channel.createWebhook("Embeds", { reason: `Création du webhook demandé par ${interaction.user.username}` }); - } - - const embed = new MessageEmbed() - .setTitle("Votre webhook") - .setDescription(`\`${webhook.url}\``) - .setColor('#0080ff'); - - const row = new MessageActionRow() - .addComponents(new MessageButton() - .setStyle("LINK") - .setLabel("Ouvrir avec Discord.club (SOON)") - .setURL(`https://discord.club/dashboard?`) - .setDisabled(true)); - - interaction.editReply({ embeds: [embed], components: [row] }) - } -} \ No newline at end of file diff --git a/commands/Role.ts b/commands/Role.ts deleted file mode 100644 index 12c3787..0000000 --- a/commands/Role.ts +++ /dev/null @@ -1,426 +0,0 @@ -import { CommandInteraction, Guild, MessageActionRow, MessageSelectMenu, SelectMenuInteraction, User } from "discord.js"; -import { DefaultPermission, Discord, Guild as Guildx, Permission, SelectMenuComponent, Slash } from "discordx"; -import { ChannelIDs, RoleIDs, ServerIDs } from "../enums/IDs"; - -@Discord() -@Guildx(ServerIDs.MAIN) // Alias @Guild due to import name conflict -@DefaultPermission(false) -@Permission({ id: RoleIDs.ADMIN, type: 'ROLE', permission: true }) -@Permission({ id: RoleIDs.STAR, type: 'ROLE', permission: true }) -abstract class Role { - - @Slash('messageroles', { description: "Envoie le message permettant d'obtenir les rôles" }) - async messageroles(interaction: CommandInteraction) { - await interaction.deferReply({ ephemeral: true }); - - const channel = interaction.channel; - - if (!channel) { interaction.editReply({ content: "Erreur, channel: " + channel }); return; } - - if (!channel.isText()) interaction.reply("Type de salon inattendu."); - - const licensesProRow = new MessageActionRow() - .addComponents( - new MessageSelectMenu() - .setCustomId('role-lp') - .setPlaceholder("Licences Professionnelles") - .addOptions([ - { - label: "Licenses Pro. ACORS", - value: "lp-ACORS", - description: "Vous êtes en Licences Professionnelle ACORS ?" - }, - { - label: "Licenses Pro. AFTER", - value: "lp-AFTER", - description: "Vous êtes en Licences Professionnelle AFTER ?" - }, - { - label: "Licenses Pro. CIASIE 1", - value: "lp-CIASIE-1", - description: "Vous êtes en Licences Professionnelle CIASIE (Groupe 1) ?" - }, - { - label: "Licenses Pro. CIASIE 2", - value: "lp-CIASIE-2", - description: "Vous êtes en Licences Professionnelle CIASIE (Groupe 2) ?" - }])); - - const BUT1ARow = new MessageActionRow() - .addComponents( - new MessageSelectMenu() - .setCustomId('role-but-1a') - .setPlaceholder("BUT 1ère Année") - .addOptions([ - { - label: "BUT 1ère Année A", - value: "but-1A-a", - description: "Vous êtes en 1ère Année de BUT (Groupe A)?" - }, - { - label: "BUT 1ère Année B", - value: "but-1A-b", - description: "Vous êtes en 1ère Année de BUT (Groupe B)?" - }, - { - label: "BUT 1ère Année C", - value: "but-1A-c", - description: "Vous êtes en 1ère Année de BUT (Groupe C)?" - }, - { - label: "BUT 1ère Année D", - value: "but-1A-d", - description: "Vous êtes en 1ère Année de BUT (Groupe D)?" - }, - { - label: "BUT 1ère Année E", - value: "but-1A-e", - description: "Vous êtes en 1ère Année de BUT (Groupe E)?" - } - ]) - ); - - const DUT2ARow = new MessageActionRow() - .addComponents( - new MessageSelectMenu() - .setCustomId('role-dut-2a') - .setPlaceholder("DUT 2ème Année") - .addOptions([ - { - label: "DUT 2ème Année A", - value: "dut-2A-a", - description: "Vous êtes en 2ème Année de DUT (Groupe A)?" - }, - { - label: "DUT 2ème Année B", - value: "dut-2A-b", - description: "Vous êtes en 2ème Année de DUT (Groupe B)?" - }, - { - label: "DUT 2ème Année C", - value: "dut-2A-c", - description: "Vous êtes en 2ème Année de DUT (Groupe C)?" - }, - { - label: "DUT 2ème Année D", - value: "dut-2A-d", - description: "Vous êtes en 2ème Année de DUT (Groupe D)?" - } - ]) - ); - - const NotStudentRow = new MessageActionRow() - .addComponents( - new MessageSelectMenu() - .setCustomId('role-other') - .setPlaceholder("Non-étudiant du département") - .addOptions([ - { - label: "Enseignant du département", - value: "teacher", - description: "Vous êtes enseignant du Département Informatique ?" - }, - { - label: "Membre de l'Université de Lorraine", - value: "membreUL", - description: "Vous êtes étudiant / enseignant / autre chose au sein de l'Université de Lorraine ?" - } - ]) - ); - - const OldStudentRow = new MessageActionRow() - .addComponents( - new MessageSelectMenu() - .setCustomId('role-old-student') - .setPlaceholder("Ancien étudiant") - .addOptions([ - /*{ - label: "Ancien LP ACORS", - value: "old-lp-ACORS", - description: "Vous êtes un ancien de la LP ACORS ?" - }, - { - label: "Ancien LP AFTER", - value: "old-lp-AFTER", - description: "Vous êtes un ancien de la LP AFTER ?" - },*/ - { - label: "Ancien LP CIASIE", - value: "old-lp-CIASIE", - description: "Vous êtes un ancien de la LP CIASIE ?" - }, - { - label: "Ancien DUT", - value: "old-DUT", - description: "Vous êtes un ancien du DUT ?" - } - ]) - ); - - try { - await channel.send({ content: "Bienvenue sur le Serveur du Département Informatique de l'IUT Nancy-Charlemagne ! Veuillez sélectionner ce qui vous correspondant via le menu juste en dessous.", components: [licensesProRow, BUT1ARow, DUT2ARow, NotStudentRow, OldStudentRow] }); - } catch (err) { - interaction.editReply({ content: "Une erreur est survenue." }); - console.log(err) - } finally { - interaction.editReply({ content: "Done." }); - } - } - - @SelectMenuComponent('role-lp') - async selectMenuLP(interaction: SelectMenuInteraction) { - await interaction.deferReply({ ephemeral: true }); - - const roleValue = interaction.values?.[0]; - - if (!roleValue) { interaction.editReply({ content: `Erreur, value = ${roleValue}` }); return; } - - const user = interaction.user; - - const guild = interaction.guild; - - if (!guild) { interaction.editReply({ content: `Erreur, value = ${guild}` }); return; } - - switch (roleValue) { - case 'lp-ACORS': - await this.assignRole(guild, user, RoleIDs.LP_ACORS, interaction); - await this.assignRole(guild, user, RoleIDs.ÉTUDIANT, interaction); - break; - case 'lp-AFTER': - await this.assignRole(guild, user, RoleIDs.LP_AFTER, interaction); - await this.assignRole(guild, user, RoleIDs.ÉTUDIANT, interaction); - break; - case 'lp-CIASIE-1': - await this.assignRole(guild, user, RoleIDs.LP_CIASIE, interaction); - await this.assignRole(guild, user, RoleIDs.LP_CIASIE_1, interaction); - await this.assignRole(guild, user, RoleIDs.ÉTUDIANT, interaction); - break; - case 'lp-CIASIE-2': - await this.assignRole(guild, user, RoleIDs.LP_CIASIE, interaction); - await this.assignRole(guild, user, RoleIDs.LP_CIASIE_2, interaction); - await this.assignRole(guild, user, RoleIDs.ÉTUDIANT, interaction); - break; - default: - await interaction.followUp({ content: `Erreur, value = ${roleValue}`, ephemeral: true }); - } - } - - @SelectMenuComponent('role-but-1a') - async selectMenuBUT1A(interaction: SelectMenuInteraction) { - await interaction.deferReply({ ephemeral: true }); - - const roleValue = interaction.values?.[0]; - - if (!roleValue) { interaction.editReply({ content: `Erreur, value = ${roleValue}` }); return; } - - const user = interaction.user; - - const guild = interaction.guild; - - if (!guild) { interaction.editReply({ content: `Erreur, value = ${guild}` }); return; } - - switch (roleValue) { - case 'but-1A-a': - await this.assignRole(guild, user, RoleIDs.BUT_1A, interaction); - await this.assignRole(guild, user, RoleIDs.BUT_1A_A, interaction); - await this.assignRole(guild, user, RoleIDs.ÉTUDIANT, interaction); - break; - case 'but-1A-b': - await this.assignRole(guild, user, RoleIDs.BUT_1A, interaction); - await this.assignRole(guild, user, RoleIDs.BUT_1A_B, interaction); - await this.assignRole(guild, user, RoleIDs.ÉTUDIANT, interaction); - break; - case 'but-1A-c': - await this.assignRole(guild, user, RoleIDs.BUT_1A, interaction); - await this.assignRole(guild, user, RoleIDs.BUT_1A_C, interaction); - await this.assignRole(guild, user, RoleIDs.ÉTUDIANT, interaction); - break; - case 'but-1A-d': - await this.assignRole(guild, user, RoleIDs.BUT_1A, interaction); - await this.assignRole(guild, user, RoleIDs.BUT_1A_D, interaction); - await this.assignRole(guild, user, RoleIDs.ÉTUDIANT, interaction); - break; - case 'but-1A-e': - await this.assignRole(guild, user, RoleIDs.BUT_1A, interaction); - await this.assignRole(guild, user, RoleIDs.BUT_1A_E, interaction); - await this.assignRole(guild, user, RoleIDs.ÉTUDIANT, interaction); - break; - default: - await interaction.followUp({ content: `Erreur, value = ${roleValue}`, ephemeral: true }); - } - } - - @SelectMenuComponent('role-dut-2a') - async selectMenuDUT2A(interaction: SelectMenuInteraction) { - await interaction.deferReply({ ephemeral: true }); - - const roleValue = interaction.values?.[0]; - - if (!roleValue) { interaction.editReply({ content: `Erreur, value = ${roleValue}` }); return; } - - const user = interaction.user; - - const guild = interaction.guild; - - if (!guild) { interaction.editReply({ content: `Erreur, value = ${guild}` }); return; } - - switch (roleValue) { - case 'dut-2A-a': - await this.assignRole(guild, user, RoleIDs.DUT_2A, interaction); - await this.assignRole(guild, user, RoleIDs.DUT_2A_A, interaction); - await this.assignRole(guild, user, RoleIDs.ÉTUDIANT, interaction); - break; - case 'dut-2A-b': - await this.assignRole(guild, user, RoleIDs.DUT_2A, interaction); - await this.assignRole(guild, user, RoleIDs.DUT_2A_B, interaction); - await this.assignRole(guild, user, RoleIDs.ÉTUDIANT, interaction); - break; - case 'dut-2A-c': - await this.assignRole(guild, user, RoleIDs.DUT_2A, interaction); - await this.assignRole(guild, user, RoleIDs.DUT_2A_C, interaction); - await this.assignRole(guild, user, RoleIDs.ÉTUDIANT, interaction); - break; - case 'dut-2A-d': - await this.assignRole(guild, user, RoleIDs.DUT_2A, interaction); - await this.assignRole(guild, user, RoleIDs.DUT_2A_D, interaction); - await this.assignRole(guild, user, RoleIDs.ÉTUDIANT, interaction); - break; - default: - await interaction.followUp({ content: `Erreur, value = ${roleValue}`, ephemeral: true }); - } - } - - @SelectMenuComponent('role-other') - async selectMenuOther(interaction: SelectMenuInteraction) { - await interaction.deferReply({ ephemeral: true }); - - const roleValue = interaction.values?.[0]; - - if (!roleValue) { interaction.editReply({ content: `Erreur, value = ${roleValue}` }); return; } - - const user = interaction.user; - - const guild = interaction.guild; - - if (!guild) { interaction.editReply({ content: `Erreur, value = ${guild}` }); return; } - - switch (roleValue) { - case 'membreUL': - await this.assignRole(guild, user, RoleIDs.MEMBRE_UL, interaction); - break; - case 'teacher': - const adminChannel = (interaction.guild.channels.resolve(ChannelIDs.ADMIN_CHANNEL)); - - if (!adminChannel) { interaction.editReply({ content: `Erreur, adminChannel = ${adminChannel}` }); return; } - - if (!adminChannel.isText()) { interaction.editReply({ content: `Erreur, adminChannelType = ${adminChannel.type}` }); return; } - - await adminChannel.send(`${interaction.guild.roles.resolve(RoleIDs.ADMIN)} ! ${user} demande le grade ${interaction?.guild?.roles?.resolve(RoleIDs.ENSEIGNANT)}.`); - interaction.followUp({ content: "Votre demande a bien été transmisse ! Étant donné les permissions attachées à ce rôle, il vous sera attribuée manuelle par un " + guild.roles.resolve(RoleIDs.ADMIN) + " après vérifications.", ephemeral: true }); - break; - default: - await interaction.followUp({ content: `Erreur, value = ${roleValue}`, ephemeral: true }); - } - } - - @SelectMenuComponent('role-old-student') - async selectMenuOldStudent(interaction: SelectMenuInteraction) { - await interaction.deferReply({ ephemeral: true }); - - const roleValue = interaction.values?.[0]; - - if (!roleValue) { interaction.editReply({ content: `Erreur, value = ${roleValue}` }); return; } - - const user = interaction.user; - - const guild = interaction.guild; - - if (!guild) { interaction.editReply({ content: `Erreur, value = ${guild}` }); return; } - - switch (roleValue) { - case 'old-lp-CIASIE': - await this.assignRole(guild, user, RoleIDs.ANCIEN_LP_CIASIE, interaction); - break; - case 'old-DUT': - await this.assignRole(guild, user, RoleIDs.ANCIEN_DUT, interaction); - break; - default: - await interaction.followUp({ content: `Erreur, value = ${roleValue}`, ephemeral: true }); - } - } - - private async assignRole(guild: Guild, user: User, roleID: RoleIDs, interaction: SelectMenuInteraction) { - await guild?.members.resolve(user)?.roles.add(roleID); - await interaction.followUp({ content: `Le rôle <@&${roleID}> a bien été assigné !`, ephemeral: true }); - } - - private async removeRole(guild: Guild, user: User, roleID: RoleIDs, interaction: SelectMenuInteraction) { - await guild?.members.resolve(user)?.roles.remove(roleID); - await interaction.followUp({ content: `Le rôle <@&${roleID}> a bien été retiré !`, ephemeral: true }); - } - - @Slash('messageRoleAlternant', { description: "Envoie le message permettant d'obtenir le rôle alternant" }) - async messageRoleAlternant(interaction: CommandInteraction) { - await interaction.deferReply({ ephemeral: true }); - - const channel = interaction.channel; - - if (!channel) { interaction.editReply({ content: "Erreur, channel: " + channel }); return; } - - if (!channel.isText()) interaction.reply("Type de salon inattendu."); - - const row = new MessageActionRow() - .addComponents( - new MessageSelectMenu() - .setCustomId('role-alternant') - .setPlaceholder("Veuillez sélectionner une réponse") - .addOptions([ - { - label: "Oui", - description: "Je suis en alternance", - value: "oui" - }, - { - label: "Non", - "description": "Je suis en formation classique", - value: "non" - } - ]) - ); - - try { - await channel.send({ content: "Êtes-vous en alternance ? Si oui, vous pouvez obtenir un rôle spécial afin de faciliter les mentions et avoir un salon regroupant les alternants.", components: [row] }); - } catch (err) { - interaction.editReply({ content: "Une erreur est survenue." }); - console.log(err) - } finally { - interaction.editReply({ content: "Done." }); - } - } - - @SelectMenuComponent('role-alternant') - async selectMenuAlternant(interaction: SelectMenuInteraction){ - await interaction.deferReply({ ephemeral: true }); - - const roleValue = interaction.values?.[0]; - - if (!roleValue) { interaction.editReply({ content: `Erreur, value = ${roleValue}` }); return; } - - const user = interaction.user; - - const guild = interaction.guild; - - if (!guild) { interaction.editReply({ content: `Erreur, value = ${guild}` }); return; } - - switch (roleValue) { - case 'oui': - await this.assignRole(guild, user, RoleIDs.ALTERNANT, interaction); - break; - case 'non': - await this.removeRole(guild, user, RoleIDs.ALTERNANT, interaction); - break; - default: - await interaction.followUp({ content: `Erreur, value = ${roleValue}`, ephemeral: true }); - } - } -} \ No newline at end of file diff --git a/commands/Sondage.ts b/commands/Sondage.ts deleted file mode 100644 index d23ba8e..0000000 --- a/commands/Sondage.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { CommandInteraction, MessageEmbed } from "discord.js"; -import { DefaultPermission, Discord, Slash, SlashOption } from "discordx"; - -@Discord() -@DefaultPermission(true) -abstract class Sondage { - - @Slash('sondage', { description: "Créé un sondage" }) - async sondage( - @SlashOption("Titre", { description: "Sujet du sondage", required: true }) - title: string, - @SlashOption("Description", { description: "Explications affichées en dessous" }) - desc: string, - @SlashOption("ImageURL", { description: "Url de l'image affichée en bas du sondage" }) - imageurl: string, - @SlashOption("VoteNeutre", { description: "Autoriser ou Interdire le vote neutre. Autorisé par défaut." }) - allowNeutralVote: boolean = true, - interaction: CommandInteraction - ) { - await interaction.deferReply(); - - const resEmbed = new MessageEmbed() - .setColor("#DD131E") - .setAuthor(interaction.user.username, (interaction.user.avatarURL({ dynamic: true }) || undefined)) - .setTitle(title) - .setFooter("Tu peux voter en cliquant sur les réactions en dessous") - .setTimestamp(); - - if (desc) { resEmbed.setDescription(desc) } - if (imageurl) { resEmbed.setImage(imageurl) } - - await interaction.editReply({ embeds: [resEmbed] }); - - const reply = await interaction.fetchReply(); - - try { - //@ts-ignore - await reply.react("👍"); - //@ts-ignore - if (allowNeutralVote) await reply.react("🤔"); - //@ts-ignore - await reply.react("👎"); - } catch (err) { - interaction.editReply("Erreur <@227882902031958016>, " + reply.type) - } - - } -} \ No newline at end of file diff --git a/events/twitter/tweet.ts b/events/twitter/tweet.ts deleted file mode 100644 index cc0eac0..0000000 --- a/events/twitter/tweet.ts +++ /dev/null @@ -1,99 +0,0 @@ -import { BaseGuildTextChannel, Client, MessageActionRow, MessageButton, MessageEmbed } from 'discord.js'; -import * as Twit from 'twit'; -import { ChannelIDs, ServerIDs } from '../../enums/IDs'; -import { getHorodateConsole } from '../../util'; - -module.exports = { - name: 'tweet', - endPoint: 'statuses/filter', - options: { - follow: ['329665290', '282478715', '4113039761', '1257587508738699265'] - }, - async execute(tweet: Twit.Twitter.Status, client: Client) { - if (!this.options.follow.includes(tweet.user.id_str)) return; - if (tweet.in_reply_to_status_id || tweet.in_reply_to_status_id_str || tweet.in_reply_to_user_id || tweet.in_reply_to_user_id_str || tweet.in_reply_to_screen_name) return; - - console.log(`${getHorodateConsole()}\t@${tweet.user.screen_name} : ${tweet.text}`); - - let channel = (client.guilds.resolve(ServerIDs.MAIN)?.channels.resolve(ChannelIDs.TWITTER)); - - if (!channel || channel.isVoice()) { console.log(`[Channel Error] Channel type = ${channel?.type}`); return; } - - channel.sendTyping(); - - const resEmbed = new MessageEmbed() - .setColor('#1DA1F2') - .setAuthor(tweet.user.name, tweet.user.profile_image_url, `https://twitter.com/${tweet.user.screen_name}`) - .setThumbnail(tweet.user.profile_image_url) - //@ts-ignore - .setDescription(tweet.extended_tweet?.full_text || tweet.text) - //@ts-ignore - .setTimestamp(tweet.created_at); - - const row = new MessageActionRow(); - - if (tweet.retweeted_status) { - resEmbed.setTitle(`${tweet.user.screen_name} a retweeté(e)`) - .setAuthor(tweet.retweeted_status.user.name, tweet.retweeted_status.user.profile_image_url, `https://twitter.com/${tweet.retweeted_status.user.screen_name}`) - .setThumbnail(tweet.retweeted_status.user.profile_image_url) - //@ts-ignore - .setDescription(tweet.retweeted_status.extended_tweet?.full_text || tweet.retweeted_status.text); - - row.addComponents( - new MessageButton() - .setStyle("LINK") - .setLabel("Voir le Tweet") - .setURL(`https://twitter.com/${tweet.retweeted_status.user.screen_name}/status/${tweet.retweeted_status.id_str}`), - new MessageButton() - .setStyle("LINK") - .setLabel("Voir le Profil") - .setURL(`https://twitter.com/${tweet.retweeted_status.user.screen_name}`) - ); - } else { - resEmbed.setTitle(`Nouveau Tweet !`) - - row.addComponents( - new MessageButton() - .setStyle("LINK") - .setLabel("Voir le Tweet") - .setURL(`https://twitter.com/${tweet.user.screen_name}/status/${tweet.id_str}`), - new MessageButton() - .setStyle("LINK") - .setLabel("Voir le Profil") - .setURL(`https://twitter.com/${tweet.user.screen_name}`) - ); - } - - if (tweet.quoted_status) { - //@ts-ignore - resEmbed.addField(`A cité un Tweet de ${tweet.quoted_status.user.name}`, tweet.quoted_status.extended_tweet?.full_text || tweet.quoted_status.text); - //@ts-ignore - if (tweet.quoted_status?.extended_tweet?.extended_entities?.media) { - //@ts-ignore - resEmbed.setImage(tweet.quoted_status?.extended_tweet?.extended_entities?.media?.[0]?.media_url); - } - - row.addComponents( - new MessageButton() - .setStyle("LINK") - .setLabel("Voir le Tweet Cité") - .setURL(`https://twitter.com/${tweet.quoted_status.user.screen_name}/status/${tweet.quoted_status.id_str}`), - new MessageButton() - .setStyle("LINK") - .setLabel("Voir le Profil Cité") - .setURL(`https://twitter.com/${tweet.quoted_status.user.screen_name}`) - ); - } - - //@ts-ignore - if (tweet.extended_entities?.media || tweet?.extended_tweet?.extended_entities?.media || tweet.retweeted_status?.extended_tweet?.extended_entities?.media) { - //@ts-ignore - resEmbed.setImage(tweet.extended_entities?.media?.[0]?.media_url || tweet.extended_tweet?.extended_entities?.media?.[0]?.media_url || tweet.retweeted_status?.extended_tweet?.extended_entities?.media?.[0]?.media_url); - } - - const message = await channel.send({ embeds: [resEmbed], components: [row] }); - - if (channel.type === "GUILD_NEWS" || channel.type === "GUILD_NEWS_THREAD") - await message.crosspost(); - } -} \ No newline at end of file diff --git a/global/GlobalVar.ts b/global/GlobalVar.ts new file mode 100644 index 0000000..d06adab --- /dev/null +++ b/global/GlobalVar.ts @@ -0,0 +1,35 @@ +import { ApplicationCommandPermissionData, ChannelResolvable } from "discord.js"; +import { Serveur } from "../IDs"; +import { Role as Role_IUT_NC_DEPINFO } from "../iut_nc_depinfo/IDs"; + +export const globalAdminPerms: ApplicationCommandPermissionData[] = [ + { id: Role_IUT_NC_DEPINFO.STAR, type: "ROLE", permission: true }, + { id: Role_IUT_NC_DEPINFO.ADMIN, type: "ROLE", permission: true }, + //{ id: RoleBeta.BETA_1, type: "ROLE", permission: true }, + //{ id: RoleBeta.BETA_2, type: "ROLE", permission: true } +] + +export const globalEnseignantPerms: ApplicationCommandPermissionData[] = [ + { id: Role_IUT_NC_DEPINFO.ENSEIGNANT, type: "ROLE", permission: true } +] + +export const allServeursIds: Serveur[] = [ + Serveur.IUT_NC_DEP_INFO, + //Serveur.BETA_2, + //Serveur.BETA_1 +] + +export const salonIdPostTwitter: ChannelResolvable[] = [ + "854010477838073876", // iut_nc_depinfo twitter + //"887879239985168384", // Bêta 1 général + //"885296615529787433", // Bêta 1 e + //"888005290958675992", // Bêta 2 e +] + +export const followTwitter = [ + '329665290', //@Univ_Lorraine + '282478715', //@iutCharlemagne + '4113039761', //@regiongrandest + '1257587508738699265', //@RA_GrandEst + //'1003899371354755072' //@Tenebrosful (tests) +] \ No newline at end of file diff --git a/global/buttons/.gitignore b/global/buttons/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/global/buttons/Pfc.ts b/global/buttons/Pfc.ts new file mode 100644 index 0000000..b088133 --- /dev/null +++ b/global/buttons/Pfc.ts @@ -0,0 +1,23 @@ +import { ButtonInteraction } from "discord.js"; +import { ButtonComponent, Discord } from "discordx"; +import { pfcIsWin, pfcPlayBot, pfcProposition, pfcTraitementResultat } from "../../libs/pfc"; + +@Discord() +abstract class Pfc { + + @ButtonComponent('pfc-rock') + @ButtonComponent('pfc-paper') + @ButtonComponent('pfc-scissors') + private async pfcButton(interaction: ButtonInteraction) { + const playerChoice = pfcProposition.buttonCustomIdToClass(interaction.customId); + + if (!playerChoice) { interaction.reply({ content: `Erreur, ${interaction.customId} est un choix invalide.`, ephemeral: true }); return; } + + const botChoice = pfcPlayBot(); + const result = pfcIsWin(playerChoice, botChoice); + + if (!result) { interaction.editReply({ content: `Erreur, ${playerChoice} vs ${botChoice} est un cas de figure non implémenté.` }); return; } + + interaction.update(pfcTraitementResultat(playerChoice, botChoice, result, interaction.user)); + } +} \ No newline at end of file diff --git a/global/contextMenus/.gitignore b/global/contextMenus/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/global/events/.gitignore b/global/events/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/global/events/twitter/tweet.ts b/global/events/twitter/tweet.ts new file mode 100644 index 0000000..c4c0573 --- /dev/null +++ b/global/events/twitter/tweet.ts @@ -0,0 +1,107 @@ +import { Client, MessageActionRow, MessageButton, MessageEmbed, MessageOptions } from 'discord.js'; +import * as Twit from 'twit'; +import { getHorodateConsole } from '../../../libs/util'; +import { followTwitter, salonIdPostTwitter } from '../../GlobalVar'; + +module.exports = { + name: 'tweet', + endPoint: 'statuses/filter', + options: { + follow: followTwitter + }, + async execute(tweet: Twit.Twitter.Status, client: Client) { + if (!this.options.follow.includes(tweet.user.id_str)) return; + if (tweet.in_reply_to_status_id || tweet.in_reply_to_status_id_str || tweet.in_reply_to_user_id || tweet.in_reply_to_user_id_str || tweet.in_reply_to_screen_name) return; + + console.log(`${getHorodateConsole()}\t[TWEET]\t@${tweet.user.screen_name} : ${tweet.text}`); + + const post = genererPost(tweet); + + const salons = salonIdPostTwitter.map(id => client.channels.resolve(id)); + + salons.forEach(async (salon, i) => { + if (!salon || !salon.isText()) { console.log(`${getHorodateConsole()}\t[Channel Error]\t${salonIdPostTwitter[i]} Channel type = ${salon?.type}`); return; } + + salon.sendTyping(); + + const message = await salon.send(post); + + if (salon.type === "GUILD_NEWS" || salon.type === "GUILD_NEWS_THREAD") + await message.crosspost(); + }) + } +} + +function genererPost(tweet: Twit.Twitter.Status): MessageOptions { + const resEmbed = new MessageEmbed() + .setColor('#1DA1F2') + .setAuthor(tweet.user.name, tweet.user.profile_image_url, `https://twitter.com/${tweet.user.screen_name}`) + .setThumbnail(tweet.user.profile_image_url) + //@ts-ignore + .setDescription(tweet.extended_tweet?.full_text || tweet.text) + //@ts-ignore + .setTimestamp(tweet.created_at); + + const row = new MessageActionRow(); + + if (tweet.retweeted_status) { + resEmbed.setTitle(`${tweet.user.screen_name} a retweeté(e)`) + .setAuthor(tweet.retweeted_status.user.name, tweet.retweeted_status.user.profile_image_url, `https://twitter.com/${tweet.retweeted_status.user.screen_name}`) + .setThumbnail(tweet.retweeted_status.user.profile_image_url) + //@ts-ignore + .setDescription(tweet.retweeted_status.extended_tweet?.full_text || tweet.retweeted_status.text); + + row.addComponents( + new MessageButton() + .setStyle("LINK") + .setLabel("Voir le Tweet") + .setURL(`https://twitter.com/${tweet.retweeted_status.user.screen_name}/status/${tweet.retweeted_status.id_str}`), + new MessageButton() + .setStyle("LINK") + .setLabel("Voir le Profil") + .setURL(`https://twitter.com/${tweet.retweeted_status.user.screen_name}`) + ); + } else { + resEmbed.setTitle(`Nouveau Tweet !`) + + row.addComponents( + new MessageButton() + .setStyle("LINK") + .setLabel("Voir le Tweet") + .setURL(`https://twitter.com/${tweet.user.screen_name}/status/${tweet.id_str}`), + new MessageButton() + .setStyle("LINK") + .setLabel("Voir le Profil") + .setURL(`https://twitter.com/${tweet.user.screen_name}`) + ); + } + + if (tweet.quoted_status) { + //@ts-ignore + resEmbed.addField(`A cité un Tweet de ${tweet.quoted_status.user.name}`, tweet.quoted_status.extended_tweet?.full_text || tweet.quoted_status.text); + //@ts-ignore + if (tweet.quoted_status?.extended_tweet?.extended_entities?.media) { + //@ts-ignore + resEmbed.setImage(tweet.quoted_status?.extended_tweet?.extended_entities?.media?.[0]?.media_url); + } + + row.addComponents( + new MessageButton() + .setStyle("LINK") + .setLabel("Voir le Tweet Cité") + .setURL(`https://twitter.com/${tweet.quoted_status.user.screen_name}/status/${tweet.quoted_status.id_str}`), + new MessageButton() + .setStyle("LINK") + .setLabel("Voir le Profil Cité") + .setURL(`https://twitter.com/${tweet.quoted_status.user.screen_name}`) + ); + } + + //@ts-ignore + if (tweet.extended_entities?.media || tweet?.extended_tweet?.extended_entities?.media || tweet.retweeted_status?.extended_tweet?.extended_entities?.media) { + //@ts-ignore + resEmbed.setImage(tweet.extended_entities?.media?.[0]?.media_url || tweet.extended_tweet?.extended_entities?.media?.[0]?.media_url || tweet.retweeted_status?.extended_tweet?.extended_entities?.media?.[0]?.media_url); + } + + return { embeds: [resEmbed], components: [row] }; +} \ No newline at end of file diff --git a/global/selectMenus/.gitignore b/global/selectMenus/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/global/slashs/.gitignore b/global/slashs/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/global/slashs/Divers.ts b/global/slashs/Divers.ts new file mode 100644 index 0000000..70fe279 --- /dev/null +++ b/global/slashs/Divers.ts @@ -0,0 +1,153 @@ +import { CommandInteraction, DMChannel, GuildChannel, Message, MessageActionRow, MessageButton, MessageEmbed, VoiceChannel } from "discord.js"; +import { DefaultPermission, Discord, Guild, Permission, Slash, SlashOption } from "discordx"; +import { allServeursIds, globalAdminPerms, globalEnseignantPerms } from "../GlobalVar"; + +@Discord() +abstract class Divers { + + @Slash('sondage', { description: "Créé un sondage" }) + async sondage( + @SlashOption("Titre", { description: "Sujet du sondage", required: true }) + titre: string, + @SlashOption("Description", { description: "Explications affichées en dessous" }) + desc: string, + @SlashOption("ImageURL", { description: "Url de l'image affichée en bas du sondage" }) + imageurl: string, + @SlashOption("VoteNeutre", { description: "Autoriser ou Interdire le vote neutre. Autorisé par défaut." }) + autoriserVoteNeutre: boolean = true, + interaction: CommandInteraction + ) { + await interaction.deferReply(); + + const resEmbed = new MessageEmbed() + .setColor("#DD131E") + .setAuthor(interaction.user.username, (interaction.user.avatarURL({ dynamic: true }) || undefined)) + .setTitle(titre) + .setFooter("Tu peux voter en cliquant sur les réactions en dessous") + .setTimestamp(); + + if (desc) { resEmbed.setDescription(desc) } + if (imageurl) { resEmbed.setImage(imageurl) } + + await interaction.editReply({ embeds: [resEmbed] }); + + const reply = await interaction.fetchReply(); + + if (!(reply instanceof Message)) { interaction.editReply("Erreur, reply: " + reply.type); return; } + + // Le processus est assez lent pour que la réponse de l'interaction soit supprimé et fasse crash le bot + try { + await reply.react("👍"); + if (autoriserVoteNeutre) await reply.react("🤔"); + await reply.react("👎"); + } catch (err) { + console.error(err); + try { interaction.editReply("Erreur, vérification des logs."); } catch (err) { console.error(err); return; } + } + } + + @Slash('webhook', { description: "Retourne le lien d'un webhook créé par le bot" }) + async webhook( + @SlashOption("Salon", { type: "CHANNEL", required: true }) + channel: GuildChannel | DMChannel, + interaction: CommandInteraction + ) { + await interaction.deferReply({ ephemeral: true }); + + if (channel.isThread()) { interaction.editReply({ content: "❌ Désolé mais je ne peux pas effectuer cette commande sur un Fil" }); return; } + if (channel.type === "DM") { interaction.editReply({ content: "❌ Désolé mais je ne peux pas effectuer cette commande en message privé sans indiquer un salon précis." }); return; } + if (!channel.isText()) { interaction.editReply({ content: "❌ Désolé mais je ne peux pas effectuer cette commande sur un salon non-textuel." }); return; } + + if (!(channel.permissionsFor(interaction.user)?.has("MANAGE_WEBHOOKS"))) { interaction.editReply({ content: `❌ Désolé mais tu n'as pas la permission "MANAGE_WEBHOOKS".` }); return; } + + const webhooks = await channel.fetchWebhooks(); + + let webhook = webhooks.find(webhook => { + if (webhook.owner === null || interaction.client === null || interaction.client.user === null) return false; + if (!webhook.owner.bot) return false; + if (webhook.owner.id !== interaction.client.user.id) return false; + return true; + }); + + if (!webhook) { + webhook = await channel.createWebhook("Embeds", { reason: `Création du webhook demandé par ${interaction.user.username}` }); + } + + const embed = new MessageEmbed() + .setTitle("Votre webhook") + .setDescription(`\`${webhook.url}\``) + .setColor('#0080ff'); + + const row = new MessageActionRow() + .addComponents(new MessageButton() + .setStyle("LINK") + .setLabel("Ouvrir avec Discord.club (SOON)") + .setURL(`https://discord.club/dashboard?`) + .setDisabled(true)); + + interaction.editReply({ embeds: [embed], components: [row] }) + } + + @Guild(...allServeursIds) + @DefaultPermission(false) + @Permission(...globalAdminPerms) + @Permission(...globalEnseignantPerms) + @Slash('groupevocal', { description: "Crée un ou plusieurs salons vocaux temporaire dans cette catégorie (1 salon pendant 60m par défaut)" }) + private async groupevocal( + @SlashOption('quantité', { description: "Nombre de salon à créer. (10 salon max)" }) + amount: number = 1, + @SlashOption('durée', { description: "Durée du salon avant sa suppression en minute. (240 minutes = 4 heures max)" }) + duration: number = 60, + interaction: CommandInteraction + ) { + if (amount <= 0) { interaction.reply({ content: "Désolé cependant vous ne pouvez créer une quantité négative ou nulle de salon.", ephemeral: true }); return; } + if (duration <= 0) { interaction.reply({ content: "Désolé cependant vous ne pouvez créer de salon pour une durée négative ou nulle.", ephemeral: true }); return; } + + if (amount > 10) { interaction.reply({ content: "Désolé cependant vous ne pouvez pas créer plus de 10 salons à la fois.", ephemeral: true }); return; } + if (duration > 240) { interaction.reply({ content: "Désolé cependant vous ne pouvez créer de salon durant plus de 4 heures.", ephemeral: true }); return; } + + if (interaction.channel?.type !== "GUILD_TEXT") { return; } + + const category = interaction.channel?.parent; + + if (!category) { interaction.reply({ content: "Désolé cependant ce salon n'a pas de catégorie.", ephemeral: true }); return; } + + const guild = interaction.guild; + + if (!guild) { interaction.reply({ content: `Une erreur est survenue (guild: ${guild})`, ephemeral: true }); return; } + + await interaction.deferReply(); + + let newSalonsPromise: Promise[] = []; + + for (let i = 0; i < amount; i++) { + newSalonsPromise.push(guild.channels.create(`💻・Groupe ${i + 1}`, { + type: "GUILD_VOICE", + parent: category, + reason: `Salon vocal de groupe temporaire créé par ${interaction.user.username}` + })); + } + + interaction.editReply({ content: "Votre demande est en cours de traitement..." }) + + const newChannels = await Promise.all(newSalonsPromise); + + newChannels.forEach(async salon => { + salon.permissionOverwrites.create(interaction.user, { 'MANAGE_CHANNELS': true }, { reason: "Permissions données au créateur du salon" }); + setTimeout((salon: VoiceChannel) => { + salon.delete(); + }, duration * 60000, salon); + }); + + interaction.editReply({ content: `Vos ${amount} salons ont été créé pour ${duration} minutes. \`${duration} minutes restantes\`` }); + + let minutesSpend = 0; + const intervalID = setInterval( + (interaction: CommandInteraction, minutesSpend: number) => { + interaction.editReply({ content: `Vos ${amount} salons ont été créé pour ${duration} minutes. \`${duration - minutesSpend} minutes restantes\`` }); + minutesSpend++ + }, 60000, interaction, ++minutesSpend); + + setTimeout((intervalID, interaction: CommandInteraction) => { clearInterval(intervalID); interaction.editReply({ content: `Temps écoulé ! Vos salons ont été supprimés.` }) }, duration * 60000, intervalID, interaction); + } +} \ No newline at end of file diff --git a/global/slashs/Fun.ts b/global/slashs/Fun.ts new file mode 100644 index 0000000..43f32a9 --- /dev/null +++ b/global/slashs/Fun.ts @@ -0,0 +1,38 @@ +import { CommandInteraction, MessageActionRow } from "discord.js"; +import { Discord, Slash, SlashChoice, SlashGroup, SlashOption } from "discordx"; +import { pfcButtons, pfcChoix, pfcIsWin, pfcPlayBot, pfcProposition, pfcTraitementResultat } from "../../libs/pfc"; + +@Discord() +@SlashGroup("fun", "Commandes orienté sur l'amusement") +abstract class Fun { + + @Slash("pfc", { description: "Quoi de plus amusant que jouer à Pierre Feuille Ciseaux avec un robot ?" }) + async pfc( + @SlashChoice(pfcChoix) + @SlashOption("Choix", { description: "Que voulez-vous jouer ?" }) + choix: pfcChoix, + interaction: CommandInteraction + ) { + if (choix) { + await interaction.deferReply({ ephemeral: true }); + + const choixJoueur = pfcProposition.nameToClass(choix); + + if (!choixJoueur) { interaction.editReply({ content: `Erreur, ${choix} est un choix invalide.` }); return; } + + const choixBot = pfcPlayBot(); + const resultat = pfcIsWin(choixJoueur, choixBot); + + if (!resultat) { interaction.editReply({ content: `Erreur, ${choixJoueur} vs ${choixBot} est un cas de figure non implémenté.` }); return; } + + interaction.editReply(pfcTraitementResultat(choixJoueur, choixBot, resultat, interaction.user)); + } else { + await interaction.deferReply(); + + const buttonRow = new MessageActionRow() + .addComponents(pfcButtons); + + interaction.editReply({ content: "Ok let's go. 1v1 Pierre Feuille Ciseaux. Vas-y choisis !", components: [buttonRow] }); + } + } +} \ No newline at end of file diff --git "a/global/slashs/Mod\303\251ration.ts" "b/global/slashs/Mod\303\251ration.ts" new file mode 100644 index 0000000..f2e1935 --- /dev/null +++ "b/global/slashs/Mod\303\251ration.ts" @@ -0,0 +1,78 @@ +import { AwaitMessagesOptions, Collection, CommandInteraction, DMChannel, GuildChannel, Message, Snowflake } from "discord.js"; +import { DefaultPermission, Discord, Guild, Permission, Slash, SlashGroup, SlashOption } from "discordx"; +import { allServeursIds, globalAdminPerms } from "../GlobalVar"; + +@Discord() +@Guild(...allServeursIds) +@DefaultPermission(false) +@Permission(...globalAdminPerms) +@SlashGroup("mod", "Commandes de modération") +abstract class Modération { + + @Slash('deleteMessages', { description: "Supprime les derniers messages envoyés il y a moins de 2 semaines. Supprime 100 messages par défaut" }) + async deleteMessages( + @SlashOption('nombre', { description: "Nombre de message à effacer" }) + amount: number, + @SlashOption('jours', { description: "Ancienneté des messages à supprimer en jours" }) + days: number = 0, + @SlashOption('heures', { description: "Ancienneté des messages à supprimer en heures" }) + hours: number = 0, + @SlashOption('minutes', { description: "Ancienneté des messages à supprimer en minutes" }) + minutes: number = 0, + interaction: CommandInteraction + ) { + await interaction.deferReply({ ephemeral: true }) + + const channel = interaction.channel; + + if (!channel) { interaction.editReply({ content: "Erreur, channel: " + channel }); return; } + + if (channel?.type === "DM") { interaction.editReply({ content: "❌ Désolé mais je ne peux pas effectuer cette commande en message privé." }); return; } + + let messageList: Collection = new Collection(); + + if (days || hours || minutes) { + const time: number = days * 8.64e+7 + hours * 3.6e+6 + minutes * 60000; + + let Options: AwaitMessagesOptions = { + time + } + + if (amount) + Options.max = amount + + const maxTimestamp = new Date(Date.now() - time); + + messageList = (await channel.messages.fetch()).filter(message => message.createdAt >= maxTimestamp); + } + + const deletedMessages = await channel.bulkDelete((messageList || amount || 100), true); + const notDeletedMessageAmount = (messageList?.size || amount || 100) - deletedMessages.size; + + let replyMessage: string = `${deletedMessages.size} ${deletedMessages.size > 1 ? "messages ont été supprimés" : "message a été supprimé"}.` + + if (notDeletedMessageAmount !== 0) + replyMessage += ` ${notDeletedMessageAmount} ${notDeletedMessageAmount > 1 ? "messages n'ont pas pu être supprimés" : "message n'a pas pu être supprimé"}. Cela est peut être dû à la limitation de 2 semaines de Discord (Ou qu'il n'y avait pas autant de message dans le salon). Pour supprimer tout un salon vous pouvez utiliser \`/purgechannel\`.`; + + interaction.reply({ content: replyMessage }) + } + + @Slash('purgeChannel', { description: "Clone et supprime le salon afin de supprimer son contenu" }) + async purgeChannel( + @SlashOption("channel", { description: "Salon à purger. Salon actuel par défaut", type: "CHANNEL", required: true }) + channel: GuildChannel | DMChannel, + interaction: CommandInteraction + ) { + await interaction.deferReply({ ephemeral: true }); + + if (channel.isThread()) { interaction.editReply({ content: "❌ Désolé mais je ne peux pas effectuer cette commande sur un Fil" }); return; } + if (channel.type === "DM") { interaction.editReply({ content: "❌ Désolé mais je ne peux pas effectuer cette commande en message privé sans indiquer un salon précis." }); return; } + + const newChannel = await channel.clone({ reason: `Purge du salon demandé par ${interaction.user.username}` }); + await channel.delete(); + + if (channel !== interaction.channel) + interaction.editReply({ content: `Salon purgé ! ${newChannel}` }); + } + +} \ No newline at end of file diff --git a/index.ts b/index.ts index 4356968..9e8dd57 100644 --- a/index.ts +++ b/index.ts @@ -2,7 +2,7 @@ require('dotenv').config() import { Intents } from 'discord.js'; import { Client } from 'discordx'; import 'reflect-metadata'; -import { getHorodateConsole, logInteraction, resetPresence } from './util'; +import { getHorodateConsole, logInteraction, resetPresence } from './libs/util'; export let SingletonClient: Client; @@ -24,15 +24,16 @@ async function start() { Intents.FLAGS.GUILD_BANS, ], classes: [ - `${__dirname}/commands/*.ts`, - `${__dirname}/events/discord/*.ts` - ], - botId: "850109914827587605" + `${__dirname}/global/*/*.ts`, + `${__dirname}/global/events/discord/*.ts`, + `${__dirname}/iut_nc_depinfo/*/*.ts`, + `${__dirname}/iut_nc_depinfo/events/discord/*.ts` + ] }); SingletonClient.once("ready", async () => { await SingletonClient.initApplicationCommands(); - console.log(`${getHorodateConsole()}\tReady !`); + console.log(`${getHorodateConsole()}\t[INFO]\tReady !`); if (SingletonClient.user) resetPresence(SingletonClient.user); @@ -47,10 +48,10 @@ async function start() { } function handleExit(signal: NodeJS.Signals) { - console.info(`${getHorodateConsole()} Signal ${signal} reçu.`); + console.info(`${getHorodateConsole()}\t[STOP]\tSignal ${signal} reçu.`); SingletonClient.user?.setPresence({ status: "idle", activities: [{ name: "Arrêt en cours", type: "COMPETING" }] }) SingletonClient.destroy(); - console.log(`${getHorodateConsole()} Arrêt du bot.`); + console.log(`${getHorodateConsole()}\t[STOP]\tArrêt du bot.`); process.exit(0); } diff --git a/enums/IDs.ts b/iut_nc_depinfo/IDs.ts similarity index 91% rename from enums/IDs.ts rename to iut_nc_depinfo/IDs.ts index e441aba..f6b6173 100644 --- a/enums/IDs.ts +++ b/iut_nc_depinfo/IDs.ts @@ -1,4 +1,4 @@ -export enum RoleIDs { +export enum Role { EVERYONE = '753171392450527282', STAR = '753181468183887914', ADMIN = '753182939352793138', @@ -30,12 +30,7 @@ export enum RoleIDs { MEMBRE_UL = '887917824822083594' } -export enum ChannelIDs{ +export enum Salon { ADMIN_CHANNEL = '753753680334815345', TWITTER = '854010477838073876' - -} - -export enum ServerIDs { - MAIN = '753171392450527282' } \ No newline at end of file diff --git a/iut_nc_depinfo/buttons/.gitignore b/iut_nc_depinfo/buttons/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/iut_nc_depinfo/contextMenus/.gitignore b/iut_nc_depinfo/contextMenus/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/iut_nc_depinfo/events/.gitignore b/iut_nc_depinfo/events/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/iut_nc_depinfo/selectMenus/.gitignore b/iut_nc_depinfo/selectMenus/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/iut_nc_depinfo/selectMenus/Roles.ts b/iut_nc_depinfo/selectMenus/Roles.ts new file mode 100644 index 0000000..6c4e992 --- /dev/null +++ b/iut_nc_depinfo/selectMenus/Roles.ts @@ -0,0 +1,348 @@ +import { Guild, SelectMenuInteraction, User } from "discord.js"; +import { Discord, SelectMenuComponent } from "discordx"; +import { Role, Salon } from "../IDs"; + +@Discord() +abstract class Roles { + + @SelectMenuComponent('role-lp') + async roleLP(interaction: SelectMenuInteraction) { + await interaction.deferReply({ ephemeral: true }); + + const roleValue = interaction.values?.[0]; + + if (!roleValue) { interaction.editReply({ content: `Erreur, value = ${roleValue}` }); return; } + + const user = interaction.user; + + const guild = interaction.guild; + + if (!guild) { interaction.editReply({ content: `Erreur, value = ${guild}` }); return; } + + switch (roleValue) { + case 'lp-ACORS': + await this.assignRole(guild, user, Role.LP_ACORS, interaction); + await this.assignRole(guild, user, Role.ÉTUDIANT, interaction); + break; + case 'lp-AFTER': + await this.assignRole(guild, user, Role.LP_AFTER, interaction); + await this.assignRole(guild, user, Role.ÉTUDIANT, interaction); + break; + case 'lp-CIASIE-1': + await this.assignRole(guild, user, Role.LP_CIASIE, interaction); + await this.assignRole(guild, user, Role.LP_CIASIE_1, interaction); + await this.assignRole(guild, user, Role.ÉTUDIANT, interaction); + break; + case 'lp-CIASIE-2': + await this.assignRole(guild, user, Role.LP_CIASIE, interaction); + await this.assignRole(guild, user, Role.LP_CIASIE_2, interaction); + await this.assignRole(guild, user, Role.ÉTUDIANT, interaction); + break; + default: + await interaction.followUp({ content: `Erreur, value = ${roleValue}`, ephemeral: true }); + } + } + + @SelectMenuComponent('role-but-1a') + async roleBUT1A(interaction: SelectMenuInteraction) { + await interaction.deferReply({ ephemeral: true }); + + const roleValue = interaction.values?.[0]; + + if (!roleValue) { interaction.editReply({ content: `Erreur, value = ${roleValue}` }); return; } + + const user = interaction.user; + + const guild = interaction.guild; + + if (!guild) { interaction.editReply({ content: `Erreur, value = ${guild}` }); return; } + + switch (roleValue) { + case 'but-1A-a': + await this.assignRole(guild, user, Role.BUT_1A, interaction); + await this.assignRole(guild, user, Role.BUT_1A_A, interaction); + await this.assignRole(guild, user, Role.ÉTUDIANT, interaction); + break; + case 'but-1A-b': + await this.assignRole(guild, user, Role.BUT_1A, interaction); + await this.assignRole(guild, user, Role.BUT_1A_B, interaction); + await this.assignRole(guild, user, Role.ÉTUDIANT, interaction); + break; + case 'but-1A-c': + await this.assignRole(guild, user, Role.BUT_1A, interaction); + await this.assignRole(guild, user, Role.BUT_1A_C, interaction); + await this.assignRole(guild, user, Role.ÉTUDIANT, interaction); + break; + case 'but-1A-d': + await this.assignRole(guild, user, Role.BUT_1A, interaction); + await this.assignRole(guild, user, Role.BUT_1A_D, interaction); + await this.assignRole(guild, user, Role.ÉTUDIANT, interaction); + break; + case 'but-1A-e': + await this.assignRole(guild, user, Role.BUT_1A, interaction); + await this.assignRole(guild, user, Role.BUT_1A_E, interaction); + await this.assignRole(guild, user, Role.ÉTUDIANT, interaction); + break; + default: + await interaction.followUp({ content: `Erreur, value = ${roleValue}`, ephemeral: true }); + } + } + + @SelectMenuComponent('role-dut-2a') + async roleDUT2A(interaction: SelectMenuInteraction) { + await interaction.deferReply({ ephemeral: true }); + + const roleValue = interaction.values?.[0]; + + if (!roleValue) { interaction.editReply({ content: `Erreur, value = ${roleValue}` }); return; } + + const user = interaction.user; + + const guild = interaction.guild; + + if (!guild) { interaction.editReply({ content: `Erreur, value = ${guild}` }); return; } + + switch (roleValue) { + case 'dut-2A-a': + await this.assignRole(guild, user, Role.DUT_2A, interaction); + await this.assignRole(guild, user, Role.DUT_2A_A, interaction); + await this.assignRole(guild, user, Role.ÉTUDIANT, interaction); + break; + case 'dut-2A-b': + await this.assignRole(guild, user, Role.DUT_2A, interaction); + await this.assignRole(guild, user, Role.DUT_2A_B, interaction); + await this.assignRole(guild, user, Role.ÉTUDIANT, interaction); + break; + case 'dut-2A-c': + await this.assignRole(guild, user, Role.DUT_2A, interaction); + await this.assignRole(guild, user, Role.DUT_2A_C, interaction); + await this.assignRole(guild, user, Role.ÉTUDIANT, interaction); + break; + case 'dut-2A-d': + await this.assignRole(guild, user, Role.DUT_2A, interaction); + await this.assignRole(guild, user, Role.DUT_2A_D, interaction); + await this.assignRole(guild, user, Role.ÉTUDIANT, interaction); + break; + default: + await interaction.followUp({ content: `Erreur, value = ${roleValue}`, ephemeral: true }); + } + } + + @SelectMenuComponent('role-other') + async roleAutre(interaction: SelectMenuInteraction) { + await interaction.deferReply({ ephemeral: true }); + + const roleValue = interaction.values?.[0]; + + if (!roleValue) { interaction.editReply({ content: `Erreur, value = ${roleValue}` }); return; } + + const user = interaction.user; + + const guild = interaction.guild; + + if (!guild) { interaction.editReply({ content: `Erreur, value = ${guild}` }); return; } + + switch (roleValue) { + case 'membreUL': + await this.assignRole(guild, user, Role.MEMBRE_UL, interaction); + break; + case 'teacher': + const adminChannel = (interaction.guild.channels.resolve(Salon.ADMIN_CHANNEL)); + + if (!adminChannel) { interaction.editReply({ content: `Erreur, adminChannel = ${adminChannel}` }); return; } + + if (!adminChannel.isText()) { interaction.editReply({ content: `Erreur, adminChannelType = ${adminChannel.type}` }); return; } + + await adminChannel.send(`${interaction.guild.roles.resolve(Role.ADMIN)} ! ${user} demande le grade ${interaction?.guild?.roles?.resolve(Role.ENSEIGNANT)}.`); + interaction.followUp({ content: "Votre demande a bien été transmisse ! Étant donné les permissions attachées à ce rôle, il vous sera attribuée manuelle par un " + guild.roles.resolve(Role.ADMIN) + " après vérifications.", ephemeral: true }); + break; + default: + await interaction.followUp({ content: `Erreur, value = ${roleValue}`, ephemeral: true }); + } + } + + @SelectMenuComponent('role-old-student') + async RoleAncien(interaction: SelectMenuInteraction) { + await interaction.deferReply({ ephemeral: true }); + + const roleValue = interaction.values?.[0]; + + if (!roleValue) { interaction.editReply({ content: `Erreur, value = ${roleValue}` }); return; } + + const user = interaction.user; + + const guild = interaction.guild; + + if (!guild) { interaction.editReply({ content: `Erreur, value = ${guild}` }); return; } + + switch (roleValue) { + case 'old-lp-CIASIE': + await this.assignRole(guild, user, Role.ANCIEN_LP_CIASIE, interaction); + break; + case 'old-DUT': + await this.assignRole(guild, user, Role.ANCIEN_DUT, interaction); + break; + default: + await interaction.followUp({ content: `Erreur, value = ${roleValue}`, ephemeral: true }); + } + } + + private async assignRole(guild: Guild, user: User, roleID: Role, interaction: SelectMenuInteraction) { + await guild?.members.resolve(user)?.roles.add(roleID); + await interaction.followUp({ content: `Le rôle <@&${roleID}> a bien été assigné !`, ephemeral: true }); + } + + private async removeRole(guild: Guild, user: User, roleID: Role, interaction: SelectMenuInteraction) { + await guild?.members.resolve(user)?.roles.remove(roleID); + await interaction.followUp({ content: `Le rôle <@&${roleID}> a bien été retiré !`, ephemeral: true }); + } + + @SelectMenuComponent('role-alternant') + async selectMenuAlternant(interaction: SelectMenuInteraction) { + await interaction.deferReply({ ephemeral: true }); + + const roleValue = interaction.values?.[0]; + + if (!roleValue) { interaction.editReply({ content: `Erreur, value = ${roleValue}` }); return; } + + const user = interaction.user; + + const guild = interaction.guild; + + if (!guild) { interaction.editReply({ content: `Erreur, value = ${guild}` }); return; } + + switch (roleValue) { + case 'oui': + await this.assignRole(guild, user, Role.ALTERNANT, interaction); + break; + case 'non': + await this.removeRole(guild, user, Role.ALTERNANT, interaction); + break; + default: + await interaction.followUp({ content: `Erreur, value = ${roleValue}`, ephemeral: true }); + } + } +} + +export const RolesLPOptions = [ + { + label: "Licenses Pro. ACORS", + value: "lp-ACORS", + description: "Vous êtes en Licences Professionnelle ACORS ?" + }, + { + label: "Licenses Pro. AFTER", + value: "lp-AFTER", + description: "Vous êtes en Licences Professionnelle AFTER ?" + }, + { + label: "Licenses Pro. CIASIE 1", + value: "lp-CIASIE-1", + description: "Vous êtes en Licences Professionnelle CIASIE (Groupe 1) ?" + }, + { + label: "Licenses Pro. CIASIE 2", + value: "lp-CIASIE-2", + description: "Vous êtes en Licences Professionnelle CIASIE (Groupe 2) ?" + } +]; + +export const RolesBUT1AOptions = [ + { + label: "BUT 1ère Année A", + value: "but-1A-a", + description: "Vous êtes en 1ère Année de BUT (Groupe A)?" + }, + { + label: "BUT 1ère Année B", + value: "but-1A-b", + description: "Vous êtes en 1ère Année de BUT (Groupe B)?" + }, + { + label: "BUT 1ère Année C", + value: "but-1A-c", + description: "Vous êtes en 1ère Année de BUT (Groupe C)?" + }, + { + label: "BUT 1ère Année D", + value: "but-1A-d", + description: "Vous êtes en 1ère Année de BUT (Groupe D)?" + }, + { + label: "BUT 1ère Année E", + value: "but-1A-e", + description: "Vous êtes en 1ère Année de BUT (Groupe E)?" + } +]; + +export const RolesDUT2AOptions = [ + { + label: "DUT 2ème Année A", + value: "dut-2A-a", + description: "Vous êtes en 2ème Année de DUT (Groupe A)?" + }, + { + label: "DUT 2ème Année B", + value: "dut-2A-b", + description: "Vous êtes en 2ème Année de DUT (Groupe B)?" + }, + { + label: "DUT 2ème Année C", + value: "dut-2A-c", + description: "Vous êtes en 2ème Année de DUT (Groupe C)?" + }, + { + label: "DUT 2ème Année D", + value: "dut-2A-d", + description: "Vous êtes en 2ème Année de DUT (Groupe D)?" + } +]; + +export const RolesAutreOptions = [ + { + label: "Enseignant du département", + value: "teacher", + description: "Vous êtes enseignant du Département Informatique ?" + }, + { + label: "Membre de l'Université de Lorraine", + value: "membreUL", + description: "Vous êtes étudiant / enseignant / autre chose au sein de l'Université de Lorraine ?" + } +]; + +export const RolesAncienOptions = [ + /*{ + label: "Ancien LP ACORS", + value: "old-lp-ACORS", + description: "Vous êtes un ancien de la LP ACORS ?" + }, + { + label: "Ancien LP AFTER", + value: "old-lp-AFTER", + description: "Vous êtes un ancien de la LP AFTER ?" + },*/ + { + label: "Ancien LP CIASIE", + value: "old-lp-CIASIE", + description: "Vous êtes un ancien de la LP CIASIE ?" + }, + { + label: "Ancien DUT", + value: "old-DUT", + description: "Vous êtes un ancien du DUT ?" + } +]; + +export const RoleAlternantCIASIEOptions = [ + { + label: "Oui", + description: "Je suis en alternance", + value: "oui" + }, + { + label: "Non", + "description": "Je suis en formation classique", + value: "non" + } +] \ No newline at end of file diff --git a/iut_nc_depinfo/slashs/.gitignore b/iut_nc_depinfo/slashs/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/iut_nc_depinfo/slashs/Divers.ts b/iut_nc_depinfo/slashs/Divers.ts new file mode 100644 index 0000000..acfeaee --- /dev/null +++ b/iut_nc_depinfo/slashs/Divers.ts @@ -0,0 +1,52 @@ +import { CommandInteraction, MessageEmbed } from 'discord.js'; +import { Discord, Guild, Slash } from 'discordx'; +import { Serveur } from '../../IDs'; +import { Role } from '../IDs'; + +@Discord() +@Guild(Serveur.IUT_NC_DEP_INFO) +abstract class Divers { + + @Slash('info', { description: "Affiche les informations du serveur" }) + private async info(interaction: CommandInteraction) { + + await interaction.deferReply(); + + const guild = interaction.guild; + + const resEmbed = new MessageEmbed() + .setColor('#DD131E') + .setTitle('Informations du Serveur') + .addFields( + { + name: 'Membres', + value: ` + <@&${Role.ADMIN}> : \`${guild?.roles.resolve(Role.ADMIN)?.members.size}\` + <@&${Role.SERVER_BOOSTER}> : \`${guild?.roles.resolve(Role.SERVER_BOOSTER)?.members.size}\` + <@&${Role.ENSEIGNANT}> : \`${guild?.roles.resolve(Role.ENSEIGNANT)?.members.size}\` + <@&${Role.DÉLÉGUÉ}> : \`${guild?.roles.resolve(Role.DÉLÉGUÉ)?.members.size}\` + Ancien Étudiant : \`${(guild?.roles.resolve(Role.ANCIEN_DUT)?.members.size || 0) + (guild?.roles.resolve(Role.ANCIEN_LP_CIASIE)?.members.size || 0)}\` + <@&${Role.ÉTUDIANT}> : \`${guild?.roles.resolve(Role.ÉTUDIANT)?.members.size}\` + <@&${Role.COMPTE_SECONDAIRE}> : \`${guild?.roles.resolve(Role.COMPTE_SECONDAIRE)?.members.size}\` + Total : \`${guild?.memberCount}\` + `, + inline: true + }, + { + name: 'Promos', + value: ` + <@&${Role.LP_ACORS}> : \`${guild?.roles.resolve(Role.LP_ACORS)?.members.size}\` + <@&${Role.LP_AFTER}> : \`${guild?.roles.resolve(Role.LP_AFTER)?.members.size}\` + <@&${Role.LP_CIASIE}> : \`${guild?.roles.resolve(Role.LP_CIASIE)?.members.size}\` + <@&${Role.BUT_1A}> : \`${guild?.roles.resolve(Role.BUT_1A)?.members.size}\` + <@&${Role.DUT_2A}> : \`${guild?.roles.resolve(Role.DUT_2A)?.members.size}\` + `, + inline: true + }, + { name: 'Nombre de salons', value: `${guild?.channels.cache.size}/500` } + ) + .setTimestamp(); + + interaction.editReply({ embeds: [resEmbed] }) + } +} \ No newline at end of file diff --git a/iut_nc_depinfo/slashs/Maintenance.ts b/iut_nc_depinfo/slashs/Maintenance.ts new file mode 100644 index 0000000..8701007 --- /dev/null +++ b/iut_nc_depinfo/slashs/Maintenance.ts @@ -0,0 +1,117 @@ +import { CategoryChannel, Channel, CommandInteraction } from "discord.js"; +import { DefaultPermission, Discord, Guild, Permission, Slash, SlashGroup, SlashOption } from "discordx"; +import { Serveur } from "../../IDs"; +import { Role } from "../IDs"; + +@Discord() +@Guild(Serveur.IUT_NC_DEP_INFO) +@DefaultPermission(false) +@Permission({ id: Role.ADMIN, type: 'ROLE', permission: true }) +@Permission({ id: Role.STAR, type: 'ROLE', permission: true }) +@SlashGroup("maintenance", "Commandes de maintenance du serveur") +abstract class Maintenance { + + @Slash("setupCategorieScolaire", { description: "Créé les salons basiques communs des catégories scolaires" }) + async setupCategorieScolaire( + @SlashOption("categorie", { description: "ID de la catégorie à affecter", required: true, type: "CHANNEL" }) + categoryParam: Channel, + @SlashOption("createAnnonces", { description: "Voulez-vous créer le salon '📢・annonces' ?" }) + createAnnonces: boolean = false, + @SlashOption("createDocuments", { description: "Voulez-vous créer le salon '📚・documents' ?" }) + createDocuments: boolean = false, + @SlashOption("createOffreDeStage", { description: "Voulez-vous créer le salon '📬・offres-de-stage' ?" }) + createOffreDeStage: boolean = false, + interaction: CommandInteraction + ) { + await interaction.deferReply({ ephemeral: true }); + + if (interaction.channel?.type === "DM") { interaction.editReply({ content: "❌ Désolé mais je ne peux pas effectuer cette commande en message privé." }); return; } + + if (categoryParam?.type !== "GUILD_CATEGORY") { interaction.editReply({ content: "❌ Cela ne s'agit pas d'une catégorie." }); return; } + + interaction.editReply({ content: `Salons en cours de création...` }); + + const guild = interaction.guild; + const category = categoryParam; + + if (createAnnonces) { + const annoncementChannel = await guild?.channels.create("📢・annonces", { + type: "GUILD_TEXT", + topic: "Annonces scolaires concernant la classe ou la promo", + parent: category, + reason: `Création du salon demandé par ${interaction.user.username} via la commande 'setupCategorieScolaire'` + }); + + await annoncementChannel?.permissionOverwrites.edit(Role.EVERYONE, { "SEND_MESSAGES": false }); + await annoncementChannel?.permissionOverwrites.edit(Role.DÉLÉGUÉ, { "SEND_MESSAGES": true }); + await annoncementChannel?.permissionOverwrites.edit(Role.ENSEIGNANT, { "SEND_MESSAGES": true }); + } + + if (createDocuments) { + const documentChannel = await guild?.channels.create("📚・documents", { + type: "GUILD_TEXT", + topic: "Documents scolaires concernant la classe ou la promo", + parent: category, + reason: `Création du salon demandé par ${interaction.user.username} via la commande 'setupCategorieScolaire'` + }); + + await documentChannel?.permissionOverwrites.edit(Role.EVERYONE, { "SEND_MESSAGES": false }); + await documentChannel?.permissionOverwrites.edit(Role.DÉLÉGUÉ, { "SEND_MESSAGES": true }); + await documentChannel?.permissionOverwrites.edit(Role.ENSEIGNANT, { "SEND_MESSAGES": true }); + } + + if (createOffreDeStage) { + const offreDeStageChannel = await guild?.channels.create("📬・offres-de-stage", { + type: "GUILD_TEXT", + topic: "Offre de stages trouvées par des enseignants ou publiés par des professionnels", + parent: category, + reason: `Création du salon demandé par ${interaction.user.username} via la commande 'setupCategorieScolaire'` + }); + + await offreDeStageChannel?.permissionOverwrites.edit(Role.EVERYONE, { "SEND_MESSAGES": false }); + await offreDeStageChannel?.permissionOverwrites.edit(Role.DÉLÉGUÉ, { "SEND_MESSAGES": true }); + await offreDeStageChannel?.permissionOverwrites.edit(Role.ENSEIGNANT, { "SEND_MESSAGES": true }); + } + + const discussionsChannel = await guild?.channels.create("💬・discussions", { + type: "GUILD_TEXT", + topic: "Salon de discussions de la classe ou de la promo. N'hésitez pas à démarrer des fils de discussions s'il s'agit d'un sujet spécifique comme un cours en particulier !", + parent: category, + reason: `Création du salon demandé par ${interaction.user.username} via la commande 'setupCategorieScolaire'` + }); + + discussionsChannel?.edit({ + defaultAutoArchiveDuration: + (guild?.features.includes("SEVEN_DAY_THREAD_ARCHIVE") ? 10080 : + (guild?.features.includes("THREE_DAY_THREAD_ARCHIVE") ? 4320 : + 1440) + ) + }); + + const amphiChannel = await guild?.channels.create("🎤・Amphithéâtre", { + type: "GUILD_STAGE_VOICE", + topic: "Conférence réservé à la classe ou à la promo. Les partages d'écrans ne sont pour l'instant pas disponibles", + parent: category, + reason: `Création du salon demandé par ${interaction.user.username} via la commande 'setupCategorieScolaire'` + }); + + await amphiChannel?.permissionOverwrites.edit(Role.ENSEIGNANT, { "MANAGE_CHANNELS": true, "MUTE_MEMBERS": true, "MOVE_MEMBERS": true }); + await amphiChannel?.permissionOverwrites.edit(Role.DÉLÉGUÉ, { "MANAGE_CHANNELS": true, "MUTE_MEMBERS": true, "MOVE_MEMBERS": true }); + + await guild?.channels.create("💻・Vocal #1", { + type: "GUILD_VOICE", + topic: "Salon vocal réservé à la classe ou à la promo", + parent: category, + reason: `Création du salon demandé par ${interaction.user.username} via la commande 'setupCategorieScolaire'` + }); + + await guild?.channels.create("💻・Vocal #2", { + type: "GUILD_VOICE", + topic: "Salon vocal réservé à la classe ou à la promo", + parent: category, + reason: `Création du salon demandé par ${interaction.user.username} via la commande 'setupCategorieScolaire'` + }); + + interaction.editReply({ content: `Les salons ont été créés !` }); + } +} \ No newline at end of file diff --git a/iut_nc_depinfo/slashs/MessagesSelectionRoles.ts b/iut_nc_depinfo/slashs/MessagesSelectionRoles.ts new file mode 100644 index 0000000..3729386 --- /dev/null +++ b/iut_nc_depinfo/slashs/MessagesSelectionRoles.ts @@ -0,0 +1,102 @@ +import { CommandInteraction, MessageActionRow, MessageSelectMenu } from 'discord.js'; +import { DefaultPermission, Discord, Guild as Guildx, Permission, Slash, SlashGroup } from 'discordx'; +import { Serveur } from "../../IDs"; +import { Role } from '../IDs'; +import { RoleAlternantCIASIEOptions, RolesAncienOptions, RolesAutreOptions, RolesBUT1AOptions, RolesDUT2AOptions, RolesLPOptions } from '../selectMenus/Roles'; + +@Discord() +@Guildx(Serveur.IUT_NC_DEP_INFO) // Alias @Guild due to import name conflict +@DefaultPermission(false) +@Permission({ id: Role.ADMIN, type: 'ROLE', permission: true }) +@Permission({ id: Role.STAR, type: 'ROLE', permission: true }) +@SlashGroup("messageSelectionRoles", "Commandes ayant pour effet d'envoyer des messages permettant de choisir des rôles") +abstract class MessagesSelectionRoles { + + @Slash('principaux', { description: "Envoie le message permettant d'obtenir les rôles principaux" }) + async principaux(interaction: CommandInteraction) { + await interaction.deferReply({ ephemeral: true }); + + const channel = interaction.channel; + + if (!channel) { interaction.editReply({ content: "Erreur, channel: " + channel }); return; } + + if (!channel.isText()) interaction.reply("Type de salon inattendu."); + + const LPRow = new MessageActionRow() + .addComponents( + new MessageSelectMenu() + .setCustomId('role-lp') + .setPlaceholder("Licences Professionnelles") + .addOptions(RolesLPOptions) + ); + + const BUT1ARow = new MessageActionRow() + .addComponents( + new MessageSelectMenu() + .setCustomId('role-but-1a') + .setPlaceholder("BUT 1ère Année") + .addOptions(RolesBUT1AOptions) + ); + + const DUT2ARow = new MessageActionRow() + .addComponents( + new MessageSelectMenu() + .setCustomId('role-dut-2a') + .setPlaceholder("DUT 2ème Année") + .addOptions(RolesDUT2AOptions) + ); + + const AutreRow = new MessageActionRow() + .addComponents( + new MessageSelectMenu() + .setCustomId('role-other') + .setPlaceholder("Autre") + .addOptions(RolesAutreOptions) + ); + + const OldStudentRow = new MessageActionRow() + .addComponents( + new MessageSelectMenu() + .setCustomId('role-old-student') + .setPlaceholder("Ancien étudiant") + .addOptions(RolesAncienOptions) + ); + + try { + await channel.send({ content: "Bienvenue sur le Serveur du Département Informatique de l'IUT Nancy-Charlemagne ! Veuillez sélectionner ce qui vous correspondant via le menu juste en dessous.", components: [LPRow, BUT1ARow, DUT2ARow, AutreRow, OldStudentRow] }); + } catch (err) { + interaction.editReply({ content: "Une erreur est survenue." }); + console.error(err) + } finally { + interaction.editReply({ content: "Done." }); + } + } + + @Slash('alternantLP', { description: "Envoie le message permettant d'obtenir le rôle alternant pour la LP CIASIE" }) + async messageRoleAlternant(interaction: CommandInteraction) { + await interaction.deferReply({ ephemeral: true }); + + const channel = interaction.channel; + + if (!channel) { interaction.editReply({ content: "Erreur, channel: " + channel }); return; } + + if (!channel.isText()) interaction.reply("Type de salon inattendu."); + + const row = new MessageActionRow() + .addComponents( + new MessageSelectMenu() + .setCustomId('role-alternant') + .setPlaceholder("Veuillez sélectionner une réponse") + .addOptions(RoleAlternantCIASIEOptions) + ); + + try { + await channel.send({ content: "Êtes-vous en alternance ? Si oui, vous pouvez obtenir un rôle spécial afin de faciliter les mentions et avoir un salon regroupant les alternants.", components: [row] }); + } catch (err) { + interaction.editReply({ content: "Une erreur est survenue." }); + console.log(err) + } finally { + interaction.editReply({ content: "Done." }); + } + } +} \ No newline at end of file diff --git a/libs/pfc.ts b/libs/pfc.ts new file mode 100644 index 0000000..9d6158c --- /dev/null +++ b/libs/pfc.ts @@ -0,0 +1,116 @@ +import { randomInt } from "crypto"; +import { EmojiIdentifierResolvable, MessageButton, User } from "discord.js"; + +export enum pfcChoix { + Pierre = "Pierre", + Feuille = "Feuille", + Ciseaux = "Ciseaux", + Puit = "Puit" +} + +export enum pfcEmoji { + Pierre = "🪨", + Feuille = "🧻", + Ciseaux = "✂️", + Puit = "❓" +} + +export enum pfcButtonId { + Pierre = "pfc-pierre", + Feuille = "pfc-feuille", + Ciseaux = "pfc-ciseaux", + Puit = "pfc-puit" +} + +export const pfcButtons: MessageButton[] = [ + new MessageButton() + .setLabel(pfcChoix.Pierre) + .setEmoji(pfcEmoji.Pierre) + .setStyle('PRIMARY') + .setCustomId(pfcButtonId.Pierre), + new MessageButton() + .setLabel(pfcChoix.Feuille) + .setEmoji(pfcEmoji.Feuille) + .setStyle('PRIMARY') + .setCustomId(pfcButtonId.Feuille), + new MessageButton() + .setLabel(pfcChoix.Ciseaux) + .setEmoji(pfcEmoji.Ciseaux) + .setStyle('PRIMARY') + .setCustomId(pfcButtonId.Ciseaux), + new MessageButton() + .setLabel(pfcChoix.Puit) + .setEmoji(pfcEmoji.Puit) + .setStyle('PRIMARY') + .setCustomId(pfcButtonId.Puit) +]; + +export enum pfcResultat { + GAGNÉ, + PERDU, + ÉGALITÉ +} + +export class pfcProposition { + public nom: pfcChoix; + public emoji: EmojiIdentifierResolvable; + public buttonCustomId: pfcButtonId; + + public static propositions = [ + new pfcProposition(pfcChoix.Pierre, pfcEmoji.Pierre, pfcButtonId.Pierre), + new pfcProposition(pfcChoix.Feuille, pfcEmoji.Feuille, pfcButtonId.Feuille), + new pfcProposition(pfcChoix.Ciseaux, pfcEmoji.Ciseaux, pfcButtonId.Ciseaux) + ] + + constructor(nom: pfcChoix, emoji: EmojiIdentifierResolvable, buttonCustomId: pfcButtonId) { + this.nom = nom; + this.emoji = emoji; + this.buttonCustomId = buttonCustomId; + } + + public static nameToClass(nom: string): pfcProposition | undefined { + return this.propositions.find(proposition => nom === proposition.nom); + } + + public static buttonCustomIdToClass(buttonCustomId: string): pfcProposition | undefined { + return this.propositions.find(proposition => buttonCustomId === proposition.buttonCustomId); + } + + public toString() { + return `${this.emoji} ${this.nom}`; + } +} + +export function pfcIsWin(choixJoueur: pfcProposition, choixBot: pfcProposition): pfcResultat | undefined { + if (choixBot.nom === choixJoueur.nom) return pfcResultat.ÉGALITÉ; + + switch (choixJoueur.nom) { + case pfcChoix.Pierre: + if (choixBot.nom === pfcChoix.Ciseaux) return pfcResultat.GAGNÉ; + if (choixBot.nom === pfcChoix.Feuille) return pfcResultat.PERDU; + break; + case pfcChoix.Feuille: + if (choixBot.nom === pfcChoix.Pierre) return pfcResultat.GAGNÉ; + if (choixBot.nom === pfcChoix.Ciseaux) return pfcResultat.PERDU; + break; + case pfcChoix.Ciseaux: + if (choixBot.nom === pfcChoix.Feuille) return pfcResultat.GAGNÉ; + if (choixBot.nom === pfcChoix.Pierre) return pfcResultat.PERDU; + break; + } +} + +export function pfcPlayBot(): pfcProposition { + return pfcProposition.propositions[randomInt(pfcProposition.propositions.length)]; +} + +export function pfcTraitementResultat(choix: pfcProposition, choixBot: pfcProposition, resultat: pfcResultat, user: User) { + switch (resultat) { + case pfcResultat.GAGNÉ: + return { content: `(${user}) ${choixBot} ! Well, noob ${choix} need nerf plz...` }; + case pfcResultat.PERDU: + return { content: `(${user}) ${choixBot} ! GG NO RE, EZ !` }; + case pfcResultat.ÉGALITÉ: + return { content: `(${user}) ${choixBot} ! Ha... Égalité...` }; + } +} \ No newline at end of file diff --git a/util.ts b/libs/util.ts similarity index 86% rename from util.ts rename to libs/util.ts index c35e0a4..95ad7a3 100644 --- a/util.ts +++ b/libs/util.ts @@ -8,7 +8,7 @@ export function getHorodateConsole() { } export function logInteraction(interaction: Interaction, client: Client) { - let log = `${getHorodateConsole()}\t${interaction.user.username}\t${interaction.type}` + let log = `${getHorodateConsole()}\t${interaction.guild?.name}\t${interaction.user.username}\t${interaction.type}` if (interaction.isCommand()) log += `\t${interaction.commandName}`; @@ -21,6 +21,8 @@ export function logInteraction(interaction: Interaction, client: Client) { } if (interaction.isContextMenu()) { + log += `\t${interaction.commandName}`; + if (interaction.targetType === "USER") log += `\t${client.users.resolve(interaction.targetId)?.username}`; else if (interaction.targetType === "MESSAGE") diff --git a/package-lock.json b/package-lock.json index a07e55a..028fa8d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "bot-discord-iut-nancy-charlemagne", - "version": "1.1.1", + "version": "2.0.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "bot-discord-iut-nancy-charlemagne", - "version": "1.1.1", + "version": "2.0.0", "license": "MIT", "dependencies": { "discord.js": "^13.1.0", @@ -131,9 +131,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "16.9.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.9.1.tgz", - "integrity": "sha512-QpLcX9ZSsq3YYUUnD3nFDY8H7wctAhQj/TFKL8Ya8v5fMm3CFXxo8zStsLAl780ltoYoo1WvKUVGBQK+1ifr7g==" + "version": "16.9.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.9.2.tgz", + "integrity": "sha512-ZHty/hKoOLZvSz6BtP1g7tc7nUeJhoCf3flLjh8ZEv1vFKBWHXcnMbJMyN/pftSljNyy0kNW/UqI3DccnBnZ8w==" }, "node_modules/@types/twit": { "version": "2.2.30", @@ -352,9 +352,9 @@ } }, "node_modules/discordx": { - "version": "5.9.1", - "resolved": "https://registry.npmjs.org/discordx/-/discordx-5.9.1.tgz", - "integrity": "sha512-hZS4k2kbRFaZ9oGa9qEFPX7hcBitmBQ8CEeT1uNQHf4TJUmGwgRV02MIRJKCKkIpDn7CTMAE0WdG6q+rUtO6Xg==", + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/discordx/-/discordx-5.9.3.tgz", + "integrity": "sha512-X1lZO6z0EH7tn3Mg/68Jc0Qe6bVHllgSDEmd4ftQ8iSwigRMHloqAOnoj8wI/aSVex3KIDWlbSm2TRlkv7cEOw==", "dependencies": { "discord.js": "^13.1.0", "glob": "^7.1.7", @@ -774,11 +774,6 @@ "safer-buffer": "^2.0.2", "tweetnacl": "~0.14.0" }, - "bin": { - "sshpk-conv": "bin/sshpk-conv", - "sshpk-sign": "bin/sshpk-sign", - "sshpk-verify": "bin/sshpk-verify" - }, "engines": { "node": ">=0.10.0" } @@ -1059,9 +1054,9 @@ "dev": true }, "@types/node": { - "version": "16.9.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.9.1.tgz", - "integrity": "sha512-QpLcX9ZSsq3YYUUnD3nFDY8H7wctAhQj/TFKL8Ya8v5fMm3CFXxo8zStsLAl780ltoYoo1WvKUVGBQK+1ifr7g==" + "version": "16.9.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.9.2.tgz", + "integrity": "sha512-ZHty/hKoOLZvSz6BtP1g7tc7nUeJhoCf3flLjh8ZEv1vFKBWHXcnMbJMyN/pftSljNyy0kNW/UqI3DccnBnZ8w==" }, "@types/twit": { "version": "2.2.30", @@ -1239,9 +1234,9 @@ } }, "discordx": { - "version": "5.9.1", - "resolved": "https://registry.npmjs.org/discordx/-/discordx-5.9.1.tgz", - "integrity": "sha512-hZS4k2kbRFaZ9oGa9qEFPX7hcBitmBQ8CEeT1uNQHf4TJUmGwgRV02MIRJKCKkIpDn7CTMAE0WdG6q+rUtO6Xg==", + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/discordx/-/discordx-5.9.3.tgz", + "integrity": "sha512-X1lZO6z0EH7tn3Mg/68Jc0Qe6bVHllgSDEmd4ftQ8iSwigRMHloqAOnoj8wI/aSVex3KIDWlbSm2TRlkv7cEOw==", "requires": { "discord.js": "^13.1.0", "glob": "^7.1.7", diff --git a/package.json b/package.json index d1b5d18..543fb05 100644 --- a/package.json +++ b/package.json @@ -1,11 +1,13 @@ { "name": "bot-discord-iut-nancy-charlemagne", - "version": "1.1.1", + "version": "2.0.0", "description": "", "main": "index.ts", "scripts": { "start": "./scripts/main_bot.bash", - "twitter": "./scripts/twitter_bot.bash" + "twitter": "./scripts/twitter_bot.bash", + "debug": "ts-node .", + "debug twitter": "ts-node twitter.ts" }, "repository": { "type": "git", diff --git a/twitter.ts b/twitter.ts index 20897cd..1c27413 100644 --- a/twitter.ts +++ b/twitter.ts @@ -2,7 +2,7 @@ require('dotenv').config() import { Client } from 'discord.js'; import * as fs from 'fs'; import * as Twit from 'twit'; -import { getHorodateConsole, resetPresence } from './util'; +import { getHorodateConsole, resetPresence } from './libs/util'; export let SingletonClient: Client; @@ -14,10 +14,10 @@ async function start() { consumer_secret: process.env.CONSUMER_SECRET ?? "", access_token: process.env.ACCESS_TOKEN ?? "", access_token_secret: process.env.ACCESS_TOKEN_SECRET ?? "" - }), SingletonClient); + }), SingletonClient, ["global"]); SingletonClient.on("ready", () => { - console.log(`${getHorodateConsole()}\tReady !`); + console.log(`${getHorodateConsole()}\t[INFO]\tReady !`); if (SingletonClient.user) resetPresence(SingletonClient.user); @@ -28,30 +28,34 @@ async function start() { -function loadTwitterEvents(Twitter: Twit, client: Client) { - const eventFiles = fs.readdirSync('./events/twitter').filter(file => file.endsWith('.ts')); +function loadTwitterEvents(Twitter: Twit, client: Client, dirs: string[]) { + dirs.forEach(dir => { + const eventFiles = fs.readdirSync(`./${dir}/events/twitter`).filter(file => file.endsWith('.ts')); - for (const file of eventFiles) { - const event = require(`./events/twitter/${file}`); - let stream; + for (const file of eventFiles) { + const event = require(`./${dir}/events/twitter/${file}`); + let stream; - try { - stream = Twitter.stream(event.endPoint, event.options); - } catch (err) { - console.log(err); - } + try { + stream = Twitter.stream(event.endPoint, event.options); + } catch (err) { + console.log(err); + } + + if (!stream) { console.error(`${getHorodateConsole()}\t[FAIL LOAD]\t${dir} ${file}, stream = ${stream}`); return; } - if (!stream) { console.error(`[FAIL LOAD] ${file}, stream = ${stream}`); return; } + stream.on(event.name, (...args) => event.execute(...args, client)); - stream.on(event.name, (...args) => event.execute(...args, client)); - } + console.log(`${getHorodateConsole()}\t[LOADED]\t${dir} ${file}`); + } + }) } function handleExit(signal: NodeJS.Signals) { - console.info(`${getHorodateConsole()} Signal ${signal} reçu.`); + console.info(`${getHorodateConsole()}\t[STOP]\tSignal ${signal} reçu.`); SingletonClient.user?.setPresence({ status: "idle", activities: [{ name: "Arrêt en cours", type: "COMPETING" }] }) SingletonClient.destroy(); - console.log(`${getHorodateConsole()} Arrêt du bot.`); + console.log(`${getHorodateConsole()}\t[STOP]\tArrêt du bot.`); process.exit(0); }