goat
command
toplevel
Premium animated level leaderboard
0
views
0
likes
0
installs
Aliases: toplvl, leveltop, toplevel
raw source
"use strict";
/**
* =====================================================
* TOPLEVEL.JS - PREMIUM LEVEL LEADERBOARD
* ARIYAN AI
*
* Same GIF setup as working spy.js
* NO mongoose
* NO utils
* NO extra database
* =====================================================
*/
if (typeof process.stderr.clearLine !== "function") {
process.stderr.clearLine = function () {};
}
if (typeof process.stderr.cursorTo !== "function") {
process.stderr.cursorTo = function () {};
}
const fs = require("fs-extra");
const path = require("path");
const Canvas = require("canvas");
const moment = require("moment");
const GIFEncoder = require("gifencoderv2");
// =====================================================
// NUMBER FORMAT
// =====================================================
const units = [
"",
"K",
"M",
"B",
"T",
"Q",
"S",
"O",
"N",
"D"
];
function shortenNumber(num) {
num = Number(num) || 0;
if (num < 1000) {
return Math.floor(num).toString();
}
let unitIndex = 0;
let n = num;
while (
n >= 1000 &&
unitIndex < units.length - 1
) {
n /= 1000;
unitIndex++;
}
return (
n.toFixed(2).replace(/\.?0+$/, "") +
units[unitIndex]
);
}
// =====================================================
// LEVEL SYSTEM
// =====================================================
function getLevelInfo(exp) {
let level = 1;
let expNeed = 100;
exp = Number(exp) || 0;
while (exp >= expNeed) {
exp -= expNeed;
level++;
expNeed =
level * 100 + 70;
}
return {
level,
curExp: exp,
expNeed
};
}
// =====================================================
// CANVAS HELPERS
// =====================================================
function roundedRect(
ctx,
x,
y,
width,
height,
radius
) {
radius = Math.min(
radius,
width / 2,
height / 2
);
ctx.beginPath();
ctx.moveTo(
x + radius,
y
);
ctx.lineTo(
x + width - radius,
y
);
ctx.quadraticCurveTo(
x + width,
y,
x + width,
y + radius
);
ctx.lineTo(
x + width,
y + height - radius
);
ctx.quadraticCurveTo(
x + width,
y + height,
x + width - radius,
y + height
);
ctx.lineTo(
x + radius,
y + height
);
ctx.quadraticCurveTo(
x,
y + height,
x,
y + height - radius
);
ctx.lineTo(
x,
y + radius
);
ctx.quadraticCurveTo(
x,
y,
x + radius,
y
);
ctx.closePath();
}
function text(
ctx,
value,
x,
y,
size,
color,
align = "left",
bold = false
) {
ctx.font =
`${bold ? "bold " : ""}${size}px Sans`;
ctx.fillStyle =
color;
ctx.textAlign =
align;
ctx.textBaseline =
"middle";
ctx.fillText(
String(value),
x,
y
);
}
// =====================================================
// PROGRESS BAR
// =====================================================
function drawProgressBar(
ctx,
x,
y,
width,
height,
percent
) {
percent = Math.max(
0,
Math.min(100, percent)
);
ctx.fillStyle =
"#24263d";
roundedRect(
ctx,
x,
y,
width,
height,
height / 2
);
ctx.fill();
const gradient =
ctx.createLinearGradient(
x,
y,
x + width,
y
);
gradient.addColorStop(
0,
"#00f5a0"
);
gradient.addColorStop(
0.5,
"#00d9ff"
);
gradient.addColorStop(
1,
"#c86cff"
);
const fillWidth =
width * (percent / 100);
if (fillWidth > 0) {
ctx.fillStyle =
gradient;
roundedRect(
ctx,
x,
y,
fillWidth,
height,
height / 2
);
ctx.fill();
}
}
// =====================================================
// DIMENSIONS
// =====================================================
const W = 1200;
const HEADER_Y = 30;
const HEADER_H = 190;
const ROW_Y = 240;
const ROW_H = 105;
const FOOTER_H = 75;
const H =
ROW_Y +
ROW_H * 10 +
FOOTER_H;
// =====================================================
// MODULE
// =====================================================
module.exports = {
config: {
name: "toplevel",
aliases: [
"toplvl",
"leveltop",
"toplevel"
],
version: "2.0.0",
author:
"ARIYAN AI",
countDown: 5,
role: 0,
shortDescription: {
en:
"Premium animated level leaderboard"
},
longDescription: {
en:
"Shows the top 10 users according to their experience and level."
},
category:
"system",
guide: {
en:
"{pn}"
}
},
// ===================================================
// START
// ===================================================
onStart: async function ({
api,
event,
usersData
}) {
let tmp = null;
try {
// ===============================================
// GET USERS
// ===============================================
const allUsers =
(await usersData.getAll()) ||
[];
if (
!Array.isArray(allUsers) ||
allUsers.length === 0
) {
return api.sendMessage(
"❌ কোনো user database-এ পাওয়া যায়নি!",
event.threadID,
event.messageID
);
}
// ===============================================
// SORT BY EXP
// ===============================================
const sortedUsers =
allUsers
.filter(
u =>
u &&
u.userID &&
u.exp !== undefined &&
u.exp !== null
)
.sort(
(a, b) =>
Number(b.exp || 0) -
Number(a.exp || 0)
);
const top10 =
sortedUsers.slice(0, 10);
// ===============================================
// PREPARE DATA
// ===============================================
const leaderboard = [];
for (
let i = 0;
i < top10.length;
i++
) {
const user =
top10[i];
const exp =
Number(user.exp) || 0;
const levelInfo =
getLevelInfo(exp);
let name =
"Unknown User";
try {
name =
await usersData.getName(
user.userID
) ||
"Unknown User";
} catch {}
leaderboard.push({
rank:
i + 1,
userID:
String(user.userID),
name:
String(name),
exp,
level:
levelInfo.level,
currentExp:
levelInfo.curExp,
expNeed:
levelInfo.expNeed
});
}
// ===============================================
// TEMP DIRECTORY
// ===============================================
const tempDir =
path.join(
__dirname,
"../temp"
);
await fs.ensureDir(
tempDir
);
tmp =
path.join(
tempDir,
`toplevel-${Date.now()}.gif`
);
// ===============================================
// GIF ENCODER
// Same setup as spy.js
// ===============================================
const enc =
new GIFEncoder(
W,
H
);
const gifStream =
fs.createWriteStream(
tmp
);
enc
.createReadStream()
.pipe(
gifStream
);
enc.start();
enc.setRepeat(0);
enc.setDelay(1000 / 8);
enc.setQuality(10);
// ===============================================
// FRAMES
// ===============================================
const FRAMES = 8;
for (
let frame = 0;
frame < FRAMES;
frame++
) {
const cv =
Canvas.createCanvas(
W,
H
);
const ctx =
cv.getContext(
"2d"
);
// =============================================
// BACKGROUND
// =============================================
const bg =
ctx.createLinearGradient(
0,
0,
W,
H
);
bg.addColorStop(
0,
"#090b1c"
);
bg.addColorStop(
0.35,
"#11142d"
);
bg.addColorStop(
0.7,
"#12112d"
);
bg.addColorStop(
1,
"#090918"
);
ctx.fillStyle =
bg;
ctx.fillRect(
0,
0,
W,
H
);
// =============================================
// PARTICLES
// =============================================
for (
let i = 0;
i < 110;
i++
) {
const x =
Math.random() * W;
const y =
Math.random() * H;
ctx.beginPath();
ctx.arc(
x,
y,
Math.random() * 1.8 + 0.3,
0,
Math.PI * 2
);
ctx.fillStyle =
`rgba(255,255,255,${
(
Math.random() *
0.65 +
0.15
).toFixed(2)
})`;
ctx.fill();
}
// =============================================
// ANIMATED BORDER
// =============================================
const phase =
frame / FRAMES;
const border =
ctx.createLinearGradient(
0,
0,
W,
H
);
border.addColorStop(
0,
`hsl(${
phase * 360
},100%,60%)`
);
border.addColorStop(
0.5,
`hsl(${
(phase + 0.5) * 360
},100%,60%)`
);
border.addColorStop(
1,
`hsl(${
(phase + 1) * 360
},100%,60%)`
);
ctx.strokeStyle =
border;
ctx.lineWidth =
10;
ctx.strokeRect(
5,
5,
W - 10,
H - 10
);
// =============================================
// HEADER CARD
// =============================================
ctx.fillStyle =
"rgba(20,22,45,0.96)";
roundedRect(
ctx,
35,
HEADER_Y,
W - 70,
HEADER_H,
28
);
ctx.fill();
// =============================================
// HEADER ICON
// =============================================
text(
ctx,
"🏆",
95,
90,
55,
"#ffffff",
"center"
);
// =============================================
// HEADER TITLE
// =============================================
text(
ctx,
"TOP 10",
150,
75,
43,
"#ffffff",
"left",
true
);
text(
ctx,
"LEVEL LEADERBOARD",
150,
125,
38,
"#c86cff",
"left",
true
);
// =============================================
// HEADER RIGHT
// =============================================
text(
ctx,
`TOTAL USERS: ${allUsers.length}`,
1120,
75,
21,
"#858aa5",
"right",
true
);
text(
ctx,
"RANKED BY EXPERIENCE",
1120,
115,
18,
"#00d9ff",
"right",
true
);
// =============================================
// LEADERBOARD
// =============================================
for (
let i = 0;
i < leaderboard.length;
i++
) {
const user =
leaderboard[i];
const y =
ROW_Y +
i * ROW_H;
// -------------------------------------------
// ROW COLOR
// -------------------------------------------
let rowColor =
"#171a32";
if (
user.rank === 1
) {
rowColor =
"rgba(255,212,59,0.17)";
} else if (
user.rank === 2
) {
rowColor =
"rgba(220,225,235,0.13)";
} else if (
user.rank === 3
) {
rowColor =
"rgba(205,127,50,0.15)";
}
ctx.fillStyle =
rowColor;
roundedRect(
ctx,
35,
y,
W - 70,
ROW_H - 10,
20
);
ctx.fill();
// -------------------------------------------
// RANK COLOR
// -------------------------------------------
let rankColor =
"#777d98";
if (
user.rank === 1
) {
rankColor =
"#ffd43b";
} else if (
user.rank === 2
) {
rankColor =
"#e2e8f0";
} else if (
user.rank === 3
) {
rankColor =
"#cd7f32";
}
// -------------------------------------------
// RANK
// -------------------------------------------
ctx.beginPath();
ctx.arc(
90,
y + 43,
31,
0,
Math.PI * 2
);
ctx.fillStyle =
"rgba(255,255,255,0.06)";
ctx.fill();
text(
ctx,
`#${user.rank}`,
90,
y + 43,
22,
rankColor,
"center",
true
);
// -------------------------------------------
// NAME
// -------------------------------------------
let displayName =
user.name;
if (
displayName.length > 24
) {
displayName =
displayName.slice(
0,
21
) + "...";
}
text(
ctx,
displayName,
145,
y + 34,
27,
"#ffffff",
"left",
true
);
text(
ctx,
`UID: ${user.userID}`,
145,
y + 68,
15,
"#707690",
"left"
);
// -------------------------------------------
// LEVEL
// -------------------------------------------
text(
ctx,
`LEVEL ${user.level}`,
650,
y + 35,
27,
"#c86cff",
"center",
true
);
text(
ctx,
"CURRENT LEVEL",
650,
y + 68,
14,
"#777d98",
"center",
true
);
// -------------------------------------------
// EXP
// -------------------------------------------
text(
ctx,
`${shortenNumber(user.exp)} XP`,
1080,
y + 34,
27,
"#00f5a0",
"right",
true
);
text(
ctx,
`${shortenNumber(
user.currentExp
)} / ${shortenNumber(
user.expNeed
)} NEXT`,
1080,
y + 68,
14,
"#777d98",
"right",
true
);
// -------------------------------------------
// EXP PROGRESS
// -------------------------------------------
const progress =
user.expNeed > 0
? (
user.currentExp /
user.expNeed
) * 100
: 0;
drawProgressBar(
ctx,
810,
y + 79,
230,
9,
progress
);
}
// =============================================
// EMPTY ROWS
// =============================================
if (
leaderboard.length < 10
) {
for (
let i =
leaderboard.length;
i < 10;
i++
) {
const y =
ROW_Y +
i * ROW_H;
ctx.fillStyle =
"rgba(255,255,255,0.025)";
roundedRect(
ctx,
35,
y,
W - 70,
ROW_H - 10,
20
);
ctx.fill();
text(
ctx,
`#${i + 1}`,
90,
y + 43,
22,
"#454a62",
"center",
true
);
text(
ctx,
"No user data",
145,
y + 43,
22,
"#454a62",
"left"
);
}
}
// =============================================
// FOOTER
// =============================================
const footerY =
ROW_Y +
ROW_H * 10;
ctx.fillStyle =
"rgba(5,6,17,0.95)";
ctx.fillRect(
0,
footerY,
W,
FOOTER_H
);
text(
ctx,
"⚡ ARIYAN AI • LEVEL SYSTEM",
40,
footerY + 35,
18,
"#777d98",
"left",
true
);
text(
ctx,
moment().format(
"DD/MM/YYYY • hh:mm A"
),
W - 40,
footerY + 35,
17,
"#777d98",
"right"
);
// =============================================
// ADD FRAME
// =============================================
enc.addFrame(ctx);
}
// ===============================================
// FINISH
// ===============================================
enc.finish();
await new Promise(
(
resolve,
reject
) => {
gifStream.on(
"finish",
resolve
);
gifStream.on(
"error",
reject
);
}
);
// ===============================================
// SEND
// ===============================================
await api.sendMessage(
{
body:
"🏆 TOP 10 LEVEL LEADERBOARD",
attachment:
fs.createReadStream(
tmp
)
},
event.threadID,
() => {
try {
if (
tmp &&
fs.existsSync(tmp)
) {
fs.unlinkSync(
tmp
);
}
} catch {}
}
);
} catch (error) {
console.error(
"[TOPLEVEL ERROR]",
error
);
try {
if (
tmp &&
fs.existsSync(tmp)
) {
fs.unlinkSync(
tmp
);
}
} catch {}
return api.sendMessage(
`⚠️ TopLevel Error:\n${error.message}`,
event.threadID,
event.messageID
);
}
}
};