refactor: break up large functions, add RPG/league/salary/burnout, fix bugs, add tests
- Refactor GenerateMonthlyReport (198→70), HandleCallback (128→85), handleHistoryCallback (131→38), handleExportCallback (100→25), checkAchievements (86→25) into extracted helper functions - Add RPG system (XP, levels, streak, achievements, burnout) - Add WorkTime League leaderboard - Add salary estimation with configurable currency/rate - Add input sanitization (SanitizeNote, SanitizeDisplayName) - Add settings select-style pickers for toggles (report, RPG, league) - Add break threshold inline picker UI - Add event notes with pending state and /note command - Add multi-calendar support (Jalali, Hijri) - Add Excel export with theme picker - Fix: getTodayBreakThreshold uses user's timezone (was UTC) - Fix: acknowledgeCallback passes real callback ID - Fix: currency symbol safety with currencySymbol() helper - Fix: count work hours in summary on day-off days - Fix: unsilence all error returns from SQL Exec/Bot API/migrations - Remove stale db/schema.sql and unreferenced computeWeekTotals - Extract constants: DateLayout, TimeLayout, secondsPerHour, etc. - Add 12 new tests (XP, salary, sanitize, currency, dateutil) - Remove duplicate package doc comment in totals.go
This commit is contained in:
@@ -24,7 +24,7 @@ func (h *Handler) clockIn(chatID int64) (string, error) {
|
||||
loc := loadLocation(user.Timezone)
|
||||
now := time.Now().In(loc)
|
||||
lastTime := time.Unix(last.OccurredAt, 0).In(loc)
|
||||
if lastTime.Format("2006-01-02") == now.Format("2006-01-02") {
|
||||
if lastTime.Format(DateLayout) == now.Format(DateLayout) {
|
||||
return "", errors.New("already clocked in")
|
||||
}
|
||||
}
|
||||
@@ -35,11 +35,12 @@ func (h *Handler) clockIn(chatID int64) (string, error) {
|
||||
if err := h.DB.CreateEvent(user.ID, day.ID, "in", &wt, now.Unix(), ""); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return fmt.Sprintf("clocked in at %s", now.Format("15:04")), nil
|
||||
return fmt.Sprintf("clocked in at %s", now.Format(TimeLayout)), nil
|
||||
}
|
||||
|
||||
// clockOut records a clock-out event for today.
|
||||
// Returns the formatted time or an error if not clocked in.
|
||||
// Also awards XP and updates streaks if RPG is enabled.
|
||||
func (h *Handler) clockOut(chatID int64) (string, error) {
|
||||
user, day, err := h.getUserToday(chatID)
|
||||
if err != nil {
|
||||
@@ -60,7 +61,30 @@ func (h *Handler) clockOut(chatID int64) (string, error) {
|
||||
if err := h.DB.CreateEvent(user.ID, day.ID, "out", nil, now.Unix(), ""); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return fmt.Sprintf("clocked out at %s", now.Format("15:04")), nil
|
||||
text := fmt.Sprintf("clocked out at %s", now.Format(TimeLayout))
|
||||
|
||||
// Award XP and update streaks if RPG enabled
|
||||
st, _ := h.DB.GetOrCreateSettings(user.ID)
|
||||
if st.RPGEnabled {
|
||||
sessionWork := now.Unix() - last.OccurredAt
|
||||
today := now.Format(DateLayout)
|
||||
isWeekend := now.Weekday() == time.Saturday || now.Weekday() == time.Sunday
|
||||
|
||||
streak, _ := h.updateStreak(user.ID, today, true)
|
||||
xp, leveledUp, newLevel, _ := h.computeAndAwardXP(user.ID, sessionWork, streak, today)
|
||||
stats, _ := h.DB.GetOrCreateRPGStats(user.ID)
|
||||
unlocked := h.checkAchievements(user.ID, stats, sessionWork, today, isWeekend)
|
||||
|
||||
text += fmt.Sprintf("\n\n+%d XP (Lv.%d)", xp, newLevel)
|
||||
if leveledUp {
|
||||
text += "\nLevel up!"
|
||||
}
|
||||
for _, a := range unlocked {
|
||||
text += fmt.Sprintf("\nAchievement: %s", a)
|
||||
}
|
||||
}
|
||||
|
||||
return text, nil
|
||||
}
|
||||
|
||||
// dayOff toggles today as a day off.
|
||||
@@ -100,11 +124,15 @@ func (h *Handler) toggleReport(chatID int64) (string, error) {
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
user.ReportEnabled = !user.ReportEnabled
|
||||
if err := h.DB.UpdateUser(user); err != nil {
|
||||
st, err := h.DB.GetOrCreateSettings(user.ID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if user.ReportEnabled {
|
||||
st.ReportEnabled = !st.ReportEnabled
|
||||
if err := h.DB.UpdateSettings(st); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if st.ReportEnabled {
|
||||
return "daily report enabled", nil
|
||||
}
|
||||
return "daily report disabled", nil
|
||||
|
||||
Reference in New Issue
Block a user