import { Colors, EmbedBuilder, Guild, Message } from "discord.js"; import type { Command } from "../types"; import { clients } from ".."; import { getJsonFromURL } from "../utils/discohook"; export default { data: { name: "mass-dm", description: "Gửi tin nhắn đến toàn bộ thành viên trong server.", toJSON() { return { name: "mass-dm", description: "Gửi tin nhắn đến toàn bộ thành viên trong server.", }; }, }, ownersOnly: true, execute: async (message: Message, args?: string[]) => { if (!message.guild || !args || args.length === 0) { return message.reply("Vui lòng cung cấp nội dung tin nhắn."); } const contentInput = args.join(" ").trim(); const guild = message.guild as Guild; const clientsInGuild = clients.filter((client) => client.guilds.cache.has(guild.id) ); if (clientsInGuild.length === 0) { return message.reply("❌ Không có client nào trong server."); } const DISCOHOOK_URL_REGEX = /^(https?:\/\/)?(www\.)?(discohook\.app|discohook\.org)\/\?data=/; let content = contentInput; const embeds: any[] = []; if (DISCOHOOK_URL_REGEX.test(contentInput)) { const discohook = getJsonFromURL(contentInput); if (discohook) { if (discohook.embeds && discohook.embeds.length > 0) { embeds.push(...discohook.embeds); } if (discohook.content) { content = discohook.content; } } } const variables: Map = new Map(); variables.set("guild.name", guild.name); variables.set("guild.id", guild.id); variables.set("guild.memberCount", guild.memberCount.toString()); const allMembers = guild.members.cache.filter((m) => !m.user.bot); const done: { clientId: string; done: number; failed: number }[] = clientsInGuild.map((client) => ({ clientId: client.user?.id as string, done: 0, failed: 0, })); const getTotallDone = () => done.reduce((acc, cur) => acc + cur.done, 0); const getTotallFailed = () => done.reduce((acc, cur) => acc + cur.failed, 0); const isDone = () => getTotallDone() + getTotallFailed() === allMembers.size; const getEmbed = () => new EmbedBuilder() .setTitle("📬 Mass DM Status") .setDescription( `Total: ${allMembers.size}\nDone: ${getTotallDone()}\nFailed: ${getTotallFailed()}\nStatus: ${isDone() ? "✅ Done" : "⏳ In Progress"}` ) .setColor(isDone() ? Colors.Green : Colors.Yellow) .setTimestamp(); const statusMessage = await message.reply({ embeds: [getEmbed()], }); let clientIndex = 0; for (const member of allMembers.values()) { if (!clientsInGuild[clientIndex]) clientIndex = 0; const client = clientsInGuild[clientIndex]; const clientDone = done.find((d) => d.clientId === client.user?.id)!; clientIndex = (clientIndex + 1) % clientsInGuild.length; // Tạo nội dung riêng biệt mỗi user const personalizedVariables = new Map(variables); personalizedVariables.set("user.username", member.user.username); personalizedVariables.set("user.id", member.user.id); personalizedVariables.set("user.tag", member.user.tag); personalizedVariables.set("user.avatar", member.user.displayAvatarURL()); personalizedVariables.set("user.mention", member.user.toString()); personalizedVariables.set("user.nickname", member.nickname ?? member.user.username); let personalizedContent = content; for (const [key, value] of personalizedVariables.entries()) { personalizedContent = personalizedContent.replace(new RegExp(`{${key}}`, "g"), value); } try { await client.users.send(member.user.id, { content: personalizedContent, embeds: embeds, }); clientDone.done++; } catch (error) { console.error(`❌ Không gửi được đến ${member.user.tag}:`, error); clientDone.failed++; } try { await statusMessage.edit({ embeds: [getEmbed()], }); } catch (err) { console.error("Không thể cập nhật tiến độ:", err); } await new Promise((res) => setTimeout(res, 400)); // delay nhẹ tránh bị rate limit } }, };