I am creating a Discord bot with discord.js and I'd like to create a command that can clear messages. For now, I have this code (only the interesting part) and I can't figure out why it doesn't work:
// Importing discord.js, creating bot and setting the prefix
const Discord = require('discord.js');
const bot = new Discord.Client();
const prefix = "/";
// Array that stores all messages sent
messages = [];
bot.on('message', (message) => {
// Store the new message in the messages array
messages.push(message);
// Split the command so that "/clear all" becames args["clear", "all"]
var args = message.content.substring(prefix.length).split(" ");
// If the command is "/clear all"
if(args[0] == "clear" && args[1] == "all") {
bot.deleteMessages(messages); // Code that doesn't work
// Resets the array
messages = [];
}
}
// CONNECT !!!
bot.login('LOGING TOKEN HERE');
Can you help me ?
You should use <TextChannel>.bulkDelete instead.
Example:
msg.channel.bulkDelete(100).then(() => {
msg.channel.send("Purged 100 messages.").then(m => m.delete(3000));
});
This would delete 2 - 100 messages in a channel for every call to this method so you would not receive 429 (Too many Requests) Error frequently which might result in your token being revoked.
I see two problems:
the messages array is always empty; there is no code that adds items to the array, so the call to bot.deleteMessages will always get an empty array;
it does not appear that deleteMessages is an available method on Discord.Client;
Based on the documentation, I think what you want is sweepMessages. The description of that states:
Sweeps all text-based channels' messages and removes the ones older than the max message lifetime. If the message has been edited, the time of the edit is used rather than the time of the original message.
Try changing the code to instead call bot.sweepMessages(1);, which I think will tell the client to clear all messages older than one second.
Another way to do this, without sweepMessages is by using fetchMessages:
let user = message.mentions.users.first();
let amount = !!parseInt(message.content.split(' ')[1]) ? parseInt(message.content.split(' ')[1]) : parseInt(message.content.split(' ')[2])
var prefix = '!'
if (message.content.startsWith(prefix + 'clear') && !amount)
return message.reply('Must specify an amount to clear!');
if (message.content.startsWith(prefix + 'clear') && !amount && !user) return message.reply('Must specify a user and amount, or just an amount, of messages to clear!');
message.channel.fetchMessages({
limit: amount,
}).then((messages) => {
if (user) {
const filterBy = user ? user.id : bot.user.id;
messages = messages.filter(m => m.author.id === filterBy).array().slice(0, amount);
}
message.channel.bulkDelete(messages).catch(error => console.log(error.stack));
});
This will allow users to use the command !clear [#] to delete that number of messages when sent. If it is run as just !clear you can set how many get deleted, without a specified number.
discord.js Documentation - TextChannel#fetchMessages
You can swap
bot.deleteMessages()
to:
messages.forEach(x => x.delete())
Its not like that. you should fetch the messages first then use bulkDelete to delete them
, here is a simple example
// Normal Javascript
<Message>.channel.fetchMessages()
.then(messages => {
// Here you can use bulkDelete(101) to delete 100 messages instead of using fetchMessages and deleting only 50
<Message>.channel.bulkDelete(messages);
});
// ES6
let messages = await <Message>.channel.fetchMessages();
// Here you can use bulkDelete(101) to delete 100 messages instead of using fetchMessages and deleting only 50
await <Message>.channel.bulkDelete(messages);
Related
I'm trying to create a discord js bot which can send a random message by a specified user.
Here's my attept:
const ch = client.channels.cache.get("12345");
const soma = client.users.cache.get("4321");
if(message.content.startsWith(prefix+'test')){
console.log(`${ch}`);
ch.messages.fetch({ limit: 100 }).then(messages => {
console.log(`Received ${messages.size} messages`);
messages.forEach(message => message.author.id)
messages.forEach(function (message){
if (message.author.id === {soma}){
console.log(message.content);
}
})
})
};
I just can't figure out how to put the author id and the message content into an array or just go thru it when the command is executed.
Ok i read ur script and at the const soma = client.users.cache.get("4321"); part i noticed that ur trying to get the id from the users of the bot which isnot needed u can just use the id instantly so all u ahve to do is making soma defined as ur id no need for client.cache.get just like this const soma = "332036461669122048" for example, and for the channel you can just make it into const ch = message.channel.id instead of getting the channel from the client cuz ur getting it in a wrong way
Edit: and at the if (message.author.id === {soma}) you dont need to add the "{","}" its not needed
I planned to create a discord server with bots. There are quite a lot (6 in total) and are just supposed to be fictional characters with some background story. I'm quite new and all of that is way too complicated for me to code myself, therefor I ask for your help! I just want to have a nice server for my friends and I with enjoyable bots and all of these desperate hours of trying to get some useful code is driving me nuts..
I only managed to get one bot to do stuff, using the prefix "-".
It can change it's status (watching, listening, playing) and the name of the thing he's doing.
I'm not quite sure why streaming doesn't work or if that's possible in general but it would be really cool if it would.
My status code: (1st Problem)
client.once('ready', () => {
console.log('Bot is ready!');
if (config.activity.streaming == true) {
client.user.setActivity(config.activity.game, {type: 'WATCHING'}); //STREAMING, PLAYING, LISTENING
} else {
client.user.setActivity(config.activity.game, {url: 'https://twitch.tv/usrname'});
client.user.setStatus('idle'); //dnd, idle, online, invisible
}
});
config.json
"activity": {
"streaming": true,
"game": "Whatevergame"
}
}
As I said, streaming is not working for some reason and the status (idle, dnd..) is also not working.
2nd Problem
If I try to add other bots with the login, it will log both bots on, but only one of them will work, what's actually pretty logical since the commands are all made for only one bot. So I'm trying to figure out how to get them all packed into the main file.
3rd Problem
I used the try - catch function to execute commands, which I pre- set up, and if theres none, it sends an error message. See for yourself:
client.on('message', message =>{
if (!message.content.startsWith(prefix) || message.author.bot) return;
const args = message.content.slice(prefix.length).split(/ +/);
const command = args.shift().toLowerCase();
try {
client.commands.get(command).execute(message, args);
}
catch {
message.channel.send("I don't know that, sorry.");
}
});
So everytime I type another command, from which I do not want the bot to respond to, it will respond with "I don't know[...]" It would be sufficient to just set up another prefix for the "other command" to fix that problem so the bot knows that for every prefix starting with a.e "-", it has to send an error message if that command is not existing. But for other prefixes, a.e "?", it's supposed to execute the other command/s.
4th Problem
My (current) last problems are the welcome messages. My code:
index.js
const welcome = require("./welcome");
welcome (client)
welcome.js
module.exports = (client) => {
const channelId = '766761508427530250' // welcome channel
const targetChannelId = '766731745960919052' //rules and info
client.on('guildMemberAdd', (member) => {
console.log(member)
const message = `New user <#${member.id}> joined the server. Please read through ${member.guild.channels.cache.get(targetChannelId).toString()} to gain full access to the server!`
const channel = member.guild.channels.cache.get(channelId)
channel.send(message)
})
}
The code is working perfectly fine, however it would be way more exciting with a little more variety. I'm trying to get multiple welcome messages that get randomly chosen by the bot.. I thought about a Mathfloor as an approach but I'm not quite sure how that would work..
Thank you for reading through my text and I hope that I will soon be able to enjoy the server with my guys!
Cheers!
First problem
I'm not sure why ClientUser.setActivity() and ClientUser.setStatus is not working. In the STREAMING example, it might be because you didn't specify the type of activity. Either way, there's an easier way to what you're doing, which is ClientUser.setPresence(). This method is kind of like a combination of the other two.
client.once('ready', () => {
console.log('Bot is ready!');
config.activity.streaming
? client.user.setPresence({
activity: { name: config.activity.game, type: 'WATCHING' },
})
: client.user.setPresence({
activity: {
name: config.activity.game,
type: 'STREAMING',
url: 'https://twitch.tv/usrname',
},
status: 'idle', // note: the idle icon is overwritten by the STREAMING icon, so this won't do much
});
});
Second Problem
It's pretty hard to make multiple bots, both duplicates of each other, in one file. I would recommend just using a lot of Array.prototype.forEach() loops to apply all events and such to both clients.
[1, 2, 3].forEach((num) =>
console.log(`The element I'm currently iterating a function through is ${num}`)
);
// example main file
const { Client, Collection } = require('discord.js');
const [roseClient, sunflowerClient] = [new Client(), new Client()];
// adding properties:
[roseClient, sunflowerClient].forEach((client) => client.commands = new Collection())
// events
[roseClient, sunflowerClient].forEach((client) =>
client.on('message', (message) => {
// ...
});
);
// login
roseClient.login('token1');
sunflowerClient.login('token2');
Third problem
Again, forEach() loops save the day (❁´◡`❁). This time, however, you should actually use Array.prototype.every(), which will return true if every element of an array passes the given test.
Basically, if we were to use a normal forEach() loop, then even if one of the prefixes found the match, the other wouldn't and the error message would always be sent out. So instead we'll use every() to only send out an error message if both prefixes find no match.
// what if we ony wanted the error message if *every* number was 3
[1, 2, 3].forEach((num) => {
if (num === 3) console.error('Error message');
});
console.log('--------------------');
// now it will only send if all numbers were three (they're not)
if ([1, 2, 3].every((num) => num === 3))
console.error('Error message');
client.on('message', (message) => {
if (['-', '?'].every((prefix) => {
if (!message.content.startsWith(prefix) || message.author.bot) return;
const args = message.content.slice(prefix.length).split(/ +/);
const command = args.shift().toLowerCase();
try {
// it did not pass the test (the test being finding no match), and the error message should not be displayed
return false;
client.commands.get(command).execute(message, args);
} catch {
// it did pass the test (by finding no match). if the next test is failed too, the error message should be displayed
return true;
message.channel.send("I don't know that, sorry.");
}
});
});
Fourth Problem
You're on the right track! Math.floor() is definitely the right way to get a random element from an array.
function chooseFood() {
// make an array
const foods = ['eggs', 'waffles', 'cereal', "nothing (●'◡'●)", 'yogurt'];
// produce a random integer from 0 to the length of the array
const index = Math.floor(Math.random() * foods.length);
// find the food at that index
console.log(`Today I will eat ${foods[index]}`);
};
<button onClick="chooseFood()">Choose What to Eat</button>
module.exports = (client) => {
client.on('guildMemberAdd', (member) => {
console.log(member);
const welcomes = [
`Welcome ${member}!`,
`Woah! Didn't see you there ${member}; welcome to the server!`,
`${member} enjoy your stay at ${member.guild}!`,
];
const message = `${
welcomes[Math.floor(Math.random() * welcomes.length)]
} Please read through ${member.guild.channels.cache.get(
'766731745960919052'
)} to gain full access to the server!`;
member.guild.channels.cache.get('766761508427530250').send(message);
});
};
That was a mouthful (╯°□°)╯︵ ┻━┻
I am coding a Discord bot in which I want to do the command !quote and it will pull a random message of of out of a specific channel with id quotesID (that may or may not be a different channel that !quote was sent in). I keep looking through the documentation for Discord.js but I can't find a way to get a TextChannel by its ID and then use a TextChannels .messages function and thus get the MessageManager and then a collection of the messages.
I know I can get the guild using msg.guild (where msg is the trigger with !quote) or get the tex
I am new to JavaScript and Discord.js so any info helps. (I am using Discord.js version 12.2.0)
If you want to get a specific channel with an id, you need to do:
const channel = client.channels.cache.get("Your channel ID");
Then, to collect messages in this channel (limit is set to 100 messages max, so you can't collect more than 100 messages in this channel except if you do your own messages storing system).
channel.messages.fetch({ limit: 100 }).then(messages => {
console.log(`Received ${messages.size} messages`);
//Iterate through the messages here with the variable "messages".
messages.forEach(message => console.log(message.content))
})
According to your title, you want all messages in a channel. Not just 100 of them. Since there is a max on number of messages you can fetch per request, we will have to use the before option to perform pagination to fetch all messages.
async function fetchAllMessages() {
const channel = client.channels.cache.get("<my-channel-id>");
let messages = [];
// Create message pointer
let message = await channel.messages
.fetch({ limit: 1 })
.then(messagePage => (messagePage.size === 1 ? messagePage.at(0) : null));
while (message) {
await channel.messages
.fetch({ limit: 100, before: message.id })
.then(messagePage => {
messagePage.forEach(msg => messages.push(msg));
// Update our message pointer to be last message in page of messages
message = 0 < messagePage.size ? messagePage.at(messagePage.size - 1) : null;
}
}
console.log(messages); // Print all messages
}
As it is said in https://discord.js.org/#/docs/collection/master/class/Collection?scrollTo=array you can use the method <Collection>.array() to get an array of the values in the Collection. It is like [...Collection.values()] or Array.from(Collection.values()).
I'm trying to make a simple discord bot, however whenever I run the -setcaps command I get :
TypeError: Cannot read property 'id' of undefined.
I'm not exactly sure what is causing this. I would appreciate whatever help you can provide. Not exactly sure what to add to this to provide more details, I'm using the latest stable version of node.js and editing with notpad++
// Call Packages
const Discord = require('discord.js');
const economy = require('discord-eco');
// Define client for Discord
const client = new Discord.Client();
// We have to define a moderator role, the name of a role you need to run certain commands
const modRole = 'Sentinel';
// This will run when a message is recieved...
client.on('message', message => {
// Variables
let prefix = '-';
let msg = message.content.toUpperCase();
// Lets also add some new variables
let cont = message.content.slice(prefix.length).split(" "); // This slices off the prefix, then stores everything after that in an array split by spaces.
let args = cont.slice(1); // This removes the command part of the message, only leaving the words after it seperated by spaces
// Commands
// Ping - Let's create a quick command to make sure everything is working!
if (message.content.toUpperCase() === `${prefix}PING`) {
message.channel.send('Pong!');
}
// Add / Remove Money For Admins
if (msg.startsWith(`${prefix}SETCAPS`)) {
// Check if they have the modRole
if (!message.member.roles.find("name", modRole)) { // Run if they dont have role...
message.channel.send('**You need the role `' + modRole + '` to use this command...**');
return;
}
// Check if they defined an amount
if (!args[0]) {
message.channel.send(`**You need to define an amount. Usage: ${prefix}SETCAPS <amount> <user>**`);
return;
}
// We should also make sure that args[0] is a number
if (isNaN(args[0])) {
message.channel.send(`**The amount has to be a number. Usage: ${prefix}SETCAPS <amount> <user>**`);
return; // Remember to return if you are sending an error message! So the rest of the code doesn't run.
}
// Check if they defined a user
let defineduser = '';
if (!args[1]) { // If they didn't define anyone, set it to their own.
defineduser = message.author.id;
} else { // Run this if they did define someone...
let firstMentioned = message.mentions.users.first();
defineduser = firstMentioned.id;
}
// Finally, run this.. REMEMBER IF you are doing the guild-unique method, make sure you add the guild ID to the end,
economy.updateBalance(defineduser + message.guild.id, parseInt(args[0])).then((i) => { // AND MAKE SURE YOU ALWAYS PARSE THE NUMBER YOU ARE ADDING AS AN INTEGER
message.channel.send(`**User defined had ${args[0]} added/subtraction from their account.**`)
});
}
// Balance & Money
if (msg === `${prefix}BALANCE` || msg === `${prefix}MONEY`) { // This will run if the message is either ~BALANCE or ~MONEY
// Additional Tip: If you want to make the values guild-unique, simply add + message.guild.id whenever you request.
economy.fetchBalance(message.author.id + message.guild.id).then((i) => { // economy.fetchBalance grabs the userID, finds it, and puts the data with it into i.
// Lets use an embed for This
const embed = new Discord.RichEmbed()
.setDescription(`**${message.guild.name} Stash**`)
.setColor(0xff9900) // You can set any HEX color if you put 0x before it.
.addField('Stash Owner',message.author.username,true) // The TRUE makes the embed inline. Account Holder is the title, and message.author is the value
.addField('Stash Contents',i.money,true)
// Now we need to send the message
message.channel.send({embed})
})
}
});
client.login('TOKEN HIDDEN');
I'm not sure if this was causing your error but let's give it a try. I edited the check if the user mentioned someone.
// Call Packages
const Discord = require('discord.js');
const economy = require('discord-eco');
// Define client for Discord
const client = new Discord.Client();
// We have to define a moderator role, the name of a role you need to run certain commands
const modRole = 'Sentinel';
// This will run when a message is recieved...
client.on('message', message => {
// Variables
let prefix = '-';
let msg = message.content.toUpperCase();
// Lets also add some new variables
let cont = message.content.slice(prefix.length).split(" "); // This slices off the prefix, then stores everything after that in an array split by spaces.
let args = cont.slice(1); // This removes the command part of the message, only leaving the words after it seperated by spaces
// Commands
// Ping - Let's create a quick command to make sure everything is working!
if (message.content.toUpperCase() === `${prefix}PING`) {
message.channel.send('Pong!');
}
// Add / Remove Money For Admins
if (msg.startsWith(`${prefix}SETCAPS`)) {
// Check if they have the modRole
if (!message.member.roles.find("name", modRole)) { // Run if they dont have role...
message.channel.send('**You need the role `' + modRole.name + '` to use this command...**');
return;
}
// Check if they defined an amount
if (!args[0]) {
message.channel.send(`**You need to define an amount. Usage: ${prefix}SETCAPS <amount> <user>**`);
return;
}
// We should also make sure that args[0] is a number
if (isNaN(args[0])) {
message.channel.send(`**The amount has to be a number. Usage: ${prefix}SETCAPS <amount> <user>**`);
return; // Remember to return if you are sending an error message! So the rest of the code doesn't run.
}
// Check if they defined a user
let defineduser = '';
let user = message.mentions.users.first() || msg.author;
defineduser = user.id
// Finally, run this.. REMEMBER IF you are doing the guild-unique method, make sure you add the guild ID to the end,
economy.updateBalance(defineduser + message.guild.id, parseInt(args[0])).then((i) => { // AND MAKE SURE YOU ALWAYS PARSE THE NUMBER YOU ARE ADDING AS AN INTEGER
message.channel.send(`**User defined had ${args[0]} added/subtraction from their account.**`)
});
}
// Balance & Money
if (msg === `${prefix}BALANCE` || msg === `${prefix}MONEY`) { // This will run if the message is either ~BALANCE or ~MONEY
// Additional Tip: If you want to make the values guild-unique, simply add + message.guild.id whenever you request.
economy.fetchBalance(message.author.id + message.guild.id).then((i) => { // economy.fetchBalance grabs the userID, finds it, and puts the data with it into i.
// Lets use an embed for This
const embed = new Discord.RichEmbed()
.setDescription(`**${message.guild.name} Stash**`)
.setColor(0xff9900) // You can set any HEX color if you put 0x before it.
.addField('Stash Owner', message.author.username, true) // The TRUE makes the embed inline. Account Holder is the title, and message.author is the value
.addField('Stash Contents', i.money, true)
// Now we need to send the message
message.channel.send({
embed
})
})
}
});
client.login('TOKEN HIDDEN')
I'm trying to find out if its possible to get the time/information of users last activity retrospectively using discord.js
Say I have something like
client.guilds.find('id', 'SERVER ID').fetchMembers().then(members => {
const role = members.roles.find('name', 'Newbies')
role.members.forEach(member => {
console.log(member.user.lastMessage) // null
})
})
Unless the member has posted, since the client is listening, the lastMessage is always null.
Is there a way to find the last activity? or a workaround, like a query to return all the users messages, which I can then take the most recent one from?
Effectively I want to know what date/time the user last posted so we can monitor non-contributing accounts.
Thanks
After looking thought the documentation I didn't find something neither so I came up with a manual search function.
Basically, it will scan every channels until finding a message from X user, or the end of the messages in the channel. It then compare the last messages of the users from every channels and print the last one.
It can be very long if the user hasn't write since a long time. Of course, you have to check lastMessage before trying this.
I would add a time limit maybe. Because if you have thousand of messages, the function will run eternally.
You can stop the function if the last message found is in the accepted time to not be kick/do whatever.
I made the search stop if the first message found in the pack of fetched messaged is older than the ban limit, however, if the first message is not older, remember that it means for the other, so we still need to check them (it can be avoided by checking the last message of the pack as well).
async function fetchMessageUser(chan, id, res) {
let option = {};
if (typeof res !== 'undefined'){
option = {before: res.id};
}
return await chan.fetchMessages(option)
.then(async msgs => {
if (msgs.size === 0){
return {continue: false, found: false};
};
if ((Date.now() - (msgs.first().createdTimestamp)) > 86400000 ) { // 1 day
return {continue: false, found: false};
}
let msgByAuthor = msgs.find(msg => {
return msg.author.id === id;
});
if (msgByAuthor === null){
return {continue: true, id: msgs.last().id};
} else {
return {continue: false, found: true, timestamp: msgByAuthor.createdTimestamp};
}
})
.catch(err => console.log('ERR>>', err));
}
client.on('message', async (msg) => {
let timestamp = [];
for (let [id, chan] of msg.guild.channels){
if (chan.type !== 'text'){ continue; }
let id = '587692527826763788'; // id of the user, here a non verified account
let res;
do {
res = await fetchMessageUser(chan, id, res);
} while(res.continue);
if (res.found) {
timestamp.push(res.timestamp);
}
}
console.log(timestamp);
let first = timestamp.sort((a,b) => (b-a))[0];
console.log(new Date(first));
});
A better variant would be to run it for an array of users, checking all 50 last messages from every channel, and associating each users with his most recent messages if he wrote one, and doing this until all the messages in all the channels are too old to avoid a kick/whatever. And then do something for all the users who don't have an associated messages.
I think what you need is one of Discord's built in features, namely: pruning. This feature will grab inactive members and lets you kick them. Luckily, discord.js has an API call for it and even lets you get the number of members first without actually kicking them by setting the dry parameter to true. The feature will also allow you to specify the amount of days a user has to be inactive.
Have a look at the docs: https://discord.js.org/#/docs/main/stable/class/Guild?scrollTo=pruneMembers
Hope that helps out!