levkhomich / scatebot

Spray-inspired framework for Telegram bot development

Version Matrix

Scatebot

Spray-inspired DSL for Telegram bot creation.

Key features:

  • strictly typed
  • fully asynchronous
  • supports full Telegram API except webhooks
  • schema code generation right from Telegram's Bot API page
  • small dependency footprint (json4s-jackson, akka-http are the ones needed)

Getting started

One example is better than a thousand words.

object BasicApp extends App {
  implicit val _ = ActorSystem()
  new TelegramBot("bot token") with BotDSL {
    // this route will handle incoming messages
    override def messageRoute(implicit ctx: MessageContext): MessageRoute = {
      message("/help") { // simple slash command
        client.request(SendChatAction(ctx.chatId, ChatAction.Typing)) // side effect
        Thread.sleep(1000) // this call will block whole route
        SendMessage(ctx.chatId, "I can't help you actually") // route result
      } ~
        (message("hi, bot") & fromUser) { user => // some text with user extractor
        Future { // async processing
          if (ctx.message.chat.`type` != ChatType.Private)
            None // do not respond in non-private chats
          else
            Some(SendMessage(ctx.chatId, s"Hi, *${user.firstName}*", parseMode = Some(ParseMode.Markdown)))
        }
      } ~ {  // raw API example
        if (ctx.message.game.isDefined) {
          // respond will finish routing, so directives below it won't be evaluated
          respond(SendChatAction(ctx.chatId, ChatAction.RecordVideo))
        } else {
          reject // means, that update was not handled here, so routing will continue
        }
      } ~ {
        // regex text extractors
        val doubleRegex = """-?\d+\.\d+""".r
        message("location " / doubleRegex ~ "," ~ doubleRegex) { case (latRaw, lonRaw) =>
          client.request(SendChatAction(ctx.chatId, ChatAction.FindLocation))
          // it will fail nicely here providing needed stacktrace
          // if unexpected values will be passed
          SendLocation(ctx.chatId, latRaw.toFloat, lonRaw.toFloat)
        }
      } ~
      messagePrefix("route nesting" /) { // textPrefix will match beginning of message
        message("case 1") { // this will match on "route nesting case 1"
          SendMessage(ctx.chatId, "case 1")
        } ~ { // this will match on everything else
          SendMessage(ctx.chatId, "and handle unmatched ones")
        }
      }
    }
  }.start()
}

See more info at directives package while more docs are being written.

Artifact

Use "com.github.levkhomich" %% "scatebot" % version for DSL,
or "com.github.levkhomich" %% "scatebot-api" % version if you only need API wrappers.

Latest stable version:
Maven Central
Latest snapshot.

WIP note

Please, be aware, that this framework is actively developed, thus, source compatibility between releases is not guaranteed yet. Any feedback is very much appreciated.