// code from goatstore // api: https://store.agi.bd const { createCanvas, loadImage } = require("canvas"); const GIFEncoder = require("gifencoder"); const fs = require("fs-extra"); const path = require("path"); const SIZE = 400; const FRAMES = 24; const DELAY = 60; // ms per frame // Fixed particle positions (protiti run-e ekoi thakbe) const PARTICLES = Array.from({ length: 22 }, (_, i) => ({ x: (i * 83) % SIZE, y: (i * 137) % SIZE, r: 2 + (i % 4), speed: 0.6 + (i % 5) * 0.25, phase: i * 0.7 })); function drawFrame(ctx, avatar, name, label, f) { const t = (f / FRAMES) * Math.PI * 2; // 1) background: dhire dhire rong bodlay const hue = (f / FRAMES) * 360; const bg = ctx.createLinearGradient(0, 0, SIZE, SIZE); bg.addColorStop(0, `hsl(${hue}, 70%, 18%)`); bg.addColorStop(1, `hsl(${(hue + 60) % 360}, 70%, 8%)`); ctx.fillStyle = bg; ctx.fillRect(0, 0, SIZE, SIZE); // 2) vaschhe emon particle for (const p of PARTICLES) { const y = (p.y - f * p.speed * (SIZE / FRAMES) + SIZE * 2) % SIZE; const x = p.x + Math.sin(t + p.phase) * 12; ctx.globalAlpha = 0.35 + 0.35 * Math.sin(t * 2 + p.phase); ctx.fillStyle = `hsl(${(hue + 120) % 360}, 90%, 75%)`; ctx.beginPath(); ctx.arc(x, y, p.r, 0, Math.PI * 2); ctx.fill(); } ctx.globalAlpha = 1; const cx = SIZE / 2; const cy = 175; const radius = 100; // 3) pulse korche emon glow const pulse = 1 + 0.06 * Math.sin(t); const glow = ctx.createRadialGradient(cx, cy, radius * 0.8, cx, cy, radius * 1.7 * pulse); glow.addColorStop(0, `hsla(${(hue + 120) % 360}, 90%, 60%, 0.6)`); glow.addColorStop(1, "rgba(0, 0, 0, 0)"); ctx.fillStyle = glow; ctx.beginPath(); ctx.arc(cx, cy, radius * 1.7 * pulse, 0, Math.PI * 2); ctx.fill(); // 4) ghurche emon ring ctx.lineWidth = 6; ctx.lineCap = "round"; for (let i = 0; i < 3; i++) { const start = t + (i * Math.PI * 2) / 3; ctx.strokeStyle = `hsl(${(hue + i * 40) % 360}, 95%, 65%)`; ctx.beginPath(); ctx.arc(cx, cy, radius + 12, start, start + Math.PI / 3); ctx.stroke(); } // 5) avatar (gol kore kata) ctx.save(); ctx.beginPath(); ctx.arc(cx, cy, radius, 0, Math.PI * 2); ctx.closePath(); ctx.clip(); ctx.drawImage(avatar, cx - radius, cy - radius, radius * 2, radius * 2); ctx.restore(); ctx.lineWidth = 4; ctx.strokeStyle = "#ffffff"; ctx.beginPath(); ctx.arc(cx, cy, radius, 0, Math.PI * 2); ctx.stroke(); // 6) label (bounce kore) const bounce = Math.sin(t) * 4; ctx.textAlign = "center"; ctx.textBaseline = "middle"; ctx.font = "bold 40px sans-serif"; ctx.fillStyle = "#000000"; ctx.fillText(label, cx + 2, 330 + bounce + 2); ctx.fillStyle = `hsl(${(hue + 180) % 360}, 95%, 75%)`; ctx.fillText(label, cx, 330 + bounce); // 7) nam ctx.font = "bold 24px sans-serif"; ctx.fillStyle = "#ffffff"; ctx.fillText(name.length > 22 ? name.slice(0, 21) + "…" : name, cx, 372); } module.exports = { config: { name: "animcard", aliases: ["gifcard", "agif"], version: "1.0", author: "Zihad", countDown: 15, role: 0, description: { en: "Avatar diye animated GIF card banay (canvas)" }, category: "image", guide: { en: "{pn}: nijer card" + "\n{pn} @tag: tag kora user-er card" + "\n{pn} : nijer label (jemon: {pn} WELCOME)" } }, onStart: async function ({ message, event, args, usersData }) { const { senderID, mentions, messageReply } = event; let uid = senderID; if (messageReply) uid = messageReply.senderID; else if (Object.keys(mentions || {}).length > 0) uid = Object.keys(mentions)[0]; // @tag-er nam bad diye baki text-ta label hobe let label = args .filter(a => !a.startsWith("@")) .join(" ") .trim() .toUpperCase() || "WELCOME"; if (label.length > 14) label = label.slice(0, 14); const name = await usersData.getName(uid); const outPath = path.join(__dirname, "tmp", `animcard_${uid}_${Date.now()}.gif`); try { const avatar = await loadImage(await usersData.getAvatarUrl(uid)); fs.ensureDirSync(path.dirname(outPath)); const encoder = new GIFEncoder(SIZE, SIZE); const stream = encoder.createReadStream().pipe(fs.createWriteStream(outPath)); const done = new Promise((resolve, reject) => { stream.on("finish", resolve); stream.on("error", reject); }); encoder.start(); encoder.setRepeat(0); // 0 = infinite loop encoder.setDelay(DELAY); encoder.setQuality(10); const canvas = createCanvas(SIZE, SIZE); const ctx = canvas.getContext("2d"); for (let f = 0; f < FRAMES; f++) { drawFrame(ctx, avatar, name, label, f); encoder.addFrame(ctx); } encoder.finish(); await done; await message.reply({ body: `✨ ${name}`, attachment: fs.createReadStream(outPath) }); } catch (err) { console.error("[animcard]", err); return message.reply("GIF banate shomossha hoyeche, abar try korun."); } finally { fs.remove(outPath).catch(() => { }); } } };