- Require both symbol+code in /setcurrency; add preset picker ($ USD,
T Toman, IRR Rial, EUR, GBP) in settings inline menu
- League: compact 2-line per entry for mobile; sort buttons in 2x2 grid
- Settings: 2-column layout with grouped buttons (timezone+calendar,
report+report time, break+accent, rpg+league, currency+rate)
- Burnout: cap break ratio at 100% to prevent nonsense values
- Add /setusername command (uses existing SanitizeDisplayName)
- Update /help with new/changed commands
- Add /summary [YYYY-MM] command for monthly work/break/day-off totals
- Add weekly totals row to main menu status
- Add /setbreak <minutes> for configurable daily break threshold
- Fix HandleMessage routing: extract command name instead of exact match (fixes /export YYYY-MM, /edit, /note, /summary, /setbreak)
- Update README with full command table and configurable break docs
- Replaced tgbotapi with go-telegram/bot (zero-dependency, context-aware, Bot API 10.0)
- All handler methods now accept context.Context; threading ctx through all sends/edits
- Changed from function-based (NewMessage/NewEditMessageText/NewInlineKeyboardMarkup) to struct-param API (SendMessageParams/EditMessageTextParams/InlineKeyboardMarkup)
- Added colored buttons: DEL buttons in calendar use Style: 'danger' (red)
- Both polling and webhook modes preserved with new library patterns
- Context-based shutdown (signal.NotifyContext) replaces stop channel
- GenerateMonthlyReport now takes explicit Gregorian start/end range,
computed via monthGregorianRange, so Jalali/Hijri months spanning
two Gregorian months are fully covered in exports.
- Added loadLocation helper (UTC fallback on error) and replaced all
11 ignored-error call sites in handlers + export.
- Added periodicCleanup goroutine in NewHandler to prevent unbounded
growth of the rateLimit map.
- schemaPath is now read from SCHEMA_PATH env var (init), defaulting
to /app/db/schema.sql.
- Migrated sendDayView to use sendOrEdit helper, removing duplicated
send/edit branching.
- Removed dead code: userYearMonthToGregorian (unused), the old
startOffset function (replaced).
- Updated README with calendar docs, new env vars, full project tree.
- Updated .env.example and docker-compose.yml with SCHEMA_PATH.
- Simplified Makefile: added fmt, tidy, check, run-dev targets;
removed broken Make-glob prerequisite pattern.
- Fix Jalali/Gregorian/Hijri day-of-week startOffset formulas
(dow returns 0=Sat, not 0=Sun as the old comment claimed)
- Fix calendar and export month picker navigation jumping 2 months
(navMonth buttons already have pre-computed values; handlers
were decrementing/incrementing them again)
- Fix GetOrCreateUser/GetOrCreateDay race condition with INSERT OR IGNORE
- Add month/day bounds validation in handleEditMsg to prevent panic
- Export now uses time.Now().In(loc) for year/month so users in
positive UTC offsets get the correct month
- Export month title for Jalali/Hijri now correctly converts the
first day of the Gregorian month to the target calendar instead
of using the Gregorian month index to look up target month names
- New /settimezone <IANA name> command to change user timezone
- Shows current timezone in settings menu
- timezone callback shows current value and instructions
- Updated /help with the new command
buildStatusText and buildDailyReport now use formatDateForCalendar
to convert the stored Gregorian date to the user's calendar type
(jalali/hijri/gregorian).
Previous approach (send \u200C then immediately delete it) lost the
reply keyboard because Telegram may not persist the keyboard from a
deleted message. Now we delete the OLD keyboard message and keep the
new one, ensuring the reply keyboard is always present and correct.
- Hijri (قمری) calendar added as 3rd calendar option with tabular conversion
- Calendar type select menu (like accent) with gregorian/jalali/hijri
- Calendar type applied to exports and reports
- Midnight crossover: virtual splits at computation time (no synthetic DB events)
- Handles both UTC and local timezone boundaries
- Timezone changes handled naturally (recomputed on-the-fly)
- Rate limiting per user ID instead of chat ID
- WAL mode enabled for SQLite
- Smart reply keyboard updates after every state change
- Overlapping time edits rejected (must preserve in/out alternation)
- Single event deletion warns if timeline needs repair
- Day off no longer prevents clock in/out
- Accent colors shown with descriptive names in select menu
- Delete button label changed to DEL for visual distinction
- Midnight crossover: auto-close previous day session on clock-in
- Rate limiting: 2s cooldown per chat_id in both messages and callbacks
- Smart reply keyboard: single button changes to Clock In/Out based on state
- Status info on main menu shows: date, work type, working status, today's total
- Settings sub-menu with inline buttons for accent, report toggle, report time
- Accent selection inline keyboard with > indicator and back button
- Notes displayed in daily report (e.g. 'auto midnight')
- Back buttons on all sub-menus (work type, accent, settings)
- Settings button added to main menu
- Add chat_id to events, settings, and days_off for per-user data
- Add proper graceful shutdown (signal handling, WaitGroup)
- Separate polling and webhook modes with goroutine management
- Add schema migration from single-user to multi-user schema
- Refactor handlers with shared actionFunc pattern (msg + callback)
- Fix schema path for Docker deployment (/app/db/schema.sql)
- Remove emoji from output text for cleaner formatting
- Show main menu with Clock In/Out, Report, Export, Remote, Day Off buttons
- Each action result includes a 'Back to Menu' button
- Handle callback queries alongside text commands
- Delete stale webhook before switching to polling
- Log i18n initialization error instead of silently ignoring it