const client = global.discord_client const pool = global.db_pool; const crypto = require('crypto'); const fs = require('fs'); const { pipeline } = require('stream'); const { promisify } = require('util'); const streamPipeline = promisify(pipeline); /** * Downloads a file from a URL to a specified destination. * @param {string} url - The URL to download the file from. * @param {string} dest - The destination file path to save the file. * @param {function} cb - Callback function called on completion or error. */ async function download(url, dest, cb) { try { const response = await fetch(url); if (!response.ok) { throw new Error(`Failed to fetch ${url}: ${response.statusText}`); } await streamPipeline(response.body, fs.createWriteStream(dest)); cb(null); // Signal success } catch (error) { cb(error); // Pass error to callback } } const cancel = (user) => { delete global.productCreationData[user.id]; delete global.dmHandlers[user.id]; user.send('Product creation cancelled.'); } const execute = async (message) => { if (!interaction.guild) return interaction.reply({ content: "This command must be used in a server!", ephemeral: true }); switch (global.productCreationData[message.author.id].step) { case 1: // Description const description = message.content.trim(); if (description.toLowerCase() === 'cancel') { cancel(message.author); return; } global.productCreationData[message.author.id].description = description; global.productCreationData[message.author.id].step = 2; message.channel.send('Description set. Please provide the dev product ID. Say `cancel` to cancel creation.'); break; case 2: // Dev product ID const devProductId = message.content.trim(); if (devProductId.toLowerCase() === 'cancel') { cancel(message.author); return; } if (!/^\d{1,15}$/.test(newDevId)) { message.channel.send('Invalid Dev Product ID. It must be a number between 1 and 15 digits.'); return; } global.productCreationData[message.author.id].devProductId = devProductId; global.productCreationData[message.author.id].step = 3; message.channel.send('Dev product ID set. Please provide the decal ID. Say `skip` to not set one, or `cancel` to cancel creation.'); break; case 3: // Image ID const imageId = message.content.trim(); if (imageId.toLowerCase() === 'cancel') { cancel(message.author); return; } if (imageId.toLowerCase() === "skip") { global.productCreationData[message.author.id].imageId = null; global.productCreationData[message.author.id].step = 4; message.channel.send('Image ID skipped. Please upload the product file. Say `cancel` to cancel creation.'); return; } if (!/^\d{1,15}$/.test(newImageId)) { message.channel.send('Invalid Image ID. It must be a number between 1 and 15 digits.'); return; } global.productCreationData[message.author.id].imageId = imageId; global.productCreationData[message.author.id].step = 4; message.channel.send('Image ID set. Please upload the product file.'); break; case 4: // File if (message.content.trim().toLowerCase() === 'cancel') { cancel(message.author); return; } if (message.attachments.size > 0) { const attachment = message.attachments.first(); const validExtensions = ['zip', 'rbxm', 'rbxmx']; const fileExtension = attachment.name.split('.').pop().toLowerCase(); if (!validExtensions.includes(fileExtension)) { message.channel.send('Invalid file type. Only zip, rbxm, and rbxmx files are allowed.'); cancel(message.author); return; } const file = fs.createWriteStream(`./productFiles/${crypto.randomBytes(8).toString('hex')}`); const prodId = crypto.randomUUID(); download(attachment.url, file.path, (err) => { if (err) { message.channel.send('Failed to download the file.'); cancel(message.author); return; } global.productCreationData[message.author.id].filePath = file.path.split('/').pop(); let fileType = attachment.name.split('.').pop(); global.productCreationData[message.author.id].step = 5; message.channel.send('File uploaded successfully. Product creation complete.'); pool.query(`INSERT INTO products (id, name, description, devProductId, decalId, file, fileType, hubId) VALUES (?, ?, ?, ?, ?, ?, ?, ?);`, [prodId, global.productCreationData[message.author.id].name, global.productCreationData[message.author.id].description, global.productCreationData[message.author.id].devProductId, global.productCreationData[message.author.id].imageId, global.productCreationData[message.author.id].filePath, fileType, global.productCreationData[message.author.id].hub], (err) => { if (err) { console.error(err); message.channel.send('An error occurred while creating the product.'); cancel(message.author); return; } message.author.send('Product created successfully.'); delete global.productCreationData[message.author.id]; delete global.dmHandlers[message.author.id]; }); }); } else { message.channel.send('No file attached. Please upload the product file.'); } break; default: message.channel.send('Invalid step.'); cancel(message.author); break; } } module.exports = execute