Enhance purge command with additional filtering options
- Added user, bots, links, attachments, and regex options to the purge command for more granular message deletion. - Updated the command execution logic to filter messages based on the new options before bulk deletion. - Modified CreateCaseInput type to use Prisma.InputJsonValue for metadata.
This commit is contained in:
@@ -356,13 +356,49 @@ const purgeCommand: SlashCommand = {
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('purge')
|
||||
.setDescription('Bulk delete messages')
|
||||
.addIntegerOption((o) => o.setName('amount').setDescription('2-100').setRequired(true).setMinValue(2).setMaxValue(100)),
|
||||
.addIntegerOption((o) =>
|
||||
o.setName('amount').setDescription('2-100').setRequired(true).setMinValue(2).setMaxValue(100)
|
||||
)
|
||||
.addUserOption((o) => o.setName('user').setDescription('Only purge this user messages'))
|
||||
.addBooleanOption((o) => o.setName('bots').setDescription('Only purge bot messages'))
|
||||
.addBooleanOption((o) => o.setName('links').setDescription('Only purge messages containing links'))
|
||||
.addBooleanOption((o) =>
|
||||
o.setName('attachments').setDescription('Only purge messages with attachments')
|
||||
)
|
||||
.addStringOption((o) =>
|
||||
o.setName('regex').setDescription('Only purge messages matching regex')
|
||||
),
|
||||
async execute(interaction, context) {
|
||||
if (!(await ensureMod(interaction, PermissionFlagsBits.ManageMessages))) return;
|
||||
const amount = interaction.options.getInteger('amount', true);
|
||||
const targetUser = interaction.options.getUser('user');
|
||||
const botsOnly = interaction.options.getBoolean('bots') ?? false;
|
||||
const linksOnly = interaction.options.getBoolean('links') ?? false;
|
||||
const attachmentsOnly = interaction.options.getBoolean('attachments') ?? false;
|
||||
const regexInput = interaction.options.getString('regex');
|
||||
let regex: RegExp | null = null;
|
||||
if (regexInput) {
|
||||
try {
|
||||
regex = new RegExp(regexInput, 'i');
|
||||
} catch {
|
||||
await interaction.reply({ content: 'Invalid regex pattern.', ephemeral: true });
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let deletedCount = 0;
|
||||
if (interaction.channel instanceof TextChannel) {
|
||||
const deleted = await interaction.channel.bulkDelete(amount, true);
|
||||
const fetched = await interaction.channel.messages.fetch({ limit: 100 });
|
||||
const filtered = fetched.filter((message) => {
|
||||
if (targetUser && message.author.id !== targetUser.id) return false;
|
||||
if (botsOnly && !message.author.bot) return false;
|
||||
if (linksOnly && !/(https?:\/\/|discord\.gg\/)/i.test(message.content)) return false;
|
||||
if (attachmentsOnly && message.attachments.size === 0) return false;
|
||||
if (regex && !regex.test(message.content)) return false;
|
||||
return true;
|
||||
});
|
||||
const toDelete = filtered.first(amount);
|
||||
const deleted = await interaction.channel.bulkDelete(toDelete, true);
|
||||
deletedCount = deleted.size;
|
||||
}
|
||||
await createCase(context, {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { moderationQueue } from '../../jobs.js';
|
||||
import type { BotContext } from '../../types.js';
|
||||
import { PermissionFlagsBits, type ChatInputCommandInteraction } from 'discord.js';
|
||||
import type { Prisma } from '@prisma/client';
|
||||
|
||||
type CreateCaseInput = {
|
||||
guildId: string;
|
||||
@@ -8,7 +9,7 @@ type CreateCaseInput = {
|
||||
moderatorId: string;
|
||||
action: string;
|
||||
reason?: string;
|
||||
metadata?: Record<string, unknown>;
|
||||
metadata?: Prisma.InputJsonValue;
|
||||
};
|
||||
|
||||
export async function createCase(context: BotContext, input: CreateCaseInput) {
|
||||
|
||||
BIN
docs/logo/nexumi-logo.png
Normal file
BIN
docs/logo/nexumi-logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 99 KiB |
37
docs/logo/nexumi-logo.svg
Normal file
37
docs/logo/nexumi-logo.svg
Normal file
@@ -0,0 +1,37 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" role="img" aria-label="Nexumi logo">
|
||||
<defs>
|
||||
<linearGradient id="bg" x1="0" y1="0" x2="1" y2="1">
|
||||
<stop offset="0" stop-color="#181C2B"/>
|
||||
<stop offset="1" stop-color="#0C0E15"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="mark" x1="0" y1="1" x2="1" y2="0">
|
||||
<stop offset="0" stop-color="#6366F1"/>
|
||||
<stop offset="1" stop-color="#8B5CF6"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<!-- App-Icon-Grundflaeche -->
|
||||
<rect width="512" height="512" rx="116" fill="url(#bg)"/>
|
||||
<rect x="1.5" y="1.5" width="509" height="509" rx="114.5" fill="none" stroke="#FFFFFF" stroke-opacity="0.06" stroke-width="3"/>
|
||||
|
||||
<!-- Glow: gestaffelte Deckkraft statt Filter, rendersicher -->
|
||||
<g fill="none" stroke="url(#mark)" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M170 362 V150 L342 362 V150" stroke-width="78" stroke-opacity="0.10"/>
|
||||
<path d="M170 362 V150 L342 362 V150" stroke-width="52" stroke-opacity="0.16"/>
|
||||
</g>
|
||||
|
||||
<!-- Kanten des N -->
|
||||
<path d="M170 362 V150 L342 362 V150" fill="none" stroke="url(#mark)"
|
||||
stroke-width="30" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
|
||||
<!-- Knoten -->
|
||||
<g fill="url(#mark)">
|
||||
<circle cx="170" cy="150" r="34"/>
|
||||
<circle cx="170" cy="362" r="34"/>
|
||||
<circle cx="342" cy="362" r="34"/>
|
||||
<circle cx="342" cy="150" r="34"/>
|
||||
</g>
|
||||
|
||||
<!-- Aktiver Knoten oben rechts -->
|
||||
<circle cx="342" cy="150" r="14" fill="#C7D2FE"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
Reference in New Issue
Block a user