Googleスプレッドシートだけで作るFB messenger bot

## 概要
* Google Spreadsheet + Google Apps ScriptだけでFacebook Messenger botを作ります。
* サーバーを立てる必要はありません。しかも運用費用はゼロ円です。
* とりあえずコピペで動きます。操作するのはスプレッドシートだけなので、非エンジニアでもできます(たぶん)

## はじめに
### Google Spreadsheet/Google Apps Scriptとは
Google Spreadsheet(スプレッドシート)は、Google Drive上で使えるExcelみたいなものです。スプレッドシートにはGoogle Apps Script(通称GAS)というものがついていて、JavaScriptでいろいろと自動化できたり何かと便利です。

それに加えて、APIとして解放することで簡易サーバーのような動きもできます。今回はこの機能を利用して、SpreadsheetをFB Messenger botのバックエンドとして使い、botを構築してみます。

## 注意
**Google Apps for Workのアカウントだとできません。**個人アカウントを使いましょう。
後述する`doGet`/`doPost`が呼べないようです。

### GASを使うメリット
まず、無料です。これが一番。

あと、前回[Facebook Messenger Botの作り方　〜AWS Lambda(Serverless)での構築〜](http://qiita.com/KeitaMoromizato/items/98cae74d8c02d289c22e)という記事を書いたのですが、これはハードルが高いよと思う方にオススメです。本格的に運用するならAWSなりherokuなりをバックエンドとして使うべきですが、本格的なビジネス用途でなければスプレッドシート/GASで十分かなと思います。

そして、GASからはスプレッドシートのシートを簡単に操作できるので、例えばシートに「このテキストが来たら、これを返す」という定義を列挙できます。これはただのExcel的なものなので、エンジニアでなくても簡単に編集できます。

## 導入方法
### 1. Facebook Developerの設定
まず、[こちらの記事を参考に](http://qiita.com/KeitaMoromizato/items/98cae74d8c02d289c22e)、「1.Facebookページの作成・アプリの登録」の手順を行って下さい。そこまでの手順はバックエンドを何にしようが同じです。

発行されるページアクセストークンは後で使うので、忘れずにメモしておきましょう(**ブログなど人が見える所に公開しないように気をつけて下さい!**)

![ページアクセストークン - Facebook Developer](https://qiita-image-store.s3.amazonaws.com/0/52054/e2dc9f29-aeeb-b7f8-5519-b0b1364aa746.png)

### 2. GASの編集

まず、使用するスプレッドシートに、以下の画像のように行を追加します。　今回はA列に書いた単語にヒットしたとき、B列の単語を返すようにプログラムを組みました。つまりユーザーが「test」とメッセージを送ると、「てすとです」と返します。

![スプレッドーシートに行を追加](https://qiita-image-store.s3.amazonaws.com/0/52054/01259cf3-b575-268a-152e-3207626e4d5d.png)

次のステップで使うので、スプレッドシートのIDをメモしておきましょう。URLのランダム文字列になっている部分がIDです。

![スプレッドシートのID](https://qiita-image-store.s3.amazonaws.com/0/52054/b5e0f0ed-a02e-3848-3620-20310de971bf.png)

次に、[ツール] => [スクリプトエディタ]でGASの編集画面に移ります。

![スクリプトエディタの起動](https://qiita-image-store.s3.amazonaws.com/0/52054/e48e5dd3-8c5a-4138-3a3f-72549f22736c.png)

するとスクリプトの編集画面が出てくるので、そこに下記のコードをコピペします。変更する箇所は3つだけで、

* `PAGE_ACCESS_TOKEN`の値にFacebookDeveloperから取得したページアクセストークンを設定
* `SHEET_ID`に先ほどメモしたスプレッドシートのIDを設定
* `SHEET_NAME`に最初に行を追加したシートの名前(デフォルトなら`シート1`)を設定

```js

var PAGE_ACCESS_TOKEN = 'ここにFacebook Developerから取得したページアクセストークンを貼り付け';
var SHEET_ID = 'ここにスプレッドシートのIDを貼り付け';
var SHEET_NAME = 'シート１';

function doGet(e) {
  var validRequest = e.parameter['hub.verify_token'] === PAGE_ACCESS_TOKEN;
  
  var res = validRequest ? e.parameter['hub.challenge'] : 'Invalid Request';

  return ContentService.createTextOutput(res).setMimeType(ContentService.MimeType.TEXT);
}

function doPost(e) {
  var params = JSON.parse(e.postData.contents);
  var messagingEvents = params.entry[0].messaging;
  
  messagingEvents.forEach(function(event) {
    if (event.message && event.message.text) {
      sendHttpPost(event.sender.id, findResponse(event.message.text));
    }
  });

  return ContentService.createTextOutput("OK").setMimeType(ContentService.MimeType.TEXT);
}

function findResponse(word) {
  debug(word);
  return getData().reduce(function(memo, row) { return memo || (row.key === word && row.value); }, false) || undefined;
}

function getData() {
  var sheet = SpreadsheetApp.openById(SHEET_ID).getSheetByName(SHEET_NAME);
  var data = sheet.getDataRange().getValues();
  
  return data.map(function(row) { return {key: row[0], value: row[1]}; });
}

function sendHttpPost(sender, text) {
  UrlFetchApp.fetch('https://graph.facebook.com/v2.6/me/messages?access_token=' + PAGE_ACCESS_TOKEN, {
    method : 'POST',
    contentType : 'application/json',
    payload : JSON.stringify({
      recipient: {
        id: sender
      },
      message: {
        text: text || '見つかりませんでした'
      }
    })
  });
}

function debug(text) {
  var sheet = SpreadsheetApp.openById(SHEET_ID).getSheetByName('debug');
  sheet.appendRow([text]);
}

function test() {
  Logger.log(findResponse("hoge"));
}
```

ここまで出来たら保存しましょう。

![プロジェクトの保存 - スクリプトエディタ](https://qiita-image-store.s3.amazonaws.com/0/52054/b85de8aa-046d-4371-636d-cef283090667.png)

### 3. APIの公開

APIとして公開する前に、`test`関数を一度実行しておきましょう。その時に認証画面が表示されますが、**これを一度パスしておかないとAPIを実行しても謎のエラーになります。**気をつけましょう。

![test関数の実行 - スクリプトエディタ](https://qiita-image-store.s3.amazonaws.com/0/52054/4efb5520-a43c-5052-b900-d7e7edbd1e5f.png)

このような承認画面が出てくるので、[許可を確認]をクリックし、その後出てくる認証画面でもOKを選択します。

![承認画面 - スクリプトエディタ](https://qiita-image-store.s3.amazonaws.com/0/52054/787eded1-f893-c050-e2f3-6a1703f00488.png)

次はAPIとして公開する手順です。[公開] => [ウェブアプリケーションとして導入]を選択します。

![ウェブアプリケーションとして導入 - スクリプトエディタ](https://qiita-image-store.s3.amazonaws.com/0/52054/66bece0d-2a16-e71d-2568-0ae2baf5f770.png)

下記のような画面が出てくるので、[次のユーザーとしてアプリケーションを実行]を**自分**、[アプリケーションにアクセスできるユーザー]を**全員(匿名ユーザーを含む)**にして、[導入]をクリックします。

![ウェブアプリケーションの設定 - スクリプトエディタ](https://qiita-image-store.s3.amazonaws.com/0/52054/46088030-92ca-3310-7c02-540567025fff.png)

これにてスプレッドシート側の設定は終わりです。URLが発行されるので、これをメモしておきましょう。

![ウェブアプリケーションのURL - スクリプトエディタ](https://qiita-image-store.s3.amazonaws.com/0/52054/3f5d76a2-dcc8-6c01-2241-703cbe6b775e.png)

### 4.Messengerの設定

またFacebook Developersのページに戻り、Webhooksの設定画面から[Setup Webhooks]を選択します。

![Setup Webhooks - Facebook Developers](https://qiita-image-store.s3.amazonaws.com/0/52054/06b4bed1-d840-1954-1344-7a92205bc355.png)

[コールバックURL]の欄に先ほど発行されたスプレッドシートのAPIのURLを、[トークンを確認]の欄には最初に作ったページアクセストークンを設定します。[mesages]にのみチェックを入れて、[確認して保存]です。とくにエラーが出ずに終了したら完璧です。

![コールバックURLの設定 - Setup Webhooks](https://qiita-image-store.s3.amazonaws.com/0/52054/3bf36a67-9541-a977-6220-a8179942872b.png)

あとはFacebookページに対してメッセージを送れば、設定したものが返ってきます！！！

![Botのテスト](https://qiita-image-store.s3.amazonaws.com/0/52054/cdf314b8-3162-a2ea-f7ab-237ce3eb6d65.png)

## Tips
### doGet/doPost
GASの最初に書いてある`doGet()`と`doPost()`という関数が、APIとして外から呼ばれる関数です。引数の`e`にパラメータやら色々入ってきて、`ContentService.createTextOutput()`でレスポンスを返します。今回はFacebook Messenger Botの仕様上、テキストレスポンスを使用していますが、`ContentService.MimeType.JSON`を指定することでJSONを返すことも可能です。

### デバッグ関数
GASには`Logger.log()`というロギング関数がありますが、どうやらこれはdoGet()などが外部から呼ばれたときは記録されないようです(たぶん)。そのため、下記のようなデバッグ関数を作成して、ログを別シートに書きだして検証しました。

```
function debug(text) {
  var sheet = SpreadsheetApp.openById(SHEET_ID).getSheetByName('debug');
  sheet.appendRow([text]);
}
```

### 何でもできます
言ってもJavaScriptなので、割りとなんでもできます。`UrlFetchApp.fetch`でAjaxRequestも飛ばせますので外部サービスとの連携も可能です(こいつ同期で結果が返ってくるのですが...)。あとはGoogle CalenderとかGoogle系のサービスとの連携もGASのAPIを使うと簡単ですね。botで自分のカレンダーの予定を公開するとかも簡単だと思います(誰得)。

## 注意
* GASを更新したら、再度[ウェブアプリケーションとして導入]でプロジェクトバージョンを**新規作成して公開**してください。
* パフォーマンスは期待しないでください。遅めです。
* GASは実行制限(1日当たり何回まで実行可能、というやつ)あったはずです。詳細は忘れましたが...

## 参考
* [Messenger Platform - Getting Started](https://developers.facebook.com/docs/messenger-platform/quickstart)
* [Google Apps Scriptを使って簡易APIをサクッと作る](http://qiita.com/edo_m18/items/0519d241ac7683815a53)
* [Facebook Messenger Botの作り方　〜AWS Lambda(Serverless)での構築〜](http://qiita.com/KeitaMoromizato/items/98cae74d8c02d289c22e)

## レポジトリ
https://github.com/KeitaMoromizato/facebook-messenger-bot-on-google-spreadsheet