GASのLINEボットで受け取った画像をGoogleドライブに保存する（+LINEボットについての雑記）

#はじめに
LINEボットの基礎的なアレコレは以下の記事が詳しいです。
> [Google Apps ScriptでLINE BOTつくったら30分で動かせた件](https://qiita.com/hakshu/items/55c2584cf82718f47464) ([@hakshu](https://qiita.com/hakshu)さん)

LINEボットを初めて作る方は、先にこちらを読んだ方が良いでしょう。手順やサンプルコードが簡潔にまとめられているので、LINEボット作成がどんなものかの理解に繋がると思います。

ただし上記記事をベースにコードを書く場合、サンプルコードの以下の部分:

```javascript
var userMessage = JSON.parse(e.postData.contents).events[0].message.text;
```
こちらは、

```javascript
var event = JSON.parse(e.postData.contents).events[0];
var userMessage = event.message.text || "";
```

```javascript
var event = JSON.parse(e.postData.contents).events[0];
if(event.message.type == "text") {
  var userMessage = event.message.text;
  ...
}
```
のように（あるいはもっと適した形に）書き直した方が良いです。本記事で示す通り、ボットが受け取るメッセージの`type`は`text`だけではないためです。…もっと詳しく言うと、`event.message`オブジェクトの中身は、そのtypeごとに異なります。

```type=textのときのevent.message
{
  type: 'text',
  text: 'aaaaa'
}
```

```type=imageのときのevent.message
{
  type: 'image',
  id: 1234567890,
  ...
}
```
typeが`image`のときは、`event.message.text`は存在しませんね。そういうことです。


----
#本題（コード）
特に説明することはないです。

```javascript:.gs

//LINEボットのアクセストークン
var ACCESS_TOKEN = 'ACCESS_TOKEN';
//Googleドライブに作ったフォルダのURLの末尾にある30文字くらいの文字列
var FOLDER_ID = 'FOLDER_ID';
//返信用エンドポイント
var REPLY_URL = 'https://api.line.me/v2/bot/message/reply';

function sendMsg(url, payload) {
  UrlFetchApp.fetch(url, {
    'headers': {
      'Content-Type': 'application/json; charset=UTF-8',
      'Authorization': 'Bearer ' + ACCESS_TOKEN,
    },
    'method': 'post',
    'payload': JSON.stringify(payload),
  });
}
function getImage(id) {
  //画像取得用エンドポイント
  var url = 'https://api-data.line.me/v2/bot/message/' + id + '/content';
  var data = UrlFetchApp.fetch(url,{
    'headers': {
      'Authorization' :  'Bearer ' + ACCESS_TOKEN,
    },
    'method': 'get'
  });
  //ファイル名を被らせないように、今日のDateのミリ秒をファイル名にしています
  var img = data.getBlob().getAs('image/png').setName(Number(new Date()) + '.png');
  return img;
}
function saveImage(blob) {
  try{
    var folder = DriveApp.getFolderById(FOLDER_ID);
    var file = folder.createFile(blob);
    return file.getUrl();
  }catch(e){
    return false;
  }
}
function doPost(e) {
  var event = JSON.parse(e.postData.contents).events[0];
  if(event.message.type == 'image') {
    try {
      var img = getImage(event.message.id);
      var url = saveImage(img);
      sendMsg(REPLY_URL, {
        'replyToken': event.replyToken,
        'messages': [{
          'type': 'text',
          'text': url ? "保存しました:\n" + url : "ダウンロードエラー",
        }]
      })
    }catch(e) {
      Logger.log(e);
    }
  }
  return ContentService.createTextOutput(JSON.stringify({'content': 'post ok'})).setMimeType(ContentService.MimeType.JSON);
}

```
受信した画像の取り扱いに非常に難儀しました。バイナリねえ…。

----

###雑記
本題から逸れますが、トークンやURLなどの変数やらは、GASの`プロジェクトのプロパティ`（「ファイル」→「プロジェクトのプロパティ」）に環境変数みたいな形で保存できます。
![2cOaJ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/532858/37933561-3a7b-9d96-dbc2-fdaca306e1ac.png)

保存した変数は以下のようにすることで取得できます。

```javascript:.gs
function PROPERTY(key) {
  return PropertiesService.getScriptProperties().getProperty(key);
}
var ACCESS_TOKEN = PROPERTY("ACCESS_TOKEN");
var FOLDER_ID = PROPERTY("FOLDER_ID");
```

ボットの機能を拡張する度に、DB用のスプレッドシートのIDや、PUSHメッセージ・ユーザープロフィール取得用のエンドポイントURLなどなど、必要な変数は一気に増えます。ヘンな文字列がいっぱいコード上にあると、コードの可読性は一気に低下します。是非ご活用ください。