Add monitoring of the current public IP #10
9 changed files with 331 additions and 29 deletions
|
@ -3,6 +3,7 @@ package bot
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.faercol.me/faercol/public-ip-tracker/tracker/config"
|
"git.faercol.me/faercol/public-ip-tracker/tracker/config"
|
||||||
|
@ -11,11 +12,17 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type Notifier struct {
|
type Notifier struct {
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
cancel context.CancelFunc
|
cancel context.CancelFunc
|
||||||
tgBot *bot.ConcreteBot
|
tgBot bot.Bot
|
||||||
notifChannel int64
|
tgChatID int64
|
||||||
ipGetter *ip.IPGetter
|
ipGetter ip.IPGetter
|
||||||
|
timeGetter func() time.Time
|
||||||
|
currentIP net.IP
|
||||||
|
frequency time.Duration
|
||||||
|
errChan chan error
|
||||||
|
changesChan chan net.IP
|
||||||
|
exitChan chan struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *Notifier) SendInitMessage() error {
|
func (n *Notifier) SendInitMessage() error {
|
||||||
|
@ -24,22 +31,67 @@ func (n *Notifier) SendInitMessage() error {
|
||||||
return fmt.Errorf("failed to get current public IP: %w", err)
|
return fmt.Errorf("failed to get current public IP: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
initMsg := fmt.Sprintf("Public IP tracker initialized at %v, public IP is %s", time.Now(), publicIP)
|
n.currentIP = publicIP
|
||||||
if err := n.tgBot.SendMessage(n.ctx, n.notifChannel, initMsg); err != nil {
|
currentTime := n.timeGetter()
|
||||||
|
|
||||||
|
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 {
|
||||||
return fmt.Errorf("failed to send initialization message: %w", err)
|
return fmt.Errorf("failed to send initialization message: %w", err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
return fmt.Errorf("failed to send update message: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Notifier) Run() {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-time.After(n.frequency):
|
||||||
|
newIP, err := n.ipGetter.GetCurrentPublicIP(n.ctx)
|
||||||
|
if err != nil {
|
||||||
|
n.errChan <- fmt.Errorf("failed to update public IP: %w", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !newIP.Equal(n.currentIP) {
|
||||||
|
n.currentIP = newIP
|
||||||
|
if err := n.sendUpdatedIPMsg(); err != nil {
|
||||||
|
n.errChan <- err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case <-n.ctx.Done():
|
||||||
|
n.exitChan <- struct{}{}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Notifier) ErrChan() <-chan error {
|
||||||
|
return n.errChan
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Notifier) Exit() <-chan struct{} {
|
||||||
|
return n.exitChan
|
||||||
|
}
|
||||||
|
|
||||||
func New(ctx context.Context, config *config.Config) *Notifier {
|
func New(ctx context.Context, config *config.Config) *Notifier {
|
||||||
subCtx, cancel := context.WithCancel(ctx)
|
subCtx, cancel := context.WithCancel(ctx)
|
||||||
tgBot := bot.New(config.Telegram.Token)
|
tgBot := bot.New(config.Telegram.Token)
|
||||||
|
|
||||||
return &Notifier{
|
return &Notifier{
|
||||||
ctx: subCtx,
|
ctx: subCtx,
|
||||||
cancel: cancel,
|
cancel: cancel,
|
||||||
tgBot: tgBot,
|
tgBot: tgBot,
|
||||||
notifChannel: config.Telegram.ChannelID,
|
tgChatID: config.Telegram.ChannelID,
|
||||||
ipGetter: ip.New(),
|
timeGetter: time.Now,
|
||||||
|
ipGetter: ip.New(),
|
||||||
|
errChan: make(chan error, 10),
|
||||||
|
exitChan: make(chan struct{}, 1),
|
||||||
|
frequency: config.PollingFrequency,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
191
tracker/bot/bot_test.go
Normal file
191
tracker/bot/bot_test.go
Normal file
|
@ -0,0 +1,191 @@
|
||||||
|
package bot
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"net"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
iptest "git.faercol.me/faercol/public-ip-tracker/tracker/ip/test"
|
||||||
|
"github.com/ahugues/go-telegram-api/structs"
|
||||||
|
)
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do nothing here, it's not used by this bot
|
||||||
|
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 {
|
||||||
|
if mb.SendMessageFunc == nil {
|
||||||
|
return mb.SendMessageProp
|
||||||
|
}
|
||||||
|
return mb.SendMessageFunc(ctx, chatID, content)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInit(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
called := false
|
||||||
|
|
||||||
|
tgBot := mockTGBot{
|
||||||
|
SendMessageFunc: func(ctx context.Context, chatID int64, content string) error {
|
||||||
|
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" {
|
||||||
|
t.Errorf("Unexpected message %s", content)
|
||||||
|
}
|
||||||
|
called = true
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
ipGetter := iptest.TestIPGetter{
|
||||||
|
PublicIPProp: net.ParseIP("198.51.100.42"),
|
||||||
|
}
|
||||||
|
|
||||||
|
bot := Notifier{
|
||||||
|
ctx: ctx,
|
||||||
|
cancel: cancel,
|
||||||
|
tgBot: &tgBot,
|
||||||
|
tgChatID: expectedChatID,
|
||||||
|
timeGetter: func() time.Time { return time.Date(2023, 1, 28, 14, 17, 12, 0, time.UTC) },
|
||||||
|
ipGetter: &ipGetter,
|
||||||
|
frequency: 1 * time.Minute,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := bot.SendInitMessage(); err != nil {
|
||||||
|
t.Fatalf("Unexpected error %s", err.Error())
|
||||||
|
}
|
||||||
|
if !called {
|
||||||
|
t.Error("Telegram bot not called")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUpdateIP(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
called := false
|
||||||
|
|
||||||
|
tgBot := mockTGBot{
|
||||||
|
SendMessageFunc: func(ctx context.Context, chatID int64, content string) error {
|
||||||
|
if chatID != expectedChatID {
|
||||||
|
t.Errorf("Unexpected chatID %d", chatID)
|
||||||
|
}
|
||||||
|
if content != "Public IP has been changed, is now 198.51.100.42" {
|
||||||
|
t.Errorf("Unexpected message %s", content)
|
||||||
|
}
|
||||||
|
called = true
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
ipGetter := iptest.TestIPGetter{
|
||||||
|
PublicIPProp: net.ParseIP("198.51.100.42"),
|
||||||
|
}
|
||||||
|
|
||||||
|
bot := Notifier{
|
||||||
|
ctx: ctx,
|
||||||
|
cancel: cancel,
|
||||||
|
tgBot: &tgBot,
|
||||||
|
tgChatID: expectedChatID,
|
||||||
|
timeGetter: func() time.Time { return time.Date(2023, 1, 28, 14, 17, 12, 0, time.UTC) },
|
||||||
|
currentIP: net.ParseIP("198.51.100.12"),
|
||||||
|
exitChan: make(chan struct{}, 1),
|
||||||
|
errChan: make(chan error, 5),
|
||||||
|
ipGetter: &ipGetter,
|
||||||
|
frequency: 500 * time.Millisecond,
|
||||||
|
}
|
||||||
|
|
||||||
|
go bot.Run()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-bot.Exit():
|
||||||
|
t.Error("Unexpected exit")
|
||||||
|
case <-time.After(1 * time.Second):
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
cancel()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-bot.Exit():
|
||||||
|
break
|
||||||
|
case <-time.After(2 * time.Second):
|
||||||
|
t.Error("Unexpected timeout")
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case err := <-bot.ErrChan():
|
||||||
|
t.Errorf("Unexpected error %s", err.Error())
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if !called {
|
||||||
|
t.Error("Telegram bot not called")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUpdateIPNoChange(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
|
||||||
|
tgBot := mockTGBot{
|
||||||
|
SendMessageProp: errors.New("should not be called"),
|
||||||
|
}
|
||||||
|
|
||||||
|
ipGetter := iptest.TestIPGetter{
|
||||||
|
PublicIPProp: net.ParseIP("198.51.100.42"),
|
||||||
|
}
|
||||||
|
|
||||||
|
bot := Notifier{
|
||||||
|
ctx: ctx,
|
||||||
|
cancel: cancel,
|
||||||
|
tgBot: &tgBot,
|
||||||
|
tgChatID: expectedChatID,
|
||||||
|
timeGetter: func() time.Time { return time.Date(2023, 1, 28, 14, 17, 12, 0, time.UTC) },
|
||||||
|
currentIP: net.ParseIP("198.51.100.42"),
|
||||||
|
exitChan: make(chan struct{}, 1),
|
||||||
|
errChan: make(chan error, 5),
|
||||||
|
ipGetter: &ipGetter,
|
||||||
|
frequency: 100 * time.Millisecond,
|
||||||
|
}
|
||||||
|
|
||||||
|
go bot.Run()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-bot.Exit():
|
||||||
|
t.Error("Unexpected exit")
|
||||||
|
case <-time.After(500 * time.Millisecond):
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
cancel()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-bot.Exit():
|
||||||
|
break
|
||||||
|
case <-time.After(2 * time.Second):
|
||||||
|
t.Error("Unexpected timeout")
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case err := <-bot.ErrChan():
|
||||||
|
t.Errorf("Unexpected error %s", err.Error())
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type TelegramConfig struct {
|
type TelegramConfig struct {
|
||||||
|
@ -12,7 +13,13 @@ type TelegramConfig struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Telegram *TelegramConfig `json:"telegram"`
|
Telegram *TelegramConfig
|
||||||
|
PollingFrequency time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
type jsonConfig struct {
|
||||||
|
Telegram *TelegramConfig `json:"telegram"`
|
||||||
|
PollingFrequency int64 `json:"polling_frequency"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(filepath string) (*Config, error) {
|
func New(filepath string) (*Config, error) {
|
||||||
|
@ -20,9 +27,12 @@ func New(filepath string) (*Config, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to read config file %q: %w", filepath, err)
|
return nil, fmt.Errorf("failed to read config file %q: %w", filepath, err)
|
||||||
}
|
}
|
||||||
var conf Config
|
var jsonConf jsonConfig
|
||||||
if err := json.Unmarshal(content, &conf); 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 &conf, nil
|
return &Config{
|
||||||
|
Telegram: jsonConf.Telegram,
|
||||||
|
PollingFrequency: time.Duration(jsonConf.PollingFrequency) * time.Second,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,4 +2,4 @@ module git.faercol.me/faercol/public-ip-tracker/tracker
|
||||||
|
|
||||||
go 1.16
|
go 1.16
|
||||||
|
|
||||||
require github.com/ahugues/go-telegram-api v0.0.0-20230125191847-f1f02f942580
|
require github.com/ahugues/go-telegram-api v0.0.0-20230128131122-4d5782beddd0
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
github.com/ahugues/go-telegram-api v0.0.0-20230125191847-f1f02f942580 h1:0EzaHeqTet8yPON8P6kHfHVl9Zb8+eWON0wPco93N7Y=
|
github.com/ahugues/go-telegram-api v0.0.0-20230128131122-4d5782beddd0 h1:1R472WmBuKK9Bvt4+rlqj01z1MwlVic3Xzgjvql3PXA=
|
||||||
github.com/ahugues/go-telegram-api v0.0.0-20230125191847-f1f02f942580/go.mod h1:8I/JWxd9GYM7dHOgGmkRI3Ei1u+nGvzeR2knIMmFw7E=
|
github.com/ahugues/go-telegram-api v0.0.0-20230128131122-4d5782beddd0/go.mod h1:8I/JWxd9GYM7dHOgGmkRI3Ei1u+nGvzeR2knIMmFw7E=
|
||||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
|
|
@ -14,13 +14,17 @@ const ifconfigURL = "https://ifconfig.me"
|
||||||
const httpMaxRead = 100
|
const httpMaxRead = 100
|
||||||
const httpTimeout = 10 * time.Second
|
const httpTimeout = 10 * time.Second
|
||||||
|
|
||||||
type IPGetter struct {
|
type IPGetter interface {
|
||||||
|
GetCurrentPublicIP(ctx context.Context) (net.IP, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type concreteIPGetter struct {
|
||||||
httpClt *http.Client
|
httpClt *http.Client
|
||||||
remoteAddress string
|
remoteAddress string
|
||||||
timeout time.Duration
|
timeout time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *IPGetter) GetCurrentPublicIP(ctx context.Context) (net.IP, error) {
|
func (c *concreteIPGetter) GetCurrentPublicIP(ctx context.Context) (net.IP, error) {
|
||||||
reqCtx, cancel := context.WithTimeout(ctx, c.timeout)
|
reqCtx, cancel := context.WithTimeout(ctx, c.timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
|
@ -52,8 +56,8 @@ func (c *IPGetter) GetCurrentPublicIP(ctx context.Context) (net.IP, error) {
|
||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func New() *IPGetter {
|
func New() IPGetter {
|
||||||
return &IPGetter{
|
return &concreteIPGetter{
|
||||||
httpClt: http.DefaultClient,
|
httpClt: http.DefaultClient,
|
||||||
remoteAddress: ifconfigURL,
|
remoteAddress: ifconfigURL,
|
||||||
timeout: httpTimeout,
|
timeout: httpTimeout,
|
||||||
|
|
|
@ -18,7 +18,7 @@ func TestGetIPOK(t *testing.T) {
|
||||||
}))
|
}))
|
||||||
defer mockSrv.Close()
|
defer mockSrv.Close()
|
||||||
|
|
||||||
clt := IPGetter{
|
clt := concreteIPGetter{
|
||||||
httpClt: mockSrv.Client(),
|
httpClt: mockSrv.Client(),
|
||||||
remoteAddress: mockSrv.URL,
|
remoteAddress: mockSrv.URL,
|
||||||
timeout: 1 * time.Second,
|
timeout: 1 * time.Second,
|
||||||
|
@ -43,7 +43,7 @@ func TestGetIPServerErr(t *testing.T) {
|
||||||
}))
|
}))
|
||||||
defer mockSrv.Close()
|
defer mockSrv.Close()
|
||||||
|
|
||||||
clt := IPGetter{
|
clt := concreteIPGetter{
|
||||||
httpClt: mockSrv.Client(),
|
httpClt: mockSrv.Client(),
|
||||||
remoteAddress: mockSrv.URL,
|
remoteAddress: mockSrv.URL,
|
||||||
timeout: 1 * time.Second,
|
timeout: 1 * time.Second,
|
||||||
|
@ -66,7 +66,7 @@ func TestGetIPUnreachable(t *testing.T) {
|
||||||
}))
|
}))
|
||||||
mockSrv.Close()
|
mockSrv.Close()
|
||||||
|
|
||||||
clt := IPGetter{
|
clt := concreteIPGetter{
|
||||||
httpClt: mockSrv.Client(),
|
httpClt: mockSrv.Client(),
|
||||||
remoteAddress: mockSrv.URL,
|
remoteAddress: mockSrv.URL,
|
||||||
timeout: 1 * time.Second,
|
timeout: 1 * time.Second,
|
||||||
|
@ -91,7 +91,7 @@ func TestGetIPInvalidResponse(t *testing.T) {
|
||||||
}))
|
}))
|
||||||
defer mockSrv.Close()
|
defer mockSrv.Close()
|
||||||
|
|
||||||
clt := IPGetter{
|
clt := concreteIPGetter{
|
||||||
httpClt: mockSrv.Client(),
|
httpClt: mockSrv.Client(),
|
||||||
remoteAddress: mockSrv.URL,
|
remoteAddress: mockSrv.URL,
|
||||||
timeout: 1 * time.Second,
|
timeout: 1 * time.Second,
|
||||||
|
@ -117,7 +117,7 @@ func TestGetIPTimeout(t *testing.T) {
|
||||||
}))
|
}))
|
||||||
defer mockSrv.Close()
|
defer mockSrv.Close()
|
||||||
|
|
||||||
clt := IPGetter{
|
clt := concreteIPGetter{
|
||||||
httpClt: mockSrv.Client(),
|
httpClt: mockSrv.Client(),
|
||||||
remoteAddress: mockSrv.URL,
|
remoteAddress: mockSrv.URL,
|
||||||
timeout: 1 * time.Millisecond,
|
timeout: 1 * time.Millisecond,
|
||||||
|
|
18
tracker/ip/test/ip.go
Normal file
18
tracker/ip/test/ip.go
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
package test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TestIPGetter struct {
|
||||||
|
PublicIPProp net.IP
|
||||||
|
PublicIPFunc func(ctx context.Context) (net.IP, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *TestIPGetter) GetCurrentPublicIP(ctx context.Context) (net.IP, error) {
|
||||||
|
if g.PublicIPFunc == nil {
|
||||||
|
return g.PublicIPProp, nil
|
||||||
|
}
|
||||||
|
return g.PublicIPFunc(ctx)
|
||||||
|
}
|
|
@ -4,6 +4,8 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
|
||||||
"git.faercol.me/faercol/public-ip-tracker/tracker/bot"
|
"git.faercol.me/faercol/public-ip-tracker/tracker/bot"
|
||||||
"git.faercol.me/faercol/public-ip-tracker/tracker/config"
|
"git.faercol.me/faercol/public-ip-tracker/tracker/config"
|
||||||
|
@ -27,6 +29,8 @@ func main() {
|
||||||
fmt.Println("Parsing arguments")
|
fmt.Println("Parsing arguments")
|
||||||
args := parseArgs()
|
args := parseArgs()
|
||||||
|
|
||||||
|
mainCtx, cancel := context.WithCancel(context.Background())
|
||||||
|
|
||||||
fmt.Println("Parsing config")
|
fmt.Println("Parsing config")
|
||||||
conf, err := config.New(args.configPath)
|
conf, err := config.New(args.configPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -34,10 +38,33 @@ func main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println("Initializing bot")
|
fmt.Println("Initializing bot")
|
||||||
notifBot := bot.New(context.Background(), conf)
|
notifBot := bot.New(mainCtx, conf)
|
||||||
if err := notifBot.SendInitMessage(); err != nil {
|
if err := notifBot.SendInitMessage(); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fmt.Println("Starting monitoring")
|
||||||
|
go notifBot.Run()
|
||||||
|
|
||||||
|
c := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(c, os.Interrupt)
|
||||||
|
|
||||||
|
outerloop:
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-c:
|
||||||
|
fmt.Println("received cancel")
|
||||||
|
cancel()
|
||||||
|
break outerloop
|
||||||
|
case err := <-notifBot.ErrChan():
|
||||||
|
fmt.Printf("Unexpected error %s", err.Error())
|
||||||
|
case <-notifBot.Exit():
|
||||||
|
fmt.Println("Unexpected exit")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
<-notifBot.Exit()
|
||||||
|
|
||||||
fmt.Println("OK")
|
fmt.Println("OK")
|
||||||
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue