Compare commits
No commits in common. "main" and "13-improve-messages" have entirely different histories.
main
...
13-improve
9 changed files with 28 additions and 212 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -16,7 +16,7 @@
|
||||||
*.out
|
*.out
|
||||||
|
|
||||||
# build directory
|
# build directory
|
||||||
**/build
|
tracker/build
|
||||||
|
|
||||||
# Dependency directories (remove the comment below to include it)
|
# Dependency directories (remove the comment below to include it)
|
||||||
# vendor/
|
# vendor/
|
||||||
|
|
|
@ -10,7 +10,7 @@ FROM --platform=$TARGETPLATFORM alpine:latest
|
||||||
WORKDIR /root
|
WORKDIR /root
|
||||||
COPY --from=builder go/src/git.faercol.me/public-ip-tracker/build/tracker ./
|
COPY --from=builder go/src/git.faercol.me/public-ip-tracker/build/tracker ./
|
||||||
|
|
||||||
VOLUME [ "/config", "/output" ]
|
VOLUME [ "/config" ]
|
||||||
|
|
||||||
ENTRYPOINT [ "./tracker" ]
|
ENTRYPOINT [ "./tracker" ]
|
||||||
CMD [ "-config", "/config/config.json" ]
|
CMD [ "-config", "/config/config.json" ]
|
||||||
|
|
|
@ -44,9 +44,6 @@ For now, the program is configured through a JSON configuration file. Here is a
|
||||||
"polling_frequency": 5,
|
"polling_frequency": 5,
|
||||||
"log": {
|
"log": {
|
||||||
"level": "info"
|
"level": "info"
|
||||||
},
|
|
||||||
"export": {
|
|
||||||
"mode": "native
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
|
@ -1,17 +0,0 @@
|
||||||
services:
|
|
||||||
telegram_exporter:
|
|
||||||
container_name: telegram_exporter
|
|
||||||
image: git.faercol.me/notification/telegram-notifier:latest
|
|
||||||
environment:
|
|
||||||
- SOCK_PATH=/input/telegram.sock
|
|
||||||
volumes:
|
|
||||||
- './build/config/exporter:/config'
|
|
||||||
- './build/run:/input'
|
|
||||||
|
|
||||||
ip_tracker:
|
|
||||||
container_name: ip_tracker
|
|
||||||
image: git.faercol.me/faercol/public-ip-tracker:latest
|
|
||||||
# Volumes store your data between container upgrades
|
|
||||||
volumes:
|
|
||||||
- './build/config/tracker:/config'
|
|
||||||
- './build/run:/output'
|
|
|
@ -10,8 +10,6 @@ import (
|
||||||
"git.faercol.me/faercol/public-ip-tracker/tracker/config"
|
"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/ip"
|
||||||
"git.faercol.me/faercol/public-ip-tracker/tracker/logger"
|
"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/bot"
|
||||||
"github.com/ahugues/go-telegram-api/notifier"
|
"github.com/ahugues/go-telegram-api/notifier"
|
||||||
"github.com/ahugues/go-telegram-api/structs"
|
"github.com/ahugues/go-telegram-api/structs"
|
||||||
|
@ -33,32 +31,20 @@ Public IP has changed, new IP is %s`, formattedHostname, currentTime.Format(time
|
||||||
}
|
}
|
||||||
|
|
||||||
type Notifier struct {
|
type Notifier struct {
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
cancel context.CancelFunc
|
cancel context.CancelFunc
|
||||||
tgBot bot.Bot
|
tgBot bot.Bot
|
||||||
tgChatID int64
|
tgChatID int64
|
||||||
tgWatcher notifier.EventNotifier
|
tgWatcher notifier.EventNotifier
|
||||||
ipGetter ip.IPGetter
|
ipGetter ip.IPGetter
|
||||||
timeGetter func() time.Time
|
timeGetter func() time.Time
|
||||||
currentIP net.IP
|
currentIP net.IP
|
||||||
frequency time.Duration
|
frequency time.Duration
|
||||||
errChan chan error
|
errChan chan error
|
||||||
exitChan chan struct{}
|
changesChan chan net.IP
|
||||||
logger logrus.Logger
|
exitChan chan struct{}
|
||||||
hostname string
|
logger logrus.Logger
|
||||||
sendMode config.ExportMode
|
hostname string
|
||||||
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 {
|
func (n *Notifier) SendInitMessage() error {
|
||||||
|
@ -73,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 := formatInitMsg(currentTime, publicIP, n.hostname)
|
if err := n.tgBot.SendMessage(n.ctx, n.tgChatID, formatInitMsg(currentTime, publicIP, n.hostname), structs.FormattingMarkdownV2); err != nil {
|
||||||
if err := n.sendMessage(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")
|
||||||
|
@ -82,8 +67,7 @@ func (n *Notifier) SendInitMessage() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *Notifier) sendUpdatedIPMsg() error {
|
func (n *Notifier) sendUpdatedIPMsg() error {
|
||||||
updateMsg := formatUpdate(n.timeGetter(), n.currentIP, n.hostname)
|
if err := n.tgBot.SendMessage(n.ctx, n.tgChatID, formatUpdate(n.timeGetter(), n.currentIP, n.hostname), structs.FormattingMarkdownV2); err != nil {
|
||||||
if err := n.sendMessage(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
|
||||||
|
@ -91,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.sendMessage(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
|
||||||
|
@ -156,26 +140,10 @@ func (n *Notifier) Exit() <-chan struct{} {
|
||||||
return n.exitChan
|
return n.exitChan
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildSender(ctx context.Context, conf *config.Config) (messager.Sender, error) {
|
func New(ctx context.Context, config *config.Config) *Notifier {
|
||||||
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)
|
subCtx, cancel := context.WithCancel(ctx)
|
||||||
tgBot := bot.New(config.Telegram.Token)
|
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{
|
return &Notifier{
|
||||||
ctx: subCtx,
|
ctx: subCtx,
|
||||||
cancel: cancel,
|
cancel: cancel,
|
||||||
|
@ -189,7 +157,5 @@ func New(ctx context.Context, config *config.Config) (*Notifier, error) {
|
||||||
tgWatcher: notifier.New(config.Telegram.Token),
|
tgWatcher: notifier.New(config.Telegram.Token),
|
||||||
logger: logger.L,
|
logger: logger.L,
|
||||||
hostname: config.Hostname,
|
hostname: config.Hostname,
|
||||||
sendMode: config.Export.Mode,
|
}
|
||||||
sender: sender,
|
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,24 +9,6 @@ import (
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ExportMode int64
|
|
||||||
|
|
||||||
const (
|
|
||||||
ExportNative = iota
|
|
||||||
ExportUnix
|
|
||||||
)
|
|
||||||
|
|
||||||
func exportModeFromStr(val string) ExportMode {
|
|
||||||
switch val {
|
|
||||||
case "native":
|
|
||||||
return ExportNative
|
|
||||||
case "unix":
|
|
||||||
return ExportUnix
|
|
||||||
default:
|
|
||||||
return ExportNative
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type TelegramConfig struct {
|
type TelegramConfig struct {
|
||||||
ChannelID int64 `json:"channel_id"`
|
ChannelID int64 `json:"channel_id"`
|
||||||
Token string `json:"token"`
|
Token string `json:"token"`
|
||||||
|
@ -40,30 +22,18 @@ type jsonLogConfig struct {
|
||||||
Level string `json:"level"`
|
Level string `json:"level"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ExportConfig struct {
|
|
||||||
Mode ExportMode
|
|
||||||
UnixSock string
|
|
||||||
}
|
|
||||||
|
|
||||||
type jsonExportConfig struct {
|
|
||||||
Mode string `json:"mode"`
|
|
||||||
UnixSock string `json:"sock_path"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Telegram *TelegramConfig
|
Telegram *TelegramConfig
|
||||||
PollingFrequency time.Duration
|
PollingFrequency time.Duration
|
||||||
Hostname string
|
Hostname string
|
||||||
Log LogConfig
|
Log *LogConfig
|
||||||
Export ExportConfig
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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"`
|
Hostname string `json:"hostname"`
|
||||||
Log jsonLogConfig `json:"log"`
|
Log *jsonLogConfig `json:"log"`
|
||||||
Export jsonExportConfig `json:"export"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseLevel(lvlStr string) logrus.Level {
|
func parseLevel(lvlStr string) logrus.Level {
|
||||||
|
@ -84,17 +54,12 @@ func New(filepath string) (*Config, error) {
|
||||||
if err := json.Unmarshal(content, &jsonConf); err != nil {
|
if err := json.Unmarshal(content, &jsonConf); err != nil {
|
||||||
return nil, fmt.Errorf("failed to parse config file: %w", err)
|
return nil, fmt.Errorf("failed to parse config file: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
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,
|
Hostname: jsonConf.Hostname,
|
||||||
Log: LogConfig{
|
Log: &LogConfig{
|
||||||
Level: parseLevel(jsonConf.Log.Level),
|
Level: parseLevel(jsonConf.Log.Level),
|
||||||
},
|
},
|
||||||
Export: ExportConfig{
|
|
||||||
UnixSock: jsonConf.Export.UnixSock,
|
|
||||||
Mode: exportModeFromStr(jsonConf.Export.Mode),
|
|
||||||
},
|
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,10 +48,7 @@ func main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.L.Debug("Initializing notification bot")
|
logger.L.Debug("Initializing notification bot")
|
||||||
notifBot, err := bot.New(mainCtx, conf)
|
notifBot := bot.New(mainCtx, conf)
|
||||||
if err != nil {
|
|
||||||
logger.L.Fatalf("Failed to create notification bot: %s", err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.L.Debug("Sending initialization message to Telegram")
|
logger.L.Debug("Sending initialization message to Telegram")
|
||||||
if err := notifBot.SendInitMessage(); err != nil {
|
if err := notifBot.SendInitMessage(); err != nil {
|
||||||
|
|
|
@ -1,26 +0,0 @@
|
||||||
package messager
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Sender interface {
|
|
||||||
SendMessage(message string) error
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
|
@ -1,66 +0,0 @@
|
||||||
package unixsender
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"net"
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"git.faercol.me/faercol/public-ip-tracker/tracker/config"
|
|
||||||
"git.faercol.me/faercol/public-ip-tracker/tracker/logger"
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
)
|
|
||||||
|
|
||||||
const method = http.MethodPost
|
|
||||||
const baseURL = "http://unix/"
|
|
||||||
|
|
||||||
type UnixSender struct {
|
|
||||||
logger *logrus.Logger
|
|
||||||
ctx context.Context
|
|
||||||
cancel context.CancelFunc
|
|
||||||
httpClt *http.Client
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *UnixSender) SendMessage(message string) error {
|
|
||||||
s.logger.Debug("Sending message to unix sock")
|
|
||||||
|
|
||||||
body := bytes.NewBufferString(message)
|
|
||||||
|
|
||||||
req, err := http.NewRequestWithContext(s.ctx, method, baseURL, body)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to prepare request: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := s.httpClt.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to run query: %w", err)
|
|
||||||
}
|
|
||||||
if resp.StatusCode != http.StatusOK {
|
|
||||||
return fmt.Errorf("invalid returncode %d", resp.StatusCode)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func New(ctx context.Context, config *config.Config) (*UnixSender, error) {
|
|
||||||
subCtx, cancel := context.WithCancel(ctx)
|
|
||||||
|
|
||||||
logger.L.Infof("Creating Unix exporter to sock %q", config.Export.UnixSock)
|
|
||||||
|
|
||||||
httpClt := http.Client{
|
|
||||||
Transport: &http.Transport{
|
|
||||||
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
|
||||||
dialer := net.Dialer{}
|
|
||||||
return dialer.DialContext(ctx, "unix", config.Export.UnixSock)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
return &UnixSender{
|
|
||||||
ctx: subCtx,
|
|
||||||
cancel: cancel,
|
|
||||||
httpClt: &httpClt,
|
|
||||||
logger: &logger.L,
|
|
||||||
}, nil
|
|
||||||
}
|
|
Loading…
Reference in a new issue