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 = "best" } videoFmt := prefs.DefaultVideoFormat if videoFmt == "" { videoFmt = "best" } containerLabel := prefs.DefaultContainer if containerLabel == "" { containerLabel = "mp4" } langLabel := prefs.DefaultLanguage if langLabel == "" { langLabel = "Default" } rows := [][]models.InlineKeyboardButton{ { {Text: fmt.Sprintf("Format: %s/%s", audioFmt, videoFmt), CallbackData: "settings_format"}, }, { {Text: fmt.Sprintf("Container: %s", containerLabel), CallbackData: "settings_container"}, {Text: fmt.Sprintf("Language: %s", langLabel), CallbackData: "settings_language"}, }, {{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 "format": text, kb := h.buildFormatPicker(prefs) h.editText(ctx, chatID, msgID, text, &kb) case "format_audio": text, kb := h.buildAudioFormatPicker(prefs) h.editText(ctx, chatID, msgID, text, &kb) case "format_video": text, kb := h.buildVideoFormatPicker(prefs) h.editText(ctx, chatID, msgID, text, &kb) case "container": text, kb := h.buildContainerPicker(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) buildFormatPicker(prefs *domain.UserPreferences) (string, models.InlineKeyboardMarkup) { rows := [][]models.InlineKeyboardButton{ {{Text: "Audio", CallbackData: "settings_format_audio"}}, {{Text: "Video", CallbackData: "settings_format_video"}}, {{Text: "Back to Settings", CallbackData: "back_settings"}}, } return "Choose format type to set default:", models.InlineKeyboardMarkup{InlineKeyboard: rows} } func (h *Handler) buildAudioFormatPicker(prefs *domain.UserPreferences) (string, models.InlineKeyboardMarkup) { bitrates := []string{"best", "128k", "192k", "256k", "320k"} rows := [][]models.InlineKeyboardButton{} for _, b := range bitrates { label := b if b == prefs.DefaultAudioFormat { label = "> " + label } rows = append(rows, []models.InlineKeyboardButton{ {Text: label, CallbackData: "settings_set_audioformat_" + b}, }) } rows = append(rows, []models.InlineKeyboardButton{ {Text: "Back", CallbackData: "back_format"}, }) return "Select default audio format:", models.InlineKeyboardMarkup{InlineKeyboard: rows} } func (h *Handler) buildVideoFormatPicker(prefs *domain.UserPreferences) (string, models.InlineKeyboardMarkup) { resolutions := []string{"best", "2160p", "1440p", "1080p", "720p", "480p", "360p"} rows := [][]models.InlineKeyboardButton{} row := []models.InlineKeyboardButton{} for i, r := range resolutions { label := r if r == prefs.DefaultVideoFormat { label = "> " + label } row = append(row, models.InlineKeyboardButton{ Text: label, CallbackData: "settings_set_videoformat_" + r, }) if len(row) == 3 || i == len(resolutions)-1 { rows = append(rows, row) row = nil } } rows = append(rows, []models.InlineKeyboardButton{ {Text: "Back", CallbackData: "back_format"}, }) return "Select default video format:", models.InlineKeyboardMarkup{InlineKeyboard: rows} } func (h *Handler) buildContainerPicker(prefs *domain.UserPreferences) (string, models.InlineKeyboardMarkup) { containers := []string{"mp4", "mkv", "webm", "mp3", "m4a", "opus"} rows := [][]models.InlineKeyboardButton{} row := []models.InlineKeyboardButton{} for i, c := range containers { label := c if c == prefs.DefaultContainer { label = "> " + label } row = append(row, models.InlineKeyboardButton{ Text: label, CallbackData: "settings_set_container_" + c, }) if len(row) == 3 || i == len(containers)-1 { rows = append(rows, row) row = nil } } rows = append(rows, []models.InlineKeyboardButton{ {Text: "Back to Settings", CallbackData: "back_settings"}, }) return "Select default container:", 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) 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 "audioformat": prefs.DefaultAudioFormat = value changed = true case "videoformat": prefs.DefaultVideoFormat = value changed = true case "container": prefs.DefaultContainer = 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) }