LINE MessagingAPIで遊ぶ①動かしてみる

小栗です．半田市のLINEアカウントのお手伝いする関係でLINE Messaging APIを使ったら面白かったので紹介です．かなり雑ですが，ほぼ0から作っていく流れを確認できるようになっているかと思います．詳細はググればたくさん出てくると思います．
前半と後半に分かれていて，今回はとりあえず動かしてみる編です．

## TL;DL
- LINE MessagingAPI＋Firebase Cloud Functions
- 語彙力低めの返答をするbotを作ります
- Firestore, Cloud Storageは使いません
- 環境構築がほぼメインになってしまった

## 準備0：いるもの
- mac(を想定して書いています)
    - npmが動く環境
- Googleのアカウント
- LINEのアカウント
- クレカ

## 準備1：LINE開発者アカウントの取得
さすがにLINEアカウントは持っていると思うので，開発者アカウントを作りましょう．
https://developers.line.biz/ja/

すると，最初だとプロバイダを作れと言われると思いますが，個人開発なら自分の名前とかでOKです．
https://developers.line.biz/ja/docs/messaging-api/getting-started/

## 準備2：チャンネルの作成
プロバイダを作れたらそのプロバイダから新しいチャンネルを作ります．
「新規チャンネル作成 > Messagng API」から適当なチャンネル名や説明などを入れていきます．これができると晴れてチャンネルを作ることができました．
ちなみに，僕は
チャンネル名「SLB」(Simple LINE Botの略)
チャンネルの説明「ボットの動作サンプル」
というような感じで作成しました．

<img width="1024" alt="スクリーンショット 2019-12-12 0.32.51.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271765/81e72bdf-1c35-b5bb-e898-303758c8912c.png">


ここで，Botを作る時に必要な2つのキーを取得します．

1つ目
チャンネル基本設定＞チャンネルシークレット

2つ目
Messaging API設定＞チャンネルアクセストークン（ロングターム）
[発行]を押すと生成されます

この2つをどこかにメモっておいてください．あとで使います．

とりあえず，自分が作ったアプリと友達になってみましょう．

「Messaging API設定＞QRコード」のQRコードをLINEから読めば，作ったチャンネルと友達になることができます．

<img width="414" alt="IMG_E2F91E2DF1DE-1.jpeg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271765/ecb4fb74-a753-d9f4-8a3d-d1d2a8e04e28.jpeg">

残念ながらこの状態では，定形文が返ってくるだけです．

## 準備3：Firebase準備
さすがにGoogleアカウントは持っていると思いますが，FirebaseでLINEアプリ動かすためにはクレカの登録が必要になります．（登録に必要なだけでいきなり課金は発生しないので大丈夫）

https://firebase.google.com

使ってみる，からアカウントを認証して，新規のプロジェクトを作成してください．

<img width="1024" alt="スクリーンショット 2019-11-28 16.36.54.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271765/f39dc423-b6e5-c281-fb67-6872cf76ba04.png">

名前はなんでもOKです．

適当にぽちぽちしていけばプロジェクトが作成されます．

すると画面の左下の方に，「Spark 無料$0/月」と出ていると思います．
実は，無料プランだと外部のネットワークへの接続ができないのでLINE MessagingAPIを使うことができません．なのでアップグレードして，Blazeプランにする必要があります．

このBlazeプランはSparkプランの無料分以上を含んだ上で，それ以上使ったら従量課金に移行するプランです．料金表を見てもらうとわかりますが，Sparkプランでは月に12.5万回の呼び出しが可能です．この時点で1回の返答が1つの呼び出しと考えて良いので，単純計算すると毎日4166回以上の会話をしない限りは課金が発生しないので，お試しで使う分には十分でしょう．
Blazeプランにすれば最初の無料枠が200万回分もあるらしいからなお余裕感が伝わるだろうか．

## Firebase CloudFunctionsを使ってみる
まず，ターミナルからFirebaseを使えるようにしたいのでツールを入れます．
FirebaseにはFirebase-CLIと言うコマンドラインツールが提供されています．

まずはこれをインストールします

```bash
$ npm install -g firebase-tools
```

で，Googleアカウントでログインします．

```bash
$ firebase login
```
くわしいのはここをみてね　https://firebase.google.com/docs/cli?hl=ja

これで準備万端です．

プロジェクトを作っていきましょう．

```bash
#プロジェクト用のディレクトリを作成する．名前は適当です．
$ mkdir /適当な/場所/hogehoge
$ cd /適当な/場所/hogehoge

#Firebaseのプロジェクトとして初期化します．今回はFunctionsしか使いませんので，Functionsだけで十分です．（欲張りな人は全部チェックしてもOKですがDatabaseとFirestoreは似ているのでどちらかだけで良いと思います．）
$ firebase init
? Which Firebase CLI features do you want to set up for this folder? Press Space to select features, then Enter to confirm your choices. (Press <space> to select, <a> to toggle all, <i> to invert selection)
 ◯ Database: Deploy Firebase Realtime Database Rules
 ◯ Firestore: Deploy rules and create indexes for Firestore
 ◉ Functions: Configure and deploy Cloud Functions
 ◯ Hosting: Configure and deploy Firebase Hosting sites
 ◯ Storage: Deploy Cloud Storage security rules
 ◯ Emulators: Set up local emulators for Firebase features

#先ほどFirebaseで作ったプロジェクトを選びます
? Please select an option:
Use an existing project
? Select a default Firebase project for this directory:
hogehoge(hogehoge)

#言語はぶっちゃけJSでもTSでも良いですが個人的にはTSがおすすめ
? What language would you like to use to write Cloud Functions? 
TypeScript

#あとは適当にエンターぽちぽち押すと終わり
．．．

✔  Firebase initialization complete!

```

するとこんな感じのディレクトリ構造になるのではなかろうか

```bash
.
├── firebase.json
└── functions
    ├── node_modules
    ├── package-lock.json
    ├── package.json
    ├── src
    |   └── index.ts
    ├── tsconfig.json
    └── tslint.json
```

基本的にはindex.tsの中にfunctionsを書いていくことでOKとなる．

index.tsを編集してみよう
最初の状態はこんな感じだと思います．

```typescript
import * as functions from 'firebase-functions';

// // Start writing Firebase Functions
// // https://firebase.google.com/docs/functions/typescript
//
// export const helloWorld = functions.https.onRequest((request, response) => {
//  response.send("Hello from Firebase!");
// });
```

コメントアウトを外してみましょう
これは，何かしらのリクエストが来たら問答無用で```"Hello from Firebase!"```を返すようなhelloWorldという名前の関数を作っています．

```typescript
import * as functions from 'firebase-functions';

// Start writing Firebase Functions
// https://firebase.google.com/docs/functions/typescript

export const helloWorld = functions.https.onRequest((request, response) => {
 response.send("Hello from Firebase!");
});
```

ではこれをデプロイしてみましょう

プロジェクトのディレクトリのトップから
ターミナルで次のコマンドを実行してみます．こんなような感じになるはず．
デプロイには少々時間がかかります．

```bash
$ cd /適当な/場所/hogehoge
$ firebase deploy

=== Deploying to 'hogehoge'...

i  deploying functions
Running command: npm --prefix "$RESOURCE_DIR" run lint

> functions@ lint /適当な/場所/hogehoge/functions
> tslint --project tsconfig.json

Running command: npm --prefix "$RESOURCE_DIR" run build

> functions@ build /適当な/場所/hogehoge/functions
> tsc

✔  functions: Finished running predeploy script.
i  functions: ensuring necessary APIs are enabled...
✔  functions: all necessary APIs are enabled
i  functions: preparing functions directory for uploading...
i  functions: packaged functions (31.25 KB) for uploading
✔  functions: functions folder uploaded successfully
i  functions: creating Node.js 8 function helloWorld(us-central1)...
✔  functions[helloWorld(us-central1)]: Successful create operation. 
Function URL (helloWorld): https://us-central1-hogehoge.cloudfunctions.net/helloWorld

✔  Deploy complete!

Project Console: https://console.firebase.google.com/project/hogehoge/overview
```

デプロイできましたかね？
webブラウザからFirebaseのコンソールをみてみましょう
https://console.firebase.google.com

<img width="1024" alt="スクリーンショット 2019-12-12 2.50.30.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271765/3c76dff7-6fe6-6872-262d-6393f943a621.png">

helloWorldと言う名前の関数が登録されています．

ここで表示されているトリガーとなっているリクエストURLにアクセスしてみましょう

<img width="663" alt="スクリーンショット 2019-12-12 2.52.47.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271765/2fe791f0-24f6-76dc-f2b3-65e7d05dbdb9.png">

お！ちゃんと出てる．良いね．
これをbotにしていきます．

## Cloud FunctionsでMessaging APIを使う
さてやっと本題に近づいてきた．

じゃあ，新しく，```lineBot```という関数を作って，これがbotになるように作っていこう．

まずは最低限欲しいライブラリを入れていく．
プロジェクトのfunctionsディレクトリで実行するのに注意

```bash
$ cd /適当な/場所/hogehoge/functions
$ npm install @line/bot-sdk
$ npm install express
```

すると，functions/package.jsonというファイルのdependenciesの項目にこれらが追加されているはずだ．

さて先ほどのindex.tsにコードを書いていこう．
ここで，最初の方で作った2つのキーが必要になるぞ

```typescript
import * as functions from 'firebase-functions';
import * as express from 'express'
import * as line from '@line/bot-sdk'

// Start writing Firebase Functions
// https://firebase.google.com/docs/functions/typescript

export const helloWorld = functions.https.onRequest((request, response) => {
 response.send("Hello from Firebase!");
});

// LINE bot
const config = {
 channelSecret: "xxxxxxxxxxxxx......." ,//チャンネルシークレット
 channelAccessToken: "yyyyyyyyyyyyyyyyyyyyyyyyyyy......" //アクセストークン
};
const client = new line.Client(config);
const app = express();
app.post('/', line.middleware(config), (req, res) => {
 Promise.all(req.body.events.map(handleEvent))
     .then(() => res.status(200).end())
     .catch((err) => {
      console.error(err);
      res.status(500).end()
     })
});
//メッセージがきたら"何かきた"というメッセージを返す
async function handleEvent(event: any) {
    if (event.type === "message" && event.message.type === "text") {
        return client.replyMessage(event.replyToken, {type: "text", text: "何かきた"})
    }
}
export const lineBot = functions.https.onRequest(app);
```
実際に開発していくことを考えると，キーをコード内に直書きしていくのは好ましくない．GitHubにあげたもんにはすぐに警告されたりすると思うので，このコードをGitHubとかにあげちゃダメだぞ．

けど，とりあえず簡単に動かす分にはこれで十分．

できたら保存して，もう一度デプロイしてみましょう．

```bash
$ firebase deploy
```

Firebaseのコンソールから確認してみましょう

<img width="1024" alt="スクリーンショット 2019-12-12 3.34.52.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271765/df893f8c-7833-3163-d18a-cd8f6e3c0fea.png">

関数増えてればOKです

## LINEアカウントにwebhookの設定をする

さて，先ほど作ったlineBotと言う関数をLINE側に設定しましょう

LINEの開発者のコンソールを開いてください
https://developers.line.biz/console/

最初に作ったプロバイダ，チャンネルの中で
「Messaging API設定＞Webhook設定」
を編集します

<img width="943" alt="スクリーンショット 2019-12-12 3.40.35.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271765/0003773b-8995-6964-46a7-fe23af2182ae.png">

ここに，先ほど作ったlineBotの関数のURLを入れましょう

<img width="694" alt="スクリーンショット 2019-12-12 3.41.29.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271765/d7c09dd0-8270-006a-092f-4990c356a33f.png">

ちなみに，検証ボタンを押すとエラーが出てしまうけど，今回に関しては特に問題はない．

webhookの設定の少し下に「応答メッセージ」の項目があるので「編集」を押して応答メッセージを無効にする．この時，開発者ページのコンソールから，LINE公式アカウントマネージャーの設定に飛ばされるけどびっくりしなくても大丈夫．

<img width="787" alt="スクリーンショット 2019-12-12 4.22.31.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271765/9ef04462-5cef-c7b6-5d21-e37783e5cd04.png">

さて，これで設定ができた．

LINEでメッセージを送ってみよう

<img width="414" alt="IMG_D51254ED5EAC-1.jpeg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271765/20949955-8d98-d576-f557-007281afc578.jpeg">

これでだいぶ雰囲気が掴めてきたと思う．

では先ほどのプログラムをもう少しだけ書き換えてみよう

```typescript
...
...
//メッセージがきたらランダムなメッセージを返す
async function handleEvent(event: any) {
    if (event.type === "message" && event.message.type === "text") {
        const replyMessage: string[] = ["それな", "ん？", "うける", "最&高", "映える", "じわる"];
        const randNum: number = Math.floor(Math.random() * Math.floor(replyMessage.length));
        return client.replyMessage(event.replyToken, {type: "text", text: replyMessage[randNum]});
    } else {
        return
    }
}
export const lineBot = functions.https.onRequest(app);
```

かけたらまたデプロイしてみよう

```bash
$ firebase deploy
```

LINEしてみよう

<img width="414" alt="IMG_17E21C21752E-1.jpeg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271765/f095e93b-637d-dde1-0a89-9db1f1b1be72.jpeg">

動きました？

コードの中にある```event.type```や```event.message.type```, ```event.message.text```あたりの中身を見て，返答を変えればもう会話できちゃうよね！

いろいろ自分で作ってみてね

## 最後に

今回はともかくシンプルにやることを第一にまとめたので，コードのまとめ方やらキーの管理方法が雑なので注意です，これを気にガッツリやってみる気持ちになったひとはぜひ調べてみてください．

誰かの興味のきっかけになれば幸いです．