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()
|
data: new SlashCommandBuilder()
|
||||||
.setName('purge')
|
.setName('purge')
|
||||||
.setDescription('Bulk delete messages')
|
.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) {
|
async execute(interaction, context) {
|
||||||
if (!(await ensureMod(interaction, PermissionFlagsBits.ManageMessages))) return;
|
if (!(await ensureMod(interaction, PermissionFlagsBits.ManageMessages))) return;
|
||||||
const amount = interaction.options.getInteger('amount', true);
|
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;
|
let deletedCount = 0;
|
||||||
if (interaction.channel instanceof TextChannel) {
|
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;
|
deletedCount = deleted.size;
|
||||||
}
|
}
|
||||||
await createCase(context, {
|
await createCase(context, {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { moderationQueue } from '../../jobs.js';
|
import { moderationQueue } from '../../jobs.js';
|
||||||
import type { BotContext } from '../../types.js';
|
import type { BotContext } from '../../types.js';
|
||||||
import { PermissionFlagsBits, type ChatInputCommandInteraction } from 'discord.js';
|
import { PermissionFlagsBits, type ChatInputCommandInteraction } from 'discord.js';
|
||||||
|
import type { Prisma } from '@prisma/client';
|
||||||
|
|
||||||
type CreateCaseInput = {
|
type CreateCaseInput = {
|
||||||
guildId: string;
|
guildId: string;
|
||||||
@@ -8,7 +9,7 @@ type CreateCaseInput = {
|
|||||||
moderatorId: string;
|
moderatorId: string;
|
||||||
action: string;
|
action: string;
|
||||||
reason?: string;
|
reason?: string;
|
||||||
metadata?: Record<string, unknown>;
|
metadata?: Prisma.InputJsonValue;
|
||||||
};
|
};
|
||||||
|
|
||||||
export async function createCase(context: BotContext, input: CreateCaseInput) {
|
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