package notify import ( "html" "regexp" "strings" "github.com/mattn/go-runewidth" ) // Проверка, что строка — число (целое или с точкой, +,-, пробелы) var numberRe = regexp.MustCompile(`^[+-]?[\d\s_]*\.?\d+$`) // Выравнивание текста по центру с учётом визуальной ширины func centerPad(s string, width int) string { w := runewidth.StringWidth(s) if w >= width { return s } left := (width - w) / 2 right := width - w - left return strings.Repeat(" ", left) + s + strings.Repeat(" ", right) } // Выравнивание текста по левому краю с учётом ширины func padRightVisual(s string, width int) string { w := runewidth.StringWidth(s) if w >= width { return s } return s + strings.Repeat(" ", width-w) } // Выравнивание текста по правому краю с учётом ширины func padLeftVisual(s string, width int) string { w := runewidth.StringWidth(s) if w >= width { return s } return strings.Repeat(" ", width-w) + s } // BuildTelegramBoxTable строит ASCII-таблицу с рамками для Telegram HTML
func BuildTelegramTable(rows [][]string) string {
if len(rows) == 0 {
return ""
}
// Экранируем HTML-символы
for i := range rows {
for j := range rows[i] {
rows[i][j] = html.EscapeString(rows[i][j])
}
}
// Определяем визуальную ширину колонок
colWidths := make([]int, len(rows[0]))
for _, row := range rows {
for i, col := range row {
w := runewidth.StringWidth(col)
if w > colWidths[i] {
colWidths[i] = w
}
}
}
// Функция для линий-разделителей
buildSep := func() string {
var s strings.Builder
s.WriteString("+")
for _, w := range colWidths {
s.WriteString(strings.Repeat("-", w+2))
s.WriteString("+")
}
return s.String()
}
var b strings.Builder
b.WriteString(buildSep() + "\n")
for ri, row := range rows {
b.WriteString("|")
for i, col := range row {
w := colWidths[i]
var cell string
switch {
case ri == 0:
// Заголовок — по центру
cell = centerPad(col, w)
case numberRe.MatchString(col):
// Число — по правому краю
cell = padLeftVisual(col, w)
default:
// Текст — по левому краю
cell = padRightVisual(col, w)
}
b.WriteString(" " + cell + " |")
}
b.WriteString("\n")
// Разделитель после заголовка
if ri == 0 {
b.WriteString(buildSep() + "\n")
}
}
b.WriteString(buildSep() + "\n")
return b.String()
}