package handler import ( "context" "fmt" "log/slog" "strings" "github.com/go-telegram/bot/models" "uptodownBot/internal/domain" ) func (h *Handler) buildSettingsKeyboard(userID int64, prefs *domain.UserPreferences) (string, models.InlineKeyboardMarkup) { audioFmt := prefs.DefaultAudioFormat if audioFmt == "" { audioFmt = "ask" } videoFmt := prefs.DefaultVideoFormat if videoFmt == "" { videoFmt = "ask" } langLabel := prefs.DefaultLanguage if langLabel == "" { langLabel = "Default" } mediaTypeLabel := "Ask" switch prefs.DefaultMediaType { case domain.MediaTypeAudio: mediaTypeLabel = "Audio" case domain.MediaTypeVideo: mediaTypeLabel = "Video" } rows := [][]models.InlineKeyboardButton{ { {Text: fmt.Sprintf("Media Type: %s", mediaTypeLabel), CallbackData: "settings_mediatype"}, {Text: fmt.Sprintf("Language: %s", langLabel), CallbackData: "settings_language"}, }, { {Text: fmt.Sprintf("Quality: %s/%s", audioFmt, videoFmt), CallbackData: "settings_quality"}, }, {{Text: "Delete all data", CallbackData: "deleteaccount", Style: "danger"}}, {{Text: "Back to Menu", CallbackData: "back_menu"}}, } return "Settings:", models.InlineKeyboardMarkup{InlineKeyboard: rows} } func (h *Handler) settingsCallback(ctx context.Context, chatID int64, msgID int, cbID string, userID int64) { defer h.answerCb(ctx, cbID) prefs, err := h.Repo.GetOrCreatePreferences(userID) if err != nil { return } text, kb := h.buildSettingsKeyboard(userID, prefs) h.editText(ctx, chatID, msgID, text, &kb) } func (h *Handler) backToSettings(ctx context.Context, chatID int64, msgID int, userID int64) { prefs, err := h.Repo.GetOrCreatePreferences(userID) if err != nil { return } text, kb := h.buildSettingsKeyboard(userID, prefs) h.editText(ctx, chatID, msgID, text, &kb) } func (h *Handler) handleSettingsSelection(ctx context.Context, chatID int64, msgID int, userID int64, setting string) { prefs, err := h.Repo.GetOrCreatePreferences(userID) if err != nil { return } switch setting { case "mediatype": text, kb := h.buildMediaTypePicker(prefs) h.editText(ctx, chatID, msgID, text, &kb) case "quality": text, kb := h.buildQualityAudioVideoPicker(prefs) h.editText(ctx, chatID, msgID, text, &kb) case "quality_audio": text, kb := h.buildAudioQualityPicker(prefs) h.editText(ctx, chatID, msgID, text, &kb) case "quality_video": text, kb := h.buildVideoQualityPicker(prefs) h.editText(ctx, chatID, msgID, text, &kb) case "language": text, kb := h.buildLanguagePicker(prefs) h.editText(ctx, chatID, msgID, text, &kb) } } func (h *Handler) buildQualityAudioVideoPicker(prefs *domain.UserPreferences) (string, models.InlineKeyboardMarkup) { rows := [][]models.InlineKeyboardButton{ {{Text: "Audio", CallbackData: "settings_quality_audio"}}, {{Text: "Video", CallbackData: "settings_quality_video"}}, {{Text: "Back to Settings", CallbackData: "back_settings"}}, } return "Choose quality type to set default:", models.InlineKeyboardMarkup{InlineKeyboard: rows} } func (h *Handler) buildAudioQualityPicker(prefs *domain.UserPreferences) (string, models.InlineKeyboardMarkup) { bitrates := []string{"ask", "best", "128k", "192k", "256k", "320k"} rows := [][]models.InlineKeyboardButton{} for _, b := range bitrates { label := b if b == "ask" && prefs.DefaultAudioFormat == "" { label = "> " + label } else if b != "ask" && b == prefs.DefaultAudioFormat { label = "> " + label } rows = append(rows, []models.InlineKeyboardButton{ {Text: label, CallbackData: "settings_set_audioquality_" + b}, }) } rows = append(rows, []models.InlineKeyboardButton{ {Text: "Back", CallbackData: "back_quality"}, }) return "Select default audio quality:", models.InlineKeyboardMarkup{InlineKeyboard: rows} } func (h *Handler) buildVideoQualityPicker(prefs *domain.UserPreferences) (string, models.InlineKeyboardMarkup) { resolutions := []string{"ask", "best", "2160p", "1440p", "1080p", "720p", "480p", "360p"} rows := [][]models.InlineKeyboardButton{} row := []models.InlineKeyboardButton{} for i, r := range resolutions { label := r if r == "ask" && prefs.DefaultVideoFormat == "" { label = "> " + label } else if r != "ask" && r == prefs.DefaultVideoFormat { label = "> " + label } row = append(row, models.InlineKeyboardButton{ Text: label, CallbackData: "settings_set_videoquality_" + r, }) if len(row) == 3 || i == len(resolutions)-1 { rows = append(rows, row) row = nil } } rows = append(rows, []models.InlineKeyboardButton{ {Text: "Back", CallbackData: "back_quality"}, }) return "Select default video quality:", models.InlineKeyboardMarkup{InlineKeyboard: rows} } func (h *Handler) buildLanguagePicker(prefs *domain.UserPreferences) (string, models.InlineKeyboardMarkup) { languages := []string{"", "en", "es", "ja", "ko", "fr", "de", "zh", "ar", "pt", "ru"} langLabels := map[string]string{ "": "Default", "en": "English", "es": "Spanish", "ja": "Japanese", "ko": "Korean", "fr": "French", "de": "German", "zh": "Chinese", "ar": "Arabic", "pt": "Portuguese", "ru": "Russian", } rows := [][]models.InlineKeyboardButton{} row := []models.InlineKeyboardButton{} for i, l := range languages { label := langLabels[l] if l == prefs.DefaultLanguage { label = "> " + label } row = append(row, models.InlineKeyboardButton{ Text: label, CallbackData: "settings_set_language_" + l, }) if len(row) == 2 || i == len(languages)-1 { rows = append(rows, row) row = nil } } rows = append(rows, []models.InlineKeyboardButton{ {Text: "Back to Settings", CallbackData: "back_settings"}, }) return "Select default language:", models.InlineKeyboardMarkup{InlineKeyboard: rows} } func (h *Handler) buildMediaTypePicker(prefs *domain.UserPreferences) (string, models.InlineKeyboardMarkup) { options := []struct { mt domain.MediaType label string }{ {domain.MediaTypeAsk, "Ask"}, {domain.MediaTypeVideo, "Video"}, {domain.MediaTypeAudio, "Audio"}, } rows := [][]models.InlineKeyboardButton{} for _, opt := range options { label := opt.label if opt.mt == prefs.DefaultMediaType { label = "> " + label } rows = append(rows, []models.InlineKeyboardButton{ {Text: label, CallbackData: "settings_set_mediatype_" + opt.mt.String()}, }) } rows = append(rows, []models.InlineKeyboardButton{ {Text: "Back to Settings", CallbackData: "back_settings"}, }) return "Select default media type:", models.InlineKeyboardMarkup{InlineKeyboard: rows} } func (h *Handler) handleSettingsSet(ctx context.Context, chatID int64, msgID int, userID int64, setting string) { parts := strings.SplitN(setting, "_", 2) if len(parts) < 2 { return } action := parts[0] value := parts[1] prefs, err := h.Repo.GetOrCreatePreferences(userID) if err != nil { return } changed := false switch action { case "mediatype": switch value { case "ask": prefs.DefaultMediaType = domain.MediaTypeAsk case "audio": prefs.DefaultMediaType = domain.MediaTypeAudio case "video": prefs.DefaultMediaType = domain.MediaTypeVideo } changed = true case "audioquality": if value == "ask" { value = "" } prefs.DefaultAudioFormat = value changed = true case "videoquality": if value == "ask" { value = "" } prefs.DefaultVideoFormat = value changed = true case "language": prefs.DefaultLanguage = value changed = true } if changed { _ = h.Repo.UpdatePreferences(userID, prefs) } h.backToSettings(ctx, chatID, msgID, userID) } // deleteAccountPrompt shows the deletion confirmation prompt. func (h *Handler) deleteAccountPrompt(ctx context.Context, chatID int64, msgID int, userID int64) { kb := deleteConfirmKeyboard(userID) h.editText(ctx, chatID, msgID, "This will permanently delete ALL your data (preferences, quotas, history).\nAre you sure?", &kb) } // deleteAccountConfirm permanently deletes all user data. func (h *Handler) deleteAccountConfirm(ctx context.Context, chatID int64, msgID int, userID int64) { if err := h.Repo.DeleteUserData(userID); err != nil { slog.Error("delete user data", "user_id", userID, "error", err) h.editText(ctx, chatID, msgID, "Error deleting data. Please try again.", nil) return } h.editText(ctx, chatID, msgID, "All your data has been deleted. You can start fresh by sending /start.", nil) }