Upload 8 files
Browse files- Commands/eval.ts +42 -0
- Commands/help.ts +29 -0
- Commands/mass-dm.ts +125 -0
- Commands/ping.ts +19 -0
- Commands/play.ts +100 -0
- Commands/reload.ts +56 -0
- Commands/skip.ts +33 -0
- Commands/stop.ts +40 -0
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 |
+
};
|