LINE Messaging APIが公開されて、面白そうだったので Swift Server Side Framework 「Vapor」 を使って実装してみました。
環境
- Sakura VPS Ubuntu 14.04
- Swift 3.0 Release
- Vapor 1.0.3
- Apache 2.4
LINE@の管理画面の使い方やMessaging APIの設定等については説明しません。
この記事では、Vaporを使ったBotアプリケーションの開発方法を紹介します。
とりあえず、送られてきたメッセージをそのまま返すだけのBotを作ってみましょう。
Vaporの基本的な使い方は https://vapor.codes/ を参照してください。
証明書の準備
Messaging API を 使用するサーバーはHTTPSに対応していないといけないっぽいので、Let's Encrypt等を使って証明書をサクッと準備します。Vaporはherokuへのデプロイも簡単にできるのでherokuでも良いでしょう。とりあえず私は、テキトーなVPSにテキトーなドメインとって証明書を発行してもらいました。
VaporのSSL周りの仕様がよくわからないのでとりあえずApacheのリバースプロキシでどうにかしました。
Vapor アプリケーションの作成
開発する環境はmacOSでもUbuntuでも良いですが、Xcodeが使えると便利なのでmacOSがオススメです。また、Swift Package Managerのプロジェクト構成も自分でやると面倒臭いのでVaporのToolboxを使用することをオススメします。
$ vapor new LineBot #SPMプロジェクト作成
$ cd LineBot
$ vapor xcode #Xcodeプロジェクト生成
webhookのハンドリング
Botへのイベントを受け取る先の実装をします。URLは何でも良いですが私は以下のようにしました。
drop.post("/callback"){ request in
// イベントを見て色々してレスポンス返す
}
送られてくるデータは「Webhook Event Object」というもので、https://devdocs.line.me/ja/#webhook-event-object に詳細なデータ構造が書かれています。
あまり詳しく見ていないですが、とりあえずはmessageのtextとreplyTokenさえ分かれば返信できそうなので取り出します。細かくイベントを判断して処理したいという場合は、ここのロジックをもう少し変える必要があります。
guard let object = req.data["events"]?.array?.first?.object else{
return Response(status: .ok, body: "this message is not supported")
}
guard let message = object["message"]?.object?["text"]?.string, let replyToken = object["replyToken"]?.string else{
return Response(status: .ok, body: "this message is not supported")
}
こんな感じ(もっと良い取り出し方ないですかね)
あとは、取り出したmessageとreplyTokenをAPIに投げるだけです。
Reply Message API を使う
次に Reply Message API を使って返信してみます。APIを使用するためには「Channel Access Token」が必要になるので取得しておきましょう。
Vapor Toolkitでプロジェクトを作成した場合、「Config/secrets」が.gitignoreに追加されているはずなのでsecretsディレクトリに「Channel Access Token」を記述したjsonファイルを配置してアプリケーションから取得すると良いでしょう。取得方法は、
let accessToken = drop.config["line_config", "access_token"]?.string
こんな感じです。
あとはAPIを呼ぶだけです。
let endpoint = "https://api.line.me/v2/bot/message/reply"
var request = URLRequest(url: URL(string: endpoint)!)
request.httpMethod = "POST"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("Bearer \(accessToken)", forHTTPHeaderField: "Authorization")
let payload: [String: Any] = [
"replyToken": replyToken,
"messages": [
["type": "text", "text": message]
]
]
request.httpBody = try? JSONSerialization.data(withJSONObject: payload, options: .prettyPrinted)
あとはこのリクエストをURLSessionに投げれば・・・と思ったのですが、UbuntuでURLSessionを使うと何故か正しく機能しないので(corelib-foundationのバグですかね?)、実際にはcurlをTask(Process)を使って叩くようにしています。Dropletのclientを使ってPOSTしてもダメだったのでよく分からない。
Bot と遊ぶ
友達に追加して、話しかけてみましょう。同じ内容のメッセージが返信されるはずです。
ソースコード
処理を色々整理したものをGithubにて公開しています。