Deleting all messages in discord.js text channel - node.js

Ok, so I searched for a while, but I couldn't find any information on how to delete all messages in a discord channel. And by all messages I mean every single message ever written in that channel. Any clues?

Try this
async () => {
let fetched;
do {
fetched = await channel.fetchMessages({limit: 100});
message.channel.bulkDelete(fetched);
}
while(fetched.size >= 2);
}

Discord does not allow bots to delete more than 100 messages, so you can't delete every message in a channel. You can delete less then 100 messages, using BulkDelete.
Example:
const Discord = require("discord.js");
const client = new Discord.Client();
const prefix = "!";
client.on("ready" () => {
console.log("Successfully logged into client.");
});
client.on("message", msg => {
if (msg.content.toLowerCase().startsWith(prefix + "clearchat")) {
async function clear() {
msg.delete();
const fetched = await msg.channel.fetchMessages({limit: 99});
msg.channel.bulkDelete(fetched);
}
clear();
}
});
client.login("BOT_TOKEN");
Note, it has to be in a async function for the await to work.

Here's my improved version that is quicker and lets you know when its done in the console but you'll have to run it for each username that you used in a channel (if you changed your username at some point):
// Turn on Developer Mode under User Settings > Appearance > Developer Mode (at the bottom)
// Then open the channel you wish to delete all of the messages (could be a DM) and click the three dots on the far right.
// Click "Copy ID" and paste that instead of LAST_MESSAGE_ID.
// Copy / paste the below script into the JavaScript console.
var before = 'LAST_MESSAGE_ID';
var your_username = ''; //your username
var your_discriminator = ''; //that 4 digit code e.g. username#1234
var foundMessages = false;
clearMessages = function(){
const authToken = document.body.appendChild(document.createElement`iframe`).contentWindow.localStorage.token.replace(/"/g, "");
const channel = window.location.href.split('/').pop();
const baseURL = `https://discordapp.com/api/channels/${channel}/messages`;
const headers = {"Authorization": authToken };
let clock = 0;
let interval = 500;
function delay(duration) {
return new Promise((resolve, reject) => {
setTimeout(() => resolve(), duration);
});
}
fetch(baseURL + '?before=' + before + '&limit=100', {headers})
.then(resp => resp.json())
.then(messages => {
return Promise.all(messages.map((message) => {
before = message.id;
foundMessages = true;
if (
message.author.username == your_username
&& message.author.discriminator == your_discriminator
) {
return delay(clock += interval).then(() => fetch(`${baseURL}/${message.id}`, {headers, method: 'DELETE'}));
}
}));
}).then(() => {
if (foundMessages) {
foundMessages = false;
clearMessages();
} else {
console.log('DONE CHECKING CHANNEL!!!')
}
});
}
clearMessages();
The previous script I found for deleting your own messages without a bot...
// Turn on Developer Mode under User Settings > Appearance > Developer Mode (at the bottom)
// Then open the channel you wish to delete all of the messages (could be a DM) and click the three dots on the far right.
// Click "Copy ID" and paste that instead of LAST_MESSAGE_ID.
// Copy / paste the below script into the JavaScript console.
// If you're in a DM you will receive a 403 error for every message the other user sent (you don't have permission to delete their messages).
var before = 'LAST_MESSAGE_ID';
clearMessages = function(){
const authToken = document.body.appendChild(document.createElement`iframe`).contentWindow.localStorage.token.replace(/"/g, "");
const channel = window.location.href.split('/').pop();
const baseURL = `https://discordapp.com/api/channels/${channel}/messages`;
const headers = {"Authorization": authToken };
let clock = 0;
let interval = 500;
function delay(duration) {
return new Promise((resolve, reject) => {
setTimeout(() => resolve(), duration);
});
}
fetch(baseURL + '?before=' + before + '&limit=100', {headers})
.then(resp => resp.json())
.then(messages => {
return Promise.all(messages.map((message) => {
before = message.id;
return delay(clock += interval).then(() => fetch(`${baseURL}/${message.id}`, {headers, method: 'DELETE'}));
}));
}).then(() => clearMessages());
}
clearMessages();
Reference: https://gist.github.com/IMcPwn/0c838a6248772c6fea1339ddad503cce

This will work on discord.js version 12.2.0
Just put this inside your client on message event
and type the command: !nuke-this-channel
Every message on channel will get wiped
then, a kim jong un meme will be posted.
if (msg.content.toLowerCase() == '!nuke-this-channel') {
async function wipe() {
var msg_size = 100;
while (msg_size == 100) {
await msg.channel.bulkDelete(100)
.then(messages => msg_size = messages.size)
.catch(console.error);
}
msg.channel.send(`<#${msg.author.id}>\n> ${msg.content}`, { files: ['http://www.quickmeme.com/img/cf/cfe8938e72eb94d41bbbe99acad77a50cb08a95e164c2b7163d50877e0f86441.jpg'] })
}
wipe()
}

This will work so long your bot has appropriate permissions.
module.exports = {
name: "clear",
description: "Clear messages from the channel.",
args: true,
usage: "<number greater than 0, less than 100>",
execute(message, args) {
const amount = parseInt(args[0]) + 1;
if (isNaN(amount)) {
return message.reply("that doesn't seem to be a valid number.");
} else if (amount <= 1 || amount > 100) {
return message.reply("you need to input a number between 1 and 99.");
}
message.channel.bulkDelete(amount, true).catch((err) => {
console.error(err);
message.channel.send(
"there was an error trying to prune messages in this channel!"
);
});
},
};
In case you didn't read the DiscordJS docs, you should have an index.js file that looks a little something like this:
const Discord = require("discord.js");
const { prefix, token } = require("./config.json");
const client = new Discord.Client();
client.commands = new Discord.Collection();
const commandFiles = fs
.readdirSync("./commands")
.filter((file) => file.endsWith(".js"));
for (const file of commandFiles) {
const command = require(`./commands/${file}`);
client.commands.set(command.name, command);
}
//client portion:
client.once("ready", () => {
console.log("Ready!");
});
client.on("message", (message) => {
if (!message.content.startsWith(prefix) || message.author.bot) return;
const args = message.content.slice(prefix.length).split(/ +/);
const commandName = args.shift().toLowerCase();
if (!client.commands.has(commandName)) return;
const command = client.commands.get(commandName);
if (command.args && !args.length) {
let reply = `You didn't provide any arguments, ${message.author}!`;
if (command.usage) {
reply += `\nThe proper usage would be: \`${prefix}${command.name} ${command.usage}\``;
}
return message.channel.send(reply);
}
try {
command.execute(message, args);
} catch (error) {
console.error(error);
message.reply("there was an error trying to execute that command!");
}
});
client.login(token);

Another approach could be cloning the channel and deleting the one with the messages you want deleted:
// Clears all messages from a channel by cloning channel and deleting old channel
async function clearAllMessagesByCloning(channel) {
// Clone channel
const newChannel = await channel.clone()
console.log(newChannel.id) // Do with this new channel ID what you want
// Delete old channel
channel.delete()
}
I prefer this method rather than the ones listed on this thread because it most likely takes less time to process and (I'm guessing) puts the Discord API under less stress. Also, channel.bulkDelete() is only able to delete messages that are newer than two weeks, which means you won't be able to delete every channel message in case your channel has messages that are older than two weeks.
The possible downside is the channel changing id. In case you rely on storing ids in a database or such, don't forget to update those documents with the id of the newly cloned channel!

Here's #Kiyokodyele answer but with some changes from #user8690818 answer.
(async () => {
let deleted;
do {
deleted = await channel.bulkDelete(100);
} while (deleted.size != 0);
})();

Related

onValue triggering multiple times

I'm using Node.js v18.12.1 and Discord.js v14. for developing the Discord bot. I need to read some data from Firebase. I'm confused because I'm used to how Java with Hibernate fetches data differently. Here, I need to use onValue() listener.
My onValue() acts strange. Instead of just reading the data from Firebase, it skips entirely, then it triggers multiple times, each time skipping the body block of its code, and then it actually does the code after.
I've read somewhere on this forum that this can happen because there are more onValue() listeners that are subscribed and they are all fired up. Someone mentioned I need to use the off() function somewhere "before" the onValue(). This confuses me because I'm using this listener in many locations. I need it in each command file, in execute(interaction) functions. You know, when you need to execute slash commands in Discord. I have it something like this:
async execute(interaction) {
const infographicRef = ref(db, '/infographics/arena/' + interaction.options.getString("arena-team"));
var imageUrl = null;
var postUrl = null;
onValue(infographicRef, (snapshot) => {
imageUrl = snapshot.child("image-url").val();
interaction.reply(imageUrl);
})
},
And I planned for each command, in each command.js file to have onValue(). I'm not sure exactly what to do.
Also, I tried to work around this with once() method, I see it in Firebase documentation, but I got the error: ref.once() is not a function.
It seems that after first triggering of onValue method when the body is not executed, my code in interactionCreate.js is triggered as well, it points for a command to be executed again:
const { Events } = require('discord.js');
module.exports = {
name: Events.InteractionCreate,
async execute(interaction) {
if (!interaction.isChatInputCommand()) return;
const command = interaction.client.commands.get(interaction.commandName);
if (!command) {
console.error(`No command matching ${interaction.commandName} was found.`);
return;
}
try {
await command.execute(interaction);
} catch (error) {
console.error(`Error executing ${interaction.commandName}`);
console.error(error);
}
},
};
my bot.js (which is in my case an index file)
const fs = require('node:fs');
const path = require('node:path');
const { Client, Collection, Events, GatewayIntentBits } = require('discord.js');
const { token } = require('./config.json');
const client = new Client({ intents: [GatewayIntentBits.Guilds] });
const eventsPath = path.join(__dirname, 'events');
const eventFiles = fs.readdirSync(eventsPath).filter(file => file.endsWith('.js'));
for (const file of eventFiles) {
const filePath = path.join(eventsPath, file);
const event = require(filePath);
if (event.once) {
client.once(event.name, (...args) => event.execute(...args));
} else {
client.on(event.name, (...args) => event.execute(...args));
}
}
client.commands = new Collection();
const commandsPath = path.join(__dirname, 'commands');
const commandFiles = fs.readdirSync(commandsPath).filter(file => file.endsWith('.js'));
for (const file of commandFiles) {
const filePath = path.join(commandsPath, file);
const command = require(filePath);
client.commands.set(command.data.name, command);
}
client.once(Events.ClientReady, () => {
console.log('Ready!');
});
client.on(Events.InteractionCreate, async interaction => {
if (!interaction.isChatInputCommand()) return;
const command = client.commands.get(interaction.commandName);
if (!command) return;
try {
await command.execute(interaction);
} catch (error) {
console.error(error);
await interaction.reply({ content: 'There was an error while executing this command!', ephemeral: true });
}
});
client.login(token);
The onValue function registers a realtime listener, that continues to monitor the value on the database.
If you want to read a value once, that'd be done with get() function in v9 (which is the equivalent of the once method in earlier SDK versions). Have a look at the code sample in the documentation on reading data once.

Command Cooldown 1 minute not working discord bot

Here I tried making a command cooldown so people wont be spamming and getting rich fast on my discord bot! The thing is though the code I wrote does not do what I want it to do! It just ignores talkRecently Here is my code:
var eco = require('discord-economy');
const editJsonFile = require("edit-json-file");
const prefix = '.';
const talkedRecently = new Set();
exports.run = async (client, message, args, ops) => {
if (talkedRecently.has(message.author.id)) {
message.channel.send("Wait 1 minute before getting typing this again. - " + message.author);
} else {
var job2 = ['actor/actress','astronaut','baker','barber','biologist','chef','doctor','dentist','farmer','nurse','jounalist','police officer','vet','vocalist','zoologist','waiter/waitress'];
const user = message.author.id;
var number = Math.floor(Math.random() * (500 + 1));
let check = eco.FetchBalance(user);
if(!check) {
eco.SetBalance(user, 0);
}
eco.AddToBalance(user, number)
var job = job2[Math.floor(Math.random() * job2.length)];
message.channel.send(`You have worked as a **${job}** and you have earned ${number}`);
}
talkedRecently.add(message.author.id);
setTimeout(() => {
// Removes the user from the set after a minute
talkedRecently.delete(message.author.id);
}, 60000);
}
Excluding boilerplate code
var arrayOfIntervalUsed = []
var arrOfIds = [];
client.on("message", (msg) => {
arrOfIds.push(msg.author.id)
if(arrOfIntervalUsed.indexOf(msg.author.id) === -1){
setInterval(() => {
arrOfIntervalUsed.push(msg.author.id) // so the bot isn't duplicating itself
if(arrOfIds.filter(() => {
return msg.author.id
}).length > 10){
msg.reply("Stop spamming")
arrOfIds = arrOfIds.filter(() => {
return !msg.author.id
})
}
}, 60000)
}
})
EDIT:
If you want none of the commands to work if they spam add a variable by default true which has to be true in your if statement for what command it is and in the code above below the message about stop spamming make it false but if you want it to come back on add another setInterval outside of that that a set amount of miliseconds it is restored.

Batch commit been called before batch finishes

I'm getting "Cannot modify a WriteBatch that has been committed." in this snippet of code. Although, I'm sure why batch.commit() is not waiting for the forEach finishes.
const db = admin.firestore();
const batch = db.batch();
const channelIds = [];
const messages = data
.map((item) => {
if (!item || !item.phone_number)
return null;
const msg = pupa(message, item);
if (!channelIds.includes(item.channel.id))
channelIds.push(item.channel.id);
return {
...item,
message: msg
};
})
.filter((msg) => msg);
logger.info(`Creating messages/${messageId}/sms entries. [Count = ${messages.length}]`);
// From all channels included in the messages array, it fetchs its remaining sms credits.
channelIds.forEach(async (channelId) => {
const subscriptionDetails = (await admin.firestore()
.collection('channels')
.doc(channelId)
.collection('subscription')
.doc('details')
.get()).data();
const creditsRemaining = subscriptionDetails.limits.snapshot.sms_notifications - subscriptionDetails.limits.used.sms_notifications;
// Sends messages according to its respective channel ID and channel remaining credits.
messages
.filter((item) => item.channel.id === channelId)
.slice(0, creditsRemaining)
.forEach((msg) => {
batch.set(db.collection('messages')
.doc(messageId)
.collection('sms')
.doc(), {
phone_number: msg.phone_number,
message: msg.message
});
});
});
await batch.commit();
EDIT: I fixed this issue by wrapping the forEach in a Promise. Thanks!
It seems you're missing an await before batch.set(db.collection('messages')..., which means that your await batch.commit() gets run before all batch.set() calls have completed.

Discord.js - counter of sent messages, DM to all members of guild

So, I have a working example of code, that sends a DM to all members of a guild.
However, I want the whole thing to end with a message, sort of: "Successfully sent XX messages".
const sentMessages = 0;
if (!message.member.hasPermission(`ADMINISTRATOR`)) {
message.channel.send(`You don't have permission to use this command!`);
message.client.channels.cache.get(logchannel).send(`${message.author} tried using DMALL! ` + dato.toLocaleTimeString() + dato.toLocaleDateString());
return;
} else {
const delay = (msec) => new Promise((resolve) => setTimeout(resolve, msec));
const sendMessage = args.join(" ");
message.channel.send(`Sending messages, please wait...`);
await delay(1000);
let interval = 500; // how much time should the delay between two iterations be (in milliseconds)?
let promise = Promise.resolve();
message.guild.members.cache.forEach(function (user) {
promise = promise.then(function () {
if (user.id != message.client.user.id) {
user.send(sendMessage);
sentMessages++;
return new Promise(function (resolve) {
setTimeout(resolve, interval);
});
}
});
message.channel.send(`Successfully sent ${sentMessages} messages!`);
});
The message at the end (Successfully...) keeps returning 0, no matter when or where I place it.
If I put a line like: message.channel.send(`Sent: ${sentMessages}); below sentMessages++; I get an incrementing number like I expect.
What and where am I doing this wrong?
Ended up turning to a friend who is much better at coding than me.
He came up with a totally different way of getting there:
if (!message.member.hasPermission(`ADMINISTRATOR`)) {
message.channel.send(`You don't have permission to use this command!`);
message.client.channels.cache.get(logchannel).send(`${message.author} tried using DMALL in **${message.guild.name}**!` + dato.toLocaleTimeString() + dato.toLocaleDateString());
return;
} else {
const delay = (msec) => new Promise((resolve) => setTimeout(resolve, msec));
const sendMessage = args.join(" ");
message.channel.send(`Sending messages, please wait...`);
await delay(1000);
const interval = 500;
let array;
try {
const all = await message.guild.members.fetch();
array = all.array();
} catch {
array = message.guild.members.cache.array();
}
let sentMessages = 0;
for (var i = 0; i < array.length; i++) {
const user = array[i];
if (!user.bot) {
try {
await user.send(sendMessage);
sentMessages++;
await delay(interval);
} catch { }
}
}
message.client.channels.cache.get(logchannel).send(`${message.author} sent a DM to all members of: **${message.guild.name}**!` + dato.toLocaleTimeString() + dato.toLocaleDateString());
message.channel.send(`Finished sending messages to all users, ${sentMessages} messages sent!`);
}
This code is tested and found to be working like a charm
Use let sentMessages = 0 instead of const sentMessages = 0
Constants can't be updated or reassigned, so sentMessages++ won't do anything
Just do not do this if you do not want to be bannes from Discord, it is forbidden to send private message to all members of a guild.

Values get overwritten by latest person to request the bot?

I have made a raffle ballot discord bot that allows a user to DM the bot their name and raffle entry amount. Once they have set the values they can start the entry of the raffle by DMing !enter. Once this has happend a function is called which then starts a for-loop the for loop will run based on the specified entry amount. I have also added in a delay within the for-loop due to the service to get the raffle tickets takes some time (Code is edited for SO Post due to sensitive API info)
Once this is complete it then sends a DM back to the user that had DMed the bot originally. The problem I am facing is that if multiple users DM at the same time or while it is running from the first DM the variables get overwritten by the latest person requesting the bot.
I assumed that by using a Discord.js bot each time a user DMs it creates a new instance of the script or node process?
Is it possible for the function that the bot calls once DMed to create a new process within the main node process so it doesn't get overwritten?
const Discord = require('discord.js');
const botconfig = require('./discordBotConfig.json');
const bot = new Discord.Client({disableEveryone: true});
const c = require('chalk');
// Chalk Theme
const ctx = new c.constructor({level: 2});
const error = c.red;
const waiting = c.magenta;
const success = c.green;
const discordBot = c.yellow;
// Current Raffles (API Link Later)
let activeRaffles = 'Raffle 1';
// User Parmas
let usrName = '';
let entryAmount = 0;
// Ticket
let raffleTicket = [];
let retryDelay = 3000;
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
// Enter
const enterIn = async () => {
console.log('User name', usrName);
raffleTicket.push(Math.random(0, 50));
}
// Init Raffle Entry
const raffleInit = async (entryAmount) => {
for (let i = 0; i < entryAmount; i++) {
enterIn();
await sleep(retryDelay);
}
dmUser();
}
const dmUser = () => {
// Discord Message Complete
let botCompleteMsg = new Discord.RichEmbed()
.setTitle('Finished!')
.setColor('#25E37A')
.addField('Name: ', usrName)
.addField('Tickets: ', raffleTicket)
.addField('Last Update: ', bot.user.createdAt);
bot.fetchUser(userID).then((user) => {
user.send(botCompleteMsg);
});
return; // End the application
}
// Discord Bot Setup
bot.on('ready', async () => {
console.log(discordBot(`${bot.user.username} is Online!`));
bot.user.setActivity('Entering Raffle');
});
bot.on('message', async message => {
if (message.channel.type === 'dm') {
let prefix = botconfig.prefix;
let messageArray = message.content.split(' ');
let cmd = messageArray[0];
if (cmd === `${prefix}name`) {
if (messageArray.length === 3) {
userID = message.author.id;
usrName = messageArray[1];
entryAmount = messageArray[2];
// Raffle summary
let raffleSummary = new Discord.RichEmbed()
.setTitle('Entry Summary')
.setColor('#8D06FF')
.addField('Name: ', usrName)
.addField('Entry Amount: ', entryAmount)
return message.author.send(raffleSummary), message.author.send('Type **!start** to begin entry or type **!set** again to set the entry details again.');
}
}
if (cmd === `${prefix}enter`) {
// Raffle summary
let startMessage = new Discord.RichEmbed()
.setTitle('Entering raffle!')
.setDescription('Thanks for entering! :)')
.setColor('#8D06FF')
return message.author.send(startMessage), raffleInit(entryAmount);
}
}
});
bot.login(botconfig.token);
You can store your user data in a list with classes.
var userData = [
{name: "sample#0000", entryNum: 0}
];

Resources