11
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

LINE DC Advent Calendar 2021 (クリスマスプレゼント付き)Advent Calendar 2021

Day 23

[GoogleAppsScript] 赤ちゃんのおむつ交換記録LINE-BOTを作成する

Last updated at Posted at 2021-12-18

はじめに

LINE を使用して 赤ちゃんのおむつ交換記録を行うBOTを作成しました。(※大きい方です。)
子供がたびたび便秘になっていたこと、便秘が続いていた日数を忘れてしまうことが多かったことがありましたので、BOTを作成しました。しかし、この記事を執筆時にはすでに子供のおむつは取れていました。(時の流れは速いです...。)

unk_3.png

unk_2.png

unk_1.png

機能としては下記の通りです。

  • キーワードを入力すると、現在日時が記録されます。
  • 履歴メッセージを入力すると、過去の記録を閲覧できます。
  • 便秘が続いていた場合、毎日何日目か通知します。

事前準備

詳しくはこちら

  1. GoogleDriveにてGoogleAppsScriptを開いて、以下のスクリプトを実装します。
  • LINE_CHANNEL_TOKENにLINE Messaging APIで発行したトークンを指定します。
  • SSIDにスプレッドシートのIDとSSN_USERSSN_HISTORYにシート名を指定します。
main.gs
/**
 * 赤ちゃんのおむつ交換BOT
 */
const LINE_CHANNEL_TOKEN = '*****'; // LINE NOTIFYのアクセストークン
const SSID = '*****';
const SSN_USER = 'user';
const SSN_HISTORY = 'history';

フォロー登録

詳しくはこちら

フォロー解除

詳しくはこちら

リプライ

メッセージを入力するとLINE Messaging APIからGASにPOSTが通知されます。GASでは、スプレッドシートにメッセージ入力時刻を保存します。結果をLINE Messaging APIにPOSTで返します。

main.gs
/**
 * メッセージイベント処理
 * @param {Object} reqEvent 
 */
function executeMessage(reqEvent) {
    let msgList = [];
    let user = getUser(reqEvent.source.userId);
    if (user) {
        let reqReport = -1;
        switch (reqEvent.message.type) {
            case 'text':
                reqReport = getReportFromText(reqEvent.message.text);
                break;
            case 'sticker':
                reqReport = getReporFromSticker(reqEvent.message.packageId, reqEvent.message.stickerId);
                break;
        }

        let nowTime = (new Date()).getTime();

        switch (reqReport) {
            case 0:
                msgList.push({
                    'type': 'text',
                    'text': 'うんこ承りました。',
                });

                if (user.item.lastDate) {
                    let diffTime = nowTime - user.item.lastDate;
                    if (DAY_TIME < diffTime) {
                        let day = Math.floor(diffTime / DAY_TIME);
                        msgList.push({
                            'type': 'text',
                            'text': `${day}日ぶりに出たね。`,
                        });
                    }
                }

                sendLineReply(reqEvent.replyToken, msgList);
                userSheet.getRange(user.index + 2, 4).setValue(nowTime);
                historySheet.appendRow([user.item.userId, nowTime]);
                break;
            case 1:
                let historyList = getHistoryList(reqEvent.source.userId);
                let msg = '直近の記録(最大10件)\n\n';
                for (let i in historyList) {
                    let history = historyList[i];
                    let timestamp = Utilities.formatDate(new Date(history.recortDt), 'Asia/Tokyo', 'MM月dd日 HH:mm');
                    msg += timestamp + '\n';
                }
                msgList.push({
                    'type': 'text',
                    'text': msg,
                });
                sendLineReply(reqEvent.replyToken, msgList);
                break;
            default:
                msgList.push({
                    'type': 'text',
                    'text': 'わからないよ\nもう一度入力してね',
                });
                sendLineReply(reqEvent.replyToken, msgList);
                break;
        }
    }
}

/**
 * テキストからイベント種別を取得する
 * @param {String} text 
 */
function getReportFromText(text) {
    const REPORT_LIST = [
        ['うんこ', 'ウンコ', '出た', 'でた'],
        ['直近', '履歴']
    ];
    for (let i in REPORT_LIST) {
        let itemList = REPORT_LIST[i];
        for (let j in itemList) {
            let item = itemList[j];
            if (text == item) {
                return parseInt(i);
            }
        }
    }
}

/**
 * スタンプからイベント種別を取得する
 * @param {String} packageId 
 * @param {String} stickerId 
 */
function getReporFromSticker(packageId, stickerId) {
    const REPORT_LIST = [
        [{
            packageId: '4',
            stickerId: '284'
        }, {
            packageId: '2000011',
            stickerId: '455795'
        }, {
            packageId: '2000011',
            stickerId: '455798'
        }, {
            packageId: '2000013',
            stickerId: '483667'
        }, {
            packageId: '2000013',
            stickerId: '483668'
        }]
    ];
    for (let i in REPORT_LIST) {
        let itemList = REPORT_LIST[i];
        for (let j in itemList) {
            let item = itemList[j];
            if (item.packageId == packageId && item.stickerId == stickerId) {
                return parseInt(i);
            }
        }
    }
}

/**
 * 履歴一覧を取得する
 * @param {String} userId 
 */
function getHistoryList(userId) {
    let historyList = [];
    let allHistoryList = getAllHistoryList();
    for (let i in allHistoryList) {
        let hitory = allHistoryList[i];
        if (hitory.userId === userId) {
            historyList.push(hitory);
        }
    }
    return historyList.slice(-10);
}

/**
 * 全履歴一覧を取得する
 */
function getAllHistoryList() {
    let allHistoryList = [];
    let lastRow = historySheet.getLastRow();
    if (1 < lastRow) {
        allHistoryList = historySheet.getRange(1, 1, lastRow, 2).getValues();
        allHistoryList = allHistoryList.map((row) => {
            return {
                userId: row[0],
                recortDt: row[1],
            }
        });
    }
    return allHistoryList;
}

/**
 * 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の場合は、メッセージの内容を解析します。メッセージの現在日時を記録します。
  • 保存結果メッセージを作成してsendLineReply関数でLINE Messaging APIにPOSTします。

プッシュ通知

GASにて、1日1回、赤ちゃんの便秘が続いていた場合、何日目であるかを通知します。

main.gs
/**
 * 通知
 */
function notify() {
    try {
        let nowTime = (new Date()).getTime();
        let userList = getUserList();
        for (let i in userList) {
            let user = userList[i];
            if (user.lastDate) {
                let diffTime = nowTime - user.lastDate;
                if (DAY_TIME < diffTime) {
                    let day = Math.floor(diffTime / DAY_TIME);
                    let msgList = [{
                        'type': 'text',
                        'text': `うんこが止まって${day}日目です。`,
                    }];
                    sendLinePush(user.userId, msgList);
                }
            }
        }
    } catch (e) {
        console.error(e.stack);
    }
}

/**
 * LINEにメッセージを送信する
 * @param {String} targetId ターゲットID(userId/groupId/roomId)
 * @param {Object} msgList メッセージリスト
 */
function sendLinePush(targetId, msgList) {
    let url = 'https://api.line.me/v2/bot/message/push';
    let options = {
        'method': 'post',
        'headers': {
            'Content-Type': 'application/json; charset=UTF-8',
            'Authorization': `Bearer ${LINE_CHANNEL_TOKEN}`
        },
        'payload': JSON.stringify({
            to: targetId,
            messages: msgList
        })
    };
    let response = UrlFetchApp.fetch(url, options);
    return JSON.parse(response.getContentText('UTF-8'));
}
  • 1日1回notify関数を実行します。

参考リンク

さいごに

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

以上です。

11
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?