feat(bot): add NodeJS implementation and deploy script

This commit is contained in:
2025-04-23 21:40:02 +08:00
parent 5c632556b7
commit 74dfdbf667
12 changed files with 704 additions and 0 deletions

View File

@@ -0,0 +1,98 @@
const logger = require('../utils/logger');
const { EmbedBuilder } = require('discord.js'); // Import EmbedBuilder
module.exports = (client) => {
if (!client || !client.manager) {
logger.error("ErelaEvents requires a client with an initialized manager.");
return;
}
client.manager
.on('nodeConnect', node => logger.info(`Node "${node.options.identifier}" connected.`))
.on('nodeError', (node, error) => logger.error(`Node "${node.options.identifier}" encountered an error: ${error.message}`))
.on('nodeDisconnect', node => logger.warn(`Node "${node.options.identifier}" disconnected.`))
.on('nodeReconnect', node => logger.info(`Node "${node.options.identifier}" reconnecting.`))
.on('trackStart', (player, track) => {
logger.info(`Track started in guild ${player.guild}: ${track.title} requested by ${track.requester?.tag || 'Unknown'}`);
// Find the text channel associated with the player (if stored)
const channel = client.channels.cache.get(player.textChannel);
if (channel) {
const embed = new EmbedBuilder()
.setColor('#0099ff')
.setTitle('Now Playing')
.setDescription(`[${track.title}](${track.uri})`)
.addFields({ name: 'Requested by', value: `${track.requester?.tag || 'Unknown'}`, inline: true })
.setTimestamp();
if (track.thumbnail) {
embed.setThumbnail(track.thumbnail);
}
channel.send({ embeds: [embed] }).catch(e => logger.error(`Failed to send trackStart message: ${e.message}`));
}
})
.on('trackEnd', (player, track, payload) => {
// Only log track end if it wasn't replaced (e.g., by skip or play next)
// 'REPLACED' means another track started immediately after this one.
if (payload && payload.reason !== 'REPLACED') {
logger.info(`Track ended in guild ${player.guild}: ${track.title}. Reason: ${payload.reason}`);
} else if (!payload) {
logger.info(`Track ended in guild ${player.guild}: ${track.title}. Reason: Unknown/Finished`);
}
// Optional: Send a message when a track ends naturally
// const channel = client.channels.cache.get(player.textChannel);
// if (channel && payload && payload.reason === 'FINISHED') {
// channel.send(`Finished playing: ${track.title}`);
// }
})
.on('trackError', (player, track, payload) => {
logger.error(`Track error in guild ${player.guild} for track ${track?.title || 'Unknown'}: ${payload.error}`);
const channel = client.channels.cache.get(player.textChannel);
if (channel) {
channel.send(`An error occurred while trying to play: ${track?.title || 'the track'}. Details: ${payload.exception?.message || 'Unknown error'}`).catch(e => logger.error(`Failed to send trackError message: ${e.message}`));
}
// Optionally destroy player or skip track on error
// player.stop();
})
.on('trackStuck', (player, track, payload) => {
logger.warn(`Track stuck in guild ${player.guild} for track ${track?.title || 'Unknown'}. Threshold: ${payload.thresholdMs}ms`);
const channel = client.channels.cache.get(player.textChannel);
if (channel) {
channel.send(`Track ${track?.title || 'the track'} seems stuck. Skipping...`).catch(e => logger.error(`Failed to send trackStuck message: ${e.message}`));
}
// Skip the track
player.stop();
})
.on('queueEnd', (player) => {
logger.info(`Queue ended for guild ${player.guild}.`);
const channel = client.channels.cache.get(player.textChannel);
if (channel) {
channel.send('Queue finished. Add more songs!').catch(e => logger.error(`Failed to send queueEnd message: ${e.message}`));
}
// Optional: Add a timeout before leaving the channel
// setTimeout(() => {
// if (player.queue.current) return; // Don't leave if something started playing again
// player.destroy();
// }, 180000); // 3 minutes
player.destroy(); // Destroy player immediately when queue ends
})
.on('playerCreate', player => logger.debug(`Player created for guild ${player.guild}`))
.on('playerDestroy', player => logger.debug(`Player destroyed for guild ${player.guild}`))
.on('playerMove', (player, oldChannel, newChannel) => {
if (!newChannel) {
logger.info(`Player for guild ${player.guild} disconnected (moved from channel ${oldChannel}). Destroying player.`);
player.destroy();
} else {
logger.debug(`Player for guild ${player.guild} moved from channel ${oldChannel} to ${newChannel}`);
player.setVoiceChannel(newChannel); // Update player's voice channel reference
}
});
logger.info("Erela.js event listeners attached.");
};