mirai
command
leave
No description
0
views
0
likes
0
installs
raw source
const { getTime, drive } = 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: "2.0",
author: "Rasin",
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 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;
const userName = await usersData.getName(leftParticipantFbId);
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);
// Register fonts with Unicode support
try {
const fontPaths = [
{ path: 'C:\\Windows\\Fonts\\seguisb.ttf', family: 'Segoe' },
{ path: 'C:\\Windows\\Fonts\\segoeui.ttf', family: 'Segoe' },
{ path: 'C:\\Windows\\Fonts\\arialuni.ttf', family: 'ArialUni' },
{ path: 'C:\\Windows\\Fonts\\msyh.ttc', family: 'MSYahei' },
{ path: '/usr/share/fonts/truetype/noto/NotoSans-Bold.ttf', family: 'Noto' },
{ path: '/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf', family: 'DejaVu' }
];
let fontRegistered = false;
for (const font of fontPaths) {
try {
if (fs.existsSync(font.path)) {
registerFont(font.path, { family: font.family });
fontRegistered = true;
break;
}
} catch (e) {
continue;
}
}
} catch (fontErr) {
console.log("Font registration error:", fontErr.message);
}
const threadInfo = await api.getThreadInfo(threadID);
const memberCount = threadInfo.participantIDs.length;
const canvas = createCanvas(1000, 600);
const ctx = canvas.getContext('2d');
// Background gradient (darker/sadder tones)
const bgGradient = ctx.createRadialGradient(500, 300, 100, 500, 300, 600);
bgGradient.addColorStop(0, '#4a4a6a');
bgGradient.addColorStop(0.5, '#3a3a52');
bgGradient.addColorStop(1, '#2a2a3a');
ctx.fillStyle = bgGradient;
ctx.fillRect(0, 0, 1000, 600);
// Bubbles with darker tones
ctx.globalAlpha = 0.1;
const bubbles = [
{ x: 150, y: 100, r: 80 },
{ x: 850, y: 150, r: 120 },
{ x: 200, y: 450, r: 60 },
{ x: 800, y: 500, r: 90 },
{ x: 500, y: 100, r: 70 }
];
bubbles.forEach(b => {
const bubbleGrad = ctx.createRadialGradient(b.x, b.y, 0, b.x, b.y, b.r);
bubbleGrad.addColorStop(0, 'rgba(255, 255, 255, 0.3)');
bubbleGrad.addColorStop(1, 'rgba(255, 255, 255, 0)');
ctx.fillStyle = bubbleGrad;
ctx.beginPath();
ctx.arc(b.x, b.y, b.r, 0, Math.PI * 2);
ctx.fill();
});
ctx.globalAlpha = 1;
// Wave patterns
ctx.strokeStyle = 'rgba(255, 255, 255, 0.08)';
ctx.lineWidth = 3;
for (let i = 0; i < 5; i++) {
ctx.beginPath();
for (let x = 0; x < 1000; x += 20) {
const y = 100 + i * 100 + Math.sin(x / 50 + i) * 30;
if (x === 0) ctx.moveTo(x, y);
else ctx.lineTo(x, y);
}
ctx.stroke();
}
// Main card background
ctx.fillStyle = 'rgba(255, 255, 255, 0.1)';
ctx.strokeStyle = 'rgba(255, 255, 255, 0.2)';
ctx.lineWidth = 3;
ctx.beginPath();
ctx.moveTo(100, 120);
ctx.lineTo(900, 80);
ctx.quadraticCurveTo(950, 80, 950, 130);
ctx.lineTo(950, 500);
ctx.quadraticCurveTo(950, 550, 900, 550);
ctx.lineTo(100, 520);
ctx.quadraticCurveTo(50, 520, 50, 470);
ctx.lineTo(50, 170);
ctx.quadraticCurveTo(50, 120, 100, 120);
ctx.closePath();
ctx.fill();
ctx.stroke();
// Top strip (gray/sad colors)
const stripGradient = ctx.createLinearGradient(0, 0, 1000, 600);
stripGradient.addColorStop(0, '#708090');
stripGradient.addColorStop(0.5, '#556B2F');
stripGradient.addColorStop(1, '#483D8B');
ctx.fillStyle = stripGradient;
ctx.beginPath();
ctx.moveTo(50, 120);
ctx.lineTo(950, 80);
ctx.lineTo(950, 100);
ctx.lineTo(50, 140);
ctx.closePath();
ctx.fill();
try {
const avatarUrl = `https://arshi-facebook-pp.vercel.app/api/pp?uid=${leftParticipantFbId}`;
const avatarResponse = await axios.get(avatarUrl, { responseType: 'arraybuffer' });
const avatar = await loadImage(Buffer.from(avatarResponse.data));
const hexSize = 110;
const hexX = 250;
const hexY = 300;
ctx.save();
ctx.beginPath();
for (let i = 0; i < 6; i++) {
const angle = (Math.PI / 3) * i;
const x = hexX + hexSize * Math.cos(angle);
const y = hexY + hexSize * Math.sin(angle);
if (i === 0) ctx.moveTo(x, y);
else ctx.lineTo(x, y);
}
ctx.closePath();
ctx.clip();
ctx.drawImage(avatar, hexX - hexSize, hexY - hexSize, hexSize * 2, hexSize * 2);
ctx.restore();
// Hexagon border (gray)
ctx.shadowColor = '#708090';
ctx.shadowBlur = 20;
ctx.strokeStyle = '#708090';
ctx.lineWidth = 5;
ctx.beginPath();
for (let i = 0; i < 6; i++) {
const angle = (Math.PI / 3) * i;
const x = hexX + hexSize * Math.cos(angle);
const y = hexY + hexSize * Math.sin(angle);
if (i === 0) ctx.moveTo(x, y);
else ctx.lineTo(x, y);
}
ctx.closePath();
ctx.stroke();
ctx.shadowBlur = 0;
// Outer hexagon ring
ctx.strokeStyle = 'rgba(112, 128, 144, 0.6)';
ctx.lineWidth = 3;
ctx.beginPath();
for (let i = 0; i < 6; i++) {
const angle = (Math.PI / 3) * i;
const x = hexX + (hexSize + 15) * Math.cos(angle);
const y = hexY + (hexSize + 15) * Math.sin(angle);
if (i === 0) ctx.moveTo(x, y);
else ctx.lineTo(x, y);
}
ctx.closePath();
ctx.stroke();
} catch (err) {
const hexSize = 110;
const hexX = 250;
const hexY = 300;
ctx.fillStyle = '#2a2a3e';
ctx.beginPath();
for (let i = 0; i < 6; i++) {
const angle = (Math.PI / 3) * i;
const x = hexX + hexSize * Math.cos(angle);
const y = hexY + hexSize * Math.sin(angle);
if (i === 0) ctx.moveTo(x, y);
else ctx.lineTo(x, y);
}
ctx.closePath();
ctx.fill();
}
// "GOODBYE" text
ctx.fillStyle = 'rgba(112, 128, 144, 0.2)';
ctx.fillRect(450, 150, 480, 80);
ctx.shadowColor = '#708090';
ctx.shadowBlur = 15;
ctx.font = 'bold 56px Segoe, Noto, ArialUni, MSYahei, DejaVu, sans-serif';
const goodbyeGrad = ctx.createLinearGradient(450, 180, 900, 180);
goodbyeGrad.addColorStop(0, '#ffffff');
goodbyeGrad.addColorStop(1, '#708090');
ctx.fillStyle = goodbyeGrad;
ctx.textAlign = 'left';
ctx.fillText('GOODBYE', 480, 210);
ctx.shadowBlur = 0;
// User name section
ctx.fillStyle = 'rgba(255, 255, 255, 0.1)';
ctx.fillRect(450, 250, 480, 90);
ctx.font = 'bold 42px Segoe, Noto, ArialUni, MSYahei, DejaVu, sans-serif';
const nameGrad = ctx.createLinearGradient(450, 280, 900, 280);
nameGrad.addColorStop(0, '#9370DB');
nameGrad.addColorStop(0.5, '#708090');
nameGrad.addColorStop(1, '#556B2F');
ctx.fillStyle = nameGrad;
let displayName = userName;
ctx.font = 'bold 38px Segoe, Noto, ArialUni, MSYahei, DejaVu, sans-serif';
if (ctx.measureText(displayName).width > 440) {
while (ctx.measureText(displayName + '...').width > 440) {
displayName = displayName.slice(0, -1);
}
displayName += '...';
}
ctx.fillText(displayName, 480, 295);
ctx.font = 'italic 22px Segoe, Noto, ArialUni, MSYahei, DejaVu, sans-serif';
ctx.fillStyle = '#ffffff';
ctx.fillText(isKicked ? 'Was Kicked From Group' : 'Left The Group', 480, 325);
// Total Members box
ctx.fillStyle = 'rgba(112, 128, 144, 0.15)';
ctx.fillRect(450, 360, 200, 60);
ctx.strokeStyle = 'rgba(112, 128, 144, 0.4)';
ctx.lineWidth = 2;
ctx.strokeRect(450, 360, 200, 60);
ctx.font = 'bold 12px Segoe, Noto, ArialUni, MSYahei, DejaVu, sans-serif';
ctx.fillStyle = '#708090';
ctx.fillText('REMAINING MEMBERS', 470, 380);
ctx.font = 'bold 20px Segoe, Noto, ArialUni, MSYahei, DejaVu, sans-serif';
ctx.fillStyle = '#ffffff';
ctx.fillText(`👥 ${memberCount}`, 470, 405);
ctx.strokeStyle = 'rgba(112, 128, 144, 0.5)';
ctx.lineWidth = 3;
ctx.beginPath();
ctx.moveTo(450, 450);
ctx.lineTo(930, 450);
ctx.stroke();
const emojis = [
{ x: 460, y: 480, emoji: '💔', size: 20 },
{ x: 700, y: 490, emoji: '🫶🏻', size: 16 },
{ x: 900, y: 475, emoji: '⭐', size: 18 }
];
emojis.forEach(item => {
ctx.font = `${item.size}px Segoe, Noto, ArialUni, MSYahei, DejaVu, sans-serif`;
ctx.fillStyle = '#ffffff';
ctx.fillText(item.emoji, item.x, item.y);
});
ctx.font = 'bold 22px Segoe, Noto, ArialUni, MSYahei, DejaVu, sans-serif';
const footerGrad = ctx.createLinearGradient(450, 515, 800, 515);
footerGrad.addColorStop(0, '#9370DB');
footerGrad.addColorStop(1, '#708090');
ctx.fillStyle = footerGrad;
ctx.fillText("We'll Miss You! Take Care 💔", 450, 515);
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 {
if (fs.existsSync(cachePath)) {
fs.unlinkSync(cachePath);
}
} catch (err) {
console.error("Error cleaning cache:", err);
}
});
} catch (error) {
console.error("Error generating leave card:", error);
message.reply(leaveMessage);
}
};
}
};