goat
command
balancec
Display your wallet balance with a professional card featuring your profile picture
0
views
0
likes
0
installs
Aliases: bal, wallet, mybalance, wcard
raw source
const { createCanvas, loadImage, registerFont } = require('canvas');
const fs = require('fs-extra');
const path = require('path');
const axios = require('axios');
const fontDir = path.join(__dirname, 'assets', 'font');
const cacheDir = path.join(__dirname, 'cache');
try {
if (fs.existsSync(path.join(fontDir, 'NotoSans-Bold.ttf'))) {
registerFont(path.join(fontDir, 'NotoSans-Bold.ttf'), { family: 'NotoSans', weight: 'bold' });
}
if (fs.existsSync(path.join(fontDir, 'NotoSans-SemiBold.ttf'))) {
registerFont(path.join(fontDir, 'NotoSans-SemiBold.ttf'), { family: 'NotoSans', weight: '600' });
}
if (fs.existsSync(path.join(fontDir, 'NotoSans-Regular.ttf'))) {
registerFont(path.join(fontDir, 'NotoSans-Regular.ttf'), { family: 'NotoSans', weight: 'normal' });
}
if (fs.existsSync(path.join(fontDir, 'BeVietnamPro-Bold.ttf'))) {
registerFont(path.join(fontDir, 'BeVietnamPro-Bold.ttf'), { family: 'BeVietnamPro', weight: 'bold' });
}
if (fs.existsSync(path.join(fontDir, 'BeVietnamPro-SemiBold.ttf'))) {
registerFont(path.join(fontDir, 'BeVietnamPro-SemiBold.ttf'), { family: 'BeVietnamPro', weight: '600' });
}
} catch (e) {
console.log("BalanceCard: Using fallback fonts");
}
const CURRENCY_SYMBOL = "$";
function formatMoney(amount) {
return amount.toLocaleString("en-US");
}
function drawRoundedRect(ctx, x, y, width, height, radius) {
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();
}
async function getProfilePicture(uid) {
try {
const avatarURL = `https://graph.facebook.com/${uid}/picture?width=512&height=512&access_token=6628568379%7Cc1e620fa708a1d5696fb991c1bde5662`;
const response = await axios.get(avatarURL, { responseType: 'arraybuffer', timeout: 10000 });
return await loadImage(Buffer.from(response.data));
} catch (error) {
console.error("Failed to fetch profile picture:", error.message);
return null;
}
}
function drawDefaultAvatar(ctx, x, y, size) {
const gradient = ctx.createRadialGradient(x + size/2, y + size/2, 0, x + size/2, y + size/2, size/2);
gradient.addColorStop(0, '#22c55e');
gradient.addColorStop(1, '#16a34a');
ctx.beginPath();
ctx.arc(x + size/2, y + size/2, size/2, 0, Math.PI * 2);
ctx.fillStyle = gradient;
ctx.fill();
ctx.fillStyle = '#ffffff';
ctx.beginPath();
ctx.arc(x + size/2, y + size/2 - 10, 25, 0, Math.PI * 2);
ctx.fill();
ctx.beginPath();
ctx.ellipse(x + size/2, y + size/2 + 45, 40, 30, 0, Math.PI, 0, true);
ctx.fill();
}
async function createBalanceCard(userData, userID, balance) {
const width = 950;
const height = 520;
const canvas = createCanvas(width, height);
const ctx = canvas.getContext('2d');
const gradient = ctx.createLinearGradient(0, 0, width, height);
gradient.addColorStop(0, '#0a0f0d');
gradient.addColorStop(0.3, '#0d1f17');
gradient.addColorStop(0.6, '#0f2a1d');
gradient.addColorStop(1, '#0a0f0d');
drawRoundedRect(ctx, 0, 0, width, height, 25);
ctx.fillStyle = gradient;
ctx.fill();
ctx.save();
for (let i = 0; i < 100; i++) {
const x = Math.random() * width;
const y = Math.random() * height;
const size = Math.random() * 1.5 + 0.3;
const opacity = Math.random() * 0.3 + 0.1;
ctx.fillStyle = `rgba(255, 255, 255, ${opacity})`;
ctx.beginPath();
ctx.arc(x, y, size, 0, Math.PI * 2);
ctx.fill();
}
ctx.restore();
ctx.save();
ctx.strokeStyle = 'rgba(34, 197, 94, 0.03)';
ctx.lineWidth = 1;
for (let i = -height; i < width; i += 50) {
ctx.beginPath();
ctx.moveTo(i, 0);
ctx.lineTo(i + height, height);
ctx.stroke();
}
ctx.restore();
ctx.strokeStyle = 'rgba(34, 197, 94, 0.2)';
ctx.lineWidth = 2;
drawRoundedRect(ctx, 12, 12, width - 24, height - 24, 20);
ctx.stroke();
ctx.strokeStyle = 'rgba(34, 197, 94, 0.08)';
ctx.lineWidth = 1;
drawRoundedRect(ctx, 20, 20, width - 40, height - 40, 16);
ctx.stroke();
const glowGradient = ctx.createLinearGradient(0, 0, 350, 0);
glowGradient.addColorStop(0, 'rgba(34, 197, 94, 0.15)');
glowGradient.addColorStop(1, 'rgba(34, 197, 94, 0)');
ctx.fillStyle = glowGradient;
ctx.fillRect(0, 0, 350, height);
ctx.save();
ctx.fillStyle = '#ffffff';
ctx.font = 'bold 30px "NotoSans", "BeVietnamPro", sans-serif';
ctx.shadowColor = 'rgba(34, 197, 94, 0.5)';
ctx.shadowBlur = 10;
ctx.fillText('WALLET BALANCE', 50, 70);
ctx.shadowBlur = 0;
ctx.restore();
ctx.font = '600 15px "NotoSans", "BeVietnamPro", sans-serif';
ctx.fillStyle = 'rgba(255, 255, 255, 0.5)';
ctx.fillText('Digital Payment Card', 50, 100);
ctx.font = '600 14px "NotoSans", "BeVietnamPro", sans-serif';
ctx.fillStyle = 'rgba(187, 247, 208, 0.7)';
ctx.fillText('AVAILABLE BALANCE', 50, 175);
ctx.save();
for (let i = 12; i > 0; i--) {
ctx.fillStyle = `rgba(34, 197, 94, ${0.015 * i})`;
ctx.font = `bold ${68 + i * 0.5}px "NotoSans", "BeVietnamPro", sans-serif`;
ctx.fillText(`${CURRENCY_SYMBOL}${formatMoney(balance)}`, 48 + (12 - i) * 0.2, 248 + (12 - i) * 0.2);
}
ctx.restore();
const balanceGradient = ctx.createLinearGradient(50, 200, 450, 250);
balanceGradient.addColorStop(0, '#4ade80');
balanceGradient.addColorStop(0.5, '#22c55e');
balanceGradient.addColorStop(1, '#16a34a');
ctx.fillStyle = balanceGradient;
ctx.font = 'bold 68px "NotoSans", "BeVietnamPro", sans-serif';
ctx.fillText(`${CURRENCY_SYMBOL}${formatMoney(balance)}`, 50, 250);
ctx.font = '600 14px "NotoSans", "BeVietnamPro", sans-serif';
ctx.fillStyle = 'rgba(187, 247, 208, 0.7)';
ctx.fillText('CARD HOLDER', 50, 320);
ctx.font = 'bold 26px "NotoSans", "BeVietnamPro", sans-serif';
ctx.fillStyle = '#ffffff';
const displayName = (userData.name || 'Unknown').toUpperCase().slice(0, 22);
ctx.fillText(displayName, 50, 355);
ctx.font = '600 14px "NotoSans", "BeVietnamPro", sans-serif';
ctx.fillStyle = 'rgba(187, 247, 208, 0.7)';
ctx.fillText('USER ID', 50, 410);
ctx.font = 'bold 18px "NotoSans", "BeVietnamPro", monospace';
ctx.fillStyle = '#bbf7d0';
ctx.fillText(userID, 50, 445);
const profilePic = await getProfilePicture(userID);
const picSize = 130;
const picX = width - picSize - 55;
const picY = 55;
ctx.save();
for (let i = 18; i > 0; i--) {
ctx.beginPath();
ctx.arc(picX + picSize / 2, picY + picSize / 2, picSize / 2 + i, 0, Math.PI * 2);
ctx.fillStyle = `rgba(34, 197, 94, ${0.02 * i})`;
ctx.fill();
}
ctx.restore();
if (profilePic) {
ctx.save();
ctx.beginPath();
ctx.arc(picX + picSize / 2, picY + picSize / 2, picSize / 2, 0, Math.PI * 2);
ctx.closePath();
ctx.clip();
ctx.drawImage(profilePic, picX, picY, picSize, picSize);
ctx.restore();
} else {
drawDefaultAvatar(ctx, picX, picY, picSize);
}
ctx.beginPath();
ctx.arc(picX + picSize / 2, picY + picSize / 2, picSize / 2, 0, Math.PI * 2);
ctx.strokeStyle = '#22c55e';
ctx.lineWidth = 4;
ctx.stroke();
ctx.beginPath();
ctx.arc(picX + picSize / 2, picY + picSize / 2, picSize / 2 + 8, 0, Math.PI * 2);
ctx.strokeStyle = 'rgba(34, 197, 94, 0.3)';
ctx.lineWidth = 2;
ctx.stroke();
const statusX = picX + picSize - 12;
const statusY = picY + picSize - 12;
ctx.beginPath();
ctx.arc(statusX, statusY, 14, 0, Math.PI * 2);
ctx.fillStyle = '#22c55e';
ctx.fill();
ctx.strokeStyle = '#0a0f0d';
ctx.lineWidth = 3;
ctx.stroke();
ctx.save();
ctx.fillStyle = 'rgba(34, 197, 94, 0.08)';
drawRoundedRect(ctx, width - 200, height - 125, 160, 85, 12);
ctx.fill();
ctx.strokeStyle = 'rgba(34, 197, 94, 0.2)';
ctx.lineWidth = 1;
ctx.stroke();
ctx.font = '600 11px "NotoSans", "BeVietnamPro", sans-serif';
ctx.fillStyle = 'rgba(187, 247, 208, 0.6)';
ctx.textAlign = 'center';
ctx.fillText('CARD STATUS', width - 120, height - 98);
ctx.font = 'bold 16px "NotoSans", "BeVietnamPro", sans-serif';
ctx.fillStyle = '#4ade80';
ctx.fillText('ACTIVE', width - 120, height - 68);
ctx.restore();
ctx.save();
ctx.fillStyle = 'rgba(34, 197, 94, 0.08)';
drawRoundedRect(ctx, width - 380, height - 125, 160, 85, 12);
ctx.fill();
ctx.strokeStyle = 'rgba(34, 197, 94, 0.2)';
ctx.lineWidth = 1;
ctx.stroke();
ctx.font = '600 11px "NotoSans", "BeVietnamPro", sans-serif';
ctx.fillStyle = 'rgba(187, 247, 208, 0.6)';
ctx.textAlign = 'center';
ctx.fillText('CARD TYPE', width - 300, height - 98);
ctx.font = 'bold 16px "NotoSans", "BeVietnamPro", sans-serif';
ctx.fillStyle = '#22c55e';
ctx.fillText('PREMIUM', width - 300, height - 68);
ctx.restore();
const chipX = width - 190;
const chipY = 210;
const chipGradient = ctx.createLinearGradient(chipX, chipY, chipX + 65, chipY + 50);
chipGradient.addColorStop(0, '#d4af37');
chipGradient.addColorStop(0.3, '#f5d76e');
chipGradient.addColorStop(0.7, '#d4af37');
chipGradient.addColorStop(1, '#a67c00');
drawRoundedRect(ctx, chipX, chipY, 65, 50, 6);
ctx.fillStyle = chipGradient;
ctx.fill();
ctx.strokeStyle = '#a67c00';
ctx.lineWidth = 1;
for (let i = 0; i < 4; i++) {
ctx.beginPath();
ctx.moveTo(chipX, chipY + 10 + i * 10);
ctx.lineTo(chipX + 65, chipY + 10 + i * 10);
ctx.stroke();
}
for (let i = 0; i < 2; i++) {
ctx.beginPath();
ctx.moveTo(chipX + 18 + i * 22, chipY);
ctx.lineTo(chipX + 18 + i * 22, chipY + 50);
ctx.stroke();
}
ctx.save();
const now = new Date();
const dateStr = now.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' });
ctx.font = '600 11px "NotoSans", "BeVietnamPro", sans-serif';
ctx.fillStyle = 'rgba(187, 247, 208, 0.4)';
ctx.textAlign = 'right';
ctx.fillText(`Generated: ${dateStr}`, width - 50, height - 18);
ctx.restore();
return canvas.toBuffer('image/png');
}
module.exports = {
config: {
name: "balancec",
aliases: ["bal", "wallet", "mybalance", "wcard"],
version: "2.0.0",
author: "Neoaz ゐ",
countDown: 10,
role: 0,
description: "Display your wallet balance with a professional card featuring your profile picture",
category: "economy",
guide: `{pn} - View your balance card
{pn} @tag - View tagged user's balance card`
},
onStart: async function({ message, event, usersData, args }) {
try {
message.reaction("⏳", event.messageID);
await fs.ensureDir(cacheDir);
let targetID = event.senderID;
if (event.messageReply) {
targetID = event.messageReply.senderID;
} else if (Object.keys(event.mentions).length > 0) {
targetID = Object.keys(event.mentions)[0];
} else if (args[0] && !isNaN(args[0])) {
targetID = args[0];
}
const userData = await usersData.get(targetID);
if (!userData) {
message.reaction("❌", event.messageID);
return message.reply("User not found in database!");
}
const balance = userData.money || 0;
const buffer = await createBalanceCard(userData, targetID, balance);
const imagePath = path.join(cacheDir, `balancecard_${targetID}_${Date.now()}.png`);
await fs.writeFile(imagePath, buffer);
const isOwn = targetID === event.senderID;
const msgBody = isOwn
? `${userData.name}\nBalance: ${CURRENCY_SYMBOL}${formatMoney(balance)}`
: `WALLET CARD\n━━━━━━━━━━━━━━━━━━\n${userData.name}\nBalance: ${CURRENCY_SYMBOL}${formatMoney(balance)}`;
await message.reply({
body: msgBody,
attachment: fs.createReadStream(imagePath)
});
message.reaction("✅", event.messageID);
setTimeout(async () => {
try {
if (await fs.pathExists(imagePath)) {
await fs.unlink(imagePath);
}
} catch (e) {}
}, 5000);
} catch (error) {
console.error("Balance Card Error:", error);
message.reaction("❌", event.messageID);
return message.reply("An error occurred while generating your balance card. Please try again.");
}
}
};