9
3

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 1 year has passed since last update.

GASとLINE Bot(Messaging API)で家計管理してみる

Last updated at Posted at 2021-11-30

概要

  • LINE Developersに登録してBotを作成
  • Google spread sheetを作ってGASでコーディング

→毎月計算していた共用分の家計計算を楽にしてみた

最後にGitHub Gistのリンクを添付しています。

はじめに

もともと共用の食費や日用品の支払いを、家計管理せめて残しておこうという意図で、それ用に作ったLINEグループにアルバムとして追加し、毎月月末に締めて精算を行っていました。

ある日仕事終わりに上司と話しているときにGASの話になり、そういえばGAS使ってなんかできないかなあと思って調べてみたところ、お手軽にできそうだったのでやってみたメモになります。(Qiita投稿していなかったのでそれも兼ねて...)

実現したいこと

毎月の共用支払いを以下のようにアナログ管理していました。
①支払いのレシートや明細ページを、LINEアルバムを毎月作成して追加する
②月末に電卓を使って合計金額を算出して、多い方から少ない方へ振込

...アナログです

実際のお金のやり取りはしょうがないとして、精算を毎月時間をとってやることをなくせれば、という感じです。

そこで

  • 現在使っているLINEグループをそのまま使う
  • 金額を自動で計算したい

を主軸に進めました。
レシートをアルバムに添付するのは、証拠を残すために継続するようにし、ミニマムな機能が一旦作れればよいかなという考えです。

機能

  • 金額の登録
  • 合計金額の表示

LINE BOTの準備

LINE Developersに登録します。
登録からBot準備は非常に簡単でした。参考にさせていただいたサイトは1番最後にまとめて載せさせていただいています。

今回ポイントとしてはLINEグループに追加したかったので、下記部分の変更が必要でした。
Messaging API設定からBotの「グループ・複数人チャットへの参加を許可する」という項目があるので、右側の編集をクリック。
LINE Official Account Managerのアカウント設定が開くので、
機能の利用>チャットへの参加の部分で「グループ・複数人チャットへの参加を許可する」を選択。
image.png

これでグループ追加が可能になります。

GASで実装

メインの処理: doPost()
spreadsheetへの登録: record()
メッセージ返却: doReply()

の3つで分けました。
(分岐部分とかきれいに分けたいところでしたが、GASのあるべき書き方が分かっておらず...)

メインの処理

const ACCESS_TOKEN = ''; // LINE botのチャネルアクセストークンを設定
const URL = 'https://api.line.me/v2/bot/message/reply'; // 応答メッセージ用のAPI URL
const DATA_SHEET = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('foo'); // 保存sheetのID
const SUM_SHEET = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('bar'); // サマリsheetのID
const USER = {'id1': 'なまえ1', 'id2': 'なまえ2'};
function doPost(e) {
  const json = JSON.parse(e.postData.contents);

  const replyToken = json.events[0].replyToken;
  if (typeof replyToken === 'undefined') {
    return;
  }

  // ユーザID取得
  const userId = json.events[0].source.userId;
  // ここで特定ユーザでなければ登録せずに返却する
  let postMessage = '';
  if (USER[userId] === undefined) {
    postMessage = 'このユーザからの登録はできません。';
    return doReply(postMessage, replyToken);
  }

  // メッセージ取得
  const userMessage = json.events[0].message.text;
  // 内容を各変数に保持
  const messages = userMessage.split(/\r\n|\n/);
  const param = messages[0];

  /**
   * 以下パラメータによって各種処理に分岐
   * ・へるぷ || help || ヘルプ
   * ・yyyy/MM
   * ・金額(+備考)
   */
  if (param.match(/[help|へるぷ|ヘルプ]/)) {
    // ヘルプ表示
    postMessage = '以下が実行できます。\n==========\n';
    postMessage += '①登録:1行目に金額を入力します。改行して備考を併せて登録可能です。\n{金額}\n{備考}\n----------\n';
    postMessage += '②金額確認:1行目に金額を確認したい年月をyyyy/MMで入力します。\n(例)2021/04\n==========';
  } else if (param.match(/[0-9]{4}\/[0-2]{2}/)) {
    // 合計額の取得
    postMessage = getAmount(param);
  } else if (param.match(/[0-9]+/)) {
    // 登録
    const cost = parseInt(param);
    const etc = messages[1];
    const date = Utilities.formatDate(new Date(), 'Asia/Tokyo', 'yyyy/MM/dd');

    if (Number.isNaN(cost) === false) {
      postMessage = record(userId, cost, etc, date);
    } else {
      postMessage = `金額\n備考(任意)\nで入力してください。`;
    }
  } else {
    postMessage = '入力が正しくありません。\n「help」や「へるぷ」、「ヘルプ」を送ることで、何ができるか確認できます。'
  }

  return doReply(postMessage, replyToken);
}

メッセージ送信者を判断するために、json.events[0].source.userIdでpostDataからuserIdを取得しています。
予めこれを持っておいて、spreadsheet入力の名前として利用しています。

spreadsheetへの登録

function record(userId, cost, etc, date)
{
  const newRow = DATA_SHEET.getLastRow() + 1;

  let etcMessage = !etc === false ? etc : 'なし';
  let replyMessage = `日付: ${date}\n金額: ${cost}円\n備考: ${etcMessage}\nを登録しました`;
  let targetUser = USER[userId];
  if (targetUser === undefined) {
    targetUser = userId;
  }
  DATA_SHEET.getRange(`A${newRow}`).setValue(targetUser);
  DATA_SHEET.getRange(`B${newRow}`).setValue(date);
  DATA_SHEET.getRange(`C${newRow}`).setValue(cost);
  DATA_SHEET.getRange(`D${newRow}`).setValue(etc);

  return replyMessage;
}

データの最下行から次に入力する行を取得し、決めておいた列に受け取った入力データを書き込んでいきます。
DB的にspreadsheetを使っている感じです。

保存sheet
保存sheet

メッセージ返却

function doReply(postMessage, replyToken)
{
  UrlFetchApp.fetch(URL, {
    'headers': {
      'Content-Type': 'application/json; charset=UTF-8',
      'Authorization': 'Bearer ' + ACCESS_TOKEN,
    },
    'method': 'post',
    'payload': JSON.stringify({
      'replyToken': replyToken,
      'messages': [{
        'type': 'text',
        'text': postMessage,
      }],
    }),
  });
  return ContentService.createTextOutput(JSON.stringify({ 'content': 'post ok' })).setMimeType(ContentService.MimeType.JSON);
}

デプロイ

コードを書き終えたらデプロイします。
右上にあるデプロイボタンから新しいデプロイを選択し、説明文を入力します。
デプロイ.png
アクセスできるユーザを「全員」に設定。

変更して更新する場合は、デプロイボタンからデプロイ管理を選択し、編集ボタンを押した後にバージョンから「新しいバージョン」を選択すればOKです。
version.png

デプロイ後に表示されるウェブアプリのURLをコピーし、Messaging APIのWebhook URLに設定すれば完了です。
webhook.png

使ってみた

試しにhelpを表示させてみました。
image.png

おわりに

今回思い立ってやってみましたが、簡易的にやりたいことが実現できました。
コードの中身とかを良い書き方にするとかは飛ばしてしまいましたが、普段使うツールの延長として簡単なアプリケーション的に作るには非常によかったです。(何よりお金がかからない範囲でできる)

受け取ったメッセージを分岐するような形である意味CLIベースのような形ですが、今度はリッチメニューを使う形にしたり、
必要な機能を追加していけたらよいかなと思います。

ソースコード

参考

LINEのBot開発 超入門(前編) ゼロから応答ができるまで
[LINE BOT]GASで家計簿BOT作ってみた
グループでも動作するLINE BOTを作る!

9
3
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
9
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?