Leaderboard in quick.db

Hello.

I want to make a leaderboard command with quick.db.
How can I do it?

2 Likes

What kind of leaderboard do you want to make?

Are you storing data already? Are you having problems storing data or sorting it?

My code is already storing data. I have problems with sorting it and displaying only 10 per page.

The leaderboard I want to make is:

  • All the users are on it;
  • Only 10 per page.

There is no method in quick.db to sort the data.

But maybe you can use db.all() which give you an array.

Then array.sort(...)

Finally a loop to make the ‘pagination’.

Tell us if you have a problem with the code. Maybe you can share how is the structure of your db.all() result array

Well, I’ve already saw the quick.db docs, ADN there’s a say to sort ALL the users. And I already tryed many times to use the loops and pagination. I’m asking here because I really don’t know how to do it.

I find a way to sort the data with an older version. In the new one there is no function to sort it, so you have to use array.sort()

OLD VERSION [6.3.2]

package.json

...
  "dependencies": {
    ...,
    "quick.db": "6.3.2"
  }
...

leaderboard.js

async function getLeaderboard (page, per_page) {
  // Get all data sorted. Replace string
  const resp = await db.startsWith('string', {sort: '.data'});

  // Pagination
  var page = page || 1,
  per_page = per_page || 10,
  offset = (page - 1) * per_page,

  paginatedItems = resp.slice(offset).slice(0, per_page),
  total_pages = Math.ceil(resp.length / per_page);
  let end = {
    page: page,
    per_page: per_page,
    pre_page: page - 1 ? page - 1 : null,
    next_page: (total_pages > page) ? page + 1 : null,
    total: resp.length,
    total_pages: total_pages,
    data: paginatedItems
  };
  
  // RESULT
  return end;
}

NEW VERSION [7.0.0-b22]

If you use the updated version of quick.db you have to sort manually your data

leaderboard.js

function getLeaderboard (page, per_page) {
  // Get all data not sorted
  const resp = db.all();
  
  // Sort from higher to lower
  resp.sort((a, b) => (a.data < b.data) ? 1 : -1);
  
  /* Pagination: copy the code from last example */

  // RESULT
  return end;
}

The return data for both examples is something like this:

getLeaderboard(1, 10);

{ page: 1,
  per_page: 10,
  pre_page: null,
  next_page: 2,
  total: 200,
  total_pages: 20,
  data: 
   [ { ID: 'Nick[83]', data: 9972 },
     { ID: 'Nick[182]', data: 9907 },
     { ID: 'Nick[106]', data: 9899 },
     { ID: 'Nick[173]', data: 9895 },
     { ID: 'Nick[116]', data: 9886 },
     { ID: 'Nick[142]', data: 9858 },
     { ID: 'Nick[148]', data: 9831 },
     { ID: 'Nick[114]', data: 9803 },
     { ID: 'Nick[153]', data: 9769 },
     { ID: 'Nick[189]', data: 9548 } ] }

OK. It seems to work. But now I have the problem with sending an embed like this:

Leaderboard
Page: 1

+++
^^^^ Here I want to show for each user: ‘Username: usercoins coins’

Edit: And I want to show only the coins. I use the db to XP, coins, and others.

  // Pagination
  var page = page || 1,
  per_page = per_page || 10,
  offset = (page - 1) * per_page,

  paginatedItems = resp.slice(offset).slice(0, per_page),
  total_pages = Math.ceil(resp.length / per_page);
  
  // Leaderboard Message -> Make your message as you want.
  var leaderboardMessage;
  for (var i in paginatedItems) {
    leaderboardMessage += `${paginatedItems[i].ID} | Data: ${paginatedItems[i].data} \n`;
  }
  
  let end = {
    page: page,
    per_page: per_page,
    pre_page: page - 1 ? page - 1 : null,
    next_page: (total_pages > page) ? page + 1 : null,
    total: resp.length,
    total_pages: total_pages,
    data: paginatedItems,
    message: leaderboardMessage
  };
 
  // RESULT
  return end;

OK. So, all I have to do for the message is put leaderboardMessage in the description field, right?
And can I replace db.all() by db.all(‘coins’)?

I don’t know about db.all('coins')

But yeah. You should know, with all the information I gave to you, how you want to make your own command

OK. I used the old version, and it works fine, but it isn’t sending the message.
Here’s my code: https://pastebin.com/Ps9i9SuW

Hey there, @TheBigerGamer!

The above post is not “at all” related to Discord.js. Please refrain from posting such questions in wrong categories.

I only asked because I’m using it in my discord.js bot.

Line 10

const resp = await db.startsWith('string', {sort: '.data'});

You should change “string” for what your query to the database.

The db.startsWith(...) function is going to searh all the keys of your db wich starts with the first argument. In this example start with “string”

Take out the return end; at your line 38. This is where your code is stop reading so it doesn’t send message.

You can add return message.channel.send(topembed) in the 46 line

It gave me this:


And I’m seeing that the code don’t get the username…

It’s a range error :woman_shrugging:

Your RichEmbed is exceeding the limit, the maximum limit is 256 characters for a RichEmbed field.

EDIT: You can split them up into two embed if it exceeds the limit

Well, I finally changed the code, and now it shows the leaderboard, but I have THIS:error

The code is this:

const db = require('quick.db');

exports.run = async (bot, message, args) => {
  async function getLeaderboard (page, per_page) {
  // Get all data not sorted
  const resp = await db.startsWith('moons_', {sort: '.data'});
  // Pagination
    
  var page = page || 1,
  per_page = per_page || 5,
  offset = (page - 1) * per_page,

  paginatedItems = resp.slice(offset).slice(0, per_page),
  total_pages = Math.ceil(resp.length / per_page);
    
    let id = resp.slice('moons_')
      console.log(id)
    
  
  // Leaderboard Message -> Make your message as you want.
  var leaderboardMessage;
  for (var i in paginatedItems) {
    leaderboardMessage += `${paginatedItems[i].ID} | Moons: ${paginatedItems[i].data} \n`;
  }
  
  let end = {
    page: page,
    per_page: per_page,
    pre_page: page - 1 ? page - 1 : null,
    next_page: (total_pages > page) ? page + 1 : null,
    total: resp.length,
    total_pages: total_pages,
    data: paginatedItems,
    message: leaderboardMessage
  };

  // RESULT
    console.log(leaderboardMessage)
    const topembed = new Discord.RichEmbed()
    .setColor(16777215)
    .setAuthor('Top de Moons')
    .setDescription('Top de Moons do S1mple', `Página: ${page}`)
    .addField(leaderboardMessage, `Top 5`)
    .setFooter(`Página: ${page} | Por página: ${per_page}`)
    return message.channel.send(topembed)
}
  getLeaderboard (1, 5)
  
}
module.exports.command = {
    name: 'topmoons',
    aliases: ['leaderboard', 'top'],
    description: 'Veja os mais ricos do servidor!',
    category: "Economia",
    usage: 'topmoons',
    enabled: false
}

You see paginatedItems[i].ID is returning undefined for the first element of the array, from what I could gather.

I already saw that. But what’s the solution?

actually, it’s the leaderboard message.
you first define it, var leaderboardMessage, but it has no value, therefor it’s undefined. then in the loop, you add on to leaderboard message, but the undefined stays there.
basically, change var leaderboardMessage to var leaderboardMessage = \``

then for the IDs, use a replace function to replace the moons_”828282idlol” to “”

const db = require('quick.db');

exports.run = async (client, message, args) => {
  
  let user = message.author;
  let guild = message.guild;
  
  let embed = new Discord.RichEmbed().setColor([54, 57, 63]).setTimestamp();
  
  embed.setAuthor("LEADERBOARD | " + guild.name, guild.iconURL);
  
  let place = "SEM COLOCAÇÃO";
  
  await db.startsWith(`total_points_`, {
    sort:'.data'
  }).then(async resp => {
    
    resp.length = 10;
    let xp, level;
    
    let a = 1;
    for (var i in resp) {
      let id = resp[i].ID.split('_')[2];
      let total = await db.fetch(`total_points_${id}`);
      level = await db.fetch(`level_${id}`);
      if (level === null) level = 0;
      xp = await db.fetch(`xp_${id}`);
      if (xp === null) xp = 0;
      if (total === null) total = 0;
      let name;
      try {
        name = await client.users.get(id).username;
      } catch (e) {
        name = `${id}`;
      }
      embed.addField(`[${a}] ${name}`, `Level: ${level} [XP: ${xp}]`, false);
      a++;
    }
    
  });
  
  embed.setDescription(`:clipboard: Top 10`);
  
  embed.setFooter(`O teu lugar no pódio é #${place}`, user.avatarURL);
  
  //embed.setThumbnail(url);
  
  message.channel.send(embed);
  
}```

//Aqui é uma leaderboard de XP, mas voce pode fazer uma para coins somente trocando as referencias.

//Here is an XP Leaderboard, but you can modify to an coin leaderboad.

It worked in most of the code. Now it doesn’t show the undefined, but I need to get the username of the user. And sorry @NexxyZ1, but your code doesn’t solve. :confused:

Ok. So. I’ve found a way to get the username from the id. But now I’ve tested and the pages doesn’t work.

Code:

const db = require('quick.db');

exports.run = async (bot, message, args) => {
  async function getLeaderboard (page, per_page) {
  // Get all data not sorted
  const resp = await db.startsWith('moons_', {sort: '.data'});
  // Pagination
    
  var page = page || 1,
  per_page = per_page || 5,
  offset = (page - 1) * per_page,

  paginatedItems = resp.slice(offset).slice(0, per_page),
  total_pages = Math.ceil(resp.length / per_page);
    
    let id = resp.slice('moons_')
      console.log(id)
    
  
  // Leaderboard Message -> Make your message as you want.
  var leaderboardMessage = '';
  for (var i in paginatedItems) {
    let id = resp[i].ID.replace('moons_', '');
    let name;
      try {
        name = await bot.users.get(id).username;
      } catch (e) {
        name = `${id}`;
      }
    leaderboardMessage += `${name} | Moons: ${paginatedItems[i].data} \n`;//${paginatedItems[i].ID}
  }
  
  let end = {
    page: page,
    per_page: per_page,
    pre_page: page - 1 ? page - 1 : null,
    next_page: (total_pages > page) ? page + 1 : null,
    total: resp.length,
    total_pages: total_pages,
    data: paginatedItems,
    message: leaderboardMessage
  };

  // RESULT
    console.log(leaderboardMessage)
    const topembed = new Discord.RichEmbed()
    .setColor(16777215)
    .setAuthor('Top de Moons')
    .setDescription('Top de Moons do S1mple', `Página: ${page}`)
    .addField(leaderboardMessage, `Top 5`)
    .setFooter(`Página: ${page} | Por página: ${per_page}`)
    return message.channel.send(topembed)
}
  
  //if (args) {
    //const lepage = parseInt(args[0]);
    //getLeaderboard (lepage, 5)
  //} else {
  getLeaderboard (1, 5)
//}
  
}

what do you mean the pages don’t work

if I use getLeaderboard (2, 5) It will give me the moons of the 2nd page, but not the usernames.

there is yt tutorials on this subject and a quickdb server. I think they could help you too.