package bot import ( "fmt" "strconv" "time" tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5" "worktimeBot/internal/db" ) // handleSetReportTime parses a HH:MM argument and updates the user's report time. func (h *Handler) handleSetReportTime(msg *tgbotapi.Message) { arg := msg.CommandArguments() if arg == "" { user, err := h.getOrCreateUser(msg.Chat.ID) if err != nil { h.sendText(msg.Chat.ID, "Error loading profile") return } h.sendText(msg.Chat.ID, fmt.Sprintf("Current report time: %s\nUsage: /setreporttime HH:MM (24-hour)", user.ReportTime)) return } if len(arg) != 5 || arg[2] != ':' { h.sendText(msg.Chat.ID, "Invalid format. Usage: /setreporttime HH:MM (24-hour)") return } hours, err1 := strconv.Atoi(arg[:2]) mins, err2 := strconv.Atoi(arg[3:]) if err1 != nil || err2 != nil || hours < 0 || hours > 23 || mins < 0 || mins > 59 { h.sendText(msg.Chat.ID, "Invalid time. Usage: /setreporttime HH:MM (24-hour)") return } user, err := h.getOrCreateUser(msg.Chat.ID) if err != nil { h.sendText(msg.Chat.ID, "Error updating report time") return } user.ReportTime = arg if err := h.DB.UpdateUser(user); err != nil { h.sendText(msg.Chat.ID, "Error updating report time") return } h.sendText(msg.Chat.ID, fmt.Sprintf("Report time set to %s", arg)) } // handleAccentMsg shows the accent color picker. func (h *Handler) handleAccentMsg(msg *tgbotapi.Message) { user, err := h.getOrCreateUser(msg.Chat.ID) if err != nil { h.sendText(msg.Chat.ID, "Error loading profile") return } text, kb := h.buildAccentKeyboard(user) reply := tgbotapi.NewMessage(msg.Chat.ID, text) reply.ReplyMarkup = &kb h.Bot.Send(reply) } // handleSetTimezone validates and updates the user's IANA timezone. func (h *Handler) handleSetTimezone(msg *tgbotapi.Message) { arg := msg.CommandArguments() if arg == "" { user, err := h.getOrCreateUser(msg.Chat.ID) if err != nil { h.sendText(msg.Chat.ID, "Error loading profile") return } h.sendText(msg.Chat.ID, fmt.Sprintf("Current timezone: %s\nUsage: /settimezone \nExamples: Asia/Tehran, Europe/London, America/New_York", user.Timezone)) return } loc, err := time.LoadLocation(arg) if err != nil { h.sendText(msg.Chat.ID, fmt.Sprintf("Invalid timezone: %s\nUse IANA format, e.g. Asia/Tehran, Europe/London", arg)) return } user, err := h.getOrCreateUser(msg.Chat.ID) if err != nil { h.sendText(msg.Chat.ID, "Error loading profile") return } user.Timezone = arg if err := h.DB.UpdateUser(user); err != nil { h.sendText(msg.Chat.ID, "Error saving timezone") return } now := time.Now().In(loc) h.sendText(msg.Chat.ID, fmt.Sprintf("Timezone set to %s (current time: %s)", arg, now.Format("15:04"))) } // settingsCallback shows the settings inline menu. func (h *Handler) settingsCallback(chatID int64, msgID int, callbackID string) { defer h.Bot.Request(tgbotapi.NewCallback(callbackID, "")) user, err := h.getOrCreateUser(chatID) if err != nil { return } text, kb := h.buildSettingsKeyboard(user) edit := tgbotapi.NewEditMessageText(chatID, msgID, text) edit.ReplyMarkup = &kb h.Bot.Send(edit) } // buildSettingsKeyboard returns the settings menu text and inline keyboard. func (h *Handler) buildSettingsKeyboard(user *db.User) (string, tgbotapi.InlineKeyboardMarkup) { reportStatus := "disabled" if user.ReportEnabled { reportStatus = "enabled" } rows := [][]tgbotapi.InlineKeyboardButton{ tgbotapi.NewInlineKeyboardRow( tgbotapi.NewInlineKeyboardButtonData(fmt.Sprintf("Timezone: %s", user.Timezone), "timezone"), ), tgbotapi.NewInlineKeyboardRow( tgbotapi.NewInlineKeyboardButtonData(fmt.Sprintf("Accent: %s", user.ExportAccent), "accent"), ), tgbotapi.NewInlineKeyboardRow( tgbotapi.NewInlineKeyboardButtonData(fmt.Sprintf("Calendar: %s", user.Calendar), "caltype"), ), tgbotapi.NewInlineKeyboardRow( tgbotapi.NewInlineKeyboardButtonData(fmt.Sprintf("Report: %s", reportStatus), "reporttoggle"), ), tgbotapi.NewInlineKeyboardRow( tgbotapi.NewInlineKeyboardButtonData(fmt.Sprintf("Report time: %s", user.ReportTime), "reporttime"), ), tgbotapi.NewInlineKeyboardRow( tgbotapi.NewInlineKeyboardButtonData("History", "history"), ), tgbotapi.NewInlineKeyboardRow( tgbotapi.NewInlineKeyboardButtonData("Back to Menu", "back_menu"), ), } return "Settings:", tgbotapi.NewInlineKeyboardMarkup(rows...) } // accentCallback shows the accent color picker (inline edit). func (h *Handler) accentCallback(chatID int64, msgID int, callbackID string) { defer h.Bot.Request(tgbotapi.NewCallback(callbackID, "")) user, err := h.getOrCreateUser(chatID) if err != nil { return } text, kb := h.buildAccentKeyboard(user) edit := tgbotapi.NewEditMessageText(chatID, msgID, text) edit.ReplyMarkup = &kb h.Bot.Send(edit) } // timezoneCallback shows the current timezone and instructions to change it. func (h *Handler) timezoneCallback(chatID int64, msgID int, callbackID string) { defer h.Bot.Request(tgbotapi.NewCallback(callbackID, "")) user, err := h.getOrCreateUser(chatID) if err != nil { return } text := fmt.Sprintf("Current timezone: %s\n\nUse /settimezone to change it.\nExamples: Asia/Tehran, Europe/London, America/New_York", user.Timezone) kb := tgbotapi.NewInlineKeyboardMarkup( tgbotapi.NewInlineKeyboardRow( tgbotapi.NewInlineKeyboardButtonData("Back to Settings", "back_settings"), ), ) edit := tgbotapi.NewEditMessageText(chatID, msgID, text) edit.ReplyMarkup = &kb h.Bot.Send(edit) } // buildAccentKeyboard returns the accent color picker keyboard. func (h *Handler) buildAccentKeyboard(user *db.User) (string, tgbotapi.InlineKeyboardMarkup) { type accentOption struct { name string color string } accents := []accentOption{ {"ocean", "Blue"}, {"beach", "Warm"}, {"rose", "Pink"}, {"catppuccin", "Purple"}, } rows := [][]tgbotapi.InlineKeyboardButton{} for _, a := range accents { label := fmt.Sprintf("%s (%s)", a.name, a.color) if a.name == user.ExportAccent { label = "> " + label } rows = append(rows, tgbotapi.NewInlineKeyboardRow( tgbotapi.NewInlineKeyboardButtonData(label, "accent_"+a.name), )) } rows = append(rows, tgbotapi.NewInlineKeyboardRow( tgbotapi.NewInlineKeyboardButtonData("Back to Settings", "back_settings"), )) return "Select accent color:", tgbotapi.NewInlineKeyboardMarkup(rows...) } // selectAccent sets the user's export accent color after validation. func (h *Handler) selectAccent(chatID int64, msgID int, callbackID, name string) { defer h.Bot.Request(tgbotapi.NewCallback(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 } user.ExportAccent = name if err := h.DB.UpdateUser(user); err != nil { return } edit := tgbotapi.NewEditMessageText(chatID, msgID, fmt.Sprintf("Accent set to: %s", name)) kb := tgbotapi.NewInlineKeyboardMarkup( tgbotapi.NewInlineKeyboardRow( tgbotapi.NewInlineKeyboardButtonData("Back to Settings", "back_settings"), ), ) edit.ReplyMarkup = &kb h.Bot.Send(edit) } // reportTimeCallback shows the current report time setting. func (h *Handler) reportTimeCallback(chatID int64, msgID int, callbackID string) { defer h.Bot.Request(tgbotapi.NewCallback(callbackID, "")) user, err := h.getOrCreateUser(chatID) if err != nil { return } text := fmt.Sprintf("Current report time: %s\nUse /setreporttime HH:MM to change it.", user.ReportTime) edit := tgbotapi.NewEditMessageText(chatID, msgID, text) kb := tgbotapi.NewInlineKeyboardMarkup( tgbotapi.NewInlineKeyboardRow( tgbotapi.NewInlineKeyboardButtonData("Back to Settings", "back_settings"), ), ) edit.ReplyMarkup = &kb h.Bot.Send(edit) } // backToSettings returns to the main settings menu. func (h *Handler) backToSettings(chatID int64, msgID int, callbackID string) { defer h.Bot.Request(tgbotapi.NewCallback(callbackID, "")) user, err := h.getOrCreateUser(chatID) if err != nil { return } text, kb := h.buildSettingsKeyboard(user) edit := tgbotapi.NewEditMessageText(chatID, msgID, text) edit.ReplyMarkup = &kb h.Bot.Send(edit) } // calTypeCallback shows the calendar type selector. func (h *Handler) calTypeCallback(chatID int64, msgID int, callbackID string) { defer h.Bot.Request(tgbotapi.NewCallback(callbackID, "")) user, err := h.getOrCreateUser(chatID) if err != nil { return } text, kb := h.buildCalendarKeyboard(user) edit := tgbotapi.NewEditMessageText(chatID, msgID, text) edit.ReplyMarkup = &kb h.Bot.Send(edit) } // buildCalendarKeyboard returns the calendar type selector keyboard. func (h *Handler) buildCalendarKeyboard(user *db.User) (string, tgbotapi.InlineKeyboardMarkup) { cals := []string{"gregorian", "jalali", "hijri"} calLabels := map[string]string{ "gregorian": "Gregorian", "jalali": "Jalali", "hijri": "Hijri", } rows := [][]tgbotapi.InlineKeyboardButton{} for _, c := range cals { label := calLabels[c] if c == user.Calendar { label = "> " + label } rows = append(rows, tgbotapi.NewInlineKeyboardRow( tgbotapi.NewInlineKeyboardButtonData(label, "caltype_"+c), )) } rows = append(rows, tgbotapi.NewInlineKeyboardRow( tgbotapi.NewInlineKeyboardButtonData("Back to Settings", "back_settings"), )) return "Select calendar type:", tgbotapi.NewInlineKeyboardMarkup(rows...) } // selectCalendar sets the user's calendar after validation. func (h *Handler) selectCalendar(chatID int64, msgID int, callbackID, name string) { defer h.Bot.Request(tgbotapi.NewCallback(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 } text, kb := h.buildSettingsKeyboard(user) edit := tgbotapi.NewEditMessageText(chatID, msgID, text) edit.ReplyMarkup = &kb h.Bot.Send(edit) }