Feature-Voting, Giveaways und Contribution-Heatmap
- /wunsch: Voting-Embed mit 👍, Reaction-Tracking (add/remove) in SQLite, Top-20-Rangliste öffentlich auf der Roadmap-Seite - /giveaway (Admin): Preis/Dauer/Gewinnerzahl, 🎉-Teilnahme-Button (toggle), Minuten-Scheduler zieht Gewinner, schließt das Embed ab und pingt sie - Contribution-Heatmap: 52-Wochen-Grid in Brand-Gelb aus dem Commit-Archiv (/api/heatmap) auf der Roadmap-Seite - Setting voting_channel_id auf der Setup-Seite Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+35
-2
@@ -5,15 +5,17 @@ import { devlogChannelId, devlogPingRoleId, playtesterRoleId } from '../runtime-
|
||||
import { archiveDevlogMessage, removeDevlog } from './devlog-archive.js';
|
||||
import { registerCommunityListeners } from './community.js';
|
||||
import { registerModTools } from './mod-tools.js';
|
||||
import { addPlaytester, removePlaytester } from '../db.js';
|
||||
import { addPlaytester, removePlaytester, isWish, bumpWish, getGiveaway, toggleGiveawayEntry, giveawayEntries } 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';
|
||||
import * as wunsch from './commands/wunsch.js';
|
||||
import * as giveaway from './commands/giveaway.js';
|
||||
|
||||
// Alle Commands hier eintragen — jedes Modul exportiert { data, execute }
|
||||
const commandModules = [ping, devlogBackfill, bug, playtesterSetup, galerieBackfill];
|
||||
const commandModules = [ping, devlogBackfill, bug, playtesterSetup, galerieBackfill, wunsch, giveaway];
|
||||
|
||||
export async function startBot() {
|
||||
const client = new Client({
|
||||
@@ -57,6 +59,19 @@ export async function startBot() {
|
||||
}
|
||||
});
|
||||
|
||||
// 👍-Reaktionen auf Feature-Wünsche zählen (für die Rangliste auf der Webseite)
|
||||
const trackWishReaction = (delta) => async (reaction) => {
|
||||
try {
|
||||
if (reaction.emoji.name !== '👍') return;
|
||||
if (reaction.partial) await reaction.fetch().catch(() => {});
|
||||
if (isWish(reaction.message.id)) bumpWish(reaction.message.id, delta);
|
||||
} catch (error) {
|
||||
console.error('[voting] Reaction-Tracking fehlgeschlagen:', error);
|
||||
}
|
||||
};
|
||||
client.on(Events.MessageReactionAdd, trackWishReaction(1));
|
||||
client.on(Events.MessageReactionRemove, trackWishReaction(-1));
|
||||
|
||||
// In Discord gelöscht = im Archiv gelöscht (inkl. lokaler Bilder)
|
||||
client.on(Events.MessageDelete, async (message) => {
|
||||
if (message.channelId !== devlogChannelId()) return;
|
||||
@@ -102,6 +117,24 @@ export async function startBot() {
|
||||
return;
|
||||
}
|
||||
|
||||
// 🎉-Button: Giveaway-Teilnahme togglen
|
||||
if (interaction.isButton() && interaction.customId === 'giveaway_enter') {
|
||||
const g = getGiveaway(interaction.message.id);
|
||||
if (!g || g.ended) {
|
||||
await interaction.reply({ content: '❌ Dieses Giveaway ist schon vorbei.', flags: MessageFlags.Ephemeral }).catch(() => {});
|
||||
return;
|
||||
}
|
||||
const entered = toggleGiveawayEntry(g.message_id, interaction.user.id);
|
||||
const count = giveawayEntries(g.message_id).length;
|
||||
await interaction.reply({
|
||||
content: entered
|
||||
? `🎉 Du bist dabei! (${count} Teilnahmen)`
|
||||
: `🚪 Teilnahme zurückgezogen. (${count} Teilnahmen)`,
|
||||
flags: MessageFlags.Ephemeral,
|
||||
}).catch(() => {});
|
||||
return;
|
||||
}
|
||||
|
||||
// 🧪-Button: Playtester-Rolle + Liste togglen
|
||||
if (interaction.isButton() && interaction.customId === 'playtester_toggle') {
|
||||
const roleId = playtesterRoleId();
|
||||
|
||||
Reference in New Issue
Block a user