Community-Paket: Playtester-Programm, Starboard, Screenshot-Galerie

- Playtester: /playtester-setup postet Bewerbungs-Embed mit 🧪-Button;
  Toggle vergibt Rolle + DB-Eintrag; Liste + Rollen-Auswahl auf der Setup-Seite
- Starboard: ab konfigurierbarem -Schwellwert Repost als Embed in den
  Best-of-Kanal (Dedupe, kein Selbst-Boarding); neue Intents/Partials für Reaktionen
- Galerie: Bilder aus dem Screenshot-Kanal lokal archiviert (live + /galerie-backfill,
  Delete-Sync, Bot reagiert mit ) → öffentliche /galerie-Seite mit Hover-Grid
- Setup-Seite: Sektion // Community (Rolle, Starboard-Kanal + Schwellwert, Screenshot-Kanal)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-23 12:34:34 +02:00
co-authored by Claude Opus 4.8
parent 6d8f383aed
commit a3c47656c5
13 changed files with 563 additions and 12 deletions
+41 -4
View File
@@ -1,14 +1,18 @@
// Discord-Client: Commands laden, registrieren und Interactions verarbeiten
import { Client, Collection, Events, GatewayIntentBits, MessageFlags, Partials, REST, Routes } from 'discord.js';
import { config } from '../config.js';
import { devlogChannelId, devlogPingRoleId } from '../runtime-settings.js';
import { devlogChannelId, devlogPingRoleId, playtesterRoleId } from '../runtime-settings.js';
import { archiveDevlogMessage, removeDevlog } from './devlog-archive.js';
import { registerCommunityListeners } from './community.js';
import { addPlaytester, removePlaytester } from '../db.js';
import * as ping from './commands/ping.js';
import * as devlogBackfill from './commands/devlog-backfill.js';
import * as bug from './commands/bug.js';
import * as playtesterSetup from './commands/playtester-setup.js';
import * as galerieBackfill from './commands/galerie-backfill.js';
// Alle Commands hier eintragen — jedes Modul exportiert { data, execute }
const commandModules = [ping, devlogBackfill, bug];
const commandModules = [ping, devlogBackfill, bug, playtesterSetup, galerieBackfill];
export async function startBot() {
const client = new Client({
@@ -18,9 +22,11 @@ export async function startBot() {
// (erfordert aktivierten "Message Content Intent" im Developer Portal!)
GatewayIntentBits.GuildMessages,
GatewayIntentBits.MessageContent,
// Für das Starboard (⭐-Reaktionen)
GatewayIntentBits.GuildMessageReactions,
],
// Partials, damit MessageDelete auch für ungecachte (ältere) Nachrichten feuert
partials: [Partials.Message],
// Partials, damit Delete/Reaction-Events auch für ungecachte Nachrichten feuern
partials: [Partials.Message, Partials.Reaction],
});
// Commands in Collection ablegen für schnellen Zugriff im Interaction-Handler
@@ -91,6 +97,35 @@ export async function startBot() {
return;
}
// 🧪-Button: Playtester-Rolle + Liste togglen
if (interaction.isButton() && interaction.customId === 'playtester_toggle') {
const roleId = playtesterRoleId();
try {
if (!roleId || !interaction.inGuild()) throw new Error('Keine Playtester-Rolle konfiguriert');
const member = interaction.member;
const hasRole = member.roles.cache.has(roleId);
if (hasRole) {
await member.roles.remove(roleId);
removePlaytester(interaction.user.id);
} else {
await member.roles.add(roleId);
addPlaytester(interaction.user.id, interaction.user.username);
}
await interaction.reply({
content: hasRole
? '🚪 Von der Playtester-Liste **abgemeldet**.'
: '🧪 **Willkommen im Playtest-Team!** Du stehst auf der Liste — sobald es losgeht, meldet sich der Bot per DM.',
flags: MessageFlags.Ephemeral,
});
} catch (error) {
console.error('[playtester] Toggle fehlgeschlagen:', error);
await interaction
.reply({ content: '❌ Konnte die Rolle nicht ändern — Rechte/Rollen-Reihenfolge prüfen.', flags: MessageFlags.Ephemeral })
.catch(() => {});
}
return;
}
if (!interaction.isChatInputCommand()) return;
const command = client.commands.get(interaction.commandName);
@@ -109,6 +144,8 @@ export async function startBot() {
}
});
registerCommunityListeners(client);
await client.login(config.discordToken);
return client;
}