Skip to content

Commit

Permalink
Created a lb image
Browse files Browse the repository at this point in the history
  • Loading branch information
Prince527GitHub committed Dec 26, 2023
1 parent 3f4ea0b commit 77b8f8a
Show file tree
Hide file tree
Showing 3 changed files with 133 additions and 13 deletions.
10 changes: 7 additions & 3 deletions assets/api/canvas/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,11 @@ function shortenText(text, len) {
}

function abbreviateNumber(num) {
return num >= 1000 ? Math.floor(num / 1000) + (num % 1000 >= 500 ? 1 : 0) + "k" : num.toString();
if (num >= 1e12) return Math.floor(num / 1e9) + 'B';
else if (num >= 1e9) return Math.floor(num / 1e9) + (num % 1e9 >= 500e6 ? 1 : 0) + 'B';
else if (num >= 1e6) return Math.floor(num / 1e6) + (num % 1e6 >= 500e3 ? 1 : 0) + 'M';
else if (num >= 1000) return Math.floor(num / 1000) + (num % 1000 >= 500 ? 1 : 0) + 'K';
else return num.toString();
}

async function drawRoundedImage(image, cornerRadius, rounded) {
Expand Down Expand Up @@ -84,9 +88,9 @@ function roundContext(ctx, x, y, width, height, radius) {
ctx.closePath();
}

async function combineImages(imageBuffers, { width = 1000, columns = 4, padding = 10 }) {
async function combineImages(imageBuffers, { width = 1000, height = null, columns = 4, padding = 10 }) {
const imageWidth = (width - (padding * (columns + 1))) / columns;
const imageHeight = imageWidth;
const imageHeight = height || imageWidth;

const canvasRows = Math.ceil(imageBuffers.length / columns);
const canvasHeight = (imageHeight * canvasRows) + (padding * (canvasRows + 1));
Expand Down
94 changes: 94 additions & 0 deletions assets/api/canvas/leaderboard.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
const { createCanvas, loadImage } = require('@napi-rs/canvas');

const { drawRoundedImage, combineImages, shortenText, abbreviateNumber } = require("./index");
const { isTooBlackOrWhite } = require("../color");

async function generateLeaderboard(users) {
const buffers = [];

for (let i = 0; i < users.length; i++) {
const userCanvas = createCanvas(680, 70);
const userCtx = userCanvas.getContext('2d');

const user = users[i];

userCtx.fillStyle = '#393f4475';
userCtx.fillRect(0, 0, 680, 70);

const avatar = await loadImage(user.avatar);
userCtx.drawImage(avatar, 0, 0, 70, 70);

userCtx.fillStyle = 'white';
userCtx.font = '24px Noto Sans';
userCtx.fillText(`${shortenText(user.username, 11)}`, 80, 45);

userCtx.textAlign = 'right';
userCtx.font = '24px Noto Sans Bold';
i === 0 ? userCtx.fillStyle = '#e7ba16' : i === 1 ? userCtx.fillStyle = '#9e9e9e' : i === 2? userCtx.fillStyle = '#94610f' : null;
userCtx.fillText(`#${user.position}`, 670, 45);

const barX = 275;
const barY = 10;
const borderRadius = 18.75;
const barWidth = 300;
const barHeight = 50;

const progressWidth = (user.currentXP / user.requiredXP) * barWidth;

userCtx.fillStyle = "#484b4E";
userCtx.beginPath();
userCtx.moveTo(barX + borderRadius, barY);
userCtx.lineTo(barX + barWidth - borderRadius, barY);
userCtx.arc(barX + barWidth - borderRadius, barY + borderRadius, borderRadius, -Math.PI / 2, 0);
userCtx.lineTo(barX + barWidth, barY + barHeight - borderRadius);
userCtx.arc(barX + barWidth - borderRadius, barY + barHeight - borderRadius, borderRadius, 0, Math.PI / 2);
userCtx.lineTo(barX + borderRadius, barY + barHeight);
userCtx.arc(barX + borderRadius, barY + barHeight - borderRadius, borderRadius, Math.PI / 2, Math.PI);
userCtx.lineTo(barX, barY + borderRadius);
userCtx.arc(barX + borderRadius, barY + borderRadius, borderRadius, Math.PI, -Math.PI / 2);
userCtx.closePath();
userCtx.fill();

userCtx.fillStyle = user.progressBar || "#ffffff";
userCtx.beginPath();
userCtx.moveTo(barX + borderRadius, barY);
userCtx.lineTo(barX + progressWidth - borderRadius, barY);
userCtx.arc(barX + progressWidth - borderRadius, barY + borderRadius, borderRadius, -Math.PI / 2, 0);
userCtx.lineTo(barX + progressWidth, barY + barHeight - borderRadius);
userCtx.arc(barX + progressWidth - borderRadius, barY + barHeight - borderRadius, borderRadius, 0, Math.PI / 2);
userCtx.lineTo(barX + borderRadius, barY + barHeight);
userCtx.arc(barX + borderRadius, barY + barHeight - borderRadius, borderRadius, Math.PI / 2, Math.PI);
userCtx.lineTo(barX, barY + borderRadius);
userCtx.arc(barX + borderRadius, barY + borderRadius, borderRadius, Math.PI, -Math.PI / 2);
userCtx.closePath();
userCtx.fill();

const textColor = isTooBlackOrWhite(user.progressBar, "#ffffff").boolean === true ? "#000000" : "#ffffff";

userCtx.textAlign = 'left';
userCtx.font = '24px Noto Sans';
userCtx.fillStyle = textColor;
userCtx.fillText(`${abbreviateNumber(user.currentXP)}/${abbreviateNumber(user.requiredXP)} (${user.level})`, 280, 44);

const result = await drawRoundedImage(await loadImage(userCanvas.toBuffer("image/png")), 10, true)

buffers.push(result.toBuffer("image/png"));
}

const buffer = await combineImages(buffers, { width: 680, height: 70, columns: 1, padding: 5 });

const canvas = createCanvas(680, buffers.length * 75);
const ctx = canvas.getContext('2d');

ctx.fillStyle = '#23272a';
ctx.fillRect(0, 0, canvas.width, canvas.height);

const lb = await loadImage(buffer);
ctx.drawImage(lb, 0, 0, 680, buffers.length * 75);

return canvas.toBuffer("image/png");
}

module.exports = {
generateLeaderboard
}
42 changes: 32 additions & 10 deletions commands/xp/leaderboard.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
const { fetchLeaderboard, computeLeaderboard } = require("../../assets/api/xp");
const { Message, Client, EmbedBuilder } = require("discord.js");
const { fetchLeaderboard, computeLeaderboard, xpFor } = require("../../assets/api/xp");
const { generateLeaderboard } = require("../../assets/api/canvas/leaderboard");
const { Message, Client, AttachmentBuilder } = require("discord.js");
const guildRankcard = require("../../models/server/guild-rankcard");
const userRankcard = require("../../models/user/user-rankcard");
const xpSchema = require("../../models/server/xp");

module.exports = {
Expand All @@ -19,15 +22,34 @@ module.exports = {
let lb = await fetchLeaderboard(message.guild.id);
if (lb.length < 1) return reply("Nobody's in leaderboard yet.");

const guildCardData = await guildRankcard.findOne({ Guild: message.guild.id });

lb = await computeLeaderboard(client, lb, true);
lb = lb.map(e => `**${e.position}**. *${e.username}*\nLevel: \`${e.level}\`\nXP: \`${e.xp.toLocaleString()}\``);

message.channel.send({
embeds: [
new EmbedBuilder()
.setTitle("**Leaderboard**:")
.setDescription(`${lb.join("\n\n")}`)
.setColor("Random")
lb = await Promise.all(lb.map(async(user) => {
const userCardData = await userRankcard.findOne({ User: user.userID });

const progressColor = userCardData?.ProgressBar ? userCardData.ProgressBar : guildCardData?.ProgressBar ? guildCardData.ProgressBar : "#ffffff";
const avatar = client.users.cache.get(user.userID).displayAvatarURL({ extension: 'png' });
const requiredXP = xpFor(user.level + 1);

return {
guild: user.guildID,
user: user.userID,
currentXP: user.xp,
requiredXP: requiredXP,
level: user.level,
position: user.position,
username: user.username,
avatar: avatar,
progressBar: progressColor || "#ffffff"
}
}));

lb = await generateLeaderboard(lb);

return message.channel.send({
files: [
new AttachmentBuilder(lb, { name: "lb.png" })
]
});
}
Expand Down

0 comments on commit 77b8f8a

Please sign in to comment.