Twan07 commited on
Commit
67bf4ee
·
verified ·
1 Parent(s): 540a7ff

Upload 8 files

Browse files
Commands/eval.ts ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { ChatInputCommandInteraction, Message } from "discord.js";
2
+
3
+ export default {
4
+ data: {
5
+ name: "eval",
6
+ description: "Chạy mã JavaScript (chỉ chủ bot).",
7
+ toJSON() {
8
+ return {
9
+ name: "eval",
10
+ description: "Chạy mã JavaScript (chỉ chủ bot).",
11
+ };
12
+ },
13
+ },
14
+ ownersOnly: true,
15
+ async execute(input: ChatInputCommandInteraction | Message, args?: string[]) {
16
+ const code = args?.join(" ");
17
+ if (!code) {
18
+ return input instanceof Message
19
+ ? input.reply("❌ Không có code để chạy.")
20
+ : input.reply({ content: "❌ Không có code để chạy.", ephemeral: true });
21
+ }
22
+
23
+ try {
24
+ let result = eval(code);
25
+ if (result instanceof Promise) result = await result;
26
+
27
+ const output = typeof result === "object" ? JSON.stringify(result, null, 2) : result;
28
+
29
+ if (input instanceof Message) {
30
+ await input.reply(`✅ Output:\n\`\`\`js\n${output}\n\`\`\``);
31
+ } else {
32
+ await input.reply({ content: `✅ Output:\n\`\`\`js\n${output}\n\`\`\``, ephemeral: true });
33
+ }
34
+ } catch (err) {
35
+ if (input instanceof Message) {
36
+ await input.reply(`❌ Error:\n\`\`\`${err}\`\`\``);
37
+ } else {
38
+ await input.reply({ content: `❌ Error:\n\`\`\`${err}\`\`\``, ephemeral: true });
39
+ }
40
+ }
41
+ },
42
+ };
Commands/help.ts ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { ChatInputCommandInteraction, Message } from "discord.js";
2
+ import { commands } from "../index";
3
+
4
+ export default {
5
+ data: {
6
+ name: "help",
7
+ description: "Hiển thị danh sách lệnh có sẵn.",
8
+ toJSON() {
9
+ return {
10
+ name: "help",
11
+ description: "Hiển thị danh sách lệnh có sẵn.",
12
+ };
13
+ },
14
+ },
15
+ ownersOnly: false,
16
+ async execute(input: ChatInputCommandInteraction | Message) {
17
+ const commandList = commands
18
+ .map((cmd) => `- \`${cmd.data.name}\`: ${cmd.data.description}`)
19
+ .join("\n");
20
+
21
+ const replyText = `📜 **Danh sách lệnh:**\n${commandList}`;
22
+
23
+ if (input instanceof Message) {
24
+ await input.reply(replyText);
25
+ } else {
26
+ await input.reply({ content: replyText, ephemeral: true });
27
+ }
28
+ },
29
+ };
Commands/mass-dm.ts ADDED
@@ -0,0 +1,125 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Colors, EmbedBuilder, Guild, Message } from "discord.js";
2
+ import type { Command } from "../types";
3
+ import { clients } from "..";
4
+ import { getJsonFromURL } from "../utils/discohook";
5
+
6
+ export default <Command>{
7
+ data: {
8
+ name: "mass-dm",
9
+ description: "Gửi tin nhắn đến toàn bộ thành viên trong server.",
10
+ toJSON() {
11
+ return {
12
+ name: "mass-dm",
13
+ description: "Gửi tin nhắn đến toàn bộ thành viên trong server.",
14
+ };
15
+ },
16
+ },
17
+ ownersOnly: true,
18
+ execute: async (message: Message, args?: string[]) => {
19
+ if (!message.guild || !args || args.length === 0) {
20
+ return message.reply("Vui lòng cung cấp nội dung tin nhắn.");
21
+ }
22
+
23
+ const contentInput = args.join(" ").trim();
24
+ const guild = message.guild as Guild;
25
+
26
+ const clientsInGuild = clients.filter((client) =>
27
+ client.guilds.cache.has(guild.id)
28
+ );
29
+ if (clientsInGuild.length === 0) {
30
+ return message.reply("❌ Không có client nào trong server.");
31
+ }
32
+
33
+ const DISCOHOOK_URL_REGEX =
34
+ /^(https?:\/\/)?(www\.)?(discohook\.app|discohook\.org)\/\?data=/;
35
+ let content = contentInput;
36
+ const embeds: any[] = [];
37
+
38
+ if (DISCOHOOK_URL_REGEX.test(contentInput)) {
39
+ const discohook = getJsonFromURL(contentInput);
40
+ if (discohook) {
41
+ if (discohook.embeds && discohook.embeds.length > 0) {
42
+ embeds.push(...discohook.embeds);
43
+ }
44
+ if (discohook.content) {
45
+ content = discohook.content;
46
+ }
47
+ }
48
+ }
49
+
50
+ const variables: Map<string, string> = new Map();
51
+ variables.set("guild.name", guild.name);
52
+ variables.set("guild.id", guild.id);
53
+ variables.set("guild.memberCount", guild.memberCount.toString());
54
+
55
+ const allMembers = guild.members.cache.filter((m) => !m.user.bot);
56
+ const done: { clientId: string; done: number; failed: number }[] =
57
+ clientsInGuild.map((client) => ({
58
+ clientId: client.user?.id as string,
59
+ done: 0,
60
+ failed: 0,
61
+ }));
62
+
63
+ const getTotallDone = () => done.reduce((acc, cur) => acc + cur.done, 0);
64
+ const getTotallFailed = () =>
65
+ done.reduce((acc, cur) => acc + cur.failed, 0);
66
+ const isDone = () => getTotallDone() + getTotallFailed() === allMembers.size;
67
+
68
+ const getEmbed = () =>
69
+ new EmbedBuilder()
70
+ .setTitle("📬 Mass DM Status")
71
+ .setDescription(
72
+ `Total: ${allMembers.size}\nDone: ${getTotallDone()}\nFailed: ${getTotallFailed()}\nStatus: ${isDone() ? "✅ Done" : "⏳ In Progress"}`
73
+ )
74
+ .setColor(isDone() ? Colors.Green : Colors.Yellow)
75
+ .setTimestamp();
76
+
77
+ const statusMessage = await message.reply({
78
+ embeds: [getEmbed()],
79
+ });
80
+
81
+ let clientIndex = 0;
82
+ for (const member of allMembers.values()) {
83
+ if (!clientsInGuild[clientIndex]) clientIndex = 0;
84
+
85
+ const client = clientsInGuild[clientIndex];
86
+ const clientDone = done.find((d) => d.clientId === client.user?.id)!;
87
+ clientIndex = (clientIndex + 1) % clientsInGuild.length;
88
+
89
+ // Tạo nội dung riêng biệt mỗi user
90
+ const personalizedVariables = new Map(variables);
91
+ personalizedVariables.set("user.username", member.user.username);
92
+ personalizedVariables.set("user.id", member.user.id);
93
+ personalizedVariables.set("user.tag", member.user.tag);
94
+ personalizedVariables.set("user.avatar", member.user.displayAvatarURL());
95
+ personalizedVariables.set("user.mention", member.user.toString());
96
+ personalizedVariables.set("user.nickname", member.nickname ?? member.user.username);
97
+
98
+ let personalizedContent = content;
99
+ for (const [key, value] of personalizedVariables.entries()) {
100
+ personalizedContent = personalizedContent.replace(new RegExp(`{${key}}`, "g"), value);
101
+ }
102
+
103
+ try {
104
+ await client.users.send(member.user.id, {
105
+ content: personalizedContent,
106
+ embeds: embeds,
107
+ });
108
+ clientDone.done++;
109
+ } catch (error) {
110
+ console.error(`❌ Không gửi được đến ${member.user.tag}:`, error);
111
+ clientDone.failed++;
112
+ }
113
+
114
+ try {
115
+ await statusMessage.edit({
116
+ embeds: [getEmbed()],
117
+ });
118
+ } catch (err) {
119
+ console.error("Không thể cập nhật tiến độ:", err);
120
+ }
121
+
122
+ await new Promise((res) => setTimeout(res, 400)); // delay nhẹ tránh bị rate limit
123
+ }
124
+ },
125
+ };
Commands/ping.ts ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { ChatInputCommandInteraction, Message } from "discord.js";
2
+
3
+ export default {
4
+ data: {
5
+ name: "ping",
6
+ description: "Replies with Pong!",
7
+ toJSON() {
8
+ return { name: "ping", description: "Replies with Pong!" };
9
+ },
10
+ },
11
+ ownersOnly: false,
12
+ async execute(input: ChatInputCommandInteraction | Message) {
13
+ if (input instanceof Message) {
14
+ await input.reply("🏓 Pong! (prefix command)");
15
+ } else {
16
+ await input.reply("🏓 Pong! (slash command)");
17
+ }
18
+ },
19
+ };
Commands/play.ts ADDED
@@ -0,0 +1,100 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Message, EmbedBuilder } from 'discord.js';
2
+ import {
3
+ joinVoiceChannel,
4
+ createAudioResource,
5
+ AudioPlayerStatus,
6
+ } from '@discordjs/voice';
7
+ import { get } from 'https';
8
+ import { MusicQueue } from '../utils/MusicQueue';
9
+ import { queues } from '../index';
10
+ import type { Command } from '../types';
11
+
12
+ async function playSong(queue: MusicQueue, guildId: string) {
13
+ if (queue.songs.length === 0) {
14
+ queue.playing = false;
15
+ return;
16
+ }
17
+
18
+ queue.playing = true;
19
+ queue.currentSong = queue.songs.shift()!;
20
+
21
+ try {
22
+ const stream = await new Promise<Readable>((resolve, reject) => {
23
+ get(queue.currentSong.url, (res) => {
24
+ if (res.statusCode !== 200) return reject(new Error('Failed to fetch MP3'));
25
+ resolve(res);
26
+ }).on('error', reject);
27
+ });
28
+
29
+ const resource = createAudioResource(stream);
30
+ queue.player.play(resource);
31
+
32
+ queue.player.once(AudioPlayerStatus.Idle, () => playSong(queue, guildId));
33
+ } catch (error) {
34
+ console.error('Error playing mp3:', error);
35
+ queue.playing = false;
36
+ }
37
+ }
38
+
39
+ export default <Command>{
40
+ data: {
41
+ name: 'play',
42
+ description: 'Phát nhạc từ link .mp3',
43
+ toJSON() {
44
+ return { name: 'play', description: 'Phát nhạc từ link .mp3' };
45
+ },
46
+ },
47
+ ownersOnly: false,
48
+ async execute(message: Message, args: string[]) {
49
+ const guildId = message.guild?.id;
50
+ if (!guildId) return message.reply('❌ Lệnh này chỉ hoạt động trong server.');
51
+
52
+ const voiceChannel = message.member?.voice.channel;
53
+ if (!voiceChannel) {
54
+ return message.reply('❌ Bạn cần tham gia voice channel để phát nhạc.');
55
+ }
56
+
57
+ const query = args.join(' ').trim();
58
+ if (!query || !query.endsWith('.mp3')) {
59
+ return message.reply('❌ Vui lòng cung cấp một link `.mp3` hợp lệ.');
60
+ }
61
+
62
+ if (!queues.has(guildId)) {
63
+ queues.set(guildId, new MusicQueue());
64
+ }
65
+ const queue = queues.get(guildId)!;
66
+
67
+ const song = {
68
+ title: query.split('/').pop() || 'MP3 File',
69
+ url: query,
70
+ duration: 0,
71
+ thumbnail: '',
72
+ requestedBy: message.author.id,
73
+ };
74
+
75
+ queue.songs.push(song);
76
+
77
+ if (!queue.playing) {
78
+ if (!queue.connection) {
79
+ queue.connection = joinVoiceChannel({
80
+ channelId: voiceChannel.id,
81
+ guildId: guildId,
82
+ adapterCreator: message.guild!.voiceAdapterCreator,
83
+ });
84
+ queue.connection.subscribe(queue.player);
85
+ }
86
+ playSong(queue, guildId);
87
+ }
88
+
89
+ const embed = new EmbedBuilder()
90
+ .setColor('#00ff00')
91
+ .setTitle('🎵 Đã thêm vào hàng đợi')
92
+ .setDescription(`**${song.title}**`)
93
+ .addFields(
94
+ { name: 'Vị trí', value: queue.songs.length.toString(), inline: true },
95
+ { name: 'Người yêu cầu', value: `<@${song.requestedBy}>`, inline: true }
96
+ );
97
+
98
+ await message.reply({ embeds: [embed] });
99
+ },
100
+ };
Commands/reload.ts ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { ChatInputCommandInteraction, Message } from "discord.js";
2
+ import fs from "fs";
3
+ import path from "path";
4
+ import { commands } from "../index";
5
+
6
+ export default {
7
+ data: {
8
+ name: "reload",
9
+ description: "Tải lại toàn bộ lệnh từ thư mục Commands.",
10
+ toJSON() {
11
+ return {
12
+ name: "reload",
13
+ description: "Tải lại toàn bộ lệnh từ thư mục Commands.",
14
+ };
15
+ },
16
+ },
17
+ ownersOnly: true,
18
+ async execute(input: ChatInputCommandInteraction | Message) {
19
+ const dir = path.join(__dirname);
20
+ const newCommands = [];
21
+
22
+ try {
23
+ const files = fs.readdirSync(dir).filter((file) =>
24
+ file.endsWith(".ts") || file.endsWith(".js")
25
+ );
26
+
27
+ for (const file of files) {
28
+ if (file === "reload.ts" || file === "reload.js") continue;
29
+
30
+ const filePath = path.join(dir, file);
31
+ delete require.cache[require.resolve(filePath)];
32
+
33
+ const command = require(filePath).default;
34
+ if (command) newCommands.push(command);
35
+ }
36
+
37
+ commands.length = 0;
38
+ newCommands.forEach((cmd) => commands.push(cmd));
39
+
40
+ const replyText = `✅ Đã reload ${newCommands.length} lệnh.`;
41
+
42
+ if (input instanceof Message) {
43
+ await input.reply(replyText);
44
+ } else {
45
+ await input.reply({ content: replyText, ephemeral: true });
46
+ }
47
+ } catch (error) {
48
+ console.error(error);
49
+ if (input instanceof Message) {
50
+ await input.reply("❌ Lỗi khi reload command.");
51
+ } else {
52
+ await input.reply({ content: "❌ Lỗi khi reload command.", ephemeral: true });
53
+ }
54
+ }
55
+ },
56
+ };
Commands/skip.ts ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Message, EmbedBuilder } from 'discord.js';
2
+ import { MusicQueue } from '../utils/MusicQueue';
3
+ import { queues } from '../index';
4
+ import type { Command } from '../types';
5
+
6
+ export default <Command>{
7
+ data: {
8
+ name: 'skip',
9
+ description: 'Skip the current song',
10
+ toJSON() {
11
+ return { name: 'skip', description: 'Skip the current song' };
12
+ },
13
+ },
14
+ ownersOnly: false,
15
+ async execute(message: Message) {
16
+ const guildId = message.guild?.id;
17
+ if (!guildId) return message.reply('❌ This command can only be used in a server.');
18
+
19
+ const queue = queues.get(guildId);
20
+ if (!queue || !queue.playing || !queue.currentSong) {
21
+ return message.reply('❌ No song is currently playing!');
22
+ }
23
+
24
+ queue.player.stop();
25
+
26
+ const embed = new EmbedBuilder()
27
+ .setColor('#ff9900')
28
+ .setTitle('⏭️ Song Skipped')
29
+ .setDescription(`Skipped: **${queue.currentSong.title}**`);
30
+
31
+ await message.reply({ embeds: [embed] });
32
+ },
33
+ };
Commands/stop.ts ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Message, EmbedBuilder } from 'discord.js';
2
+ import { MusicQueue } from '../utils/MusicQueue';
3
+ import { queues } from '../index';
4
+ import type { Command } from '../types';
5
+
6
+ export default <Command>{
7
+ data: {
8
+ name: 'stop',
9
+ description: 'Stop playing and clear the queue',
10
+ toJSON() {
11
+ return { name: 'stop', description: 'Stop playing and clear the queue' };
12
+ },
13
+ },
14
+ ownersOnly: false,
15
+ async execute(message: Message) {
16
+ const guildId = message.guild?.id;
17
+ if (!guildId) return message.reply('❌ This command can only be used in a server.');
18
+
19
+ const queue = queues.get(guildId);
20
+ if (!queue || !queue.playing) {
21
+ return message.reply('❌ No music is currently playing!');
22
+ }
23
+
24
+ queue.songs = [];
25
+ queue.playing = false;
26
+ queue.currentSong = null;
27
+ queue.player.stop();
28
+ if (queue.connection) {
29
+ queue.connection.destroy();
30
+ queue.connection = null;
31
+ }
32
+
33
+ const embed = new EmbedBuilder()
34
+ .setColor('#ff0000')
35
+ .setTitle('⏹️ Music Stopped')
36
+ .setDescription('Stopped playing and cleared the queue.');
37
+
38
+ await message.reply({ embeds: [embed] });
39
+ },
40
+ };