├── .gitignore ├── .eslintrc.js ├── package.json ├── LICENSE ├── index.js └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .idea 3 | test 4 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | env: { 5 | commonjs: true, 6 | es2021: true, 7 | node: true 8 | }, 9 | extends: 'eslint:recommended', 10 | parserOptions: { 11 | ecmaVersion: 12 12 | }, 13 | rules: { 14 | } 15 | }; 16 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "discord-tts", 3 | "version": "1.2.2", 4 | "description": "Node.js module to make your discord bot talk", 5 | "keywords": [ 6 | "discord", 7 | "bot", 8 | "discord bot", 9 | "text", 10 | "to", 11 | "speech", 12 | "text to speech", 13 | "discord-tts", 14 | "tts" 15 | ], 16 | "main": "index.js", 17 | "scripts": { 18 | "lint": "eslint --report-unused-disable-directives .", 19 | "test": "node test/index.js" 20 | }, 21 | "author": "https://github.com/mundoex", 22 | "repository": { 23 | "type": "git", 24 | "url": "git+https://github.com/mundoex/discord-tts.git" 25 | }, 26 | "license": "MIT", 27 | "dependencies": { 28 | "google-tts-api": "^2.0.2" 29 | }, 30 | "devDependencies": { 31 | "@discordjs/opus": "^0.7.0", 32 | "@discordjs/rest": "^0.1.0-canary.0", 33 | "@discordjs/voice": "^0.6.0", 34 | "@sapphire/snowflake": "^1.3.6", 35 | "discord-tts": "^1.2.2", 36 | "discord.js": "^13.2.0", 37 | "eslint": "^7.27.0", 38 | "libsodium-wrappers": "^0.7.9" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 mundoex 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | //@ts-nocheck 2 | /** 3 | * @typedef {( 4 | * "en" 5 | * "af-ZA"|"am-ET"|"hy-AM"|"az-AZ"|"id-ID"|"ms-MY"|"bn-BD"|"bn-IN"|"ca-ES"|"cs-CZ"|"da-DK"|"de-DE"| 6 | * "en-AU"|"en-CA"|"en-GH"|"en-GB"|"en-IN"|"en-IE"|"en-KE"|"en-NZ"|"en-NG"|"en-PH"|"en-SG"|"en-ZA"| 7 | * "en-TZ"|"en-US"|"es-AR"|"es-BO"|"es-CL"|"es-CO"|"es-CR"|"es-EC"|"es-SV"|"es-ES"|"es-US"|"es-GT"| 8 | * "es-HN"|"es-MX"|"es-NI"|"es-PA"|"es-PY"|"es-PE"|"es-PR"|"es-DO"|"es-UY"|"es-VE"|"eu-ES"|"fil-PH"| 9 | * "fr-CA"|"fr-FR"|"gl-ES"|"ka-GE"|"gu-IN"|"hr-HR"|"zu-ZA"|"is-IS"|"it-IT"|"jv-ID"|"kn-IN"|"km-KH"| 10 | * "lo-LA"|"lv-LV"|"lt-LT"|"hu-HU"|"ml-IN"|"mr-IN"|"nl-NL"|"ne-NP"|"nb-NO"|"pl-PL"|"pt-BR"|"pt-PT"| 11 | * "ro-RO"|"si-LK"|"sk-SK"|"sl-SI"|"su-ID"|"sw-TZ"|"sw-KE"|"fi-FI"|"sv-SE"|"ta-IN"|"ta-SG"|"ta-LK"| 12 | * "ta-MY"|"te-IN"|"vi-VN"|"tr-TR"|"ur-PK"|"ur-IN"|"el-GR"|"bg-BG"|"ru-RU"|"sr-RS"|"uk-UA"|"he-IL"| 13 | * "ar-IL"|"ar-JO"|"ar-AE"|"ar-BH"|"ar-DZ"|"ar-SA"|"ar-IQ"|"ar-KW"|"ar-MA"|"ar-TN"|"ar-OM"|"ar-PS"| 14 | * "ar-QA"|"ar-LB"|"ar-EG"|"fa-IR"|"hi-IN"|"th-TH"|"ko-KR"|"zh-TW"|"yue-Hant-HK"|"ja-JP"|"zh-HK"|"zh" 15 | * )} Language 16 | */ 17 | const googleTTS = require('google-tts-api'); // CommonJS 18 | const fs = require('fs'); 19 | const Stream = require('stream'); 20 | 21 | function base64toBinaryStream(base64Text){ 22 | // Convert base64 stream to binary stream 23 | const audioBinaryStream = new Stream.Readable(); 24 | audioBinaryStream.push(Buffer.from(base64Text, 'base64')); 25 | // Indicate end of stream 26 | audioBinaryStream.push(null); 27 | return audioBinaryStream; 28 | } 29 | 30 | /** 31 | * @param {string} text 32 | * @param {PlainObject} cfg 33 | * @param {Language} cfg.lang 34 | * @param {boolean} cfg.slow 35 | * @param {string} cfg.host 36 | * @param {number} cfg.timeout 37 | * @param {string} cfg.splitPunct 38 | */ 39 | function downloadFromInfoCallback(stream, text, {lang, slow, host, timeout, splitPunct}) { 40 | googleTTS.getAudioBase64(text, {lang, slow, host, timeout, splitPunct}) 41 | .then(base64Audio => base64toBinaryStream(base64Audio)) 42 | .then(audioStream => audioStream.pipe(stream)) 43 | .catch(console.error); 44 | } 45 | 46 | /** 47 | * @param {string} text 48 | * @param {Language} [lang="en-GB"] 49 | * @param {boolean} [slow=false] 50 | * @param {string} cfg.host 51 | * @param {number} cfg.timeout 52 | * @param {string} cfg.splitPunct 53 | */ 54 | function getVoiceStream(text, {lang = 'en', slow = false, host = 'https://translate.google.com', timeout = 10000, splitPunct} = {}) { 55 | const stream = new Stream.PassThrough(); 56 | downloadFromInfoCallback(stream, text, {lang, slow, host, timeout, splitPunct }); 57 | return stream; 58 | } 59 | 60 | /** 61 | * @param {string} filePath 62 | * @param {string} text 63 | * @param {PlainObject} cfg 64 | * @param {Language} [cfg.lang="en-GB"] 65 | * @param {number} [cfg.slow=false] 66 | * @param {string} cfg.host 67 | * @param {number} cfg.timeout 68 | * @param {string} cfg.splitPunct 69 | */ 70 | function saveToFile(filePath, text, {lang = 'en-GB', slow = false, host, timeout, splitPunct} = {}) { 71 | const stream = new Stream.PassThrough(); 72 | const writeStream = fs.createWriteStream(filePath); 73 | downloadFromInfoCallback(stream, text, {lang, slow, host, timeout, splitPunct }); 74 | stream.pipe(writeStream); 75 | stream.on('end', () => writeStream.close()); 76 | } 77 | 78 | module.exports.getVoiceStream = getVoiceStream; 79 | module.exports.saveToFile = saveToFile; -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # discord-tts 2 | Node.js module to make your discord bot talk 3 | 4 | ## Quick Example for discord.js v12 5 | ```js 6 | const secret = require('./secret.json'); //file with your bot credentials/token/etc 7 | const discord = require('discord.js'); 8 | const discordTTS = require('discord-tts'); 9 | const client = new discord.Client(); 10 | client.login(secret.token); 11 | 12 | client.on('ready', () => { 13 | console.log('Online'); 14 | }); 15 | 16 | client.on('message', msg => { 17 | if(msg.content === 'say test 123'){ 18 | const broadcast = client.voice.createBroadcast(); 19 | const channelId = msg.member.voice.channelID; 20 | const channel = client.channels.cache.get(channelId); 21 | channel.join().then(connection => { 22 | broadcast.play(discordTTS.getVoiceStream('test 123')); 23 | const dispatcher = connection.play(broadcast); 24 | }); 25 | } 26 | }); 27 | ``` 28 | ## Tested working with discord.js v12 29 | OS Windows 10 30 | Node.js v12.16.1 31 | discord.js v^12.2.0 32 | @discordjs/opus: github:discordjs/opus, 33 | ffmpeg v0.0.4 34 | ffmpeg-binaries v3.2.2-3 35 | opusscript v0.0.7 36 | 37 | ## Quick Example for discord.js v13 38 | ```js 39 | const secret = require('./secret.json'); //file with your bot credentials/token/etc 40 | const discordTTS=require("discord-tts"); 41 | const {Client, Intents} = require("discord.js"); 42 | const {AudioPlayer, createAudioResource, StreamType, entersState, VoiceConnectionStatus, joinVoiceChannel} = require("@discordjs/voice"); 43 | 44 | const intents= 45 | [ 46 | Intents.FLAGS.GUILD_VOICE_STATES, 47 | Intents.FLAGS.GUILD_MESSAGES, 48 | Intents.FLAGS.GUILD_MEMBERS, 49 | Intents.FLAGS.GUILDS 50 | ]; 51 | 52 | const client = new Client({intents:intents}); 53 | client.login(secret.token); 54 | 55 | client.on("ready", () => console.log("Online")); 56 | 57 | let voiceConnection; 58 | let audioPlayer=new AudioPlayer(); 59 | 60 | client.on("messageCreate", async (msg)=>{ 61 | if(msg.content=="tts") 62 | { 63 | const stream=discordTTS.getVoiceStream("hello text to speech world"); 64 | const audioResource=createAudioResource(stream, {inputType: StreamType.Arbitrary, inlineVolume:true}); 65 | if(!voiceConnection || voiceConnection?.status===VoiceConnectionStatus.Disconnected){ 66 | voiceConnection = joinVoiceChannel({ 67 | channelId: msg.member.voice.channelId, 68 | guildId: msg.guildId, 69 | adapterCreator: msg.guild.voiceAdapterCreator, 70 | }); 71 | voiceConnection=await entersState(voiceConnection, VoiceConnectionStatus.Connecting, 5_000); 72 | } 73 | 74 | if(voiceConnection.status===VoiceConnectionStatus.Connected){ 75 | voiceConnection.subscribe(audioPlayer); 76 | audioPlayer.play(audioResource); 77 | } 78 | } 79 | }); 80 | ``` 81 | ## Tested working with for discord.js v13 82 | OS Windows 10 83 | Node.js v16.8.0 84 | @discordjs/opus ^0.6.0 85 | @discordjs/rest ^0.1.0-canary.0 86 | @discordjs/voice ^0.6.0 87 | @sapphire/snowflake ^1.3.6 88 | discord-tts ^1.2.0 89 | discord.js ^13.2.0 90 | libsodium-wrappers ^0.7.9 91 | 92 | ## Contributing 93 | - Have [Git](https://git-scm.com/) Installed 94 | - Have [Node.js](https://nodejs.org/en/) Installed 95 | 96 | ```bash 97 | $ git clone https://github.com/mundoex/discord-tts.git 98 | $ npm install 99 | ``` 100 | Make a pull request with your changes
101 | Contributions, features request or any other kind of help are very welcome :) 102 | 103 | ## License 104 | [MIT](LICENSE) 105 | --------------------------------------------------------------------------------