我が家の課題をLINE Messaging APIで解決した話  #doda Developer Group Advent Calendar 2024

この記事は doda Developer Group Advent Calendar 2024 の 15 日目の記事です。

こちらの記事以外にも沢山の記事が掲載されていますので、興味のある方は # doda Developer Group Advent Calendar 2024 で検索してみてください。

目次

はじめに

こんにちは。アドベントカレンダー15 日目担当の奥野と申します。 パーソルキャリア株式会社で doda マイクロサービスグループに所属しておりバックエンドエンジニアを担当しています。 本記事は、家庭の課題を技術を使ってさくっと解決したお話になります。具体的には LINE Bot を使って家事タスクの管理を自動化した話になります。

我が家の課題

同棲を始めて 3 年ほどになりますが共働きということもあるので我が家では家事を分担しています。また家事ごとに担当を分けるとお互いに不公平感が出るので、週替わりに家事内容をスイッチするようにしています。最初はこのルールでうまく回っていたのですが、最近は段々と適当になっていってしまい、「相手もやっていないから自分もやらなくていいや」の悪循環が発生してお互いに不満が溜まっているという状況がありました。

課題に対するアプローチ

上記の課題感を解消するために下記が必要だと考えました。

  • 今日の自分の家事タスクが何であるかすぐにわかる(毎朝通知される)
  • 家事タスクの完了を相手に通知できる
  • 家事タスクの完了が記録として残り実績が確認できる

解決したソリューション

全体図

普段から触るツールで手軽にできることが重要だと考え、LINE Bot を作成して上記を実現しました。 また、イベントのスケジュール発火やデータの永続化が必要なのでそのあたりの機能が標準で揃っておりセットアップが容易な開発プラットフォーム Firebase を利用しました。

システム全体図

機能イメージ

機能(1)毎朝 LINE メッセージに今日の家事タスクが通知される

毎朝通知の LINE 画面

機能(2)完了したら LINE のメニューをクリックして完了報告→相手に通知される

完了報告 LINE 画面

相手にはこのように通知が走る。

相手への通知 LINE 画面

機能(3)メニューのレポートをクリックすると今月の実績を取得できる

実績の取得 LINE 画面

ここからは実装までにやることと実装方法を簡単に紹介します。

(全文ソースはありませんが必要な実装イメージはつかめるように書いています。)

実装準備

(1) Firebase の開発環境作成

下記手順で簡単にセットアップできます。(Node.js 必要)

# 資材インストール
npm install firebase-functions@latest firebase-admin@latest --save
npm install -g firebase-tools
# 下記実行でブラウザが開くのでログインする
firebase login
# 下記を実行してプロジェクト名や使用言語を選択します
firebase init functions

デプロイするときは下記コマンドを実行します。

firebase deploy --only functions

その他詳細は下記を参考にして下さい。

https://firebase.google.com/docs/functions/get-started?hl=ja&gen=2nd

(2)LINE MessagingAPI の登録

以前は「LINE Notify」というサービスを使用することで公式アカウントがなくてもメッセージを送信できたのですが、 2025 年 3 月にサービス終了してしまうので今回は MessagingAPI を使用します。 下記の公式手順に沿って LINE 公式アカウントの作成と MessagingAPI の有効化を設定します。 Messaging APIを始めよう | LINE Developers

(3)チャネルアクセストークンの発行

メッセージを送信する際に必要になるので下記手順で発行します。 developers.line.biz

(4)上記で作成した LINE 公式アカウントを自身の LINE に追加する

自分とメッセージを送信したい相手の LINE に上記で作成した公式アカウントを追加します。QR コードから追加すると楽です。

(5)LINE のリッチメニューを登録する

LINE の「リッチメニュー」という機能を使い、公式アカウントにメニューを表示させます。メニューをクリックすると特定のメッセージを送信する設定も加えます。

developers.line.biz

下記のようにボタンを 3 つ作成してそれぞれのボタン押下時にテキストメッセージが飛ぶようにします。

リッチメニューの設定画面

実装

(1)Cloud Functions for Firebase で API を実装する

下記のように記載すると RestAPI として受け付けるようになります。

import * as functions from "firebase-functions";

/**
 * メッセージを webhook 経由で受け取る
 */
export const recieveMessage = functions.https.onRequest(async (req, res) => {
// 処理を記載
})

デプロイすると CloudFunctions のコンソール画面に URL が表示されます。

CloudFunctions コンソール画面

上記の URL を LINE のコンソール画面の Webhook URL に登録します。これで公式アカウントにメッセージを送ると CloudFunctions 側にリクエストが飛ぶようになります。

LINE Messaging API の Webhook 設定

(2)特定のキーワードを受け取ったときに処理を実行する

リッチメニューから送信されるメッセージをキーに処理を分岐します。

  • 「完了」というキーワード→ DB 登録+相手に終わったことをメッセージ
  • 「レポート」というキーワード→DB 取得+数値を合算して自分にメッセージ送信

といった具合に実装しました。

import * as functions from "firebase-functions";
export const recieveMessage = functions.https.onRequest(async (req, res) => {
  try {
    const events: WebhookEvent[]= req.body.events;
    for (const event of events) {
      if (event.type === "message" && event.message?.type === "text") {
        const action = event.message.text;
        
        if (action === MessageKey.FINISH && event.source?.userId) {
          //  DB 登録+相手に終わったことをメッセージ
        }
        if (action === MessageKey.REPORT && event.source?.userId) {
          // DB 取得+数値を合算して自分にメッセージ送信        }
      }
    }

(3)LINE にメッセージを送信・返信する

LINE へメッセージを送信するには、REST でリクエストする必要があります。 先ほど発行したチャネルアクセストークンを CloudFunctions の環境変数として設定しておきます。

firebase functions:config:set line_config.access_token="xxx"

メッセージの受信に対してそのメッセージの返信する場合の処理は下記のように書けます。replyTokenというパラメータを受信リクエストから取得して、送信リクエストに含めます。

// LINE Messaging API
const LINE_MESSAGING_API_URL = "https://api.line.me/v2/bot/message/reply";
// 環境変数
const LINE_CHANNEL_ACCESS_TOKEN = functions.config().line_config.access_token;

/**
 * LINE メッセージを返信する
 * replyToken を渡すとそのまま発信されたトークルームに返信できる
 */
async function replyMessage(replyToken: string, text: string) {
  await axios.post(
    LINE_MESSAGING_API_URL,
    {
      replyToken,
      messages:[{ type: "text", text }],
    },
    {
      headers: {
        "Content-Type": "application/json",
        Authorization: `Bearer ${LINE_CHANNEL_ACCESS_TOKEN}`,
      },
    }
  );
}

replyToken は下記にあります。

interface WebhookEvent {
  type: string;
  message?: {
    type: string;
    text: string;
  };
  source?: {
    userId?: string;
  };
  replyToken: string;
}

返信ではなく LINE メッセージを特定ユーザーに送信するには下記のようにユーザーID を直接指定する必要があります。

/**
 * LINE メッセージを送信する
 * to にユーザーID を含めると、そのユーザーが公式アカウントを追加していれば
 * メッセージを送信できる
 */
async function sendMessage(userId: string, text: string) {
  await axios.post(
    LINE_MESSAGING_API_URL,
    {
      to: userId,
      messages:[{ type: "text", text }],
    },
    {
      headers: {
        "Content-Type": "application/json",
        Authorization: `Bearer ${LINE_CHANNEL_ACCESS_TOKEN}`,
      },
    }
  );
}

注意点としてユーザーID を確認するには、実際に API を呼び出してログ出力するしか方法がないので下記のようにログを仕込んで確認してください。

/**
 * メッセージを webhook 経由でを受け取る
 */
export const recieveMessage = functions.https.onRequest(
async (req, res) => {
      console.log(JSON.stringify(req.body.events;, null, 5));

(4)Cloud Functions でスケジュール実行する

下記のように cron で指定することでスケジュール実行できます。 https://cloud.google.com/scheduler/docs/configuring/cron-job-schedules?hl=ja

import * as functions from "firebase-functions";

/**
 * 毎朝 7 時にイベントを呼び出す
 */
export const schedule = functions.pubsub.schedule('every day 7:00')
  .timeZone("Asia/Tokyo")
.onRun(async (context) => {
// 家事タスクを通知する
  });

(5)CloudStore に保存・取得する

CloudStore への登録や取得は下記のように実装します。

import * as admin from "firebase-admin";

// 初期化
admin.initializeApp();
const db = admin.firestore();

// 家事タスク完了のDB登録
await db.collection("t_status_daily").add({
  user_id: userId,
  is_finish: true,
  date: admin.firestore.FieldValue.serverTimestamp(),
});

// 今月の実績のDB取得
const now = new Date();
const startOfMonth = new Date(now.getFullYear(), now.getMonth(), 1);
const endOfMonth = new Date(now.getFullYear(), now.getMonth() + 1, 0);
const snapshot = await admin
  .firestore()
  .collection("t_status_daily")
  .where("user_id", "==", userId)
  .where("is_finish", "==", true)
  .where("date", ">=", admin.firestore.Timestamp.fromDate(startOfMonth)) // 開始日以上
  .where("date", "<=", admin.firestore.Timestamp.fromDate(endOfMonth)) // 終了日以下
  .get();

実装については以上です。上記を組み合わせることで今回の機能を実現しています。

導入してみての感想

家事タスクの実施状況がわかるようになったのでお互い進んで家事をやるようになりました。

LINE を使用することで習慣化しやすく、改めて LINE というプラットフォームの強さを感じました。

今後の拡張としては、レポート機能に LIFF を使用することでよりリッチな UI を実現できるのでやってみたいと思います。 developers.line.biz

運用コストについて

Cloud Functions for Firebase を使うには従量課金プランにする必要がありますが、月200万回までは無料で利用できます。 ログ出力やデプロイのアーティファクト作成により CloudStorage の料金が発生しますが、毎月1~2円ほどでしたのでほとんど無料で利用できています。 LINEMessagingAPI も月に 200 通までは無料で利用できます。

最後に

今回は日常の困りごとを IT で解決した事例を紹介しました。 エンジニアとして「日々の困りごとをどうすればより効率的に、どうすればより快適にできるのか」をユーザー視点で考えることはとても重要なのでこういった取り組みは大切だと思いました。 最後までご覧いただきありがとうございました。

alt

奥野 堅斗 Kento Okuno

プロダクト開発統括部 グロース開発部 dodaサイト開発1グループ リードエンジニア

2022年1月にパーソルキャリアへ中途入社。2023年4月にdodaサイトのマイクロサービスPJTに参画。

※2024年12月現在の情報です。