LineBotで愛犬「もなかBot」を作ってみた

![Screenshot_20160408-151644.png](https://qiita-image-store.s3.amazonaws.com/0/16532/bf8a46b1-889f-66a7-224a-51e94ada3ad5.png)

LineBotやってみました。
お金が惜しいのでGAEで構築しました。
JavaはしんどいのでPython/Flask使いました。

## GCPでプロジェクト生成
はじめての方は、ぐぐると出てきます。

## app.yamlの準備

ソースはこんな感じ。

```app.yaml
runtime: python27
api_version: 1
threadsafe: yes

- url: .*  # This regex directs all routes to main.app
  script: main.app

libraries:
- name: ssl
  version: latest
```

最初、sslのライブラリを追加してなくて嵌まりました。

## callbackの実装

ユーザーがBotに対して喋ったりすると、callbackで指定したAPIが呼ばれます。
callbackの中でLineのAPI呼んでテキストや写真を送る感じです。
callbackの設定などはマニュアルや他のqiitaをご参照ください。

ソースはこんな感じ。

```main.py
# -*- coding: utf-8 -*-

import logging, random
import requests

from flask import Flask, request,Response

app = Flask(__name__)
app.config.from_object(__name__)
app.logger.setLevel(logging.DEBUG)

LINE_ENDPOINT = "https://trialbot-api.line.me"
HEADERS = {
    "X-Line-ChannelID": "<BasincInformation参照>",
    "X-Line-ChannelSecret": "<BasincInformation参照>",
    "X-Line-Trusted-User-With-ACL": "<BasincInformation参照>"
}

# 固定で返すメッセージ群
TALKS = [
    "わんわん！ごはんよこせ！！",
    "うんこ・・・したい・・・・",
    "しっこ！しっこ！",
    "飼い主どこ行ったのかな",
    "しのとも〜〜〜",
    "んぐ〜んぐ〜〜",
    "へっへっへっへっへっへっ",
    "Uo･ｪ･oU",
    "ペロペロペロペロ",
    "かいぬしの手をペロペロしたいなぁ",
    "ごはん落ちてないかな",
    "すやすやすやすや",
    "おらおらー！",
    "ダンス！ダンスダンス！！"
]

# 画像群
IMAGES = [
    {"origin": "https://storage.googleapis.com/linebot-1275.appspot.com/monaka1.jpg",
     "thumb": "https://storage.googleapis.com/linebot-1275.appspot.com/monaka1thum.jpg"}
]


@app.route("/")
def hello():
    return "line bot api"


@app.route("/callback", methods=["POST"])
def callback():
    # TODO Signature validation

    app.logger.info(request.json)
    app.logger.info(request.headers)
    req = request.json["result"][0]

    if req["eventType"] == "138311609100106403":
        """
        友だち申請の受信はここに来る。
        申請されたらすぐお礼メッセージを送る。
        TODO 未検証
        """

        send_text([req["from"]], u"もなかだよ。友だち申請ありがとわん。")

    elif req["eventType"] == "138311609000106303":
        """
        会話メッセージを受信したらここに来る。
        「もなか」と声をかけると写真とメッセージを返す。
        それ以外は準備したテキストをランダムで返す。
        """

        to = [req["content"]["from"]]
        if req["content"]["text"] == u"もなか":
            # 写真送付
            i = random.randint(0, len(IMAGES) - 1)
            send_picture(to, IMAGES[i])
            send_text(to, "よんだー？")
        else:
            # メッセージ送信
            i = random.randint(0, len(TALKS) - 1)
            send_text(to, TALKS[i])

    # 戻り値は200固定
    return Response(status=200)


def send_text(to, text):
    """
    toに対してテキストを送る
    """
    content = {
        "contentType": 1,
        "toType": 1,
        "text": text
    }
    events(to, content)


def send_picture(to, img):
    """
    toに対して画像を送る
    """
    content = {
        "contentType": 2,
        "toType": 1,
        "originalContentUrl": img["origin"],
        "previewImageUrl": img["thumb"]
    }
    events(to, content)


def events(to, content):
    """
    toに対してデータ(テキスト・画像・動画)を送る
    """
    app.logger.info(content)
    data = {
        "to": to,
        "toChannel": "1383378250",
        "eventType": "138311608800106203",
        "content": content
    }
    r = requests.post(LINE_ENDPOINT + "/v1/events", json=data, headers=HEADERS)
    app.logger.info(r.text)

```

## staticIPAddressの設定
サーバーがホワイトリスト制になっているので、IPAddressを設定しないとLineのサーバーはエラーを返します。しかしGAEなので固定IPAddressはありません。
試しにアクセスしたら 403 エラーになり、以下のメッセージが表示されました。

> {"statusCode":"427","statusMessage":"Your ip address [107.178.194.118] is not allowed to access this API."}

というわけで、このIPAddressを追加して動かすと

>{"statusCode":"427","statusMessage":"Your ip address [107.178.194.122, 10.128.141.149] is not allowed to access this API."}

次は内部IPAddressらしきものも設定しろよと出てきたので素直に設定します。

これで動きました。いつかIPAddressが変わって動かなくなる可能性が高いです。解決方法は未来の僕に委ねたいと思います。

## フレンド申請する
QRコードから出来るようです。（分からなくてずっと苦しんだ）
今回の僕の愛犬「もなかBot」は

![rjs9046g.png](https://qiita-image-store.s3.amazonaws.com/0/16532/42d6f2c5-72c3-9601-8dde-05718907b992.png)

から追加できます。