← back to browse
goat command

prefix

CPANEL style prefix panel with video

0
views
0
likes
0
installs
raw source
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
    });
  }
};

View raw file