Reputation: 15
So I am trying to make a simple level-XP system in discord where it would level up every time someone sends a message. I have this for my level system in my message event. Level is defined as the schema of it as I am using Mongoose. There problem is it can save it the person has no data and they send a message it creates data like XP: 0 level: 1 but afterwards won't update again when they send a message to like say XP: 25 etc. The error is (node:50868) UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'save' of null
level1.findOne({ guildID: message.guild.id, userID: message.author.id }, async (err, res) => {
if(err) return console.error(err)
if(!res) {
let newdata = new level1({
guildID: message.guild.id,
userID: message.author.id,
level: 1,
xp: 0,
totalxp: 0,
time: Date.now()
})
newdata.save()
res = newdata
} else {
if (message.content.startsWith(`${prefix}`)) return;
const generate = Math.floor(Math.random() * 18);
(await level1.findOneAndUpdate({ guildID: message.guild.id, userID: message.guild.id }, { totalxp: res.totalxp += generate, xp: res.xp + generate, time: Date.now() })).save()
if(res.xp >= res.level * 300) {
(await level1.findOneAndUpdate({ guildID: message.guild.id, userID: message.guild.id }, { level: res.level++, xp: 0 })).save()
message.channel.send(`Congratulations you are now Level **${res.level}**, ${message.author}`);
}
}
})
Upvotes: 0
Views: 686
Reputation: 2184
you don't need to add .save()
to findOneAndUpdate
findOneAndUpdate
will update the record in db without the need of using .save()
so your update queries should be like
await level1.findOneAndUpdate(
{ guildID: message.guild.id, userID: message.guild.id }, // filter part
{ totalxp: res.totalxp += generate, xp: res.xp + generate, time: Date.now() } // update part
)
and
await level1.findOneAndUpdate(
{ guildID: message.guild.id, userID: message.guild.id },
{ level: res.level++, xp: 0 }
)
I think this could do the trick
regarding the logic of the code, I think we can combine both the update queries in one query using some logic before the update query
we can use this logic in the else part
const generate = Math.floor(Math.random() * 18);
let updatePart = {
totalxp: (res.totalxp || 0) + generate, // this will be updated in all cases, even if the level is upgraded or not
time: Date.now() // this will also be updated in all cases
}
let msg = ''; // a message to be returned after the update
if (res.xp + generate >= res.level * 300) {
// if experience + random generate is greater than level * 300,
// then we need to update the level, and set the xp to 0
updatePart.level = res.level++;
updatePart.xp = 0;
// update the msg with the new level
msg = `Congratulations you are now Level **${res.level++}**, ${message.author}`
} else {
// if experience + random generate is lower than level * 300, this means the level is the same
// then we need to update the xp only
updatePart.xp = res.xp + generate;
// update the msg
msg = `Experience updated successfully`
}
// just do the update query once,
// If we just need to update the document,
// we can use updateOne instead of findOneAndUpdate
await level1.updateOne( // you can use findOneAndUpdate also, it's okay
{ guildID: message.guild.id, userID: message.guild.id }, // filter part
updatePart // this is the update object
)
message.channel.send(msg);
hope it helps
Upvotes: 2