Getting Started

2020/10/09

OK、Riiiver! 今日何回怒った?

どうも、こんにちは!JellyWareです。

Getting Started!! Riiiver tutorialの第18弾となります。

このシリーズは、Riiiverやプログラミングを学び、初心者の方でもEco-Drive Riiiverを活用したオリジナルアプリを理解して作れるようになることを目指します。

前回学んだ内容を活かして、今回はPieceを作成します。

スプレッドシートをデータベース代わりにして、怒った回数を記録させます。また、実行した時間に応じて以下のように処理を変えます。

  • 0 ~ 22時:怒った回数をカウント
  • 22時以降:怒った回数をカウントせずに取得のみ

目次


iiidea

順に Trigger, Service, Action Piece を解説します。

Trigger

既存の「ボタン押し」を使用します。

Eco-Drive Riiiver のボタンを押すと iiidea を実行できます。

Service

今回新しく作成します。

Google Sheets APIを使って、スプレッドシートと連携させます。

Action

既存の「端末に文字を通知」を使用します。

Eco-Drive Riiiverと連携している端末に通知が届きます。

Piece JSON

まずは、Piece JSONについて解説します。

{
    "title": {
        "en": "Anger Management",
        "ja": "怒りっぽさを改善"
    },
    "version": "1.0.0",
    "sdkVersion": "1.0.0",
    "deviceId": "none",
    "vendorId": "none",
    "description": {
        "en": "Register every time you get angry.  Every time you trigger this Piece, the date and time you triggered it will be sent to a connected Google Sheet.*  A message is then sent on to the next Piece based on the number of times so far today you’ve been marked as angry.\n\n*You need to create and publish your own Google sheet to connect to the Piece.  The sheet sharing settings must be set to “Anyone on the internet with this link can edit.”\n\nNOTE:  This Piece currently only outputs Japanese text and registers time based on Japan Standard Time."
        "ja": "スプレッドシートをデータベースの代わりにして、何回怒ったかを記録します。スプレッドシートの日付と怒った回数を更新し、怒った回数に応じて次のPieceに渡すテキストが変更します。使用するスプレッドシートのキーを入力してください。また、権限を「このリンクを知っているインターネット上の全員が編集できます」にしてください。"
    },
    "blockType": "service",
    "executor": "RBCCommonWebServiceExecutor",
    "serviceProxy": {
        "service": "cureIrritability"
    },
    "osType": "none",
    "categoryIds": ["cat_0001"],
    "preferences": {
        "type": "object",
        "properties": {
            "spreadSheetKey": {
                "x-input-type": "text",
                "type": "string",
                "x-title": {
                    "en": "Spreadsheet Key",
                    "ja": "スプレッドシートのキー"
                },
                "x-description": {
                    "en": "Enter Google Sheet key, which is the string of numbers and letters located in your sheet URL where “KEY” is written here: https://docs.google.com/spreadsheets/d/KEY/",
                    "ja": "スプレッドシートのキーを入力してください。"
                }
            }
        }
    },
    "output": {
        "type": "object",
        "properties": {
            "message": {
                "type": "string",
                "format": "text"
            }
        }
    }
}

今回作成するPieceの要点となる、"preferences", "output"について解説します。

preferences

ユーザーがスプレッドシートのシートキーを入力できるようにします。

"preferences": {
    "type": "object",
    "properties": {
        "spreadSheetKey": {
            "x-input-type": "text",
            "type": "string",
            "x-title": {
                "en": "Spreadsheet Key",
                "ja": "スプレッドシートのキー"
            },
            "x-description": {
                "en": "Enter Google Sheet key, which is the string of numbers and letters located in your sheet URL where “KEY” is written here: https://docs.google.com/spreadsheets/d/KEY/",
                "ja": "スプレッドシートのキーを入力してください。"
            }
        }
    }
}

実際のアプリの画面では、以下のようになります。

output

次のPieceに文字列を渡せるようにします。

"output": {
    "type": "object",
    "properties": {
        "message": {
            "type": "string",
            "format": "text"
        }
    }
}

文字列のみを渡せるように、"type""string","format""text"にしています。

"format"については、ドキュメントを参照ください。

Piece Func

次に Piece Funcを解説します。

// google-spreaesheetの読み込み const { GoogleSpreadsheet } = require('google-spreadsheet'); // 認証情報jsonファイルを読み込む const credit = require('./credentials.json'); exports.handler = async (event) => { if (event.call === 'lambda') { console.log('CALLED:LAMBDA'); /* If you use the external modules, please code the following: - 外部モジュールを使う場合に記入してくだい。 e.g. if ( xxxx !== null ){} // xxxx: instance for the module. - xxxx : 生成したインスタンス */ if (GoogleSpreadsheet !== null) { } return; } // スプレッドシートキー const spreadSheetKey = event["properties"]["preferences"]["spreadSheetKey"] // スプレッドシートの指定 const doc = new GoogleSpreadsheet(spreadSheetKey); // 認証 await doc.useServiceAccountAuth({ client_email: credit["client_email"], private_key: credit["private_key"] }); // スプレッドシートの情報取得 await doc.loadInfo(); // シートの取得 const sheet = doc.sheetsByIndex[0]; // 通知メッセージ let message; // 次のPieceに渡す let response; // ヘッダーの追加 await sheet.setHeaderRow(["日にち", "怒った回数"]); // タイムスタンプ(年と月と日) const today = new Date(); today.setHours(today.getHours() + 9);// 日本時間に変換 const timeStamp = `${today.getFullYear()}${today.getMonth() + 1}${today.getDate()}日`; // 行情報の取得 let rows = await sheet.getRows(); // 最終行の日にちと怒った回数 let lastDate; let angryNumber; try { lastDate = rows.slice(-1)[0]["日にち"]; angryNumber = rows.slice(-1)[0]["怒った回数"]; angryNumber = Number(angryNumber); } catch(error) { // 初回実行時はデータが空のため作成 lastDate = timeStamp; angryNumber = 0; // 行を追加 await sheet.addRow([timeStamp, angryNumber]); // 再取得 rows = await sheet.getRows(); } // 22時以降の場合 怒った回数のみを返す if (today.getHours() > 21) { // 実行日のデータがない場合 if (timeStamp == lastDate) { message = `今日の怒った回数は${angryNumber}回です`; } else { message = "今日は一度も怒っていません!"; } response = { status: 200, body: { message: message } } return response; } if (timeStamp == lastDate) { // 更新 rows[rows.length - 1]['怒った回数'] = angryNumber + 1; await rows[rows.length - 1].save(); } else { // 今日初めての実行 angryNumber = 1; // シートに追加 await sheet.addRow([timeStamp, angryNumber]); } // 怒った回数に応じてテキストを変更 if (angryNumber < 3) { message = `今日の怒った回数は、まだ${angryNumber}回です。このまま落ち着いていきましょう`; } else if (angryNumber < 5) { message = `今日の怒った回数は、現在${angryNumber}回です。少し気分をリフレッシュしましょう`; } else { message = `今日の怒った回数は、現在${angryNumber}回です。気分転換をして落ち着きましょう`; } response = { status: 200, body: { message: message } }; return response; }

今回は、少し複雑なコードになっています。以下が行っている内容です。

  • スプレッドシートの認証〜シート情報の取得
  • ヘッダーの追加
  • タイムスタンプの作成
  • 最終行の日にちと怒った回数の取得(初回実行時はダミーデータ作成)
  • 実行タイミングが22時以降
    • その日の怒っている回数を取得して通知メッセージを作成
    • 1度も怒っていない場合は別のメッセージを作成
  • 実行タイミングが22時以前
    • 怒った回数を1増やす
    • その日初めての実行の場合は、タイムスタンプと怒った回数1を追加
    • 怒った回数に応じて通知メッセージを作成

それでは1行目から順に解説していきます。

// google-spreaesheetの読み込み
const { GoogleSpreadsheet } = require('google-spreadsheet');
// 認証情報jsonファイルを読み込む
const credit = require('./credentials.json');

exports.handler = async (event) => {
    if (event.call === 'lambda') {
        console.log('CALLED:LAMBDA');
        /* If you use the external modules, please code the following: - 外部モジュールを使う場合に記入してくだい。
           e.g.  if ( xxxx !== null ){} // xxxx: instance for the module.  -  xxxx : 生成したインスタンス */

        if (GoogleSpreadsheet !== null) { }
        return;
    }

    // スプレッドシートキー
    const spreadSheetKey = event["properties"]["preferences"]["spreadSheetKey"]
    // スプレッドシートの指定
    const doc = new GoogleSpreadsheet(spreadSheetKey);

    // 認証
    await doc.useServiceAccountAuth({
        client_email: credit["client_email"],
        private_key: credit["private_key"]
    });

    // スプレッドシートの情報取得
    await doc.loadInfo(); 

    // シートの取得
    const sheet = doc.sheetsByIndex[0];

    // 通知メッセージ
    let message;

    // 次のPieceに渡す
    let response;

    ...
}

1~38行目です。

モジュールの読み込み、スプレッドシートの指定〜認証〜シートの取得、変数の宣言を行っています。

また、認証情報はJSONファイルからrequireを使って読み込んでいます。ファイル名はわかりやすいようにcredentials.jsonにしています。

// ヘッダーの追加
await sheet.setHeaderRow(["日にち", "怒った回数"]);

// タイムスタンプ(年と月と日)
const today = new Date();
today.setHours(today.getHours() + 9);// 日本時間に変換
const timeStamp = `${today.getFullYear()}${today.getMonth() + 1}${today.getDate()}日`;

40~46行目です。

ヘッダーを追加して、タイムスタンプを作成しています。Lambda上でnew Date()を実行すると、協定世界時で時刻を取得するので、日本時間に変換する必要があります。

// 行情報の取得
let rows  = await sheet.getRows();

// 最終行の日にちと怒った回数
let lastDate;
let angryNumber;
try {
    lastDate = rows.slice(-1)[0]["日にち"];
    angryNumber = rows.slice(-1)[0]["怒った回数"];
    angryNumber = Number(angryNumber);
} catch(error) { // 初回実行時はデータが空のため作成
    lastDate = timeStamp;
    angryNumber = 0;
    // 行を追加
    await sheet.addRow([timeStamp, angryNumber]);
    
    // 再取得
    rows  = await sheet.getRows();
}

48 ~ 66行目です。

シートから行情報を取得します。そしてrows.slice(-1)[0]["日にち"]rows.slice(-1)[0]["怒った回数"] とそれぞれ最終行の「日にち」「怒った回数」を取得しています。

またNumber()を使って文字列を数値に変換しています。

ただし初めて実行する場合、上記画像のようにシートには何も記載されていません。そのため「日にち」「怒った回数」を取得しようとしてもエラーが発生します。そのためtry…catch文を利用して、エラーが起きる場合(=初回実行時)は行を追加して、行情報を再取得しています。

// 22時以降の場合 怒った回数のみを返す
if (today.getHours() > 21) {
    // 実行日のデータがない場合
    if (timeStamp == lastDate) {
        message = `今日の怒った回数は${angryNumber}回です`;
    } else {
        message = "今日は一度も怒っていません!";
    }

    response = {
        status: 200,
        body: {
            message: message
        }
    }

    return response;
}

68~85行目です。

if文を使って、実行した時間(=today.getHours())が22時以降である場合に実行されます。22時以降の場合は、怒った回数をカウントするのではなく、怒った回数が何回かを通知するのみとなります。

また、最終行の日にちとタイムスタンプが一致するかをさらにif文で判定しています。一致する場合は、そのまま取得した怒った回数を通知します。一致しない場合というのは、その日初めて実行することになるので今日は一度も怒っていません!と通知するようにしています。

また、returnを実行すると、それ以降のコードは実行されません。つまり、22時以降の場合は、ここで処理が終わります。

if (timeStamp == lastDate) {
    angryNumber = angryNumber + 1;
    
    // 更新
    rows[rows.length - 1]['怒った回数'] = angryNumber + 1;
    await rows[rows.length - 1].save();

} else { // 今日初めての実行
    angryNumber = 1;

    // シートに追加
    await sheet.addRow([timeStamp, angryNumber]);
}

87 ~ 97行目です。

ここからは、22時以前に実行した場合の処理となります。同じように、最終行の日にちとタイムスタンプが一致するかを判定して、その日初めて実行したかをチェックしています。

2回目以降の場合は、怒った回数に1を足して更新します。初めての実行の場合は、怒った回数を1として行を追加しています。

// 怒った回数に応じてテキストを変更
if (angryNumber < 3) {
    message = `今日の怒った回数は、まだ${angryNumber}回です。このまま落ち着いていきましょう`;
} else if (angryNumber < 5) {
    message = `今日の怒った回数は、現在${angryNumber}回です。少し気分をリフレッシュしましょう`;
} else {
    message = `今日の怒った回数は、現在${angryNumber}回です。気分転換をして落ち着きましょう`;
}

response = {
    status: 200,
    body: {
        message: message
    }
};

return response;

99 ~ 115行目です。

怒った回数に応じて、通知するメッセージを変更しています。


Google Sheets APIを使用すると、Node.js側でスプレッドシートの操作を行えるのが分かったと思います。

また、通常データベースと言えばMySQLなどのミドルソフトウェアだと思いますが、このようにスプレッドシートを簡易データベースとして活用することで時間短縮やコストの削減ができます。

今回は「怒った回数」を記録するようにしましたが、他のものを記録できるPieceを是非作ってみてください。

ご覧くださいましてありがとうございました。