- 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
640 lines
19 KiB
Go
640 lines
19 KiB
Go
package bot
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/go-telegram/bot/models"
|
|
|
|
"worktimeBot/internal/db"
|
|
)
|
|
|
|
// handleSetReportTime parses a HH:MM argument and updates the user's report time.
|
|
func (h *Handler) handleSetReportTime(ctx context.Context, msg *models.Message) {
|
|
args := ""
|
|
if len(msg.Text) > 16 {
|
|
args = strings.TrimSpace(msg.Text[15:])
|
|
}
|
|
if args == "" {
|
|
user, err := h.getOrCreateUser(msg.Chat.ID)
|
|
if err != nil {
|
|
h.sendText(ctx, msg.Chat.ID, "Error loading profile")
|
|
return
|
|
}
|
|
st, _ := h.DB.GetOrCreateSettings(user.ID)
|
|
h.sendText(ctx, msg.Chat.ID, fmt.Sprintf("Current report time: %s\nUsage: /setreporttime HH:MM (24-hour)", st.ReportTime))
|
|
return
|
|
}
|
|
if len(args) != 5 || args[2] != ':' {
|
|
h.sendText(ctx, msg.Chat.ID, "Invalid format. Usage: /setreporttime HH:MM (24-hour)")
|
|
return
|
|
}
|
|
hours, err1 := strconv.Atoi(args[:2])
|
|
mins, err2 := strconv.Atoi(args[3:])
|
|
if err1 != nil || err2 != nil || hours < 0 || hours > 23 || mins < 0 || mins > 59 {
|
|
h.sendText(ctx, msg.Chat.ID, "Invalid time. Usage: /setreporttime HH:MM (24-hour)")
|
|
return
|
|
}
|
|
user, err := h.getOrCreateUser(msg.Chat.ID)
|
|
if err != nil {
|
|
h.sendText(ctx, msg.Chat.ID, "Error updating report time")
|
|
return
|
|
}
|
|
st, err := h.DB.GetOrCreateSettings(user.ID)
|
|
if err != nil {
|
|
h.sendText(ctx, msg.Chat.ID, "Error updating report time")
|
|
return
|
|
}
|
|
st.ReportTime = args
|
|
if err := h.DB.UpdateSettings(st); err != nil {
|
|
h.sendText(ctx, msg.Chat.ID, "Error updating report time")
|
|
return
|
|
}
|
|
h.sendText(ctx, msg.Chat.ID, fmt.Sprintf("Report time set to %s", args))
|
|
}
|
|
|
|
// handleAccentMsg shows the accent color picker.
|
|
func (h *Handler) handleAccentMsg(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 profile")
|
|
return
|
|
}
|
|
text, kb := h.buildAccentKeyboard(st)
|
|
h.sendWithKB(ctx, msg.Chat.ID, text, kb)
|
|
}
|
|
|
|
// handleSetTimezone validates and updates the user's IANA timezone.
|
|
func (h *Handler) handleSetTimezone(ctx context.Context, msg *models.Message) {
|
|
args := ""
|
|
if len(msg.Text) > 13 { // "/settimezone " is 12 chars
|
|
args = strings.TrimSpace(msg.Text[13:])
|
|
}
|
|
if args == "" {
|
|
user, err := h.getOrCreateUser(msg.Chat.ID)
|
|
if err != nil {
|
|
h.sendText(ctx, msg.Chat.ID, "Error loading profile")
|
|
return
|
|
}
|
|
h.sendText(ctx, msg.Chat.ID, fmt.Sprintf("Current timezone: %s\nUsage: /settimezone <IANA timezone>\nExamples: Asia/Tehran, Europe/London, America/New_York", user.Timezone))
|
|
return
|
|
}
|
|
loc, err := time.LoadLocation(args)
|
|
if err != nil {
|
|
h.sendText(ctx, msg.Chat.ID, fmt.Sprintf("Invalid timezone: %s\nUse IANA format, e.g. Asia/Tehran, Europe/London", args))
|
|
return
|
|
}
|
|
user, err := h.getOrCreateUser(msg.Chat.ID)
|
|
if err != nil {
|
|
h.sendText(ctx, msg.Chat.ID, "Error loading profile")
|
|
return
|
|
}
|
|
user.Timezone = args
|
|
if err := h.DB.UpdateUser(user); err != nil {
|
|
h.sendText(ctx, msg.Chat.ID, "Error saving timezone")
|
|
return
|
|
}
|
|
now := time.Now().In(loc)
|
|
h.sendText(ctx, msg.Chat.ID, fmt.Sprintf("Timezone set to %s (current time: %s)", args, now.Format(TimeLayout)))
|
|
}
|
|
|
|
// getTodayBreakThreshold returns today's break threshold in seconds for the given chat.
|
|
func (h *Handler) getTodayBreakThreshold(chatID int64) int64 {
|
|
user, err := h.getOrCreateUser(chatID)
|
|
if err != nil {
|
|
return defaultBreakThreshold
|
|
}
|
|
loc, err := time.LoadLocation(user.Timezone)
|
|
if err != nil {
|
|
loc = time.UTC
|
|
}
|
|
date := time.Now().In(loc).Format(DateLayout)
|
|
day, err := h.DB.GetDay(user.ID, date)
|
|
if err != nil {
|
|
return defaultBreakThreshold
|
|
}
|
|
return day.MinBreakThreshold
|
|
}
|
|
|
|
// handleSetBreakMsg sets the minimum break threshold for today.
|
|
func (h *Handler) handleSetBreakMsg(ctx context.Context, msg *models.Message) {
|
|
args := strings.TrimSpace(strings.TrimPrefix(msg.Text, "/setbreak"))
|
|
if args == "" {
|
|
_, day, err := h.getUserToday(msg.Chat.ID)
|
|
if err != nil {
|
|
h.sendText(ctx, msg.Chat.ID, "Error loading profile")
|
|
return
|
|
}
|
|
h.sendText(ctx, msg.Chat.ID, fmt.Sprintf("Current break threshold: %dm\nUsage: /setbreak <minutes> (e.g. /setbreak 15)", day.MinBreakThreshold/60))
|
|
return
|
|
}
|
|
mins, err := strconv.Atoi(args)
|
|
if err != nil || mins < 0 || mins > 480 {
|
|
h.sendText(ctx, msg.Chat.ID, "Invalid value. Must be between 0 and 480 minutes (8 hours).")
|
|
return
|
|
}
|
|
user, err := h.getOrCreateUser(msg.Chat.ID)
|
|
if err != nil {
|
|
h.sendText(ctx, msg.Chat.ID, "Error loading profile")
|
|
return
|
|
}
|
|
now := time.Now()
|
|
date := now.Format(DateLayout)
|
|
day, err := h.DB.GetOrCreateDay(user.ID, date)
|
|
if err != nil {
|
|
h.sendText(ctx, msg.Chat.ID, "Error updating break threshold")
|
|
return
|
|
}
|
|
day.MinBreakThreshold = int64(mins) * 60
|
|
if err := h.DB.UpdateDay(day); err != nil {
|
|
h.sendText(ctx, msg.Chat.ID, "Error saving break threshold")
|
|
return
|
|
}
|
|
h.sendText(ctx, msg.Chat.ID, fmt.Sprintf("Break threshold set to %d minutes", mins))
|
|
}
|
|
|
|
// settingsCallback shows the settings inline menu.
|
|
func (h *Handler) settingsCallback(ctx context.Context, chatID int64, msgID int, callbackID 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
|
|
}
|
|
bt := h.getTodayBreakThreshold(chatID)
|
|
text, kb := h.buildSettingsKeyboard(user, st, bt)
|
|
h.editText(ctx, chatID, msgID, text, &kb)
|
|
}
|
|
|
|
// buildSettingsKeyboard returns the settings menu text and inline keyboard.
|
|
func (h *Handler) buildSettingsKeyboard(user *db.User, st *db.UserSettings, breakThreshold int64) (string, models.InlineKeyboardMarkup) {
|
|
reportStatus := "off"
|
|
if st.ReportEnabled {
|
|
reportStatus = "on"
|
|
}
|
|
rpgStatus := "off"
|
|
if st.RPGEnabled {
|
|
rpgStatus = "on"
|
|
}
|
|
leagueStatus := "off"
|
|
if st.LeagueOptIn {
|
|
leagueStatus = "on"
|
|
}
|
|
rows := [][]models.InlineKeyboardButton{
|
|
{
|
|
{Text: fmt.Sprintf("Timezone: %s", user.Timezone), CallbackData: "timezone"},
|
|
{Text: fmt.Sprintf("Calendar: %s", user.Calendar), CallbackData: "caltype"},
|
|
},
|
|
{
|
|
{Text: fmt.Sprintf("Report: %s", reportStatus), CallbackData: "reporttoggle"},
|
|
{Text: fmt.Sprintf("Report time: %s", st.ReportTime), CallbackData: "reporttime"},
|
|
},
|
|
{
|
|
{Text: fmt.Sprintf("Break: %dm", breakThreshold/60), CallbackData: "breakthreshold"},
|
|
{Text: fmt.Sprintf("Accent: %s", st.ExportAccent), CallbackData: "accent"},
|
|
},
|
|
{
|
|
{Text: fmt.Sprintf("RPG: %s", rpgStatus), CallbackData: "rpgtoggle"},
|
|
{Text: fmt.Sprintf("League: %s", leagueStatus), CallbackData: "leaguetoggle"},
|
|
},
|
|
{
|
|
{Text: fmt.Sprintf("Currency: %s", st.Currency), CallbackData: "currency"},
|
|
{Text: fmt.Sprintf("Rate: %.2f", st.HourlyRate), CallbackData: "setrate"},
|
|
},
|
|
{{Text: "Back to Menu", CallbackData: "back_menu"}},
|
|
}
|
|
return "Settings:", models.InlineKeyboardMarkup{InlineKeyboard: rows}
|
|
}
|
|
|
|
// accentCallback shows the accent color picker (inline edit).
|
|
func (h *Handler) accentCallback(ctx context.Context, chatID int64, msgID int, callbackID 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
|
|
}
|
|
text, kb := h.buildAccentKeyboard(st)
|
|
h.editText(ctx, chatID, msgID, text, &kb)
|
|
}
|
|
|
|
// timezoneCallback shows the current timezone and instructions to change it.
|
|
func (h *Handler) timezoneCallback(ctx context.Context, chatID int64, msgID int, callbackID string) {
|
|
defer h.answerCb(ctx, callbackID)
|
|
user, err := h.getOrCreateUser(chatID)
|
|
if err != nil {
|
|
return
|
|
}
|
|
text := fmt.Sprintf("Current timezone: %s\n\nUse /settimezone <name> to change it.\nExamples: Asia/Tehran, Europe/London, America/New_York", user.Timezone)
|
|
kb := models.InlineKeyboardMarkup{
|
|
InlineKeyboard: [][]models.InlineKeyboardButton{
|
|
{{Text: "Back to Settings", CallbackData: "back_settings"}},
|
|
},
|
|
}
|
|
h.editText(ctx, chatID, msgID, text, &kb)
|
|
}
|
|
|
|
// buildAccentKeyboard returns the accent color picker keyboard.
|
|
func (h *Handler) buildAccentKeyboard(st *db.UserSettings) (string, models.InlineKeyboardMarkup) {
|
|
type accentOption struct {
|
|
name string
|
|
color string
|
|
}
|
|
accents := []accentOption{
|
|
{"ocean", "Blue"},
|
|
{"beach", "Warm"},
|
|
{"rose", "Pink"},
|
|
{"catppuccin", "Purple"},
|
|
}
|
|
rows := [][]models.InlineKeyboardButton{}
|
|
for _, a := range accents {
|
|
label := fmt.Sprintf("%s (%s)", a.name, a.color)
|
|
if a.name == st.ExportAccent {
|
|
label = "> " + label
|
|
}
|
|
rows = append(rows, []models.InlineKeyboardButton{
|
|
{Text: label, CallbackData: "accent_" + a.name},
|
|
})
|
|
}
|
|
rows = append(rows, []models.InlineKeyboardButton{
|
|
{Text: "Back to Settings", CallbackData: "back_settings"},
|
|
})
|
|
return "Select accent color:", models.InlineKeyboardMarkup{InlineKeyboard: rows}
|
|
}
|
|
|
|
// selectAccent sets the user's export accent color after validation.
|
|
func (h *Handler) selectAccent(ctx context.Context, chatID int64, msgID int, callbackID, name string) {
|
|
defer h.answerCb(ctx, callbackID)
|
|
user, err := h.getOrCreateUser(chatID)
|
|
if err != nil {
|
|
return
|
|
}
|
|
valid := map[string]bool{"ocean": true, "beach": true, "rose": true, "catppuccin": true}
|
|
if !valid[name] {
|
|
return
|
|
}
|
|
st, err := h.DB.GetOrCreateSettings(user.ID)
|
|
if err != nil {
|
|
return
|
|
}
|
|
st.ExportAccent = name
|
|
if err := h.DB.UpdateSettings(st); err != nil {
|
|
return
|
|
}
|
|
kb := models.InlineKeyboardMarkup{
|
|
InlineKeyboard: [][]models.InlineKeyboardButton{
|
|
{{Text: "Back to Settings", CallbackData: "back_settings"}},
|
|
},
|
|
}
|
|
h.editText(ctx, chatID, msgID, fmt.Sprintf("Accent set to: %s", name), &kb)
|
|
}
|
|
|
|
// reportTimeCallback shows the current report time setting.
|
|
func (h *Handler) reportTimeCallback(ctx context.Context, chatID int64, msgID int, callbackID 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
|
|
}
|
|
text := fmt.Sprintf("Current report time: %s\nUse /setreporttime HH:MM to change it.", st.ReportTime)
|
|
kb := models.InlineKeyboardMarkup{
|
|
InlineKeyboard: [][]models.InlineKeyboardButton{
|
|
{{Text: "Back to Settings", CallbackData: "back_settings"}},
|
|
},
|
|
}
|
|
h.editText(ctx, chatID, msgID, text, &kb)
|
|
}
|
|
|
|
// breakThresholdCallback shows the break threshold picker.
|
|
func (h *Handler) breakThresholdCallback(ctx context.Context, chatID int64, msgID int, callbackID string) {
|
|
defer h.answerCb(ctx, callbackID)
|
|
bt := h.getTodayBreakThreshold(chatID)
|
|
text, kb := h.buildBreakThresholdKeyboard(bt)
|
|
h.editText(ctx, chatID, msgID, text, &kb)
|
|
}
|
|
|
|
// buildBreakThresholdKeyboard returns the break threshold picker.
|
|
func (h *Handler) buildBreakThresholdKeyboard(current int64) (string, models.InlineKeyboardMarkup) {
|
|
values := []int{0, 5, 10, 15, 30, 60}
|
|
labels := map[int]string{
|
|
0: "0m",
|
|
5: "5m",
|
|
10: "10m",
|
|
15: "15m",
|
|
30: "30m",
|
|
60: "1h",
|
|
}
|
|
rows := [][]models.InlineKeyboardButton{}
|
|
for _, v := range values {
|
|
label := labels[v]
|
|
if int(current/60) == v {
|
|
label = "> " + label
|
|
}
|
|
rows = append(rows, []models.InlineKeyboardButton{
|
|
{Text: label, CallbackData: fmt.Sprintf("breakthreshold_%d", v)},
|
|
})
|
|
}
|
|
rows = append(rows, []models.InlineKeyboardButton{
|
|
{Text: "Back to Settings", CallbackData: "back_settings"},
|
|
})
|
|
return "Select break threshold:", models.InlineKeyboardMarkup{InlineKeyboard: rows}
|
|
}
|
|
|
|
// selectBreakThreshold saves the chosen break threshold and returns to settings.
|
|
func (h *Handler) selectBreakThreshold(ctx context.Context, chatID int64, msgID int, callbackID, valStr string) {
|
|
defer h.answerCb(ctx, callbackID)
|
|
mins, err := strconv.Atoi(valStr)
|
|
if err != nil || mins < 0 || mins > maxBreakThresholdMins {
|
|
return
|
|
}
|
|
user, err := h.getOrCreateUser(chatID)
|
|
if err != nil {
|
|
return
|
|
}
|
|
now := time.Now()
|
|
date := now.Format(DateLayout)
|
|
day, err := h.DB.GetOrCreateDay(user.ID, date)
|
|
if err != nil {
|
|
return
|
|
}
|
|
day.MinBreakThreshold = int64(mins) * 60
|
|
if err := h.DB.UpdateDay(day); err != nil {
|
|
return
|
|
}
|
|
bt := h.getTodayBreakThreshold(chatID)
|
|
st, err := h.DB.GetOrCreateSettings(user.ID)
|
|
if err != nil {
|
|
return
|
|
}
|
|
text, kb := h.buildSettingsKeyboard(user, st, bt)
|
|
h.editText(ctx, chatID, msgID, text, &kb)
|
|
}
|
|
|
|
// backToSettings returns to the main settings menu.
|
|
func (h *Handler) backToSettings(ctx context.Context, chatID int64, msgID int, callbackID 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
|
|
}
|
|
bt := h.getTodayBreakThreshold(chatID)
|
|
text, kb := h.buildSettingsKeyboard(user, st, bt)
|
|
h.editText(ctx, chatID, msgID, text, &kb)
|
|
}
|
|
|
|
// calTypeCallback shows the calendar type selector.
|
|
func (h *Handler) calTypeCallback(ctx context.Context, chatID int64, msgID int, callbackID string) {
|
|
defer h.answerCb(ctx, callbackID)
|
|
user, err := h.getOrCreateUser(chatID)
|
|
if err != nil {
|
|
return
|
|
}
|
|
text, kb := h.buildCalendarKeyboard(user)
|
|
h.editText(ctx, chatID, msgID, text, &kb)
|
|
}
|
|
|
|
// buildCalendarKeyboard returns the calendar type selector keyboard.
|
|
func (h *Handler) buildCalendarKeyboard(user *db.User) (string, models.InlineKeyboardMarkup) {
|
|
cals := []string{"gregorian", "jalali", "hijri"}
|
|
calLabels := map[string]string{
|
|
"gregorian": "Gregorian",
|
|
"jalali": "Jalali",
|
|
"hijri": "Hijri",
|
|
}
|
|
rows := [][]models.InlineKeyboardButton{}
|
|
for _, c := range cals {
|
|
label := calLabels[c]
|
|
if c == user.Calendar {
|
|
label = "> " + label
|
|
}
|
|
rows = append(rows, []models.InlineKeyboardButton{
|
|
{Text: label, CallbackData: "caltype_" + c},
|
|
})
|
|
}
|
|
rows = append(rows, []models.InlineKeyboardButton{
|
|
{Text: "Back to Settings", CallbackData: "back_settings"},
|
|
})
|
|
return "Select calendar type:", models.InlineKeyboardMarkup{InlineKeyboard: rows}
|
|
}
|
|
|
|
// selectCalendar sets the user's calendar after validation.
|
|
func (h *Handler) selectCalendar(ctx context.Context, chatID int64, msgID int, callbackID, name string) {
|
|
defer h.answerCb(ctx, callbackID)
|
|
valid := map[string]bool{"gregorian": true, "jalali": true, "hijri": true}
|
|
if !valid[name] {
|
|
return
|
|
}
|
|
user, err := h.getOrCreateUser(chatID)
|
|
if err != nil {
|
|
return
|
|
}
|
|
user.Calendar = name
|
|
if err := h.DB.UpdateUser(user); err != nil {
|
|
return
|
|
}
|
|
bt := h.getTodayBreakThreshold(chatID)
|
|
st, err := h.DB.GetOrCreateSettings(user.ID)
|
|
if err != nil {
|
|
return
|
|
}
|
|
text, kb := h.buildSettingsKeyboard(user, st, bt)
|
|
h.editText(ctx, chatID, msgID, text, &kb)
|
|
}
|
|
|
|
// handleRPGSettingsMsg shows the RPG enable/disable picker (command).
|
|
func (h *Handler) handleRPGSettingsMsg(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
|
|
}
|
|
text, kb := h.buildRPGToggleKeyboard(user.ID)
|
|
h.sendWithKB(ctx, msg.Chat.ID, text, kb)
|
|
}
|
|
|
|
// handleLeagueSettingsMsg shows the League enable/disable picker (command).
|
|
func (h *Handler) handleLeagueSettingsMsg(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
|
|
}
|
|
text, kb := h.buildLeagueToggleKeyboard(user.ID)
|
|
h.sendWithKB(ctx, msg.Chat.ID, text, kb)
|
|
}
|
|
|
|
// rpgToggleCallback shows the RPG enable/disable picker (inline).
|
|
func (h *Handler) rpgToggleCallback(ctx context.Context, chatID int64, msgID int, callbackID string) {
|
|
defer h.answerCb(ctx, callbackID)
|
|
user, err := h.getOrCreateUser(chatID)
|
|
if err != nil {
|
|
return
|
|
}
|
|
text, kb := h.buildRPGToggleKeyboard(user.ID)
|
|
h.editText(ctx, chatID, msgID, text, &kb)
|
|
}
|
|
|
|
// leagueToggleCallback shows the League enable/disable picker (inline).
|
|
func (h *Handler) leagueToggleCallback(ctx context.Context, chatID int64, msgID int, callbackID string) {
|
|
defer h.answerCb(ctx, callbackID)
|
|
user, err := h.getOrCreateUser(chatID)
|
|
if err != nil {
|
|
return
|
|
}
|
|
text, kb := h.buildLeagueToggleKeyboard(user.ID)
|
|
h.editText(ctx, chatID, msgID, text, &kb)
|
|
}
|
|
|
|
// reportToggleCallback shows the Report enable/disable picker (inline) — replaces simple toggle.
|
|
func (h *Handler) reportToggleCallback(ctx context.Context, chatID int64, msgID int, callbackID string) {
|
|
defer h.answerCb(ctx, callbackID)
|
|
user, err := h.getOrCreateUser(chatID)
|
|
if err != nil {
|
|
return
|
|
}
|
|
text, kb := h.buildReportToggleKeyboard(user.ID)
|
|
h.editText(ctx, chatID, msgID, text, &kb)
|
|
}
|
|
|
|
// buildRPGToggleKeyboard returns the RPG enable/disable picker.
|
|
func (h *Handler) buildRPGToggleKeyboard(userID int64) (string, models.InlineKeyboardMarkup) {
|
|
st, err := h.DB.GetOrCreateSettings(userID)
|
|
current := false
|
|
if err == nil {
|
|
current = st.RPGEnabled
|
|
}
|
|
rows := [][]models.InlineKeyboardButton{}
|
|
for _, opt := range []struct {
|
|
label string
|
|
data string
|
|
value bool
|
|
}{
|
|
{"Enabled", "rpg_enable", true},
|
|
{"Disabled", "rpg_disable", false},
|
|
} {
|
|
label := opt.label
|
|
if opt.value == current {
|
|
label = "> " + label
|
|
}
|
|
rows = append(rows, []models.InlineKeyboardButton{
|
|
{Text: label, CallbackData: opt.data},
|
|
})
|
|
}
|
|
rows = append(rows, []models.InlineKeyboardButton{
|
|
{Text: "Back to Settings", CallbackData: "back_settings"},
|
|
})
|
|
return "RPG System:", models.InlineKeyboardMarkup{InlineKeyboard: rows}
|
|
}
|
|
|
|
// buildLeagueToggleKeyboard returns the League enable/disable picker.
|
|
func (h *Handler) buildLeagueToggleKeyboard(userID int64) (string, models.InlineKeyboardMarkup) {
|
|
st, err := h.DB.GetOrCreateSettings(userID)
|
|
current := false
|
|
if err == nil {
|
|
current = st.LeagueOptIn
|
|
}
|
|
rows := [][]models.InlineKeyboardButton{}
|
|
for _, opt := range []struct {
|
|
label string
|
|
data string
|
|
value bool
|
|
}{
|
|
{"Enabled", "league_enable", true},
|
|
{"Disabled", "league_disable", false},
|
|
} {
|
|
label := opt.label
|
|
if opt.value == current {
|
|
label = "> " + label
|
|
}
|
|
rows = append(rows, []models.InlineKeyboardButton{
|
|
{Text: label, CallbackData: opt.data},
|
|
})
|
|
}
|
|
rows = append(rows, []models.InlineKeyboardButton{
|
|
{Text: "Back to Settings", CallbackData: "back_settings"},
|
|
})
|
|
return "WorkTime League:", models.InlineKeyboardMarkup{InlineKeyboard: rows}
|
|
}
|
|
|
|
// buildReportToggleKeyboard returns the Report enable/disable picker.
|
|
func (h *Handler) buildReportToggleKeyboard(userID int64) (string, models.InlineKeyboardMarkup) {
|
|
st, err := h.DB.GetOrCreateSettings(userID)
|
|
current := true
|
|
if err == nil {
|
|
current = st.ReportEnabled
|
|
}
|
|
rows := [][]models.InlineKeyboardButton{}
|
|
for _, opt := range []struct {
|
|
label string
|
|
data string
|
|
value bool
|
|
}{
|
|
{"Enabled", "report_enable", true},
|
|
{"Disabled", "report_disable", false},
|
|
} {
|
|
label := opt.label
|
|
if opt.value == current {
|
|
label = "> " + label
|
|
}
|
|
rows = append(rows, []models.InlineKeyboardButton{
|
|
{Text: label, CallbackData: opt.data},
|
|
})
|
|
}
|
|
rows = append(rows, []models.InlineKeyboardButton{
|
|
{Text: "Back to Settings", CallbackData: "back_settings"},
|
|
})
|
|
return "Daily Report:", models.InlineKeyboardMarkup{InlineKeyboard: rows}
|
|
}
|
|
|
|
// handleSelectCallback handles binary select callbacks (report_enable, rpg_enable, etc.).
|
|
func (h *Handler) handleSelectCallback(ctx context.Context, chatID int64, msgID int, callbackID string, setting string, value bool) {
|
|
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
|
|
}
|
|
switch setting {
|
|
case "report_enable":
|
|
st.ReportEnabled = value
|
|
case "rpg_enable":
|
|
st.RPGEnabled = value
|
|
if value {
|
|
h.DB.GetOrCreateRPGStats(user.ID)
|
|
}
|
|
case "league_enable":
|
|
st.LeagueOptIn = value
|
|
}
|
|
if err := h.DB.UpdateSettings(st); err != nil {
|
|
return
|
|
}
|
|
bt := h.getTodayBreakThreshold(chatID)
|
|
text, kb := h.buildSettingsKeyboard(user, st, bt)
|
|
h.editText(ctx, chatID, msgID, text, &kb)
|
|
}
|