mirai
command
welcome
No description
0
views
0
likes
0
installs
raw source
const {
createCanvas,
loadImage,
registerFont
} = require('canvas');
const fs = require('fs-extra');
const path = require('path');
const { getTime, drive } = global.utils;
const fontDir = process.cwd() + "/scripts/cmds/assets/font";
const canvasFontDir = process.cwd() + "/scripts/cmds/canvas/fonts";
registerFont(path.join(fontDir, "NotoSans-Bold.ttf"), {
family: 'NotoSans',
weight: 'bold'
});
registerFont(path.join(fontDir, "NotoSans-SemiBold.ttf"), {
family: 'NotoSans',
weight: '600'
});
registerFont(path.join(fontDir, "NotoSans-Regular.ttf"), {
family: 'NotoSans',
weight: 'normal'
});
registerFont(path.join(fontDir, "BeVietnamPro-Bold.ttf"), {
family: 'BeVietnamPro',
weight: 'bold'
});
registerFont(path.join(fontDir, "BeVietnamPro-SemiBold.ttf"), {
family: 'BeVietnamPro',
weight: '600'
});
registerFont(path.join(fontDir, "BeVietnamPro-Regular.ttf"), {
family: 'BeVietnamPro',
weight: 'normal'
});
registerFont(path.join(fontDir, "Kanit-SemiBoldItalic.ttf"), {
family: 'Kanit',
weight: '600',
style: 'italic'
});
registerFont(path.join(canvasFontDir, "Rounded.otf"), {
family: 'Rounded'
});
async function createWelcomeCanvas(gcImg, img1, img2, userName, userNumber, threadName, potato) {
const width = 1200;
const height = 600;
const canvas = createCanvas(width, height);
const ctx = canvas.getContext('2d');
ctx.fillStyle = '#0a0a0a';
ctx.fillRect(0, 0, width, height);
function fitAndSetFont(family, weight, maxSize, minSize, text, maxWidth, style = '') {
let size = maxSize;
for (; size >= minSize; size -= 1) {
ctx.font = `${style ? style + ' ' : ''}${weight} ${size}px ${family}`;
if (ctx.measureText(text).width <= maxWidth) break;
}
ctx.font = `${style ? style + ' ' : ''}${weight} ${Math.max(size, minSize)}px ${family}`;
return Math.max(size, minSize);
}
function drawTextWithStroke(text, x, y, align = 'left') {
ctx.textAlign = align;
ctx.strokeStyle = 'rgba(0,0,0,0.65)';
ctx.lineWidth = 4;
ctx.strokeText(text, x, y);
ctx.fillText(text, x, y);
}
ctx.strokeStyle = 'rgba(255, 255, 255, 0.05)';
ctx.lineWidth = 2;
for (let i = -height; i < width; i += 60) {
ctx.beginPath();
ctx.moveTo(i, 0);
ctx.lineTo(i + height, height);
ctx.stroke();
}
const lightGradient = ctx.createLinearGradient(0, 0, width, height);
lightGradient.addColorStop(0, 'rgba(255, 255, 255, 0.02)');
lightGradient.addColorStop(0.5, 'rgba(255, 255, 255, 0.05)');
lightGradient.addColorStop(1, 'rgba(255, 255, 255, 0.02)');
ctx.fillStyle = lightGradient;
ctx.fillRect(0, 0, width, height);
const squares = [{
x: 50,
y: 50,
size: 80,
rotation: 15
},
{
x: 1100,
y: 80,
size: 60,
rotation: -20
},
{
x: 150,
y: 500,
size: 50,
rotation: 30
},
{
x: 1050,
y: 480,
size: 70,
rotation: -15
},
{
x: 900,
y: 30,
size: 40,
rotation: 45
},
{
x: 200,
y: 150,
size: 35,
rotation: -30
},
{
x: 400,
y: 80,
size: 45,
rotation: 60
},
{
x: 700,
y: 520,
size: 55,
rotation: -40
},
{
x: 950,
y: 250,
size: 38,
rotation: 25
},
{
x: 300,
y: 350,
size: 42,
rotation: -50
}];
squares.forEach(sq => {
ctx.save();
ctx.translate(sq.x + sq.size / 2, sq.y + sq.size / 2);
ctx.rotate((sq.rotation * Math.PI) / 180);
const sqGradient = ctx.createLinearGradient(-sq.size / 2, -sq.size / 2, sq.size / 2, sq.size / 2);
sqGradient.addColorStop(0, 'rgba(34, 197, 94, 0.3)');
sqGradient.addColorStop(1, 'rgba(22, 163, 74, 0.1)');
ctx.fillStyle = sqGradient;
ctx.fillRect(-sq.size / 2, -sq.size / 2, sq.size, sq.size);
ctx.strokeStyle = 'rgba(34, 197, 94, 0.5)';
ctx.lineWidth = 2;
ctx.strokeRect(-sq.size / 2, -sq.size / 2, sq.size, sq.size);
ctx.restore();
});
const circles = [{
x: 250,
y: 250,
radius: 30,
alpha: 0.15
},
{
x: 850,
y: 150,
radius: 25,
alpha: 0.12
},
{
x: 600,
y: 50,
radius: 20,
alpha: 0.1
},
{
x: 100,
y: 350,
radius: 35,
alpha: 0.18
},
{
x: 1000,
y: 380,
radius: 28,
alpha: 0.14
},
{
x: 450,
y: 480,
radius: 22,
alpha: 0.11
}];
circles.forEach(circ => {
ctx.beginPath();
ctx.arc(circ.x, circ.y, circ.radius, 0, Math.PI * 2);
const circGradient = ctx.createRadialGradient(circ.x, circ.y, 0, circ.x, circ.y, circ.radius);
circGradient.addColorStop(0, `rgba(34, 197, 94, ${circ.alpha})`);
circGradient.addColorStop(1, 'rgba(22, 163, 74, 0)');
ctx.fillStyle = circGradient;
ctx.fill();
ctx.strokeStyle = `rgba(34, 197, 94, ${circ.alpha * 2})`;
ctx.lineWidth = 1.5;
ctx.stroke();
});
const triangles = [{
x: 550,
y: 150,
size: 40,
rotation: 0
},
{
x: 180,
y: 420,
size: 35,
rotation: 180
},
{
x: 1080,
y: 320,
size: 38,
rotation: 90
},
{
x: 380,
y: 200,
size: 32,
rotation: -45
}];
triangles.forEach(tri => {
ctx.save();
ctx.translate(tri.x, tri.y);
ctx.rotate((tri.rotation * Math.PI) / 180);
ctx.beginPath();
ctx.moveTo(0, -tri.size / 2);
ctx.lineTo(-tri.size / 2, tri.size / 2);
ctx.lineTo(tri.size / 2, tri.size / 2);
ctx.closePath();
const triGradient = ctx.createLinearGradient(-tri.size / 2, 0, tri.size / 2, 0);
triGradient.addColorStop(0, 'rgba(34, 197, 94, 0.2)');
triGradient.addColorStop(1, 'rgba(22, 163, 74, 0.1)');
ctx.fillStyle = triGradient;
ctx.fill();
ctx.strokeStyle = 'rgba(34, 197, 94, 0.4)';
ctx.lineWidth = 2;
ctx.stroke();
ctx.restore();
});
async function drawCircularImage(imageSrc, x, y, radius, borderColor, borderWidth = 5) {
try {
const image = await loadImage(imageSrc);
ctx.shadowColor = borderColor;
ctx.shadowBlur = 15;
ctx.beginPath();
ctx.arc(x, y, radius + borderWidth, 0, Math.PI * 2);
ctx.fillStyle = borderColor;
ctx.fill();
ctx.shadowBlur = 0;
ctx.beginPath();
ctx.arc(x, y, radius + borderWidth, 0, Math.PI * 2);
ctx.fillStyle = borderColor;
ctx.fill();
ctx.save();
ctx.beginPath();
ctx.arc(x, y, radius, 0, Math.PI * 2);
ctx.closePath();
ctx.clip();
ctx.drawImage(image, x - radius, y - radius, radius * 2, radius * 2);
ctx.restore();
} catch (err) {
ctx.beginPath();
ctx.arc(x, y, radius, 0, Math.PI * 2);
ctx.fillStyle = '#1f1f1f';
ctx.fill();
}
}
await drawCircularImage(img2, width - 120, 100, 55, '#22c55e');
fitAndSetFont('"NotoSans", "BeVietnamPro", sans-serif', 'bold', 22, 14, 'Added by ' + potato, 320);
ctx.fillStyle = '#22c55e';
ctx.textAlign = 'right';
drawTextWithStroke('Added by ' + potato, width - 190, 105, 'right');
await drawCircularImage(img1, 120, height - 100, 55, '#16a34a');
fitAndSetFont('"NotoSans", "BeVietnamPro", sans-serif', 'bold', 28, 16, userName, width - 250);
ctx.fillStyle = '#ffffff';
ctx.textAlign = 'left';
drawTextWithStroke(userName, 190, height - 95, 'left');
await drawCircularImage(gcImg, width / 2, 200, 90, '#22c55e', 6);
fitAndSetFont('"NotoSans", "BeVietnamPro", sans-serif', '600', 42, 20, threadName, width * 0.8);
ctx.fillStyle = '#ffffff';
ctx.textAlign = 'center';
drawTextWithStroke(threadName, width / 2, 335, 'center');
fitAndSetFont('"Kanit", "NotoSans", sans-serif', '600', 56, 28, 'WELCOME', width * 0.9, 'italic');
const nameGradient = ctx.createLinearGradient(width / 2 - 200, 0, width / 2 + 200, 0);
nameGradient.addColorStop(0, '#4ade80');
nameGradient.addColorStop(1, '#22c55e');
ctx.fillStyle = nameGradient;
drawTextWithStroke('WELCOME', width / 2, 410, 'center');
ctx.strokeStyle = 'rgba(34, 197, 94, 0.4)';
ctx.lineWidth = 2;
ctx.beginPath();
ctx.moveTo(width / 2 - 180, 430);
ctx.lineTo(width / 2 + 180, 430);
ctx.stroke();
fitAndSetFont('"NotoSans", "BeVietnamPro", sans-serif', '600', 26, 16, `You are the ${userNumber}th member`, width * 0.9);
ctx.fillStyle = '#a0a0a0';
ctx.textAlign = 'center';
drawTextWithStroke(`You are the ${userNumber}th member`, width / 2, 480, 'center');
return canvas.createPNGStream();
}
module.exports = {
config: {
name: "welcome",
version: "1.3",
author: "Neoaz ゐ",//Adapted from @procoder Allou Mohammed
category: "events"
},
langs: {
vi: {
session1: "sáng",
session2: "trưa",
session3: "chiều",
session4: "tối",
defaultWelcomeMessage: "Chào mừng {userName} đến với {boxName}"
},
en: {
session1: "morning",
session2: "noon",
session3: "afternoon",
session4: "evening",
defaultWelcomeMessage: "Welcome {userName} to {boxName}"
}
},
onStart: async ({
threadsData, event, message, usersData, getLang
}) => {
const type = "log:subscribe";
if (event.logMessageType != type) return;
try {
await threadsData.refreshInfo(event.threadID);
const threadsInfo = await threadsData.get(event.threadID);
if (!threadsInfo.settings.sendWelcomeMessage)
return;
const gcImg = threadsInfo.imageSrc;
const threadName = threadsInfo.threadName;
const addedList = event.logMessageData.addedParticipants || [];
const joined = addedList[0]?.userFbId;
const by = event.author;
if (!joined) return;
const img1 = await usersData.getAvatarUrl(joined).catch(() => null);
const img2 = await usersData.getAvatarUrl(by).catch(() => null);
const usernumber = threadsInfo.members?.length || 1;
const userName = event.logMessageData.addedParticipants[0].fullName || (await usersData.getName(joined));
const authorN = await usersData.getName(by);
const welcomeImage = await createWelcomeCanvas(gcImg, img1, img2, userName, usernumber, threadName, authorN);
const imagePath = path.join(__dirname, '../cmds/', global.utils.randomString(4) + ".png");
const writeStream = fs.createWriteStream(imagePath);
welcomeImage.pipe(writeStream);
await new Promise((resolve) => {
writeStream.on('finish', resolve);
});
const hours = getTime("HH");
const data = threadsInfo.data || {};
let { welcomeMessage = getLang("defaultWelcomeMessage") } = data;
const names = await Promise.all(addedList.map(p => usersData.getName(p.userFbId)));
const nameJoined = names.join(", ");
const multipleText = addedList.length > 1 ? "you guys" : "you";
const form = {};
if (welcomeMessage.includes("{userNameTag}")) {
form.mentions = [{
id: joined,
tag: userName
}];
}
welcomeMessage = (welcomeMessage || "")
.replace(/\{userName\}/g, nameJoined)
.replace(/\{userNameTag\}/g, userName)
.replace(/\{threadName\}|\{boxName\}/g, threadName)
.replace(/\{time\}/g, hours)
.replace(/\{multiple\}/g, multipleText)
.replace(/\{session\}/g, hours <= 10
? getLang("session1")
: hours <= 12
? getLang("session2")
: hours <= 18
? getLang("session3")
: getLang("session4")
);
form.body = welcomeMessage;
const attachments = [];
if (threadsInfo.data?.welcomeAttachment && Array.isArray(threadsInfo.data.welcomeAttachment)) {
const files = threadsInfo.data.welcomeAttachment;
const fromDrive = await Promise.allSettled(files.map(file => drive.getFile(file, "stream")));
for (const r of fromDrive)
if (r.status == "fulfilled")
attachments.push(r.value);
}
attachments.push(fs.createReadStream(imagePath));
form.attachment = attachments;
await message.send(form);
fs.unlink(imagePath).catch(() => {});
} catch (error) {
console.error("[WELCOME] Error:", error.message);
console.error(error.stack);
}
}
};