закуп много каналов

This commit is contained in:
Artem Tsyrulnikov
2026-01-04 18:06:12 +03:00
parent 9637f6ec3a
commit 5251ec3015
7 changed files with 115 additions and 15 deletions

View File

@@ -206,6 +206,30 @@ func (c *Client) GetWorkspaces(
return resp.Items, err
}
func (c *Client) CreateWorkspace(
ctx context.Context,
jwt string,
name string,
) (*Workspace, error) {
req := struct {
Name string `json:"name"`
}{
Name: name,
}
var workspace Workspace
err := c.do(
ctx,
http.MethodPost,
"api/v1/workspaces",
req,
&workspace,
withBearer(jwt),
)
return &workspace, err
}
func (c *Client) GetProjects(
ctx context.Context,
jwt string,

View File

@@ -3,6 +3,7 @@ package bot
import (
"context"
"fmt"
"strings"
"example.com/m/adapter/backend"
"github.com/NicoNex/echotron/v3"
@@ -143,6 +144,11 @@ func (b *Bot) SendNew(text string, keyboard echotron.InlineKeyboardMarkup) {
func (b *Bot) Edit(text string, keyboard echotron.InlineKeyboardMarkup) {
if b.LastMessageID != 0 {
log.Info().
Int("last_msg_id", b.LastMessageID).
Int("keyboard_rows", len(keyboard.InlineKeyboard)).
Int("text_len", len(text)).
Msg("EditMessageText request")
_, err := b.EditMessageText(
text,
echotron.NewMessageID(b.ChatID, b.LastMessageID),
@@ -152,6 +158,13 @@ func (b *Bot) Edit(text string, keyboard echotron.InlineKeyboardMarkup) {
},
)
if err != nil {
if strings.Contains(err.Error(), "message is not modified") {
log.Info().Msg("EditMessageText ignored: message is not modified")
if b.exec != nil {
b.exec.handled = true
}
return
}
log.Error().Err(err).Msg("EditMessageText")
}
} else {
@@ -195,6 +208,9 @@ func (b *Bot) Update(u *echotron.Update) {
log.Info().
Int64("chat_id", b.ChatID).
Str("current_state", fmt.Sprintf("%T", b.CurrentState)).
Bool("has_callback", u.CallbackQuery != nil).
Bool("has_message", u.Message != nil).
Bool("has_edited_message", u.EditedMessage != nil).
Msg("Update received")
// Обрабатываем системные события (добавление в канал, подписки и т.д.)
@@ -255,7 +271,21 @@ func (b *Bot) Update(u *echotron.Update) {
}
if u.CallbackQuery != nil {
log.Info().
Str("callback_data", u.CallbackQuery.Data).
Int("callback_msg_id", func() int {
if u.CallbackQuery.Message != nil {
return u.CallbackQuery.Message.ID
}
return 0
}()).
Int("last_msg_id", b.LastMessageID).
Msg("Callback received")
b.cleanupCallbackUI(u)
if u.CallbackQuery.Data == "empty" || u.CallbackQuery.Data == "page_info" || u.CallbackQuery.Data == "noop" {
exec.handled = true
return
}
if b.GlobalCallbackRouter != nil && u.CallbackQuery.Data != "" {
if newState := b.GlobalCallbackRouter(u.CallbackQuery.Data); newState != nil {
@@ -272,11 +302,23 @@ func (b *Bot) Update(u *echotron.Update) {
}
if !exec.handled && !exec.transitioned {
log.Info().
Bool("has_callback", u.CallbackQuery != nil).
Bool("has_message", u.Message != nil).
Bool("handled", exec.handled).
Bool("transitioned", exec.transitioned).
Msg("No handler handled update, calling common handler")
log.Info().Msg("Common handler")
b.CurrentState.Handle(b, u)
}
if !exec.handled && !exec.transitioned {
log.Info().
Bool("has_callback", u.CallbackQuery != nil).
Bool("has_message", u.Message != nil).
Bool("handled", exec.handled).
Bool("transitioned", exec.transitioned).
Msg("Default feedback triggered")
log.Info().Msg("Default feedback")
b.SetState(b.DefaultStateFactory(), NewMessage)
}

View File

@@ -85,7 +85,7 @@ https://t.me/+AbCdEfGhIjKlMn</i>`, [][]echotron.InlineKeyboardButton{
// Начинаем процесс добавления медиа
s.WaitingForMedia = true
s.ShowControlPanel(b, `<b> Добавить медиа</b>
s.ShowControlPanel(b, `<b> Добавить медиа</b>
Отправьте <b>фото, видео или GIF</b>

View File

@@ -116,7 +116,7 @@ https://t.me/+AbCdEfGhIjKlMn</i>`, [][]echotron.InlineKeyboardButton{
case data == "add_media":
s.WaitingForMedia = true
s.ShowControlPanel(b, `<b> Добавить медиа</b>
s.ShowControlPanel(b, `<b> Добавить медиа</b>
Отправьте <b>фото, видео или GIF</b>

View File

@@ -283,7 +283,7 @@ func (e *CreativeEditorFields) ShowManagementPanel(b *bot.Bot, confirmButtonText
// Блок редактирования контента
buttons = append(buttons, bot.Row(
bot.Button("✎ Текст", "edit_text"),
bot.Button(" Медиа", "add_media"),
bot.Button(" Медиа", "add_media"),
))
// Добавление кнопки сразу под основным блоком
buttons = append(buttons, bot.Row(bot.Button(" Добавить кнопку", "add_button")))

View File

@@ -38,13 +38,35 @@ func (s *MyProjects) Enter(b *bot.Bot, mode bot.RenderMode) {
}
if len(workspaces) == 0 {
b.SendNew("У вас пока нет workspace'ов", bot.Keyboard())
// Автоматически создаем workspace с именем пользователя
username := "My Workspace"
chatInfo, err := b.GetChat(b.ChatID)
if err == nil && chatInfo.Result != nil {
if chatInfo.Result.Username != "" {
username = chatInfo.Result.Username
} else if chatInfo.Result.FirstName != "" {
username = chatInfo.Result.FirstName
if chatInfo.Result.LastName != "" {
username += " " + chatInfo.Result.LastName
}
}
}
log.Info().Str("username", username).Msg("Creating default workspace")
workspace, err := b.Backend.CreateWorkspace(context.Background(), jwt, username)
if err != nil {
log.Error().Err(err).Msg("Failed to create workspace")
b.SendNew("❌ Не удалось создать workspace", bot.Keyboard())
return
}
s.WorkspaceID = workspace.ID
log.Info().Str("workspace_id", s.WorkspaceID).Str("name", workspace.Name).Msg("Workspace created successfully")
} else {
// Используем первый workspace по умолчанию
s.WorkspaceID = workspaces[0].ID
}
}
s.renderProjects(b, mode, jwt)
}

View File

@@ -570,9 +570,9 @@ func (s *PurchaseOptionalDetails) renderParamChannels(b *bot.Bot, mode bot.Rende
label := fmt.Sprintf("%d", i+1)
rows = append(rows, bot.Row(
bot.Button(label, fmt.Sprintf("param_edit:%s:%d", param, i)),
bot.Button("", fmt.Sprintf("param_copy:%s:%d", param, i)),
bot.Button("", fmt.Sprintf("param_paste:%s:%d", param, i)),
bot.Button("", fmt.Sprintf("param_edit:%s:%d", param, i)),
bot.Button("", fmt.Sprintf("param_copy:%s:%d", param, i)),
bot.Button("", fmt.Sprintf("param_paste:%s:%d", param, i)),
bot.Button("Изменить", fmt.Sprintf("param_edit:%s:%d", param, i)),
bot.Button("⌫", fmt.Sprintf("param_clear:%s:%d", param, i)),
))
}
@@ -630,7 +630,8 @@ func (s *PurchaseOptionalDetails) handleParamCallback(b *bot.Bot, data string) b
case strings.HasPrefix(data, "param_edit:"):
param, index, ok := s.parseParamIndex(data, "param_edit:")
if !ok {
return false
s.Enter(b, bot.EditMessage)
return true
}
if index >= 0 && index < len(s.Channels) {
s.CurrentParam = param
@@ -638,10 +639,13 @@ func (s *PurchaseOptionalDetails) handleParamCallback(b *bot.Bot, data string) b
s.openChannelEditor(b, param, s.CurrentChannel)
return true
}
s.Enter(b, bot.EditMessage)
return true
case strings.HasPrefix(data, "param_copy:"):
param, index, ok := s.parseParamIndex(data, "param_copy:")
if !ok {
return false
s.Enter(b, bot.EditMessage)
return true
}
if index >= 0 && index < len(s.Channels) {
username := s.Channels[index].Username
@@ -649,10 +653,13 @@ func (s *PurchaseOptionalDetails) handleParamCallback(b *bot.Bot, data string) b
s.Enter(b, bot.EditMessage)
return true
}
s.Enter(b, bot.EditMessage)
return true
case strings.HasPrefix(data, "param_paste:"):
param, index, ok := s.parseParamIndex(data, "param_paste:")
if !ok {
return false
s.Enter(b, bot.EditMessage)
return true
}
if index >= 0 && index < len(s.Channels) {
username := s.Channels[index].Username
@@ -660,10 +667,13 @@ func (s *PurchaseOptionalDetails) handleParamCallback(b *bot.Bot, data string) b
s.Enter(b, bot.EditMessage)
return true
}
s.Enter(b, bot.EditMessage)
return true
case strings.HasPrefix(data, "param_clear:"):
param, index, ok := s.parseParamIndex(data, "param_clear:")
if !ok {
return false
s.Enter(b, bot.EditMessage)
return true
}
if index >= 0 && index < len(s.Channels) {
username := s.Channels[index].Username
@@ -671,6 +681,8 @@ func (s *PurchaseOptionalDetails) handleParamCallback(b *bot.Bot, data string) b
s.Enter(b, bot.EditMessage)
return true
}
s.Enter(b, bot.EditMessage)
return true
case strings.HasPrefix(data, "param_apply_all:"):
param := strings.TrimPrefix(data, "param_apply_all:")
s.applyParamToAll(param)