Idiomatic Scala wrapper for the Telegram Bot API. The full API is supported, inline queries, callbacks, edit messages, games, custom markups, uploading files, chat actions... while being strongly-typed (no JSON strings), fully asynchronous (on top of Akka), and transparently camelCased.
Cross-compiled to Scala 2.11 and 2.12.
I encourage users to report any bug or broken functionality, I'll do my best to give proper support in a reasonable time frame.
- FSM handlers (on the works)
- Per-user synchronous requests (coming soon)
- Request feature here
Add to your build.sbt
file:
resolvers += Resolver.sonatypeRepo("snapshots")
libraryDependencies += "info.mukel" %% "telegrambot4s" % "2.2.1-SNAPSHOT"
I find Jitpack quite convenient, even if I don't officially support it.
To pull the latest and greatest directly from master (maybe unstable/breaking changes to the API), add to your build.sbt
file
scalaVersion := "2.11.8" // or 2.12.1
resolvers += "jitpack" at "https://jitpack.io"
libraryDependencies += "com.github.mukel" %% "telegrambot4s" % "master-SNAPSHOT"
When using Jitpack, make sure to specify Scala version in your build, inserting a line like the first one, or Jitpack will default to Scala 2.10 which is unsupported.
You can also pull any branch or previous release from Jitpack, check it out.
Do not expose tokens unintentionally.
In order to avoid unintentional token sharing, do as follows:
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. Polling has been radically improved, it doesn't flood the server 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.
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.FromByteString("voice.mp3", bytes)
request(SendVoice(msg.sender, 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.