package handler import ( "fmt" "strings" "github.com/go-telegram/bot/models" "uptodownBot/internal/domain" ) func mainKeyboard() models.InlineKeyboardMarkup { return models.InlineKeyboardMarkup{ InlineKeyboard: [][]models.InlineKeyboardButton{ { {Text: "Download", CallbackData: "download"}, {Text: "Settings", CallbackData: "settings"}, }, { {Text: "History", CallbackData: "history"}, {Text: "Help", CallbackData: "help"}, }, }, } } func backKeyboard() models.InlineKeyboardMarkup { return models.InlineKeyboardMarkup{ InlineKeyboard: [][]models.InlineKeyboardButton{ {{Text: "Back to Menu", CallbackData: "back_menu"}}, }, } } func settingsBackKeyboard() models.InlineKeyboardMarkup { return models.InlineKeyboardMarkup{ InlineKeyboard: [][]models.InlineKeyboardButton{ {{Text: "Back to Settings", CallbackData: "back_settings"}}, }, } } // mediaTypeKeyboard builds the three-button row for media type selection + Cancel. func mediaTypeKeyboard() models.InlineKeyboardMarkup { return models.InlineKeyboardMarkup{ InlineKeyboard: [][]models.InlineKeyboardButton{ { {Text: "Audio Only", CallbackData: "type_audio"}, {Text: "Video Only", CallbackData: "type_video"}, {Text: "Video+Audio", CallbackData: "type_both"}, }, {{Text: "Cancel", CallbackData: "cancel_dl"}}, }, } } // formatKeyboard builds a keyboard from a slice of format labels. func formatKeyboard(prefix string, labels []string) models.InlineKeyboardMarkup { rows := [][]models.InlineKeyboardButton{} row := []models.InlineKeyboardButton{} for i, label := range labels { data := prefix + label row = append(row, models.InlineKeyboardButton{Text: label, CallbackData: data}) if len(row) == 2 || i == len(labels)-1 { rows = append(rows, row) row = nil } } rows = append(rows, []models.InlineKeyboardButton{ {Text: "Back", CallbackData: "back_step"}, {Text: "Cancel", CallbackData: "cancel_dl"}, }) return models.InlineKeyboardMarkup{InlineKeyboard: rows} } // languageKeyboard builds a keyboard from available languages. func languageKeyboard(languages []string) models.InlineKeyboardMarkup { rows := [][]models.InlineKeyboardButton{} row := []models.InlineKeyboardButton{} for i, lang := range languages { data := "lang_" + lang row = append(row, models.InlineKeyboardButton{Text: lang, CallbackData: data}) if len(row) == 2 || i == len(languages)-1 { rows = append(rows, row) row = nil } } rows = append(rows, []models.InlineKeyboardButton{ {Text: "Back", CallbackData: "back_step"}, {Text: "Cancel", CallbackData: "cancel_dl"}, }) return models.InlineKeyboardMarkup{InlineKeyboard: rows} } // containerKeyboard builds keyboard for container format selection. func containerKeyboard(containers []string) models.InlineKeyboardMarkup { rows := [][]models.InlineKeyboardButton{} row := []models.InlineKeyboardButton{} for i, c := range containers { data := "container_" + c row = append(row, models.InlineKeyboardButton{Text: strings.ToUpper(c), CallbackData: data}) if len(row) == 3 || i == len(containers)-1 { rows = append(rows, row) row = nil } } rows = append(rows, []models.InlineKeyboardButton{ {Text: "Back", CallbackData: "back_step"}, {Text: "Cancel", CallbackData: "cancel_dl"}, }) return models.InlineKeyboardMarkup{InlineKeyboard: rows} } // progressKeyboard builds the progress display with a dummy button and cancel. func progressKeyboard(pct float64, speed, eta string) models.InlineKeyboardMarkup { progressText := fmt.Sprintf("%.1f%%", pct) if speed != "" { progressText += fmt.Sprintf(" | %s/s", speed) } if eta != "" { progressText += fmt.Sprintf(" | ETA %s", eta) } return models.InlineKeyboardMarkup{ InlineKeyboard: [][]models.InlineKeyboardButton{ {{Text: progressText, CallbackData: "noop"}}, {{Text: "Cancel", CallbackData: "cancel_dl"}}, }, } } // buildToggleKeyboard creates an on/off picker for a boolean setting. // Matches worktimeBot's pattern with "> " prefix for the current selection. func buildToggleKeyboard(userID int64, setting, label string, st *domain.UserPreferences) (string, models.InlineKeyboardMarkup) { current := false switch setting { case "quality": current = st.DefaultQuality == "best" case "container": current = st.DefaultContainer == "mp4" } options := []struct { label string data string value bool }{ {"Enabled", setting + "_enable", true}, {"Disabled", setting + "_disable", false}, } rows := [][]models.InlineKeyboardButton{} for _, opt := range options { l := opt.label if opt.value == current { l = "> " + l } rows = append(rows, []models.InlineKeyboardButton{ {Text: l, CallbackData: opt.data}, }) } rows = append(rows, []models.InlineKeyboardButton{ {Text: "Back to Settings", CallbackData: "back_settings"}, }) return label + ":", models.InlineKeyboardMarkup{InlineKeyboard: rows} } // playlistNavKeyboard builds the paginated navigation for playlist selection. func playlistNavKeyboard(state *domain.PlaylistState, selectedCount int) models.InlineKeyboardMarkup { totalPages := (len(state.Entries) + state.PerPage - 1) / state.PerPage if totalPages < 1 { totalPages = 1 } // Build entry rows (2 per row) rows := [][]models.InlineKeyboardButton{} start := state.Page * state.PerPage end := start + state.PerPage if end > len(state.Entries) { end = len(state.Entries) } for i := start; i < end; i += 2 { row := []models.InlineKeyboardButton{} // First entry e := state.Entries[i] prefix := " " if state.Selected[e.ID] { prefix = "> " } // Truncate title for button title := e.Title if len(title) > 30 { title = title[:27] + "..." } row = append(row, models.InlineKeyboardButton{ Text: prefix + title, CallbackData: "pl_entry_" + e.ID, }) // Second entry if available if i+1 < end { e2 := state.Entries[i+1] prefix2 := " " if state.Selected[e2.ID] { prefix2 = "> " } title2 := e2.Title if len(title2) > 30 { title2 = title2[:27] + "..." } row = append(row, models.InlineKeyboardButton{ Text: prefix2 + title2, CallbackData: "pl_entry_" + e2.ID, }) } rows = append(rows, row) } // Navigation row navRow := []models.InlineKeyboardButton{} navRow = append(navRow, models.InlineKeyboardButton{ Text: "<", CallbackData: fmt.Sprintf("pl_page_%d", state.Page-1), }) navRow = append(navRow, models.InlineKeyboardButton{ Text: fmt.Sprintf("Page %d/%d", state.Page+1, totalPages), CallbackData: "noop", }) navRow = append(navRow, models.InlineKeyboardButton{ Text: ">", CallbackData: fmt.Sprintf("pl_page_%d", state.Page+1), }) rows = append(rows, navRow) // Action row actionRow := []models.InlineKeyboardButton{ {Text: "Select All", CallbackData: "pl_select_all"}, } if selectedCount > 0 { actionRow = append(actionRow, models.InlineKeyboardButton{ Text: fmt.Sprintf("Confirm (%d)", selectedCount), CallbackData: "pl_confirm", }) } rows = append(rows, actionRow) // Cancel rows = append(rows, []models.InlineKeyboardButton{ {Text: "Cancel", CallbackData: "cancel_dl"}, }) return models.InlineKeyboardMarkup{InlineKeyboard: rows} } // deleteConfirmKeyboard builds the confirmation prompt for account deletion. func deleteConfirmKeyboard(userID int64) models.InlineKeyboardMarkup { return models.InlineKeyboardMarkup{ InlineKeyboard: [][]models.InlineKeyboardButton{ { {Text: "Yes, delete everything", CallbackData: fmt.Sprintf("delaccount_%d", userID)}, {Text: "Cancel", CallbackData: "back_settings"}, }, }, } }