- 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
139 lines
3.8 KiB
Go
139 lines
3.8 KiB
Go
package bot
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/go-telegram/bot/models"
|
|
)
|
|
|
|
// LeagueSortOption represents a valid league sort column.
|
|
type LeagueSortOption string
|
|
|
|
const (
|
|
LeagueSortXP LeagueSortOption = "xp"
|
|
LeagueSortLevel LeagueSortOption = "level"
|
|
LeagueSortStreak LeagueSortOption = "streak"
|
|
LeagueSortHours LeagueSortOption = "hours"
|
|
)
|
|
|
|
// buildLeagueText returns the formatted league rankings (mobile-friendly narrow layout).
|
|
func (h *Handler) buildLeagueText(orderBy LeagueSortOption) string {
|
|
entries, err := h.DB.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
|
|
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()
|
|
}
|
|
|
|
// buildLeagueRankText returns the user's personal rank in the league.
|
|
func (h *Handler) buildLeagueRankText(userID int64) string {
|
|
entries, err := h.DB.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 ""
|
|
}
|
|
|
|
// handleLeagueMsg handles the /league command.
|
|
func (h *Handler) handleLeagueMsg(ctx context.Context, msg *models.Message) {
|
|
user, err := h.getOrCreateUser(msg.Chat.ID)
|
|
if err != nil {
|
|
h.sendText(ctx, msg.Chat.ID, "Error loading profile")
|
|
return
|
|
}
|
|
st, err := h.DB.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(LeagueSortXP)
|
|
kb := h.buildLeagueKeyboard("xp")
|
|
h.sendWithKB(ctx, msg.Chat.ID, text, kb)
|
|
}
|
|
|
|
// handleLeagueCallback handles league inline view.
|
|
func (h *Handler) handleLeagueCallback(ctx context.Context, chatID int64, msgID int, callbackID, orderBy string) {
|
|
defer h.answerCb(ctx, callbackID)
|
|
user, err := h.getOrCreateUser(chatID)
|
|
if err != nil {
|
|
return
|
|
}
|
|
st, err := h.DB.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(LeagueSortOption(orderBy))
|
|
kb := h.buildLeagueKeyboard(orderBy)
|
|
h.editText(ctx, chatID, msgID, text, &kb)
|
|
}
|
|
|
|
// buildLeagueKeyboard creates the league navigation keyboard (2x2 sort grid).
|
|
func (h *Handler) buildLeagueKeyboard(currentOrder string) models.InlineKeyboardMarkup {
|
|
type sortBtn struct {
|
|
label string
|
|
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}
|
|
}
|