LINEアプリの開発手順(2)-リマインドメッセージ

アプリ

LINEアプリのメッセージは、ユーザーアクションをきっかけに送信されます。例えば予約完了のお知らせは、ユーザーの予約申し込みアクションに基づいて送信されます。では、予約一日前とか2時間前とかにリマインドメッセージを送信する場合にはどうするのか、解説したいと思います。

メッセージの送信は基本的にはユーザーアクションをきっかけにする必要があります。最初のメッセージ送信は「ユーザーによる予約」アクションに対して実行されることになりますが、そのメッセージ送信に対する戻り値として、後続メッセージ送信のための「サービス通知トークン」が得られるので、それを後続メッセージを送信で使うことになります。後続メッセージを送信する際に「チャンネルアクセストークン」(LINEアプリにログインしていなくてもサーバーのみで発行可能)を発行し、このアクセストークンをヘッダーに、サービス通知トークンをボディとして記述することでメッセージ送信が可能になります。一方、「予約一日前」とか「2時間前に送信」にメッセージを送信するアクションの起点は、例えばAWS Lambdaなどを使って(予約データ確認とメッセージ送信を行う)APIを定時実行する関数を用意することになります。ここではアプリをデプロイしているrender.comのサービスを使います。

render.comではCron Jobの仕組みを利用して、サーバーレス関数(Serverless Functions)を定期的に実行することができます。Cron Jobは、UNIX系オペレーティングシステムで利用されるスケジューリング機能で、定期的に繰り返し実行するタスク(Cronタスク)を自動化するための仕組みです。render.comのコンソールにて上部メニューの「+」からCron Jobを選択すれば簡単に設定できます。(Cron Jobは定額$1/month + バックグランド実行時間に応じた従量料金がかかります)。設定のポイントとなる項目は、「Schedule」と「Command」です。

Schedule: Cronの書式は5つの数字からなります。左から、分 時 日 年 曜日 を表す数字で、例えば、毎日0時0分(世界標準時)にタスクを実行するには以下になります。

0 0 * * *

Command:LINUXのコマンドです。例えば”/api/cron”のapiを実行したいとすると

curl https://{url root}/api/cron

となります。本番環境ではセキュリティを考慮した対応が必要です。

api/cronの内容は例えばこんな感じ。

//予約前日の日時が取得されたとする。この翌日に予約が入っているユーザーに送信する
export async function GET(request: NextRequest) {
    let date = new Date() 
    date.setDate(date.getDate()+1) //翌日のDateオブジェクトを取得
    const tomorrowStr = date.toISOString() //標準時で9時間遅れ。正午ならば日付は日本と同じ
    const tomorrow = tomorrowStr.split("T")[0] //翌日の日付

    //ステートレスアクセストークンを取得する関数(詳細は前回記事)
    const accessToken = await issueChannelAccessToken()

    try {
        //予約日がtomorrowと一致する予約を抽出
        let { data: reservations, error } = await supabase
          .from('reservation')
          .select("*")
          .eq("day",tomorrow)

          if (reservations){
            for (const reservation of reservations){
                const date = reservation.start
                const message:Message2 = {
                    templateName:"remind_d_b_ja",
                    params:{
                        date: date,
                        address: "----",
                        daytime: "1日",
                        shop_name: reservation.shop,
                        charge_name: reservation.staff,
                        reservation_contents: "カット"
                      },
                    notificationToken: reservation.notificationToken
                }
                //後続メッセージを送信する関数(基本は最初のメッセージと同じ)
                const postData = await sendServiceMessage2(accessToken, message)
                //notificationTokenを更新
                updateReservation(reservation.id, postData.notificationToken)
            }
            return NextResponse.json({date:tomorrowStr, reservation:reservations})
          } else {
            return NextResponse.json({ error: "エラー" }, { status: 500 });
          }
    } catch (error) {
        return NextResponse.json({ error: "エラー" }, { status: 500 });
    }
}

以上により、予約アプリのメッセージ関連の実装が完了しました。