const { SlashCommandBuilder, AttachmentBuilder, ChannelFlagsBitField, EmbedBuilder } = require('discord.js') const fs = require('fs') const path = require('path'); const Character = require('../../database/models/characters'); const searchEngine = require('../../systems/storage/searchEngine'); const Item = require('../../database/models/items'); const config = require('../../config.json'); const extractConfig = require('../../systems/storage/extractor'); const { addItem, removeItem } = require('../../systems/inventory/invManager'); const { promises } = require('fs') const { join } = require('path') const { createCanvas, loadImage } = require('@napi-rs/canvas') module.exports = { data: new SlashCommandBuilder() .setName('admin-inventaire') .setDescription('Gestion des inventaires') .addSubcommandGroup(subcommandGroup => subcommandGroup .setName('afficher') .setDescription("Afficher un objet ou un inventaire") .addSubcommand(subcommand => subcommand.setName('objet') .setDescription("Afficher un objet spécifique dans l'inventaire de quelqu'un") .addStringOption(option => option.setName('personnage') .setDescription("Votre personnage") .setAutocomplete(true) .setRequired(true)) .addStringOption(option => option.setName('objet') .setDescription("L'objet recherché") .setAutocomplete(true) .setRequired(true))) .addSubcommand(subcommand => subcommand.setName('entier') .setDescription("Afficher l'inventaire d'un joueur") .addStringOption(option => option.setName('personnage') .setDescription("Votre personnage") .setAutocomplete(true) .setRequired(true)))) .addSubcommandGroup(subcommandGroup => subcommandGroup.setName('objet') .setDescription('Gérer les objets') .addSubcommand(subcommand => subcommand.setName('ajouter') .setDescription('Ajouter un objet dans l\'inventaire de quelqu\'un') .addStringOption(option => option.setName('personnage') .setDescription("Votre personnage") .setAutocomplete(true) .setRequired(true)) .addStringOption(option => option.setName('objet-libre') .setDescription("L'objet a ajouter dans l'inventaire") .setAutocomplete(true) .setRequired(true))) .addSubcommand(subcommand => subcommand.setName('retirer') .setDescription("Retirer un objet de l'inventaire de quelqu'un, le rendant sans propriétaire") .addStringOption(option => option.setName('personnage') .setDescription("Votre personnage") .setAutocomplete(true) .setRequired(true)) .addStringOption(option => option.setName('objet') .setDescription("L'objet a retirer") .setAutocomplete(true) .setRequired(true))) ) , async execute(interaction) { if (interaction.options.getSubcommand() === 'ajouter') { const characterId = await interaction.options.getString('personnage') const itemToAdd = await interaction.options.getString('objet-libre') let character = await Character.findOne({where: {id: characterId}}) // Vérifie si le personnage existe if (character == null) { const errorEmbed = { title: '❌ Impossible de trouver le personnage', description: `L'identifiant \`${characterId}\` est peut être invalide ou le personnage n'existe pas !`, color: config.embeds.error } return interaction.reply({embeds: [errorEmbed], ephemeral: true}) } character.inventory = JSON.parse(character.inventory) let item = await Item.findOne({where: {id: itemToAdd}}) // Vérifie si l'objet existe if (item == null) { const errorEmbed = { title: '❌ Impossible de trouver l\'item !', description: `L'identifiant \`${itemToAdd}\` est peut être invalide ou l'item n'existe pas !`, color: config.embeds.error } return interaction.reply({embeds: [errorEmbed], ephemeral: true}) } item.characteristics = JSON.parse(item.characteristics) // Vérifie si l'objet appartient à quelqu'un if (item.owner != null) { const errorEmbed = { title: "❌ L'item appartient déjà à quelqu'un !", color: config.embeds.error } return interaction.reply({embeds: [errorEmbed], ephemeral: true}) } // Cherche la config de l'objet const itemddb = await (async() => { const query = await extractConfig('items') const search = query.find(i => i.id === item.type) if (search == null) return null; else { return search } })() // Vérifie si l'objet n'est pas trop lourd if (itemddb.weight + character.inventory.occupied > character.inventory.capacity) { const errorEmbed = { title: "❌ L'objet est trop lourd pour pouvoir être ajouté à l'inventaire de" + character.name + ".", color: config.embeds.error } return interaction.reply({embeds: [errorEmbed], ephemeral: true}) } await addItem(character.id, item.id) const successEmbed = { title: `✅ L'item a été ajouté à l'inventaire de ${character.name} avec succès !`, color: config.embeds.classic } await interaction.reply({embeds: [successEmbed], ephemeral: true}) } if (interaction.options.getSubcommand() === 'retirer') { const characterId = interaction.options.getString('personnage') const itemToRemove = interaction.options.getString('objet') let character = await Character.findOne({where: {id: characterId}}) character.inventory = JSON.parse(character.inventory) // Vérifie si le personnage existe if (character == null) { const errorEmbed = { title: '❌ Impossible de trouver le personnage', description: `L'identifiant \`${characterId}\` est peut être invalide ou le personnage n'existe pas !`, color: config.embeds.error } return interaction.reply({embeds: [errorEmbed], ephemeral: true}) } // Vérifie si l'objet existe const item = await Item.findOne({where: {id: itemToRemove}}) if (item == null) { const errorEmbed = { title: '❌ Impossible de trouver l\'item !', description: `L'identifiant \`${itemToRemove}\` est peut être invalide ou l'item n'existe pas !`, color: config.embeds.error } return interaction.reply({embeds: [errorEmbed], ephemeral: true}) } await removeItem(characterId, itemToRemove) const successEmbed = { title: `✅ L'item a été retiré de l'inventaire de ${character.name} avec succès !`, color: config.embeds.classic } await interaction.reply({embeds: [successEmbed], ephemeral: true}) } if (interaction.options.getSubcommand() === 'objet') { const characterId = interaction.options.getString('personnage') const itemId = interaction.options.getString('objet') let character = await Character.findOne({where: {id: characterId}}) character.characteristics = JSON.parse(character.characteristics) // Vérifie si le personnage existe if (character == null) { const errorEmbed = { title: '❌ Impossible de trouver le personnage', description: `L'identifiant \`${characterId}\` est peut être invalide ou le personnage n'existe pas !`, color: config.embeds.error } return interaction.reply({embeds: [errorEmbed], ephemeral: true}) } // Vérifie si l'objet existe let item = await Item.findOne({where: {id: itemId}}) if (item == null) { const errorEmbed = { title: '❌ Impossible de trouver l\'item !', description: `L'identifiant \`${itemId}\` est peut être invalide ou l'item n'existe pas !`, color: config.embeds.error } return interaction.reply({embeds: [errorEmbed], ephemeral: true}) } item.characteristics = JSON.parse(item.characteristics) const itemConfig = await searchEngine.getItemInfos(item.type) const description = (() => { if (itemConfig?.description == undefined || itemConfig.description == '') { return '*Aucune description n\'a été définie*' } else { return itemConfig.description }})() let respondEmbed = { title: `${itemConfig.fullName} appartenant à ${character.name}`, color: config.embeds.classic, author: { name: character.name, icon_url: character.characteristics.avatar }, thumbnail: { url: itemConfig.avatarLink }, fields: [ { name: 'Informations', value: `**Nom:** ${itemConfig.fullName}\n**État:** ${item.status}%\n**Poids:** ${itemConfig.weight} kg\n**Identifiant:** \`${itemId}\`\n**Utilisabilité:** ${itemConfig.usable ? "Cet objet est utilisable" : "Cet objet n'est pas utilisable"}`, inline: true, }, { name: 'Description', value: description, inline: true } ] } if (itemConfig.type === 'food') { respondEmbed.fields.push({name: 'Description spécifique', value: `**Regain de nourriture moyen:** ${(itemConfig.feed > 0) ? `${itemConfig.feed}%` : '*Aucun*'}\n**Regain de soif moyen:** ${(itemConfig.thirst > 0) ? `${itemConfig.thirst}%` : '*Aucun*'} **Taux de remplissage:** ${item.characteristics.filledLevel}%\n**État de l'objet:** ${item.characteristics.opened ? 'Ouvert' : 'Fermé'}\n**Maladie possible:** ${(item.characteristics.contaminated == false) ? '*Stérile*' : item.characteristics.contaminated}`}) } await interaction.reply({embeds: [respondEmbed]}) } if (interaction.options.getSubcommand() === 'entier') { const canvas = createCanvas(1700, 900) const ctx = canvas.getContext('2d') const background = await loadImage('./storage/inventory-background.png'); ctx.drawImage(background, 0, 0, canvas.width, canvas.height); const applyText = (canvas, text) => { const context = canvas.getContext('2d'); // Declare a base size of the font let fontSize = 52; do { // Assign the font to the context and decrement it so it can be measured again context.font = `${fontSize -= 8}px sans-serif`; // Compare pixel width of the text to the canvas minus the approximate avatar size } while (context.measureText(text).width > canvas.width - 200); // Return the result to use in the actual canvas return context.font; }; const applyLittleText = (canvas, text) => { const context = canvas.getContext('2d'); // Declare a base size of the font let fontSize = 32; do { // Assign the font to the context and decrement it so it can be measured again context.font = `${fontSize-= 10}px sans-serif`; // Compare pixel width of the text to the canvas minus the approximate avatar size } while (context.measureText(text).width > canvas.width - 1350); // Return the result to use in the actual canvas return context.font; }; // Using undici to make HTTP requests for better performance const avatar = await loadImage("https://i.ibb.co/WK7F2r0/Doric.png"); ctx.font = applyText(canvas, 'Michelle Jones') // Select the style that will be used to fill the text in ctx.fillStyle = '#cacccc'; ctx.textAlign = 'center'; // Actually fill the text with a solid color ctx.fillText('Michelle Jones', 840, 132); ctx.textAlign = 'left'; ctx.font = "50 px sans-serif"; ctx.fillText('0/40 kg', 1440, 105); const testImage = await loadImage("https://i.ibb.co/NZ0rHY7/Logo-canette.png") ctx.drawImage(testImage, 120, 195, 110, 110); ctx.fillStyle = '#22f222' ctx.textAlign = 'center'; ctx.font = applyLittleText(canvas, 'Canette de Coca-Cola') ctx.fillText('Canette de Coca-Cola', 180, 340); ctx.strokeRect(0, 0, canvas.width, canvas.height); // Pick up the pen ctx.beginPath(); // Start the arc to form a circle ctx.arc(100, 80, 70, 0, Math.PI * 2, true); // Put the pen down ctx.closePath(); // Clip off the region you drew on ctx.clip(); ctx.drawImage(avatar, 10, 0, 180, 180); ctx.strokeRect(0, 0, canvas.width, canvas.height); // Select the font size and type from one of the natively available fonts // If you don't care about the performance of HTTP requests, you can instead load the avatar using // const avatar = await Canvas.loadImage(interaction.user.displayAvatarURL({ extension: 'jpg' })); // Draw a shape onto the main canvas async function main() { const pngData = await canvas.encode('png') // JPEG, AVIF and WebP are also supported // encoding in libuv thread pool, non-blocking console.log(attachment) await promises.writeFile(join(__dirname, 'simple.png'), pngData) } const image = await canvas.encode('png') const attachment = new AttachmentBuilder(image, { name: 'inventaire.png' }); console.log(image) console.log(attachment) const exampleEmbed = new EmbedBuilder() .setColor(0x0099FF) .setTitle('Some title') .setURL('https://discord.js.org/') .setAuthor({ name: 'Some name', iconURL: 'https://i.imgur.com/AfFp7pu.png', url: 'https://discord.js.org' }) .setDescription('Some description here') .setThumbnail('https://i.imgur.com/AfFp7pu.png') .addFields( { name: 'Regular field title', value: 'Some value here' }, { name: '\u200B', value: '\u200B' }, { name: 'Inline field title', value: 'Some value here', inline: true }, { name: 'Inline field title', value: 'Some value here', inline: true }, ) .addFields({ name: 'Inline field title', value: 'Some value here', inline: true }) .setTimestamp() .setFooter({ text: 'Some footer text here', iconURL: 'https://i.imgur.com/AfFp7pu.png' }); await interaction.reply({embeds: [exampleEmbed], files: [{attachment: attachment.attachment, name: attachment.name}]}) } }, async autocomplete(interaction) { const focusedValue = await interaction.options.getFocused(true); if (focusedValue.name === 'personnage') { if (focusedValue.value == '') return interaction.respond([]); try { await interaction.respond(await searchEngine.search(focusedValue.value, 'characters', true)) } catch (err) {console.log(err)} } if (focusedValue.name === 'objet-libre') { if (focusedValue.value == '') return interaction.respond([]); try { const ddbQuery = await Item.findAll({where: {owner: null}}) const filteredQuery = ddbQuery.filter(key => key.id.toUpperCase().includes(focusedValue.value.toUpperCase())) const configQuery = await extractConfig('items') const choices = [] for (let v in filteredQuery) { const infos = await configQuery.find(item => item.id == filteredQuery[v].type) choices.push({name: `${infos.fullName} / ${filteredQuery[v].id}`, value: filteredQuery[v].id}) } await interaction.respond(choices) } catch (err) {console.log(err)} } if (focusedValue.name === 'objet') { const character = await interaction.options.getString('personnage') if (character == null) { return interaction.respond([]) } try { const ddbQuery = await Item.findAll({where: {owner: character}}) const filteredQuery = ddbQuery.filter(key => (key.id.toUpperCase().includes(focusedValue.value.toUpperCase())) ) const configQuery = await extractConfig('items') const choices = [] for (let v in filteredQuery) { const infos = await configQuery.find(item => item.id == filteredQuery[v].type) choices.push({name: `${infos.fullName} / ${filteredQuery[v].id}`, value: filteredQuery[v].id}) } await interaction.respond(choices) } catch (err) {console.log(err)} } } };