Tak's Notebook

Kaggle, Machine Learning, Engineering

Slack と Google Spreadsheet で家計簿管理する

TOC

Slack と Google Spreadsheet で家計簿管理する

Slack に使ったお金を書くと、それが Spreadsheet に記録されるようにしている。

これまでは Zapier を利用していた。一定の利用範囲内で無料で Slack の特定チャンネルに投稿されると その内容を Spreadsheet に転記することができて 便利だった。

ただ、自分の支出管理目的から妻との共同利用目的に移行したため、最近利用頻度が増えてきて 無料枠内では制限に頻繁に引っかかるようになってしまった。

そこで Zapier からの移行を考えたのが 今回の記事を書くきっかけになった。

ちなみに以下のような Slack での投稿が Zapier を通して Spreadsheet に新しい行として書き込まれる

f:id:takaishikawa42:20220216231425p:plain
Slack 投稿画面例

f:id:takaishikawa42:20220216231604p:plain
Spreadsheet への転記例

既存のアプリや共同口座・家族カードではダメな理由

家計は折半で、家のものは基本的には自分が買って 月末にまとめてかかった額の半分を妻に請求する形を取っていた。

しかし、子供が生まれてから妻が家のものを買う機会がそれまで以上に増えたため これまでとはやり方を変える必要が出てきた。

折半するので「何に いくら使って それは誰がどの程度負担するか」を把握する必要がある。

この時点で 既存のアプリで要求を満たすことは難しそうだった。

また、共同口座は自分が調べた限りだと どちらか一方の名義で作る必要があり 不便そうな気がした。(そもそも新規口座開設も面倒...)

家族カードは検討したが、家のものを買うときはこのカード、自分のものを買うときはまた別のカード......とするのも煩雑なので これも気が進む選択肢でなかった。

そのため妻にも、それまで独自の支出管理に使ってた方法に参加してもらうことになった。

実装

Slack の特定チャンネルに送信されたメッセージを Spreadsheet に転記する。

そのために 以下を定期実行したい。

  1. Slack API を利用して Slack メッセージを取得する
  2. 特定期間に投稿されたメッセージのみに絞る
  3. IFTTT の Webhook URL を利用して Google Sheet に上記の絞られた投稿内容を追加する

フローチャートにすると以下のようなイメージ。さっそく Mermaid 記法を試してみた(ref. Include diagrams in your Markdown files with Mermaid | The GitHub Blog)。

f:id:takaishikawa42:20220216205505p:plain
takaiyuk/kakeibo

実装は以下のリポジトリにまとめている。

github.com

Slack メッセージの取得

Web API メソッドのうち conversations.history を利用して特定チャンネルのメッセージを取得する。以下に詳細やその他のメソッドがまとめられている。

Web API methods | Slack

SlackMessage という適当なデータクラスを用意して、以下のようにしてメッセージを取得する。

Pagination のため最新100件のみ取得するが、今回は投稿されるメッセージの頻度は多くないので これで問題ない。

日付の取得はレスポンスが持ってるタイムスタンプを利用して Spreadsheet 側で日付に変換する。そのため データとしてはタイムスタンプで持っておく。

from dataclasses import dataclass

import requests


@dataclass
class SlackMessage:
    ts: float
    text: str


def get_slack_messages(config: Config) -> list[SlackMessage]:
    url = "https://slack.com/api/conversations.history"
    token = config.slack_token
    header = {"Authorization": f"Bearer {token}"}
    channel_id = config.slack_channel_id
    payload = {"channel": channel_id}
    response = requests.get(url, headers=header, params=payload)
    messages = response.json().get("messages")
    slack_messages = [
        SlackMessage(ts=float(message.get("ts")), text=message.get("text"))
        for message in messages
    ]
    return slack_messages

IFTTT の Webhook URL 経由で Spreadsheet に行を追加する

IFTTT が用意した Web API を利用して、何かしらの動作を発火させることができる。

Webhooks works better with IFTTT

ここでは上記の SlackMessage の値をパラメータに持たせて Spreadsheet にそれらをもとにした文字列を書き込ませる。

当然 IFTTT 側での設定も必要だが、実行コードとしては以下のようなイメージになる。

def post_ifttt_webhook(config: Config, slack_messages: list[SlackMessage]) -> None:
    event = config.ifttt_event_name
    token = config.ifttt_webhook_token
    url = f"https://maker.ifttt.com/trigger/{event}/with/key/{token}"
    for message in slack_messages:
        payload = {"value1": message.ts, "value2": message.text}
        requests.post(url, data=payload)

これで Slack で コンビニ,1000,1 といった文字列をそのまま Spreadsheet の特定シートに転記することができる。

AWS Lambda

ここまでで Slack からメッセージを取得し、IFTTT の Webhook API に投げる機能まで出来た。

あとは これを定期実行できるようにするために AWS Lambda を利用する。Event Bridge をトリガーにして簡単に定期的なプラグラムの実行ができる上に、軽い処理なので頻繁に実行しても無料枠に十分収まるため。

ただし requests ライブラリはサードパーティなので別途ローカルディレクトリに用意が必要。Lambda Layer を利用すると、依存パッケージとコードを分離できるので良い。

Lambda レイヤーの作成と共有 - AWS Lambda

python ディレクトリにインストールすると 特別にパスを指定しなくても import できるようになるらしい。

$ mkdir python
$ pip install -t python requests
$ zip -r9 layer.zip python
$ rm -r python

おわりに

IFTTT 側での Google アカウントへの連携が知らぬ間に切れていたエラーにハマったりもしたが、すぐに解決できた。

ということで、難しいこともほとんどなく、Zapier から IFTTT へ移行しても それまでと同等の処理をお金をかけずに実現することができた。