-
Notifications
You must be signed in to change notification settings - Fork 101
Create a bot
To create a bot/token follow these instructions. Add telegrambot4s to your build file.
The Google TTS API is very handy, but Google will rightfully block you if abused.
Let's start with the bot skeleton, and add features one by one.
object TextToSpeechBot extends TelegramBot
with Polling
with Commands
with ChatActions
with InlineQueries {
// Hardcoded tokens are discouraged.
// lazy val token = "110201543:AAHdqTcvCH1vGWJxfSeofSAs0K5PALDsaw"
lazy val token = scala.util.Properties
.envOrNone("BOT_TOKEN")
.getOrElse(Source.fromFile("bot.token").getLines().mkString)
def ttsUrl(text: String): String =
s"http://translate.google.com/translate_tts?client=tw-ob&tl=en-us&q=${URLEncoder.encode(text, "UTF-8")}"
}
/speak Education is a progressive discovery of our own ignorance.
Should return an audio transcription of the text.
onCommand('speak) { implicit msg =>
// Extracts command arguments (does not includes /command)
withArgs { args =>
val text = args.mkString(" ")
// Using Akka-Http to query the Google TTS API.
for {
response <- Http().singleRequest(HttpRequest(uri = Uri(ttsUrl(text))))
if response.status.isSuccess()
bytes <- Unmarshal(response).to[ByteString]
} /* do */ {
val voiceMp3 = InputFile("voice.mp3", bytes)
request(SendVoice(msg.source, voiceMp3))
}
}
}
Since uploading an audio file can be time-consuming, let's give the user some feedback:
Just mix ChatActions and use the uploadingAudio to warn the user.
...
uploadingAudio // hint the user about time-consuming operation
val voiceMp3 = InputFile("voice.mp3", bytes)
request(SendVoice(msg.source, voiceMp3))
...
}
Adding support for listening "previews" is way nicer than blindly download audio files over and over.
@MyAwesomeBot Glory is fleeting, but obscurity is forever.
Should spawn a preview of the audio. Since multiple results can be sent, having a fallback to the /speak command directly from inline mode is also possible.
Mix InlineQueries to have the declarative onInlineQuery method.
Is possible to use onInlineQuery directly but empty queries are problematic.
onInlineQuery { implicit iq =>
...
}
Note that inline queries must be "answered" even if the query is empty/unhandled.
The "whenOrElse" helper method provides a simple way to filter incoming queries/commands...
def nonEmptyQuery(iq: InlineQuery): Boolean = iq.query.nonEmpty
whenOrElse(onInlineQuery, nonEmptyQuery) {
implicit iq =>
answerInlineQuery(Seq(
// Inline "playable" preview
InlineQueryResultVoice("inline: " + iq.query, ttsUrl(iq.query), iq.query),
// Redirection to /speak command
InlineQueryResultArticle("command: " + iq.query, iq.query,
inputMessageContent = InputTextMessageContent("/speak " + iq.query),
description = "/speak " + iq.query)))
} /* empty query */ {
answerInlineQuery(Seq())(_)
}
The full source code can be found here: TextToSpeechBot