Files
tgex-backend/tg_bot/screens/my_projects.go
2026-01-20 15:16:06 +03:00

198 lines
4.9 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"
"strings"
"time"
"github.com/NicoNex/echotron/v3"
"github.com/TelegramExchange/tgex-backend/tg_bot/backend"
"github.com/TelegramExchange/tgex-backend/tg_bot/bot"
"github.com/TelegramExchange/tgex-backend/tg_bot/screens/ui"
"github.com/rs/zerolog/log"
)
const projectsPerPage = 6
const projectsPerRow = 2
type MyProjects struct {
CurrentPage int
BackState bot.State
OpenPlacements bool
cancel context.CancelFunc
lastProjectCount int
}
const msgNoProjects = `
<b>Мои проекты</b>
У вас пока нет подключенных каналов.
<i>Что дает подключение канала:</i>
▸ Управление типом приглашений (публичные/с одобрением)
▸ Автоматическая отправка креативов в канал
▸ Интеграция с планом закупов
▸ Статистика и аналитика подписчиков
▸ Настройка уведомлений
Начните с добавления первого проекта
`
const msgProjects = `
<b>Мои проекты</b>
Управление вашими Telegram-каналами.
Выберите канал
`
func (s *MyProjects) Enter(b *bot.Bot, mode bot.RenderMode) {
s.renderProjects(b, mode)
s.startPolling(b)
}
func (s *MyProjects) renderProjects(b *bot.Bot, mode bot.RenderMode) {
page, err := b.Backend.GetProjects(context.Background(), b.Session.JWT, b.Session.WorkspaceID, s.CurrentPage+1, projectsPerPage)
if err != nil {
b.SendNew("❌ Не удалось загрузить проекты", refreshKeyboard)
return
}
s.lastProjectCount = page.Total
var kbRows [][]echotron.InlineKeyboardButton
grid := ui.BuildGrid(page.Items, projectsPerRow, projectsPerPage, page.Pages,
func(project backend.Project) (string, string) { return project.Title, "project:" + project.ID },
)
kbRows = append(kbRows, grid...)
if navRow := ui.BuildNavigationRow(ui.PaginationConfig{
CurrentPage: s.CurrentPage,
TotalPages: page.Pages,
MiddleButtons: Row(Button(" Добавить", "add_project")),
}); navRow != nil {
kbRows = append(kbRows, navRow)
}
kbRows = append(kbRows, Row(Button("← Назад", "main_menu"), Button("≡ Архив", "archive")))
keyboard := Keyboard(kbRows...)
text := msgProjects
if len(page.Items) == 0 {
text = msgNoProjects
}
b.Render(text, keyboard, mode)
}
func (s *MyProjects) HandleCallback(b *bot.Bot, u *echotron.Update) {
if u.CallbackQuery == nil || u.CallbackQuery.Data == "" {
return
}
data := u.CallbackQuery.Data
switch {
case data == "prev":
if s.CurrentPage > 0 {
s.CurrentPage--
}
s.Enter(b, bot.EditMessage)
case data == "next":
s.CurrentPage++
s.Enter(b, bot.EditMessage)
case data == "main_menu":
b.SetState(&MainMenu{}, bot.EditMessage)
case data == "archive":
b.Edit("Эта функция в разработке", Keyboard(Row(Button("← Назад", "in_dev"))))
case data == "add_project":
b.SetState(&AddProject{BackState: &MyProjects{}}, bot.EditMessage)
case strings.HasPrefix(data, "project:"):
projectID, ok := strings.CutPrefix(data, "project:")
if !ok || projectID == "" {
return
}
project, err := b.Backend.GetProject(context.Background(), b.Session.JWT, b.Session.WorkspaceID, projectID)
if err != nil {
log.Error().Err(err).Str("project_id", projectID).Msg("Failed to get project")
return
}
if s.OpenPlacements {
b.SetState(&Placements{
ProjectID: project.ID,
ProjectTitle: project.Title,
BackState: &MyProjects{OpenPlacements: true},
}, bot.EditMessage)
} else {
b.SetState(&ProjectDetails{
Project: project,
BackState: &MyProjects{},
}, bot.EditMessage)
}
default:
s.Enter(b, bot.EditMessage)
}
return
}
func (s *MyProjects) HandleMessage(_ *bot.Bot, _ *echotron.Update) { return }
func (s *MyProjects) Handle(_ *bot.Bot, _ *echotron.Update) { return }
func (s *MyProjects) Exit() {
if s.cancel != nil {
log.Info().Msg("MyProjects: cancelling polling goroutine")
s.cancel()
}
}
func (s *MyProjects) startPolling(b *bot.Bot) {
if s.cancel != nil {
s.cancel()
}
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Minute)
s.cancel = cancel
go s.pollProjects(ctx, b)
}
func (s *MyProjects) pollProjects(ctx context.Context, b *bot.Bot) {
interval := 3 * time.Second
timeToSlow := time.Now().Add(30 * time.Second)
timer := time.NewTimer(interval)
defer timer.Stop()
for {
select {
case <-ctx.Done():
return
case <-timer.C:
if time.Now().After(timeToSlow) {
interval = 10 * time.Second
}
page, err := b.Backend.GetProjects(context.Background(), b.Session.JWT, b.Session.WorkspaceID, s.CurrentPage+1, projectsPerPage)
if err == nil && page.Total != s.lastProjectCount {
s.lastProjectCount = page.Total
s.renderProjects(b, bot.EditMessage)
}
timer.Reset(interval)
}
}
}