Files
tgex-backend/telegram_bot/screens/placement_purchase_plan.go
2026-01-01 19:33:37 +03:00

223 lines
6.0 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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 := "<b>📋 План закупа — Каналы</b>\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<b>Добавление канала:</b>\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 }