【LINE Messaging API】みんなの LINE へプログラムからメッセージを送りたい

## 概要

- 友達登録したユーザに個別にメッセージを送りたい。
- プログラムからそれを行いたい。

![line18.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/270842/411f0a12-b2e4-78e8-21d1-d9b7c979ab5c.jpeg)

同じような内容のページはすでにネットにあります。だけれど自分が持った次のような疑問に、ストレートに応えるページがあってもいいかな、と思い、同じような疑問をもつ誰かに刺さるよう記事を書こうと思います。

## もった疑問

- 「LINE プログラムからメッセージ送信」と調べてみたけれど、 LINE Notify とか LINE公式アカウントとか LINE@ とか出てきてよくわからない。
- 送信をしたいだけなのに、ネットの記事をみると「さてサーバを用意しよう」と突然サーバが出てきて困惑。

ざっくり簡単に回答すると……

- 使うのは、 LINE公式アカウントを取得したあとに利用できる LINE Messaging API。
- 「送信する」のは自分のぱそこから(サーバなしで)出来るんだけれど、送信先を指定するためには送信先ユーザの ID が必要。それを取得するために、サーバが必要になってしまう。でも Heroku で無料で出来るから大丈夫。

## なぜ LINE Messaging API

「LINE Notify とか LINE公式アカウントとか LINE@ とか出てきてようわからん」に応えます。

- **LINE Notify**
    - 自分か自分の含まれるグループへメッセージを送れる。
    - プログラムから送れる。
    - しかし友達へ個別に送ることはできなさそう。
- **LINE公式アカウント**
    - 公式アカウントを作ると、友達登録をしたユーザに一斉送付が出来る。個人メッセもできる。
    - しかしそれらはすべてブラウザから手打ちで、自動で個人にメッセすることはできないみたい。
- **LINE@**
    - 2018年の終わりに公式アカウントに統合された。コイツのことはもう忘れていい。
    - 参考: [【重要】LINE@サービス統合のお知らせ](http://blog-at.line.me/archives/52626249.html)
- **LINE Messaging API**
    - 公式アカウントの中に含まれる機能。
    - やりたいことができる。

というわけで、まず LINE公式アカウントを作成して、それから LINE Messaging API を利用することになります。

### 料金

[LINE公式アカウントとは丨料金紹介](https://www.linebiz.com/lp/line-official-account/plan/)に載っています。たぶん、自動返信については無料でいくらでも使えて、 push 機能(能動的送信。今回やりたいことのゴール)については月に1,000通まで無料で使えます。


## これから作るものを図にして整理

図説するとこんな感じになります。図をみると、なんでサーバが必要になるのかわかるかと。

![line17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/270842/274bc51d-c500-10bf-1011-bf6e310cef04.png)


## チャネルを作成

各ユーザにメッセージを送るのは「LINE公式アカウント」ではない。「チャネル」なのです。

![line15.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/270842/dfdae51f-c652-0774-93be-4fdef3a0b1f2.jpeg)

この左側、会話相手は公式アカウントではなくチャネル。だからまず「LINE公式アカウント」を作って、その子要素の「プロバイダ」を作って、その子要素の「チャネル」を作ることになります。

- **LINE公式アカウント**
    - [https://developers.line.biz/](https://developers.line.biz/) からどうぞ。
- **プロバイダ**
    - 突然出てきた「プロバイダ」はアプリを提供する組織のことで、自分の名前や企業名を入力するものらしい。が、自分の名前は LINE公式アカウントに紐付いており自分の名前をここで使う違和感がある……。
- **チャネル**
    - LINE に表示させたい名前をつければいい。

### 新規チャネル作成まで

「LINE公式アカウント」と「プロバイダ」はすでに作った前提で進めてみます。

![line01.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/270842/aea83b4b-3db1-e53b-d848-0bb35fd6c212.jpeg)

![line02.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/270842/231106da-d588-bb62-8837-3c79c277f8f6.jpeg)

![line03.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/270842/6fe57e14-f3ff-2456-d14b-c2bab2441123.jpeg)

![line04.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/270842/5fa31ac8-bea5-f72a-cd28-de2d60a018d8.jpeg)

![line05.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/270842/a8bc7509-1181-e975-e090-9e7ecd2fabf4.jpeg)

![line06.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/270842/c8053f7a-eef9-ba17-bf76-2878fd13f161.jpeg)

![line07.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/270842/c0c357ff-4870-ce34-e20c-5a6df8d3edab.jpeg)

![line08.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/270842/de17e9e3-7983-4101-b625-bf3e2444b21c.jpeg)

チャネルを作り、友達登録をしてしまえば、この時点で [https://manager.line.biz/](https://manager.line.biz/) 上で会話ができます。

### トークン類の取得

チャネル基本設定から、「Channel Secret」と「アクセストークン」を取得します。

![line09.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/270842/ec2df19a-355f-6d07-c6d9-27cdc0925cc0.jpeg)

![line10.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/270842/17db57a4-6896-7bd6-e36b-5b9ee04a6611.jpeg)


## サーバを用意

上述したように、誰かにメッセージを自動送信するためにはそのユーザの ID が必要になります。

ユーザの ID は、一度ユーザからチャネル宛にメッセージを送ってもらい、そのときチャネルが自分のサーバに送信してくれる Json の中から取得できます。次は、サーバに配置するウェブアプリを Python で作成します。


### Python スクリプトを作成

自分の場合、 pipenv を使うのでこんなふうに環境を準備しています。

```bash
pipenv install flask line-bot-sdk gunicorn
```

Python スクリプトは my_flask_script.py って名前にしてみました。ベースのコードは [line-bot-sdk のドキュメント](https://github.com/line/line-bot-sdk-python#synopsis)から取得し、そこへ手を加えました。

```python:my_flask_script.py
from flask import Flask, request, abort

from linebot import (
    LineBotApi, WebhookHandler
)
from linebot.exceptions import (
    InvalidSignatureError
)
from linebot.models import (
    MessageEvent, TextMessage, TextSendMessage,
)

# 環境変数取得のため。
import os

# ログを出力するため。
import logging
import sys

app = Flask(__name__)

# ログを標準出力へ。heroku logs --tail で確認するためです。
# app.logger.info で出力するため、レベルは INFO にする。
app.logger.addHandler(logging.StreamHandler(sys.stdout))
app.logger.setLevel(logging.INFO)

# 大事な情報は環境変数から取得。
CHANNEL_ACCESS_TOKEN = os.environ['CHANNEL_ACCESS_TOKEN']
CHANNEL_SECRET = os.environ['CHANNEL_SECRET']

line_bot_api = LineBotApi(CHANNEL_ACCESS_TOKEN)
handler = WebhookHandler(CHANNEL_SECRET)


# 必須ではないけれど、サーバに上がったとき確認するためにトップページを追加しておきます。
@app.route('/')
def top_page():
    return 'Here is root page.'


# ユーザがメッセージを送信したとき、この URL へアクセスが行われます。
@app.route('/callback', methods=['POST'])
def callback_post():
    # get X-Line-Signature header value
    signature = request.headers['X-Line-Signature']

    # get request body as text
    body = request.get_data(as_text=True)
    app.logger.info('Request body: ' + body)

    # handle webhook body
    try:
        handler.handle(body, signature)
    except InvalidSignatureError:
        abort(400)

    return 'OK'


@handler.add(MessageEvent, message=TextMessage)
def reply_message(event):
    # reply のテスト。
    line_bot_api.reply_message(
        event.reply_token,
        TextSendMessage(text='こちらこーるばっく処理からお送りします:'+event.message.text))


if __name__ == '__main__':
    app.run()
```


### Heroku 用ファイル作成

```bash
# runtime.txt: Python のバージョンを記載。
echo python-3.7.4 > runtime.txt

# requirements.txt: 依存ライブラリの記載。
pip freeze > requirements.txt

# Procfile: プログラムの実行方法を記載。
echo web: gunicorn my_flask_script:app --log-file - > Procfile
```

Procfile は `web: python my_flask_script.py` ではなぜだか動きませんでした。Procfile の `--log-file` はログを吐くオプション。 `-` は stdout を指す。これを設定することでのちに `heroku logs --tail` によるログ確認ができます。


### Heroku へアップ

Heroku のアカウントはすでに持っているという前提でざくざく進めます。

```bash
# Git リポジトリ作成。
git init
# 個人的な趣味で、最初に空っぽのコミットを作成。
git commit --allow-empty -m "Initial Commit"
# 全ファイルをコミット。
git add --all
git commit -m "Add all files"

# 今回のアプリ名は line-messaging-py-py-py にしてみる。
heroku create line-messaging-py-py-py

# 環境変数を設定。
heroku config:set CHANNEL_ACCESS_TOKEN="チャネル基本設定のページからアクセストークンをコピーしてくる" --app line-messaging-py-py-py
heroku config:set CHANNEL_SECRET="チャネル基本設定のページから Channel Secret をコピーしてくる" --app line-messaging-py-py-py

# Heroku のリポジトリへアップ。
git push heroku master

# 途中でなにか失敗したら destroy で一度消し、 create からやりなおす。
# heroku apps:destroy --app line-messaging-py-py-py
```

トップページのメソッドも作ってあるので開いてみます。

![line11.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/270842/48054a6d-d329-229e-9e97-a3e015a2ccf8.jpeg)

アップが成功していることがわかりました。


## Heroku URL をチャネルへ登録

チャネル基本設定のページで Webhook URL を登録し、 Webhook送信 を有効にします。上の Python スクリプトで、 callback を受け付ける URL は `/callback` にしたので、今回の Webhook URL は `https://line-messaging-py-py-py.herokuapp.com/callback` になりますね。

これはよくわからないのだけれど、「接続確認」をすると赤文字が出て我々を不安にさせます。だがこのまま進んでも問題ありませんでした……。

![line12.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/270842/75d8c5da-8f3f-5ae7-3228-38be9248dfd6.jpeg)


## reply_message を使ってみる

チャネル基本設定のページの下の方に QR コードがあるので、そこからこのチャネルを友達登録します。

![line13.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/270842/3a170800-9a9e-f8e2-1732-374e2eceb8fc.jpeg)

![line14.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/270842/c2bad335-6280-d2fc-c6dc-929bc43bdc44.jpeg)

![line15.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/270842/dfdae51f-c652-0774-93be-4fdef3a0b1f2.jpeg)

Python で書いたコールバック処理を通過してメッセージが返されたことがわかります。いまはチャネルの設定がデフォルトなので色々自動返信されちゃっているけれど、それはのちのち編集したらいいですね。


## event と userId の内容

my_flask_script.py では、 `app.logger.info` によって、このスクリプトへ送られてきた情報を出力しています。 `heroku logs --tail` で確認できます。

```json
{
    "events": [
        {
            "type": "message",
            "replyToken": "********************************",
            "source": {
                "userId": "*********************************",
                "type": "user"
            },
            "timestamp": 1572247838104,
            "message": {
                "type": "text",
                "id": "**************",
                "text": "foo bar baz"
            }
        }
    ],
    "destination": "*********************************"
}
```

この中の `userId` はあとで push_message に使いたいので、控えておきます。コード内で取得したい場合、以下のように取得します。

```python
event.source.user_id
```


## push_message を使ってみる

今回のゴールですね。上で控えた「アクセストークン」と「userId」を使用すれば能動的にメッセージ送信ができます。もちろんこれは Heroku にアップする必要はなく、ローカルから試せます。

```python:push_message.py
from linebot import LineBotApi
from linebot.models import TextSendMessage

CHANNEL_ACCESS_TOKEN = '上で使った CHANNEL_ACCESS_TOKEN と同じ'
USER_ID = '上で控えた userId の値'

line_bot_api = LineBotApi(CHANNEL_ACCESS_TOKEN)

line_bot_api.push_message(
    USER_ID,
    TextSendMessage(text='ぷっしゅめっせーじです。やあ!'))
```

![line16.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/270842/661d1e1b-c394-9383-2b59-6500f96cdee7.jpeg)

このとおり、ユーザごとにユニークである `user_id` をおさえておればいつでもメッセージを送付することができることがわかります。おわり。長い記事になってしまった……。