goat
command
leave
No description
0
views
0
likes
0
installs
raw source
const { getTime } = global.utils;
const axios = require("axios");
const fs = require("fs-extra");
const path = require("path");
const { createCanvas, loadImage, registerFont } = require("canvas");
module.exports = {
config: {
name: "leave",
version: "3.0",
author: "Mamun",
category: "events"
},
langs: {
vi: {
session1: "sáng",
session2: "trưa",
session3: "chiều",
session4: "tối",
leaveType1: "tự rời",
leaveType2: "bị kick",
defaultLeaveMessage: "{userName} đã {type} khỏi nhóm"
},
en: {
session1: "morning",
session2: "noon",
session3: "afternoon",
session4: "evening",
leaveType1: "left",
leaveType2: "was kicked from",
defaultLeaveMessage: "💔 {userName} {type} the group.\n\n🌸 We'll miss you! Take care and stay safe ✨"
}
},
onStart: async ({ threadsData, message, event, api, usersData, getLang }) => {
if (event.logMessageType !== "log:unsubscribe") return;
return async function () {
const { threadID } = event;
const threadData = await threadsData.get(threadID);
if (!threadData.settings.sendLeaveMessage) return;
const { leftParticipantFbId } = event.logMessageData;
if (leftParticipantFbId == api.getCurrentUserID()) return;
const hours = getTime("HH");
const threadName = threadData.threadName;
// ========== Name Getting ==========
let userName = null;
try {
userName = await usersData.getName(leftParticipantFbId);
} catch (e) {}
if (!userName || userName === "null" || userName === "undefined" || userName.trim() === "") {
try {
const info = await api.getUserInfo(leftParticipantFbId);
userName = info[leftParticipantFbId]?.name;
} catch (e) {}
}
if (!userName || userName === "null" || userName === "undefined") {
userName = "Someone";
}
const isKicked = leftParticipantFbId != event.author;
let { leaveMessage = getLang("defaultLeaveMessage") } = threadData.data;
const session = hours <= 10 ? getLang("session1") :
hours <= 12 ? getLang("session2") :
hours <= 18 ? getLang("session3") : getLang("session4");
leaveMessage = leaveMessage
.replace(/\{userName\}/g, userName)
.replace(/\{type\}/g, isKicked ? getLang("leaveType2") : getLang("leaveType1"))
.replace(/\{time\}/g, hours)
.replace(/\{session\}/g, session)
.replace(/\{threadName\}/g, threadName);
try {
const cacheDir = path.join(__dirname, "cache");
await fs.ensureDir(cacheDir);
const fontPaths = [
{ path: "/usr/share/fonts/truetype/noto/NotoSans-Bold.ttf", family: "Noto" },
{ path: "/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf", family: "DejaVu" },
{ path: "C:\\Windows\\Fonts\\segoeui.ttf", family: "Segoe" },
{ path: "C:\\Windows\\Fonts\\arial.ttf", family: "Arial" }
];
for (const f of fontPaths) {
try {
if (fs.existsSync(f.path)) {
registerFont(f.path, { family: f.family });
break;
}
} catch {}
}
const threadInfo = await api.getThreadInfo(threadID);
const memberCount = threadInfo.participantIDs.length;
// ===== Canvas =====
const canvas = createCanvas(1100, 520);
const ctx = canvas.getContext("2d");
const bg = ctx.createRadialGradient(550, 260, 50, 550, 260, 700);
bg.addColorStop(0, "#0a1628");
bg.addColorStop(0.5, "#07101c");
bg.addColorStop(1, "#040a12");
ctx.fillStyle = bg;
ctx.fillRect(0, 0, 1100, 520);
ctx.globalAlpha = 0.05;
ctx.font = "bold 180px Noto, DejaVu, Segoe, Arial, sans-serif";
ctx.fillStyle = "#3b82f6";
ctx.textAlign = "center";
ctx.fillText("LEAVE", 550, 310);
ctx.globalAlpha = 1;
// Left card
ctx.fillStyle = "rgba(10, 20, 40, 0.9)";
roundRect(ctx, 40, 40, 340, 440, 28);
ctx.fill();
ctx.strokeStyle = "#3b82f6";
ctx.lineWidth = 3;
ctx.shadowColor = "#3b82f6";
ctx.shadowBlur = 18;
roundRect(ctx, 40, 40, 340, 440, 28);
ctx.stroke();
ctx.shadowBlur = 0;
// ========== Strong Avatar Loader ==========
const avatarSize = 210;
const avatarX = 210;
const avatarY = 200;
async function loadAvatar(uid) {
const urls = [
`https://graph.facebook.com/${uid}/picture?width=512&height=512&access_token=6628568379%7Cc1e620fa708a1d2527f5e94678934e26`,
`https://graph.facebook.com/${uid}/picture?type=large`,
`https://www.facebook.com/${uid}/picture?height=500&width=500`,
`https://graph.facebook.com/${uid}/picture?height=500&width=500`
];
for (const url of urls) {
try {
const res = await axios.get(url, {
responseType: "arraybuffer",
timeout: 8000,
headers: {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
}
});
if (res.data && res.data.byteLength > 1000) {
return await loadImage(Buffer.from(res.data));
}
} catch (e) {
continue;
}
}
return null;
}
try {
const avatar = await loadAvatar(leftParticipantFbId);
if (avatar) {
ctx.save();
ctx.beginPath();
ctx.arc(avatarX, avatarY, avatarSize / 2, 0, Math.PI * 2);
ctx.closePath();
ctx.clip();
ctx.drawImage(avatar, avatarX - avatarSize / 2, avatarY - avatarSize / 2, avatarSize, avatarSize);
ctx.restore();
} else {
ctx.fillStyle = "#0f1c2e";
ctx.beginPath();
ctx.arc(avatarX, avatarY, avatarSize / 2, 0, Math.PI * 2);
ctx.fill();
}
} catch (err) {
console.log("Avatar final error:", err.message);
ctx.fillStyle = "#0f1c2e";
ctx.beginPath();
ctx.arc(avatarX, avatarY, avatarSize / 2, 0, Math.PI * 2);
ctx.fill();
}
// Blue ring
ctx.strokeStyle = "#3b82f6";
ctx.lineWidth = 6;
ctx.shadowColor = "#3b82f6";
ctx.shadowBlur = 14;
ctx.beginPath();
ctx.arc(avatarX, avatarY, avatarSize / 2 + 4, 0, Math.PI * 2);
ctx.stroke();
ctx.shadowBlur = 0;
// Developer: Mamun
ctx.font = "bold 20px Noto, DejaVu, Segoe, Arial, sans-serif";
ctx.fillStyle = "#93c5fd";
ctx.textAlign = "center";
ctx.fillText("💻 Developer: Mamun", 210, 380);
// Right side
ctx.textAlign = "left";
ctx.font = "bold 58px Noto, DejaVu, Segoe, Arial, sans-serif";
ctx.fillStyle = "#ffffff";
ctx.shadowColor = "rgba(59, 130, 246, 0.45)";
ctx.shadowBlur = 12;
ctx.fillText("GOODBYE", 430, 110);
ctx.shadowBlur = 0;
ctx.font = "bold 36px Noto, DejaVu, Segoe, Arial, sans-serif";
ctx.fillStyle = "#f1f5f9";
let displayName = userName;
if (ctx.measureText(displayName).width > 520) {
while (ctx.measureText(displayName + "...").width > 520) {
displayName = displayName.slice(0, -1);
}
displayName += "...";
}
ctx.fillText(displayName, 430, 165);
ctx.fillStyle = "rgba(255, 255, 255, 0.06)";
roundRect(ctx, 430, 195, 520, 55, 14);
ctx.fill();
ctx.strokeStyle = "rgba(59, 130, 246, 0.3)";
ctx.lineWidth = 1.5;
roundRect(ctx, 430, 195, 520, 55, 14);
ctx.stroke();
ctx.font = "22px Noto, DejaVu, Segoe, Arial, sans-serif";
ctx.fillStyle = "#e2e8f0";
ctx.fillText(isKicked ? "💔 Was kicked from the group." : "💔 Has left the group.", 455, 230);
ctx.fillStyle = "rgba(59, 130, 246, 0.08)";
roundRect(ctx, 430, 270, 520, 50, 12);
ctx.fill();
ctx.font = "15px monospace";
ctx.fillStyle = "#93c5fd";
ctx.globalAlpha = 0.9;
ctx.fillText("01F 01F 01F 01F 01F 01F 01F 01F 01F 01F 01F ...", 450, 302);
ctx.globalAlpha = 1;
ctx.fillStyle = "#3b82f6";
roundRect(ctx, 430, 350, 180, 55, 14);
ctx.fill();
ctx.font = "bold 22px Noto, DejaVu, Segoe, Arial, sans-serif";
ctx.fillStyle = "#ffffff";
ctx.textAlign = "center";
ctx.fillText(`👤 ${memberCount} LEFT`, 520, 385);
ctx.fillStyle = "rgba(255, 255, 255, 0.07)";
roundRect(ctx, 630, 350, 320, 55, 14);
ctx.fill();
ctx.strokeStyle = "rgba(255, 255, 255, 0.12)";
ctx.lineWidth = 1.5;
roundRect(ctx, 630, 350, 320, 55, 14);
ctx.stroke();
const now = new Date();
const timeStr = now.toLocaleTimeString("en-US", { hour: "2-digit", minute: "2-digit", hour12: true });
const dateStr = now.toLocaleDateString("en-GB", { day: "2-digit", month: "short", year: "numeric" });
ctx.font = "18px Noto, DejaVu, Segoe, Arial, sans-serif";
ctx.fillStyle = "#e2e8f0";
ctx.fillText(`🕒 ${timeStr} · ${dateStr}`, 790, 385);
const cachePath = path.join(cacheDir, `leave_\( {leftParticipantFbId}_ \){Date.now()}.png`);
const buffer = canvas.toBuffer("image/png");
await fs.writeFile(cachePath, buffer);
message.reply({
body: leaveMessage,
attachment: fs.createReadStream(cachePath)
}, () => {
try { fs.unlinkSync(cachePath); } catch {}
});
} catch (err) {
console.error("Leave card error:", err);
message.reply(leaveMessage);
}
};
}
};
function roundRect(ctx, x, y, w, h, r) {
if (w < 2 * r) r = w / 2;
if (h < 2 * r) r = h / 2;
ctx.beginPath();
ctx.moveTo(x + r, y);
ctx.arcTo(x + w, y, x + w, y + h, r);
ctx.arcTo(x + w, y + h, x, y + h, r);
ctx.arcTo(x, y + h, x, y, r);
ctx.arcTo(x, y, x + w, y, r);
ctx.closePath();
}