text table, telegram html message
This commit is contained in:
5
go.mod
5
go.mod
@@ -6,3 +6,8 @@ require (
|
||||
github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/clipperhouse/uax29/v2 v2.2.0 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.19 // indirect
|
||||
)
|
||||
|
||||
4
go.sum
4
go.sum
@@ -1,5 +1,9 @@
|
||||
github.com/clipperhouse/uax29/v2 v2.2.0 h1:ChwIKnQN3kcZteTXMgb1wztSgaU+ZemkgWdohwgs8tY=
|
||||
github.com/clipperhouse/uax29/v2 v2.2.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM=
|
||||
github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1 h1:wG8n/XJQ07TmjbITcGiUaOtXxdrINDz1b0J1w0SzqDc=
|
||||
github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1/go.mod h1:A2S0CWkNylc2phvKXWBBdD3K0iGnDBGbzRpISP2zBl8=
|
||||
github.com/mattn/go-runewidth v0.0.19 h1:v++JhqYnZuu5jSKrk9RbgF5v4CGUjqRfBm05byFGLdw=
|
||||
github.com/mattn/go-runewidth v0.0.19/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
|
||||
@@ -60,3 +60,9 @@ func (s *Bot) SendTextMessage(chatID int64, text string) (tgbotapi.Message, erro
|
||||
message.ParseMode = "Markdown"
|
||||
return s.api.Send(message)
|
||||
}
|
||||
|
||||
func (s *Bot) SendHTMLMessage(chatID int64, text string) (tgbotapi.Message, error) {
|
||||
message := tgbotapi.NewMessage(chatID, text)
|
||||
message.ParseMode = "HTML"
|
||||
return s.api.Send(message)
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package notify_test
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
@@ -14,17 +13,19 @@ type ConfigTelegram struct {
|
||||
Token string `yaml:"token"`
|
||||
}
|
||||
|
||||
func TestMessage(t *testing.T) {
|
||||
func loadConfig(t *testing.T) (conf ConfigTelegram) {
|
||||
cData, err := os.ReadFile("test_data/telegram.yaml")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var conf ConfigTelegram
|
||||
if err = yaml.Unmarshal(cData, &conf); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
log.Println(conf)
|
||||
func TestMessage(t *testing.T) {
|
||||
conf := loadConfig(t)
|
||||
bot, err := notify.NewTelegram(conf.Token, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -35,3 +36,30 @@ func TestMessage(t *testing.T) {
|
||||
}
|
||||
t.Log(m)
|
||||
}
|
||||
|
||||
func TestSendTable(t *testing.T) {
|
||||
rows := [][]string{
|
||||
{"Название", "Цена", "Кол-во", "Рост"},
|
||||
{"Яблоки", "120", "5", "3.5"},
|
||||
{"Бананы", "-90", "30", "12 000"},
|
||||
{"Киви", "+200", "1", "-1.2"},
|
||||
{"Груши", "100", "10", "0.5"},
|
||||
{"Апельсины", "150", "10", "1.5"},
|
||||
{"Мандарины", "100", "10", "0.5"},
|
||||
{"Персики", "100", "10", "0.5"},
|
||||
{"Виноград", "100", "10", "0.5"},
|
||||
{"Абрикосы", "100", "10", "0.5"},
|
||||
{"Слива Абрикосы Абрикосы", "100", "10", "0.5"},
|
||||
}
|
||||
|
||||
msg := notify.BuildTelegramHTMLTable(rows)
|
||||
|
||||
conf := loadConfig(t)
|
||||
bot, err := notify.NewTelegram(conf.Token, true)
|
||||
|
||||
m, err := bot.SendHTMLMessage(conf.GroupID, "<pre>"+msg+"</pre>")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log(m)
|
||||
}
|
||||
|
||||
112
tools.go
Normal file
112
tools.go
Normal file
@@ -0,0 +1,112 @@
|
||||
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 <pre>
|
||||
func BuildTelegramHTMLTable(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()
|
||||
}
|
||||
20
tools_test.go
Normal file
20
tools_test.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package notify_test
|
||||
|
||||
import (
|
||||
"log"
|
||||
"testing"
|
||||
|
||||
"git.gm6.ru/icewind/notify"
|
||||
)
|
||||
|
||||
func TestCreateTable(t *testing.T) {
|
||||
rows := [][]string{
|
||||
{"Название", "Цена", "Кол-во", "Рост"},
|
||||
{"Яблоки", "120", "5", "3.5"},
|
||||
{"Бананы", "-90", "30", "12 000"},
|
||||
{"Киви", "+200", "1", "-1.2"},
|
||||
}
|
||||
|
||||
msg := notify.BuildTelegramHTMLTable(rows)
|
||||
log.Println(msg)
|
||||
}
|
||||
Reference in New Issue
Block a user