|
import { Colors, EmbedBuilder, Guild, Message } from "discord.js"; |
|
import type { Command } from "../types"; |
|
import { clients } from ".."; |
|
import { getJsonFromURL } from "../utils/discohook"; |
|
|
|
export default <Command>{ |
|
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<string, string> = 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; |
|
|
|
|
|
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)); |
|
} |
|
}, |
|
}; |