Improve bot messages
All checks were successful
continuous-integration/drone/push Build is passing

Ref #13

This commit uses markdown formatting to improve the messages sent to
the Telegram bot.
This commit is contained in:
Melora Hugues 2023-02-04 19:07:04 +01:00
parent 51bc7cb3a0
commit d9c57f7441
6 changed files with 54 additions and 15 deletions

View file

@ -4,6 +4,7 @@ import (
"context" "context"
"fmt" "fmt"
"net" "net"
"strings"
"time" "time"
"git.faercol.me/faercol/public-ip-tracker/tracker/config" "git.faercol.me/faercol/public-ip-tracker/tracker/config"
@ -15,6 +16,20 @@ import (
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
) )
func formatInitMsg(currentTime time.Time, publicIP net.IP, hostname string) string {
formattedHostname := fmt.Sprintf("`%s`", hostname)
formattedIP := strings.ReplaceAll(publicIP.String(), ".", "\\.")
return fmt.Sprintf(`\[Host %s\] %s
Public IP tracker initialized\. Current IP is %s`, formattedHostname, currentTime.Format(time.RFC1123), formattedIP)
}
func formatUpdate(currentTime time.Time, publicIP net.IP, hostname string) string {
formattedHostname := fmt.Sprintf("`%s`", hostname)
formattedIP := strings.ReplaceAll(publicIP.String(), ".", "\\.")
return fmt.Sprintf(`\[Host %s\] %s
Public IP has changed, new IP is %s`, formattedHostname, currentTime.Format(time.RFC1123), formattedIP)
}
type Notifier struct { type Notifier struct {
ctx context.Context ctx context.Context
cancel context.CancelFunc cancel context.CancelFunc
@ -29,6 +44,7 @@ type Notifier struct {
changesChan chan net.IP changesChan chan net.IP
exitChan chan struct{} exitChan chan struct{}
logger logrus.Logger logger logrus.Logger
hostname string
} }
func (n *Notifier) SendInitMessage() error { func (n *Notifier) SendInitMessage() error {
@ -43,8 +59,7 @@ func (n *Notifier) SendInitMessage() error {
currentTime := n.timeGetter() currentTime := n.timeGetter()
n.logger.Debug("Sending init message to Telegram") n.logger.Debug("Sending init message to Telegram")
initMsg := fmt.Sprintf("Public IP tracker initialized at %v, public IP is %s", currentTime, publicIP) if err := n.tgBot.SendMessage(n.ctx, n.tgChatID, formatInitMsg(currentTime, publicIP, n.hostname), structs.FormattingMarkdownV2); err != nil {
if err := n.tgBot.SendMessage(n.ctx, n.tgChatID, initMsg); err != nil {
return fmt.Errorf("failed to send initialization message: %w", err) return fmt.Errorf("failed to send initialization message: %w", err)
} }
n.logger.Debug("Message sent") n.logger.Debug("Message sent")
@ -52,8 +67,7 @@ func (n *Notifier) SendInitMessage() error {
} }
func (n *Notifier) sendUpdatedIPMsg() error { func (n *Notifier) sendUpdatedIPMsg() error {
updateMsg := fmt.Sprintf("Public IP has been changed, is now %s", n.currentIP) if err := n.tgBot.SendMessage(n.ctx, n.tgChatID, formatUpdate(n.timeGetter(), n.currentIP, n.hostname), structs.FormattingMarkdownV2); err != nil {
if err := n.tgBot.SendMessage(n.ctx, n.tgChatID, updateMsg); err != nil {
return fmt.Errorf("failed to send update message: %w", err) return fmt.Errorf("failed to send update message: %w", err)
} }
return nil return nil
@ -61,7 +75,7 @@ func (n *Notifier) sendUpdatedIPMsg() error {
func (n *Notifier) sendCurrentIP() error { func (n *Notifier) sendCurrentIP() error {
statusMsg := fmt.Sprintf("Current public IP is %s", n.currentIP) statusMsg := fmt.Sprintf("Current public IP is %s", n.currentIP)
if err := n.tgBot.SendMessage(n.ctx, n.tgChatID, statusMsg); err != nil { if err := n.tgBot.SendMessage(n.ctx, n.tgChatID, statusMsg, structs.FormattingMarkdownV2); err != nil {
return fmt.Errorf("failed to send message: %w", err) return fmt.Errorf("failed to send message: %w", err)
} }
return nil return nil
@ -142,5 +156,6 @@ func New(ctx context.Context, config *config.Config) *Notifier {
frequency: config.PollingFrequency, frequency: config.PollingFrequency,
tgWatcher: notifier.New(config.Telegram.Token), tgWatcher: notifier.New(config.Telegram.Token),
logger: logger.L, logger: logger.L,
hostname: config.Hostname,
} }
} }

View file

@ -17,7 +17,7 @@ const expectedChatID = 42
// Need to mock the telegram bot, because no mock is provided by my own lib, what a shame. // Need to mock the telegram bot, because no mock is provided by my own lib, what a shame.
type mockTGBot struct { type mockTGBot struct {
SendMessageProp error SendMessageProp error
SendMessageFunc func(context.Context, int64, string) error SendMessageFunc func(context.Context, int64, string, structs.FormattingOption) error
} }
// Do nothing here, it's not used by this bot // Do nothing here, it's not used by this bot
@ -25,11 +25,11 @@ func (mb *mockTGBot) GetMe(ctx context.Context) (structs.User, error) {
return structs.User{}, nil return structs.User{}, nil
} }
func (mb *mockTGBot) SendMessage(ctx context.Context, chatID int64, content string) error { func (mb *mockTGBot) SendMessage(ctx context.Context, chatID int64, content string, option structs.FormattingOption) error {
if mb.SendMessageFunc == nil { if mb.SendMessageFunc == nil {
return mb.SendMessageProp return mb.SendMessageProp
} }
return mb.SendMessageFunc(ctx, chatID, content) return mb.SendMessageFunc(ctx, chatID, content, option)
} }
// Need to mock the notifier here too, but will do later on, I need to improve my lib in the future // Need to mock the notifier here too, but will do later on, I need to improve my lib in the future
@ -49,12 +49,17 @@ func TestInit(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
called := false called := false
expectedMsg := "\\[Host `test`\\] Sat, 28 Jan 2023 14:17:12 UTC\nPublic IP tracker initialized\\. Current IP is 198\\.51\\.100\\.42"
tgBot := mockTGBot{ tgBot := mockTGBot{
SendMessageFunc: func(ctx context.Context, chatID int64, content string) error { SendMessageFunc: func(ctx context.Context, chatID int64, content string, option structs.FormattingOption) error {
if option != structs.FormattingMarkdownV2 {
t.Errorf("Unexpected formatting option %v", option)
}
if chatID != expectedChatID { if chatID != expectedChatID {
t.Errorf("Unexpected chatID %d", chatID) t.Errorf("Unexpected chatID %d", chatID)
} }
if content != "Public IP tracker initialized at 2023-01-28 14:17:12 +0000 UTC, public IP is 198.51.100.42" { if content != expectedMsg {
t.Errorf("Unexpected message %s", content) t.Errorf("Unexpected message %s", content)
} }
called = true called = true
@ -75,6 +80,7 @@ func TestInit(t *testing.T) {
ipGetter: &ipGetter, ipGetter: &ipGetter,
frequency: 1 * time.Minute, frequency: 1 * time.Minute,
tgWatcher: &mockTGNotifier{}, tgWatcher: &mockTGNotifier{},
hostname: "test",
} }
if err := bot.SendInitMessage(); err != nil { if err := bot.SendInitMessage(); err != nil {
@ -91,12 +97,17 @@ func TestUpdateIP(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
called := false called := false
expectedMsg := "\\[Host `test`\\] Sat, 28 Jan 2023 14:17:12 UTC\nPublic IP has changed, new IP is 198\\.51\\.100\\.42"
tgBot := mockTGBot{ tgBot := mockTGBot{
SendMessageFunc: func(ctx context.Context, chatID int64, content string) error { SendMessageFunc: func(ctx context.Context, chatID int64, content string, option structs.FormattingOption) error {
if option != structs.FormattingMarkdownV2 {
t.Errorf("Unexpected formatting option %v", option)
}
if chatID != expectedChatID { if chatID != expectedChatID {
t.Errorf("Unexpected chatID %d", chatID) t.Errorf("Unexpected chatID %d", chatID)
} }
if content != "Public IP has been changed, is now 198.51.100.42" { if content != expectedMsg {
t.Errorf("Unexpected message %s", content) t.Errorf("Unexpected message %s", content)
} }
called = true called = true
@ -120,6 +131,7 @@ func TestUpdateIP(t *testing.T) {
ipGetter: &ipGetter, ipGetter: &ipGetter,
tgWatcher: &mockTGNotifier{}, tgWatcher: &mockTGNotifier{},
frequency: 500 * time.Millisecond, frequency: 500 * time.Millisecond,
hostname: "test",
} }
go bot.Run() go bot.Run()

View file

@ -25,12 +25,14 @@ type jsonLogConfig struct {
type Config struct { type Config struct {
Telegram *TelegramConfig Telegram *TelegramConfig
PollingFrequency time.Duration PollingFrequency time.Duration
Hostname string
Log *LogConfig Log *LogConfig
} }
type jsonConfig struct { type jsonConfig struct {
Telegram *TelegramConfig `json:"telegram"` Telegram *TelegramConfig `json:"telegram"`
PollingFrequency int64 `json:"polling_frequency"` PollingFrequency int64 `json:"polling_frequency"`
Hostname string `json:"hostname"`
Log *jsonLogConfig `json:"log"` Log *jsonLogConfig `json:"log"`
} }
@ -55,6 +57,7 @@ func New(filepath string) (*Config, error) {
return &Config{ return &Config{
Telegram: jsonConf.Telegram, Telegram: jsonConf.Telegram,
PollingFrequency: time.Duration(jsonConf.PollingFrequency) * time.Second, PollingFrequency: time.Duration(jsonConf.PollingFrequency) * time.Second,
Hostname: jsonConf.Hostname,
Log: &LogConfig{ Log: &LogConfig{
Level: parseLevel(jsonConf.Log.Level), Level: parseLevel(jsonConf.Log.Level),
}, },

View file

@ -3,7 +3,7 @@ module git.faercol.me/faercol/public-ip-tracker/tracker
go 1.16 go 1.16
require ( require (
github.com/ahugues/go-telegram-api v0.1.0 github.com/ahugues/go-telegram-api v0.2.0
github.com/google/uuid v1.3.0 github.com/google/uuid v1.3.0
github.com/sirupsen/logrus v1.9.0 github.com/sirupsen/logrus v1.9.0
) )

View file

@ -1,5 +1,5 @@
github.com/ahugues/go-telegram-api v0.1.0 h1:CGJG0WR282O0hAO9JH2RutPj+Vn+Q+zbjdSHjuvN5yY= github.com/ahugues/go-telegram-api v0.2.0 h1:wAY4qr0T0I2mgW1HxAZFdzhOdXuZ5WE4e8GNR4t7Yrs=
github.com/ahugues/go-telegram-api v0.1.0/go.mod h1:8I/JWxd9GYM7dHOgGmkRI3Ei1u+nGvzeR2knIMmFw7E= github.com/ahugues/go-telegram-api v0.2.0/go.mod h1:8I/JWxd9GYM7dHOgGmkRI3Ei1u+nGvzeR2knIMmFw7E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=

View file

@ -38,6 +38,15 @@ func main() {
logger.Init(conf.Log.Level) logger.Init(conf.Log.Level)
logger.L.Infof("Intialized logger with level %v", conf.Log.Level) logger.L.Infof("Intialized logger with level %v", conf.Log.Level)
if conf.Hostname == "" {
logger.L.Warn("Unspecified hostname, trying to get current hostname, this might not be reliable")
conf.Hostname, err = os.Hostname()
if err != nil {
logger.L.Errorf("Failed to get hostname, using a default value: %s", err.Error())
conf.Hostname = "default"
}
}
logger.L.Debug("Initializing notification bot") logger.L.Debug("Initializing notification bot")
notifBot := bot.New(mainCtx, conf) notifBot := bot.New(mainCtx, conf)