фиксы
This commit is contained in:
@@ -52,6 +52,20 @@ func New(cfg Config) (*Client, error) {
|
|||||||
return fmt.Errorf("session not authorized")
|
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)
|
close(ready)
|
||||||
|
|
||||||
<-ctx.Done()
|
<-ctx.Done()
|
||||||
|
|||||||
@@ -146,6 +146,10 @@ func (s *AddPurchase) renderSelection(b *bot.Bot, mode bot.RenderMode) {
|
|||||||
text += `Для создания закупа сначала нужно создать креатив.
|
text += `Для создания закупа сначала нужно создать креатив.
|
||||||
|
|
||||||
Вернитесь назад и создайте креатив в разделе <b>Креативы</b>`
|
Вернитесь назад и создайте креатив в разделе <b>Креативы</b>`
|
||||||
|
|
||||||
|
buttons = append(buttons, Row(
|
||||||
|
Button("🎨 Перейти к креативам", "go_to_creatives"),
|
||||||
|
))
|
||||||
} else {
|
} else {
|
||||||
text += fmt.Sprintf("Креативы%s\n\n", ui.FormatPageInfo(s.CreativePage, page.Pages))
|
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.ActivePicker = ""
|
||||||
s.Enter(b, bot.EditMessage)
|
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":
|
case "prev":
|
||||||
if s.ActivePicker == "project_list" {
|
if s.ActivePicker == "project_list" {
|
||||||
if s.ProjectPage > 0 {
|
if s.ProjectPage > 0 {
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import (
|
|||||||
|
|
||||||
"github.com/NicoNex/echotron/v3"
|
"github.com/NicoNex/echotron/v3"
|
||||||
"github.com/TelegramExchange/tgex-backend/tg_bot/bot"
|
"github.com/TelegramExchange/tgex-backend/tg_bot/bot"
|
||||||
"github.com/rs/zerolog/log"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Placements struct {
|
type Placements struct {
|
||||||
@@ -18,72 +17,24 @@ type Placements struct {
|
|||||||
BackState bot.State
|
BackState bot.State
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const msgPlacements = `
|
||||||
|
<b>Размещения</b>
|
||||||
|
|
||||||
|
Управление размещениями для проекта.
|
||||||
|
`
|
||||||
|
|
||||||
func (s *Placements) Enter(b *bot.Bot, mode bot.RenderMode) {
|
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) {
|
rows = append(rows, Row(Button("+ Создать размещение", "add_purchase")))
|
||||||
resp, err := b.Backend.GetPlacements(context.Background(), b.Session.JWT, b.Session.WorkspaceID, s.ProjectID)
|
rows = append(rows, Row(Button("Размещения", "placements_list")))
|
||||||
if err != nil {
|
rows = append(rows, Row(URLButton("План закупов", "https://example.com")))
|
||||||
log.Error().Err(err).Msg("Failed to get placements")
|
rows = append(rows, Row(Button("← Назад", "back")))
|
||||||
b.SendNew("❌ Не удалось загрузить размещения", Keyboard())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
text := "<b>Размещения</b>\n\n"
|
keyboard := Keyboard(rows...)
|
||||||
|
b.Render(msgPlacements, keyboard, mode)
|
||||||
|
|
||||||
var buttons [][]echotron.InlineKeyboardButton
|
updateProjectHeaderMedia(b, b.LastMessageID, msgPlacements, keyboard, s.ProjectTelegramID, s.ProjectTitle, s.ProjectUsername, s.ProjectStatus)
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Placements) HandleCallback(b *bot.Bot, u *echotron.Update) {
|
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,
|
BackState: s,
|
||||||
}, bot.EditMessage)
|
}, 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:
|
default:
|
||||||
// Проверяем, не нажали ли на конкретное размещение
|
// Проверяем, не нажали ли на конкретное размещение
|
||||||
if len(data) > 10 && data[:10] == "placement:" {
|
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() {}
|
||||||
@@ -12,15 +12,14 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type ProjectDetails struct {
|
type ProjectDetails struct {
|
||||||
Project *backend.Project
|
Project *backend.Project
|
||||||
BackState bot.State
|
BackState bot.State
|
||||||
confirmingLinkTypeChange bool // Флаг для двухшагового подтверждения смены типа ссылки
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ProjectDetails) Enter(b *bot.Bot, mode bot.RenderMode) {
|
func (s *ProjectDetails) Enter(b *bot.Bot, mode bot.RenderMode) {
|
||||||
if s.Project == nil {
|
if s.Project == nil {
|
||||||
b.SendNew("❌ Проект не найден", Keyboard(
|
b.SendNew("❌ Проект не найден", Keyboard(
|
||||||
Row(Button("← Назад", "back_to_projects")),
|
Row(Button("← Назад", "back")),
|
||||||
))
|
))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -40,32 +39,25 @@ func (s *ProjectDetails) formatProjectDetails() string {
|
|||||||
|
|
||||||
text := fmt.Sprintf("<b>%s</b>\n\n", p.Title)
|
text := fmt.Sprintf("<b>%s</b>\n\n", p.Title)
|
||||||
|
|
||||||
// Информация о канале
|
|
||||||
if p.Username != nil && *p.Username != "" {
|
if p.Username != nil && *p.Username != "" {
|
||||||
text += fmt.Sprintf("▸ Telegram: @%s\n", *p.Username)
|
text += fmt.Sprintf("▸ Tg username: @%s\n", *p.Username)
|
||||||
} else {
|
} else {
|
||||||
text += "▸ Telegram: <i>не привязан</i>\n"
|
text += "▸ Tg username: <i>не привязан</i>\n"
|
||||||
}
|
}
|
||||||
|
|
||||||
// Статус проекта
|
// Статус проекта
|
||||||
var statusSymbol string
|
var statusSymbol, statusText string
|
||||||
var statusText string
|
|
||||||
switch p.Status {
|
switch p.Status {
|
||||||
case "active":
|
case "active":
|
||||||
statusSymbol = "●"
|
statusSymbol, statusText = "●", "Активный"
|
||||||
statusText = "Активный"
|
|
||||||
case "inactive":
|
case "inactive":
|
||||||
statusSymbol = "○"
|
statusSymbol, statusText = "○", "Неактивен"
|
||||||
statusText = "Неактивен"
|
|
||||||
case "archived":
|
case "archived":
|
||||||
statusSymbol = "■"
|
statusSymbol, statusText = "■", "Архивный"
|
||||||
statusText = "Архивный"
|
|
||||||
case "paused":
|
case "paused":
|
||||||
statusSymbol = "◐"
|
statusSymbol, statusText = "◐", "Приостановлен"
|
||||||
statusText = "Приостановлен"
|
|
||||||
default:
|
default:
|
||||||
statusSymbol = "○"
|
statusSymbol, statusText = "○", p.Status
|
||||||
statusText = p.Status
|
|
||||||
}
|
}
|
||||||
text += fmt.Sprintf("▸ Статус: %s %s\n", statusSymbol, statusText)
|
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)),
|
Button("Размещения", fmt.Sprintf("placements:%s", s.Project.ID)),
|
||||||
))
|
))
|
||||||
|
|
||||||
// Второй ряд: Тип вступления по умолчанию для закупов (с подтверждением)
|
// Второй ряд: Тип вступления по умолчанию для закупов
|
||||||
var linkTypeButtonText string
|
var currentType string
|
||||||
if s.confirmingLinkTypeChange {
|
if s.Project.PurchaseInviteTypeDefault == "public" {
|
||||||
// Показываем подтверждение
|
currentType = "открытая"
|
||||||
var newType string
|
|
||||||
if s.Project.PurchaseInviteTypeDefault == "public" {
|
|
||||||
newType = "По заявке"
|
|
||||||
} else {
|
|
||||||
newType = "Свободное"
|
|
||||||
}
|
|
||||||
linkTypeButtonText = fmt.Sprintf("Изменить → %s", newType)
|
|
||||||
} else {
|
} else {
|
||||||
// Показываем текущий тип вступления
|
currentType = "с заявками"
|
||||||
var currentType string
|
|
||||||
if s.Project.PurchaseInviteTypeDefault == "public" {
|
|
||||||
currentType = "Свободное"
|
|
||||||
} else {
|
|
||||||
currentType = "По заявке"
|
|
||||||
}
|
|
||||||
linkTypeButtonText = fmt.Sprintf("Вступление: %s", currentType)
|
|
||||||
}
|
}
|
||||||
buttons = append(buttons, Row(
|
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(
|
buttons = append(buttons, Row(
|
||||||
Button("← Назад", "back_to_projects"),
|
Button("← Назад", "back"),
|
||||||
Button("≡ Архивировать", fmt.Sprintf("archive:%s", s.Project.ID)),
|
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
|
data := u.CallbackQuery.Data
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case data == "back_to_projects", data == "back":
|
case data == "back":
|
||||||
s.confirmingLinkTypeChange = false // Сбрасываем флаг подтверждения
|
|
||||||
if s.BackState != nil {
|
if s.BackState != nil {
|
||||||
s.transitionWithNewMessage(b, s.BackState)
|
s.transitionWithNewMessage(b, s.BackState)
|
||||||
}
|
}
|
||||||
|
|
||||||
case strings.HasPrefix(data, "creatives:"):
|
case strings.HasPrefix(data, "creatives:"):
|
||||||
s.confirmingLinkTypeChange = false // Сбрасываем флаг подтверждения
|
|
||||||
b.SetState(&Creatives{
|
b.SetState(&Creatives{
|
||||||
ProjectID: s.Project.ID,
|
ProjectID: s.Project.ID,
|
||||||
ProjectTitle: s.Project.Title,
|
ProjectTitle: s.Project.Title,
|
||||||
@@ -142,7 +118,6 @@ func (s *ProjectDetails) HandleCallback(b *bot.Bot, u *echotron.Update) {
|
|||||||
}, bot.EditMessage)
|
}, bot.EditMessage)
|
||||||
|
|
||||||
case strings.HasPrefix(data, "placements:"):
|
case strings.HasPrefix(data, "placements:"):
|
||||||
s.confirmingLinkTypeChange = false // Сбрасываем флаг подтверждения
|
|
||||||
b.SetState(&Placements{
|
b.SetState(&Placements{
|
||||||
ProjectID: s.Project.ID,
|
ProjectID: s.Project.ID,
|
||||||
ProjectTitle: s.Project.Title,
|
ProjectTitle: s.Project.Title,
|
||||||
@@ -153,38 +128,28 @@ func (s *ProjectDetails) HandleCallback(b *bot.Bot, u *echotron.Update) {
|
|||||||
}, bot.EditMessage)
|
}, bot.EditMessage)
|
||||||
|
|
||||||
case data == "back_to_project":
|
case data == "back_to_project":
|
||||||
s.confirmingLinkTypeChange = false // Сбрасываем флаг подтверждения
|
|
||||||
s.Enter(b, bot.EditMessage)
|
s.Enter(b, bot.EditMessage)
|
||||||
|
|
||||||
case strings.HasPrefix(data, "link_type:"):
|
case strings.HasPrefix(data, "link_type:"):
|
||||||
if !s.confirmingLinkTypeChange {
|
var newType string
|
||||||
// Первое нажатие - включаем режим подтверждения
|
if s.Project.PurchaseInviteTypeDefault == "public" {
|
||||||
s.confirmingLinkTypeChange = true
|
newType = "approval"
|
||||||
s.Enter(b, bot.EditMessage)
|
|
||||||
} else {
|
} else {
|
||||||
// Второе нажатие - меняем тип ссылки
|
newType = "public"
|
||||||
var newType string
|
|
||||||
if s.Project.PurchaseInviteTypeDefault == "public" {
|
|
||||||
newType = "approval"
|
|
||||||
} else {
|
|
||||||
newType = "public"
|
|
||||||
}
|
|
||||||
|
|
||||||
updatedProject, err := b.Backend.UpdateProjectInviteLinkType(context.Background(), b.Session.JWT, b.Session.WorkspaceID, s.Project.ID, newType)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
b.SendNew(fmt.Sprintf("❌ Ошибка при изменении типа ссылки: %v", err), Keyboard(
|
|
||||||
Row(Button("← Назад", "back_to_project")),
|
|
||||||
))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Обновляем проект и сбрасываем флаг подтверждения
|
|
||||||
s.Project = updatedProject
|
|
||||||
s.confirmingLinkTypeChange = false
|
|
||||||
s.Enter(b, bot.EditMessage)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updatedProject, err := b.Backend.UpdateProjectInviteLinkType(context.Background(), b.Session.JWT, b.Session.WorkspaceID, s.Project.ID, newType)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
b.SendNew(fmt.Sprintf("❌ Ошибка при изменении типа ссылки: %v", err), Keyboard(
|
||||||
|
Row(Button("← Назад", "back_to_project")),
|
||||||
|
))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
s.Project = updatedProject
|
||||||
|
s.Enter(b, bot.EditMessage)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
// Для остальных кнопок показываем заглушку
|
// Для остальных кнопок показываем заглушку
|
||||||
b.Edit(fmt.Sprintf("🚧 Функция в разработке\n\nCallback: %s", data), Keyboard(
|
b.Edit(fmt.Sprintf("🚧 Функция в разработке\n\nCallback: %s", data), Keyboard(
|
||||||
|
|||||||
Reference in New Issue
Block a user