bot changes

This commit is contained in:
Artem Tsyrulnikov
2026-01-03 11:24:24 +03:00
parent 0577d2a00e
commit 922067d194
13 changed files with 312 additions and 182 deletions

View File

@@ -2,6 +2,7 @@ package screens
import (
"fmt"
"sort"
"strings"
"example.com/m/bot"
@@ -26,6 +27,9 @@ type CreativeEditorFields struct {
MediaType string // "photo", "video", "animation"
MediaFileID string // File ID из Telegram
// Навигация (хлебные крошки)
Navigation string
// Состояние для добавления кнопки
WaitingForButtonText bool
WaitingForButtonURL bool
@@ -43,6 +47,13 @@ type CreativeEditorFields struct {
// GetCreativeText возвращает текст для превью креатива
func (e *CreativeEditorFields) GetCreativeText() string {
var result string
// Добавляем навигацию если она есть
if e.Navigation != "" {
result = fmt.Sprintf("<i>%s</i>\n\n", e.Navigation)
}
if e.Text != nil && *e.Text != "" {
// Проверяем что текст не является "визуально пустым" HTML
// Например: "<a class=\"tg-link\"></a>" - пустая ссылка
@@ -51,7 +62,7 @@ func (e *CreativeEditorFields) GetCreativeText() string {
// Убираем пустые HTML-теги (простая проверка)
if text == "<a class=\"tg-link\"></a>" || text == "" {
log.Info().Str("text", *e.Text).Msg("GetCreativeText: text is visually empty, using default")
return `<i>💭 Я твой креатив
return result + `<i>💭 Я твой креатив
У меня пока нет текста, добавь его ниже.
@@ -59,10 +70,10 @@ func (e *CreativeEditorFields) GetCreativeText() string {
}
log.Info().Str("text", *e.Text).Msg("GetCreativeText: returning creative text")
return *e.Text
return result + *e.Text
}
log.Info().Bool("text_is_nil", e.Text == nil).Msg("GetCreativeText: returning default text")
return `<i>💭 Я твой креатив
return result + `<i>💭 Я твой креатив
У меня пока нет текста, добавь его ниже.
@@ -361,54 +372,89 @@ func (e *CreativeEditorFields) GetFormattedText(message *echotron.Message) strin
return message.Text
}
type tagSpan struct {
open string
close string
start int
end int
len int
}
// Преобразуем текст в руны для правильной работы с UTF-8
runes := []rune(message.Text)
result := ""
lastPos := 0
// Обрабатываем entities
var spans []tagSpan
for _, entity := range message.Entities {
// Добавляем текст до entity
if entity.Offset > lastPos {
result += string(runes[lastPos:entity.Offset])
// URL уже присутствует в сыром тексте, чтобы не дублировать - пропускаем entity.
if entity.Type == "url" {
continue
}
// Получаем текст entity
entityText := string(runes[entity.Offset : entity.Offset+entity.Length])
entityText := ""
if entity.Offset+entity.Length <= len(runes) {
entityText = string(runes[entity.Offset : entity.Offset+entity.Length])
}
// Оборачиваем в соответствующие HTML-теги
var openTag, closeTag string
switch entity.Type {
case "bold":
result += "<b>" + entityText + "</b>"
openTag, closeTag = "<b>", "</b>"
case "italic":
result += "<i>" + entityText + "</i>"
openTag, closeTag = "<i>", "</i>"
case "underline":
result += "<u>" + entityText + "</u>"
openTag, closeTag = "<u>", "</u>"
case "strikethrough":
result += "<s>" + entityText + "</s>"
openTag, closeTag = "<s>", "</s>"
case "code":
result += "<code>" + entityText + "</code>"
openTag, closeTag = "<code>", "</code>"
case "pre":
result += "<pre>" + entityText + "</pre>"
openTag, closeTag = "<pre>", "</pre>"
case "text_link":
if entity.URL != "" {
result += fmt.Sprintf("<a href=\"%s\">%s</a>", entity.URL, entityText)
} else {
result += entityText
openTag = fmt.Sprintf("<a href=\"%s\">", entity.URL)
closeTag = "</a>"
}
case "url":
result += entityText
default:
result += entityText
_ = entityText
}
lastPos = entity.Offset + entity.Length
if openTag != "" && closeTag != "" {
spans = append(spans, tagSpan{
open: openTag,
close: closeTag,
start: entity.Offset,
end: entity.Offset + entity.Length,
len: entity.Length,
})
}
}
// Добавляем оставшийся текст
if lastPos < len(runes) {
result += string(runes[lastPos:])
opens := make(map[int][]tagSpan)
closes := make(map[int][]tagSpan)
for _, span := range spans {
opens[span.start] = append(opens[span.start], span)
closes[span.end] = append(closes[span.end], span)
}
return result
// Пишем текст один раз, вставляя HTML-теги по позициям
var result strings.Builder
for i := 0; i <= len(runes); i++ {
if closing, ok := closes[i]; ok {
sort.Slice(closing, func(a, b int) bool { return closing[a].len < closing[b].len })
for _, span := range closing {
result.WriteString(span.close)
}
}
if opening, ok := opens[i]; ok {
sort.Slice(opening, func(a, b int) bool { return opening[a].len > opening[b].len })
for _, span := range opening {
result.WriteString(span.open)
}
}
if i < len(runes) {
result.WriteRune(runes[i])
}
}
return result.String()
}