const { ButtonBuilder: ButtonBuilder, ButtonStyle: ButtonStyle, ActionRowBuilder: ActionRowBuilder } = require("discord.js"), styleChoicesArr = ["Abstract", "Realistic", "Cartoonish", "Sketch", "Pop Art", "Vintage", "Cyberpunk", "Grunge", "Fantasy", "Steampunk", "Noir", "Surreal", "Minimalist", "Art Deco", "Impressionist", "Manga", "Horror", "Street Art", "Pixel Art", "Rainbow", "Nostalgia", "Cubism", "Expressionist", "Fauvism", "Photorealistic", "Pointillism", "Rococo", "Dada", "Futurism", "Romanticism", "Neoclassical", "Pre-Raphaelite", "Constructivism", "Baroque", "Suprematism", "Naive Art", "Art Nouveau", "De Stijl", "Ukiyo-e", "Ornamental", "Hard Edge", "Color Field", "Optical Art", "Kinetic Art", "Bauhaus", "Academic", "Surrealist", "Symbolist", "Byzantine", "Gothic Revival", "Renaissance", "Mannerism", "Metaphysical", "Naturalism", "Realism", "Post-Impressionism", "Art Brut", "Outsider Art", "Tachisme", "Lyrical Abstraction", "Abstract Expressionism", "Op Art", "Pop Surrealism", "Lowbrow", "Graffiti", "Conceptual Art", "Land Art", "Performance Art", "Installation Art", "Video Art", "Digital Art"].map((name => ({ name: name, value: name.toLowerCase() .replace(/ /g, "-") }))), [styleChoices1, styleChoices2, styleChoices3] = [styleChoicesArr.slice(0, 25), styleChoicesArr.slice(25, 50), styleChoicesArr.slice(50)]; let midjourneyClient; (async () => { try { const module = await import("midjourney-client"); midjourneyClient = module.default, console.log("Midjourney-client successfully imported") } catch (error) { console.error("Failed to load midjourney-client:", error) } })(); const cooldowns = new Map; module.exports = { name: "imagine", description: "There are endless possibilities", usage: "", category: "info", userPerms: [""], botPerms: [""], guildOnly: !1, ownerOnly: !1, toggleOff: !1, nsfwOnly: !1, maintenance: !1, options: [{ name: "prompt", description: "Add a prompt to generate an image", type: 3, required: !0 }, { name: "image_url", description: "Specify a URL to use as a base for image generation", type: 3, required: !1 }, { name: "uploaded_image", description: "Specify an uploaded image to use as a base for image generation", type: 11, required: !1 }, { name: "negative_prompt", description: "Specify things to avoid in the generated image", type: 3, required: !1 }, { name: "prompt_strength", description: "Prompt strength when using init image. 1.0 corresponds to full destruction of information.", type: 10, required: !1 }, { name: "ephemeral", description: "Make image generation ephemeral (visible only to the user who executed the command)", type: 5, required: !1 }, { name: "width", description: "Width of output image (Maximum size is 1024x768 or 768x1024 because of memory limits)", type: 4, required: !1, choices: [{ name: "128", value: 128 }, { name: "256", value: 256 }, { name: "512", value: 512 }, { name: "768", value: 768 }, { name: "1024", value: 1024 }] }, { name: "height", description: "Height of output image (Maximum size is 1024x768 or 768x1024 because of memory limits)", type: 4, required: !1, choices: [{ name: "128", value: 128 }, { name: "256", value: 256 }, { name: "512", value: 512 }, { name: "768", value: 768 }, { name: "1024", value: 1024 }] }, { name: "num_outputs", description: "Number of images to output (minimum: 1; maximum: 4)", type: 4, required: !1, choices: [{ name: "1", value: 1 }, { name: "2", value: 2 }, { name: "3", value: 3 }, { name: "4", value: 4 }] }, { name: "num_inference_steps", description: "Number of de-noise steps (minimum: 1; maximum: 500)", type: 4, required: !1 }, { name: "guidance_scale", description: "Scale for classifier-free guidance (minimum: 1; maximum: 20)", type: 10, required: !1 }, { name: "seed", description: "Set a seed for image generation (enter an integer/number only)", type: 4, required: !1 }, { name: "random_seed", description: "Enable this option to generate a random seed", type: 5, required: !1 }, { name: "style_1", description: "Add the first image style", type: 3, required: !1, choices: styleChoices1 }, { name: "style_2", description: "Add the second image style", type: 3, required: !1, choices: styleChoices2 }, { name: "style_3", description: "Add the third image style", type: 3, required: !1, choices: styleChoices3 }, { name: "author", description: "The name of the author to credit for the generated image", type: 3, required: !1 }, { name: "output_name", description: "The name of the output image file", type: 3, required: !1 }, { name: "image_type", description: "The type of image to generate", type: 3, required: !1, choices: [{ name: "Portrait", value: "portrait" }, { name: "Landscape", value: "landscape" }, { name: "Square", value: "square" }] }, { name: "scheduler", description: "The scheduler to use for image generation", type: 3, required: !1, choices: [{ name: "DDIM", value: "DDIM" }, { name: "K_EULER", value: "K_EULER" }, { name: "DPMSolverMultistep", value: "DPMSolverMultistep" }, { name: "K_EULER_ANCESTRAL", value: "K_EULER_ANCESTRAL" }, { name: "PNDM", value: "PNDM" }, { name: "KLMS", value: "KLMS" }] }], type: 1, cooldown: 10, run: async (interaction, args) => { try { if (!midjourneyClient) throw new Error("Midjourney client not loaded"); args.user.id; // Extract all parameters into a configuration object const config = { userId: args.user.id, prompt: args.options.getString("prompt"), isEphemeral: args.options.getBoolean("ephemeral") || !1, styles: [args.options.getString("style_1"), args.options.getString("style_2"), args.options.getString("style_3")].filter(Boolean), imageType: args.options.getString("image_type") || "", author: args.options.getString("author") || "", scheduler: args.options.getString("scheduler") || "", negativePrompt: args.options.getString("negative_prompt") || "", width: args.options.getInteger("width") || 1024, height: args.options.getInteger("height") || 768, numOutputs: args.options.getInteger("num_outputs") || 1, imageUrlOption: args.options.get("image_url"), uploadedImageOption: args.options.get("uploaded_image"), numInferenceSteps: args.options.getInteger("num_inference_steps") || 50, randomSeed: args.options.getBoolean("random_seed") || !1, seedOption: args.options.getInteger("seed") || null, guidanceScale: args.options.getNumber("guidance_scale") || 6, promptStrength: args.options.getNumber("prompt_strength") || null }; // Now you can use the config object to access the parameters if (cooldowns.has(config.userId)) { const remainingTime = (cooldowns.get(config.userId) - Date.now()) / 1e3; if (remainingTime > 0) return await args.reply({ content: `Hold your horses! 🐎 You need to wait ${remainingTime.toFixed(1)} seconds before using this command again.`, ephemeral: !0 }) } // Use the config object to access the parameters // Validate parameters if (cooldowns.set(config.userId, Date.now() + 1e3 * module.exports.cooldown), setTimeout((() => cooldowns.delete(config.userId)), 1e3 * module.exports.cooldown), (steps = config.numInferenceSteps) < 1 || steps > 500) return await args.reply({ content: "Number of Inference Steps must be between 1 and 500. Please try again with a valid value.", ephemeral: !0 }); if (!((value = config.guidanceScale) >= 1 && value <= 20)) return await args.reply({ content: "Guidance Scale must be between 1 and 20. Please try again with a valid value.", ephemeral: !0 }); if (config.imageUrlOption && config.uploadedImageOption) return await args.reply({ content: "Whoa there, Picasso! 🎨 One masterpiece at a time, please. Provide either an image URL or an uploaded image, but not both. Let's try that again!", ephemeral: !0 }); if (config.randomSeed && null !== config.seedOption) return await args.reply({ content: "You can't have your cake and eat it too! 🍰 Choose either 'random_seed' or 'seed', but not both. Let's try again!", ephemeral: !0 }); config.seed = config.randomSeed ? Math.floor(1e5 * Math.random()) : config.seedOption; // Prepare for image generation const loadingMessage = config.isEphemeral ? "Psst! 🤫 I'm secretly conjuring up an image just for your eyes! It's our little secret, so no one else will see it! Hang tight, it's almost ready! 🎨✨" : "Your image is being created in the digital realm. It might take a few moments for it to fully materialize. Hold tight and keep your imagination running wild. 🧙‍♂️🌌"; await args.reply({ content: loadingMessage, ephemeral: config.isEphemeral }); let generatedImageURLs = []; async function sendGeneratedImage() { const stylesStr = config.styles.length > 0 ? ", " + config.styles.join(", ") : "", styledPrompt = `${config.prompt.trim()}${stylesStr}`, imageUrl = config.imageUrlOption ? config.imageUrlOption.value : null, uploadedImage = config.uploadedImageOption && "MESSAGE" === config.uploadedImageOption.type ? config.uploadedImageOption.message : null; let additionalOptions = { image: imageUrl || (uploadedImage ? uploadedImage.attachments.first() .url : null), negative_prompt: config.negativePrompt || void 0, width: config.width, height: config.height, num_outputs: config.numOutputs, num_inference_steps: config.numInferenceSteps, guidance_scale: config.guidanceScale, scheduler: config.scheduler || void 0, seed: config.seed, prompt_strength: config.promptStrength || void 0 }; // Filter out undefined and null properties additionalOptions = Object.fromEntries(Object.entries(additionalOptions) .filter((([_, v]) => null != v))), // Log the filtered options console.log("Filtered additional options:", additionalOptions), console.log("Input parameters:", { styledPrompt: styledPrompt, additionalOptions: additionalOptions }); const generatedImage = await midjourneyClient(styledPrompt, additionalOptions); if (console.log("Generated image:", generatedImage), !generatedImage) return await args.deleteReply(), void await args.reply({ content: "Oh no, the art goblins are at it again! 😱 They messed up our masterpiece. Let's give it another go, shall we? 🎨", ephemeral: !0 }); const outputName = args.options.getString("output_name") || "generated-image", imageAttachment = { attachment: generatedImage[0], name: `${outputName}.png` }, tempMessage = await args.channel.send({ files: [imageAttachment] }), imageURL = tempMessage.attachments.first() .url; generatedImageURLs.push(imageURL), await tempMessage.delete(); const newRow = createButtons(generatedImageURLs.length - 1, imageURL), paramsMessage = "**Parameters:**\n" + (config.negativePrompt ? `• Negative Prompt: ${config.negativePrompt}\n` : "") + (config.promptStrength ? `• Prompt Strength: ${config.promptStrength}\n` : "") + (config.width ? `• Width: ${config.width}\n` : "") + (config.height ? `• Height: ${config.height}\n` : "") + (config.numOutputs ? `• Number of Outputs: ${config.numOutputs}\n` : "") + (config.numInferenceSteps ? `• Number of Denoising Steps: ${config.numInferenceSteps}\n` : "") + (config.guidanceScale ? `• Guidance Scale: ${config.guidanceScale}\n` : "") + (null !== config.seed ? `• Seed: ${config.seed}\n` : "") + (config.styles.length > 0 ? `• Styles: ${config.styles.join(", ")}\n` : "") + (config.author ? `• Author: ${config.author}\n` : "") + (config.imageType ? `• Image Type: ${config.imageType}\n` : "") + (config.scheduler ? `• Scheduler: ${config.scheduler}\n` : "") + "\n"; promptMessage = `${paramsMessage}**Prompt:** ${styledPrompt}\n`, await args.editReply({ content: promptMessage, files: [imageAttachment], components: [newRow] }) } function createButtons(index, imageURL) { const row = new ActionRowBuilder; if (!config.seed) { const regenerateButton = (new ButtonBuilder) .setCustomId("regenerate") .setLabel("Regenerate") .setStyle(ButtonStyle.Primary); row.addComponents(regenerateButton) } if (!config.isEphemeral) { const dismissButton = (new ButtonBuilder) .setCustomId("dismiss") .setLabel("Dismiss") .setStyle(ButtonStyle.Danger); row.addComponents(dismissButton) } if (null !== index && imageURL) { const viewImageButton = (new ButtonBuilder) .setLabel("URL") .setStyle(ButtonStyle.Link) .setURL(imageURL) .setDisabled(!1); row.addComponents(viewImageButton) } return row } await sendGeneratedImage(); const message = await args.fetchReply(), filter = args => ("regenerate" === args.customId || "dismiss" === args.customId) && args.user.id === interaction.user.id, collector = message.createMessageComponentCollector(filter, { time: 6e4 }); collector.on("collect", (async buttonInteraction => { if ("regenerate" === buttonInteraction.customId) await buttonInteraction.deferUpdate(), await args.editReply({ content: "Hold on to your hats! We're going for another spin! 🎡🎢", ephemeral: config.isEphemeral }), await sendGeneratedImage(); else if ("dismiss" === buttonInteraction.customId) { if (config.isEphemeral) return; await buttonInteraction.deferUpdate(); try { await args.deleteReply() } catch (error) { 10008 === error.code ? console.log("Message not found or already deleted.") : console.error(error) } } })), collector.on("end", (async () => { const newRow = createButtons(generatedImageURLs.length - 1); newRow.components[0] && newRow.components[0].setDisabled(!0), newRow.components[1] && newRow.components[1].setDisabled(!0), newRow.components[2] && newRow.components[2].setDisabled(!1); try { await message.edit({ components: [newRow] }) } catch (error) { 10008 === error.code ? console.log("Message not found or already deleted.") : console.error(error) } })) } catch (error) { console.error(error), args.replied || args.deferred || args.reply({ content: "An error occurred while generating the image 😭.", ephemeral: !0 }) } var value, steps } };