Idiomatic Scala wrapper for the Telegram Bot API.
The full API is supported: Payments, inline queries, upload files, callbacks, custom markups, games, chat actions... while being strongly-typed, fully asynchronous, and transparently camelCased.
Add to your build.sbt
file:
libraryDependencies += "info.mukel" %% "telegrambot4s" % "2.9.5"
Do not expose tokens unintentionally.
Here's an example that avoids 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 file.
lazy val token = scala.util.Properties
.envOrNone("BOT_TOKEN")
.getOrElse(Source.fromFile("bot.token").getLines().mkString)
on("/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; refer to official payments documetation for details. I'll support developers willing to integrate and/or improve the payments API; please report issues here.
Beside the usual ways, I've managed to use the library on a Raspberry Pi 2, and most notably on an old Android (4.1.2) phone with a broken screen. It's also possible to docker-ize a bot.
Contributions are highly appreciated, documentation improvements/corrections, idiomatic Scala, bug reports, even feature requests.
- Alexey Alekhin
- Andrey Romanov
- Dmitry Kurinskiy
- ex0ns
- hamidr
- hugemane
- Juan Julián Merelo Guervós
- Kirill Lastovirya
- Maxim Cherkasov
- Onilton Maciel
- Pedro Larroy
- reimai
Just import info.mukel.telegrambot4s._, api._, methods._, models._, Implicits._
and you are good to go.
Implicits are provided to reduce boilerplate when dealing with the API; think seamless Option[T] and Either[L,R] conversions. Be aware that most examples need the implicits to compile.
import info.mukel.telegrambot4s.Implicits._
Get into the test console in sbt
sbt
[info] Loading project definition from ./telegrambot4s/project
[info] Set current project to telegrambot4s (in build file:./telegrambot4s/)
> test:console
[info] Compiling 10 Scala sources to ./telegrambot4s/target/scala-2.11/test-classes...
[info] Starting scala interpreter...
[info]
Welcome to Scala 2.11.8 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_101).
Type in expressions for evaluation. Or try :help.
scala> import info.mukel.telegrambot4s.examples._
import info.mukel.telegrambot4s.examples._
scala> new RandomBot("TOKEN_HERE").run()
Change RandomBot
to whatever bot you find interesting here.
object LmgtfyBot extends TelegramBot with Polling with Commands {
def token = "TOKEN"
on("/lmgtfy") { implicit msg => args =>
reply(
"http://lmgtfy.com/?q=" + URLEncoder.encode(args.mkString(" "), "UTF-8"),
disableWebPagePreview = true
)
}
}
LmgtfyBot.run()
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="
on("/speak") { implicit msg => 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 WebhookBot extends TelegramBot with Webhook with Commands {
def token = "TOKEN"
override val port = 8443
override val webhookUrl = "https://ed88ff73.ngrok.io"
import info.mukel.telegrambot4s.Implicits._
val rng = new Random(System.currentTimeMillis())
on("/coin", "head or tail") { implicit msg => _ =>
reply(if (rng.nextBoolean()) "Head!" else "Tail!")
}
on("/real", "real number in [0, 1]") { implicit msg => _ =>
reply(rng.nextDouble().toString)
}
on("/die", "classic die [1 .. 6]") { implicit msg => _ =>
reply((rng.nextInt(6) + 1).toString)
}
on("/dice", "throw two classic dice [1 .. 6]") { implicit msg => _ =>
reply((rng.nextInt(6) + 1) + " " + (rng.nextInt(6) + 1))
}
on("/random", "integer in [0, n)") { implicit msg => {
case Seq(Extractor.Int(n)) if n > 0 =>
reply(rng.nextInt(n).toString)
case _ =>
reply("Invalid argumentヽ(ಠ_ಠ)ノ")
}
}
on("/choose", "randomly picks one of the arguments") {
implicit msg => args =>
reply(if (args.isEmpty) "Empty list." else args(rng.nextInt(args.size)))
}
}
WebhookBot.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.
...
on("/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.