Why am I only receiving 50 messages with MessageManager.fetch()? - discord

I'm in the process of debugging the /purge command for my Discord bot.
My intention is to fetch the entirety of a text channel, and delete any amount of messages, by calling the TextChannel.bulkDelete method multiple times, since that method has a limit of deleting 100 messages at a time. This is my code:
async purgeDelete(
channel: TextChannel,
amount: number | undefined,
target: GuildMember | undefined,
keyword: string | undefined
): Promise<number> {
// Most confused about this line: Am I doing the right thing?
const messages = await channel.messages.fetch();
const twoWeeksAgo = new Date();
twoWeeksAgo.setDate(twoWeeksAgo.getDate() - 14);
const purgelist = messages.filter(message => (
(!target || message.author.id === target.id)
&& (!keyword || message.content.includes(keyword))
&& this.resultMessage?.id !== message.id
&& message.createdAt > twoWeeksAgo
));
let purgeAmount: number;
if (amount === undefined) {
purgeAmount = purgelist.size;
} else {
console.log(purgelist.size, messages.size);
purgeAmount = Math.min(amount, purgelist.size);
}
const slicedPurgelist = purgelist.first(purgeAmount);
const partitionedPurgelist = [];
for (let i = 0; i < slicedPurgelist.length; i += 100) {
partitionedPurgelist.push(slicedPurgelist.slice(i, i + 100));
}
await Promise.all(partitionedPurgelist.map(messages => channel.bulkDelete(messages)));
return purgeAmount;
}
I'm pretty sure the only line that matters is the fetch() call. When called in my program, the API is giving me 50 messages. Is that intentional? I know there is an option for limit, but that only goes up to 100. If there is any workarounds to this, please let me know!

The Discord API has a hard limit of 100 messages per GET request. Unfortunately, this is a hard limit you can't bypass, and is intentional on Discord's part.
Furthermore, fetching the entirety of a text channel is probably a bad idea, especially with larger servers which could have 100k+ messages per channel.
A "sort-of" workaround is to use the before param in FetchMessageOptions plus a loop to continue fetching messages. See below for an example:
const messages = [];
const messagesToFetch = 1000
while(messages.length < messagesToFetch) {
// Handle first run
if(!messages.length) {
const msg = await channel.messages.fetch({ limit: 100 })
messages.push(msg)
continue;
}
// Fetch messages before the oldest message in the array
messages.push(await channel.messages.fetch({ limit: 100, before: messages[0].id }))
}

Related

Discord.JS, Numbering users that send a specific phrase to Bot DMs

I'm making a function in my bot where when a phrase is sent to the bot, it displays that it's the first user to send that phrase, then the second user, it displays second, and so on. As of now I have the for loop so it displays all numbers 1-3 all at once. I am just having some difficulty creating the function to display one number for each user that sends the message.
For more clarification
Any Help is appreciated, thank you!
Code:
const channel = bot.channels.cache.get('732757852615344139');
channel.updateOverwrite(message.author,{
VIEW_CHANNEL: true,
})
for(let i = 1; i< 4; i++){
let scavWelcome = new Discord.MessageEmbed()
.setTitle('Good Work')
.setDescription(`Welcome ${message.author}, you placed number ${i}`)
channel.send(scavWelcome)
}
}
Set up a global counter variable along with a max count, either by global variable or hoisting to the bot object. Increment the counter on each message. Once the counter reaches max, reset the counter back to 1.
// Under where you defined bot
bot.counter = 1;
bot.maxCount = 4;
bot.on('message', message => {
// Your message event code...
const channel = bot.channels.cache.get('732757852615344139');
channel.updateOverwrite(message.author,{
VIEW_CHANNEL: true
})
if (message.content === 'Phrase Here') {
if (bot.counter === bot.maxCount) bot.counter = 1;
else bot.counter++;
}
let scavWelcome = new Discord.MessageEmbed()
.setTitle('Good Work')
.setDescription(`Welcome ${message.author}, you placed number ${bot.counter}`)
channel.send(scavWelcome);
});
If i understood your question correctly, you could use an array to store the users and get the index of the elements which would correspond to their position
client.on('message', message => {
const userArr = [];
if (message.content.toLowerCase() == 'a phrase') userArr.push(`${message.author.username`);
});
Then you can view the list in appropriate order by doing
for (let i = 0; i < userArr.length; i++) arrWithIndex.push(`${i + 1} ${userArr[i]}`) // i + 1 because index starts with 0, but we're counting from 1
message.channel.send(arrWithIndex.join('\n'));
if i misinterpreted your question, could you please explain it in the comments, ty

How do i keep taking inputs from multiple users until a specific user says a particular keyword (discord.js)?

So I've been trying to make an auction bot on discord. I've figured out how to handle a single item, but I'm having issues regarding bundles.
My goal - Let the user info any/all items when auctioning a bundle till he says the keyword 'done'. The bot stores the url of the first message (embed sent by the bot) and then does nothing till the user says done. The two users are 1. the user himself, 2. the bot who's item(s) he wants to auction.
I tried using the message collector but the bot straight up ignores it. After searching around on the web and trying to adapt to my situation, the closest thing i found is using async/await with awaitMessages while inside a for loop. After that failed too, i tried to use a chain of then() with the awaitMessages. Here's my code snippet. Any help is appreciated.
let flg =0
if(arguments[3] === 'bundle'){
message.channel.send('Say `done` when you have info-ed all your pokemons')
await message.channel.awaitMessages(m => m.author.id === '666956518511345684' || m.author.id === message.author.id, {time : 30000, errors : ['time']}).then(col1 => {
if (col1.first().author.id === '666956518511345684')
details.poke = col1.first().url
}).then(col2 => {
if (col2.first().author.id === message.author.id)
while(flg = 0){
if (col2.first().content.toLowerCase() === 'done')
flg = 1
}
})
Have the collector ignore bot messages, take multiple inputs and check for message content. Once time runs out, or the max input has been reached, the collection of messages will be returned.
if(arguments[3] === 'bundle'){
message.channel.send('Say `done` when you have info-ed all your pokemons')
const filter = m => {
return !m.user.bot && ['666956518511345684', message.author.id].includes(m.author.id) && m.content.toLowerCase() === 'done';
}
message.channel.awaitMessages(filter, {
// Take a max of 5 inputs for example
max: 5
time: 30000,
errors: ['time']
}).then(collected => {
// Your code
})
}
Documentation on TextChannel#awaitMessages()
I tried message collector once again and i made it work somehow. Here's how :
let i = 0
let filter = m => {m.author.id === '<Bot's id>' || m.author.id === message.author.id}
let collector = message.channel.createMessageCollector(filter, {time : 30000})
collector.on('collect', m => {
if (m.author.id === '<bot's id>' && i === 0){
details.poke = m.url
i = 1
}
if (m.author.id === message.author.id && m.content.toLowerCase() === 'done'){
collector.stop()
}
})
collector.on('end', col => { //code here })
The variable i is a flag, since i only want the url of the first message sent by the bot. It can be removed/more flags can be added up to your needs/requirements.
Lemme know if you need anymore help regarding this.

.toLowercase in client.on('message', async message => {

I want my bot to respond to commands that are typen with capital letters, but where should I put it, I really don't know... :heh:. So yea where should I put the .toLowercase for my bot to respond to capital letters?
client.on('message', async message => {
if(message.content.startsWith(";stats")){
cpuStat.usagePercent(function (error, percent) {
if (error) {
return console.error(error)
}
const cores = os.cpus().length // Counting how many cores your hosting has.
const cpuModel = os.cpus()[0].model // Your hosting CPU model.
const guild = client.guilds.cache.size.toLocaleString() // Counting how many servers invite your bot. Tolocalestring, meaning separate 3 numbers with commas.
const user = client.users.cache.size.toLocaleString() // Counting how many members in the server that invite your bot.
const channel = client.channels.cache.size.toLocaleString() // Counting how many channels in the server that invite your bot
const Node = process.version // Your node version.
const CPU = percent.toFixed(2) // Your CPU usage.
const d = moment.duration(message.client.uptime);
const days = (d.days() == 1) ? `${d.days()} day` : `${d.days()} days`;
const hours = (d.hours() == 1) ? `${d.hours()} hour` : `${d.hours()} hours`;
const minutes = (d.minutes() == 1) ? `${d.minutes()} minute` : `${d.minutes()} minutes`;
const seconds = (d.seconds() == 1) ? `${d.seconds()} second` : `${d.seconds()} seconds`;
const date = moment().subtract(d, 'ms').format('dddd, MMMM Do YYYY');
const embed = new Discord.MessageEmbed() // Stable or < below than 11.x.x use RichEmbed. More than 12.x.x or Master or https://github.com/discordjs/discord.js/ (github:discordjs/discord.js) use MessageEmbed.
embed.setTitle('Axmy#0102', message.author.displayAvatarURL());
embed.setDescription('Really non-useful bot, I know that everyone hates Axmy')
embed.addField('Servers 🏠', `\n${client.guilds.cache.size}`)
embed.addField('Users 🧑‍🤝‍🧑', `${client.guilds.cache.reduce((a, g) => a + g.memberCount, 0)}`)
embed.addField('Uptime <:tired:811194778149715979>', `${days}, ${hours}, ${minutes}, ${seconds}`)
embed.addField("Discord.js Version <:discord:811200194640216094> ", '12.3.1')
embed.addField("CPU Usage <:down:811195966714937395>", ` ${CPU}%`)
embed.addField("Node.js Version <:node:811196465095376896>", `${Node}`)
embed.setColor('PURPLE')
message.channel.send(embed)
})
}
})
A quick solution to your problem is this:
const prefix=";";
client.on('message', async message => {
if (message.author.bot) return;
if (!message.content.toLowerCase().startsWith(prefix)) return;
const args=message.content.slice(prefix.length).trim().split(/ +/g);
const command = args.shift().toLowerCase();
if (command==="stats") {
cpuStat.usagePercent(function (error, percent) {
if (error) {
return console.error(error)
}
//rest of the code...
But I suggest you to learn about command-handling, your codebase will look much better when you seperate commands into seperate files. There are a lot of youtube tutorials about it.

Restful API failing sporadically but "catch" statement is always called, even when it does not fail

We are using a restful API to retrieve information about esports matches being played. From time to time the page simply loads, with no info being returned form the API.
I am fairly confident that the issue is with the API itself, but wanted to double-check that we are not doing anything wrong. Please see our code below:
const proxyurl = "https://cors-anywhere.herokuapp.com/";
const url = "http://datafeed.bet/en/esports.json ";
fetch(proxyurl + url)
.then(response => response.json())
.then(data => {
const list = data;
const games =
list &&
list.Sport &&
list.Sport.Events &&
list.Sport.Events.map((match) =>
match.Name.substr(0, match.Name.indexOf(","))
);
const uniqueGames = [...new Set(games)];
let combinedMatches = [];
data &&
data.Sport &&
data.Sport.Events &&
data.Sport.Events.map((game) => {
game.Matches.map((match) => {
match.Logo = game.Logo;
match.TournamentName = game.Name;
match.CategoryID = game.CategoryID;
match.ID = game.ID;
});
combinedMatches = combinedMatches.concat(game.Matches);
});
this.setState({
gameData: data,
games: games,
uniqueGames: uniqueGames,
preloading: false,
filteredGames: combinedMatches,
allMatches: combinedMatches,
count: Math.ceil(combinedMatches.length / this.state.pageSize),
});
var i;
let allMatches = 0;
let temp;
for (i = 0; i < this.state.filteredGames.length; i++) {
temp = allMatches =
allMatches + this.state.filteredGames[i].Matches.length;
}
this.setState({ allMatches: allMatches });
})
.catch(console.log('error');
Something that confuses me is that whether the data is returned or not, the "catch" statement gets called, outputting "error" to the console. I would like to build in some workaround for when the data is not returned. Would this be placed in the "catch" statement? If so, why how do I only let the catch run if the operation actually fails.
When you do this:
.catch(console.log('error'))
You immediately invoke console.log('error') and pass its result (which is undefined) to the catch. In this case it's invoking it right away, before the AJAX operation is even performed.
What you want to pass to catch is a function which would invoke that operation if/when it needs to:
.catch(e => console.log('error'))
As an aside, you'd probably also want to log the error itself so you can see what happened, as opposed to just the string 'error':
.catch(e => console.log('error', e))

Automatic Slowmode Discord.js

I've been trying to create a system that automatically sets the slowmode in a channel to a certain amount depending on how many messages have been sent. Lua is my primary language, and not Node.js, therefore I'm having quite a bit of trouble as to how I would go about this. If anyone had any suggestions, please let me know.
Two ways to go about it:
Use discord.js's <TextChannel>.setRateLimitPerUser(number)
https://discord.js.org/#/docs/main/stable/class/TextChannel?scrollTo=setRateLimitPerUser
Not sure if this actually does what you want though, the other option is creating a session storage of the text channels msgCount and compare it to time, i found setRateLimitPerUser in the middle of writing the code so didn't finish it, be here's a start:
const { Client, Collection } = require("discord.js");
const client = new Client();
client.slowdown = new Collection();
client.on("message", msg => {
const id = msg.channel.id;
const attempt = client.slowdown.get(id);
//4 messages at most per second
const ratio = 4;
//look at last how many seconds
const timeSpace = 5;
//TODO: check if channel already has cooldown
if (attempt) {
attempt.msgCount++;
const currentTime = Date.now();
const timePassed = (currentTime - attempt.time) / 1000;
if (attempt.msgCount >= ratio && attempt.msgCount / timePassed >= ratio) {
//setCoolDown
}
if (timePassed >= timeSpace) {
attempt.time = currentTime;
attempt.msgCount = 0;
}
} else {
client.slowdown.set(id, {
time: Date.now(),
msgCount: 1
});
}
});

Resources