Only show guides for your app
A /purge command that clears a set number of recent messages — optionally only from one member — with the 14-day limit handled and the reply kept private.
This recipe gives your discord.js bot a /purge command: pick a number from 1 to 100 and it clears that many recent messages from the channel, optionally filtered to a single member. It uses Discord's bulk-delete, which is fast but has one important rule this code handles for you.
| At a glance | |
|---|---|
| You need | a working bot from Host a discord.js bot |
| Plan | Free or premium |
| Time | About fifteen minutes |
This builds directly on the discord.js bot guide — you should already have a bot answering /ping. We reuse its structure and add one command.
The complete index.js:
require('dotenv').config();
const {
Client, Events, GatewayIntentBits,
SlashCommandBuilder, PermissionFlagsBits,
} = require('discord.js');
const client = new Client({ intents: [GatewayIntentBits.Guilds] });
const purge = new SlashCommandBuilder()
.setName('purge')
.setDescription('Bulk-delete recent messages in this channel')
.addIntegerOption(o =>
o.setName('amount')
.setDescription('How many messages (1-100)')
.setRequired(true)
.setMinValue(1)
.setMaxValue(100))
.addUserOption(o =>
o.setName('from')
.setDescription('Only delete messages from this member'))
.setDefaultMemberPermissions(PermissionFlagsBits.ManageMessages);
client.once(Events.ClientReady, async (c) => {
await c.application.commands.set([purge]);
console.log(`Listening as ${c.user.tag}`);
});
client.on(Events.InteractionCreate, async (interaction) => {
if (!interaction.isChatInputCommand()) return;
if (interaction.commandName !== 'purge') return;
const amount = interaction.options.getInteger('amount');
const from = interaction.options.getUser('from');
await interaction.deferReply({ ephemeral: true });
let messages = await interaction.channel.messages.fetch({ limit: amount });
if (from) messages = messages.filter(m => m.author.id === from.id);
const deleted = await interaction.channel.bulkDelete(messages, true);
await interaction.editReply(
`🧹 Deleted ${deleted.size} message(s).` +
(deleted.size < amount ? ' (Messages older than 14 days can\'t be bulk-deleted.)' : '')
);
});
client.login(process.env.DISCORD_TOKEN);
Paste it into index.js and restart.
.setMinValue(1).setMaxValue(100) on the integer option keeps the amount in Discord's legal range. Bulk-delete refuses fewer than 2 or more than 100 messages at once, and Discord enforces these bounds before your handler runs.interaction.deferReply({ ephemeral: true }) does two jobs. It buys you more than the usual three seconds (fetching and deleting takes a moment), and ephemeral: true makes the confirmation visible only to the moderator who ran the command — so your "deleted 50 messages" reply doesn't become the newest thing in a freshly-cleared channel.channel.messages.fetch({ limit: amount }) pulls the most recent messages so you can count and optionally filter them. When a from user is given, .filter() keeps only theirs.channel.bulkDelete(messages, true) is the delete. That true is the key: it tells discord.js to silently skip messages older than 14 days instead of throwing an error. Without it, one old message in the batch would fail the entire call.🎯 Good to know: Discord's bulk-delete API physically cannot remove messages older than 14 days — that's a platform rule, not a bot limitation. The
trueflag and the "older than 14 days" note in the reply are how this recipe stays honest about it. To clear ancient messages you'd delete them one at a time, which is slow and rate-limited.
No privileged intents — GatewayIntentBits.Guilds is all you need, nothing to toggle in the Developer Portal.
The bot's role needs two permissions in the server:
| Permission | Why |
|---|---|
| Manage Messages | to delete other people's messages |
| Read Message History | to fetch the messages before deleting them |
setDefaultMemberPermissions(PermissionFlagsBits.ManageMessages) also hides the command from members who can't Manage Messages, so only your moderators see /purge.
.filter(m => m.content.includes('spam')) deletes only matching messages — handy for cleaning up one bad link.&& !m.pinned to your filter so important pins survive a purge.true second argument to bulkDelete. Add it; the code above already does.from filter matched nobody. Check the channel's age and the user you picked.deferReply() in the code prevents this; make sure it's still there and runs before the fetch.