Update from Git Manager GUI

This commit is contained in:
2026-02-25 18:51:08 +01:00
parent de4341a0a9
commit 1567151fce
21 changed files with 2023 additions and 0 deletions

216
commands/addauthor.js Normal file
View File

@@ -0,0 +1,216 @@
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: [] });
},
};