← back to browse
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();
		}

View raw file