Level system Discordjs - node.js

I am making a Level system for my bot. It's working perfectly, but there's one problem about the level system, It is when the bot making a restart, the current exp is resetting too. How can I stop it like that?
Here is my current code.
const db = require('quick.db');
if (!db[msg.author.id]) db[msg.author.id] = {
xp: 0,
level: 0
};
db[msg.author.id].xp++;
let userInfo = db[msg.author.id];
if(userInfo.xp > 100) {
userInfo.level++
userInfo.xp = 0
msg.reply("Congratulations, you level up")
}
if(msg.content.toLowerCase().startsWith(`${prefix}level`)) {
let userInfo = db[msg.author.id];
let member = msg.mentions.members.first();
let embed = new MessageEmbed()
.setTitle('Level Checking')
.setDescription("<#" + msg.member.id + ">" + ` Your current level is: ${userInfo.level}`)
.setColor('RANDOM')
.addFields({name:"Level", value: `${userInfo.level}`})
.addField("XP", userInfo.xp+"/100")
.setTimestamp()
.setFooter('Level | Reborn')
if(!member) return msg.channel.send({embeds: [embed]})
let memberInfo = db[member.id]
let embed2 = new MessageEmbed()
.setTitle('Level Checking')
.setDescription("<#" + msg.member.id + ">" + ` Your current level is: ${memberInfo.level}`)
.setColor('RANDOM')
.addFields({name:"Level", value: `${memberInfo.level}`})
.addField("XP", memberInfo.xp+"/100")
.setTimestamp()
.setFooter('Level | Reborn')
msg.channel.sendEmbed({embeds: [embed2]})
}
I'm using Discordjs v13

I don't see you are connecting to a database. All I see that you are using quick.db
I have not used quick.db but from the way you have written I am assuming that it is storing the data in a temp variable called db. You need to store that db in a database (for you library it should be sqlite3)

You are storing the data in the variable db.
Variables always get resettet when your bot restarts.
To keep the data you need to write the data to the actual database
For example:
db.set(msg.author.id.'level', 0) would set the users level to 0
Check the docs for more inforamtion about how getting data(and how to set/modify it)

You can use mongo db its a good database service, you can find a lot of documentation on internet and its easy to use
https://www.mongodb.com/, its free to use.
https://www.youtube.com/watch?v=8no3SktqagY, this video will explain you everything
I hope it will help

Related

How to make a new embed when the first embed reaches the description limit (4096)

I have added the logs case for my warn slash command, but I have a problem.. that problem is that if the embed description reaches the limit, i get an error and thats not what I want.
So basically, I want the a new embed to be created as like a "second page", and I can use my pagination function to help with navigating between pages and so on. I just don't exactly know how to do that or how to get started.
I am asking for some assistance here because my goal is to have a functional "warning logs" embed with buttons to navigate through the pages if there are more than one like most users will have.
case "logs": {
const buttonPages = require("../../functions/pagination");
const user = interaction.options.getUser("user");
const userWarnings = await warnSchema.find({ Guild: interaction.guild.id, User: user.id });
if (!userWarnings?.length) return interaction.reply({ content: `\`${user.tag}\` does not have any warnings.`, ephemeral: true });
const embedDescription = userWarnings.map((warn) => {
const moderator = interaction.guild.members.cache.get(warn.Moderator);
return [
`<:CL_Shield:937188831227183135> Warn ID: ${warn.id}`,
`<:CL_ReplyContinued:909444370221137930> Moderator: ${moderator || "unknown"}`,
`<:CL_ReplyContinued:909444370221137930> User: ${user}`,
`<:CL_ReplyContinued:909444370221137930> Reason: \`${warn.Reason}\``,
`<:CL_Reply:909436090413363252> Date: ${warn.Date}`,
].join("\n");
}).join("\n\n");
const embed = new EmbedBuilder()
.setTitle(`${user.tag}'s warnings`)
.setDescription(embedDescription)
.setColor("#2f3136");
//const pages = [embed];
//buttonPages(interaction, pages);
await interaction.reply({ embeds: [embed] });
}
What you're trying to do is called pagination. Code is below, but here is how it works:
First what you've got to do is determine how many different embeds it'll be. We can do that with description.length/4096, but we need to round up so we'd use Math.ceil. The Array().keys() allows us to get a list of all the numbers up to it to iterate over. For example Array(3).keys() would give us [0, 1, 2] so we can iterate over it.
We then need to select which part of the description we want to send. We want to start at (i*4096) since all previous embeds have 4096 characters, and then we want it to be 4096 characters long so we simply end it at (i*4096)+4096).
You also need to consider that we cannot always use interaction.reply as interactions can only be replied to once, so we must use interaction.channel.send to send them all to the channel. But, we will want some sort of response to the interaction, so we send the first one as a response to the interaction, and all following ones to the channel.
Here's the code:
for (const i of Array(Math.ceil(embedDescription.length/4096)).keys()) {
const embed = new EmbedBuilder().setDescription(embedDescription.substring((i*4096), (i*4096)+4096))
if(i === 0) await interaction.reply({embeds: [embed]})
else await interaction.channel.send({embeds: [embed]})
}

Best way to organize firebase writes on update trigger

There may be more than one correct answer to this question, but here's my issue: I have a user document in firebase with many fields that can be updated and which interact in different ways. If one field is updated, it may require a change to another field on the backend. Is it better to have a whole bunch of if statements each with their own write action if the condition is met or, or do single write at the end of the function for all the fields that might change. If a field does not change, I would have to write its original value back to itself. That seems clunky, but so does the other option. Am I missing a third option? What is the best practice here?
Here's an example of what I'm talking about. Updating fields one at a time is what I have now, which looks like this:
export const userUpdate = functions.firestore
.document("users/{userID}")
.onUpdate(async (change) => {
const beforeData = change.before.data();
const afterData = change.after.data();
// user levels up and gets more HP
if(beforeData.userLevel != afterData.userLevel){
const newMaxHP = 15 + 5 * afterData.userLevel;
change.after.ref.update({
maxHp: newMaxHP
})
}
//update user rating
if (beforeData.numberOfRatings != afterData.numberOfRatings) {
const newRating = placerRating(beforeData.userRating, beforeData.numberOfRatings, afterData.latestRating);
change.after.ref.update({
userRating: newRating
})
}
//replenish user funds from zero
if (afterData.money == 0){
change.after.ref.update({
money: 20
})
}
If I did it all in a single write, the if statements would assign a value to a variable, but not update the firestore document. Each if statement would include an else statement assigning the variable to the field's original value. There would be a single write at the end like this:
change.after.ref.update({
maxHp: newMaxHP,
userRating: newRating,
money: 20
})
I hope that helps.
[edit to add follow-up question about updating a map value]
#Dharmaraj's answer works great, but I'm struggling to apply it when updating a map value. BTW - I'm using Typescript.
Before using #Dharmaraj's solution, I was doing this:
admin.firestore().collection("users").doc(lastPlayerAttacker).update({
"equipped.weapon.usesLeft": admin.firestore.FieldValue.increment(-1)
});
Using the update object, I'm trying it like this, but I get the error "Object is of type 'unknown'"
const lastPlayerUpdates:{[key:string]:unknown} = {};
lastPlayerUpdates.equipped.weapon.usesLeft = admin.firestore.FieldValue.increment(-1);
admin.firestore().collection("users").doc(lastPlayerAttacker).update(lastPlayerUpdates);
Any advice on how to fix it?
Every time you call update(), you are being charged for 1 write operation. It'll be best to accumulate all updated fields in an object and then update the document only once as it'll be more efficient too. Try refactoring the code as shown below:
export const userUpdate = functions.firestore
.document("users/{userID}")
.onUpdate(async (change) => {
const beforeData = change.before.data();
const afterData = change.after.data();
const updatedData = {};
// user levels up and gets more HP
if (beforeData.userLevel != afterData.userLevel) {
const newMaxHP = 15 + 5 * afterData.userLevel;
updatedData.maxHp = newMaxHP;
}
//update user rating
if (beforeData.numberOfRatings != afterData.numberOfRatings) {
const newRating = placerRating(beforeData.userRating, beforeData.numberOfRatings, afterData.latestRating);
updatedData.userRating = newRating;
}
//replenish user funds from zero
if (afterData.money == 0) {
updatedData.money = 20;
}
await change.after.ref.update(updatedData);
console.log("Data updated");
return null;
})

Leaderboards level system discord js

I have this xp system and I save each id in a json with a name.
How can I display a top 5 people?
Is there a way to retrieve the data and display 5 at a time?
I would try to sort and after creating a forum to display fixed 5 but I don't know exactly how I could do that.
Any ideas?
const xp = require("../../xp.json")
if(!xp[message.author.id]) {
xp[message.author.id] = {
xp: 0,
level:1
};
}
let curxp = xp[message.author.id].xp;
let curlvl = xp[message.author.id].level;
let nxtLevelXp = curlvl * 300;
let difference = nxtLevelXp - curxp;
For example, this is how I retrieve the data for display
Use the Object.entries , here is an example -
const xp = require("../../xp.json");
var top5 = Object.entries(xp).sort((a,b)=>b[1].level - a[1].level).splice(0,5);
Try mapping them

How do I get the userid of second mention in a command

So I'm trying to make a command where someone can do -kill #firstUser #secondUser. This will increase the first user's kills by 1 and add a role to the second user mentioned. I can access the the first user mentioned by doing const firstUser = message.mentions.users.first(); but I'm not sure how to do the same for the second user.
I've tried accessing the message.mentions.users collection and converting it to an array (and trying to access that) but I can't get it to work.
const firstUser = message.mentions.users.get(0);
const secondUser = message.mentions.users.get(1);
How do I get the user class from a message with multiple mentions?
And what I found was, it returns an object, not a mention, and as you can't send an object, it will return as an empty message error.
So to send a mention, you send:
// Getting the first and second users
const allMentioned = message.mentions.users.array()
// First User `[0]`
const firstUser = allMentioned[0];
// Second User `[1]`
const secondUser = allMentioned[1];
// And so on and so forth...
// Add `<#` to the begginning of the id and `>` to the end of it to make a mention.
const mentionSecondUser = "<#" + secondUser.id + ">";
// Sending a message using the fetched property
message.channel.send(`Hey ${mentionSecondUser}, or whatever.`);
Alternatively, you can try using the other fetched properties using the following format, received from getting the property, say secondUser:
User {
id: '<secondUser's id>',
username: '<secondUser's username>',
bot: <true if secondUser a bot>,
discriminator: '<secondUser's discriminator>',
avatar: '<secondUser's avatarId>',
lastMessageID: <secondUser's lastMessageId>,
lastMessageChannelID: <secondUser's lastMessageChannelId>,
flags: UserFlags { bitfield: 0 }
}
An example of this is in the picture showed above.
You can use:
message.mentions.users.array()[1]
To get the second user in a message. Appropriately, use [2] for the third, [3] for the fourth, and so on.

Retrieving all Documents from couchdb using Node.js

I am writing a simple test app to experiment with the functionality of node.js and couchdb, so far i am loving it, but i ran in a snag. i have looked for and wide but can't seem to find an answer. My test server(a simple address book) does 2 things:
if the user goes to localhost:8000/{id} then my app returns the name and address of the user with that id.
if the user goes to localhost:8000/ then my app needs to return a list a names that are hyperlinks and takes them to the page localhost:8000/{id}.
I was able to get the first requirement working. i cant not seem to find how to retrieve a list of all names from my couchdb. that is what i need help with. here is my code:
var http = require('http');
var cradle = require('cradle');
var conn = new(cradle.Connection)();
var db = conn.database('users');
function getUserByID(id) {
var rv = "";
db.get(id, function(err,doc) {
rv = doc.name;
rv += " lives at " + doc.Address;
});
return rv;
}
function GetAllUsers() {
var rv = ""
return rv;
}
var server = http.createServer(function(req,res) {
res.writeHead(200, {'Content-Type':'text/plain'});
var rv = "" ;
var id = req.url.substr(1);
if (id != "")
rv = getUserByID(id);
else
rv = GetAllUsers();
res.end(rv);
});
server.listen(8000);
console.log("server is runnig");
As you can see, I need to fill in the GetAllUsers() function. Any help would be appreciated. Thanks in advance.
I would expect you to be doing something like (using nano, which is a library I authored):
var db = require('nano')('http://localhost:5984/my_db')
, per_page = 10
, params = {include_docs: true, limit: per_page, descending: true}
;
db.list(params, function(error,body,headers) {
console.log(body);
});
I'm not pretty sure what you are trying to accomplish with http over there but feel free to head to my blog if you are looking for some more examples. Just wrote a blog post for people getting started with node and couch
As said above it will come a time when you will need to create your own view. Check up the CouchDB API Wiki, then scan thru the book, check what are design documents, then if you like you can go and check the test code I have for view generation and querying.
You can create a CouchDB view which will list the users. Here are several resources on CouchDB views which you should read in order to get a bigger picture on this topic:
Introduction to CouchDB Views
Finding Your Data with Views
View Cookbook for SQL Jockeys
HTTP View API
So let's say you have documents structured like this:
{
"_id": generated by CouchDB,
"_rev": generated by CouchDB,
"type": "user",
"name": "Johny Bravo",
"isHyperlink": true
}
Then you can create a CouchDB view (the map part) which would look like this:
// view map function definition
function(doc) {
// first check if the doc has type and isHyperlink fields
if(doc.type && doc.isHyperlink) {
// now check if the type is user and isHyperlink is true (this can also inclided in the statement above)
if((doc.type === "user") && (doc.isHyperlink === true)) {
// if the above statements are correct then emit name as it's key and document as value (you can change what is emitted to whatever you want, this is just for example)
emit(doc.name, doc);
}
}
}
When a view is created you can query it from your node.js application:
// query a view
db.view('location of your view', function (err, res) {
// loop through each row returned by the view
res.forEach(function (row) {
// print out to console it's name and isHyperlink flag
console.log(row.name + " - " + row.isHyperlink);
});
});
This is just an example. First I would recommend to go through the resources above and learn the basics of CouchDB views and it's capabilities.

Resources