GoogleAppsScript で LINE WORKS のチャット BOT を作る

#### お知らせ
こちらはAPI 1.0対応の記事です。
最新のAPI 2.0対応バージョンの記事は[こちら](https://qiita.com/kunihiros/items/4cdec5784f21598cfee0)で解説しています。

---

独学で WEB API を勉強したいけど、自由に使えるサーバはない。
業務で検証作業を頼まれたけど、やっぱり自由に使えるサーバはない。
ngrok はとても便利なんだけど毎回 URL が変わるから常用はできない。
Heroku は無料なんだけど英語だし Git するのが面倒くさい。

そんなこと、ありませんか？　私は痛感しました（笑）

### それなら GoogleAppsScript を使えばいいじゃないか！

言語は何でもよくて、取り敢えず動作が見たい or 動作すればいい。
って言うのなら GoogleAppsScript がオススメです。
Google アカウントがあれば利用できるし、サーバも必要ない。
インターネット接続環境さえあればいつでも開発できる。
何といっても、**無料！！**
他にも色々と利点はありますので、興味のある方は他の方の記事を検索してみてください。

# LINE WORKS のチャット BOT を作る

このあと、作り上げるまでの経緯を書こうと思っているのですが、
「そんなことはどうでもいいから取り敢えず動かしたい！」
という方が多々いるはずなので、先に作り方を説明します。

## アカウント登録等の事前準備

まず、準備として以下のことをやっておきます。

1. Google アカウントの取得
2. LINE WORKS アカウントの取得
3. LINE WORKS Developr Console で以下の ID や Key を取得
    - API ID 
    - Server API Consumer Key
    - Server List(ID登録タイプ) の ID
    - Server List(ID登録タイプ) の認証キー
4. LINE WORKS Developr Console で Bot を登録して botNo を取得
5. LINE WORKS Admin（管理者画面）で Bot を追加

LINE WORKS については公式サイトを参考にしてください。
https://developers.worksmobile.com/jp/document/300130001?lang=ja

公式はわかり辛い！って人はこの方の記事も参考になりますよ。
[LINE WORKSで初めてのBot開発！(前編)](https://qiita.com/tokotan/items/f615f4a62219d655436f)

## 準備ができたら早速作る

サクサクっと作っていきましょう。

### 1. Google Spreadsheet に空のシートを作る

http://spreadsheet.google.com にアクセスして空のシートを作ります。
シート名はなんでも良いので「chat bot」としました。

### 2. スクリプトエディタを開く

「ツール」-「スクリプト エディタ」を選んでスクリプトエディタを開きます。

### 3. ライブラリを登録する

LINE WORKS の API を使用するにあたり、JWT の認証が必要になるのですが、この処理が非常に面倒だったので私の作ったライブラリを登録して使ってください。
自分でコード書きたい！って人は後ほど解説しますのでそちらを参照してください。

1. 「リソース」-「ライブラリ」をクリック
2. 「ライブラリを追加」の欄に `1aLcCr3CWqfenPMyM0_FWIDUgRcTxsit9bO6BTx61NCXrCtkY2zbHBlod` を入力して追加ボタンをクリック
3. 「LINEWORKS」ライブラリが追加されるので、一番新しいバージョンを選んで保存ボタンをクリック

### 4. コードを書く。

以下のコードを丸っとコピペしちゃってください。

```js
function doPost(e) {
  if (e == null || e.postData == null || e.postData.contents == null) return;
  var requestJSON = e.postData.contents;
  var requestObj = JSON.parse(requestJSON);
  var sendMsg = requestObj.content.text; // 応答メッセージ（今回はオウム返し）
  
  // LINE WORKS にメッセージを送信
  LINEWORKS.sendMsg(setOptions(), requestObj.source.accountId, sendMsg);
}

function setOptions(){
  return {
    "apiId" : "API ID",
    "consumerKey" : "Server API Consumer Key",
    "serverId" : "Server List(ID登録タイプ) の ID",
    "privateKey" : "Server List(ID登録タイプ) の認証キー",
    "botNo" : "botNo"
  };
}
```

`function setOptions()`　内の各パラメータには事前準備で取得した ID や Key を入力します。
認証キーについてですが、実際に見てもらうとわかる通りもの凄く長い上に変な改行が入っています。少々面倒なのですが、
`"-----BEGIN PRIVATE KEY-----\nMIIEvAIBADANBgkqhkiG9...\n-----END PRIVATE KEY-----"` となるように `\n` と `\n` の間に認証キーを入れて一行で表記します。

### 5. コードを公開する

完成したコードをウェブアプリケーションとして公開します。
 
1. 「公開」-「ウェブアプリケーションとして導入」をクリック
2. 出てきた画面で以下の設定をします。
    - プロジェクトバージョン ： NEW
    - 次のユーザーとしてアプリケーションを実行 ： Me(自分のアカウント名) ~~自分~~
    - アプリケーションにアクセスできるユーザー : Anyone, even anymous ~~全員（匿名ユーザーを含む）~~
3. 更新をクリック

このとき、Google からセキュリティの警告が出て許可を求めることがあります。
気にしない人はそのまま許可出しちゃってください。
気になる人は調べてから許可してください。

公開が完了すると「現在のウェブ アプリケーションの URL」が取得できます。
これが取得できれば、GoogleAppsScript 側の操作はおちまいです。

### 6. Bot に URL を登録する

LINE WORKS Developr Console で Bot 情報を修正します。
![SS002.png](https://qiita-image-store.s3.amazonaws.com/0/363367/9dac970b-203c-ffcc-5db6-0763c6be29bf.png)

1. Callback URL を ON にします。
2. 先ほど取得した「現在のウェブ アプリケーションの URL」を入力します。
3. テキストにチェックを入れます。
4. 保存をクリックします。

これで設定はすべて終わりです。

## チャット Bot が完成！

さぁ、Bot に話しかけてみましょう！hogehoge！

![SS001.png](https://qiita-image-store.s3.amazonaws.com/0/363367/8bb58798-8268-e320-bcfe-b6bcbbcbe6ce.png)

ちゃんとメッセージを返してくれましたね！本当に良かった。
意外と長くなってしまいましたが、いかがだったでしょうか？
参考になれば幸いです。


# さて、ここからが本番だ。
冒頭でも書きましたが、ここに辿り着くまでにつまづいたところなどを書いていきたいと思います。
私の愚痴なども多大に含まれておりますが、読み飛ばしてくださって大丈夫です。

さて、一番最初に恥を忍んで申し上げておかねばならぬことがあります。
実は、他の作業しながらとは言え、GoogleAppsScript × LINE WORKS のチャット Bot 作成、途中で詰まりに詰まってしまい、ここまで辿り着くのに３か月近くかかってしまいました！

もうね。途中、何度叫んだことか！( ﾟДﾟ)
LINE@ × LINE WORKS とか FaceBook × LINE WORKS とか、
LINE@ × GoogleAppsScript とかはそれなりにスムーズにできたのにね！

それでは、思いっきり文体崩して書いていきたいと思います（遅

## まず、知見がほとんどない

GoogleAppsScript × LINE WORKS の知見、検索してみてもらえばわかるのですがほとんどありません！

なので、この方たちの記事を参考に制作を試みました。
[LINE WORKSボットへのメッセージをGoogle Spreadsheetだけで受ける](https://qiita.com/KIchiyanagi/items/bce6c260686f8b930eda)
[LINE WORKSで初めてのBot開発！(前編)](https://qiita.com/tokotan/items/f615f4a62219d655436f)


しかし、すぐに暗礁に乗り上げた。なぜかと言うと、

1. express モジュールがない
2. jsonwebtoken モジュールがない

そう、参考にした記事で作成している Bot は node.js を使用している。
GoogleAppsScript は Javascript をベースにしてはいるがまったく同じ SDK が使えるわけではない。
なんてこった。

## 仕方がないので公式サイト（英語）を漁る

Google 翻訳さんに頼りっぱなしの私は、英語サイトを読むのはもちろん苦手。
でも、そんなことは言ってられないので [GoogleAppsScript 公式サイト](https://developers.google.com/apps-script/reference/) を頑張って読み解きました。

拙い英語力と強力なサポート（Google 翻訳）、それから先輩エンジニアに泣きついて色々と調べたところ、express については [UrlFetchApp.fetch](https://developers.google.com/apps-script/reference/url-fetch/url-fetch-app) 関数を使ってどうにか乗り切れることが判明。

しかし、一筋縄でいかなかったのが jsonwebtoken のやつデスヨ。。。

## ミッション１　JWT 生成

先の参照記事を読んでいただくとわかるのですが、LINE WORKS API を GoogleAppsScript で利用しようとすると、どうしても JWT での認証が必要になる。
しかし JWT の公式 SDK が存在しないため自力で生成、認証しなければならない。

まずは JWT を生成するためにパラメータを BASE64 エンコードする必要がある。
これは [Utilities.base64Encode](https://developers.google.com/apps-script/reference/utilities/utilities#base64Decode(String,Charset)) という関数があったのでやってみたが、何度やっても同じ値しか出てこないという罠に引っかかった。

この罠の原因は、JSON 形式に変換してからエンコードしないといけないということ。base64Encode 関数の引数は、String なのだ。

故に、パラメータを JSON.stringify() してからエンコードする。

```js
function getJwtToken(serverId, privateKey){
  const header = Utilities.base64Encode(JSON.stringify({"alg":"RS256","typ":"JWT"}), Utilities.Charset.UTF_8);
  const claimSet = JSON.stringify({
    "iss": serverId,
    "iat": Math.floor(Date.now() / 1000),
    "exp": Math.floor(Date.now() / 1000 + 2000)
  });
  const encodeText = header + "." + Utilities.base64Encode(claimSet, Utilities.Charset.UTF_8).slice(0, -2);
  const signature = Utilities.computeRsaSha256Signature(encodeText, privateKey);
  const jwtToken = encodeText + "." + Utilities.base64Encode(signature).slice(0, -2);
  return jwtToken;
}
```

成功。
これで、ミッション1 JWT 生成 はクリアした。

## ミッション２　JWT 電子署名(signature)

次なるミッションは電子署名。ミッション１でエンコードしたテキストと、LINE WORKS の認証キーを使って RSA SHA-256 アルゴリズムで暗号化し、BASE64 エンコードせよ、ということだ。

こちらは公式サイトを探して [Utilities.computeRsaSha256Signature](https://developers.google.com/apps-script/reference/utilities/utilities#computeRsaSha256Signature(String,String)) 関数を発見した。

おかげで問題なく電子署名完了。なーんだ順調じゃん。
base64Encode して先に作成していた header とくっつけて JWT トークンっぽいものが完成。
ようし。これで LINE WORKS 認証サーバーにトークンリクエストしてみるぞ！

`{"message":"invalid param","detail":"grant_type is invalid","code":"11"}`

はっはー！エラーだってさ！(^_^)-☆
なになに？grant_type が無効…？
request の form に encodeURIComponent してのっけてるんだけど、何が悪いのか…
元のデータ、そのままコピペしてもダメっぽい。んー、どうしたものか。

#### そして数週間放置。

あれこれいじったり、調べたりしてもまったく進展せず。
もはや諦めて放置していました。
でもやっぱり GoogleAppsScript でできると非常に便利！
他の作業でレベルアップした俺ならば倒せるはずだ！(｀・ω・´)ｷﾘｯ

…まぁ、結果は惨敗だったけどさ( ﾟДﾟ)

でも、そこに救世主が現れたのです。

## Please Mr. Postman

![postman.jpeg](https://qiita-image-store.s3.amazonaws.com/0/363367/01da95c1-6e4e-1d62-7a81-f6776fbfc5db.jpeg)
他の作業をしてたときに、API の開発がはかどるよーって [Postman](https://www.getpostman.com/postman) というツールを紹介してもらいました。
凄い便利で助かるツールなのです。

んで、惨敗したあとにたまたま Postman で認証サーバーにトークンリクエストしたんですよね。
GoogleAppsScript だとレスポンスを見るのが結構面倒だったんで Postman にお願いしたんですが、なんとなんとトークンの作成、成功してるじゃないですか！！！！

（　＾ω＾）・・・あっれぇ？

ってことは、JWT 電子署名はうまくいっていて、JWT トークンの作成は成功していた。
しかし、その後の POST が失敗してたってことかな？？

じゃぁ、もう一度 UrlFetchApp.fetch() の記述と使い方を確認だ！

## ラストミッション　LINE WORKS 認証サーバーへの Token リクエスト

…んんん、どうやら payload の記述が良くなかったみたいだな。
修正前のコードは恥ずかしくてお見せできないので完成したコードをば(*ﾉωﾉ)

```js
function getServerToken(apiId, jwtToken) {
  const uri = 'https:authapi.worksmobile.com/b/' + apiId + '/server/token';
  const payload = {
    "grant_type" : encodeURIComponent("urn:ietf:params:oauth:grant-type:jwt-bearer"),
    "assertion" : jwtToken
  };
  const options = {
    'method': 'post',
    'headers': {'Content-Type' : 'application/x-www-form-urlencoded'},
    "payload": payload
  };
  const body = UrlFetchApp.fetch(uri, options);
  return JSON.parse(body).access_token;
}
```

修正して実行したらエラー内容が変わって、コンシューマキーが間違ってるよって内容に！やった！これで次のステップに進めるぞー♪(*´▽｀*)

発行してからずっとエラーしまくってて、ずいぶん時間が経ってるからね！
そりゃもう有効期限切れだよね！
とか思ったけど、さっき Postman でやったときは普通にトークン発行されてたぞ…？あれ？( ﾟДﾟ)
いろいろ見直してみたが、さっぱりわからない。むむむ…どうしたものか。

しかし、よくよく考えてみたら、発行したのが昔過ぎて、API ID まで古いものを使用していたことが判明。なんてこった！
Postman ではコンソール画面からコピーしたからな。最新だよな。
すんごい凡ミスですが、エラーなんてだいたい自分の見落としなのデスヨ。。。

でも、ようやくトークンが発行でき、LINE WORKS の API が Google Apps Script で利用できるようになりました！
これで、開発、検証作業もぐっと楽になるぞ！やったね！(*´▽｀*)



# おわりに

ここまでお付き合いいただきありがとうございました。
これが Qiita での初投稿になります。
今までは読むばかりで助けられてばかりだったので、この記事で誰かの助けになれば嬉しいです。

次は何を書こうかな～。
ではまた！(^^)/

# 参考にさせていただきましたm(_ _)m

[GoogleAppsScript 公式サイト](https://developers.google.com/apps-script/reference/)
[LINE WORKS Developers](https://developers.worksmobile.com/jp/document?lang=ja)
[LINE WORKSボットへのメッセージをGoogle Spreadsheetだけで受ける](https://qiita.com/KIchiyanagi/items/bce6c260686f8b930eda)
[LINE WORKSで初めてのBot開発！(前編)](https://qiita.com/tokotan/items/f615f4a62219d655436f)
[LINE WORKSで初めてのBot開発！(後編)](https://qiita.com/tokotan/items/976d35ca56132e0bb5c1)