[GoogleAppsScript] 画像アップロードLINE-BOTを作成する

# はじめに
LINE を使用して GoogleDriveに画像アップロードを行うBOTを作成しました。
![img_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50563/cf27312f-b379-3682-d0da-971bf03b63a1.png)

![img_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50563/7f62b60d-95b0-cc5c-0486-85399951199b.png)

# 事前準備
[詳しくはこちら](https://qiita.com/yun_bow/items/33879bca8f82d1ac6cfa)

1. GoogleDriveにてGoogleAppsScriptを開いて、以下のスクリプトを実装します。

- `LINE_CHANNEL_TOKEN`にLINE Messaging APIで発行したトークンを指定します。
- `SSID`にスプレッドシートのIDと`SSN_USER`にシート名を指定します。
- `DFID`にGoogleDriveのIDを指定します。

```JavaScript:main.gs
/**
 * 画像アップロードBOT
 */
const LINE_CHANNEL_TOKEN = '*****'; // LINE NOTIFYのアクセストークン
const SSID = '*****';
const SSN_USER = 'user';
const DFID = '*****'
```

# フォロー登録
[詳しくはこちら](https://qiita.com/yun_bow/items/33879bca8f82d1ac6cfa)

# フォロー解除
[詳しくはこちら](https://qiita.com/yun_bow/items/33879bca8f82d1ac6cfa)

# リプライ
ユーザーが画像を入力すると`LINE Messaging API`からGASにPOSTが通知されます。GASでは、画像を取得して、GoogleDriveに保存します。保存結果を`LINE Messaging API`にPOSTで返します。

```plantuml
@startuml
actor ユーザー
ユーザー -> LINEアプリ: 画像入力
activate LINEアプリ
LINEアプリ -> "LINE Messaging API"
deactivate LINEアプリ
activate "LINE Messaging API"
  "LINE Messaging API" -> GoogleAppsScript: ...POST (type: message)
deactivate "LINE Messaging API"
  activate GoogleAppsScript
    GoogleAppsScript -> "LINE Messaging API": GET /message/id/content
activate "LINE Messaging API"
    GoogleAppsScript <-- "LINE Messaging API": 画像バイナリファイル
deactivate "LINE Messaging API"
    GoogleAppsScript -> GoogleAppsScript: GoogleDriveに保存
    GoogleAppsScript -> "LINE Messaging API": POST /message/reply
  deactivate GoogleAppsScript
activate "LINE Messaging API"
"LINE Messaging API" -> LINEアプリ
deactivate "LINE Messaging API"
activate LINEアプリ
LINEアプリ -> LINEアプリ: 保存結果
deactivate LINEアプリ
@enduml
```

```JavaScript:main.gs
/**
 * メッセージイベント処理
 * @param {Object} reqEvent 
 */
function executeMessage(reqEvent) {
    let msgList = [];
    let user = getUser(reqEvent.source.userId);
    if (user) {
        switch (reqEvent.message.type) {
            case 'image':
                let childFolder = getChildFolder(reqEvent.source.userId);
                if (!childFolder) {
                    childFolder = folder.createFolder(reqEvent.source.userId);
                }

                let response = getLineContent(reqEvent.message.id);
                let file = response.getBlob();
                let timestamp = Utilities.formatDate(new Date(), 'Asia/Tokyo', 'yyyyMMddHHmmss');
                file.setName(timestamp);
                childFolder.createFile(file);

                msgList.push({
                    'type': 'text',
                    'text': '写真承りました。',
                });
                sendLineReply(reqEvent.replyToken, msgList);
                break;
        }
    }
}

/**
 * サブフォルダーを取得する
 * @param {String} folderName 
 */
function getChildFolder(folderName) {
    let childFolders = folder.getFolders();
    while (childFolders.hasNext()) {
        let childFolder = childFolders.next();
        if (childFolder.getName() == folderName) {
            return childFolder;
        }
    }
    return null;
}

/**
 * LINEからコンテンツを取得する
 * @param {String} messageId メッセージID
 */
function getLineContent(messageId) {
    let url = `https://api.line.me/v2/bot/message/${messageId}/content`;
    let options = {
        'method': 'get',
        'headers': {
            'Content-Type': 'application/json; charset=UTF-8',
            'Authorization': `Bearer ${LINE_CHANNEL_TOKEN}`
        }
    };
    return UrlFetchApp.fetch(url, options);
}

/**
 * LINEに応答メッセージを送信する
 * @param {String} replyToken リプライトークン
 * @param {Object} msgList メッセージリスト
 */
function sendLineReply(replyToken, msgList) {
    let url = 'https://api.line.me/v2/bot/message/reply';
    let options = {
        'method': 'post',
        'headers': {
            'Content-Type': 'application/json; charset=UTF-8',
            'Authorization': `Bearer ${LINE_CHANNEL_TOKEN}`
        },
        'payload': JSON.stringify({
            replyToken: replyToken,
            messages: msgList
        })
    };
    let response = UrlFetchApp.fetch(url, options);
    return JSON.parse(response.getContentText('UTF-8'));
}
```
- `execute`関数では、LINE Messaging APIからの通知内容により、処理を振り分けしています。`message`の場合は、メッセージの内容を解析します。メッセージの種別が`image`の場合は、GoogleDriveに保存します。
- 保存結果メッセージを作成して`sendLineReply`関数でLINE Messaging APIにPOSTします。

# 参考リンク
- [冷蔵庫の余った食材からレシピを提案するボットをリリースしました](https://qiita.com/msh5/items/b2350bab800eddaecad3)
- [素人がLINEの｢文字起こし君｣を1日で作る](https://qiita.com/MPO/items/175ac5710384495eb72e)

# さいごに
ソースコードをGitHubに公開しています。

- [ソースファイルはこちら](https://github.com/yunbow/GoogleAppsScript-LINE-ImageUploader)

以上です。