diff --git a/tracker/bot/bot.go b/tracker/bot/bot.go index 4cb3d9c..0a4827c 100644 --- a/tracker/bot/bot.go +++ b/tracker/bot/bot.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "net" + "strings" "time" "git.faercol.me/faercol/public-ip-tracker/tracker/config" @@ -15,6 +16,20 @@ import ( "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 { ctx context.Context cancel context.CancelFunc @@ -29,6 +44,7 @@ type Notifier struct { changesChan chan net.IP exitChan chan struct{} logger logrus.Logger + hostname string } func (n *Notifier) SendInitMessage() error { @@ -43,8 +59,7 @@ func (n *Notifier) SendInitMessage() error { currentTime := n.timeGetter() 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, initMsg); err != nil { + if err := n.tgBot.SendMessage(n.ctx, n.tgChatID, formatInitMsg(currentTime, publicIP, n.hostname), structs.FormattingMarkdownV2); err != nil { return fmt.Errorf("failed to send initialization message: %w", err) } n.logger.Debug("Message sent") @@ -52,8 +67,7 @@ func (n *Notifier) SendInitMessage() 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, updateMsg); err != nil { + if err := n.tgBot.SendMessage(n.ctx, n.tgChatID, formatUpdate(n.timeGetter(), n.currentIP, n.hostname), structs.FormattingMarkdownV2); err != nil { return fmt.Errorf("failed to send update message: %w", err) } return nil @@ -61,7 +75,7 @@ func (n *Notifier) sendUpdatedIPMsg() error { func (n *Notifier) sendCurrentIP() error { 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 nil @@ -142,5 +156,6 @@ func New(ctx context.Context, config *config.Config) *Notifier { frequency: config.PollingFrequency, tgWatcher: notifier.New(config.Telegram.Token), logger: logger.L, + hostname: config.Hostname, } } diff --git a/tracker/bot/bot_test.go b/tracker/bot/bot_test.go index a21b1e7..d1fe850 100644 --- a/tracker/bot/bot_test.go +++ b/tracker/bot/bot_test.go @@ -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. type mockTGBot struct { 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 @@ -25,11 +25,11 @@ func (mb *mockTGBot) GetMe(ctx context.Context) (structs.User, error) { 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 { 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 @@ -49,12 +49,17 @@ func TestInit(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) 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{ - 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 { 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) } called = true @@ -75,6 +80,7 @@ func TestInit(t *testing.T) { ipGetter: &ipGetter, frequency: 1 * time.Minute, tgWatcher: &mockTGNotifier{}, + hostname: "test", } if err := bot.SendInitMessage(); err != nil { @@ -91,12 +97,17 @@ func TestUpdateIP(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) 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{ - 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 { 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) } called = true @@ -120,6 +131,7 @@ func TestUpdateIP(t *testing.T) { ipGetter: &ipGetter, tgWatcher: &mockTGNotifier{}, frequency: 500 * time.Millisecond, + hostname: "test", } go bot.Run() diff --git a/tracker/config/config.go b/tracker/config/config.go index 827c003..fbb1bc5 100644 --- a/tracker/config/config.go +++ b/tracker/config/config.go @@ -25,12 +25,14 @@ type jsonLogConfig struct { type Config struct { Telegram *TelegramConfig PollingFrequency time.Duration + Hostname string Log *LogConfig } type jsonConfig struct { Telegram *TelegramConfig `json:"telegram"` PollingFrequency int64 `json:"polling_frequency"` + Hostname string `json:"hostname"` Log *jsonLogConfig `json:"log"` } @@ -55,6 +57,7 @@ func New(filepath string) (*Config, error) { return &Config{ Telegram: jsonConf.Telegram, PollingFrequency: time.Duration(jsonConf.PollingFrequency) * time.Second, + Hostname: jsonConf.Hostname, Log: &LogConfig{ Level: parseLevel(jsonConf.Log.Level), }, diff --git a/tracker/go.mod b/tracker/go.mod index 5f6a200..3ca322d 100644 --- a/tracker/go.mod +++ b/tracker/go.mod @@ -3,7 +3,7 @@ module git.faercol.me/faercol/public-ip-tracker/tracker go 1.16 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/sirupsen/logrus v1.9.0 ) diff --git a/tracker/go.sum b/tracker/go.sum index 486d07f..747bbc8 100644 --- a/tracker/go.sum +++ b/tracker/go.sum @@ -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.1.0/go.mod h1:8I/JWxd9GYM7dHOgGmkRI3Ei1u+nGvzeR2knIMmFw7E= +github.com/ahugues/go-telegram-api v0.2.0 h1:wAY4qr0T0I2mgW1HxAZFdzhOdXuZ5WE4e8GNR4t7Yrs= +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.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= diff --git a/tracker/main.go b/tracker/main.go index 2b88227..59f5643 100644 --- a/tracker/main.go +++ b/tracker/main.go @@ -38,6 +38,15 @@ func main() { logger.Init(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") notifBot := bot.New(mainCtx, conf)