結構ハマったんですけど、エコーボット作れたので記録します。
Swift愛好会合宿 2018/1/20-1/21で記事書いてます。 #love_swift
やっぱり言語勉強にBOTはいい題材な気がします。
環境構築
こっちにまとめました。
バージョンなどは
- Ubuntu 16.04
- Swift 4.0.3
- Vapor 2.4.0
となっています。
Package.swiftの準備
// swift-tools-version:4.0
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "webapp",
dependencies: [
// Dependencies declare other packages that this package depends on.
.package(url: "https://github.com/vapor/vapor.git", from: "2.4.0")
],
targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
// Targets can depend on other targets in this package, and on products in packages which this package depends on.
.target(
name: "webapp",
dependencies: ["Vapor"]),
]
)
main.swiftの編集
import Vapor
let drop = try Droplet()
let endpoint = "https://api.line.me/v2/bot/message/reply"
let accessToken = ""
drop.get("hello") { req in
print(req)
return "Hello Vapor!!!"
}
drop.post("callback"){ req in
print(req);
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")
}
print("-----------------");
print(message);
var requestData: JSON = JSON()
try requestData.set("replyToken", replyToken)
try requestData.set("messages", [
["type": "text", "text": message]
])
let response: Response = try drop.client.post(
endpoint,
query: ["name": "mybot"],
[
"Content-Type": "application/json",
"Authorization": "Bearer \(accessToken)"
],
requestData
)
print(response)
return Response(status: .ok, body: "reply")
}
try drop.run()
実行
$ swift build
$ ./.build/x86_64-unknown-linux/debug/webapp
これでおうむ返しのLINE BOTができました。
この辺をみると良さそう
ハマったポイントはHTTP Request
基本的にはVapor + LINE Messaging API で Bot を開発するを参考にしながら書いたのですが、
あとはこのリクエストをURLSessionに投げれば・・・と思ったのですが、UbuntuでURLSessionを使うと何故か正しく機能しないので(corelib-foundationのバグですかね?)、実際にはcurlをTask(Process)を使って叩くようにしています。Dropletのclientを使ってPOSTしてもダメだったのでよく分からない。
と参考記事にも書いてあった通り、URLRequest(url: URL(string: endpoint)!)
が動いてくれず、VaporのHTTP Clientのdrop.client.post()
なども調べたのですが、ドキュメントに書いてある記述だとエラーになり、Header情報を付加しないでのリクエストだと問題ないけどLINE BOTの場合はAuthorizationヘッダーにシークレット情報を詰め込む必要があったりと、やり方を検討するところでかなり時間がかかりました汗
ドキュメントに書いてある
try drop.client.get("http://some-endpoint/json", headers: [
"API-Key": "vapor123"
])
の記述はどうやらもう使えない(?)みたいで、 Server-Side-Swift VaporでAPIを作って学んだことまとめに書いてあるHTTPリクエストのやり方だとコンパイルエラーが出なかったです。
let response: Response = try drop.client.get("https://qiita.com/api/v2/items", query: [
"page": 1,
"per_page": 100,
"query": "user:hirothings"
], [
"Authorization": "Bearer hogehogehoge"
])
また、全くドキュメントに書いてなかったですが、Vaporでの実装例をGitHubで探して
https://github.com/FabrizioBrancati/SwiftyBot/blob/master/Sources/SwiftyBot/main.swift#L403
こちらのソースに行き着きました。どうやらPOSTリクエストのBODYは一番最後の引数になるようです。
ドキュメントではdrop.client.get(エンドポイント,ヘッダー)
といった呼び出し方でしたが、
実際は **drop.client.get(クエリ,ヘッダー,ボディ)
**という指定の仕方でした。
VaporのHTTP Clientの仕様が変わってるようでしたがドキュメントにも記載がないので大元のコードを探してたけどたどり着けず... 隣にいた@jollyjoesterさんに助けてもらい(ありがとうございました!)
https://github.com/vapor/engine/blob/master/Sources/HTTP/Request/Request.swift#L12-L17
にたどり着き確認できました。
public init(
method: Method,
uri: URI,
version: Version = Version(major: 1, minor: 1),
headers: [HeaderKey: String] = [:],
body: Body = .data([])
)
ログを見てたら2.0.0-bata.3
以降はこの仕様になっているようです。
ドキュメントが追いついてない可能性がたかそうですね..
ということでdrop.client.post()
はendpoint,query,header,body
の順番で引数を与えることで利用できます。
try drop.client.post(
endpoint,
query: ["name": "mybot"],
[
"Content-Type": "application/json",
"Authorization": "Bearer \(accessToken)"
],
responseData
)
何はともあれ、これで最新バージョンのServer Side SwiftでLINE BOTを作ることができました!
まだまだServer Side Swiftは調べてても日本語記事が全然出てこなく、発展途中感がハンパないですが興味のある人がいたら参考にしてみてもらえたら幸いです。