Idiomatic Scala wrapper for the Telegram Bot API
The full API is supported: Payments, inline queries, upload files, callbacks, custom markups, games, stickers, chat actions... while being strongly-typed, fully asynchronous, and transparently camelCased.
- Quick start
- Leaking bot tokens
- Webhooks vs Polling
- Payments
- Games
- Deployment (or how to turn a spare phone into a Telegram Bot)
- Usage
- A note on implicits
- Running the examples
- Examples
- Versioning
- Authors
- License
Add to your build.sbt
file:
libraryDependencies += "info.mukel" %% "telegrambot4s" % "3.0.9"
Don't ever expose your bot's token.
Here's how to avoid unintentional token sharing:
object SafeBot extends TelegramBot with Polling with Commands {
// Use 'def' or 'lazy val' for the token, using a plain 'val' may/will
// lead to initialization order issues.
// Fetch the token from an environment variable or untracked file.
lazy val token = scala.util.Properties
.envOrNone("BOT_TOKEN")
.getOrElse(Source.fromFile("bot.token").getLines().mkString)
onCommand('hello) { implicit msg => reply("My token is SAFE!") }
}
SafeBot.run()
Both methods are fully supported. Polling is the easiest method; it can be used locally without any additional requirements. It has been radically improved, doesn't flood the server (like other libraries do) and it's pretty fast.
Using webhooks requires a server (it won't work on your laptop). For a comprehensive reference check Marvin's Patent Pending Guide to All Things Webhook.
Payments are supported since version 3.0; refer to official payments documentation for details. I'll support developers willing to integrate and/or improve the payments API; please report issues here.
Games support comes in two different flavors, self-hosted (served by the bot itself), and external, hosted on e.g. GitHub Pages. Check both the self-hosted and GitHub-hosted versions of the popular 2048 game.
Beside the usual ways, I've managed to use run bots on a Raspberry Pi 2, and most notably on an old Android (4.1.2) phone with a broken screen.
Distribution/deployment is outside the scope of the library, but all platforms where Java is supported should be compatible (with the notable exception of Google AppEngine). You may find sbt-assembly and sbt-docker very useful.
Just import info.mukel.telegrambot4s._, api._, methods._, models._, declarative._
and you are good to go.
A few implicits are provided to reduce boilerplate, but are discouraged because unexpected side-effects.
Think seamless/scary T => Option[T]
conversion, Markdown string extensions (these are fine)...
Be aware that, for conciseness, most examples need the implicits to compile, be sure to include them.
import info.mukel.telegrambot4s.Implicits._
Clone this repo and get into the test console in sbt
sbt
[info] Loading global plugins from ~/.sbt/0.13/plugins
[info] Loading project definition from ~/telegrambot4s/project
[info] Set current project to telegrambot4s (in build file:~/telegrambot4s/)
[rootProject]> project examples
[info] Set current project to examples (in build file:~/telegrambot4s/)
[examples]> console
[info] Starting scala interpreter...
[info]
Welcome to Scala 2.12.3 (OpenJDK 64-Bit Server VM, Java 1.8.0_141).
Type in expressions for evaluation. Or try :help.
scala> new RandomBot("TOKEN").run()
Change RandomBot
to whatever bot you find interesting here.
Let me Google that for you! (full example)
object LmgtfyBot extends TelegramBot with Polling with Commands {
def token = "TOKEN"
onCommand("/lmgtfy") { implicit msg =>
withArgs { args =>
reply(
"http://lmgtfy.com/?q=" + URLEncoder.encode(args.mkString(" "), "UTF-8"),
disableWebPagePreview = true
)
}
}
}
LmgtfyBot.run()
Google TTS (full example)
object TextToSpeechBot extends TelegramBot with Polling with Commands with ChatActions {
def token = "TOKEN"
val ttsApiBase = "http://translate.google.com/translate_tts?ie=UTF-8&client=tw-ob&tl=en-us&q="
onCommand('speak, 'talk, 'tts) { implicit msg =>
withArgs { args =>
val text = args mkString " "
val url = ttsApiBase + URLEncoder.encode(text, "UTF-8")
for {
response <- Http().singleRequest(HttpRequest(uri = Uri(url)))
if response.status.isSuccess()
bytes <- Unmarshal(response).to[ByteString]
} /* do */ {
uploadingAudio // Hint the user
val voiceMp3 = InputFile("voice.mp3", bytes)
request(SendVoice(msg.source, voiceMp3))
}
}
}
}
TextToSpeechBot.run()
object RandomBot extends TelegramBot with Webhook with Commands {
def token = "TOKEN"
override val port = 8443
override val webhookUrl = "https://1d1ceb07.ngrok.io"
val rng = new Random(System.currentTimeMillis())
onCommand("coin", "flip") { implicit msg => reply(if (rng.nextBoolean()) "Head!" else "Tail!") }
onCommand("real") { implicit msg => reply(rng.nextDouble().toString) }
onCommand("die") { implicit msg => reply((rng.nextInt(6) + 1).toString) }
onCommand("dice") { implicit msg => reply((rng.nextInt(6) + 1) + " " + (rng.nextInt(6) + 1)) }
onCommand("random", "rand") { implicit msg =>
withArgs {
case Seq(Extractors.Int(n)) if n > 0 =>
reply(rng.nextInt(n).toString)
case _ =>
reply("Invalid argumentヽ(ಠ_ಠ)ノ")
}
}
onCommand("/choose", "/pick") { implicit msg =>
withArgs { args =>
reply(if (args.isEmpty) "Empty list." else args(rng.nextInt(args.size)))
}
}
}
RandomBot.run()
It's rather easy to augment your bot with custom DSL-ish shortcuts; e.g.
this authenticatedOrElse
snippet is taken from the AuthenticationBot
example.
...
onCommand("/secret") { implicit msg =>
authenticatedOrElse {
admin =>
reply(
s"""${admin.firstName}:
|The answer to life the universe and everything: 42.
|You can /logout now.""".stripMargin)
} /* or else */ {
user =>
reply(s"${user.firstName}, you must /login first.")
}
}
Check out the sample bots for more functionality.
This library uses Semantic Versioning. For the versions available, see the tags on this repository.
- Alfonso² Peterssen - Owner/maintainer - mukel
Looking for maintainers!
See also the list of awesome contributors who participated in this project. Contributions are very welcome, documentation improvements/corrections, bug reports, even feature requests.
This project is licensed under the Apache 2.0 License - see the LICENSE file for details.