фиксы
This commit is contained in:
@@ -52,6 +52,20 @@ func New(cfg Config) (*Client, error) {
|
||||
return fmt.Errorf("session not authorized")
|
||||
}
|
||||
|
||||
// Получаем информацию о текущем пользователе
|
||||
self, err := tg.Self(ctx)
|
||||
if err != nil {
|
||||
log.Warn().Err(err).Msg("Failed to get user info")
|
||||
} else {
|
||||
log.Info().
|
||||
Int64("id", self.ID).
|
||||
Str("username", "@"+self.Username).
|
||||
Str("first_name", self.FirstName).
|
||||
Str("last_name", self.LastName).
|
||||
Str("phone", self.Phone).
|
||||
Msg("Authorized as user")
|
||||
}
|
||||
|
||||
close(ready)
|
||||
|
||||
<-ctx.Done()
|
||||
|
||||
@@ -146,6 +146,10 @@ func (s *AddPurchase) renderSelection(b *bot.Bot, mode bot.RenderMode) {
|
||||
text += `Для создания закупа сначала нужно создать креатив.
|
||||
|
||||
Вернитесь назад и создайте креатив в разделе <b>Креативы</b>`
|
||||
|
||||
buttons = append(buttons, Row(
|
||||
Button("🎨 Перейти к креативам", "go_to_creatives"),
|
||||
))
|
||||
} else {
|
||||
text += fmt.Sprintf("Креативы%s\n\n", ui.FormatPageInfo(s.CreativePage, page.Pages))
|
||||
|
||||
@@ -219,6 +223,17 @@ func (s *AddPurchase) HandleCallback(b *bot.Bot, u *echotron.Update) {
|
||||
s.ActivePicker = ""
|
||||
s.Enter(b, bot.EditMessage)
|
||||
|
||||
case "go_to_creatives":
|
||||
b.SetState(&Creatives{
|
||||
CurrentPage: 0,
|
||||
ProjectID: s.ProjectID,
|
||||
ProjectTitle: s.ProjectTitle,
|
||||
ProjectTelegramID: s.ProjectTelegramID,
|
||||
ProjectUsername: s.ProjectUsername,
|
||||
ProjectStatus: s.ProjectStatus,
|
||||
BackState: s,
|
||||
}, bot.EditMessage)
|
||||
|
||||
case "prev":
|
||||
if s.ActivePicker == "project_list" {
|
||||
if s.ProjectPage > 0 {
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
|
||||
"github.com/NicoNex/echotron/v3"
|
||||
"github.com/TelegramExchange/tgex-backend/tg_bot/bot"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
type Placements struct {
|
||||
@@ -18,72 +17,24 @@ type Placements struct {
|
||||
BackState bot.State
|
||||
}
|
||||
|
||||
const msgPlacements = `
|
||||
<b>Размещения</b>
|
||||
|
||||
Управление размещениями для проекта.
|
||||
`
|
||||
|
||||
func (s *Placements) Enter(b *bot.Bot, mode bot.RenderMode) {
|
||||
s.renderPurchases(b, mode)
|
||||
}
|
||||
var rows [][]echotron.InlineKeyboardButton
|
||||
|
||||
func (s *Placements) renderPurchases(b *bot.Bot, mode bot.RenderMode) {
|
||||
resp, err := b.Backend.GetPlacements(context.Background(), b.Session.JWT, b.Session.WorkspaceID, s.ProjectID)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("Failed to get placements")
|
||||
b.SendNew("❌ Не удалось загрузить размещения", Keyboard())
|
||||
return
|
||||
}
|
||||
rows = append(rows, Row(Button("+ Создать размещение", "add_purchase")))
|
||||
rows = append(rows, Row(Button("Размещения", "placements_list")))
|
||||
rows = append(rows, Row(URLButton("План закупов", "https://example.com")))
|
||||
rows = append(rows, Row(Button("← Назад", "back")))
|
||||
|
||||
text := "<b>Размещения</b>\n\n"
|
||||
keyboard := Keyboard(rows...)
|
||||
b.Render(msgPlacements, keyboard, mode)
|
||||
|
||||
var buttons [][]echotron.InlineKeyboardButton
|
||||
|
||||
if len(resp.Placements) == 0 {
|
||||
text += `Список размещений пуст.
|
||||
|
||||
<i>Размещение — это план публикации рекламы в канале:</i>
|
||||
‣ Выбор креатива
|
||||
‣ Канал размещения
|
||||
‣ Стоимость и формат
|
||||
‣ Статус выполнения
|
||||
|
||||
Создайте первое размещение`
|
||||
} else {
|
||||
text += "Управление размещениями для проекта.\n\n"
|
||||
text += "Выберите размещение для деталей:\n\n"
|
||||
|
||||
for i, placement := range resp.Placements {
|
||||
statusSymbol := "●"
|
||||
if placement.Status == "archived" {
|
||||
statusSymbol = "■"
|
||||
}
|
||||
|
||||
channelName := "Без названия"
|
||||
if placement.Channel.Title != nil && *placement.Channel.Title != "" {
|
||||
channelName = *placement.Channel.Title
|
||||
} else if placement.Channel.Username != nil && *placement.Channel.Username != "" {
|
||||
channelName = "@" + *placement.Channel.Username
|
||||
}
|
||||
|
||||
buttonText := fmt.Sprintf("%s #%d %s", statusSymbol, i+1, channelName)
|
||||
|
||||
buttons = append(buttons, Row(
|
||||
Button(buttonText, fmt.Sprintf("placement:%s", placement.ID)),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
// Кнопка создания нового размещения
|
||||
buttons = append(buttons, Row(
|
||||
Button("+ Создать размещение", "add_purchase"),
|
||||
))
|
||||
|
||||
// Кнопка назад
|
||||
buttons = append(buttons, Row(
|
||||
Button("← Назад", "back"),
|
||||
))
|
||||
|
||||
keyboard := Keyboard(buttons...)
|
||||
b.Render(text, keyboard, mode)
|
||||
|
||||
messageID := b.LastMessageID
|
||||
updateProjectHeaderMedia(b, messageID, text, keyboard, s.ProjectTelegramID, s.ProjectTitle, s.ProjectUsername, s.ProjectStatus)
|
||||
updateProjectHeaderMedia(b, b.LastMessageID, msgPlacements, keyboard, s.ProjectTelegramID, s.ProjectTitle, s.ProjectUsername, s.ProjectStatus)
|
||||
}
|
||||
|
||||
func (s *Placements) HandleCallback(b *bot.Bot, u *echotron.Update) {
|
||||
@@ -110,6 +61,106 @@ func (s *Placements) HandleCallback(b *bot.Bot, u *echotron.Update) {
|
||||
BackState: s,
|
||||
}, bot.EditMessage)
|
||||
|
||||
case "placements_list":
|
||||
b.SetState(&PlacementsList{
|
||||
ProjectID: s.ProjectID,
|
||||
ProjectTitle: s.ProjectTitle,
|
||||
ProjectTelegramID: s.ProjectTelegramID,
|
||||
ProjectUsername: s.ProjectUsername,
|
||||
ProjectStatus: s.ProjectStatus,
|
||||
BackState: s,
|
||||
}, bot.EditMessage)
|
||||
|
||||
default:
|
||||
s.Enter(b, bot.NewMessage)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Placements) HandleMessage(_ *bot.Bot, _ *echotron.Update) { return }
|
||||
|
||||
func (s *Placements) Handle(_ *bot.Bot, _ *echotron.Update) { return }
|
||||
|
||||
func (s *Placements) Exit() {}
|
||||
|
||||
// PlacementsList - экран со списком размещений
|
||||
type PlacementsList struct {
|
||||
ProjectID string
|
||||
ProjectTitle string
|
||||
ProjectTelegramID int64
|
||||
ProjectUsername string
|
||||
ProjectStatus string
|
||||
BackState bot.State
|
||||
}
|
||||
|
||||
func (s *PlacementsList) Enter(b *bot.Bot, mode bot.RenderMode) {
|
||||
s.render(b, mode)
|
||||
}
|
||||
|
||||
func (s *PlacementsList) render(b *bot.Bot, mode bot.RenderMode) {
|
||||
resp, err := b.Backend.GetPlacements(context.Background(), b.Session.JWT, b.Session.WorkspaceID, s.ProjectID)
|
||||
if err != nil {
|
||||
b.SendNew("❌ Не удалось загрузить размещения", Keyboard())
|
||||
return
|
||||
}
|
||||
|
||||
text := "<b>Список размещений</b>\n\n"
|
||||
|
||||
var buttons [][]echotron.InlineKeyboardButton
|
||||
|
||||
if len(resp.Placements) == 0 {
|
||||
text += `Список размещений пуст.
|
||||
|
||||
<i>Размещение — это план публикации рекламы в канале:</i>
|
||||
‣ Выбор креатива
|
||||
‣ Канал размещения
|
||||
‣ Стоимость и формат
|
||||
‣ Статус выполнения
|
||||
|
||||
Создайте первое размещение`
|
||||
} else {
|
||||
for i, placement := range resp.Placements {
|
||||
statusSymbol := "●"
|
||||
if placement.Status == "archived" {
|
||||
statusSymbol = "■"
|
||||
}
|
||||
|
||||
channelName := "Без названия"
|
||||
if placement.Channel.Title != nil && *placement.Channel.Title != "" {
|
||||
channelName = *placement.Channel.Title
|
||||
} else if placement.Channel.Username != nil && *placement.Channel.Username != "" {
|
||||
channelName = "@" + *placement.Channel.Username
|
||||
}
|
||||
|
||||
buttonText := fmt.Sprintf("%s #%d %s", statusSymbol, i+1, channelName)
|
||||
|
||||
buttons = append(buttons, Row(
|
||||
Button(buttonText, fmt.Sprintf("placement:%s", placement.ID)),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
// Кнопка назад
|
||||
buttons = append(buttons, Row(
|
||||
Button("← Назад", "back"),
|
||||
))
|
||||
|
||||
keyboard := Keyboard(buttons...)
|
||||
b.Render(text, keyboard, mode)
|
||||
}
|
||||
|
||||
func (s *PlacementsList) HandleCallback(b *bot.Bot, u *echotron.Update) {
|
||||
if u.CallbackQuery == nil || u.CallbackQuery.Data == "" {
|
||||
return
|
||||
}
|
||||
|
||||
data := u.CallbackQuery.Data
|
||||
|
||||
switch data {
|
||||
case "back":
|
||||
if s.BackState != nil {
|
||||
b.SetState(s.BackState, bot.EditMessage)
|
||||
}
|
||||
|
||||
default:
|
||||
// Проверяем, не нажали ли на конкретное размещение
|
||||
if len(data) > 10 && data[:10] == "placement:" {
|
||||
@@ -125,8 +176,8 @@ func (s *Placements) HandleCallback(b *bot.Bot, u *echotron.Update) {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Placements) HandleMessage(_ *bot.Bot, _ *echotron.Update) { return }
|
||||
func (s *PlacementsList) HandleMessage(_ *bot.Bot, _ *echotron.Update) { return }
|
||||
|
||||
func (s *Placements) Handle(_ *bot.Bot, _ *echotron.Update) { return }
|
||||
func (s *PlacementsList) Handle(_ *bot.Bot, _ *echotron.Update) { return }
|
||||
|
||||
func (s *Placements) Exit() {}
|
||||
func (s *PlacementsList) Exit() {}
|
||||
@@ -14,13 +14,12 @@ import (
|
||||
type ProjectDetails struct {
|
||||
Project *backend.Project
|
||||
BackState bot.State
|
||||
confirmingLinkTypeChange bool // Флаг для двухшагового подтверждения смены типа ссылки
|
||||
}
|
||||
|
||||
func (s *ProjectDetails) Enter(b *bot.Bot, mode bot.RenderMode) {
|
||||
if s.Project == nil {
|
||||
b.SendNew("❌ Проект не найден", Keyboard(
|
||||
Row(Button("← Назад", "back_to_projects")),
|
||||
Row(Button("← Назад", "back")),
|
||||
))
|
||||
return
|
||||
}
|
||||
@@ -40,32 +39,25 @@ func (s *ProjectDetails) formatProjectDetails() string {
|
||||
|
||||
text := fmt.Sprintf("<b>%s</b>\n\n", p.Title)
|
||||
|
||||
// Информация о канале
|
||||
if p.Username != nil && *p.Username != "" {
|
||||
text += fmt.Sprintf("▸ Telegram: @%s\n", *p.Username)
|
||||
text += fmt.Sprintf("▸ Tg username: @%s\n", *p.Username)
|
||||
} else {
|
||||
text += "▸ Telegram: <i>не привязан</i>\n"
|
||||
text += "▸ Tg username: <i>не привязан</i>\n"
|
||||
}
|
||||
|
||||
// Статус проекта
|
||||
var statusSymbol string
|
||||
var statusText string
|
||||
var statusSymbol, statusText string
|
||||
switch p.Status {
|
||||
case "active":
|
||||
statusSymbol = "●"
|
||||
statusText = "Активный"
|
||||
statusSymbol, statusText = "●", "Активный"
|
||||
case "inactive":
|
||||
statusSymbol = "○"
|
||||
statusText = "Неактивен"
|
||||
statusSymbol, statusText = "○", "Неактивен"
|
||||
case "archived":
|
||||
statusSymbol = "■"
|
||||
statusText = "Архивный"
|
||||
statusSymbol, statusText = "■", "Архивный"
|
||||
case "paused":
|
||||
statusSymbol = "◐"
|
||||
statusText = "Приостановлен"
|
||||
statusSymbol, statusText = "◐", "Приостановлен"
|
||||
default:
|
||||
statusSymbol = "○"
|
||||
statusText = p.Status
|
||||
statusSymbol, statusText = "○", p.Status
|
||||
}
|
||||
text += fmt.Sprintf("▸ Статус: %s %s\n", statusSymbol, statusText)
|
||||
|
||||
@@ -82,34 +74,20 @@ func (s *ProjectDetails) buildKeyboard() echotron.InlineKeyboardMarkup {
|
||||
Button("Размещения", fmt.Sprintf("placements:%s", s.Project.ID)),
|
||||
))
|
||||
|
||||
// Второй ряд: Тип вступления по умолчанию для закупов (с подтверждением)
|
||||
var linkTypeButtonText string
|
||||
if s.confirmingLinkTypeChange {
|
||||
// Показываем подтверждение
|
||||
var newType string
|
||||
if s.Project.PurchaseInviteTypeDefault == "public" {
|
||||
newType = "По заявке"
|
||||
} else {
|
||||
newType = "Свободное"
|
||||
}
|
||||
linkTypeButtonText = fmt.Sprintf("Изменить → %s", newType)
|
||||
} else {
|
||||
// Показываем текущий тип вступления
|
||||
// Второй ряд: Тип вступления по умолчанию для закупов
|
||||
var currentType string
|
||||
if s.Project.PurchaseInviteTypeDefault == "public" {
|
||||
currentType = "Свободное"
|
||||
currentType = "открытая"
|
||||
} else {
|
||||
currentType = "По заявке"
|
||||
}
|
||||
linkTypeButtonText = fmt.Sprintf("Вступление: %s", currentType)
|
||||
currentType = "с заявками"
|
||||
}
|
||||
buttons = append(buttons, Row(
|
||||
Button(linkTypeButtonText, fmt.Sprintf("link_type:%s", s.Project.ID)),
|
||||
Button(fmt.Sprintf("Ссылка: %s", currentType), fmt.Sprintf("link_type:%s", s.Project.ID)),
|
||||
))
|
||||
|
||||
// Нижний ряд: Назад и Архивировать
|
||||
buttons = append(buttons, Row(
|
||||
Button("← Назад", "back_to_projects"),
|
||||
Button("← Назад", "back"),
|
||||
Button("≡ Архивировать", fmt.Sprintf("archive:%s", s.Project.ID)),
|
||||
))
|
||||
|
||||
@@ -124,14 +102,12 @@ func (s *ProjectDetails) HandleCallback(b *bot.Bot, u *echotron.Update) {
|
||||
data := u.CallbackQuery.Data
|
||||
|
||||
switch {
|
||||
case data == "back_to_projects", data == "back":
|
||||
s.confirmingLinkTypeChange = false // Сбрасываем флаг подтверждения
|
||||
case data == "back":
|
||||
if s.BackState != nil {
|
||||
s.transitionWithNewMessage(b, s.BackState)
|
||||
}
|
||||
|
||||
case strings.HasPrefix(data, "creatives:"):
|
||||
s.confirmingLinkTypeChange = false // Сбрасываем флаг подтверждения
|
||||
b.SetState(&Creatives{
|
||||
ProjectID: s.Project.ID,
|
||||
ProjectTitle: s.Project.Title,
|
||||
@@ -142,7 +118,6 @@ func (s *ProjectDetails) HandleCallback(b *bot.Bot, u *echotron.Update) {
|
||||
}, bot.EditMessage)
|
||||
|
||||
case strings.HasPrefix(data, "placements:"):
|
||||
s.confirmingLinkTypeChange = false // Сбрасываем флаг подтверждения
|
||||
b.SetState(&Placements{
|
||||
ProjectID: s.Project.ID,
|
||||
ProjectTitle: s.Project.Title,
|
||||
@@ -153,16 +128,9 @@ func (s *ProjectDetails) HandleCallback(b *bot.Bot, u *echotron.Update) {
|
||||
}, bot.EditMessage)
|
||||
|
||||
case data == "back_to_project":
|
||||
s.confirmingLinkTypeChange = false // Сбрасываем флаг подтверждения
|
||||
s.Enter(b, bot.EditMessage)
|
||||
|
||||
case strings.HasPrefix(data, "link_type:"):
|
||||
if !s.confirmingLinkTypeChange {
|
||||
// Первое нажатие - включаем режим подтверждения
|
||||
s.confirmingLinkTypeChange = true
|
||||
s.Enter(b, bot.EditMessage)
|
||||
} else {
|
||||
// Второе нажатие - меняем тип ссылки
|
||||
var newType string
|
||||
if s.Project.PurchaseInviteTypeDefault == "public" {
|
||||
newType = "approval"
|
||||
@@ -179,11 +147,8 @@ func (s *ProjectDetails) HandleCallback(b *bot.Bot, u *echotron.Update) {
|
||||
return
|
||||
}
|
||||
|
||||
// Обновляем проект и сбрасываем флаг подтверждения
|
||||
s.Project = updatedProject
|
||||
s.confirmingLinkTypeChange = false
|
||||
s.Enter(b, bot.EditMessage)
|
||||
}
|
||||
|
||||
default:
|
||||
// Для остальных кнопок показываем заглушку
|
||||
|
||||
Reference in New Issue
Block a user