From 8690e1e80af391dd5b378df4b800daee9719b1e8 Mon Sep 17 00:00:00 2001 From: sarah Date: Mon, 3 Aug 2026 19:06:59 +0200 Subject: [PATCH] feat: make /stopwatch status public & restrict start/stop to mods --- README.md | 8 +-- src/commands/utility/stopwatch.ts | 87 ++++++++++++++++++------------- 2 files changed, 56 insertions(+), 39 deletions(-) diff --git a/README.md b/README.md index 95ad80f..9b92de4 100644 --- a/README.md +++ b/README.md @@ -90,10 +90,10 @@ npm run deploy - `/timer help` – Timer-Hilfe ### Stoppuhr (Hochzählender Timer) -- `/stopwatch start [startzeit]` – Startet eine neue Stoppuhr mit Namen (optional mit vergangener Startzeit z.B. `14:30`, `2h`) -- `/stopwatch status [name]` – Abgelaufene Zeit abfragen -- `/stopwatch stop ` – Stoppuhr beenden & Laufzeit ausgeben -- `/stopwatch list` – Aktive Stoppuhren auflisten +- `/stopwatch start [startzeit]` – Startet eine neue Stoppuhr mit Namen (Mod) +- `/stopwatch status [name]` – Abgelaufene Zeit öffentlich abfragen (Für alle sichtbar) +- `/stopwatch stop ` – Stoppuhr beenden & Laufzeit ausgeben (Mod) +- `/stopwatch list` – Aktive Stoppuhren des Servers öffentlich auflisten (Für alle sichtbar) - `/stopwatch help` – Stoppuhr-Hilfe ### Trigger (Auto-Antworten) diff --git a/src/commands/utility/stopwatch.ts b/src/commands/utility/stopwatch.ts index b2693bf..6a93c07 100644 --- a/src/commands/utility/stopwatch.ts +++ b/src/commands/utility/stopwatch.ts @@ -1,4 +1,4 @@ -import { SlashCommandBuilder, EmbedBuilder } from 'discord.js'; +import { SlashCommandBuilder, EmbedBuilder, PermissionFlagsBits } from 'discord.js'; import { Command } from '../../structures/Command.js'; import { ExtendedClient } from '../../structures/ExtendedClient.js'; @@ -92,7 +92,7 @@ const command: Command = { .addSubcommand(subcommand => subcommand .setName('start') - .setDescription('Startet eine neue Stoppuhr.') + .setDescription('Startet eine neue Stoppuhr (Nur für Moderatoren).') .addStringOption(option => option.setName('name') .setDescription('Der Name der Stoppuhr (z.B. "Gaming", "Lernen")') @@ -104,7 +104,7 @@ const command: Command = { .addSubcommand(subcommand => subcommand .setName('status') - .setDescription('Zeigt den aktuellen Stand einer oder aller Stoppuhren an.') + .setDescription('Zeigt den aktuellen Stand einer oder aller Stoppuhren öffentlich an.') .addStringOption(option => option.setName('name') .setDescription('Der Name der Stoppuhr (optional)') @@ -112,7 +112,7 @@ const command: Command = { .addSubcommand(subcommand => subcommand .setName('stop') - .setDescription('Stoppt eine laufende Stoppuhr.') + .setDescription('Stoppt eine laufende Stoppuhr (Nur für Moderatoren).') .addStringOption(option => option.setName('name') .setDescription('Der Name der zu stoppenden Stoppuhr') @@ -120,7 +120,7 @@ const command: Command = { .addSubcommand(subcommand => subcommand .setName('list') - .setDescription('Listet alle deine aktiven Stoppuhren auf.')) + .setDescription('Listet alle aktiven Stoppuhren auf diesem Server öffentlich auf.')) .addSubcommand(subcommand => subcommand .setName('help') @@ -135,17 +135,21 @@ const command: Command = { const channelId = interaction.channelId; const DB = client.DB; + const isMod = interaction.memberPermissions?.has(PermissionFlagsBits.ModerateMembers) || + interaction.memberPermissions?.has(PermissionFlagsBits.ManageGuild) || + interaction.memberPermissions?.has(PermissionFlagsBits.Administrator); + if (subcommand === 'help') { const embed = new EmbedBuilder() .setTitle('⏱️ Stoppuhr Hilfe') .setColor(0x3498db) - .setDescription('Verwalte hochzählende Timer (Stoppuhren).') + .setDescription('Verwalte hochzählende Timer (Stoppuhren). Status und Abfragen sind für alle Mitglieder öffentlich sichtbar.') .addFields( - { name: '`/stopwatch start [startzeit]`', value: 'Startet eine neue Stoppuhr mit Namen.\nOptional kann eine vergangene Startzeit angegeben werden (z.B. `14:30`, `03.08.2026 14:30` oder `2h`).', inline: false }, - { name: '`/stopwatch status [name]`', value: 'Zeigt die aktuelle Laufzeit einer oder aller Stoppuhren an.', inline: false }, - { name: '`/stopwatch stop `', value: 'Stoppt eine Stoppuhr und gibt die finale Laufzeit aus.', inline: false }, - { name: '`/stopwatch list`', value: 'Listet alle deine aktiven Stoppuhren auf diesem Server auf.', inline: false }, - { name: 'Beispiele', value: '`/stopwatch start name: Zocksession`\n`/stopwatch start name: Lernen startzeit: 14:00`\n`/stopwatch start name: Workout startzeit: 30m`\n`/stopwatch status name: Zocksession`\n`/stopwatch stop name: Zocksession`', inline: false } + { name: '`/stopwatch start [startzeit]` 🛡️', value: '*(Mods/Admins)* Startet eine neue Stoppuhr mit Namen.\nOptional kann eine vergangene Startzeit angegeben werden (z.B. `14:30`, `03.08.2026 14:30` oder `2h`).', inline: false }, + { name: '`/stopwatch status [name]` 📢', value: '*(Für alle sichtbar)* Zeigt die aktuelle Laufzeit einer oder aller Stoppuhren öffentlich im Kanal an.', inline: false }, + { name: '`/stopwatch stop ` 🛡️', value: '*(Mods/Admins)* Stoppt eine Stoppuhr und gibt die finale Laufzeit aus.', inline: false }, + { name: '`/stopwatch list` 📢', value: '*(Für alle sichtbar)* Listet alle aktiven Stoppuhren auf diesem Server öffentlich auf.', inline: false }, + { name: 'Beispiele', value: '`/stopwatch start name: Zocksession`\n`/stopwatch start name: Lernen startzeit: 14:00`\n`/stopwatch status name: Zocksession`\n`/stopwatch stop name: Zocksession`', inline: false } ) .setTimestamp(); @@ -154,14 +158,21 @@ const command: Command = { } if (subcommand === 'start') { + if (!isMod) { + await interaction.reply({ + content: '❌ Du benötigst Moderations-Berechtigungen (z.B. Mitglieder verwalten / Server verwalten), um Stoppuhren zu starten.', + ephemeral: true + }); + return; + } + const name = interaction.options.getString('name')!.trim(); const startInput = interaction.options.getString('startzeit')?.trim(); - // Duplicate check (case-insensitive) + // Duplicate check on this guild (case-insensitive) const existing: any = DB.get( - 'SELECT * FROM stopwatches WHERE guild_id = ? AND user_id = ? AND LOWER(name) = ?', + 'SELECT * FROM stopwatches WHERE guild_id = ? AND LOWER(name) = ?', guildId, - userId, name.toLowerCase() ); @@ -169,7 +180,7 @@ const command: Command = { const startTime = new Date(existing.start_time); const unixTs = Math.floor(startTime.getTime() / 1000); await interaction.reply({ - content: `⚠️ Du hast bereits eine Stoppuhr namens **"${existing.name}"** laufen (gestartet ).\nNutze \`/stopwatch status name:${existing.name}\` oder \`/stopwatch stop name:${existing.name}\`.`, + content: `⚠️ Es gibt auf diesem Server bereits eine Stoppuhr namens **"${existing.name}"** (gestartet ).\nNutze \`/stopwatch status name:${existing.name}\` oder \`/stopwatch stop name:${existing.name}\`.`, ephemeral: true }); return; @@ -218,7 +229,7 @@ const command: Command = { ) .setTimestamp(); - await interaction.reply({ embeds: [embed], ephemeral: true }); + await interaction.reply({ embeds: [embed], ephemeral: false }); return; } @@ -227,15 +238,14 @@ const command: Command = { if (name) { const sw: any = DB.get( - 'SELECT * FROM stopwatches WHERE guild_id = ? AND user_id = ? AND LOWER(name) = ?', + 'SELECT * FROM stopwatches WHERE guild_id = ? AND LOWER(name) = ?', guildId, - userId, name.toLowerCase() ); if (!sw) { await interaction.reply({ - content: `❌ Keine aktive Stoppuhr namens **"${name}"** gefunden.`, + content: `❌ Keine aktive Stoppuhr namens **"${name}"** auf diesem Server gefunden.`, ephemeral: true }); return; @@ -255,18 +265,18 @@ const command: Command = { ) .setTimestamp(); - await interaction.reply({ embeds: [embed], ephemeral: true }); + // Public output for status query + await interaction.reply({ embeds: [embed], ephemeral: false }); return; } else { const stopwatches: any[] = DB.all( - 'SELECT * FROM stopwatches WHERE guild_id = ? AND user_id = ? ORDER BY start_time ASC', - guildId, - userId + 'SELECT * FROM stopwatches WHERE guild_id = ? ORDER BY start_time ASC', + guildId ); if (stopwatches.length === 0) { await interaction.reply({ - content: 'ℹ️ Du hast aktuell keine aktiven Stoppuhren.', + content: 'ℹ️ Auf diesem Server gibt es aktuell keine aktiven Stoppuhren.', ephemeral: true }); return; @@ -281,29 +291,37 @@ const command: Command = { }).join('\n'); const embed = new EmbedBuilder() - .setTitle(`⏱️ Deine aktiven Stoppuhren (${stopwatches.length})`) + .setTitle(`⏱️ Aktive Stoppuhren auf diesem Server (${stopwatches.length})`) .setColor(0x3498db) .setDescription(listStr) .setTimestamp(); - await interaction.reply({ embeds: [embed], ephemeral: true }); + // Public output for list query + await interaction.reply({ embeds: [embed], ephemeral: false }); return; } } if (subcommand === 'stop') { + if (!isMod) { + await interaction.reply({ + content: '❌ Du benötigst Moderations-Berechtigungen (z.B. Mitglieder verwalten / Server verwalten), um Stoppuhren zu stoppen.', + ephemeral: true + }); + return; + } + const name = interaction.options.getString('name')!.trim(); const sw: any = DB.get( - 'SELECT * FROM stopwatches WHERE guild_id = ? AND user_id = ? AND LOWER(name) = ?', + 'SELECT * FROM stopwatches WHERE guild_id = ? AND LOWER(name) = ?', guildId, - userId, name.toLowerCase() ); if (!sw) { await interaction.reply({ - content: `❌ Keine aktive Stoppuhr namens **"${name}"** gefunden.`, + content: `❌ Keine aktive Stoppuhr namens **"${name}"** auf diesem Server gefunden.`, ephemeral: true }); return; @@ -326,20 +344,19 @@ const command: Command = { ) .setTimestamp(); - await interaction.reply({ embeds: [embed], ephemeral: true }); + await interaction.reply({ embeds: [embed], ephemeral: false }); return; } if (subcommand === 'list') { const stopwatches: any[] = DB.all( - 'SELECT * FROM stopwatches WHERE guild_id = ? AND user_id = ? ORDER BY start_time ASC', - guildId, - userId + 'SELECT * FROM stopwatches WHERE guild_id = ? ORDER BY start_time ASC', + guildId ); if (stopwatches.length === 0) { await interaction.reply({ - content: 'ℹ️ Du hast aktuell keine aktiven Stoppuhren.', + content: 'ℹ️ Auf diesem Server gibt es aktuell keine aktiven Stoppuhren.', ephemeral: true }); return; @@ -359,7 +376,7 @@ const command: Command = { .setDescription(listStr) .setTimestamp(); - await interaction.reply({ embeds: [embed], ephemeral: true }); + await interaction.reply({ embeds: [embed], ephemeral: false }); } }, };