Reminders & Cron
The NIVAA Telegram bot sends automated reminders to educators via Cloudflare Cron Triggers. There are two reminder types: a daily morning digest and pre-session alerts.
Daily 7am SGT Digest
Every morning at 7:00 AM Singapore Time (SGT, UTC+8), each linked educator receives a personalized digest message.
Message Contents
- Personalized greeting with the tutor's display name
- Today's session count
- For each session: time range, student name, and address with a Google Maps link
- Count of session notes pending upload (sessions where the tutor has checked out but not yet submitted notes)
- If no sessions are scheduled, the message says so
Good morning, Alex!
2 sessions today:
- 09:00-10:00 - Emily Chen
Open in Maps
- 14:00-15:00 - James Tan
Open in Maps
1 session note pending upload.
Have a great day!Implementation Details
- Only tutors with linked Telegram accounts receive the digest
- The SGT date is computed as
Date.now() + 8 * 3600000(UTC offset) - Sessions with status
DisputedorCancelled_By_Tutorare excluded - Address is parsed from the child profile's
address_dataJSON field
Pre-session 1-hour Reminders
Approximately 1 hour before each session, the assigned tutor receives a targeted reminder.
Message Contents
- Session date and time
- Student name
- Address and Google Maps link
- "Check In Now" inline button (if the 30-minute check-in window is already open)
- Note about when check-in opens (30 mins before start)
Session in ~1 hour!
2026-03-27 at 14:00-15:00
James Tan
123 Orchard Road, Singapore 238888
Open in Google Maps
Check-in opens 30 mins before start.
[Check In Now]Timing Logic
The cron job runs every 15 minutes. To avoid missing sessions, it looks for sessions starting in the 45-75 minute window from the current time. This ensures each session is caught exactly once within the 15-minute cron interval.
Deduplication
Both reminder types use the telegram_reminder_log table to prevent duplicate sends. This is essential because cron jobs may overlap or retry.
| Reminder Type | Dedup Key | Lookback Window |
|---|---|---|
daily_7am | chat_id + reminder_type | 20 hours (ensures one per day even with timezone edge cases) |
pre_session_1hr | chat_id + reminder_type + appointment_id | No lookback window (exact match, one per appointment) |
The daily digest uses INSERT OR IGNORE and checks for existing records within the past 20 hours. Pre-session reminders check for any existing record with the same chat_id and appointment_id, regardless of timing.
Error Handling
If sending a message fails (e.g., the user has blocked the bot or Telegram is unavailable), the error is logged to the console but does not prevent other reminders from being sent. Each reminder is sent independently in a loop.
Cron Schedule
| Trigger | Schedule | Function |
|---|---|---|
| Daily digest | 0 23 * * * (23:00 UTC = 07:00 SGT) | sendDailyReminders(env) |
| Pre-session | */15 * * * * (every 15 minutes) | sendPreSessionReminders(env) |