Rate limit on guild.roles.create? - javascript

My project involves checking a json with course data and creating roles for each course. This worked fine the first few times but after making a few errors, deleting and recreating, the roles aren't being created.
Update roles command:
Essentially the roles get fetched once so multiple fetch requests aren't needed.
const guild = interaction.guild;
await guild.roles.fetch();
const rolesAdded = Array.from(guild.roles.cache.values()).map((r) => { return r.name; });
for(const d of getDisciplines()){
const roles = getDiscplineRoles(d);
for(const r of roles){
if(rolesAdded.includes(r[0])) continue;
let colour;
if(r[1] == "M"){
colour = "Purple";
} else if (r[1] == "S"){
colour = "Blue";
}
addRole(guild, r[0], colour);
rolesAdded.push(r[0]);
}
let courses = getCourses(d);
courses = courses.map((c) => {
return c[0];
});
for(const course of courses){
if(rolesAdded.includes(course)) continue;
addRole(interaction.guild, course, "LightGrey");
rolesAdded.push(course);
}
}
await interaction.reply("Roles created");
addRole function:
The console.log runs fine so i know this works
addRole: async function(guild, roleName, colour) {
// Get role and create if it doesn't exist
const role = await guild.roles.cache.find((r) => r.name === roleName);
if (role !== undefined){
return;
}
console.log(`${roleName} does not exists`);
await guild.roles.create({
name: roleName,
color: colour,
});
}
I have tried looking up something about a limit but have only found 2 forum posts mentioning 2 numbers, 250 and 1000.
Note: No error is returned at all, the program just seems to be waiting for the request to be filled
Links:
https://github.com/discord/discord-api-docs/issues/1480
https://support.discord.com/hc/en-us/community/posts/360050533812-Extreme-rate-limits-on-the-role-create-endpoint

Related

How Would I Make an External Database File? - Discord.JS

I am trying to build a discord bot, that would do leveling and warns and such, but i have no idea how I would link a file to the main javascript file so i can have all the database code be "external". I want to use SQLite for my database.
This is currently what i have
client.on("ready", () => {
// Check if the table "points" exists.
const table = sql.prepare("SELECT count(*) FROM sqlite_master WHERE type='table' AND name = 'scores';").get();
if (!table['count(*)']) {
// If the table isn't there, create it and setup the database correctly.
sql.prepare("CREATE TABLE scores (id TEXT PRIMARY KEY, user TEXT, guild TEXT, points INTEGER, level INTEGER);").run();
// Ensure that the "id" row is always unique and indexed.
sql.prepare("CREATE UNIQUE INDEX idx_scores_id ON scores (id);").run();
sql.pragma("synchronous = 1");
sql.pragma("journal_mode = wal");
}
// And then we have two prepared statements to get and set the score data.
client.getScore = sql.prepare("SELECT * FROM scores WHERE user = ? AND guild = ?");
client.setScore = sql.prepare("INSERT OR REPLACE INTO scores (id, user, guild, points, level) VALUES (#id, #user, #guild, #points, #level);");
});
client.on("messageCreate", message => {
if (message.author.bot) return;
let score;
if (message.guild) {
score = client.getScore.get(message.author.id, message.guild.id);
if (!score) {
score = { id: `${message.guild.id}-${message.author.id}`, user: message.author.id, guild: message.guild.id, points: 0, level: 1 }
}
score.points++;
const curLevel = Math.floor(0.1 * Math.sqrt(score.points));
if (score.level < curLevel) {
score.level++;
message.reply(`You've leveled up to level **${curLevel}**! Ain't that dandy?`);
}
client.setScore.run(score);
}
if (message.content.indexOf(config.prefix) !== 0) return;
const args = message.content.slice(config.prefix.length).trim().split(/ +/g);
const command = args.shift().toLowerCase();
if (command === "give") {
// Limited to guild owner - adjust to your own preference!
if (!message.author.id === message.guild.ownerId) return message.reply("You're not the boss of me, you can't do that!");
const user = message.mentions.users.first() || client.users.cache.get(args[0]);
if (!user) return message.reply("You must mention someone or give their ID!");
const pointsToAdd = parseInt(args[1], 10);
if (!pointsToAdd) return message.reply("You didn't tell me how many points to give...");
// Get their current points.
let userScore = client.getScore.get(user.id, message.guild.id);
// It's possible to give points to a user we haven't seen, so we need to initiate defaults here too!
if (!userScore) {
userScore = { id: `${message.guild.id}-${user.id}`, user: user.id, guild: message.guild.id, points: 0, level: 1 }
}
userScore.points += pointsToAdd;
// We also want to update their level (but we won't notify them if it changes)
let userLevel = Math.floor(0.1 * Math.sqrt(score.points));
userScore.level = userLevel;
// And we save it!
client.setScore.run(userScore);
return message.channel.send(`${user.tag} has received ${pointsToAdd} points and now stands at ${userScore.points} points.`);
}
if (command === "leaderboard") {
/*const top10 = sql.prepare("SELECT * FROM scores WHERE guild = ? ORDER BY points DESC LIMIT 10;").all(message.guild.id);*/
// Now shake it and show it! (as a nice embed, too!)
const embed = new EmbedBuilder()
.setTitle("Leader board")
.setDescription("Our top 10 points leaders!")
.setColor("#ff0000")
.addFields({ name: '\u200b', value: '\u200b' });
/*for (const data of top10) {
embed.addFields({ name: client.users.cache.get(data.user).tag, value: `${data.points} points (level ${data.level})` });
}*/
return message.channel.send({ embed: embed });
}
// Command-specific code here!
});
The Idea is that when someone messages in chat, the bot will see that message and randomize the amount of XP given to a member. I dont have a full idea on how this can be done externally, if it can.
If i understand this correctly, you want another JS file to be run inside of your current file. Node JS makes this pretty easy!
require('./filename.js')
(also, you might want to use external files to store your commands too!)

Discord.js Reaction collector failing if no user reacts with first reaction. Cannot read property 'count' of undefined

I've set up a pretty basic reaction collector for discord.js - i've got 2 emojis that get used to react to a message. However if users only react with downVote (not upvote, the system errors):
D:\tzobot\commands\poll.js:102
if (reacts.get(downVote).count == reacts.get(upVote).count) { draw = true; }
^
TypeError: Cannot read property 'count' of undefined
at ReactionCollector.<anonymous> (D:\tzobot\commands\poll.js:102:55)
at ReactionCollector.emit (events.js:311:20)
at ReactionCollector.stop (D:\tzobot\node_modules\discord.js\src\structures\interfaces\Collector.js:149:10)
at D:\tzobot\node_modules\discord.js\src\structures\interfaces\Collector.js:72:73
at Timeout.<anonymous> (D:\tzobot\node_modules\discord.js\src\client\Client.js:436:7)
at listOnTimeout (internal/timers.js:549:17)
at processTimers (internal/timers.js:492:7)
This doesn't occur if the opposite happens (users react with upvote not downvote).
Relevant code:
const Discord = require('discord.js');
const downVote = "👎";
const upVote = "👍";
//irrelevantcode here setting properties such as duration etc.
const mesEmbed = new Discord.RichEmbed()
.setColor('#0099ff')
.setTitle(`Poll: ${pollQuestion}`)
//.setURL('https://discord.js.org/')
.setAuthor(`${message.author.username}`, `${message.author.avatarURL}`, 'https://discord.js.org')
.setDescription('Reply with 👍 or 👎 to give your opinion.')
sendmes(message, mesEmbed);
},
};
async function sendmes(message, mesEmbed) {
let msg = await message.reply(mesEmbed);
await msg.react(upVote);
await msg.react(downVote);
await startTimer(msg);
}
async function startTimer(mes) {
let filter = (reaction) => reaction.emoji.name == upVote || reaction.emoji.name == downVote;
const collector = mes.createReactionCollector(filter, { time: pollDuration });
collector.on('end', collected => {
console.log(`Collected ${collected.size} items`);
if (collected.size == 0) return mes.reply(`Poll: "${pollQuestion}" has now finished. The result is a tie with no votes.`);
var draw = Boolean(false);
var winner = null;
var loser = null;
var reacts = collected;
console.log(reacts);
if (reacts.get(downVote).count == reacts.get(upVote).count) { draw = true; }
else if (reacts.get(upVote).count > reacts.get(downVote).count) { winner = (upVote), loser = (downVote) }
else { winner = (downVote), loser = (upVote) }
//Check it wasn't a draw
if (draw == false) return mes.reply(`Poll: "${pollQuestion}" has now finished. The final decision is: ${winner} with ${reacts.get(winner).count} votes. ${loser} recieved ${reacts.get(loser).count} votes.`);
//Return draw message if it was
else return mes.reply(`Poll: "${pollQuestion}" has now finished. The result is a tie with ${reacts.get(upVote).count} votes each.`);
});
How can I prevent/handle the current error I am receiving better. I've tried setting downvote.count to 0 if it is null however this doesn't resolve it. It is very confusing how this only occurs when upvote isnt reacted but not visa-versa.
if (reacts.get(downVote).count == null) reacts.get(downVote).count = 0;
I'm not certain as to why it doesn't fail when users react with upvote only, I would expect it to fail in both scenarios. The reason it fails is because .get() returns undefined when nothing is available to get, and undefined does not have a count property. You just have to guard against such an outcome.
let uv = reacts.get(upVote);
let dv = reacts.get(downVote);
if (!uv && !dv) {
draw = true; // Both were undefined, nobody voted.
} else if (uv && dv && dv.count == uv.count) {
draw = true; // Neither was undefined and both had the same value.
}
Compact version
let uv = reacts.get(upVote);
let dv = reacts.get(downVote);
let draw = (!uv && !dv) || (uv && dv && dv.count == uv.count);

Command for index.js

How would i create a help command that displays all the commands when people do !help as i have no idea how to do a help command in the index.js file or would it be easir to put the help would be mutch apreated. its using the dicord econmy code but it dosnt have a help command to display all the commands
const eco = require("discord-economy");
//Create the bot client
const client = new Discord.Client();
//Set the prefix and token of the bot.
const settings = {
prefix: '!',
token: 'token'
}
//Whenever someone types a message this gets activated.
//(If you use 'await' in your functions make sure you put async here)
client.on('message', async message => {
//This reads the first part of your message behind your prefix to see which command you want to use.
var command = message.content.toLowerCase().slice(settings.prefix.length).split(' ')[0];
//These are the arguments behind the commands.
var args = message.content.split(' ').slice(1);
//If the message does not start with your prefix return.
//If the user that types a message is a bot account return.
if (!message.content.startsWith(settings.prefix) || message.author.bot) return;
if (command === 'balance') {
var output = await eco.FetchBalance(message.author.id)
message.channel.send(`Hey ${message.author.tag}! You own ${output.balance} coins.`);
}
if (command === 'daily') {
var output = await eco.Daily(message.author.id)
//output.updated will tell you if the user already claimed his/her daily yes or no.
if (output.updated) {
var profile = await eco.AddToBalance(message.author.id, 100)
message.reply(`You claimed your daily coins successfully! You now own ${profile.newbalance} coins.`);
} else {
message.channel.send(`Sorry, you already claimed your daily coins!\nBut no worries, over ${output.timetowait} you can daily again!`)
}
}
if (command === 'resetdaily') {
var output = await eco.ResetDaily(message.author.id)
message.reply(output) //It will send 'Daily Reset.'
}
if (command === 'leaderboard') {
//If you use discord-economy guild based you can use the filter() function to only allow the database within your guild
//(message.author.id + message.guild.id) can be your way to store guild based id's
//filter: x => x.userid.endsWith(message.guild.id)
//If you put a mention behind the command it searches for the mentioned user in database and tells the position.
if (message.mentions.users.first()) {
var output = await eco.Leaderboard({
filter: x => x.balance > 50,
search: message.mentions.users.first().id
})
message.channel.send(`The user ${message.mentions.users.first().tag} is number ${output} on my leaderboard!`);
} else {
eco.Leaderboard({
limit: 3, //Only takes top 3 ( Totally Optional )
filter: x => x.balance > 50 //Only allows people with more than 100 balance ( Totally Optional )
}).then(async users => { //make sure it is async
if (users[0]) var firstplace = await client.fetchUser(users[0].userid) //Searches for the user object in discord for first place
if (users[1]) var secondplace = await client.fetchUser(users[1].userid) //Searches for the user object in discord for second place
if (users[2]) var thirdplace = await client.fetchUser(users[2].userid) //Searches for the user object in discord for third place
message.channel.send(`My leaderboard:
1 - ${firstplace && firstplace.tag || 'Nobody Yet'} : ${users[0] && users[0].balance || 'None'}
2 - ${secondplace && secondplace.tag || 'Nobody Yet'} : ${users[1] && users[1].balance || 'None'}
3 - ${thirdplace && thirdplace.tag || 'Nobody Yet'} : ${users[2] && users[2].balance || 'None'}`)
})
}
}
if (command === 'transfer') {
var user = message.mentions.users.first()
var amount = args[1]
if (!user) return message.reply('Reply the user you want to send money to!')
if (!amount) return message.reply('Specify the amount you want to pay!')
var output = await eco.FetchBalance(message.author.id)
if (output.balance < amount) return message.reply('You have fewer coins than the amount you want to transfer!')
var transfer = await eco.Transfer(message.author.id, user.id, amount)
message.reply(`Transfering coins successfully done!\nBalance from ${message.author.tag}: ${transfer.FromUser}\nBalance from ${user.tag}: ${transfer.ToUser}`);
}
if (command === 'coinflip') {
var flip = args[0] //Heads or Tails
var amount = args[1] //Coins to gamble
if (!flip || !['heads', 'tails'].includes(flip)) return message.reply('Please specify the flip, either heads or tails!')
if (!amount) return message.reply('Specify the amount you want to gamble!')
var output = await eco.FetchBalance(message.author.id)
if (output.balance < amount) return message.reply('You have fewer coins than the amount you want to gamble!')
var gamble = await eco.Coinflip(message.author.id, flip, amount).catch(console.error)
message.reply(`You ${gamble.output}! New balance: ${gamble.newbalance}`)
}
if (command === 'dice') {
var roll = args[0] //Should be a number between 1 and 6
var amount = args[1] //Coins to gamble
if (!roll || ![1, 2, 3, 4, 5, 6].includes(parseInt(roll))) return message.reply('Specify the roll, it should be a number between 1-6')
if (!amount) return message.reply('Specify the amount you want to gamble!')
var output = eco.FetchBalance(message.author.id)
if (output.balance < amount) return message.reply('You have fewer coins than the amount you want to gamble!')
var gamble = await eco.Dice(message.author.id, roll, amount).catch(console.error)
message.reply(`The dice rolled ${gamble.dice}. So you ${gamble.output}! New balance: ${gamble.newbalance}`)
}
if (command == 'delete') { //You want to make this command admin only!
var user = message.mentions.users.first()
if (!user) return message.reply('Please specify a user I have to delete in my database!')
if (!message.guild.me.hasPermission(`ADMINISTRATION`)) return message.reply('You need to be admin to execute this command!')
var output = await eco.Delete(user.id)
if (output.deleted == true) return message.reply('Successfully deleted the user out of the database!')
message.reply('Error: Could not find the user in database.')
}
if (command === 'work') { //I made 2 examples for this command! Both versions will work!
var output = await eco.Work(message.author.id)
//50% chance to fail and earn nothing. You earn between 1-100 coins. And you get one out of 20 random jobs.
if (output.earned == 0) return message.reply('Awh, you did not do your job well so you earned nothing!')
message.channel.send(`${message.author.username}
You worked as a \` ${output.job} \` and earned :money_with_wings: ${output.earned}
You now own :money_with_wings: ${output.balance}`)
var output = await eco.Work(message.author.id, {
failurerate: 10,
money: Math.floor(Math.random() * 500),
jobs: ['cashier', 'shopkeeper']
})
//10% chance to fail and earn nothing. You earn between 1-500 coins. And you get one of those 3 random jobs.
if (output.earned == 0) return message.reply('Awh, you did not do your job well so you earned nothing!')
message.channel.send(`${message.author.username}
You worked as a \` ${output.job} \` and earned :money_with_wings: ${output.earned}
You now own :money_with_wings: ${output.balance}`)
}
if (command === 'slots') {
var amount = args[0] //Coins to gamble
if (!amount) return message.reply('Specify the amount you want to gamble!')
var output = await eco.FetchBalance(message.author.id)
if (output.balance < amount) return message.reply('You have fewer coins than the amount you want to gamble!')
var gamble = await eco.Slots(message.author.id, amount, {
width: 3,
height: 1
}).catch(console.error)
message.channel.send(gamble.grid)//Grid checks for a 100% match vertical or horizontal.
message.reply(`You ${gamble.output}! New balance: ${gamble.newbalance}`)
}
});
//Your secret token to log the bot in. (never show this to anyone!)
client.login(settings.token) ```
The easiest way to do it would be to add something like this
if (command === 'help') {
message.reply('here are my commands: commamd1 commamd2')
}
however, you could use an embed to make it look nicer, see here fore some info about them

Update roles only allowing one to the user at a time

Currently, the user is able to choose as many roles given within the section, however, I would like to be able to do something along the lines of:
(roleA OR roleB) AND (roleC OR roleD) AND roleE etc
The above is all meant to be triggered by checking what reactions they've already submitted and removing them if the new selection contradicts the current.
Users are able to apply reaction roles to themselves by reacting to a specified message. Depending on whether a certain choice is made, it's to be able to add/remove from another role i.e
How old are you?
- 18-24
- 25-30
- 31+
What's your Gender?
- Male
- Female
What Continent are you in?
- IDK, I'm running out of
- Bogus Questions
- To fill in space.
if user clicks, 25-30, but then realises they're 24, and click that instead, I'd like the prior reaction & role to be removed without manual interference required.
Not only 1 option will be available, so would like to have multiple selections available as well.
bot.on("raw", event =>{
console.log(event);
const eventName = event.t;
if(eventName === 'MESSAGE_REACTION_ADD')
{
if(event.d.message_id === '<REMOVED ID>')
{
var reactionChannel = bot.channels.get(event.d.channel_id);
if(reactionChannel.messages.has(event.d.message_id))
return;
else {
reactionChannel.fetchMessage(event.d.message_id)
.then(msg => {
var msgReaction = msg.reactions.get(event.d.emoji.name + ":" + event.d.emoji.id);
var user = bot.users.get(event.d.user_id);
bot.emit('messageReactionAdd', msgReaction, user);
})
.catch(err => console.log(err));
}
}
}
else if (eventName === 'MESSAGE_REACTION_REMOVE')
{
if(event.d.message_id === '<REMOVED ID>')
{
var reactionChannel = bot.channels.get(event.d.channel_id);
if(reactionChannel.messages.has(event.d.message_id))
return;
else{
reactionChannel.fetchMessage(event.d.message_id)
.then(msg => {
var msgReaction = msg.reactions.get(event.d.emoji.name + ":" + event.d.emoji.id);
var user = bot.users.get(event.d.user_id);
bot.emit('messageReactionRemove', msgReaction, user);
})
.catch(err => console.log(err));
}
}
}
});
bot.on('messageReactionAdd', (messageReaction, user) => {
var roleName = messageReaction.emoji.name;
var role = messageReaction.message.guild.roles.find(role => role.name.toLowerCase() === roleName.toLowerCase());
if(role)
{
var member = messageReaction.message.guild.members.find(member => member.id === user.id);
if(member)
{
member.addRole(role.id);
console.log('Success. Added role.');
}
}
});
bot.on('messageReactionRemove', (messageReaction, user) => {
var roleName = messageReaction.emoji.name;
var role = messageReaction.message.guild.roles.find(role => role.name.toLowerCase() === roleName.toLowerCase());
if(role)
{
var member = messageReaction.message.guild.members.find(member => member.id === user.id);
if(member)
{
member.removeRole(role.id);
console.log('Success. Removed role.');
}
}
});```
In your messageReactionAdd event, you can try to find a reaction or role applied by/to the user that corresponds with a specific choice. If there is one, that means they had already chose that answer. You can then remove them before adding the new role to the user. If not, the code should continue as usual.
bot.on('messageReactionAdd', async (messageReaction, user) => { // async needed for 'await'
const name = messageReaction.emoji.name.toLowerCase();
const message = messageReaction.message;
const role = message.guild.roles.find(role => role.name.toLowerCase() === name);
const member = message.guild.member(user);
if (!role || !member) return;
const emojis = message.reactions.map(emoji => emoji.name);
const conflictingReaction = message.reactions.find(reaction => reaction.users.get(user.id) && emojis.includes(reaction.emoji.name));
const conflictingRole = member.roles.find(role => emojis.includes(role.name.toLowerCase());
try {
if (conflictingReaction || conflictingRole) {
await conflictingReaction.remove(user);
await member.removeRole(conflictingRole);
}
await member.addRole(role);
console.log('Success.');
} catch(err) {
console.error(err);
}
});

if else in loop bringing up errors in typescript

I have this function that is supposed to get referral codes from users. User gives a code and the referral code checked if it exists in the database then evaluated if
it does not match the current user, so that one should not refer himself and
it is a match with one of the codes in the database
This code however just does not find a match even if the code given is in the database. If the referral code matches the one of the current user, it works correctly and points that out i.e one cannot refer themselves.
But if the referral code is a match to that of another user which is how a referral system should work, it still says no match.
How can I remove this error
export const getID = functions.https.onCall(async(data, context) => {
const db = admin.firestore();
const usersSnapshot = await db.collection("user").get();
const allUIDs = usersSnapshot.docs.map(doc => doc.data().userID);
const userID = context.auth.uid;
const providedID = "cNx7IuY6rZlR9mYSfb1hY7ROFY2";
//db.collection("user").doc(providedID).collection("referrals").doc(userID);
await check();
function check() {
let result;
allUIDs.forEach(idFromDb => {
if (providedID === idFromDb && (idFromDb === userID)) {
result = "ownmatch";
} else if (providedID === idFromDb && (idFromDb !== userID)) {
result = "match";
} else {
result = "nomatch";
}
});
return result;
}
if (check() === "match") {
return {
message: `Match Found`,
};
} else if (check() === "ownmatch") {
return {
message: `Sorry, you can't use your own invite code`,
};
} else {
return {
message: `No User with that ID`
};
}
});
(This is not an answer, but a simple refactoring.)
This is what your code is currently doing (roughly, I didn't run it):
const resultMsgs = {
nomatch: 'No User With That ID',
ownmatch: 'Sorry, you can\'t use your own invite code',
match: 'Match Found',
}
function check(uids, providedId, userId) {
let result
uids.forEach(idFromDb => {
if (providedId !== idFromDb) {
result = 'nomatch'
return
}
if (userID === idFromDb) {
result = 'ownmatch'
return
}
result = 'match'
})
return result
}
export const getID = functions
.https
.onCall(async (data, context) => {
const userId = context.auth.uid
const providedId = 'cNx7IuY6rZlR9mYSfb1hY7ROFY2'
const db = admin.firestore()
const user = await db.collection('user').get()
const uids = user.docs.map(doc => doc.data().userId)
const checkResult = check(uids, providedId, userId)
return { message: resultMsgs[checkResult] }
})
(I removed the seemingly-spurious db collection operation.)
Your forEach is iterating over all of the uuids, but result will be set to whatever the last comparison was. Perhaps this is correct, but:
If you're looking for any match, this is not what you want.
If you're looking for all matches, this is not what you want.
If you're looking to match the last UUID, it's what you want, but an odd way to go about it.
So:
If you want any matches, use... ahem any form of an any function.
If you want all matches, use any form of an all function.
If you want the first match, then just check the first element.
If you want the complete set of comparisons then you'll need to use map instead of forEach, and handle each result appropriately, whatever that means in your case.
In any event, I'd recommend breaking up your code more cleanly. It'll be much easier to reason about, and fix.

Categories