LINE BOTからAWS Lambda介してGoogle Cloud Vision APIを呼ぶPython

[こちらの記事](http://qiita.com/dkawashi/items/4329d8ec085678b346e4
)を参考にさせて頂き、pythonでほとんど同じものを作ってみました。
LINE BOT、使ってみて初めてその面白さと可能性に気づきます。すごく楽しいし便利、これ！

トライアルが終わった後、いったいどうなるのか非常に気になりますが、まずは実装をば＾＾

## handler.py
```
# coding:utf-8
# !/usr/bin/python

import line
import json


def lambda_handler(event, context):
    print(json.dumps(event, indent=4, separators=(',', ': ')))
    content = event.get("result")[0].get("content") # 本当は複数resultきます。lambdaならとりあえずマルチスレッドで。（SQSに入れてもいいけど）
    line.set_return_text(content)
    line.send_to_line(content)
```

とりあえずAWS Lambdaの入り口。
eventにLINEからの情報が詰まったjsonをもらえるので、そこからレスポンスを作成してLINEサーバに送ります。

## line.py
```
# coding:utf-8
# !/usr/bin/python

import vision
import json
import os
import requests

CONTENT_TYPE_TEXT = 1  # Text message
CONTENT_TYPE_IMAGE = 2  # Image message
CONTENT_TYPE_VIDEO = 3  # Video message
CONTENT_TYPE_AUDIO = 4  # Audio message
CONTENT_TYPE_LOCATION = 7  # Location message
CONTENT_TYPE_STICKER = 8  # Sticker message
CONTENT_TYPE_CONTACT = 10  # Contact message

LINE_BOT_API_EVENT = 'https://trialbot-api.line.me/v1/events'
LINE_HEADERS = {
    'Content-type': 'application/json; charset=UTF-8',
    'X-Line-ChannelID': 999999999,  # Channel ID
    'X-Line-ChannelSecret': 'hogehoge',  # Channel secre
    'X-Line-Trusted-User-With-ACL': 'hogehoge'  # MID (of Channel)
}

def set_return_text(content):
    content_type = content.get("contentType")
    if content_type == CONTENT_TYPE_TEXT:
        content["text"] = u"'" + content.get("text") + u"' ですか、、それは難しい質問ですね。" + os.linesep + \
                          u"写真なら得意ですよ！"
    elif content_type == CONTENT_TYPE_IMAGE:
        image = get_message_content(content)
        content["text"] = vision.get_image_text(image)
    else:
        content["text"] = u"すいません、よくわかりません >_<" + os.linesep + \
                          u"写真なら得意ですよ！"
    content["contentType"] = CONTENT_TYPE_TEXT


def send_to_line(content):
    data = {
        'to': [content.get('from')],
        'toChannel': 1383378250, #FIX
        'eventType': "138311608800106203", #FIX
        'content': content
    };
    r = requests.post(LINE_BOT_API_EVENT, headers=LINE_HEADERS, data=json.dumps(data))
    print(r.content)


def get_message_content(content):
    url = 'https://trialbot-api.line.me/v1/bot/message/%s/content' % content.get("id")
    r = requests.get(url, headers=LINE_HEADERS)
    return r.content
```
LINE_HEADERに認証情報を詰め込みます。今はベタ書きですが、本当はAPIGatewayのrequestTemplateとかstageVariableあたりから認証情報埋め込むとリポジトリが汚れなくて良いですね。

写真が送られてきた場合、そのidから写真データをもらってきて、vision.pyに渡してレスポンスメッセージを取得します。

## vision.py
```
# coding:utf-8
# !/usr/bin/python

# Copyright 2016 Google, Inc
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import base64
import os

from googleapiclient import discovery
from oauth2client.service_account import ServiceAccountCredentials

DISCOVERY_URL = 'https://{api}.googleapis.com/$discovery/rest?version={apiVersion}'
GOOGLE_APPLICATION_CREDENTIALS = {
    "type": "service_account" # 色々省略。GoogleDeveloperConsoleから認証情報もらってきてください。
}

def get_image_text(image):
    request = get_vision_service().images().annotate(body={
        'requests': {
            'image': {
                'content': base64.b64encode(image)
            },
            'features': [
                {"type": "FACE_DETECTION", "maxResults": 5},
                {"type": "LABEL_DETECTION", "maxResults": 5},
                {"type": "TEXT_DETECTION", "maxResults": 5},
                {"type": "LANDMARK_DETECTION", "maxResults": 5},
                {"type": "LOGO_DETECTION", "maxResults": 5},
                {"type": "SAFE_SEARCH_DETECTION", "maxResults": 5}
            ],
            'imageContext': {
                'languageHints': [
                    "ja",
                    "en"
                ]
            }
        },
    })
    response = request.execute()

    annotation = response['responses'][0].get('safeSearchAnnotation')
    if annotation.get("adult") == "POSSIBLE" or annotation.get("adult") == "LIKELY" or annotation.get(
            "adult") == "VERY_LIKELY":
        return u"えっちなのはいけないと思います！"
    if annotation.get("medical") == "POSSIBLE" or annotation.get("medical") == "LIKELY" or annotation.get(
            "adult") == "VERY_LIKELY":
        return u"そういうのはちょっと、、"
    if annotation.get("spoof") == "POSSIBLE" or annotation.get("spoof") == "LIKELY" or annotation.get(
            "adult") == "VERY_LIKELY":
        return u"詐欺にかけようったってそうはいきません！？"
    if annotation.get("violence") == "POSSIBLE" or annotation.get("violence") == "LIKELY" or annotation.get(
            "adult") == "VERY_LIKELY":
        return u"あっ、暴力はダメです！ダメです！ぎゃー！"

    text = u""
    annotations = response['responses'][0].get('labelAnnotations')
    if annotations != None:
        text = text + u'多分こんな感じの写真だと思います。' + os.linesep
        for annotation in annotations:
            text = text + u'[ ' + annotation.get("description") + u' ]' + os.linesep
        text = text + os.linesep

    annotations = response['responses'][0].get('textAnnotations')
    if annotations != None:
        text = text + u"こんな文字が写ってますよね。" + os.linesep
        for annotation in annotations:
            text = text + u'[ ' + annotation.get("description") + u' ]' + os.linesep
        text = text + os.linesep

    annotations = response['responses'][0].get('faceAnnotations')
    if annotations != None:
        text = text + str(len(annotations)) + u"人写ってるのが分かりました！" + os.linesep
        count = 1
        for annotation in annotations:
            text = text + str(count) + u'人目は'
            if annotation.get("joyLikelihood") == "POSSIBLE" or annotation.get("joyLikelihood") == "LIKELY" or annotation.get("joyLikelihood") == "VERY_LIKELY":
                text = text + u"楽しそう!" + os.linesep
            elif annotation.get("sorrowLikelihood") == "POSSIBLE" or annotation.get("sorrowLikelihood") == "LIKELY" or annotation.get("sorrowLikelihood") == "VERY_LIKELY":
                text = text + u"悲しそう、、!" + os.linesep
            elif annotation.get("angerLikelihood") == "POSSIBLE" or annotation.get("angerLikelihood") == "LIKELY" or annotation.get("angerLikelihood") == "VERY_LIKELY":
                text = text + u"怒ってます？" + os.linesep
            elif annotation.get("surpriseLikelihood") == "POSSIBLE" or annotation.get("surpriseLikelihood") == "LIKELY" or annotation.get("surpriseLikelihood") == "VERY_LIKELY":
                text = text + u"驚いてる!!" + os.linesep
            elif annotation.get("underExposedLikelihood") == "POSSIBLE" or annotation.get("underExposedLikelihood") == "LIKELY" or annotation.get("underExposedLikelihood") == "VERY_LIKELY":
                text = text + u"あれ、露出不足ですかね。" + os.linesep
            elif annotation.get("blurredLikelihood") == "POSSIBLE" or annotation.get("blurredLikelihood") == "LIKELY" or annotation.get("blurredLikelihood") == "VERY_LIKELY":
                text = text + u"ピンボケだ >_<" + os.linesep
            elif annotation.get("headwearLikelihood") == "POSSIBLE" or annotation.get("headwearLikelihood") == "LIKELY" or annotation.get("headwearLikelihood") == "VERY_LIKELY":
                text = text + u"帽子かぶってます？" + os.linesep
            else:
                text = text + u"普通？" + os.linesep
            count = count + 1
        text = text + os.linesep

    annotations = response['responses'][0].get('landmarkAnnotations')
    if annotations != None:
        text = text + u"おっ、多分この場所ですよね！" + os.linesep
        for annotation in annotations:
            text = text + u'[ ' + annotation.get("description") + u' ]' + os.linesep
        text = text + os.linesep

    annotations = response['responses'][0].get('logoAnnotations')
    if annotations != None:
        text = text + u"あっ、知ってますよこのロゴ。" + os.linesep
        for annotation in annotations:
            text = text + u'[ ' + annotation.get("description") + u' ]' + os.linesep
        text = text + os.linesep

    print text
    return text


def get_vision_service():
    credentials = ServiceAccountCredentials.from_json_keyfile_dict(GOOGLE_APPLICATION_CREDENTIALS)
    return discovery.build('vision', 'v1', credentials=credentials,
                           discoveryServiceUrl=DISCOVERY_URL)

```

認証情報を[サンプル](https://github.com/GoogleCloudPlatform/cloud-vision/tree/master/python/text)にあるGoogleCredentialsではなく、ServiceAccountCredentialsで取得してるのは、gitにファイル形式で認証情報保存したくなかった（API Gateway経由で渡したかった）からです。line.pyと同じく未実装ですけど。

# 感想
LINE BOTはSlack的な便利さを多くのユーザに届けられる、ということになると思うので、すごく可能性を感じます。

社内に限ってだけでも、業務系でやってること、例えばAWSリソースのプロビジョニングだとか、アクセス状況の確認だとか、開発者がツールを駆使してやってたことが、LINEのインターフェースなら非開発者でもスイスイ使えるようにできる気がする。

とりあえず、次はVision APIのLABEL_DETECTIONで取得した結果からメッセージを面白おかしくする実装でも入れようかな！