- 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
112 lines
3.2 KiB
Go
112 lines
3.2 KiB
Go
package handler
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/go-telegram/bot/models"
|
|
|
|
"worktimeBot/internal/domain"
|
|
)
|
|
|
|
// -- Command --
|
|
|
|
func (h *Handler) handleLeague(ctx context.Context, msg *models.Message, user *domain.User) {
|
|
st, err := h.Repo.GetOrCreateSettings(user.ID)
|
|
if err != nil {
|
|
h.sendText(ctx, msg.Chat.ID, "Error loading settings")
|
|
return
|
|
}
|
|
if !st.LeagueOptIn {
|
|
h.sendText(ctx, msg.Chat.ID, "League participation is disabled.\nEnable it in Settings or use /leaguetoggle.")
|
|
return
|
|
}
|
|
text := h.buildLeagueText(domain.LeagueSortXP)
|
|
kb := h.buildLeagueKeyboard(string(domain.LeagueSortXP))
|
|
h.sendWithKB(ctx, msg.Chat.ID, text, kb)
|
|
}
|
|
|
|
// -- Callback --
|
|
|
|
func (h *Handler) leagueCallback(ctx context.Context, chatID int64, msgID int, cbID string, user *domain.User, orderBy string) {
|
|
defer h.answerCb(ctx, cbID)
|
|
st, err := h.Repo.GetOrCreateSettings(user.ID)
|
|
if err != nil {
|
|
return
|
|
}
|
|
if !st.LeagueOptIn {
|
|
kb := backKeyboard()
|
|
h.editText(ctx, chatID, msgID, "League participation is disabled.\nEnable it in Settings to view rankings.", &kb)
|
|
return
|
|
}
|
|
text := h.buildLeagueText(domain.LeagueSortOption(orderBy))
|
|
kb := h.buildLeagueKeyboard(orderBy)
|
|
h.editText(ctx, chatID, msgID, text, &kb)
|
|
}
|
|
|
|
// -- Builders --
|
|
|
|
func (h *Handler) buildLeagueText(orderBy domain.LeagueSortOption) string {
|
|
entries, err := h.Repo.GetLeagueRankings(string(orderBy))
|
|
if err != nil {
|
|
return "League rankings unavailable."
|
|
}
|
|
if len(entries) == 0 {
|
|
return "No participants in the league yet.\nEnable League participation in Settings to join!"
|
|
}
|
|
var b strings.Builder
|
|
orderLabel := map[string]string{"xp": "XP", "level": "Level", "streak": "Streak", "hours": "Hours"}
|
|
b.WriteString(fmt.Sprintf("WorkTime League - %s\n\n", orderLabel[string(orderBy)]))
|
|
for i, e := range entries {
|
|
rank := i + 1
|
|
name := e.Username
|
|
if name == "" {
|
|
name = "Anonymous"
|
|
}
|
|
b.WriteString(fmt.Sprintf("%d %s\n", rank, name))
|
|
b.WriteString(fmt.Sprintf(" XP:%d Lv:%d S:%d %.1fh\n", e.XP, e.Level, e.Streak, e.TotalHours))
|
|
}
|
|
b.WriteString(fmt.Sprintf("\n%d participant(s)\n", len(entries)))
|
|
return b.String()
|
|
}
|
|
|
|
func (h *Handler) buildLeagueRankText(userID int64) string {
|
|
entries, err := h.Repo.GetLeagueRankings("xp")
|
|
if err != nil || len(entries) == 0 {
|
|
return ""
|
|
}
|
|
for i, e := range entries {
|
|
if e.UserID == userID {
|
|
return fmt.Sprintf("League rank: #%d of %d", i+1, len(entries))
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func (h *Handler) buildLeagueKeyboard(currentOrder string) models.InlineKeyboardMarkup {
|
|
type sortBtn struct{ label, data string }
|
|
orders := []sortBtn{
|
|
{"XP", "league_xp"},
|
|
{"Level", "league_level"},
|
|
{"Streak", "league_streak"},
|
|
{"Hours", "league_hours"},
|
|
}
|
|
rows := [][]models.InlineKeyboardButton{}
|
|
for i := 0; i < len(orders); i += 2 {
|
|
row := []models.InlineKeyboardButton{}
|
|
for j := i; j < i+2 && j < len(orders); j++ {
|
|
label := orders[j].label
|
|
if orders[j].data == "league_"+currentOrder {
|
|
label = "> " + label
|
|
}
|
|
row = append(row, models.InlineKeyboardButton{Text: label, CallbackData: orders[j].data})
|
|
}
|
|
rows = append(rows, row)
|
|
}
|
|
rows = append(rows, []models.InlineKeyboardButton{
|
|
{Text: "Back to Menu", CallbackData: "back_menu"},
|
|
})
|
|
return models.InlineKeyboardMarkup{InlineKeyboard: rows}
|
|
}
|