Files
SpigotWatch/commands/addauthor.js
2026-02-25 18:51:08 +01:00

216 lines
7.1 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import {
EmbedBuilder,
PermissionsBitField,
SlashCommandBuilder,
ActionRowBuilder,
ButtonBuilder,
ButtonStyle,
} from "discord.js";
import fs from "fs/promises";
export default {
name: "addauthor",
description: "Fügt alle Plugins eines SpigotMC-Autors zur Beobachtungsliste hinzu",
aliases: ["addall"],
guild: ["all"],
nsfw: false,
user_permissions: [PermissionsBitField.Flags.Administrator],
bot_permissions: [],
args_required: 2,
args_usage: "[author_id] [#kanal] Beispiel: vn!addauthor 618600 #plugin-updates",
cooldown: 10,
data: new SlashCommandBuilder()
.setName("addauthor")
.setDescription("Fügt alle Plugins eines SpigotMC-Autors zur Beobachtungsliste hinzu")
.addStringOption((opt) =>
opt
.setName("author_id")
.setDescription("SpigotMC Autor-ID (aus der Profil-URL)")
.setRequired(true)
)
.addChannelOption((opt) =>
opt
.setName("kanal")
.setDescription("Kanal für Update-Benachrichtigungen")
.setRequired(true)
),
async execute(client, ctx, args) {
const authorID = ctx.isSlash
? ctx.interaction.options.getString("author_id")
: args[0];
const channel = ctx.isSlash
? ctx.interaction.options.getChannel("kanal")
: ctx.mentions?.channels?.first();
if (!channel) {
return ctx.reply("Dieser Kanal ist ungültig. Bitte erwähne einen Kanal auf diesem Server.");
}
if (!ctx.guild.channels.cache.has(channel.id)) {
return ctx.reply("Dieser Kanal befindet sich nicht auf diesem Server!");
}
// Fetch author info
let authorName = authorID;
try {
const res = await fetch(`https://api.spiget.org/v2/authors/${authorID}`);
if (res.ok) {
const data = await res.json();
authorName = data.name ?? authorID;
}
} catch { /* ignore */ }
// Fetch all resources by author
let resources;
try {
const res = await fetch(
`https://api.spiget.org/v2/authors/${authorID}/resources?size=50&sort=-updateDate`
);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
resources = await res.json();
} catch (e) {
client.logger.error(e);
return ctx.reply("Ressourcen konnten nicht von der Spiget-API abgerufen werden. Bitte versuche es erneut.");
}
if (!Array.isArray(resources) || resources.length === 0) {
return ctx.reply(`Für Autor \`${authorID}\` wurden keine Ressourcen gefunden.`);
}
const guildID = ctx.guild.id;
const filePath = `./serverdata/${guildID}.json`;
// Load existing data
let saveData = { watchedResources: [] };
try {
const raw = await fs.readFile(filePath, "utf8");
saveData = JSON.parse(raw);
} catch { /* Datei existiert noch nicht */ }
// Filter out already watched and check limit
const alreadyWatched = resources.filter((r) =>
saveData.watchedResources.some((w) => String(w.resourceID) === String(r.id))
);
const toAdd = resources.filter((r) =>
!saveData.watchedResources.some((w) => String(w.resourceID) === String(r.id))
);
const willAdd = toAdd;
if (willAdd.length === 0 && alreadyWatched.length > 0) {
return ctx.reply(
`Alle ${alreadyWatched.length} Ressourcen von **${authorName}** werden bereits beobachtet.`
);
}
if (willAdd.length === 0) {
return ctx.reply("Es gibt keine neuen Ressourcen zum Hinzufügen.");
}
// Build confirmation embed
const resourceList = willAdd
.map((r) => `• **${r.name}** (\`${r.id}\`)`)
.join("\n");
const warnings = [];
if (alreadyWatched.length > 0)
warnings.push(`⚠️ ${alreadyWatched.length} bereits beobachtet (übersprungen)`);
const confirmEmbed = new EmbedBuilder()
.setColor(ctx.guild.members.me.displayHexColor)
.setTitle(`📦 Alle Plugins von ${authorName} beobachten`)
.setDescription(
`Folgende **${willAdd.length}** Ressourcen werden in <#${channel.id}> beobachtet:\n\n${resourceList}` +
(warnings.length > 0 ? `\n\n${warnings.join("\n")}` : "")
)
.setFooter({ text: `Autor-ID: ${authorID} • SpigotMC` })
.setTimestamp();
const row = new ActionRowBuilder().addComponents(
new ButtonBuilder()
.setCustomId("addauthor_confirm")
.setLabel(`✅ Alle ${willAdd.length} hinzufügen`)
.setStyle(ButtonStyle.Success),
new ButtonBuilder()
.setCustomId("addauthor_cancel")
.setLabel("❌ Abbrechen")
.setStyle(ButtonStyle.Danger)
);
const confirmMsg = await ctx.reply({ embeds: [confirmEmbed], components: [row], fetchReply: true });
// Wait for button click (30 seconds)
let btnInteraction;
try {
btnInteraction = await confirmMsg.awaitMessageComponent({
filter: (i) => i.user.id === ctx.author.id,
time: 30_000,
});
} catch {
const timeoutEmbed = EmbedBuilder.from(confirmEmbed)
.setColor("#808080")
.setDescription("⏱️ Zeit abgelaufen Befehl wurde abgebrochen.");
return confirmMsg.edit({ embeds: [timeoutEmbed], components: [] });
}
if (btnInteraction.customId === "addauthor_cancel") {
const cancelEmbed = EmbedBuilder.from(confirmEmbed)
.setColor("#FF0000")
.setDescription("❌ Abgebrochen.");
return btnInteraction.update({ embeds: [cancelEmbed], components: [] });
}
// Defer immediately version fetching can take longer than 3 seconds
await btnInteraction.deferUpdate();
// Fetch latest versions and save
const added = [];
const failed = [];
for (const resource of willAdd) {
let latestVersion = "unbekannt";
try {
const res = await fetch(`https://api.spigotmc.org/legacy/update.php?resource=${resource.id}`);
if (res.ok) latestVersion = (await res.text()).trim();
} catch { /* ignore speichern ohne Version */ }
saveData.watchedResources.push({
resourceID: resource.id,
resourceName: resource.name,
channelID: channel.id,
lastCheckedVersion: latestVersion,
});
added.push(`✅ **${resource.name}** (v${latestVersion})`);
// Rate limiting
await new Promise((r) => setTimeout(r, 300));
}
try {
await fs.mkdir("./serverdata", { recursive: true });
await fs.writeFile(filePath, JSON.stringify(saveData, null, 2));
} catch (e) {
client.logger.error(e);
return btnInteraction.editReply({
content: "Beim Speichern der Daten ist ein Fehler aufgetreten.",
components: [],
});
}
const successEmbed = new EmbedBuilder()
.setColor("#00FF00")
.setTitle(`${added.length} Plugins von ${authorName} werden jetzt beobachtet`)
.setDescription(added.join("\n"))
.addFields([
{ name: "📢 Kanal", value: `<#${channel.id}>`, inline: true },
{ name: "📊 Gesamt", value: `${saveData.watchedResources.length} beobachtet`, inline: true },
])
.setFooter({ text: `Autor-ID: ${authorID}` })
.setTimestamp();
return btnInteraction.editReply({ embeds: [successEmbed], components: [] });
},
};