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
|
github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1
|
||||||
gopkg.in/yaml.v2 v2.4.0
|
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 h1:wG8n/XJQ07TmjbITcGiUaOtXxdrINDz1b0J1w0SzqDc=
|
||||||
github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1/go.mod h1:A2S0CWkNylc2phvKXWBBdD3K0iGnDBGbzRpISP2zBl8=
|
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 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
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"
|
message.ParseMode = "Markdown"
|
||||||
return s.api.Send(message)
|
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
|
package notify_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
@@ -14,17 +13,19 @@ type ConfigTelegram struct {
|
|||||||
Token string `yaml:"token"`
|
Token string `yaml:"token"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMessage(t *testing.T) {
|
func loadConfig(t *testing.T) (conf ConfigTelegram) {
|
||||||
cData, err := os.ReadFile("test_data/telegram.yaml")
|
cData, err := os.ReadFile("test_data/telegram.yaml")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
var conf ConfigTelegram
|
|
||||||
if err = yaml.Unmarshal(cData, &conf); err != nil {
|
if err = yaml.Unmarshal(cData, &conf); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
log.Println(conf)
|
func TestMessage(t *testing.T) {
|
||||||
|
conf := loadConfig(t)
|
||||||
bot, err := notify.NewTelegram(conf.Token, true)
|
bot, err := notify.NewTelegram(conf.Token, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@@ -35,3 +36,30 @@ func TestMessage(t *testing.T) {
|
|||||||
}
|
}
|
||||||
t.Log(m)
|
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