discord.js find claim time in giveaways using roles - node.js

I recently added this to my bot where it checks for the roles and sends a message in the channel where it sends the claim time of the user.
module.exports = {
name: 'giveaway',
description: ':tada: the new winner is',
execute(message, args){
let winner = message.mentions.members.first();
const ouser = message.mentions.users.first();
var time = 10000;
var support = 0;
var donate = 0;
var boost = 0;
const allowedRole = winner.roles.cache.find(r => r.name === '・Supporter') || winner.roles.cache.find(r => r.name === 'Nitro・Donator') || winner.roles.cache.find(r => r.name === '・Booster')
if (!allowedRole) {
message.channel.send(`Congratulations **${ouser.username}**! You have ${time / 1000} seconds to DM the host!`)
.then(message => {
setTimeout(function() {
message.channel.send(`${time / 1000} seconds up!`)
}, time)
})
return;
}
switch (allowedRole.name) {
case '・Supporter':
support = 3000;
break;
case 'Nitro・Donator':
donate = 5000;
break;
case '・Booster':
boost = 5000;
}
var newTime = (time + support + donate + boost));
const user = message.mentions.users.first();
message.channel.send(`Congratulations **${user.username}**! You have ${newTime / 1000} seconds to DM the host!`)
.then(message => {
setTimeout(function() {
message.channel.send(`${newTime / 1000} seconds up!`)
}, newTime)
})
}
}
I'm not sure how to add the time for the total claim time since + isn't working. I tried using time - (- support) - (-donate) - (-boost)) but it only showed 13 seconds (supporter role). Any fix to this?

The problem is in this line
const allowedRole = winner.roles.cache.find(r => r.name === '・Supporter') || winner.roles.cache.find(r => r.name === 'Nitro・Donator') || winner.roles.cache.find(r => r.name === '・Booster')
allowedRole can be set to only one role but a user can have multiple roles.
You can do something like this
module.exports = {
name: "giveaway",
description: ":tada: the new winner is",
execute(message, args) {
const winner = message.mentions.members.first();
let DefaultTime = 10;
let support = 0;
let donate = 0;
let boost = 0;
//get all the roles of the winner in an array
const userRoles = winner.roles.cache.map((r) => r.name);
//Check if the user have the allowed roles and set time according to that (There might be a better way to do this instead of using if statements for each of them seperately)
if (userRoles.includes("・Supporter")) {
support = 3;
}
if (userRoles.includes("Nitro・Donator")) {
donate = 5;
}
if (userRoles.includes("・Booster")) {
boost = 5;
}
const TotalTime = DefaultTime + support + donate + boost;
message.channel
.send(
`Congratulations **${winner.user.username}**! You have ${TotalTime} seconds to DM the host!`
)
.then((message) => {
setTimeout(function () {
message.channel.send(`${TotalTime} seconds up!`);
}, TotalTime * 1000);
});
},
};

Related

How can I make my leaderboard command sorted from highest to lowest on discord.js?

This is my current code:
function leaderboardembed() {
const filtered = client.points.filter(p => p.guild === message.guild.id).array();
let orilent;
const sorted = filtered.sort((a, b) => b.vouches - a.vouches );
let embeds = [];
let j = 0;
let first = (10)
let maxnum = 50;
orilent = sorted.length;
if(isNaN(maxnum)) {
console.log("maximum_leaderboard NOT A NUMBER")
maxnum = 50;}
if (maxnum > sorted.length)
maxnum = sorted.length + (10 - Number(String(sorted.length/10).slice(2)));
if (maxnum < 10) maxnum = 10;
for (let i = 10; i <= maxnum; i += 10) {
const top = sorted.splice(0, 10);
const embed = new Discord.MessageEmbed()
.setTitle(`\`${message.guild.name}\` | Leaderboard`)
.setTimestamp()
.setDescription(`Top ${i<orilent?i:orilent}/${orilent} Ranking:`)
.setColor(embedcolor);
for (const data of top) {
j++;
try {
embed.addField(`**${j}**. \`${data.usertag}\``, ` | **Vouches:** \`${data.vouch}\``);
} catch {
embed.addField(`**${j}**. \`${data.usertag}\``, ` | **Vouches:** \`${data.vouch}\``);
}
}
embeds.push(embed);
}
return embeds;
}
async function leaderboard() {
let currentPage = 0;
const embeds = leaderboardembed();
if (embeds.length == 1)
return message.channel.send(embeds[0]).catch(e=>console.log("ranking: " + e))
const lbembed = await message.channel.send(
`**Current Page - ${currentPage + 1}/${embeds.length}**`,
embeds[currentPage]).catch(e=>console.log("ranking: " + e));
try {
await lbembed.react("⏪");
await lbembed.react("⏹");
await lbembed.react("⏩");
} catch (error) {
console.error(error);
}
const filter = (reaction, user) => ["⏪", "⏹", "⏩"].includes(reaction.emoji.name) && message.author.id === user.id;
const collector = lbembed.createReactionCollector(filter, {
time: 60000
});
collector.on("collect", async (reaction, user) => {
try {
if (reaction.emoji.name === "⏩") {
if (currentPage < embeds.length - 1) {
currentPage++;
lbembed.edit(`**Current Page - ${currentPage + 1}/${embeds.length}**`, embeds[currentPage]);
}
} else if (reaction.emoji.name === "⏪") {
if (currentPage !== 0) {
--currentPage;
lbembed.edit(`**Current Page - ${currentPage + 1}/${embeds.length}**`, embeds[currentPage]);
}
} else {
collector.stop();
reaction.message.reactions.removeAll();
}
await reaction.users.remove(message.author.id);
} catch (error) {
console.error(error);
}
});
}
This is how it displays when doing the command
The problem is that there's about 1000 members and some don't have the "vouches" and id rather just display the people that have the highest to lowest.
Here's an example of what I'm aiming it to be like:
Firstly, to sort all of them, you do need them all in an array. Otherwise, it will be excessively difficult. Here is a simple sort function that JavaScript provides for us:
var ranks = [7, 2, 9, 5, 10, 110]
ranks.sort() //don’t use this, this would put 10, 110, 2 etc since it’s alphabetical, you need to specify how to sort it
ranks.sort((a, b)=> a - b) //2, 5, 7, 9, 10, 110
//to reverse that
ranks.sort((a, b) => b-a)

How can i make this function to restart itself?

I have a Discord BOT written with Node.JS using Discord.JS
There i have a function that starts when the bot becomes online
It consists in getting an array from an API: https://jacob.rede-epic.com/api/upcomingEvents
With the timestamp, it is supposed to send a message 10, 5 and 1 minute before it happens, when it happens (Exact hour + 15 minutes, e.g: 08:15/09:15/10:15 etc), and when it finishes (20 minutes later, e.g: 08:35/09:35/10:35 etc)
How can i make it to reset the whole system ?
Code:
const { MessageEmbed } = require('discord.js');
const axios = require('axios');
class Jacob {
constructor(client) {
this.client = client;
}
async start() {
const jacobContestsChannel = this.client.channels.cache.get('852596632591269968');
let nextEventObject = null;
let sentNotification = false;
let sentNotification2 = false;
let sentNotification3 = false;
let sentNotificationActive = false;
let sentNotificationFinished = false;
const cropNames = [
'Cactus',
'Carrot',
'Cocoa beans',
'Melon',
'Mushroom',
'Nether wart',
'Potato',
'Pumpkin',
'Sugar cane',
'Wheat',
];
setInterval(async () => {
let upcomingEvents = [];
let response = await axios.get('https://jacob.rede-epic.com/api/upcomingEvents');
let request = response.data;
for (let i in request) {
upcomingEvents.push(request[i]);
}
if (nextEventObject == null) {
nextEventObject = upcomingEvents[0];
} else {
let diff = nextEventObject.timestamp - Date.now();
let active = diff < 0;
setInterval(() => {
try {
diff = nextEventObject.timestamp - Date.now();
} catch {
nextEventObject = null;
}
}, 1000);
if (diff < -20 * 60 * 1000) {
sentNotification = false;
sentNotification2 = false;
sentNotification3 = false;
sentNotificationActive = false;
nextEventObject == null;
if (!sentNotificationFinished) {
jacobContestsChannel.send(new MessageEmbed()
.setColor(this.client.embedColor)
.setTitle('The current contest has ended!')
);
sentNotificationFinished = true;
}
} else if (!active && diff < 10 * 60 * 1000 && !sentNotification) {
jacobContestsChannel.send(new MessageEmbed()
.setColor(this.client.embedColor)
.setTitle('A contest is starting in 10 minutes')
.setDescription(`Crops: ${nextEventObject.crops.map(crop => cropNames[crop]).join(', ')}`)
);
sentNotification = true;
} else if (!active && diff < 5 * 60 * 1000 && sentNotification && !sentNotification2) {
jacobContestsChannel.send(new MessageEmbed()
.setColor(this.client.embedColor)
.setTitle('A contest is starting in 5 minutes')
.setDescription(`Crops: ${nextEventObject.crops.map(crop => cropNames[crop]).join(', ')}`)
);
sentNotification2 = true;
} else if (!active && diff < 60 * 1000 && sentNotification && sentNotification2 && !sentNotification3) {
jacobContestsChannel.send(new MessageEmbed()
.setColor(this.client.embedColor)
.setTitle('A contest is starting in 1 minute')
.setDescription(`Crops: ${nextEventObject.crops.map(crop => cropNames[crop]).join(', ')}`)
);
sentNotification3 = true;
} else if (active && !sentNotificationActive) {
jacobContestsChannel.send(new MessageEmbed()
.setColor(this.client.embedColor)
.setTitle('A contest has started!')
.setDescription(`Crops: ${nextEventObject.crops.map(crop => cropNames[crop]).join(', ')}`)
);
sentNotificationActive = true;
}
console.clear();
console.log(nextEventObject);
console.log(`Diff: ${diff}`);
console.log(`Active: ${active}`);
console.log(`Notification: ${sentNotification}`);
console.log(`Notification Active: ${sentNotificationActive}`);
console.log(`Notification Finished: ${sentNotificationFinished}`);
}
}, 1000);
}
async requestUpcomingEvents() {
let response = await axios.get('https://jacob.rede-epic.com/api/upcomingEvents');
return response.data;
}
}
module.exports = Jacob;

How to change discord-xp from giving XP per message to giving XP per minute?

The bot gives random XP between 50 and 100 points every random message, which is really slow. Having XP per minute would be better.
This is the code that gives XP, from levels.js in discord-xp:
client.on("message", async message => {
if (!message.guild) return;
if (message.author.bot) return;
if (!message.content.startsWith(prefix) || message.author.bot) return;
const args = message.content.slice(prefix.length).trim().split(/ +/);
const command = args.shift().toLowerCase();
const randomXp = Math.floor(Math.random() * 50) + 50;
console.log(randomXp);
const hasLeveledUp = await Levels.appendXp(message.author.id, message.guild.id, randomXp);
if (hasLeveledUp) {
const user = await Levels.fetch(message.author.id, message.guild.id);
message.channel.send(`<:GG:807231058507464735> ${message.author}, you just leveled up to **level ${user.level}!** Keep going!`);
}
});
I've tried to fit
const mongoose = require("mongoose");
const LevelSchema = new mongoose.Schema({
userID: { type: String },
guildID: { type: String },
xp: { type: Number, default: 0 },
level: { type: Number, default: 0 },
lastUpdated: { type: Date, default: new Date() },
last_message: 0,
});
module.exports = mongoose.model('Levels', LevelSchema);
and
if (Date.now() - levels.last_message > 60000) {
const randomXp = levels.xp += random.int(15, 25);
levels.last_message = Date.now();
}
in there, but it's not quite doing what I want.
I'm not sure if Mongoose is really necessary. I would start with something like this.
const checkpoint = {};
client.on("message", async message => {
if (!message.guild) return;
if (message.author.bot) return;
if (!message.content.startsWith(prefix) || message.author.bot) return;
const args = message.content.slice(prefix.length).trim().split(/ +/);
const command = args.shift().toLowerCase();
const authorKey = message.author.id + '|' + message.guild.id;
if (!(authorKey in checkpoint)) checkpoint[authorKey] = Date.now();
const minutes = Math.floor((Date.now() - checkpoint[authorKey]) / 60000);
if (!minutes) return; else checkpoint[authorKey] = Date.now();
// add a random XP amount for each minute (better than `minutes * random.int(15, 25)`)
let xpEarned = 0;
for (let m = 0; m < minutes; m++) xpEarned += random.int(15, 25);
const hasLeveledUp = await Levels.appendXp(message.author.id, message.guild.id, xpEarned);
if (hasLeveledUp) {
const user = await Levels.fetch(message.author.id, message.guild.id);
message.channel.send(`<:GG:807231058507464735> ${message.author}, you just leveled up to **level ${user.level}!** Keep going!`);
}
});

createReactionCollector not triggering

I'm building a bot sending an embed and collecting reaction from it. Upon reaction, the bot edit its embed to show all the users who reacted.
Sometimes, it works for 2 or 3 users reaction quickly, especially when the bot starts. But, afterward, it doesn't collect anymore reactions.
The bot is hosted on Heroku, using worker in procfile.
I tried on local, but, sometimes, it blocks after 1 user reaction.
const client = new Discord.Client({ partials: ['MESSAGE', 'CHANNEL', 'REACTION'] });
function sendEmbed(message) {
var users = [];
message.channel.send(startingEmbed).then(async function(message){
const filter = (reaction, user) => reaction.emoji.name === '👍';
const collector = message.createReactionCollector(filter, { max: 50, time: 3 * 24 * 60 * 60 * 1000 });
collector.on('collect', async (reaction, user) => {
var users = [];
console.log(`Collected ${reaction.emoji.name} from ${user.tag}`);
if (reaction.message.partial) await reaction.message.fetch();
if (reaction.partial) await reaction.fetch();
var result = false;
for (var u in users) {
if (users[u].name === user.username) {
result = true;
break;
}
}
if(result == false) {
var roll = {};
roll.name = user.username;
roll.value = random(); //Math.random() method
users.push(roll);
var description = '';
users.forEach(element => {
description += '🎲 ' + element.value + ' for ' + element.name + '\n';
});
var edittedEmbed = new Discord.MessageEmbed()
.setColor('#0099ff')
.addFields(
{ name: 'rolls', value: `${description}`, inline: true },
);
message.edit(edittedEmbed);
console.log('newest user ' + roll.name + ' ' + roll.value);
users.forEach(element => console.log('user table' + element.name + ' ' + element.value));
}
});
nevermind, This issue is caused by discord api. It was fixed by adding GUILD_MEMBER to the partials.
As documented : https://github.com/discordjs/discord.js/issues/4980#issuecomment-723519865

How to make the bot charge coins regardless the state of the user in the voice channel

I’m making a bot with a coin system, one man has already helped me (thx very much, #Federico Grandi
) so that the bot check user in the voice channel and how much he is there. Now I do not know how to make the bot list the coins immediately after a minute has passed, not when the user has logged out.
let coins = require("./coins.json");
let voiceStates = {}
bot.on('voiceStateUpdate', (oldState, newState) => {
let { id } = oldState
if (!oldState.channel) {
console.log('user joined voice channel');
voiceStates[id] = new Date()
} else if (!newState.channel) {
console.log('user left voice channel')
let now = new Date()
let joined = voiceStates[id] || new Date()
let dateDiff = now.getTime() - joined.getTime();
if (oldState.selfMute === true && newState.selfMute === true){
console.log('2');
}
if (dateDiff > 60 * 1000) {
console.log('user earned 200 coins');
coins[message.author.id] = {
coins: coins[message.author.id].coins + 200
};
}
}
});
Try this:
let coins = require("./coins.json");
let voiceStates = {};
bot.on('voiceStateUpdate', (oldState, newState) => {
let { id } = oldState;
if (!oldState.channel) {
console.log('user joined voice channel');
voiceStates[id] = { joined: new Date() };
// If the user is not muted...
if (!newState.selfMute) {
// ...execute a callback a minute later (this may be cancelled: see below)
voiceStates[id].timeout = setTimeout(() => {
// charge the coins
// do whatever after the user has been on the channel unmuted for a minute
}, 60000);
}
} else if (!newState.channel || newState.selfMute) {
// If the user left the channel or muted themselves and there is a timeout...
if (voiceStates[id].timeout) {
// ...cancel the timeout
clearTimeout(voiceStates[id].timeout);
voiceStates[id].timeout = undefined;
}
if (!newState.channel) {
// rest of your code for if the user left the channel...
console.log('user left voice channel');
let now = new Date();
// Only difference from your code is here
let joined = voiceStates[id] && voiceStates[id].joined || new Date();
let dateDiff = now.getTime() - joined.getTime();
if (oldState.selfMute === true && newState.selfMute === true) {
console.log('2');
}
if (dateDiff > 60 * 1000) {
console.log('user earned 200 coins');
coins[message.author.id] = {
coins: coins[message.author.id].coins + 200
};
}
}
}
});
This uses setTimeout to charge the coins after a minute. If the user leaves the channel or mutes themselves, this timer will be cancelled/cleared.

Resources