const fs = require("fs-extra"); const moment = require("moment-timezone"); const path = require("path"); const { createCanvas } = require("canvas"); const os = require("os"); const CACHE_DIR = path.join(__dirname, "cache"); const VIDEO_PATH = path.join(CACHE_DIR, "prefix.mp4"); module.exports = { config: { name: "prefix", version: "7.0.0", author: "Efx Asif", countDown: 5, role: 0, description: "CPANEL style prefix panel with video", category: "config" }, langs: { en: { onlyAdmin: "╭━━━〔 ⛔ ACCESS DENIED 〕━━━╮\n" + "┃\n" + "┃ Only bot admin can change\n" + "┃ the global prefix.\n" + "┃\n" + "╰━━━━━━━━━━━━━━━━━━━━╯", confirmGlobal: "╭━━━〔 🌈 GLOBAL PREFIX 〕━━━╮\n" + "┃\n" + "┃ 🆕 New Prefix : 『 %1 』\n" + "┃ 👑 Owner : Efx Asif\n" + "┃\n" + "┃ 👉 React to confirm.\n" + "┃\n" + "╰━━━━━━━━━━━━━━━━━━━━╯", confirmThisThread: "╭━━━〔 🌈 GROUP PREFIX 〕━━━╮\n" + "┃\n" + "┃ 🆕 New Prefix : 『 %1 』\n" + "┃ 👑 Owner : Efx Asif\n" + "┃\n" + "┃ 👉 React to confirm.\n" + "┃\n" + "╰━━━━━━━━━━━━━━━━━━━━╯", successGlobal: "╭━━━〔 ✅ GLOBAL UPDATED 〕━━━╮\n" + "┃\n" + "┃ 🌈 New Prefix : 『 %1 』\n" + "┃ 👑 Owner : Efx Asif\n" + "┃\n" + "╰━━━━━━━━━━━━━━━━━━━━╯", successThisThread: "╭━━━〔 ✅ GROUP UPDATED 〕━━━╮\n" + "┃\n" + "┃ 🌈 New Prefix : 『 %1 』\n" + "┃ 👑 Owner : Efx Asif\n" + "┃\n" + "╰━━━━━━━━━━━━━━━━━━━━╯", reset: "╭━━━〔 🔄 PREFIX RESET 〕━━━╮\n" + "┃\n" + "┃ ✅ Group prefix reset successful!\n" + "┃ 🔰 System Prefix : 『 %1 』\n" + "┃ 👑 Owner : Efx Asif\n" + "┃\n" + "╰━━━━━━━━━━━━━━━━━━━━╯" } }, // ========================================== // HOSTING // ========================================== getHostingName() { const env = process.env; if (env.RENDER) return "Render"; if (env.RAILWAY_ENVIRONMENT) return "Railway"; if (env.VERCEL) return "Vercel"; if (env.REPL_ID) return "Replit"; if (env.FLY_APP_NAME) return "Fly.io"; if (env.AWS_EXECUTION_ENV) return "AWS"; if (env.DYNO) return "Heroku"; if (env.GITHUB_ACTIONS) return "GitHub"; return "VPS / Local"; }, // ========================================== // UPTIME // ========================================== formatUptime(ms) { let sec = Math.floor(ms / 1000); if (sec <= 0) return "0s"; const days = Math.floor(sec / 86400); sec %= 86400; const hours = Math.floor(sec / 3600); sec %= 3600; const minutes = Math.floor(sec / 60); const seconds = sec % 60; const parts = []; if (days) parts.push(`${days}d`); if (hours) parts.push(`${hours}h`); if (minutes) parts.push(`${minutes}m`); if (!parts.length || seconds) parts.push(`${seconds}s`); return parts.join(" "); }, // ========================================== // FONT FIT // ========================================== fitFont(ctx, text, maxWidth, size) { let fontSize = size; while (fontSize > 8) { ctx.font = `bold ${fontSize}px Arial`; if ( ctx.measureText(String(text)).width <= maxWidth ) { break; } fontSize--; } return `bold ${fontSize}px Arial`; }, // ========================================== // CIRCLE // ========================================== drawCircle(ctx, x, y, radius, color) { ctx.save(); ctx.shadowColor = color; ctx.shadowBlur = 28; ctx.beginPath(); ctx.arc( x, y, radius, 0, Math.PI * 2 ); ctx.fillStyle = "#08101c"; ctx.fill(); ctx.shadowBlur = 0; ctx.strokeStyle = color; ctx.lineWidth = 3; ctx.stroke(); ctx.beginPath(); ctx.arc( x, y, radius - 9, 0, Math.PI * 2 ); ctx.strokeStyle = "rgba(255,255,255,0.10)"; ctx.lineWidth = 1; ctx.stroke(); ctx.restore(); }, // ========================================== // CONNECTION // ========================================== drawConnection( ctx, x1, y1, x2, y2, color ) { ctx.save(); ctx.beginPath(); ctx.moveTo(x1, y1); ctx.lineTo(x2, y2); ctx.strokeStyle = color; ctx.globalAlpha = 0.18; ctx.lineWidth = 1.5; ctx.stroke(); ctx.restore(); }, // ========================================== // GRID // ========================================== drawGrid(ctx, width, height, color) { ctx.save(); ctx.globalAlpha = 0.055; ctx.strokeStyle = color; ctx.lineWidth = 1; const gap = 45; for (let x = 0; x < width; x += gap) { ctx.beginPath(); ctx.moveTo(x, 0); ctx.lineTo(x, height); ctx.stroke(); } for (let y = 0; y < height; y += gap) { ctx.beginPath(); ctx.moveTo(0, y); ctx.lineTo(width, y); ctx.stroke(); } ctx.restore(); }, // ========================================== // CENTER TEXT // ========================================== drawCenterText(ctx, x, y, color) { ctx.save(); ctx.textAlign = "center"; ctx.textBaseline = "middle"; ctx.font = "bold 27px Arial"; ctx.shadowColor = color; ctx.shadowBlur = 18; ctx.fillStyle = color; ctx.fillText( "PREFIX", x, y ); ctx.restore(); }, // ========================================== // STAT TEXT // ========================================== drawStatText(ctx, circle, color) { ctx.save(); ctx.textAlign = "center"; ctx.textBaseline = "middle"; ctx.font = circle.valueFont; ctx.shadowColor = color; ctx.shadowBlur = 12; ctx.fillStyle = color; ctx.fillText( circle.value, circle.x, circle.y - 7 ); ctx.shadowBlur = 0; ctx.font = circle.labelFont; ctx.fillStyle = "#c5ccd6"; ctx.fillText( circle.label, circle.x, circle.y + 13 ); ctx.restore(); }, // ========================================== // CREATE CPANEL IMAGE // ========================================== createCPanelImage: async function ({ groupName, systemPrefix, groupPrefix, owner }) { await fs.ensureDir(CACHE_DIR); const width = 900; const height = 600; const canvas = createCanvas(width, height); const ctx = canvas.getContext("2d"); const centerX = width / 2; const centerY = height / 2 + 15; const time = moment() .tz("Asia/Dhaka") .format("hh:mm A"); const date = moment() .tz("Asia/Dhaka") .format("DD MMM YYYY"); const stats = [ { label: "SYSTEM", value: systemPrefix }, { label: "GROUP", value: groupPrefix }, { label: "NODE", value: process.version.replace("v", "") }, { label: "HOST", value: this.getHostingName() }, { label: "UPTIME", value: this.formatUptime( process.uptime() * 1000 ) }, { label: "TIME", value: time }, { label: "DATE", value: date }, { label: "OWNER", value: owner }, { label: "GROUP", value: groupName }, { label: "STATUS", value: "ONLINE" } ]; const orbitRadius = 210; const circleRadius = 67; const statCircles = stats.map((stat, index) => { const angle = (Math.PI * 2 / stats.length) * index - Math.PI / 2; const x = centerX + orbitRadius * Math.cos(angle); const y = centerY + orbitRadius * Math.sin(angle); return { x, y, label: stat.label, value: String(stat.value), valueFont: this.fitFont( ctx, stat.value, 100, 15 ), labelFont: this.fitFont( ctx, stat.label, 100, 10 ) }; }); // ======================================== // STATIC FRAME // ======================================== const hue = Math.floor( (Date.now() / 25) % 360 ); const glowColor = `hsl(${hue}, 100%, 65%)`; // Background const bg = ctx.createLinearGradient( 0, 0, width, height ); bg.addColorStop(0, "#030611"); bg.addColorStop(0.5, "#0a1020"); bg.addColorStop(1, "#020409"); ctx.fillStyle = bg; ctx.fillRect( 0, 0, width, height ); // Grid this.drawGrid( ctx, width, height, glowColor ); // Header ctx.textAlign = "left"; ctx.font = "bold 27px Arial"; ctx.shadowColor = glowColor; ctx.shadowBlur = 14; ctx.fillStyle = "#ffffff"; ctx.fillText( "🤖 ASIF EVO BOT V3", 30, 38 ); ctx.shadowBlur = 0; ctx.font = "bold 12px Arial"; ctx.fillStyle = glowColor; ctx.fillText( "PREFIX SYSTEM PANEL", 32, 59 ); // Time ctx.textAlign = "right"; ctx.font = "bold 15px Arial"; ctx.fillStyle = "#ffffff"; ctx.fillText( time, width - 30, 38 ); ctx.font = "bold 10px Arial"; ctx.fillStyle = "#8994a6"; ctx.fillText( "BANGLADESH TIME", width - 30, 58 ); // Connections statCircles.forEach(circle => { this.drawConnection( ctx, centerX, centerY, circle.x, circle.y, glowColor ); }); // Center this.drawCircle( ctx, centerX, centerY, 105, glowColor ); this.drawCenterText( ctx, centerX, centerY, glowColor ); // Stats statCircles.forEach(circle => { this.drawCircle( ctx, circle.x, circle.y, circleRadius, glowColor ); this.drawStatText( ctx, circle, glowColor ); }); // Footer ctx.textAlign = "center"; ctx.font = "bold 12px Arial"; ctx.fillStyle = "#707b8c"; ctx.fillText( "ASIF EVO • PREFIX SYSTEM • 🟢 ONLINE", centerX, height - 20 ); const imagePath = path.join( CACHE_DIR, `prefix_cpanel_${Date.now()}.png` ); await fs.writeFile( imagePath, canvas.toBuffer("image/png") ); return imagePath; }, // ========================================== // SEND PREFIX PANEL + VIDEO // ========================================== sendPrefixPanel: async function ({ message, event, threadsData }) { const systemPrefix = global.GoatBot.config.prefix; const groupPrefix = global.utils.getPrefix( event.threadID ); const threadInfo = await threadsData.get( event.threadID ); const groupName = threadInfo?.threadName || "Unknown Group"; const owner = global.GoatBot.config.adminName || "Efx Asif"; const image = await this.createCPanelImage({ groupName, systemPrefix, groupPrefix, owner }); // Image + video if (await fs.pathExists(VIDEO_PATH)) { return message.reply({ body: `╭━━━〔 🌈 PREFIX PANEL 〕━━━╮\n` + `┃ 🔰 System : 『 ${systemPrefix} 』\n` + `┃ 👥 Group : 『 ${groupPrefix} 』\n` + `┃ 🟢 Status : ONLINE\n` + `┃\n` + `╰━━━━━━━━━━━━━━━━━━━━╯`, attachment: [ fs.createReadStream(image), fs.createReadStream(VIDEO_PATH) ] }); } // Video missing → image only return message.reply({ body: `🌈 PREFIX SYSTEM\n` + `🔰 System Prefix : 『 ${systemPrefix} 』\n` + `👥 Group Prefix : 『 ${groupPrefix} 』\n` + `🟢 Status : ONLINE`, attachment: fs.createReadStream(image) }); }, // ========================================== // COMMAND // ========================================== onStart: async function ({ message, role, args, commandName, event, threadsData, getLang }) { // Show panel if (!args[0]) { return this.sendPrefixPanel({ message, event, threadsData }); } // Reset if ( args[0].toLowerCase() === "reset" ) { await threadsData.set( event.threadID, null, "data.prefix" ); return message.reply( getLang( "reset", global.GoatBot.config.prefix ) ); } const newPrefix = args[0]; const setGlobal = args[1] === "-g"; // Global admin check if ( setGlobal && role < 2 ) { return message.reply( getLang("onlyAdmin") ); } const confirmMsg = setGlobal ? getLang( "confirmGlobal", newPrefix ) : getLang( "confirmThisThread", newPrefix ); message.reply( confirmMsg, (err, info) => { if (err) return; global.GoatBot.onReaction.set( info.messageID, { commandName, author: event.senderID, newPrefix, setGlobal } ); } ); }, // ========================================== // REACTION // ========================================== onReaction: async function ({ event, message, threadsData, Reaction, getLang }) { if ( event.userID !== Reaction.author ) { return; } global.GoatBot.onReaction.delete( event.messageID ); if (Reaction.setGlobal) { global.GoatBot.config.prefix = Reaction.newPrefix; fs.writeFileSync( global.client.dirConfig, JSON.stringify( global.GoatBot.config, null, 2 ) ); return message.reply( getLang( "successGlobal", Reaction.newPrefix ) ); } await threadsData.set( event.threadID, Reaction.newPrefix, "data.prefix" ); return message.reply( getLang( "successThisThread", Reaction.newPrefix ) ); }, // ========================================== // AUTO PREFIX CHAT // ========================================== onChat: async function ({ event, message, threadsData }) { if ( !event.body || event.body.toLowerCase() !== "prefix" ) { return; } return this.sendPrefixPanel({ message, event, threadsData }); } };