220 lines
5.8 KiB
Go
220 lines
5.8 KiB
Go
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 PurchaseChannelInput struct {
|
||
Username string
|
||
PlannedCost *float64
|
||
Comment *string
|
||
}
|
||
|
||
type SelectChannelsForPurchase struct {
|
||
WorkspaceID string
|
||
ProjectID string
|
||
ProjectTitle string
|
||
CreativeID string
|
||
Channels []PurchaseChannelInput
|
||
BackState bot.State
|
||
}
|
||
|
||
func (s *SelectChannelsForPurchase) Enter(b *bot.Bot, mode bot.RenderMode) {
|
||
s.renderChannelSelection(b, mode)
|
||
}
|
||
|
||
func (s *SelectChannelsForPurchase) renderChannelSelection(b *bot.Bot, mode bot.RenderMode) {
|
||
text := "<i>… › Создание › Каналы</i>\n\n"
|
||
text += "<b>Создание закупа</b>\n\n"
|
||
text += "<b>Шаг 2/2:</b> Добавление каналов\n\n"
|
||
|
||
var buttons [][]echotron.InlineKeyboardButton
|
||
|
||
if len(s.Channels) == 0 {
|
||
text += `Добавьте каналы для размещения рекламы
|
||
|
||
Отправьте username канала (без @)
|
||
Например: <code>channel_name</code>
|
||
|
||
После добавления всех каналов нажмите <b>Создать закуп</b>`
|
||
} else {
|
||
text += fmt.Sprintf("Добавлено каналов: <b>%d</b>\n\n", len(s.Channels))
|
||
|
||
for i, ch := range s.Channels {
|
||
channelText := fmt.Sprintf(" %d. @%s", i+1, ch.Username)
|
||
if ch.PlannedCost != nil {
|
||
channelText += fmt.Sprintf(" — %.0f₽", *ch.PlannedCost)
|
||
}
|
||
text += channelText + "\n"
|
||
}
|
||
|
||
text += "\nДобавьте еще каналы или создайте закуп"
|
||
|
||
// Кнопка создания закупа (доступна только если есть каналы)
|
||
buttons = append(buttons, bot.Row(
|
||
bot.Button("● Создать закуп", "create"),
|
||
))
|
||
}
|
||
|
||
// Кнопки навигации
|
||
buttons = append(buttons, bot.Row(
|
||
bot.Button("← Назад", "back"),
|
||
))
|
||
|
||
keyboard := bot.Keyboard(buttons...)
|
||
b.Render(text, keyboard, mode)
|
||
}
|
||
|
||
func (s *SelectChannelsForPurchase) 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 *SelectChannelsForPurchase) HandleCallback(b *bot.Bot, u *echotron.Update) {
|
||
if u.CallbackQuery == nil || u.CallbackQuery.Data == "" {
|
||
return
|
||
}
|
||
|
||
data := u.CallbackQuery.Data
|
||
|
||
switch data {
|
||
case "back":
|
||
// Возвращаемся к выбору креатива
|
||
b.SetState(&AddPurchase{
|
||
WorkspaceID: s.WorkspaceID,
|
||
ProjectID: s.ProjectID,
|
||
ProjectTitle: s.ProjectTitle,
|
||
BackState: s.BackState,
|
||
}, bot.EditMessage)
|
||
|
||
case "create":
|
||
if len(s.Channels) == 0 {
|
||
b.Edit("❌ Добавьте хотя бы один канал", bot.Keyboard(
|
||
bot.Row(bot.Button("← Назад", "back_to_select")),
|
||
))
|
||
return
|
||
}
|
||
|
||
jwt, err := s.ensureJWT(b)
|
||
if err != nil {
|
||
log.Error().Err(err).Msg("Failed to get JWT")
|
||
b.Edit("❌ Ошибка авторизации", bot.Keyboard())
|
||
return
|
||
}
|
||
|
||
// Создаем закуп
|
||
s.createPurchase(b, jwt)
|
||
|
||
case "back_to_select":
|
||
s.Enter(b, bot.EditMessage)
|
||
|
||
default:
|
||
s.Enter(b, bot.NewMessage)
|
||
}
|
||
}
|
||
|
||
func (s *SelectChannelsForPurchase) createPurchase(b *bot.Bot, jwt string) {
|
||
// Преобразуем каналы в формат API
|
||
var apiChannels []backend.CreatePurchaseChannelInput
|
||
for _, ch := range s.Channels {
|
||
apiChannels = append(apiChannels, backend.CreatePurchaseChannelInput{
|
||
Username: ch.Username,
|
||
PlannedCost: ch.PlannedCost,
|
||
Comment: ch.Comment,
|
||
})
|
||
}
|
||
|
||
input := backend.CreatePurchaseInput{
|
||
CreativeID: s.CreativeID,
|
||
Channels: apiChannels,
|
||
}
|
||
|
||
purchase, err := b.Backend.CreatePurchase(
|
||
context.Background(),
|
||
jwt,
|
||
s.WorkspaceID,
|
||
s.ProjectID,
|
||
input,
|
||
)
|
||
|
||
if err != nil {
|
||
log.Error().Err(err).Msg("Failed to create purchase")
|
||
b.Edit("❌ Не удалось создать закуп\n\nВозможные причины:\n• Один из каналов не найден\n• Ошибка сервера", bot.Keyboard(
|
||
bot.Row(bot.Button("← Назад", "back_to_select")),
|
||
))
|
||
return
|
||
}
|
||
|
||
// Показываем успешное создание
|
||
successText := fmt.Sprintf(`<i>… › Создание › Готово</i>
|
||
|
||
<b>Закуп создан</b>
|
||
|
||
▪ <b>ID:</b> %s
|
||
▪ <b>Креатив:</b> %s
|
||
▪ <b>Каналов:</b> %d
|
||
|
||
Закуп создан и готов к работе`, purchase.ID, purchase.CreativeID, len(purchase.Channels))
|
||
|
||
b.Edit(successText, bot.Keyboard(
|
||
bot.Row(bot.Button("● Готово", "done")),
|
||
))
|
||
}
|
||
|
||
func (s *SelectChannelsForPurchase) 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
|
||
}
|
||
|
||
// Проверяем, не добавлен ли уже этот канал
|
||
for _, ch := range s.Channels {
|
||
if ch.Username == username {
|
||
b.SendNew(fmt.Sprintf("⚠️ Канал @%s уже добавлен в список", username), bot.Keyboard())
|
||
return
|
||
}
|
||
}
|
||
|
||
// Добавляем канал в список
|
||
s.Channels = append(s.Channels, PurchaseChannelInput{
|
||
Username: username,
|
||
})
|
||
|
||
// Обновляем экран
|
||
s.Enter(b, bot.NewMessage)
|
||
}
|
||
|
||
func (s *SelectChannelsForPurchase) Handle(b *bot.Bot, u *echotron.Update) {
|
||
// Обрабатываем callback после успешного создания
|
||
if u.CallbackQuery != nil && u.CallbackQuery.Data == "done" {
|
||
// Возвращаемся к списку закупов
|
||
if s.BackState != nil {
|
||
b.SetState(s.BackState, bot.EditMessage)
|
||
}
|
||
}
|
||
}
|