const { SlashCommandBuilder, EmbedBuilder, PermissionFlagsBits } = require('discord.js'); const { joinVoiceChannel, createAudioPlayer, createAudioResource, AudioPlayerStatus, VoiceConnectionStatus, entersState } = require('@discordjs/voice'); const googleTTS = require('google-tts-api'); const ttsSchema = require('../../Schemas/ttsSchema'); module.exports = { data: new SlashCommandBuilder() .setName('tts') .setDescription('Setup the tts and use it.') .addSubcommand(subcommand => subcommand .setName('set-channel') .setDescription('Set the channel for TTS commands') .addChannelOption(option => option .setName('channel') .setDescription('The channel to set for TTS commands') .setRequired(true) ) ) .addSubcommand(subcommand => subcommand .setName('speak') .setDescription('Speak a message in TTS') .addStringOption(option => option .setName('message') .setDescription('The message to speak') .setRequired(true) ) ), async execute(interaction) { const subcommand = interaction.options.getSubcommand(); if (subcommand === 'set-channel') { const channel = interaction.options.getChannel('channel'); await ttsSchema.findOneAndUpdate( { guildId: interaction.guild.id }, { guildId: interaction.guild.id, channelId: channel.id }, { upsert: true } ); const embed = new EmbedBuilder() .setColor('#00FF00') .setTitle('TTS Channel Set') .setDescription(`🔊 The TTS channel has been set to ${channel}`) .setTimestamp(); return interaction.reply({ embeds: [embed], ephemeral: true }); } if (subcommand === 'speak') { const ttsSetting = await ttsSchema.findOne({ guildId: interaction.guild.id }); if (!ttsSetting) { const embed = new EmbedBuilder() .setColor('#FF0000') .setTitle('TTS Channel Not Set') .setDescription('❌ The TTS channel has not been set. Use `/tts set-channel #channel` to set the TTS channel.') .setTimestamp(); return interaction.reply({ embeds: [embed], ephemeral: true }); } if (interaction.channel.id !== ttsSetting.channelId) { const embed = new EmbedBuilder() .setColor('#FF0000') .setTitle('Wrong Channel') .setDescription(`❌ You cannot use this command here. Please use it in <#${ttsSetting.channelId}>.`) .setTimestamp(); return interaction.reply({ embeds: [embed], ephemeral: true }); } const member = interaction.member; const channel = member.voice.channel; if (!channel) { const embed = new EmbedBuilder() .setColor('#FF0000') .setTitle('Not in Voice Channel') .setDescription('❌ You need to be in a voice channel to use this command.') .setTimestamp(); return interaction.reply({ embeds: [embed], ephemeral: true }); } const message = interaction.options.getString('message'); const audioURL = googleTTS.getAudioUrl(message, { lang: 'en', slow: false, host: 'https://translate.google.com', }); const connection = joinVoiceChannel({ channelId: channel.id, guildId: interaction.guild.id, adapterCreator: interaction.guild.voiceAdapterCreator, }); connection.on(VoiceConnectionStatus.Ready, () => { console.log('The bot has connected to the channel!'); }); connection.on(VoiceConnectionStatus.Disconnected, async (oldState, newState) => { try { await Promise.race([ entersState(connection, VoiceConnectionStatus.Signalling, 5_000), entersState(connection, VoiceConnectionStatus.Connecting, 5_000), ]); } catch (error) { console.log('Disconnected and failed to reconnect within 5 seconds:', error); connection.destroy(); } }); const player = createAudioPlayer(); const resource = createAudioResource(audioURL); player.play(resource); connection.subscribe(player); player.on(AudioPlayerStatus.Playing, () => { console.log('The bot is speaking...'); }); player.on(AudioPlayerStatus.Idle, () => { console.log('Finished playing!'); connection.destroy(); }); player.on('error', error => { console.error(`Error: ${error.message}`); connection.destroy(); }); const embed = new EmbedBuilder() .setColor('#00FF00') .setTitle('TTS Speaking') .setDescription(`🔊 Speaking the message: "${message}"`) .setTimestamp(); return interaction.reply({ embeds: [embed], ephemeral: true }); } } };