Compare commits

..

2 Commits

Author SHA1 Message Date
3da019b8c7 Docs: Add purge command to README and AGENTS-BOT.md
All checks were successful
Auto Build and Push Docker Image / build (push) Successful in 7s
- Added purge subcommands to admin command list
- Documented purge implementation details in AGENTS-BOT.md
- Listed Discord API constraints (100 message limit, 14 day filter)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 15:03:50 +01:00
b0f153f135 Add: Purge command for message deletion
Added /admin purge with subcommands:
- amount: Delete X messages (1-100)
- time: Delete messages from last X minutes/hours
- user: Delete X messages from specific user
- all: Delete all possible messages (max 100)

Features:
- Only works in the channel where executed
- Requires ModerateMembers permission
- Filters messages older than 14 days (Discord API limit)
- Logs purge actions to moderation log channel

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 15:01:44 +01:00
3 changed files with 190 additions and 1 deletions

View File

@@ -351,6 +351,29 @@ if (unit === 'perm') {
} }
``` ```
### 11. Purge Command (Message Deletion)
**Subcommands:**
- `purge amount <1-100>` - Delete X messages
- `purge time <duration> <min|hour>` - Delete messages from last X time
- `purge user <user> <1-100>` - Delete X messages from specific user
- `purge all` - Delete all possible messages (max 100)
**Discord API Constraints:**
- Max 100 messages per bulk delete
- Messages older than 14 days cannot be bulk deleted
- Requires `ModerateMembers` permission
```typescript
// Filter messages older than 14 days (Discord API limit)
const twoWeeksAgo = Date.now() - 14 * 24 * 60 * 60 * 1000;
const filtered = messages.filter((m: any) => m.createdTimestamp > twoWeeksAgo);
await channel.bulkDelete(filtered, true);
```
**Logging:** Purge actions are logged to the moderation log channel with event type `messages`.
## 🚀 Build & Deploy ## 🚀 Build & Deploy
```bash ```bash
@@ -388,5 +411,5 @@ docker-compose up -d --build
| `/timer` | add, remove, list, listall, help | Public/Admin | | `/timer` | add, remove, list, listall, help | Public/Admin |
| `/welcome` | setchannel, add, remove, list, help | Admin | | `/welcome` | setchannel, add, remove, list, help | Admin |
| `/log` | setchannel, enable, disable, list, help | Admin | | `/log` | setchannel, enable, disable, list, help | Admin |
| `/admin` | kick, warn, unwarn, mute, unmute, ban, unban, config, help | Admin | | `/admin` | kick, warn, unwarn, mute, unmute, ban, unban, purge, config, help | Admin |
| `/owner` | deploy, stats, servers, help | Owner | | `/owner` | deploy, stats, servers, help | Owner |

View File

@@ -93,6 +93,10 @@ npm run deploy
- `/admin unmute <user>` Nutzer entmuten - `/admin unmute <user>` Nutzer entmuten
- `/admin ban <user>` Nutzer bannen - `/admin ban <user>` Nutzer bannen
- `/admin unban <id>` Nutzer entbannen - `/admin unban <id>` Nutzer entbannen
- `/admin purge amount <1-100>` X Nachrichten löschen
- `/admin purge time <dauer> <min|hour>` Nachrichten der letzten X Zeit löschen
- `/admin purge user <user> <1-100>` X Nachrichten eines Nutzers löschen
- `/admin purge all` Alle Nachrichten löschen (max 100)
- `/admin config` Warn-System konfigurieren - `/admin config` Warn-System konfigurieren
- `/admin help` Admin-Hilfe - `/admin help` Admin-Hilfe

View File

@@ -137,6 +137,55 @@ const command: Command = {
option.setName('duration_value') option.setName('duration_value')
.setDescription('Dauer in Sekunden') .setDescription('Dauer in Sekunden')
.setMinValue(0))) .setMinValue(0)))
.addSubcommandGroup(group =>
group
.setName('purge')
.setDescription('Löscht Nachrichten im Channel')
.addSubcommand(subcommand =>
subcommand
.setName('amount')
.setDescription('Löscht X Nachrichten')
.addIntegerOption(option =>
option.setName('count')
.setDescription('Anzahl der Nachrichten (1-100)')
.setRequired(true)
.setMinValue(1)
.setMaxValue(100)))
.addSubcommand(subcommand =>
subcommand
.setName('time')
.setDescription('Löscht Nachrichten der letzten X Minuten/Stunden')
.addIntegerOption(option =>
option.setName('duration')
.setDescription('Dauer')
.setRequired(true)
.setMinValue(1))
.addStringOption(option =>
option.setName('unit')
.setDescription('Zeiteinheit')
.setRequired(true)
.addChoices(
{ name: 'Minuten', value: 'min' },
{ name: 'Stunden', value: 'hour' }
)))
.addSubcommand(subcommand =>
subcommand
.setName('user')
.setDescription('Löscht Nachrichten eines bestimmten Nutzers')
.addUserOption(option =>
option.setName('target')
.setDescription('Der Nutzer')
.setRequired(true))
.addIntegerOption(option =>
option.setName('count')
.setDescription('Anzahl der Nachrichten (1-100)')
.setRequired(true)
.setMinValue(1)
.setMaxValue(100)))
.addSubcommand(subcommand =>
subcommand
.setName('all')
.setDescription('Löscht alle möglichen Nachrichten (max 100)')))
.addSubcommand(subcommand => .addSubcommand(subcommand =>
subcommand subcommand
.setName('help') .setName('help')
@@ -162,6 +211,10 @@ const command: Command = {
{ name: '`/admin unmute <nutzer>`', value: 'Entmutet einen Nutzer.', inline: false }, { name: '`/admin unmute <nutzer>`', value: 'Entmutet einen Nutzer.', inline: false },
{ name: '`/admin ban <nutzer> [grund] [delete_days]`', value: 'Bannt einen Nutzer vom Server.\ndelete_days: Nachrichten der letzten X Tage löschen (0-7).', inline: false }, { name: '`/admin ban <nutzer> [grund] [delete_days]`', value: 'Bannt einen Nutzer vom Server.\ndelete_days: Nachrichten der letzten X Tage löschen (0-7).', inline: false },
{ name: '`/admin unban <id>`', value: 'Entbannt einen Nutzer (via ID oder @username).', inline: false }, { name: '`/admin unban <id>`', value: 'Entbannt einen Nutzer (via ID oder @username).', inline: false },
{ name: '`/admin purge amount <1-100>`', value: 'Löscht X Nachrichten im Channel.', inline: false },
{ name: '`/admin purge time <dauer> <min|hour>`', value: 'Löscht Nachrichten der letzten X Minuten/Stunden.', inline: false },
{ name: '`/admin purge user <nutzer> <1-100>`', value: 'Löscht X Nachrichten eines bestimmten Nutzers.', inline: false },
{ name: '`/admin purge all`', value: 'Löscht alle möglichen Nachrichten (max 100).', inline: false },
{ name: '`/admin config <setting> [wert]`', value: 'Konfiguriert das Warn-System.\nSettings: warn_threshold, warn_action, warn_mute_duration, show', inline: false }, { name: '`/admin config <setting> [wert]`', value: 'Konfiguriert das Warn-System.\nSettings: warn_threshold, warn_action, warn_mute_duration, show', inline: false },
{ name: 'Warn-System', value: 'Automatische Aktionen bei X Warnungen:\n`/admin config warn_action kick|mute|ban`', inline: false } { name: 'Warn-System', value: 'Automatische Aktionen bei X Warnungen:\n`/admin config warn_action kick|mute|ban`', inline: false }
) )
@@ -507,6 +560,115 @@ const command: Command = {
return; return;
} }
} }
// Handle purge subcommands
const subcommandGroup = interaction.options.getSubcommandGroup();
if (subcommandGroup === 'purge') {
const purgeSubcommand = interaction.options.getSubcommand();
const channel = interaction.channel;
if (!channel || !channel.isTextBased()) {
await interaction.reply({ content: '❌ Dieser Befehl kann nur in Text-Channels verwendet werden.', flags: [MessageFlags.Ephemeral] });
return;
}
// Defer reply since bulk delete can take time
await interaction.deferReply({ flags: [MessageFlags.Ephemeral] });
let deletedCount = 0;
try {
if (purgeSubcommand === 'amount') {
const count = interaction.options.getInteger('count')!;
const messages = await channel.messages.fetch({ limit: count + 1 }); // +1 for the command message
const twoWeeksAgo = Date.now() - 14 * 24 * 60 * 60 * 1000;
const filtered = messages.filter((m: any) => m.createdTimestamp > twoWeeksAgo);
await (channel as any).bulkDelete(filtered, true);
deletedCount = filtered.size - 1; // -1 for command message
await interaction.editReply(`${deletedCount} Nachrichten gelöscht.`);
}
else if (purgeSubcommand === 'time') {
const duration = interaction.options.getInteger('duration')!;
const unit = interaction.options.getString('unit')!;
const unitMs: Record<string, number> = {
min: 60 * 1000,
hour: 60 * 60 * 1000
};
const cutoff = Date.now() - (duration * unitMs[unit]);
const messages = await channel.messages.fetch({ limit: 100 });
const twoWeeksAgo = Date.now() - 14 * 24 * 60 * 60 * 1000;
const filtered = messages.filter((m: any) =>
m.createdTimestamp > cutoff &&
m.createdTimestamp > twoWeeksAgo
);
await (channel as any).bulkDelete(filtered, true);
deletedCount = filtered.size;
const unitName = unit === 'min' ? 'Minute(n)' : 'Stunde(n)';
await interaction.editReply(`${deletedCount} Nachrichten der letzten ${duration} ${unitName} gelöscht.`);
}
else if (purgeSubcommand === 'user') {
const target = interaction.options.getUser('target')!;
const count = interaction.options.getInteger('count')!;
const messages = await channel.messages.fetch({ limit: 100 });
const twoWeeksAgo = Date.now() - 14 * 24 * 60 * 60 * 1000;
const userMessages = messages.filter((m: any) =>
m.author.id === target.id &&
m.createdTimestamp > twoWeeksAgo
).first(count);
if (userMessages.length === 0) {
await interaction.editReply(`Keine Nachrichten von ${target.tag} gefunden.`);
return;
}
await (channel as any).bulkDelete(userMessages, true);
deletedCount = userMessages.length;
await interaction.editReply(`${deletedCount} Nachrichten von ${target.tag} gelöscht.`);
}
else if (purgeSubcommand === 'all') {
const messages = await channel.messages.fetch({ limit: 100 });
const twoWeeksAgo = Date.now() - 14 * 24 * 60 * 60 * 1000;
const filtered = messages.filter((m: any) => m.createdTimestamp > twoWeeksAgo);
await (channel as any).bulkDelete(filtered, true);
deletedCount = filtered.size;
await interaction.editReply(`${deletedCount} Nachrichten gelöscht.`);
}
// Log the purge action
if (deletedCount > 0) {
const embed = new EmbedBuilder()
.setTitle('🗑️ Nachrichten gelöscht (Purge)')
.setColor(0x9b59b6)
.addFields(
{ name: 'Channel', value: `<#${channel.id}>`, inline: true },
{ name: 'Moderator', value: `${interaction.user.tag} (<@${interaction.user.id}>)`, inline: true },
{ name: 'Anzahl', value: `${deletedCount} Nachrichten`, inline: true },
{ name: 'Typ', value: purgeSubcommand === 'amount' ? 'Nach Anzahl' :
purgeSubcommand === 'time' ? 'Nach Zeit' :
purgeSubcommand === 'user' ? 'Nach Nutzer' : 'Alle', inline: true }
)
.setTimestamp();
await sendModerationLog(interaction.client, guildId, 'messages', embed);
}
} catch (error) {
console.error('[ADMIN] Purge error:', error);
await interaction.editReply('❌ Fehler beim Löschen der Nachrichten.');
}
return;
}
}, },
}; };