FLINTERS Engineer's Blog

FLINTERSのエンジニアによる技術ブログ

Akka HTTP で LINE bot を作ってみました

こんにちは、Xiaoです。

最近Akka HTTPの正式版 X がで出ましたね。 自分は前からAkka HTTPに興味があって、ちょうどいい機会なので、Akka HTTPを詳しく勉強するため、簡単のWebサービスを作ってみたいと思います。

LINE Bot とは

LINE が提供する Messaging API により、LINEのトーク画面を使った対話型Botアプリケーションの開発が可能になります。

図

※ Image taken from developers.line.me

この図を説明すると、

  • サーバのURLを LINE Bot にするアカウントと繋ぎます。
  • ユーザがBotに対して、友たち追加、メッセージ送信などを行う際に、設定されたサーバにイベント情報が送られます。
  • サーバがイベント情報を基づいて、LINE Messaging API経由でユーザに返信できます。

※ LINE アカウントの設定について、LINEの公式ドキュメントに記載されているので、割愛します。

Akka HTTPについて

Akka HTTP は Akka Actor と Akka Stream をベースに作られた、HTTPサーバ/クライエント処理をサポートするためのツール、ライブラリです(フレームワークではありません!)。

LINE botを作るには、Akka HTTP みたいなツールが丁度良いと思います。

今回で作る Bot

今回は Akka HTTP の特性の紹介するため、一番シンプルな「受けたメッセージをそのまま返す」Botを例にして、Akka HTTP の特性を幾つか紹介したいと思います。

  • JSON を変換する ((un)Marshalling)
  • HTTP request を処理する
  • HTTP request を送る

最終のコードは こちら でたどり着けます。

TL;DR

JSON を変換する ((un)Marshalling)

LINE Messaging API とのやりとりは全部JSONを扱っています。これに対して、Akka HTTP の特徴の一つは、(un)Marshalling 機構によって、JSONXML或いはバイナリデータを簡単サポート出来ます。

◆ spray-jsonモジュールを使ため、Dependencyに"akka-http-spray-json"を追加。

"com.typesafe.akka" %% "akka-http-spray-json" % "10.0.0"

◆ 次は、スコープ内に RootJsonFormat[T] を用意する。

  • Case Class がある場合、要素の数に応じて、jsonFormatX メソッドを使えばいいです。
  • それ以外の場合、RootJsonFormat[T] を実装する必要があります。

※ 詳しい説明は spray-json を参照ください。

◆ 最後、Marshalling と Unmarshalling 機構を使うため、SprayJsonSupport を導入する。

Reply Message API のRequestをJSONに変換したい場合は、こんな感じです。

case class TextMessage(`type`: String = "text", text: String)

case class Messages(replyToken: String, messages: List[TextMessage])

trait MessagesJsonSupport extends SprayJsonSupport with DefaultJsonProtocol {
  implicit val textMessageFormat = jsonFormat2(TextMessage)
  implicit val messageFormat = jsonFormat2(Messages)
}

HTTP requestを処理する

ユーザがBotに対して、友たち追加、メッセージ送信などを行う際に、設定されたサーバにイベント情報が送られます。 HTTP サービスをシンプルに実装出来るため、Akka HTTP は High Level Routing DSL を提供しています。

こんな感じで使います。

def routes: Route = {
  path("line" / "callback") {
    (post & entity(as[Events])) { request =>
      
      ...

      complete {
        "OK"
      }
    }
  }
}

見た目の通り、今回は以下のDSLを使いました。

  • path("line" / "callback") はサービスのエンドポイントURLです。
  • (post & entity(as[Events])) は POST メソッドをマッチし、Requestデータを Events に変換します。
  • complete は処理成功を表します。

※ 詳しい説明は High-level Server-Side API を参照ください。

関連するコードは こちら で参照できます。

HTTP request を送る

ユーザに返信する場合、LINE の Reply Message API を経由で実現します。

Akka HTTPでは、こんな感じで書けます。

val auth = headers.Authorization(OAuth2BearerToken(accessToken))
val content = Messages(
  replyToken = token,
  messages = List(TextMessage(text = message))
)

val request = RequestBuilding.Post(
  uri = "https://api.line.me/v2/bot/message/reply",
  content = content
).withHeaders(auth)

val responseFuture: Future[HttpResponse] = Http().singleRequest(request)
  • Akka HTTPは、Authorization 機構を通じで、OAuth認証などをサポートしています。
  • RequestBuilding を使って、Requestを作成していますが、HttpRequest を直接実装するのもできる。
  • HTTPコネクションを管理したくない場合、一番簡単な Http().singleRequest を使います。

※ 詳しい説明は Consuming HTTP-based Services (Client-Side) を参照ください。

関連するコードは こちら で参照できます。

まとめ

今回はテキストベースのBotに必要のものを紹介しました。

後は、DBやAkka Presistenceを使えば、情報を保持出来るBotを作れるし、他の 3rd Party API に繋げば、いろんなサービスを提供することが出来ます。