Discord.JS Leaderboards - javascript

I currently have some code to detect how many times you can click a reaction in a certain amount of seconds.
I am trying to make a leaderboard for everyone that saves the top 10 with the highest CPS (clicks per second). The code works perfect otherwise, but I am stuck on the leaderboard.
This is my code:
let CPStest = new Discord.MessageEmbed()
.setColor("#8f82ff")
.setTitle("CPS Test")
.setDescription(`Alright, ${message.author.username}. Ready to begin your CPS test? All you have to do is spam click this reaction above ^ as fast as you can, you have 10 seconds before the timer runs out. Good Luck!`)
.setFooter("Note: This may be inaccurate depending on Discord API and Rate Limits.")
message.channel.send(CPStest)
message.react('🖱️');
// Create a reaction collector
const filter = (reaction, user) => reaction.emoji.name === '🖱️' && user.id === message.author.id;
var clicks = 1 * 3; // (total clicks)
var timespan = 10; // (time in seconds to run the CPS test)
const collector = message.createReactionCollector(filter, { time: timespan * 1000 });
collector.on('collect', r => {
console.log(`Collected a click on ${r.emoji.name}`);
clicks++;
});
collector.on('end', collected => {
message.channel.send(`Collected ${clicks} raw clicks (adding reactions only)`);
clicks *= 2;
message.channel.send(`Collected ${clicks} total approximate clicks.`);
var cps = clicks / timespan / 0.5; // Use Math.round if you want to round the decimal CPS
message.channel.send(`Your CPS averaged ~${cps} clicks per second over ${timespan} seconds.`);
let userNames = '';
const user = (bot.users.fetch(message.author)).tag;
userNames += `${user}\n`;
let cpsLeaderboard = new Discord.MessageEmbed()
.setColor("#8f82ff")
.setTitle("Current CPS Record Holders:")
.addFields({ name: 'Top 10', value: userNames, inline: true },
{ name: 'CPS', value: cps, inline: true })
.setTimestamp()
message.channel.send(cpsLeaderboard)
});

If you want to have a leaderboard, you either need to save it to a file, use a database, or (if you're okay with it that you'll lose it when you restart your bot) you can declare a variable outside of your client.on('message'). It seems to be good enough for testing purposes.
You can create a topUsers array, push the player to that every time the game ends, then sort it and create a leaderboard.
Check the following snippet:
const topUsers = [];
client.on('message', (message) => {
if (message.author.bot) return;
let CPStest = new Discord.MessageEmbed()
.setColor('#8f82ff')
.setTitle('CPS Test')
.setDescription(
`Alright, ${message.author.username}. Ready to begin your CPS test? All you have to do is spam click this reaction above ^ as fast as you can, you have 10 seconds before the timer runs out. Good Luck!`,
)
.setFooter(
'Note: This may be inaccurate depending on Discord API and Rate Limits.',
);
message.channel.send(CPStest);
message.react('🖱️');
// Create a reaction collector
const filter = (reaction, user) =>
reaction.emoji.name === '🖱️' && user.id === message.author.id;
let clicks = 1 * 3; // (total clicks)
const timespan = 10; // (time in seconds to run the CPS test)
const collector = message.createReactionCollector(filter, {
time: timespan * 1000,
});
collector.on('collect', (r) => {
console.log(`Collected a click on ${r.emoji.name}`);
clicks++;
});
collector.on('end', (collected) => {
message.channel.send(
`Collected ${clicks} raw clicks (adding reactions only)`,
);
clicks *= 2;
message.channel.send(`Collected ${clicks} total approximate clicks.`);
const cps = clicks / timespan / 0.5;
message.channel.send(
`Your CPS averaged ~${cps} clicks per second over ${timespan} seconds.`,
);
topUsers.push({ tag: message.author, cps });
let cpses = '';
let usernames = '';
topUsers
// sort the array by CPS in descending order
.sort((a, b) => b.cps - a.cps)
// only show the first ten users
.slice(0, 10)
.forEach((user) => {
cpses += `${user.cps}\n`;
usernames += `${user.tag}\n`;
});
const cpsLeaderboard = new Discord.MessageEmbed()
.setColor('#8f82ff')
.setTitle('Current CPS Record Holders:')
.addFields(
{ name: 'Top 10', value: usernames, inline: true },
{ name: 'CPS', value: cpses, inline: true },
)
.setTimestamp();
message.channel.send(cpsLeaderboard);
});
});

Related

How to fetch more than 1000 bans of a discord server using discord.js?

I have a command serverinfo and it shows how many bans there are on the server. If the server has 1200 bans, the bot will show that the server has 1000 bans.
Is it possible to somehow make the bot shows how many bans really are?
In the documentation the following parameters are specified limit?| number | number of users to return (up to maximum 1000) | default 1000
How to change default 1000 to e.g. 1500?
I am using discord.js vers 13.8.0 and node.js version 16.15.1.
For a v13 discord.js solution to your problem, this is what I did.
I edited this to include the while loop.
let completeBanIdList = await (async (a = [], last = 0, limit = 1000) => {
while(limit === 1000){
let bans = await guild.bans.fetch({after: last, limit: limit});
let banlist = bans.map(user => user.user.id);
last = bans.last().user.id;
limit = banlist.length;
for(let i = 0; i < limit; i++){a.push(banlist[i]);}
}
return a;
})();
// below is just console logging to show that it worked properly...
let banIdObj = ((o = {}) => {
for(let i = 0; i < completeBanIdList.length; i++){
o[completeBanIdList[i]] = 1;
}
return o;
})();
console.log(`unique ban id count:`, completeBanIdList.length, `should equal number of id keys in object:`, Object.keys(banIdObj).length);
Although the max limit is 1000, you can use the limit and before options to fetch the previous bans, too. before is a snowflake, it tells Discord to consider only bans before this id.
So, if you have around 1200 bans, you can fetch the first 1000, grab the id of the last item in the returned collection and use its id as the before option in your second fetch:
let firstHalf = await guild.bans.fetch({ limit: 1000 });
let secondHalf = await guild.bans.fetch({ before: firstHalf.last().id, limit: 1000 });
You can also create a new function that fetches more than 1000 bans. There is an example below. This function returns a collection of GuildBans, so you can use the usual collection methods, like filter, each, etc.
async function fetchMoreBans(guild, limit = 2000) {
if (!guild || typeof guild !== 'object')
throw new Error(`Expected a guild, got "${typeof guild}"`);
if (!Number.isInteger(limit))
throw new Error(`Expected an integer, got ${typeof limit}.`);
if (limit <= 1000) return guild.bans.fetch({ limit });
let collection = new Collection();
let lastId = null;
let options = {};
let remaining = limit;
while (remaining > 0) {
options.limit = remaining > 1000 ? 1000 : remaining;
remaining = remaining > 1000 ? remaining - 1000 : 0;
if (lastId) options.before = lastId;
let bans = await guild.bans.fetch(options);
if (!bans.last()) break;
collection = collection.concat(bans);
lastId = bans.last().id;
}
return collection;
}
Example usage:
client.on('messageCreate', async (message) => {
if (message.author.bot) return;
let banList = await fetchMore(message.guild, 1500);
let filteredBanList = banList.filter(({ user }) =>
user.username.toLowerCase().startsWith('e'),
);
console.log(`There are ${banList.size} bans fetched`);
console.log(
`Number of bans where the user's username starts with the letter "e": ${filteredBanList.size}`,
);
});

Discord JS join to voice channel at specific time

I know my code look like sh*t but im new im nodejs 😞 i don't know why my code is not work correctly (bot join to voice channel, theoretically is playing a music but practically no)
code:
while (true)
{
d = new Date();
currtime = d.getHours() * 100 + d.getMinutes();
console.log(currtime);
if (currtime == 1912 && loop == true)
{
loop = false;
console.log("21:37");
const connection = await client.channels.cache.get(VOICECHANNEL).join();
connection.play(LINK, { volume: VOLUME }); // link = url (mp3)
setTimeout(() => { client.channels.cache.get(VOICECHANNEL).leave();},204000); // 204000 = length of the song
}
else
{
sleep(204000);
loop = true;
}
}
I'm not completely sure what you are trying to accomplish, but here are a few issues that I can see.
First, you are not waiting for your sleep() function, so your loop will execute as fast as possible, over and over again.
Second, after you are waiting for your sleep(), it is currently set to sleep for 204 seconds, but you are checking to see if curtime is exactly the target time, rather than greater than.you either need to reduce the sleep time, or change the if statement (or both).
I cant debug your discord code, as I dont know the syntax, but this should at least get you started.
const targetTime = 1326;
while (true) {
d = new Date();
currtime = d.getHours() * 100 + d.getMinutes();
console.log(currtime);
if (currtime >= targetTime && currtime < targetTime + 1) {
//const connection = await client.channels.cache.get(VOICECHANNEL).join();
console.log('connected');
//connection.play(LINK, { volume: VOLUME }); // link = url (mp3)
//setTimeout(() => { client.channels.cache.get(VOICECHANNEL).leave(); }, 204000); // 204000 = length of the song
break;
}
else {
console.log('sleep')
await sleep(60000);
loop = true;
}
}
It may also be worth looking into something like this: https://www.npmjs.com/package/node-schedule
Which is optimized for scheduling tasks.

Discord Bot: How to check if the client message is the correct answer?

I'm trying to build a simple math game and it works fine, but I can't check if the answer someone gives to the math problem is correct or not.
I made an if statement to see if the answer matches the message content:
if (msg.content == answer) {
msg.reply('correct');
}
The problem is that msg.content only accepts a string, not an integer. Can anyone help me fix that issue?
Here is the full code:
const Discord = require('discord.js');
const client = new Discord.Client();
client.on('ready', () => {
console.log(`Logged in as ${client.user.tag}!`);
});
client.on('message', (msg) => {
var minimum = 1;
var maximum = 100;
var int1 = Math.floor(Math.random() * (maximum - minimum + 1)) + minimum;
var int2 = Math.floor(Math.random() * (maximum - minimum + 1)) + minimum;
if (msg.author.bot) return;
//step 1
if (msg.content === 'mathplus') {
msg.reply(`hi what is ${int1} + ${int2}`);
var answer = int1 + int2;
console.log(answer);
}
//check if answer is correct -- where the problem is
if (msg.content == answer) {
msg.reply('correct');
}
});
client.login(process.env.TOKEN);
The problem is NOT that the msg.content is not an integer. You're correctly using double equals here (and 5 == '5'). The problem is that answer is no longer the sum of int1 and int2, it's undefined. When you use the mathplus command, you define the answer but if you send a new message with the answer, it's no longer available.
Check out the example below:
function test(command) {
if (command === 'mathplus') {
var answer = 5
console.log(`"mathplus" command. The answer is ${answer}`)
}
if (command == answer) {
console.log('correct')
}
console.log({
answer,
command
})
}
test('mathplus')
test('5')
As Radnerus mentioned in their comment, you can use message collectors to wait for an answer from the user. I've added an example below how you could use it with lots of comments:
const Discord = require('discord.js');
const client = new Discord.Client();
const prefix = '!';
// helper function to get a number between min and max
function randomInt(min, max) {
if (min > max) [min, max] = [max, min];
return Math.floor(Math.random() * (max - min + 1) + min);
}
client.on('message', async (message) => {
if (message.author.bot) return;
const args = message.content.slice(prefix.length).split(/ +/);
const command = args.shift().toLowerCase();
if (command === 'mathplus') {
const int1 = randomInt(0, 100);
const int2 = randomInt(0, 100);
const answer = int1 + int2;
// we only wait for 30s for an answer
const maxWait = 30000; // in ms
const embed = new Discord.MessageEmbed()
.setColor('#f8cf4d')
.setTitle(`Hey ${message.author.username}! What is ${int1} + ${int2}? 🙈`);
await message.channel.send(embed);
// filter checks if the response is from the same author who typed the command
const filter = (response) => response.author.id === message.author.id;
const collector = message.channel.createMessageCollector(filter, {
// set up the max wait time the collector runs
time: maxWait,
});
// fires when a response is collected
collector.on('collect', (response) => {
if (parseInt(response.content, 10) === answer) {
message.channel.send(
`🎉🎉🎉 Woohoo, ${response.author}! 🎉🎉🎉\n\nYou're a maths genius, the correct answer was \`${answer}\`.`,
);
// the answer is correct, so stop this collector and emit the "end" event
collector.stop();
} else {
// give the user another chance if the response is incorrect
message.channel.send(
`Oh, ${response.author}, \`${response.content}\` is not correct... 🙊\nDo you want to try again?`,
);
}
});
// fires when the collector is finished collecting
collector.on('end', (collected, reason) => {
// only send a message when the "end" event fires because of timeout
if (reason !== 'time') return;
// if there are incorrect answers
if (collected.size > 0) {
return message.channel.send(
`Ah, ${message.author}. Out of ${collected.size} guess${
collected.size > 1 ? 'es' : ''
} you couldn't find the number \`${answer}\`. I'm not saying you're slow, but no more answers are accepted.`,
);
}
// if the user haven't submitted any answer, let's get a bit more aggressive
return message.channel.send(
`Okay, ${message.author}, I'm bored and I can't wait any longer. No more answers are accepted. At least you could have tried...`,
);
});
}
});
client.on('ready', () => {
console.log(`Logged in as ${client.user.tag}!`);
});
client.login(process.env.TOKEN);
The result:
You might try this approach
if(!parseInt(message.content)){ // If the message wasn't actually a number
return message.reply("You can only answer numbers");
}
if(parseInt(message.content) === answer){ // Correct answer
return message.reply("Correct answer");
}else{ // Wrong answer
return message.reply("Oops");
}
parseInt() converts string to integers. So parseInt("10.103") would return 10. If you need to work with floating numbers try parseFloat()
Use these resources to learn more about these functions
Resources
parseInt()
parseFloat()

How to send a message every 10 seconds discord.js?

I am trying to send a message every x amount of seconds in a discord.js bot. I know how to do this put the problem I am having is that it is spamming messages even when I have slowmode enabled. How can I fix this?
client.on('message', message => {
if (message.content === '$ww) {
let count = 0;
let ecount = 0;
for(let x = 0; x < 9000; x++) {
message.channel.send(`hy`)
.then(m => {
count++;
})
}
}
});
You can use setInterval() to repeat your function every X milliseconds. For example:
setInterval(() => {
message.channel.send(`hy`).then(() => count++);
}, 10000);
setInterval(() => console.log('hey'), 1000)
The code you've provided is spamming because it is not waiting 10 seconds; it is counting from 0 to 9000 and for every count, it sends the message 'hy', so it quickly spams 9000 'hy' messages.

Arranging the leaderboards from highest balance to the lowest balance

There are no errors when I run this code. However, the bot does not send who has the highest balance through leaderboards. It sends who the first person who executed the command is. I just wanted to know how I can make the bot return the message through descending order. Here's the image if you want to see what I mean. Thanks! Database: v7.0.0
if (args[0] == 'coins') {
let coins = db.all().filter(a => a.ID.startsWith(`coins_${message.guild.id}`, { sort: '.data'}))
let content = "";
for (let i = 0; i < coins.length; i++) {
let user = client.users.cache.get(coins[i].ID.split('_')[2]).username
content += `${i+1}. ${user} ~ ${coins[i].data}\n`
}
const embed = new Discord.MessageEmbed()
.setDescription(`**${message.guild.name}'s Coin Leaderboard**\n\n${content}`)
.setColor(colored[~~(Math.random() * colored.length)])
message.channel.send(embed)
}

Categories

Resources