- Restore TotalWorkSeconds, CurrentStreak, LongestStreak writes in computeAndAwardXP and updateStreak (incremental on clock out) - Add recalcDayWorkSeconds and recalcAggregates for event edit path (recompute day + total + streak from scratch when events change) - Call recalcAggregates after editEventTimeSet, deleteEventConfirm, addEventDo in calendar.go - Remove runtime computeStreaks and totalWorkSeconds (N+1 scan) from RPG display, burnout, and clockOut - Add SetDailyWorkSeconds, SumDailyWorkSeconds store methods - Clean up unused dead functions and dead code in rpg.go
WorkTime Bot
A single-user Telegram time-tracking bot with multi-calendar support (Gregorian, Jalali/Persian, Hijri). Clock in/out, toggle remote/onsite, mark day off, get daily reports at a configurable time, browse history by month, and export monthly .xlsx reports.
Quick Start
cp .env.example .env
# edit .env — set BOT_TOKEN and BOT_ALLOWED_USERS
docker compose up -d
Environment Variables
| Variable | Default | Purpose |
|---|---|---|
BOT_TOKEN |
— | Telegram Bot API token (required) |
BOT_ALLOWED_USERS |
(all) | Comma-separated Telegram user IDs (empty = allow all) |
HTTP_PROXY |
— | Outbound HTTP proxy for Telegram API |
HTTPS_PROXY |
— | Outbound HTTPS proxy for Telegram API |
DB_PATH |
db.sqlite3 |
Path to SQLite database file |
BOT_WEBHOOK_URL |
— | Set for webhook mode (omit for long-polling) |
BOT_LISTEN |
:8080 |
Listen address for webhook mode |
BOT_TLS_CERT |
— | TLS certificate path (webhook HTTPS) |
BOT_TLS_KEY |
— | TLS key path (webhook HTTPS) |
Calendar Support
Users can choose between three calendars via /settings:
- Gregorian — standard international calendar
- Jalali (Persian/Solar Hijri) — official calendar of Iran and Afghanistan
- Hijri (Islamic lunar) — used in many Muslim-majority countries
The history view, export month picker, and date displays all adapt to the user's chosen calendar. The /export and /edit commands accept dates in the user's calendar. Internally, all dates are stored as Gregorian.
Usage
Persistent reply keyboard at the bottom of the chat: Clock In / Clock Out
Inline menu on /start:
| Button | Action |
|---|---|
| Clock In / Clock Out | Record work event |
| Report | Today's work & break summary |
| Export | Pick a month → download .xlsx |
| History | Browse past days in calendar view |
| Toggle Remote/Onsite | Switch work type |
| Day Off | Mark today as day off |
| Settings | Timezone, accent, calendar type |
Commands
| Command | Action |
|---|---|
/start |
Show inline menu |
/clockin |
Record clock-in |
/clockout |
Record clock-out |
/remote |
Toggle remote/onsite |
/report |
Today's work & break summary |
/export [YYYY-MM] |
Export monthly report (optional YYYY-MM in your calendar) |
/summary [YYYY-MM] |
Show monthly summary (optional YYYY-MM) |
/dayoff |
Toggle today as day off |
/edit YYYY-MM-DD |
View/edit events for a specific date |
/note [DATE] HH:MM <text> |
Set note for an event at a specific time |
/settimezone <IANA> |
Set timezone (e.g. Asia/Tehran) |
/setbreak <minutes> |
Set minimum break threshold (0-480, default 5) |
/setaccent |
Select export accent color |
/setreporttime HH:MM |
Set daily auto-report time |
/reporttoggle |
Enable/disable daily auto-report |
/settings |
Open settings menu |
Daily Report
A daily summary is sent automatically at the user's configured report time (default 23:00). The scheduler checks every 30 seconds.
How Break Time Works
The gap between a clock-out and the next clock-in is counted as break. Gaps shorter than the min break threshold (default 5 minutes) are absorbed into work time. Use /setbreak <minutes> to adjust per day (0-480).
09:00 Clock In → work starts
12:00 Clock Out → work: 3h, break starts
13:00 Clock In → break: 1h, work resumes
17:00 Clock Out → work: 4h
total: 7h work, 1h break
Export
The /export command opens a month picker. Select any month in your calendar and download an .xlsx file with columns: Date, Clock In, Clock Out, Type, Work, Break.
Four color themes are available in settings: Ocean, Beach, Rose, Catppuccin.
Project Structure
├── cmd/bot/
│ ├── main.go Entrypoint, polling/webhook, scheduler
│ └── webhook.go Webhook mode with TLS support
├── internal/
│ ├── bot/
│ │ ├── handlers.go Message/callback routing, views, menu builders
│ │ ├── totals.go Break/work computation state machine
│ │ ├── export.go Excel (.xlsx) report generation
│ │ └── dateutil.go Gregorian/Jalali/Hijri conversions & calendars
│ └── db/
│ ├── store.go SQLite store
│ └── migrations/ Goose-managed SQL migrations
│ └── 001_init.sql Initial schema
├── .gitea/workflows/
│ └── ci.yml Gitea Actions CI
├── Dockerfile
├── docker-compose.yml
├── Makefile
├── .env.example
└── README.md
Build & Run
# Local (requires Go 1.26+ and CGo-free SQLite)
make run
# Docker
docker compose up -d --build