Hello I am trying to build a discord bot that can perform text to speech using AWS Polly in JavaScript. This is my first time using JavaScript and I'm running into issues. I'm hoping someone with more experience with discord.js/building discord bots/writing code in JavaScript can help me and provide insight on how to fix this.
Below is my entire source code:
var AWS = require('aws-sdk');
const Discord = require('discord.js');
const fs = require('fs');
var auth = require('./auth.json');
var m_currentPlaying = "";
const client = new Discord.Client();
const prefix = '$';
const Polly = new AWS.Polly({
region: 'us-east-1',
accessKeyId: auth.accessKey,
secretAccessKey: auth.secret
});
function getTTS(msg, text) {
var params = {
OutputFormat: "mp3",
Text: text,
TextType: "text",
VoiceId: "Brian"
};
console.log("processing command:" + text);
var fileName = 'sounds/' + msg.id + '.mp3';
Polly.synthesizeSpeech(params, (err, data) => {
if (err) {
console.log("Error getting from polly");
return err;
}
if (data.AudioStream instanceof Buffer) {
fs.writeFile(fileName, data.AudioStream, function (fsErr) {
if (fsErr) {
console.log("Error writing " + fileName);
return fsErr;
} else {
console.log(fileName + ' written sucessfully');
}
})
}
});
return fileName;
}
function deleteFile(fileName) {
fs.unlinkSync("./" + fileName, function(err){
if (err) {
console.log("failed to delete " + fileName);
console.log(err);
return;
}
console.log(fileName + ' successfully deleted');
});
}
function playSound(msg, fileName) {
var voiceChannel = msg.member.voice.channel;
voiceChannel.join()
.then(connection => {
m_currentPlaying = fileName;
const dispatcher = connection.play(fileName);
dispatcher.on("finish", end => {
voiceChannel.leave();
deleteFile(fileName);
m_currentPlaying = "";
});
})
}
client.on('ready', () => {
console.log(`Logged in as ${client.user.tag}!`);
});
client.on('message', async msg => {
if(!msg.content.startsWith(prefix) || msg.author.bot) return;
const args = msg.content.slice(prefix.length).split(/ +/);
const command = args.shift().toLowerCase();
if (command === 'tts') {
if (msg.member.voice.channel) {
var sentence = args.join(" ");
try {
var file = getTTS(msg, sentence);
console.log(file);
playSound(msg, file);
}
catch(err) {
msg.reply("There was an error with that command, I'm sorry");
console.log(err);
return;
}
}
}
else if (command === 'stop') {
if (msg.member.voice.channel)
{
console.log(m_currentPlaying);
msg.guild.voice.connection.dispatcher.destroy();
msg.guild.voice.connection.disconnect();
deleteFile(m_currentPlaying);
m_currentPlaying = "";
}
}
});
client.login(auth.token);
Right now the bot does the following:
- The bot starts up and connects to the discord api with my token stored in a json file
- It then waits for commands in the put into the discord that contain the prefix $
- If it sees the command '$tts' it will make sure the user is in a voice channel. It then will use the AWS polly API to create a mp3 file of what the user put in their message.
- Then it will join the discord voice channel and play the mp3 file. After the mp3 file is done playing it will leave the voice channel and delete the mp3 file with no issues.
- The "$stop" command also works with no issues. It will destroy the dispatcher currently playing the mp3, disconnect the bot, and delete the mp3 file created
The issue:
- Right now the bot works great and will join the voice channel, speak the tts message, delete the mp3 file. However what I want the code to do is I want the bot to join the voice channel and stay in the voice channel ready to receive commands and play mp3s.
- I want it to work the way rhythm bot works. Rhythm bot will hangout in your voice channel and eventually disconnect due to an inactive timer. But otherwise the bot will stay in the voice channel listening for more song requests. I want my bot to act the same way.
- I've tried writing my play function as follows:
function playSound(msg, fileName) {
var voiceChannel = msg.member.voice.channel;
if (!msg.guild.me.voice.channel) {
voiceChannel.join()
.then(connection => {
m_currentPlaying = fileName;
const dispatcher = connection.play(fileName);
dispatcher.on("finish", end => {
deleteFile(fileName);
m_currentPlaying = "";
});
})
}
else { // else already in a voice chat
m_currentPlaying = fileName;
console.log(fileName);
const dispatcher = msg.guild.voice.connection.play(fileName);
dispatcher.on("finish", end => {
deleteFile(fileName);
m_currentPlaying = "";
});
}
}
When I write the play function in this way I receive this error:
It seems like it is getting to the play() function before the fs.writeFileSync() is called and thus the file doesn't exist yet and I'm not really sure why. I think this could be an issue with synchronous vs asynchronous but I'm having trouble solving this bug. Thanks for anyone who takes the time to look through my code and provide insight