Specification
nia Reminder 仕様書
自然な日本語でLINE/Chatworkへリマインダーを送るWebアプリ「nia Reminder」のCloudflare版(現行本番)の詳細仕様書。実際のソースコードを精査して作成している。
0概要
スマートフォン/PCから自然文で「いつ・誰に・何を」送るかを入力するだけで、指定日時にLINEまたはChatworkへ自動でメッセージを送信するWebアプリ。元はGoogle Apps Script(GAS) + Google Sheetsで構築されていたが、現在はCloudflare Pages + D1へ完全移植されている。
- 「明日の11:45にリーダーCWへ締め直しお願いします」のような自然文を解析し、送信先・日時・繰り返しを自動抽出する
- 送信先(Destination)はLINE Group ID / Chatwork Room IDを直接持たず、必ず内部IDを経由する(グループ作り直し時もリマインダーの再作成が不要)
- 送信失敗は自動リトライ(即時→1分後→5分後)し、3回失敗で確定的に失敗扱いとする
- LINEグループの退出/解散は、Webhookのleaveイベントと6時間毎の定期ヘルスチェックの両方で検知する
1技術スタック
| 領域 | 技術 | 備考 |
|---|---|---|
| フロントエンド | 素のHTML + CSS + JavaScript | フレームワーク不使用。public/index.html(9画面) + public/pending.html |
| バックエンドAPI | Cloudflare Pages Functions | functions/api.js。/apiへの単一POSTエンドポイント(RPC方式) |
| データベース | Cloudflare D1(SQLite互換) | 5テーブル |
| 定期実行・Webhook | 独立したCloudflare Worker(notify-worker/) | 1分毎cron・6時間毎cron・毎朝6:00 cron・LINE Webhook受信を1つに統合 |
| 認証 | Web Crypto API(HMAC-SHA256) | チームパスワード + 署名付きトークン方式 |
| 外部連携 | LINE Messaging API / Chatwork API | トークンはCloudflare Secretsで管理 |
2アーキテクチャ
リクエストは3系統。(A) ブラウザ→Pages Functions→D1 のオンデマンドAPI、(B) notify-worker→D1/外部API の定期実行、(C) LINE→notify-worker→D1 のWebhook受信。全てD1(nia-reminder-db)を共有する。
(A) オンデマンドAPI
(B) Scheduler(1分毎)/ HealthCheck(6時間毎)
0 */6 * * *
(C) LINE Webhook
LockService(スクリプト全体ロック)は、D1の行単位CAS(UPDATE Reminders SET status='PROCESSING' WHERE status='SCHEDULED'、影響行数1件で成功とみなす)に置き換えている。二重送信しないという挙動は同じだが、GASより粒度が細かい。3データモデル
D1(SQLite)上の5テーブル。ReminderからLINE Group ID / Chatwork Room IDを直接参照せず、必ずDestinationを経由する構造を維持している(最重要設計原則)。
Reminders
| 列 | 内容 |
|---|---|
reminder_id | PK。RMD_yyyyMMdd_XXXXXX |
destination_id | 送信先(Destinations.destination_id) |
scheduled_at / next_run_at | +09:00固定のISO文字列。次回はrecurrenceから計算 |
status | DRAFT/SCHEDULED/PROCESSING/SENT/CANCELLED/FAILED/PAUSED_DESTINATION |
recurrence_type / recurrence_rule | ONCE/DAILY/WEEKDAYS/WEEKLY/MONTHLY。WEEKLY/MONTHLYはJSON文字列で詳細を持つ |
failed_count | 連続失敗回数としてリトライ管理に使い回す(専用カラムを増やさない設計) |
request_id | 二重登録防止用の一意キー |
Destinations / DeliveryLogs / WebhookEvents / Settings
| テーブル | 役割 |
|---|---|
Destinations | 送信先マスタ。channel(LINE/CHATWORK)・external_id・status(ACTIVE/DISCONNECTED/DISABLED) |
DeliveryLogs | 追記専用の送信結果ログ(SUCCESS/FAILED/RETRY/SKIPPED) |
WebhookEvents | LINE Webhookイベントの記録。event_idで二重処理防止 |
Settings | 現行仕様では未使用(将来用に予約) |
4認証・権限モデル
GAS版はGoogle Workspaceドメイン限定のWebアプリで保護されていたが、Cloudflareには相当機能が無いため、nia-shiftと同じ「チームパスワード + 署名付きトークン」方式(functions/lib/auth.js)に置き換えている。
- 共有パスワード(
ACCESS_PASSWORD)をverifyTeamAccessに渡し、成功するとHMAC-SHA256署名付きトークン(有効期限6時間)を発行 - 全APIハンドラが
teamTokenを必須引数として検証する(_assertTeamAccess) public/pending.html・public/spec.html・public/infra-map.html・public/structure.htmlも同じゲートで保護- Ver.1は管理者利用前提(個人ログイン・RBACは無し)。将来USER/LEADER/MANAGER/OWNERへ拡張可能な構造にはしてある
5API リファレンス
POST /api に {"fn": "関数名", "args": [teamToken, ...]} を送るRPC方式。応答は{ok:true, result} / {ok:false, error:{code, message}}。
| 関数 | 認可 | 概要 |
|---|---|---|
verifyTeamAccess | 公開 | チームパスワード検証・トークン発行 |
parseReminderText | チーム | 自然文解析(RuleParser→AIParser) |
previewReminder | チーム | 保存せず確認画面表示用の整形のみ |
createReminder | チーム | 登録。request_idで二重登録防止 |
getReminder / listReminders | チーム | 取得・一覧(statusフィルタ) |
updateReminder / cancelReminder | チーム | 編集・キャンセル |
listDestinations / getDestination / createDestination | チーム | 送信先の一覧・取得・手動登録 |
listPendingLineGroups | チーム | 未登録の新規LINEグループ一覧(pending.html用) |
registerPendingLineGroup | チーム | 検知済みグループへ名前を付けてDestination登録 |
getUsageStatus | チーム | D1ストレージ使用量・LINE月間メッセージ枠の最新値を返す(保存済みの値を読むだけ) |
getRawDump | チーム | 指定テーブルの生データ(先着50件)。設定画面のDBダンプ用 |
getRawDumpTables | チーム | ダンプ可能なテーブル名一覧(許可リスト) |
5.1使用量チェック(D1ストレージ・LINE月間メッセージ枠)
notify-workerの3つ目のcron(毎朝6:00 JST = 21:00 UTC、functions/lib/usageCheckService.js)が2つの値を定期チェックし、Settingsテーブルへ保存する。設定画面はこの保存済みの値をgetUsageStatus経由で読むだけで、外部API認証情報はPages Functions側には一切持たせない。
- D1ストレージ: Cloudflare管理API(
GET /accounts/{id}/d1/database/{id})のfile_sizeを取得し、5GB(想定の無料枠上限)に対する割合を計算する。専用のAPIトークン(Account>D1>Read権限のみ)をCF_API_TOKENとしてnotify-workerにのみ設定する - LINE月間メッセージ枠:
GET /v2/bot/message/quota(上限。typeがlimitedならvalueに上限数)とGET /v2/bot/message/quota/consumption(当月の送信済み数)の2本を叩き、割合を計算する。従量課金プラン等で上限が無い(type: 'none')場合はパーセント表示をしない
6自然言語Parser
functions/lib/ruleParserService.js。信頼度スコア(初期値0.5)を各段階で加減点しながら、繰り返し→日付→時刻→午前午後→送信先の順に抽出する。
- 日付: 今日/明日/明後日(各+0.15)、○月○日(+0.2、過去日付は翌年に繰り上げ)、曜日指定(来週/今週プレフィックス対応、+0.18)
- 時刻: HH:mm・○時○分(+0.25)、曖昧語「朝/昼/夕方/夜」(+0.05、警告付きで仮確定)、特定不可ならデフォルト9:00(-0.2)
- 送信先: 文中に登録済み送信先名が複数マッチしたら最長一致を優先(+0.2)。同点複数なら警告付きで仮確定(-0.1)
- 信頼度が
PARSER_CONFIDENCE_THRESHOLD(0.6)未満だとAIParserへフォールバック(現状スタブのみ、未実装)
ruleParserService.js(JS Date基準 0=日〜6=土)とrecurrenceService.js(ISO基準 1=月〜7=日)で別実装。意図的に統一していない(GAS版からの移行時に挙動を変えないため)。7Scheduler・Retry
functions/lib/schedulerService.js。notify-workerの1分毎cronからrunScheduler(db, env)が呼ばれる。
findDueReminders: status=SCHEDULED AND next_run_at<=現在時刻- 各リマインダーをD1 CASでPROCESSINGへ(競合時はスキップ)
- 送信先ACTIVE確認 → 送信 → 結果に応じてSENT/SCHEDULED(次回計算)/リトライ/FAILED
- 送信先レベルのエラー(LINE_TARGET_ERROR/CHATWORK_PERMISSION_ERROR)は
disconnectDestinationを自動発火
リトライ: failed_countを連続失敗回数として使い回す。1回目失敗→1分後、2回目失敗→5分後、3回目でFAILED確定。
8Webhook・HealthCheck
POST /webhook:x-line-signatureをHMAC-SHA256で検証(functions/lib/lineSignature.js)。失敗イベントは処理しないjoin: グループ名取得を試み、WebhookEventsへ記録。Chatwork通知(任意設定)leave: 対象DestinationをDISCONNECTED化し、関連するSCHEDULEDなReminderをPAUSED_DESTINATIONへ- 6時間毎の
runHealthCheck: ACTIVEな送信先へ生死確認APIを叩き、不健全ならdisconnectDestinationと同じ処理を実行(Webhookに依存しない補完的検知)
9フロントエンド
public/index.html(1ファイルSPA)。9画面: ホーム/解析確認/登録完了/予定一覧/詳細/編集/送信先一覧/送信先追加/設定。送信先セレクトはLINE/Chatworkでoptgroupにグループ化。public/pending.htmlは独立ページ(Chatwork通知からの導線を素早く保つため)。
「設定」画面には、未登録LINEグループ・仕様書/運用基盤マップ/ソースコード構成へのリンクに加え、使用量(D1ストレージ・LINE月間メッセージ枠。色分け表示、70%で黄・90%で赤)とDBダンプ(テーブル選択→先着50件を表形式で表示)を配置している。
10開発の経緯
GAS + Google Sheets(Ver.1完成) → 一部機能(LINE Webhook)のみCloudflare Workers化 → 今回、GASを完全廃止してCloudflare Pages + D1へ全面移行。観測可能な挙動(API入出力・parserの解析結果・retry/recurrenceの計算)は変えず、GAS特有API(LockService・PropertiesService)のみ同等のCloudflare実装に置き換えた。テストはGAS版のほぼ全ケースを移植(108件、Node標準テストランナーのみで実行可能)。
11既知の制限
- AIParserServiceは骨組みのみ(実プロバイダ未実装)。RuleParserの信頼度が低い場合は警告を付けてそのまま返す
- MONTHLY繰り返しは1〜28日のみ許可(29〜31日は月によって存在しないため)
- 個人ログイン・RBACは未実装(Ver.1は管理者利用前提)
- Settingsテーブルは現行仕様でも未使用
本仕様書は実際のソースコード(functions/, public/, notify-worker/)を精査して作成。コードと差異がある場合はコードを正とする。関連: 運用基盤マップ / ソースコード構成