Help. So my tip/give economy command is not working very well.
The structure is
!tip user 10
There is a single space between "user" and 10 but if someone put another space like
!tip user 10
The db will crash and the economy will be null! Help, how i can fix that?
let Discord = require('discord.js')
const db = require("quick.db")
module.exports = {
name: "tip",
aliases: ['give'],
async run(client, message, args) { let currency = "yeni" //your currency
let number = message.content.substring(10)
let amount = parseInt(number.split(" ")[1])
if(amount == null) return message.reply("Ceva nu a mers bine. Încearcă din nou!")
let EASPORTS = message.mentions.members.first()
if (!EASPORTS) return message.channel.send(`**${message.author.username}** menționează pe cineva!`)
let currentWallet = await db.get(`wallet_${message.author.id}`)
let currentWallet2 = await db.get(`wallet_${EASPORTS.id}`)
if (currentWallet < amount) return message.channel.send(`**Nu ai suficienți yeni!**`)
else if (amount <= 5) return message.channel.send(`**Nu poți da ${amount}${currency} cuiva!**`)
else if (isNaN(args[1])) return message.channel.send('Pune un număr valid! n\ **Structura comenzii: !tip #mihaitza 78**');
else {
db.set(`wallet_${message.author.id}`, currentWallet - amount)
db.set(`wallet_${EASPORTS.id}`, currentWallet2 + amount)
message.channel.send(`**${message.author.username}**, ai dat ${amount} ${currency} lui **${EASPORTS.user.username}** *iar suma de yeni rămasă pe care o mai ai este ${currentWallet - amount} ${currency}*`)
}
}
}
/**
* #param { db } async [ exports ] => {
* node .
* }
*/
That's the only problem. When someone put another space between user and number,the db crash to NaN.
Related
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}`,
);
});
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()
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);
});
});
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)
}
I'm trying to learn javascript and how to use the developer api provided by discord.
I do believe everything I want so far is working except I wanted to create a system like how a database works. Where each player in the table has a unique id key. I'm not sure if this is possible without using a db.
[Index.js]
/* Discord API Information */
const Discord = require('discord.js');
const token = '';
const Game = require('./game.js');
const client = new Discord.Client();
let playersName = []; // tracks each player that joins
let currPlayers = 0; //tracker for total players online
let INIT_GAME;
let userJoined = false;
let inBattle = false;
client.on('message', (msg) => {
if(msg.content === '!join'){
// Prevents multiple instances of the same person from joining
for(var x = 0; x < playersName.length; x++){
if(playersName[x]===msg.author.username){
return playersName[x];
}
}
currPlayers++;
userJoined = true;
playersName.push(msg.author.username);
//My attempt at having the question im asking
function convertToID(arr, width) {
return arr.reduce(function (rows, key, index) {
return (index % width == 0 ? rows.push([key])
: rows[rows.length-1].push(key)) && rows;
}, []);
}
console.log(convertToID(playersName,1)); /* Tracks players by ID in developer tools */
INIT_GAME = new Game(playersName, client, 'bot-testing', currPlayers);
let myRet = INIT_GAME.startGame();
const embed = new Discord.RichEmbed()
.setTitle("Welcome To Era Online")
.setColor(0xFF0000)
.addField(`${msg.author.username} has Joined`, myRet);
msg.channel.send(embed);
msg.channel.send(`${msg.author} has joined the game.`);
return;
}
if(userJoined == true){
if(msg.content === '!fight' && (!inBattle)){
let grabCurrPlayer = msg.author.username;
msg.channel.send(`${INIT_GAME.initBattle(grabCurrPlayer)}`);
}
else if(msg.content === '!leave'){
let tempLeave = msg.author.username;
for(var y = 0; y < playersName.length; y++){
if(playersName[y] == msg.author.username){
playersName[y] = [`${playersName[y]} was previously ID: ` + [y]];
currPlayers--;
}
}
msg.channel.send([`${tempLeave} has left the server.`]);
userJoined = false;
}
else if(msg.content === '!newgame'){
msg.channel.send(INIT_GAME.newGame());
}
/* Simply checks the bonus damage. command for developer*/
else if(msg.content === '!bonus'){
msg.channel.send(INIT_GAME.bonusAttack());
}
}
/* checks whose currently online. command for developer*/
if(msg.content === '!online'){
msg.channel.send(INIT_GAME.getOnline());
}
});
client.on('ready', () => {
console.log('Bot is now connected');
});
client.login(token);
[game.js]
class Game {
constructor(player, client, channelName='bot-testing', playersOnline){
this.client = client;
this.channelName = channelName;
this.currentPlayer = player;
this.playersOnline = [];
this.hitpoints = 120;
this.damage = '';
this.chance = 3;
this.inBattle = false;
this.online = playersOnline;
this.monster = [{
hp: Math.floor(Math.random() * 200),
temphp: 0,
damage: 10
}];
};
/* main menu information, players online */
startGame(){
for(var x = 0; x < this.currentPlayer.length; x++){
this.playersOnline.push(this.currentPlayer[x]);
if(this.playersOnline[x] === this.currentPlayer[x]){
return [`Players Online: ${this.online}\n`];
}
}
}
/* Battle system */
initBattle(currPlayer){
this.inBattle = true;
let npcHP = this.monster[0].hp;
let numberOfAttacks = 0;
let totalDamage=0, totalBonusDamage=0;
while( this.monster[0].hp > 0 ){
let playerDamage = Math.floor(Math.random() * (npcHP / 4));
if(this.bonusAttack() === 2){
console.log(`Bonus Attack: ${this.bonusAttack()}`);
console.log(`Regular damage without bonus attack: ${playerDamage}`);
playerDamage = playerDamage + 2;
}
this.monster[0].hp -= playerDamage;
this.hitpoints -= this.monster[0].damage;
console.log('Monster: ' + this.monster[0].hp);
console.log('Player: ' + this.hitpoints);
console.log(`${currPlayer} has attacked for ${playerDamage}`);
console.log(`NPC health: ${this.monster[0].hp}`);
if(this.hitpoints <= 0){
return [`You lost the battle.`];
}
this.inBattle = false;
numberOfAttacks++;
totalDamage += playerDamage;
totalBonusDamage = playerDamage + this.bonusAttack();
}
if(this.monster[0].hp <= 0 && this.inBattle !== true){
let maxDamage = totalDamage + totalBonusDamage;
return [`${currPlayer} has attacked ${numberOfAttacks} times dealing ${totalDamage} + (${totalBonusDamage}) bonus damage for a total of ${maxDamage} damage. The monster is dead.\n
Your Health: ${this.hitpoints}`];
}
else{
this.newGame();
return [`You rejuvenated your hitpoints and are ready for battle. \nType !fight again to start a new battle!`];
}
}
/* bonus attack damage [ 1 in 3 chance ] */
bonusAttack(bonusDamage){
let chance = Math.floor(Math.random() * 3);
return chance === 2 ? bonusDamage = 2 : false;
}
/* displays players currently online */
getOnline(){
console.log(this.currentPlayer);
return this.currentPlayer;
}
/* refresh stats */
newGame(){
this.monster[0].hp = Math.floor(Math.random() * 50);
this.hitpoints = 150;
}
}
module.exports = Game;
[My Question]
The only real important part within those 2 files is in index.js at the line that speaks about when a player leaves. So !leave.
I had a problem where one player typed !leave, both people would leave. That is the solution I used to fix it.
I could not get it to empty the array for ONLY the person who typed the command.
Example:
Person A types !join
Players online = [PlayerA]
Person B types !join
Players online = [PlayerA, PlayerB]
Player A types !leave
Players online = [[], PlayerB]]
It would always insert a empty array in the spot. So what I did was just fill that spot with the users previous name and their array id.
What I want is so that it completely deletes the person from the array AND removes that empty spot.
I would also like to know if it's possible that every time someone types !join, I would be able to insert them into an new array that is multidimensional and has the id for each player so that when I type !online, it would display
[[0, PlayerA], [1, PlayerB]] . Like a database where I can always see their index if needed.
What I have so far:
https://i.imgur.com/lWrtEtB.png
It only tracks the last index after they leave. How do I make it display the current index of the players online?
Use findIndex() to find the position of the name in your array. Then use the splice() method to remove a value from your array. You don't need to use that for loop, as findIndex will run a similar loop.
var playerIndex = playersName.findIndex(function(index) {
return index === tempLeave
})
playersName.splice(playerIndex, 1)
After reading the second part of your question though, I think you should be creating objects inside of your array. An example would be:
[
{
playerName: "Foo",
id: indexNumber,
isOnline: true
},
{
playerName: "Bar",
id: indexNumber,
isOnline: true
}
]
When someone joins, you check if their name is already assigned to an object (you can use findIndex again here). If not you create a new player, else you will change the players isOnline attribute to true. I'm sure this is the best way to go about storing user info, but it might work for you.