Fix autocomplete bugs, bulk upload

This commit is contained in:
cirroskais 2024-03-04 00:17:06 -05:00
parent 4cd8f68e92
commit 4ec01a6ab9
No known key found for this signature in database
GPG key ID: 5FC73EBF2678E33D
5 changed files with 269 additions and 170 deletions

View file

@ -1,54 +1,58 @@
import { InteractionResponseTypes } from "@discordeno/types" import { InteractionResponseTypes } from "@discordeno/types";
import REST from "../lib/handlers/RESTHandler" import REST from "../lib/handlers/RESTHandler";
import { staged } from "../lib/modpack" import { staged } from "../lib/modpack";
import { getAllMods } from "../lib/database" import { getAllMods } from "../lib/database";
function search(arr, search) { function search(arr, search) {
if (!search.length) return false if (!search.length) return false;
var matches = arr.filter(function (str) { var matches = arr.filter(function (str) {
const regex = new RegExp(search, "i") const regex = new RegExp(search, "i");
return str.search(regex) > -1 return str.search(regex) > -1;
}) });
return matches.length > 0 ? matches : false return matches.length > 0 ? matches : false;
} }
export default class Autocomplete { export default class Autocomplete {
static name = "modify" static name = "modify";
constructor(data) {} constructor(data) {}
async run(interaction) { async run(interaction) {
const entry = interaction.data.options[0].options[0].value const entry = interaction.data.options[0].options.find((_) => _.name == "name").value;
const from_modpack = interaction.data.options[0].options[1]?.value const from_modpack = interaction.data.options[0].options.find((_) => _.name == "from_modpack")?.value;
if (!from_modpack) { if (!from_modpack) {
const keys = Array.from(staged.keys()) const keys = Array.from(staged.keys());
let choices = search(keys, entry) let choices = search(keys, entry);
if (!choices) choices = keys if (!choices) choices = keys;
return REST.sendInteractionResponse(interaction.id, interaction.token, { return REST.sendInteractionResponse(interaction.id, interaction.token, {
type: InteractionResponseTypes.ApplicationCommandAutocompleteResult, type: InteractionResponseTypes.ApplicationCommandAutocompleteResult,
data: { data: {
choices: choices.map((_) => { choices: choices
return { name: _, value: _ } .map((_) => {
}), return { name: _, value: _ };
}, })
}) .slice(0, 24),
} else { },
const keys = getAllMods.all().map((_) => _.name) });
let choices = search(keys, entry) } else {
if (!choices) choices = keys const keys = getAllMods.all().map((_) => _.name);
let choices = search(keys, entry);
if (!choices) choices = keys;
return REST.sendInteractionResponse(interaction.id, interaction.token, { return REST.sendInteractionResponse(interaction.id, interaction.token, {
type: InteractionResponseTypes.ApplicationCommandAutocompleteResult, type: InteractionResponseTypes.ApplicationCommandAutocompleteResult,
data: { data: {
choices: choices.map((_) => { choices: choices
return { name: _, value: _ } .map((_) => {
}), return { name: _, value: _ };
}, })
}) .slice(0, 24),
} },
} });
}
}
} }

88
src/commands/bulk.js Normal file
View file

@ -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") {
}
}
}

View file

@ -1,24 +1,25 @@
import BaseCommand from "../lib/classes/BaseCommand" import BaseCommand from "../lib/classes/BaseCommand";
import { staged } from "../lib/modpack" import { staged } from "../lib/modpack";
export default class Command extends BaseCommand { export default class Command extends BaseCommand {
static type = 1 static type = 1;
static name = "changes" static name = "changes";
static description = "Get staged changes to the modpack" static description = "Get staged changes to the modpack";
constructor(data) { constructor(data) {
super(data) super(data);
} }
async run(interaction) { async run(interaction) {
let text = "" 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()) { for (let [key, value] of staged.entries()) {
text += (value.action == "add" ? "+" : "-") + " " + key + " (" + value.url + ")\n" 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```");
}
} }

View file

@ -1,106 +1,108 @@
import BaseCommand from "../lib/classes/BaseCommand" import BaseCommand from "../lib/classes/BaseCommand";
import { staged } from "../lib/modpack" import { staged } from "../lib/modpack";
import * as curseforge from "../lib/curseforge" import * as curseforge from "../lib/curseforge";
import { getMod } from "../lib/database" import { getMod } from "../lib/database";
export default class Command extends BaseCommand { export default class Command extends BaseCommand {
static type = 1 static type = 1;
static name = "modify" static name = "modify";
static description = "Modify the modpack" static description = "Modify the modpack";
static options = [ static options = [
{ {
type: 1, type: 1,
name: "add", name: "add",
description: "Add a mod", description: "Add a mod",
options: [ options: [
{ {
type: 3, type: 3,
name: "url", name: "url",
description: "A link to a Modrinth/Curseforge mod", description: "A link to a Modrinth/Curseforge mod",
required: true, required: true,
}, },
], ],
}, },
{ {
type: 1, type: 1,
name: "remove", name: "remove",
description: "Remove a mod", description: "Remove a mod",
options: [ options: [
{ {
type: 5, type: 5,
name: "from_modpack", name: "from_modpack",
description: "Toggle removal from staging or the modpack", description: "Toggle removal from staging or the modpack",
required: true, required: true,
}, },
{ {
type: 3, type: 3,
name: "name", name: "name",
description: "The name of the mod", description: "The name of the mod",
autocomplete: true, autocomplete: true,
required: true, required: true,
}, },
], ],
}, },
] ];
constructor(data) { constructor(data) {
super(data) super(data);
} }
async run(interaction) { async run(interaction) {
const subcommand = interaction.data.options[0].name const subcommand = interaction.data.options[0].name;
const subcommandOptions = interaction.data.options[0].options const subcommandOptions = interaction.data.options[0].options;
if (subcommand == "add") { if (subcommand == "add") {
const url = subcommandOptions.find((_) => _.name == "url").value const url = subcommandOptions.find((_) => _.name == "url").value;
const parsedUrl = new URL(url) const parsedUrl = new URL(url);
if (parsedUrl.host.includes("curseforge.com")) { if (parsedUrl.host.includes("curseforge.com")) {
const mod = await curseforge.getMod(url) const mod = await curseforge.getMod(url);
if (!mod) return interaction.respond("Mod not found.") 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 (staged.get(mod.name))
if (getMod.get(mod.name)) return interaction.respond(`**${mod.name}** is already in the modpack.`, { isPrivate: true }) 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, { staged.set(mod.name, {
modId: mod.id, modId: mod.id,
fileId: file.id, fileId: file.id,
provider: "curseforge", provider: "curseforge",
url: mod.links.websiteUrl, url: mod.links.websiteUrl,
action: "add", action: "add",
}) });
interaction.respond(`Added **${mod.name}** to the staged mods.`) interaction.respond(`Added **${mod.name}** to the staged mods.`);
} else if (parsedUrl.host.includes("modrinth.com")) { } else if (parsedUrl.host.includes("modrinth.com")) {
console.log("Modrinth mod detected") console.log("Modrinth mod detected");
} }
} else if (subcommand == "remove") { } else if (subcommand == "remove") {
const name = subcommandOptions.find((_) => _.name == "name").value const name = subcommandOptions.find((_) => _.name == "name").value;
const from_modpack = subcommandOptions.find((_) => _.name == "from_modpack")?.value const from_modpack = subcommandOptions.find((_) => _.name == "from_modpack")?.value;
if (!from_modpack) { if (!from_modpack) {
const mod = staged.get(name) const mod = staged.get(name);
if (!mod) return interaction.respond("Mod not found.") if (!mod) return interaction.respond("Mod not found.");
staged.delete(name) staged.delete(name);
interaction.respond(`Removed **${name}** from the staged mods.`) interaction.respond(`Removed **${name}** from the staged mods.`);
} else { } else {
const mod = getMod.get(name) const mod = getMod.get(name);
if (!mod) return interaction.respond("Mod not found.") if (!mod) return interaction.respond("Mod not found.");
const modData = await curseforge.getModFromId(mod.modId) const modData = await curseforge.getModFromId(mod.modId);
staged.set(mod.name, { staged.set(mod.name, {
modId: mod.modId, modId: mod.modId,
url: modData.data.links.websiteUrl, url: modData.data.links.websiteUrl,
provider: "curseforge", provider: "curseforge",
action: "remove", action: "remove",
}) });
interaction.respond(`Staged the removal of **${name}**.`) interaction.respond(`Staged the removal of **${name}**.`);
} }
} }
} }
} }

View file

@ -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() { export default async function discord() {
const client = createBot({ const client = createBot({
token: import.meta.env.DISCORD_TOKEN, token: import.meta.env.DISCORD_TOKEN,
// intents: [], // 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.content = true;
client.transformers.desiredProperties.message.author = true client.transformers.desiredProperties.message.author = true;
client.transformers.desiredProperties.user.id = true client.transformers.desiredProperties.user.id = true;
client.transformers.desiredProperties.user.username = true client.transformers.desiredProperties.user.username = true;
client.transformers.desiredProperties.user.avatar = true client.transformers.desiredProperties.user.avatar = true;
client.transformers.desiredProperties.member.user = true client.transformers.desiredProperties.member.user = true;
client.transformers.desiredProperties.member.roles = true client.transformers.desiredProperties.member.roles = true;
client.transformers.desiredProperties.member.permissions = true client.transformers.desiredProperties.member.permissions = true;
client.transformers.desiredProperties.interaction.id = true client.transformers.desiredProperties.interaction.id = true;
client.transformers.desiredProperties.interaction.type = true client.transformers.desiredProperties.interaction.type = true;
client.transformers.desiredProperties.interaction.data = true client.transformers.desiredProperties.interaction.data = true;
client.transformers.desiredProperties.interaction.token = true client.transformers.desiredProperties.interaction.token = true;
client.transformers.desiredProperties.interaction.guildId = true client.transformers.desiredProperties.interaction.guildId = true;
client.transformers.desiredProperties.interaction.channelId = true client.transformers.desiredProperties.interaction.channelId = true;
client.transformers.desiredProperties.interaction.member = 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();
} }