こちらの記事を参考にさせて頂き、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)
認証情報をサンプルにあるGoogleCredentialsではなく、ServiceAccountCredentialsで取得してるのは、gitにファイル形式で認証情報保存したくなかった(API Gateway経由で渡したかった)からです。line.pyと同じく未実装ですけど。
感想
LINE BOTはSlack的な便利さを多くのユーザに届けられる、ということになると思うので、すごく可能性を感じます。
社内に限ってだけでも、業務系でやってること、例えばAWSリソースのプロビジョニングだとか、アクセス状況の確認だとか、開発者がツールを駆使してやってたことが、LINEのインターフェースなら非開発者でもスイスイ使えるようにできる気がする。
とりあえず、次はVision APIのLABEL_DETECTIONで取得した結果からメッセージを面白おかしくする実装でも入れようかな!