package screens import ( "context" "fmt" "strings" "example.com/m/adapter/backend" "example.com/m/bot" "github.com/NicoNex/echotron/v3" "github.com/rs/zerolog/log" ) type PlacementPurchasePlan struct { WorkspaceID string ProjectID string AddPlacementState *AddPlacement } func (s *PlacementPurchasePlan) Enter(b *bot.Bot, mode bot.RenderMode) { jwt, err := s.ensureJWT(b) if err != nil { log.Error().Err(err).Msg("Failed to get JWT") b.SendNew("❌ Ошибка авторизации. Попробуйте /start", bot.Keyboard()) return } s.renderChannels(b, mode, jwt) } func (s *PlacementPurchasePlan) renderChannels(b *bot.Bot, mode bot.RenderMode, jwt string) { channels, err := b.Backend.GetPurchasePlanChannels( context.Background(), jwt, s.WorkspaceID, s.ProjectID, ) if err != nil { log.Error().Err(err).Msg("Failed to get purchase plan channels") b.SendNew("❌ Не удалось загрузить каналы", bot.Keyboard()) return } text := "📋 План закупа — Каналы\n\n" var buttons [][]echotron.InlineKeyboardButton if len(channels) == 0 { text += "Список каналов пуст.\n\n" } else { text += "Нажмите на канал для удаления:\n\n" for _, ch := range channels { channelName := "Без названия" if ch.Channel.Title != nil { channelName = *ch.Channel.Title } else if ch.Channel.Username != nil { channelName = "@" + *ch.Channel.Username } // Показываем статус и стоимость statusEmoji := "📌" switch ch.Status { case "planned": statusEmoji = "📝" case "approved": statusEmoji = "✅" case "rejected": statusEmoji = "❌" case "in_progress": statusEmoji = "🔄" case "completed": statusEmoji = "✔️" } buttonText := fmt.Sprintf("%s %s", statusEmoji, channelName) if ch.PlannedCost != nil { buttonText += fmt.Sprintf(" (%.0f₽)", *ch.PlannedCost) } buttons = append(buttons, bot.Row( bot.Button(buttonText, fmt.Sprintf("remove:%s", ch.ID)), )) } } // Инструкция по добавлению канала text += "\nДобавление канала:\nОтправьте username канала (без @)" // Навигация var navRow []echotron.InlineKeyboardButton navRow = append(navRow, bot.Button("« Назад", "back")) navRow = append(navRow, bot.Button("Далее →", "next")) buttons = append(buttons, navRow) keyboard := bot.Keyboard(buttons...) b.Render(text, keyboard, mode) } func (s *PlacementPurchasePlan) ensureJWT(b *bot.Bot) (string, error) { if b.Session.JWT != "" { return b.Session.JWT, nil } jwt, err := b.Backend.GetJWTByTelegramID(context.Background(), b.ChatID) if err != nil { return "", err } b.Session.JWT = jwt return jwt, nil } func (s *PlacementPurchasePlan) HandleCallback(b *bot.Bot, u *echotron.Update) { if u.CallbackQuery == nil || u.CallbackQuery.Data == "" { return } data := u.CallbackQuery.Data switch { case data == "back": if s.AddPlacementState != nil { b.SetState(s.AddPlacementState, bot.EditMessage) } case data == "next": // TODO: переход к следующему шагу (детали размещения) b.Edit("🚧 Следующий шаг в разработке", bot.Keyboard( bot.Row(bot.Button("« Назад", "back_to_purchase_plan")), )) case data == "back_to_purchase_plan": s.Enter(b, bot.EditMessage) case strings.HasPrefix(data, "remove:"): parts := strings.Split(data, ":") if len(parts) == 2 { purchasePlanChannelID := parts[1] // ID записи PurchasePlanChannel jwt, err := s.ensureJWT(b) if err != nil { log.Error().Err(err).Msg("Failed to get JWT") return } // Удаляем канал из плана закупа err = b.Backend.RemoveChannelFromPurchasePlan( context.Background(), jwt, s.WorkspaceID, s.ProjectID, purchasePlanChannelID, ) if err != nil { log.Error().Err(err).Str("purchase_plan_channel_id", purchasePlanChannelID).Msg("Failed to remove channel") b.Edit("❌ Не удалось удалить канал", bot.Keyboard( bot.Row(bot.Button("« Назад", "back_to_purchase_plan")), )) return } // Обновляем экран s.Enter(b, bot.EditMessage) } default: s.Enter(b, bot.NewMessage) } } func (s *PlacementPurchasePlan) HandleMessage(b *bot.Bot, u *echotron.Update) { if u.Message == nil || u.Message.Text == "" { return } username := strings.TrimSpace(u.Message.Text) // Убираем @ если пользователь его добавил username = strings.TrimPrefix(username, "@") if username == "" { return } jwt, err := s.ensureJWT(b) if err != nil { log.Error().Err(err).Msg("Failed to get JWT") b.SendNew("❌ Ошибка авторизации. Попробуйте /start", bot.Keyboard()) return } // Отправляем запрос на добавление канала input := backend.AttachChannelToPurchasePlanInput{ Username: username, } _, err = b.Backend.AttachChannelToPurchasePlan( context.Background(), jwt, s.WorkspaceID, s.ProjectID, input, ) if err != nil { log.Error().Err(err).Str("username", username).Msg("Failed to attach channel") // Проверяем, содержит ли ошибка информацию о том, что канал не найден if strings.Contains(err.Error(), "not found") || strings.Contains(err.Error(), "404") { b.SendNew(fmt.Sprintf("❌ Канал @%s не найден в Telegram.\n\nПроверьте правильность username и попробуйте еще раз.", username), bot.Keyboard()) } else { b.SendNew("❌ Не удалось добавить канал. Попробуйте позже.", bot.Keyboard()) } return } // Обновляем экран со списком каналов s.Enter(b, bot.NewMessage) } func (s *PlacementPurchasePlan) Handle(_ *bot.Bot, _ *echotron.Update) { return }