こんにちは @wezardnet です。
今年も Google I/O (2017) の時期がやってきました。残念ながら健康上の理由により参加を見送りましたが(チケット当選してないけど )、また機会があれば参加してみたいです。
さて、今回は Google Cloud Vision API & Google Cloud Translation API を使って LINE で画像を送ると、その画像に何が写っているかを返す bot を作ってみました。プラットフォームは GAE/Go です
LINE Messaging API + Google Cloud Vision API & Google Translate API を使って写真を送ったら解析結果を返す bot を作ってみた。プラットフォームは GAE/Go でござる。 #gcpja #gcpug pic.twitter.com/fAoU9yrUhM
— wezardnet (@wezardnet) 2017年5月10日
QiitaでTwitterのツイートを埋め込める時代は終わりました という事なのでサンプルアプリのスクショを貼り付けることにします ^^;
1. LINE bot を作る準備
はじめに LINE bot を作るには LINE BUSINESS CENTER で登録した後 LINE Developers で Messaging API を使えるようにします。このあたりの事前準備は他にたくさん解説記事がありますので、今さら説明するまでもありません。特に以下の記事を読むと良いでしょう
2. 中身(コード)について
LINE bot には SDK が用意されているので、こちらを使うと Messaging API を直に使うより楽ちんです。今回はプラットフォームに GAE/Go を使うので Go 用の line-bot-sdk-go を使います。ざっと見た感じ Go 以外には以下の言語の LINE bot SDK があるみたいです。
まずは必要なパッケージをインポートします。尚、コードは見づらくなるので一部省略してます。
ちなみにパッケージ管理は glide を使ってます。また Cloud Vision API と Cloud Translation API は独自のヘルパーパッケージにまとめてます。
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"os"
"bitbucket.org/{my project}/lib/helper"
"github.com/joho/godotenv"
"github.com/line/line-bot-sdk-go/linebot"
"github.com/zenazn/goji"
"google.golang.org/appengine"
"google.golang.org/appengine/log"
"google.golang.org/appengine/urlfetch"
)
私はウェブフレームワークに zenazn/goji を使っているので、ルーティングは次のようになります。
func init() {
m := goji.DefaultMux
m.Post("/line/callback", lineCallbackHandler)
}
LINE bot 側のメッセージを受け取る(フックする) URL を LINE Developers の Channel 設定画面で登録してあげます。
HTTP ハンドラ部分はおおむね line-bot-sdk-go の Example を真似して実装すれば OK です。注意点としては GAE を使う場合、外部 API を叩く場合は urlfetch を使わなければならない点でしょうか…
func lineCallbackHandler(c web.C, w http.ResponseWriter, r *http.Request) {
ctx := appengine.NewContext(r)
// bot 用の設定ファイルを読み込む
err := godotenv.Load("/line.env")
if err != nil {
log.Errorf(ctx, "LINE bot Setting file read error.: %v", err)
return
}
bot, err := linebot.New(
os.Getenv("LINE_BOT_CHANNEL_SECRET"),
os.Getenv("LINE_BOT_CHANNEL_TOKEN"),
linebot.WithHTTPClient(urlfetch.Client(ctx)),
)
if err != nil {
log.Errorf(ctx, "LINE bot new error.: %v", err)
return
}
// リクエストを受信する
events, err := bot.ParseRequest(r)
if err != nil {
if err == linebot.ErrInvalidSignature {
w.WriteHeader(http.StatusBadRequest)
} else {
w.WriteHeader(http.StatusInternalServerError)
}
return
}
for _, event := range events {
if event.Type == linebot.EventTypeMessage {
var replyMsg string = ""
switch message := event.Message.(type) {
case *linebot.TextMessage:
log.Infof(ctx, fmt.Sprintf("message.Text = %s", message.Text))
replyMsg = fmt.Sprintf("「%s」じゃねーよ。画像送ってくれよぉぉー", message.Text)
case *linebot.ImageMessage:
// 送信された画像を取得する
mc, err := bot.GetMessageContent(message.ID).Do()
if err != nil {
log.Errorf(ctx, "LINE bot get content error.: %v", err)
w.WriteHeader(http.StatusInternalServerError)
return
}
data, _ := ioutil.ReadAll(mc.Content)
vision, err := helper.NewVision(ctx, r)
if err != nil {
log.Errorf(ctx, "fatal new vision error.: %v", err)
w.WriteHeader(http.StatusInternalServerError)
return
}
// Cloud Vision API で画像を解析させる
ret, err := vision.AnnotateImage(data)
if err != nil {
log.Errorf(ctx, "fatal annotate image error.: %v", err)
w.WriteHeader(http.StatusInternalServerError)
return
}
translate, _ := helper.NewTranslate(ctx, r)
responses := ret.Get("responses")
for i := 0; i < len(responses.MustArray()); i++ {
annotations := responses.GetIndex(i).Get("labelAnnotations")
for j := 0; j < len(annotations.MustArray()); j++ {
annotation := annotations.GetIndex(j)
jdescription, _ := translate.TranslateEn2Ja(annotation.Get("description").MustString())
replyMsg += "お前が送ってきた画像だけど、『" + jdescription + "』だろ。おれの画像認識力を甘く見ちゃダメだぜw"
break
}
if replyMsg == "" {
replyMsg += "なんだこれ? (´・ω・`)"
}
break
}
}
if replyMsg != "" {
if _, err = bot.ReplyMessage(event.ReplyToken, linebot.NewTextMessage(replyMsg)).Do(); err != nil {
log.Errorf(ctx, "LINE bot Reply Message error.: %v", err)
}
}
}
}
}
Cloud Vision API で画像識別させた結果は複数の候補が返ってきますが、一番スコアが高いモノを採用して返すようにしてます。
また、テキストメッセージを受信した場合は、↓のような感じで画像を送ってもらうように催促しましょうw
3. あとがき
今回初めて LINE bot を作ったんですけど、サンプルが多いので意外と手軽に作れますね。実用的な bot を作るにはアイデア次第な気がします。ちなみに他にはどんな bot が作られているかは以下の記事を読むと良いでしょう。みなさん、おもしろい bot 作ってますね!