Track daily work hours by chatting with your own Telegram bot. Messages are parsed, stored in MongoDB, surfaced in quick summary commands, and optionally translated into pay estimates (weekday/weekend/holiday). Scheduled reminders keep you logging on time.
- Webhook: Telegram sends updates to
/api/bot; we authenticate the sender and route either to command handlers or work-log parsing. - Logging: Natural language like “Worked 6 hours on design yesterday” becomes a normalised entry (
date,hours,tag,raw_message,timestamp). - Summaries:
/summary,/today,/paycycle,/paycycles, etc. aggregate from MongoDB and return Markdown responses with pay breakdowns when configured. - Reminders: GitHub Actions triggers
/api/reminderfour times daily (only one run sends); the reminder engine checks the 3–11 PM window inDEFAULT_TIMEZONE. - Tags & pay: Any entry whose tag matches
PAY_RATE_HOLIDAY_TAGSuses the holiday rate./categorywithout arguments shows all discovered tags so you can drill into one.
For deeper architecture context, see the Technical Overview.
- Create a bot with @BotFather and copy the token.
- Get your Telegram user ID via @userinfobot.
- Provision MongoDB (Atlas recommended) and copy the connection string.
- Set the environment variables (below) on your host.
- Deploy (Vercel/Railway/Render/Fly or
node bot.jslocally). - Register the webhook:
curl https://api.telegram.org/bot<token>/setWebhook -d '{"url":"https://<domain>/api/bot"}'. - DM the bot:
Worked 6 hours today→ expect a ✅ reply.
| Command | Description |
|---|---|
/summary |
Weekly & monthly totals with weekday/weekend/holiday breakdowns and pay estimates (if rates configured). |
/today |
Today’s entries, time/tags per line, holiday marker, pay totals. |
/log |
Latest 5 entries (newest first). |
/category [tag] |
Lists all tags when omitted; totals and entry count when provided. |
/paycycle [tag] |
Current pay-cycle hours plus a detailed entry list (capped 50). Works standalone or with optional tag to filter by project. |
/paycycles [tag] |
Last 5 pay cycles summary with totals and pay estimates. Works standalone or with optional tag to filter. |
/help |
Cheat sheet including configured pay rates. |
/delete … |
Preview/delete the most recent entries (confirmation required). |
/stats, /validate, /reset confirm, /backup, /reminder … |
Admin utilities. |
The /paycycle and /paycycles commands work standalone and support optional tag filtering to view earnings for specific projects/categories:
/paycycle → Shows all entries in current pay cycle (default)
/paycycle project1 → Shows only "project1" entries
/paycycle freelance → Shows only "freelance" entries
/paycycles → Shows last 5 cycles with all entries (default)
- Tag parameter is completely optional - commands work fine without it
- Filtering uses case-insensitive partial matching (e.g., "proj" matches "project1", "project2")
- Displays filtered count vs total entries when active
- Works across all 5 pay cycles when using
/paycyclesThe API Quick Reference describes each command’s response format.
# Required
TELEGRAM_BOT_TOKEN=...
AUTHORIZED_USER_ID=...
MONGODB_URI=...
# Optional pay/rate settings
PAY_RATE=45.0
PAY_RATE_WEEKDAY=45.0
PAY_RATE_WEEKEND=60.0
PAY_RATE_SATURDAY=55.0
PAY_RATE_SUNDAY=65.0
PAY_RATE_HOLIDAY=80.0
PAY_RATE_HOLIDAY_TAGS=holiday,public_holiday,public holiday
PAY_RATE_HOLIDAY_MESSAGE=Public Holiday
PAY_RATE_CURRENCY=AUD
PAY_RATE_LOCALE=en-AU
PAY_RATE_SYMBOL=$
# Date format preference (optional)
DATE_FORMAT_PREFERENCE=DMY
# Options: DMY (DD/MM/YYYY, default) or MDY (MM/DD/YYYY)
# Pay cycle configuration (optional)
PAY_CYCLE_START_DATE=2025-08-04
# Format: YYYY-MM-DD (recommended to be a Monday for easier tracking)
# Default: 2025-08-04 - All pay periods are calculated from this date
PAY_CYCLE_LENGTH_DAYS=14
# Number of days in each pay cycle
# Default: 14 (bi-weekly/fortnightly)
# Reminder API authentication
REMINDER_SECRET=...
- Leave the pay variables unset or zero to hide earnings in outputs.
- Holiday pay: Activates when the message tag matches any value in
PAY_RATE_HOLIDAY_TAGS. Examples:"Worked 8 hours on holiday"→ Uses holiday pay rate"7.5 hours on public holiday"→ Uses holiday pay rate"6 hrs on public_holiday"→ Uses holiday pay rate
DEFAULT_TIMEZONEinsidesrc/bot/services/reminder.jsdefaults to Australia/Melbourne; change it (and cron schedules) if you deploy elsewhere.
- Workflow:
.github/workflows/daily-reminder.yml(four cron slots per day; deterministic slot selection). - Secrets:
BOT_WEBHOOK_URL,AUTHORIZED_USER_ID,REMINDER_SECRET(optionalREMINDER_RANDOM_SEED). - Endpoint:
POST /api/reminderwithAuthorization: Bearer <REMINDER_SECRET>. - See the GitHub Actions Guide for cron tuning and troubleshooting.
- Mongo collection
work_entrieswith{ date, hours, tag, raw_message, timestamp }and indexes ondate,tag,timestamp. - Key folders:
api/→ Webhook + reminder handlers.src/bot/handlers/→commands.js,messageParser.js.src/bot/services/→database.js,reminder.js.
| Issue | Quick check |
|---|---|
| Bot silent | curl getWebhookInfo; confirm HTTPS domain + AUTHORIZED_USER_ID. |
| Reminders missing | Inspect GitHub Actions logs; ensure run time is inside 3–11 PM in DEFAULT_TIMEZONE and REMINDER_SECRET matches. |
| Pay/holiday missing | Check PAY_RATE* env vars > 0 and log message contains a recognised tag. |
/category empty |
Log entries with …on <tag> then rerun /category. |
- Technical Overview – architecture, modules, env vars.
- API Quick Reference – endpoints & command formats.
- Deployment Guide – platform setup + cron secrets.
- GitHub Actions Guide – reminder workflow.
- Testing Checklist – post-deployment smoke tests.
- Reset Guide –
/resetflow and recovery.