Melora Hugues
bd14b3c731
All checks were successful
continuous-integration/drone/push Build is passing
This commit allows using an external unix exporter to send the messages instead of directly sending the messages to Telegram
195 lines
5.5 KiB
Go
195 lines
5.5 KiB
Go
package bot
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net"
|
|
"strings"
|
|
"time"
|
|
|
|
"git.faercol.me/faercol/public-ip-tracker/tracker/config"
|
|
"git.faercol.me/faercol/public-ip-tracker/tracker/ip"
|
|
"git.faercol.me/faercol/public-ip-tracker/tracker/logger"
|
|
"git.faercol.me/faercol/public-ip-tracker/tracker/messager"
|
|
"git.faercol.me/faercol/public-ip-tracker/tracker/unixsender"
|
|
"github.com/ahugues/go-telegram-api/bot"
|
|
"github.com/ahugues/go-telegram-api/notifier"
|
|
"github.com/ahugues/go-telegram-api/structs"
|
|
"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
|
|
tgBot bot.Bot
|
|
tgChatID int64
|
|
tgWatcher notifier.EventNotifier
|
|
ipGetter ip.IPGetter
|
|
timeGetter func() time.Time
|
|
currentIP net.IP
|
|
frequency time.Duration
|
|
errChan chan error
|
|
exitChan chan struct{}
|
|
logger logrus.Logger
|
|
hostname string
|
|
sendMode config.ExportMode
|
|
sender messager.Sender
|
|
}
|
|
|
|
func (n *Notifier) sendMessage(msg string) error {
|
|
switch n.sendMode {
|
|
case config.ExportNative:
|
|
return n.tgBot.SendMessage(n.ctx, n.tgChatID, msg, structs.FormattingMarkdownV2)
|
|
case config.ExportUnix:
|
|
return n.sender.SendMessage(msg)
|
|
default:
|
|
return fmt.Errorf("invalid sending mode %v", n.sendMode)
|
|
}
|
|
}
|
|
|
|
func (n *Notifier) SendInitMessage() error {
|
|
n.logger.Debug("Getting current public IP")
|
|
publicIP, err := n.ipGetter.GetCurrentPublicIP(n.ctx)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get current public IP: %w", err)
|
|
}
|
|
n.logger.Debugf("Current public IP is %s", publicIP.String())
|
|
|
|
n.currentIP = publicIP
|
|
currentTime := n.timeGetter()
|
|
|
|
n.logger.Debug("Sending init message to Telegram")
|
|
initMsg := formatInitMsg(currentTime, publicIP, n.hostname)
|
|
if err := n.sendMessage(initMsg); err != nil {
|
|
return fmt.Errorf("failed to send initialization message: %w", err)
|
|
}
|
|
n.logger.Debug("Message sent")
|
|
return nil
|
|
}
|
|
|
|
func (n *Notifier) sendUpdatedIPMsg() error {
|
|
updateMsg := formatUpdate(n.timeGetter(), n.currentIP, n.hostname)
|
|
if err := n.sendMessage(updateMsg); err != nil {
|
|
return fmt.Errorf("failed to send update message: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (n *Notifier) sendCurrentIP() error {
|
|
statusMsg := fmt.Sprintf("Current public IP is %s", n.currentIP)
|
|
if err := n.sendMessage(statusMsg); err != nil {
|
|
return fmt.Errorf("failed to send message: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (n *Notifier) watchTG() {
|
|
n.logger.Debug("Subscribing to messages notificator")
|
|
id, updateChan := n.tgWatcher.Subscribe([]structs.UpdateType{structs.UpdateMessage})
|
|
go n.tgWatcher.Run(n.ctx)
|
|
|
|
for {
|
|
select {
|
|
case update := <-updateChan:
|
|
if update.Message.Text == "/getIP" {
|
|
if err := n.sendCurrentIP(); err != nil {
|
|
n.errChan <- fmt.Errorf("failed to reply current public IP: %w", err)
|
|
}
|
|
}
|
|
case <-n.ctx.Done():
|
|
n.tgWatcher.Unsubscribe(id)
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
func (n *Notifier) Run() {
|
|
go n.watchTG()
|
|
n.logger.Infof("Start watching for public IP changes, polling frequency is %v", n.frequency)
|
|
for {
|
|
select {
|
|
case <-time.After(n.frequency):
|
|
n.logger.Debug("Checking if current IP has changed")
|
|
newIP, err := n.ipGetter.GetCurrentPublicIP(n.ctx)
|
|
if err != nil {
|
|
n.errChan <- fmt.Errorf("failed to update public IP: %w", err)
|
|
continue
|
|
}
|
|
n.logger.Debugf("Got new public IP %s", newIP.String())
|
|
if !newIP.Equal(n.currentIP) {
|
|
n.logger.Debug("New public IP is different from previous IP")
|
|
n.logger.Warnf("Public IP has changed from %s to %s", n.currentIP.String(), newIP.String())
|
|
n.currentIP = newIP
|
|
if err := n.sendUpdatedIPMsg(); err != nil {
|
|
n.errChan <- err
|
|
}
|
|
} else {
|
|
n.logger.Debug("Public IP has not changed")
|
|
}
|
|
case <-n.ctx.Done():
|
|
n.logger.Info("Stopping notification daemon")
|
|
n.exitChan <- struct{}{}
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
func (n *Notifier) ErrChan() <-chan error {
|
|
return n.errChan
|
|
}
|
|
|
|
func (n *Notifier) Exit() <-chan struct{} {
|
|
return n.exitChan
|
|
}
|
|
|
|
func buildSender(ctx context.Context, conf *config.Config) (messager.Sender, error) {
|
|
switch conf.Export.Mode {
|
|
case config.ExportUnix:
|
|
logger.L.Infof("Building notifier in Unix mode")
|
|
return unixsender.New(ctx, conf)
|
|
default:
|
|
return nil, nil
|
|
}
|
|
}
|
|
|
|
func New(ctx context.Context, config *config.Config) (*Notifier, error) {
|
|
subCtx, cancel := context.WithCancel(ctx)
|
|
tgBot := bot.New(config.Telegram.Token)
|
|
|
|
sender, err := buildSender(subCtx, config)
|
|
if err != nil {
|
|
cancel()
|
|
return nil, fmt.Errorf("failed to build message sender: %w", err)
|
|
}
|
|
|
|
return &Notifier{
|
|
ctx: subCtx,
|
|
cancel: cancel,
|
|
tgBot: tgBot,
|
|
tgChatID: config.Telegram.ChannelID,
|
|
timeGetter: time.Now,
|
|
ipGetter: ip.New(),
|
|
errChan: make(chan error, 10),
|
|
exitChan: make(chan struct{}, 1),
|
|
frequency: config.PollingFrequency,
|
|
tgWatcher: notifier.New(config.Telegram.Token),
|
|
logger: logger.L,
|
|
hostname: config.Hostname,
|
|
sendMode: config.Export.Mode,
|
|
sender: sender,
|
|
}, nil
|
|
}
|