goat
command
fbcover
Facebook cover generate
0
views
0
likes
0
installs
Aliases: cover
raw source
const axios = require("axios");
const { createCanvas, loadImage, registerFont } = require("canvas");
const fs = require("fs-extra");
const path = require("path");
const FB_TOKEN = "6628568379%7Cc1e620fa708a1d5696fb991c1bde5662";
const FONTS_DIR = path.join(__dirname, "cache", "fonts");
const FONT_LIST = [
{ file: "PlayfairDisplay-Bold.ttf", url: "https://github.com/google/fonts/raw/main/ofl/playfairdisplay/static/PlayfairDisplay-Bold.ttf", family: "Playfair", weight: "bold" },
{ file: "Outfit-Regular.ttf", url: "https://github.com/google/fonts/raw/main/ofl/outfit/static/Outfit-Regular.ttf", family: "Outfit", weight: "normal" },
{ file: "Outfit-Bold.ttf", url: "https://github.com/google/fonts/raw/main/ofl/outfit/static/Outfit-Bold.ttf", family: "Outfit", weight: "bold" },
{ file: "SpaceMono-Bold.ttf", url: "https://github.com/google/fonts/raw/main/ofl/spacemono/SpaceMono-Bold.ttf", family: "SpaceMono", weight: "bold" },
{ file: "Rajdhani-Bold.ttf", url: "https://github.com/google/fonts/raw/main/ofl/rajdhani/Rajdhani-Bold.ttf", family: "Rajdhani", weight: "bold" },
];
// ── color themes ──────────────────────────────────────────
const THEMES = {
white: { a: "#ffffff", b: "#cccccc", bg1: "#1a1a1a", bg2: "#2a2a2a" },
red: { a: "#ff3b3b", b: "#ff7c7c", bg1: "#1a0000", bg2: "#2d0a0a" },
blue: { a: "#3b8bff", b: "#7cb9ff", bg1: "#000e1a", bg2: "#0a1a2d" },
green: { a: "#2ecc71", b: "#82e0aa", bg1: "#001a0a", bg2: "#0a2d18" },
black: { a: "#aaaaaa", b: "#666666", bg1: "#000000", bg2: "#111111" },
orange: { a: "#ff8c00", b: "#ffb347", bg1: "#1a0a00", bg2: "#2d1a00" },
purple: { a: "#9b59b6", b: "#c39bd3", bg1: "#0d001a", bg2: "#1a0a2d" },
pink: { a: "#ff69b4", b: "#ffb6c1", bg1: "#1a0010", bg2: "#2d0020" },
yellow: { a: "#f1c40f", b: "#f7dc6f", bg1: "#1a1500", bg2: "#2d2500" },
cyan: { a: "#00bcd4", b: "#80deea", bg1: "#001a1a", bg2: "#002d2d" },
};
function getTheme(colour) {
const key = (colour || "white").toLowerCase().trim();
return THEMES[key] || THEMES.white;
}
let fontsReady = false;
async function setupFonts() {
if (fontsReady) return;
await fs.ensureDir(FONTS_DIR);
for (const f of FONT_LIST) {
const dest = path.join(FONTS_DIR, f.file);
if (!fs.existsSync(dest)) {
try {
const res = await axios.get(f.url, { responseType: "arraybuffer", timeout: 15000 });
await fs.writeFile(dest, Buffer.from(res.data));
} catch (e) {
console.error(`[fbcover] font fail: ${f.file}`, e.message);
}
}
if (fs.existsSync(dest)) registerFont(dest, { family: f.family, weight: f.weight });
}
fontsReady = true;
}
async function fetchAvatar(uid) {
try {
const url = `https://graph.facebook.com/${uid}/picture?height=300&width=300&access_token=${FB_TOKEN}`;
const res = await axios.get(url, { responseType: "arraybuffer", timeout: 8000 });
return await loadImage(Buffer.from(res.data));
} catch {
const c = createCanvas(300, 300);
const x = c.getContext("2d");
x.fillStyle = "#444"; x.fillRect(0, 0, 300, 300);
x.fillStyle = "#fff"; x.font = "bold 120px sans-serif";
x.textAlign = "center"; x.textBaseline = "middle";
x.fillText("?", 150, 150);
return await loadImage(c.toBuffer());
}
}
function drawAvatar(ctx, img, cx, cy, r) {
ctx.save();
ctx.beginPath(); ctx.arc(cx, cy, r, 0, Math.PI * 2); ctx.clip();
ctx.drawImage(img, cx - r, cy - r, r * 2, r * 2);
ctx.restore();
}
function rrect(ctx, x, y, w, h, r) {
ctx.beginPath();
ctx.moveTo(x+r,y); ctx.lineTo(x+w-r,y); ctx.quadraticCurveTo(x+w,y,x+w,y+r);
ctx.lineTo(x+w,y+h-r); ctx.quadraticCurveTo(x+w,y+h,x+w-r,y+h);
ctx.lineTo(x+r,y+h); ctx.quadraticCurveTo(x,y+h,x,y+h-r);
ctx.lineTo(x,y+r); ctx.quadraticCurveTo(x,y,x+r,y);
ctx.closePath();
}
function fitText(ctx, text, font, maxW) {
ctx.font = font;
if (ctx.measureText(text).width <= maxW) return text;
while (text.length > 1 && ctx.measureText(text + "…").width > maxW) text = text.slice(0, -1);
return text + "…";
}
// ── V1: Minimal Dark ──────────────────────────────────────
function drawV1(av, d, colour) {
const W = 820, H = 312;
const cv = createCanvas(W, H);
const ctx = cv.getContext("2d");
const t = getTheme(colour);
ctx.fillStyle = "#0a0a0a"; ctx.fillRect(0, 0, W, H);
// grid
ctx.strokeStyle = "rgba(255,255,255,0.022)"; ctx.lineWidth = 1;
for (let x = 0; x < W; x += 40) { ctx.beginPath(); ctx.moveTo(x,0); ctx.lineTo(x,H); ctx.stroke(); }
for (let y = 0; y < H; y += 40) { ctx.beginPath(); ctx.moveTo(0,y); ctx.lineTo(W,y); ctx.stroke(); }
// right glow — accent color
const glow = ctx.createRadialGradient(730,60,0,730,60,300);
glow.addColorStop(0, t.a + "18"); glow.addColorStop(1, "transparent");
ctx.fillStyle = glow; ctx.fillRect(0,0,W,H);
// left bar — accent color
const bar = ctx.createLinearGradient(0,0,0,H);
bar.addColorStop(0, t.a); bar.addColorStop(1, t.b);
ctx.fillStyle = bar; ctx.fillRect(0,0,4,H);
// avatar glow
const ag = ctx.createRadialGradient(158,156,0,158,156,88);
ag.addColorStop(0, t.a + "33"); ag.addColorStop(1, "transparent");
ctx.fillStyle = ag; ctx.fillRect(70,68,176,176);
// avatar ring
ctx.save(); ctx.strokeStyle = t.a + "66"; ctx.lineWidth = 2;
ctx.beginPath(); ctx.arc(158,156,66,0,Math.PI*2); ctx.stroke(); ctx.restore();
drawAvatar(ctx, av, 158, 156, 62);
const RX = 268;
// badge
ctx.fillStyle = t.a + "22"; rrect(ctx,RX,50,34,19,3); ctx.fill();
ctx.strokeStyle = t.a + "55"; ctx.lineWidth=1; rrect(ctx,RX,50,34,19,3); ctx.stroke();
ctx.fillStyle = t.a; ctx.font="bold 10px 'SpaceMono'";
ctx.textAlign="left"; ctx.textBaseline="middle"; ctx.fillText("V1",RX+8,59);
// name
ctx.fillStyle="#ffffff"; ctx.font="bold 40px 'Playfair'"; ctx.textBaseline="alphabetic";
ctx.fillText(fitText(ctx,d.name,"bold 40px 'Playfair'",516),RX,112);
// title — accent color
ctx.fillStyle = t.a; ctx.font="bold 13px 'Rajdhani'";
ctx.fillText(fitText(ctx,d.subname.toUpperCase(),"bold 13px 'Rajdhani'",516),RX,138);
// divider
ctx.strokeStyle="#252525"; ctx.lineWidth=1;
ctx.beginPath(); ctx.moveTo(RX,154); ctx.lineTo(RX+90,154); ctx.stroke();
// detail rows
[d.address, d.email, d.number].forEach((txt, i) => {
const y = 180 + i * 26;
ctx.fillStyle = t.a;
ctx.beginPath(); ctx.arc(RX+4,y-5,3,0,Math.PI*2); ctx.fill();
ctx.fillStyle="#888"; ctx.font="normal 13px 'Outfit'"; ctx.textBaseline="alphabetic";
ctx.fillText(fitText(ctx,txt,"normal 13px 'Outfit'",498),RX+16,y);
});
return cv;
}
// ── V2: Glass Card ────────────────────────────────────────
function drawV2(av, d, colour) {
const W = 820, H = 312;
const cv = createCanvas(W, H);
const ctx = cv.getContext("2d");
const t = getTheme(colour);
// bg — use theme bg colors
const bg = ctx.createLinearGradient(0,0,W,H);
bg.addColorStop(0, t.bg1); bg.addColorStop(0.45, t.bg2); bg.addColorStop(1, "#0a0a0a");
ctx.fillStyle=bg; ctx.fillRect(0,0,W,H);
// blobs — accent color
[[640,-50,260,t.a+"29"],[420,330,180,t.b+"1c"],[310,50,150,t.a+"22"]].forEach(([cx,cy,r,col])=>{
const g=ctx.createRadialGradient(cx,cy,0,cx,cy,r);
g.addColorStop(0,col); g.addColorStop(1,"transparent");
ctx.fillStyle=g; ctx.fillRect(cx-r,cy-r,r*2,r*2);
});
// left panel
ctx.fillStyle="rgba(255,255,255,0.045)"; ctx.fillRect(0,0,260,H);
ctx.strokeStyle="rgba(255,255,255,0.07)"; ctx.lineWidth=1;
ctx.beginPath(); ctx.moveTo(260,0); ctx.lineTo(260,H); ctx.stroke();
// avatar ring — accent color gradient
const gr=ctx.createLinearGradient(74,82,186,194);
gr.addColorStop(0,t.a); gr.addColorStop(1,t.b);
ctx.save(); ctx.strokeStyle=gr; ctx.lineWidth=3;
ctx.beginPath(); ctx.arc(130,138,60,0,Math.PI*2); ctx.stroke(); ctx.restore();
drawAvatar(ctx,av,130,138,55);
// handle
ctx.fillStyle="rgba(255,255,255,0.3)"; ctx.font="bold 11px 'SpaceMono'";
ctx.textAlign="center"; ctx.textBaseline="alphabetic";
ctx.fillText(fitText(ctx,"@"+d.name.toLowerCase().replace(/\s+/g,""),"bold 11px 'SpaceMono'",220),130,222);
ctx.textAlign="left";
const RX=286;
// badge
ctx.fillStyle=t.a+"22"; rrect(ctx,RX,50,34,18,3); ctx.fill();
ctx.strokeStyle=t.a+"55"; ctx.lineWidth=1; rrect(ctx,RX,50,34,18,3); ctx.stroke();
ctx.fillStyle=t.a; ctx.font="bold 10px 'SpaceMono'"; ctx.textBaseline="middle"; ctx.fillText("V2",RX+8,59);
// label
ctx.fillStyle=t.a+"aa"; ctx.font="bold 10px 'SpaceMono'"; ctx.textBaseline="alphabetic"; ctx.fillText("PROFILE",RX,82);
// name
ctx.fillStyle="#ffffff"; ctx.font="bold 36px 'Outfit'";
ctx.fillText(fitText(ctx,d.name,"bold 36px 'Outfit'",504),RX,118);
// subname
ctx.fillStyle="rgba(255,255,255,0.44)"; ctx.font="normal 13px 'Outfit'";
ctx.fillText(fitText(ctx,d.subname,"normal 13px 'Outfit'",504),RX,142);
// divider
ctx.strokeStyle="rgba(255,255,255,0.07)"; ctx.lineWidth=1;
ctx.beginPath(); ctx.moveTo(RX,156); ctx.lineTo(790,156); ctx.stroke();
// info grid
[{k:"ADDRESS",v:d.address,x:286,ky:176,vy:196},{k:"EMAIL",v:d.email,x:548,ky:176,vy:196},{k:"PHONE",v:d.number,x:286,ky:228,vy:248}].forEach(g=>{
ctx.fillStyle=t.a+"66"; ctx.font="bold 9px 'SpaceMono'"; ctx.textBaseline="alphabetic"; ctx.fillText(g.k,g.x,g.ky);
ctx.fillStyle="rgba(255,255,255,0.82)"; ctx.font="normal 13px 'Outfit'";
ctx.fillText(fitText(ctx,g.v,"normal 13px 'Outfit'",230),g.x,g.vy);
});
return cv;
}
// ── V3: Vibrant Gradient ──────────────────────────────────
const V3_GRADIENTS = {
white: ["#e0e0e0","#ffffff","#aaaaaa","#555555"],
red: ["#ff0000","#ff6b6b","#c0392b","#7b0000"],
blue: ["#0070ff","#00c6ff","#0035a0","#001060"],
green: ["#00c853","#69f0ae","#007c36","#003820"],
black: ["#333333","#777777","#111111","#000000"],
orange: ["#ff6f00","#ffab40","#e65100","#7c2d00"],
purple: ["#7c3aed","#c084fc","#4c1d95","#1e0050"],
pink: ["#ec4899","#f9a8d4","#9d174d","#500020"],
yellow: ["#fbbf24","#fde68a","#b45309","#5c2d00"],
cyan: ["#06b6d4","#67e8f9","#0e7490","#003344"],
};
function drawV3(av, d, colour) {
const W = 820, H = 312;
const cv = createCanvas(W, H);
const ctx = cv.getContext("2d");
const key = (colour||"white").toLowerCase().trim();
const gc = V3_GRADIENTS[key] || V3_GRADIENTS.white;
const t = getTheme(colour);
// bg gradient — theme colors
const bg=ctx.createLinearGradient(0,0,W,H);
bg.addColorStop(0,gc[0]); bg.addColorStop(0.35,gc[1]);
bg.addColorStop(0.65,gc[2]); bg.addColorStop(1,gc[3]);
ctx.fillStyle=bg; ctx.fillRect(0,0,W,H);
ctx.fillStyle="rgba(0,0,0,0.3)"; ctx.fillRect(0,0,W,H);
// deco circles
ctx.save(); ctx.strokeStyle="rgba(255,255,255,0.07)"; ctx.lineWidth=36;
ctx.beginPath(); ctx.arc(710,-30,185,0,Math.PI*2); ctx.stroke();
ctx.lineWidth=20; ctx.beginPath(); ctx.arc(590,295,105,0,Math.PI*2); ctx.stroke(); ctx.restore();
ctx.save(); ctx.translate(446,58); ctx.rotate(Math.PI/4);
ctx.fillStyle="rgba(255,255,255,0.05)"; ctx.fillRect(-34,-34,68,68); ctx.restore();
// avatar ring
ctx.save(); ctx.strokeStyle="rgba(255,255,255,0.38)"; ctx.lineWidth=4;
ctx.beginPath(); ctx.arc(152,156,72,0,Math.PI*2); ctx.stroke(); ctx.restore();
drawAvatar(ctx,av,152,156,66);
const CX=268;
// badge
ctx.fillStyle="rgba(255,255,255,0.18)"; rrect(ctx,CX,58,34,19,3); ctx.fill();
ctx.strokeStyle="rgba(255,255,255,0.38)"; ctx.lineWidth=1; rrect(ctx,CX,58,34,19,3); ctx.stroke();
ctx.fillStyle="#fff"; ctx.font="bold 10px 'SpaceMono'"; ctx.textBaseline="middle"; ctx.textAlign="left"; ctx.fillText("V3",CX+8,68);
// name
ctx.fillStyle="#ffffff"; ctx.font="bold 44px 'Playfair'"; ctx.textBaseline="alphabetic";
ctx.fillText(fitText(ctx,d.name,"bold 44px 'Playfair'",520),CX,108);
// subname
ctx.fillStyle="rgba(255,255,255,0.78)"; ctx.font="normal 14px 'Outfit'";
ctx.fillText(fitText(ctx,d.subname.toUpperCase(),"normal 14px 'Outfit'",520),CX,134);
// pills
const rows=[
{items:[{icon:"📍",txt:d.address},{icon:"✉",txt:d.email}],y:156,maxEach:220},
{items:[{icon:"📞",txt:d.number}],y:196,maxEach:500},
];
rows.forEach(row=>{
let px=CX;
row.items.forEach(p=>{
const label=p.icon+" "+p.txt;
ctx.font="normal 12px 'Outfit'";
const cl=fitText(ctx,label,"normal 12px 'Outfit'",row.maxEach-28);
const tw=ctx.measureText(cl).width+28;
ctx.fillStyle="rgba(255,255,255,0.14)"; rrect(ctx,px,row.y,tw,28,14); ctx.fill();
ctx.strokeStyle="rgba(255,255,255,0.28)"; ctx.lineWidth=1; rrect(ctx,px,row.y,tw,28,14); ctx.stroke();
ctx.fillStyle="#fff"; ctx.textBaseline="middle"; ctx.fillText(cl,px+14,row.y+14);
px+=tw+10;
});
});
return cv;
}
// ── Main module ───────────────────────────────────────────
module.exports = {
config: {
name: "fbcover",
aliases: ["cover"],
version: "1.1",
author: "MOHAMMAD AKASH",
countDown: 10,
role: 0,
shortDescription: "Facebook cover generate",
longDescription: "Generate Facebook cover photo using canvas",
category: "utility",
guide: { en: "{pn} v1/v2/v3 - name - title - address - email - phone - color\n\n🎨 Colors: white, red, blue, green, black, orange, purple, pink, yellow, cyan" }
},
onStart: async function ({ api, event, args, usersData }) {
const input = args.join(" ");
if (!input) {
return api.sendMessage(
`⚠ Wrong format!\n\n📌 Usage:\nfbcover v1 - Name - Title - Address - Email - Phone - Color\n\n✅ Example:\nfbcover v2 - Mohammad Akash - Developer - Dhaka - akash@mail.com - 01700000000 - red\n\n🎨 Colors: white, red, blue, green, black, orange, purple, pink, yellow, cyan`,
event.threadID, event.messageID
);
}
const parts = input.split("-").map(p => p.trim());
const version = (parts[0] || "v1").toLowerCase();
const name = parts[1] || "Your Name";
const subname = parts[2] || "Your Title";
const address = parts[3] || "Your Address";
const email = parts[4] || "your@email.com";
const number = parts[5] || "+00 0000-000000";
const colour = parts[6] || "white";
if (!["v1","v2","v3"].includes(version)) {
return api.sendMessage(`✖ Invalid version "${version}"\n📌 Use: v1, v2 or v3`, event.threadID, event.messageID);
}
let uid;
if (event.type === "message_reply") {
uid = event.messageReply.senderID;
} else {
uid = Object.keys(event.mentions)[0] || event.senderID;
}
const userName = await usersData.getName(uid);
const wait = await api.sendMessage("⏳ Generating your cover...", event.threadID);
try {
await setupFonts();
const avatar = await fetchAvatar(uid);
const data = { name, subname, address, email, number };
let canvas;
if (version === "v1") canvas = drawV1(avatar, data, colour);
else if (version === "v2") canvas = drawV2(avatar, data, colour);
else canvas = drawV3(avatar, data, colour);
const cachePath = path.join(__dirname, "cache", `fbcover_${Date.now()}.png`);
await fs.ensureDir(path.join(__dirname, "cache"));
await fs.writeFile(cachePath, canvas.toBuffer("image/png"));
api.unsendMessage(wait.messageID);
await api.sendMessage(
{
body:
`✅ Cover generated!\n` +
`📌 Version : ${version.toUpperCase()}\n` +
`👤 Name : ${name}\n` +
`🏷 Title : ${subname}\n` +
`📍 Address : ${address}\n` +
`✉ Email : ${email}\n` +
`📞 Phone : ${number}\n` +
`🎨 Color : ${colour}\n` +
`💁 User : ${userName}`,
attachment: fs.createReadStream(cachePath)
},
event.threadID,
() => fs.remove(cachePath),
event.messageID
);
} catch (err) {
console.error("[fbcover]", err.message);
api.unsendMessage(wait.messageID);
api.sendMessage("✖ Failed to generate cover.\n" + err.message, event.threadID, event.messageID);
}
}
};