From 4ec01a6ab934016dd01b93f7c3f9f4e94246173e Mon Sep 17 00:00:00 2001 From: cirroskais Date: Mon, 4 Mar 2024 00:17:06 -0500 Subject: [PATCH] Fix autocomplete bugs, bulk upload --- src/autocomplete/modify.js | 86 +++++++++--------- src/commands/bulk.js | 88 ++++++++++++++++++ src/commands/changes.js | 33 +++---- src/commands/modify.js | 182 +++++++++++++++++++------------------ src/discord.js | 50 +++++----- 5 files changed, 269 insertions(+), 170 deletions(-) create mode 100644 src/commands/bulk.js diff --git a/src/autocomplete/modify.js b/src/autocomplete/modify.js index 45f6950..7de40a8 100644 --- a/src/autocomplete/modify.js +++ b/src/autocomplete/modify.js @@ -1,54 +1,58 @@ -import { InteractionResponseTypes } from "@discordeno/types" -import REST from "../lib/handlers/RESTHandler" -import { staged } from "../lib/modpack" -import { getAllMods } from "../lib/database" +import { InteractionResponseTypes } from "@discordeno/types"; +import REST from "../lib/handlers/RESTHandler"; +import { staged } from "../lib/modpack"; +import { getAllMods } from "../lib/database"; function search(arr, search) { - if (!search.length) return false + if (!search.length) return false; - var matches = arr.filter(function (str) { - const regex = new RegExp(search, "i") - return str.search(regex) > -1 - }) + var matches = arr.filter(function (str) { + const regex = new RegExp(search, "i"); + return str.search(regex) > -1; + }); - return matches.length > 0 ? matches : false + return matches.length > 0 ? matches : false; } export default class Autocomplete { - static name = "modify" + static name = "modify"; - constructor(data) {} + constructor(data) {} - async run(interaction) { - const entry = interaction.data.options[0].options[0].value - const from_modpack = interaction.data.options[0].options[1]?.value + async run(interaction) { + const entry = interaction.data.options[0].options.find((_) => _.name == "name").value; + const from_modpack = interaction.data.options[0].options.find((_) => _.name == "from_modpack")?.value; - if (!from_modpack) { - const keys = Array.from(staged.keys()) - let choices = search(keys, entry) - if (!choices) choices = keys + if (!from_modpack) { + const keys = Array.from(staged.keys()); + let choices = search(keys, entry); + if (!choices) choices = keys; - return REST.sendInteractionResponse(interaction.id, interaction.token, { - type: InteractionResponseTypes.ApplicationCommandAutocompleteResult, - data: { - choices: choices.map((_) => { - return { name: _, value: _ } - }), - }, - }) - } else { - const keys = getAllMods.all().map((_) => _.name) - let choices = search(keys, entry) - if (!choices) choices = keys + return REST.sendInteractionResponse(interaction.id, interaction.token, { + type: InteractionResponseTypes.ApplicationCommandAutocompleteResult, + data: { + choices: choices + .map((_) => { + return { name: _, value: _ }; + }) + .slice(0, 24), + }, + }); + } else { + const keys = getAllMods.all().map((_) => _.name); + let choices = search(keys, entry); + if (!choices) choices = keys; - return REST.sendInteractionResponse(interaction.id, interaction.token, { - type: InteractionResponseTypes.ApplicationCommandAutocompleteResult, - data: { - choices: choices.map((_) => { - return { name: _, value: _ } - }), - }, - }) - } - } + return REST.sendInteractionResponse(interaction.id, interaction.token, { + type: InteractionResponseTypes.ApplicationCommandAutocompleteResult, + data: { + choices: choices + .map((_) => { + return { name: _, value: _ }; + }) + .slice(0, 24), + }, + }); + } + } } diff --git a/src/commands/bulk.js b/src/commands/bulk.js new file mode 100644 index 0000000..4f01d60 --- /dev/null +++ b/src/commands/bulk.js @@ -0,0 +1,88 @@ +import BaseCommand from "../lib/classes/BaseCommand"; +import { getModFromId } from "../lib/curseforge"; +import { staged } from "../lib/modpack"; + +function progress(percent) { + return "#".repeat(percent * 25).padEnd(25, " "); +} + +export default class Command extends BaseCommand { + static type = 1; + static name = "bulk"; + static description = "Make changes in builk"; + static options = [ + { + type: 1, + name: "add", + description: "Add mods in bulk", + options: [ + { + type: 11, + name: "file", + description: "A CurseForge manifest file", + required: true, + }, + ], + }, + { + type: 1, + name: "remove", + description: "Remove mods in bulk", + options: [ + { + type: 11, + name: "file", + description: "A curseforge manifest file", + required: true, + }, + ], + }, + ]; + + constructor(data) { + super(data); + } + + async run(interaction) { + const subcommand = interaction.data.options[0].name; + const subcommandOptions = interaction.data.options[0].options; + const attachmentId = BigInt(subcommandOptions[0].value); + const file = interaction.data.resolved.attachments.get(attachmentId); + + let succeeded = []; + let failed = []; + + if (subcommand == "add") { + await interaction.defer(); + + if (file.contentType !== "application/json; charset=utf-8" || file.filename !== "manifest.json") + return interaction.respond("That is not a valid manifest.json file.", { isPrivate: true }); + + const manifest = await (await fetch(file.url)).json(); + + for (let i = 0; i < manifest.files.length; i++) { + const { projectID, fileID } = manifest.files[i]; + const mod = (await getModFromId(projectID)).data; + + if (mod && !staged.get(mod.name)) { + staged.set(mod.name, { + modId: mod.id, + fileId: fileID, + provider: "curseforge", + url: mod.links.websiteUrl, + action: "add", + }); + + succeeded.push(mod.name); + + if (succeeded.length % 5 == 0) await interaction.edit(`[${progress(i / manifest.files.length)}]`); + } else { + failed.push({ modId: projectID }); + } + } + + return interaction.edit(`Bulk added **${succeeded.length} mods**. (${failed.length} failed)`); + } else if (subcommand == "remove") { + } + } +} diff --git a/src/commands/changes.js b/src/commands/changes.js index 2ed4a1e..dd9b6e8 100644 --- a/src/commands/changes.js +++ b/src/commands/changes.js @@ -1,24 +1,25 @@ -import BaseCommand from "../lib/classes/BaseCommand" -import { staged } from "../lib/modpack" +import BaseCommand from "../lib/classes/BaseCommand"; +import { staged } from "../lib/modpack"; export default class Command extends BaseCommand { - static type = 1 - static name = "changes" - static description = "Get staged changes to the modpack" + static type = 1; + static name = "changes"; + static description = "Get staged changes to the modpack"; - constructor(data) { - super(data) - } + constructor(data) { + super(data); + } - async run(interaction) { - let text = "" + async run(interaction) { + let text = ""; - if (!staged.size) return interaction.respond("No changes to the modpack have been staged.") + if (!staged.size) return interaction.respond("No changes to the modpack have been staged."); - for (let [key, value] of staged.entries()) { - text += (value.action == "add" ? "+" : "-") + " " + key + " (" + value.url + ")\n" - } + for (let [key, value] of staged.entries()) { + text += (value.action == "add" ? "+" : "-") + " " + key + " (" + value.url + ")\n"; + } - interaction.respond("```diff\n" + text + "\n```") - } + if (text.length >= 2000) return interaction.respond("Too many changes to display."); + interaction.respond("```diff\n" + text + "\n```"); + } } diff --git a/src/commands/modify.js b/src/commands/modify.js index 2088ee9..dfad641 100644 --- a/src/commands/modify.js +++ b/src/commands/modify.js @@ -1,106 +1,108 @@ -import BaseCommand from "../lib/classes/BaseCommand" -import { staged } from "../lib/modpack" -import * as curseforge from "../lib/curseforge" -import { getMod } from "../lib/database" +import BaseCommand from "../lib/classes/BaseCommand"; +import { staged } from "../lib/modpack"; +import * as curseforge from "../lib/curseforge"; +import { getMod } from "../lib/database"; export default class Command extends BaseCommand { - static type = 1 - static name = "modify" - static description = "Modify the modpack" - static options = [ - { - type: 1, - name: "add", - description: "Add a mod", - options: [ - { - type: 3, - name: "url", - description: "A link to a Modrinth/Curseforge mod", - required: true, - }, - ], - }, - { - type: 1, - name: "remove", - description: "Remove a mod", - options: [ - { - type: 5, - name: "from_modpack", - description: "Toggle removal from staging or the modpack", - required: true, - }, - { - type: 3, - name: "name", - description: "The name of the mod", - autocomplete: true, - required: true, - }, - ], - }, - ] + static type = 1; + static name = "modify"; + static description = "Modify the modpack"; + static options = [ + { + type: 1, + name: "add", + description: "Add a mod", + options: [ + { + type: 3, + name: "url", + description: "A link to a Modrinth/Curseforge mod", + required: true, + }, + ], + }, + { + type: 1, + name: "remove", + description: "Remove a mod", + options: [ + { + type: 5, + name: "from_modpack", + description: "Toggle removal from staging or the modpack", + required: true, + }, + { + type: 3, + name: "name", + description: "The name of the mod", + autocomplete: true, + required: true, + }, + ], + }, + ]; - constructor(data) { - super(data) - } + constructor(data) { + super(data); + } - async run(interaction) { - const subcommand = interaction.data.options[0].name - const subcommandOptions = interaction.data.options[0].options + async run(interaction) { + const subcommand = interaction.data.options[0].name; + const subcommandOptions = interaction.data.options[0].options; - if (subcommand == "add") { - const url = subcommandOptions.find((_) => _.name == "url").value - const parsedUrl = new URL(url) + if (subcommand == "add") { + const url = subcommandOptions.find((_) => _.name == "url").value; + const parsedUrl = new URL(url); - if (parsedUrl.host.includes("curseforge.com")) { - const mod = await curseforge.getMod(url) - if (!mod) return interaction.respond("Mod not found.") + if (parsedUrl.host.includes("curseforge.com")) { + const mod = await curseforge.getMod(url); + if (!mod) return interaction.respond("Mod not found."); - const file = await curseforge.getModFiles(mod.id) + const file = await curseforge.getModFiles(mod.id); - if (staged.get(mod.name)) return interaction.respond(`**${mod.name}** is already in the staged mods.`, { isPrivate: true }) - if (getMod.get(mod.name)) return interaction.respond(`**${mod.name}** is already in the modpack.`, { isPrivate: true }) + if (staged.get(mod.name)) + return interaction.respond(`**${mod.name}** is already in the staged mods.`, { isPrivate: true }); + if (getMod.get(mod.name)) + return interaction.respond(`**${mod.name}** is already in the modpack.`, { isPrivate: true }); - staged.set(mod.name, { - modId: mod.id, - fileId: file.id, - provider: "curseforge", - url: mod.links.websiteUrl, - action: "add", - }) + staged.set(mod.name, { + modId: mod.id, + fileId: file.id, + provider: "curseforge", + url: mod.links.websiteUrl, + action: "add", + }); - interaction.respond(`Added **${mod.name}** to the staged mods.`) - } else if (parsedUrl.host.includes("modrinth.com")) { - console.log("Modrinth mod detected") - } - } else if (subcommand == "remove") { - const name = subcommandOptions.find((_) => _.name == "name").value - const from_modpack = subcommandOptions.find((_) => _.name == "from_modpack")?.value + interaction.respond(`Added **${mod.name}** to the staged mods.`); + } else if (parsedUrl.host.includes("modrinth.com")) { + console.log("Modrinth mod detected"); + } + } else if (subcommand == "remove") { + const name = subcommandOptions.find((_) => _.name == "name").value; + const from_modpack = subcommandOptions.find((_) => _.name == "from_modpack")?.value; - if (!from_modpack) { - const mod = staged.get(name) - if (!mod) return interaction.respond("Mod not found.") + if (!from_modpack) { + const mod = staged.get(name); + if (!mod) return interaction.respond("Mod not found."); - staged.delete(name) + staged.delete(name); - interaction.respond(`Removed **${name}** from the staged mods.`) - } else { - const mod = getMod.get(name) - if (!mod) return interaction.respond("Mod not found.") - const modData = await curseforge.getModFromId(mod.modId) + interaction.respond(`Removed **${name}** from the staged mods.`); + } else { + const mod = getMod.get(name); + if (!mod) return interaction.respond("Mod not found."); + const modData = await curseforge.getModFromId(mod.modId); - staged.set(mod.name, { - modId: mod.modId, - url: modData.data.links.websiteUrl, - provider: "curseforge", - action: "remove", - }) + staged.set(mod.name, { + modId: mod.modId, + url: modData.data.links.websiteUrl, + provider: "curseforge", + action: "remove", + }); - interaction.respond(`Staged the removal of **${name}**.`) - } - } - } + interaction.respond(`Staged the removal of **${name}**.`); + } + } + } } diff --git a/src/discord.js b/src/discord.js index e0f4b2a..2fbec83 100644 --- a/src/discord.js +++ b/src/discord.js @@ -1,33 +1,37 @@ -import { createBot, Intents, logger } from "@discordeno/bot" +import { createBot, Intents, logger } from "@discordeno/bot"; -import EventHandler from "./lib/handlers/EventHandler" +import EventHandler from "./lib/handlers/EventHandler"; export default async function discord() { - const client = createBot({ - token: import.meta.env.DISCORD_TOKEN, - // intents: [], - }) + const client = createBot({ + token: import.meta.env.DISCORD_TOKEN, + // intents: [], + }); - client.events = await new EventHandler(client).load() + client.events = await new EventHandler(client).load(); - client.transformers.desiredProperties.message.content = true - client.transformers.desiredProperties.message.author = true + client.transformers.desiredProperties.message.content = true; + client.transformers.desiredProperties.message.author = true; - client.transformers.desiredProperties.user.id = true - client.transformers.desiredProperties.user.username = true - client.transformers.desiredProperties.user.avatar = true + client.transformers.desiredProperties.user.id = true; + client.transformers.desiredProperties.user.username = true; + client.transformers.desiredProperties.user.avatar = true; - client.transformers.desiredProperties.member.user = true - client.transformers.desiredProperties.member.roles = true - client.transformers.desiredProperties.member.permissions = true + client.transformers.desiredProperties.member.user = true; + client.transformers.desiredProperties.member.roles = true; + client.transformers.desiredProperties.member.permissions = true; - client.transformers.desiredProperties.interaction.id = true - client.transformers.desiredProperties.interaction.type = true - client.transformers.desiredProperties.interaction.data = true - client.transformers.desiredProperties.interaction.token = true - client.transformers.desiredProperties.interaction.guildId = true - client.transformers.desiredProperties.interaction.channelId = true - client.transformers.desiredProperties.interaction.member = true + client.transformers.desiredProperties.interaction.id = true; + client.transformers.desiredProperties.interaction.type = true; + client.transformers.desiredProperties.interaction.data = true; + client.transformers.desiredProperties.interaction.token = true; + client.transformers.desiredProperties.interaction.guildId = true; + client.transformers.desiredProperties.interaction.channelId = true; + client.transformers.desiredProperties.interaction.member = true; - client.start() + client.transformers.desiredProperties.attachment.contentType = true; + client.transformers.desiredProperties.attachment.filename = true; + client.transformers.desiredProperties.attachment.url = true; + + client.start(); }