All /setrate, /setcurrency, /settimezone, /setbreak, /setusername,
/setreporttime now send a confirmation message, matching the UI flow.
Also fixes rate confirmation to show the actual currency symbol.
- ComputeDailyTotals: only assume working from dayStart when first event
is 'out' (cross-midnight session); otherwise start idle. Fixes inflated
work/break counts in reports, status, export, burnout, salary, and RPG.
- leagueToggleCallback: add settings back keyboard when RPG is disabled.
- webhook: add graceful HTTP server shutdown on context cancellation.
- Add delete all data button with red danger style and confirmation prompt
- Add DeleteUserData repo method that wipes user from all tables in a transaction
- Add helper methods deleteAccountPrompt and deleteAccountConfirm
- Implement consistency_master, perfect_week, remote_veteran, onsite_master achievements
- Add GetDailyXPInRange and SumWorkSecondsByWorkType repo methods
- Update Dockerfile to alpine3.23, docker-compose to use env vars for proxy/paths
- Fix 'string(d)' int-to-rune conversion in rpg_test.go by using fmt.Sprintf
- Fix ComputeDailyTotals dayStart logic: initial state should be StateWorking
when dayStart > 0, correctly counting work from dayStart to first out event
- Update README project structure to reflect new domain/handler/repo layout
- Implement missing achievements: rpg_pioneer (on RPG enable) and
salary_setter (on first rate set)
- Add tryUnlockAchievement helper to Handler
- handleSelectCallback: disabling RPG also disables League; enabling
League when RPG is off shows toast error via answerCbWithText
- handleLeagueToggleCmd (/leaguetoggle): rejects with message if RPG off
- leagueToggleCallback (inline): rejects with message if RPG off
- buildSettingsKeyboard: League status shows as 'off' when RPG is disabled
(even if the DB still has LeagueOptIn=true, since RPG disable forces it false)
- Add GetDistinctEventDates, UpdateEventWorkType, UpdateEventTime,
DeleteEvent, GetEventByID store methods to calendar.go
- Replace all 5 raw h.DB.DB().Exec/Query calls with proper store methods
- Remove unused database/sql import from calendar.go
- Rewrite GetLastEvent to use QueryRow instead of Query+manual iteration
(fixing row-vs-rows naming issue; removing rows.Close boilerplate)
- Add errors import to store.go for errors.Is
- Fix nil pointer dereference in clock.go (guard GetOrCreateSettings and
GetOrCreateRPGStats errors before accessing fields)
- Fix timezone bug in handleSetBreakMsg (was using system time.Now()
instead of user's configured timezone)
- Remove dead code: sendExportMonthPicker (unused wrapper)
- Simplify xpForWorkSeconds (xpPerSecond=1, inline to just return sec)
- Extract computeStreakFromDates as pure testable function from
recalcAggregates; add 6 tests covering empty, current, gaps,
no-today, long streak, longest-not-current edge cases
- Update README with full feature documentation: RPG, League, Salary,
Burnout, Work Types, Inline History Editing, all missing commands
- 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
- 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
buildMonthlySummary now includes hours for days marked as day-off, matching buildStatusText behavior. Shows "Xh worked (Day Off)" when a day-off day has events.
- 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
- Empty day view now shows Add IN / Add OUT buttons
- Day view with events also shows Add IN / Add OUT below existing events
- Hour picker → minute picker (15-min intervals) → event creation flow
- Back button returns to the day view
- 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.