Skip to content

Commit

Permalink
- Add configurable retries for do-retry with default of 3 (alerting,
Browse files Browse the repository at this point in the history
  mails, scraping)
- Log maxprocs failures through logrus
  • Loading branch information
dkorunic committed Dec 22, 2022
1 parent 0f04cfc commit fe51490
Show file tree
Hide file tree
Showing 9 changed files with 38 additions and 20 deletions.
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ Za instalaciju dovoljno je skinuti izvršnu datoteku sa [releases](https://githu
### Usage / Upute za upotrebu

```shell
Usage: e-dnevnik-bot [-?dtv] [-b value] [-f value] [-i value] [parameters ...]
Usage: e-dnevnik-bot [-?dtv] [-b value] [-f value] [-i value] [-r value] [parameters ...]
-?, --help display help
-b, --database=value
alert database file [.e-dnevnik.db]
Expand All @@ -69,6 +69,8 @@ Usage: e-dnevnik-bot [-?dtv] [-b value] [-f value] [-i value] [parameters ...]
configuration file (in TOML) [.e-dnevnik.toml]
-i, --interval=value
interval between polls when in daemon mode [1h]
-r, --retries=value
default retry attempts on error [3]
-t, --test send a test event (to check if messaging works)
-v, --verbose enable verbose/debug log level
```
Expand All @@ -81,6 +83,7 @@ Other flags are:
- `-d`: enable daemon mode aka service mode where bot works continously, waking up on regular intervals (specified with `-i`) and by default this is disabled,
- `-f`: configuration file path to configure usernames, passwords and various messaging services (in [TOML](https://github.com/toml-lang/toml) format),
- `-i`: interval between polls when in daemon/service mode (at minimum 1h, default 1h),
- `-r`: retries between unsuccessful attempts to scrape and/or send alerts (default 3),
- `-t`: sends a test message to all configured messaging services,
- `-v`: enables verbose/debug messages for more insight into bot operation and by default this is disabled.
Expand All @@ -94,6 +97,7 @@ Ostali parametri su:
- `-d`: omogućuje servisni rad gdje bot radi kontinuirano i budi se u regularnim intervalima (koje odabiremo sa `-i` parametrom) te je ovakav način rada standardno ugašen,
- `-f`: staza do konfiguracijske datoteke koja sadrži korisnička imena, lozinke i ostalu konfiguraciju za servise slanja poruka odnosno e-maila (u [TOML](https://github.com/toml-lang/toml) sintaksi),
- `-i`: interval između buđenja bota (minimalno 1h, standardno 1h),
- `-r`: broj pokušaja kod neuspjeha prilikom dohvata ocjena i/ili slanja poruka odnosno e-mailova,
- `-t`: služi za slanje testne poruke na sve konfigurirane servise slanja poruka odnosno e-maila,
- `-v`: omogućuje prikaz više informacija o radu servisa, te je standardno ova opcija ugašena.
Expand Down
3 changes: 3 additions & 0 deletions flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,14 @@ import (
const (
DefaultConfFile = ".e-dnevnik.toml" // default configuration filename
DefaultTickInterval = "1h" // default (and minimal permitted value) is 1 tick per 1h
DefaultRetries = 3 // default retry attempts
)

var (
debug, daemon, help, emulation *bool
confFile, dbFile, tickIntervalString *string
tickInterval time.Duration
retries *uint
)

// init initializes flags configuration.
Expand All @@ -51,6 +53,7 @@ func init() {
dbFile = getopt.StringLong("database", 'b', db.DefaultDBPath, "alert database file")
tickIntervalString = getopt.StringLong("interval", 'i', DefaultTickInterval,
"interval between polls when in daemon mode")
retries = getopt.UintLong("retries", 'r', DefaultRetries, "default retry attempts on error")
}

// parseFlags parses input arguments and flags.
Expand Down
15 changes: 13 additions & 2 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ package main

import (
"context"
"errors"
"os"
"os/signal"
"sync"
Expand All @@ -36,7 +37,7 @@ import (

_ "github.com/KimMachineGun/automemlimit"
"github.com/dkorunic/e-dnevnik-bot/msgtypes"
_ "go.uber.org/automaxprocs"
"go.uber.org/automaxprocs/maxprocs"
)

const (
Expand All @@ -49,7 +50,10 @@ const (
testField = "Testna vrijednost"
)

var exitWithError atomic.Bool
var (
exitWithError atomic.Bool
ErrMaxProc = errors.New("failed to set GOMAXPROCS")
)

func main() {
parseFlags()
Expand All @@ -66,6 +70,13 @@ func main() {
logrus.SetLevel(logrus.DebugLevel)
}

// auto-configure GOMAXPROCS
undo, err := maxprocs.Set()
defer undo()
if err != nil {
logrus.Warnf("%v: %v", ErrMaxProc, err)
}

// context with signal integration
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
defer stop()
Expand Down
3 changes: 2 additions & 1 deletion messenger/discord.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ var (

// Discord messenger processes events from a channel and attempts to communicate to one or more UserIDs, optionally
// returning an error.
func Discord(ctx context.Context, ch <-chan interface{}, token string, userIDs []string) error {
func Discord(ctx context.Context, ch <-chan interface{}, token string, userIDs []string, retries uint) error {
if token == "" {
return fmt.Errorf("%w", ErrDiscordEmptyAPIKey)
}
Expand Down Expand Up @@ -124,6 +124,7 @@ func Discord(ctx context.Context, ch <-chan interface{}, token string, userIDs [

return err
},
retry.Attempts(retries),
retry.Context(ctx),
)
if err != nil {
Expand Down
3 changes: 2 additions & 1 deletion messenger/mail.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ var (
// Mail messenger processes events from a channel and attempts to send emails to one or more recipients,
// optionally returning an error.
func Mail(ctx context.Context, ch <-chan interface{}, server, port, username, password, from, subject string,
to []string,
to []string, retries uint,
) error {
logrus.Debug("Sending message through mail service")

Expand Down Expand Up @@ -94,6 +94,7 @@ func Mail(ctx context.Context, ch <-chan interface{}, server, port, username, pa
func() error {
return d.DialAndSend(m)
},
retry.Attempts(retries),
retry.Context(ctx),
)
if err != nil {
Expand Down
3 changes: 2 additions & 1 deletion messenger/slack.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ var (

// Slack messenger processes events from a channel and attempts to communicate to one or more ChatIDs, optionally
// returning an error.
func Slack(ctx context.Context, ch <-chan interface{}, token string, chatIDs []string) error {
func Slack(ctx context.Context, ch <-chan interface{}, token string, chatIDs []string, retries uint) error {
if token == "" {
return fmt.Errorf("%w", ErrSlackEmptyAPIKey)
}
Expand Down Expand Up @@ -89,6 +89,7 @@ func Slack(ctx context.Context, ch <-chan interface{}, token string, chatIDs []s

return err
},
retry.Attempts(retries),
retry.Context(ctx),
)
if err != nil {
Expand Down
3 changes: 2 additions & 1 deletion messenger/telegram.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ var (

// Telegram messenger processes events from a channel and attempts to communicate to one or more ChatIDs, optionally
// returning an error.
func Telegram(ctx context.Context, ch <-chan interface{}, apiKey string, chatIDs []string) error {
func Telegram(ctx context.Context, ch <-chan interface{}, apiKey string, chatIDs []string, retries uint) error {
if apiKey == "" {
return fmt.Errorf("%w", ErrTelegramEmptyAPIKey)
}
Expand Down Expand Up @@ -106,6 +106,7 @@ func Telegram(ctx context.Context, ch <-chan interface{}, apiKey string, chatIDs

return err
},
retry.Attempts(retries),
retry.Context(ctx),
)
if err != nil {
Expand Down
10 changes: 5 additions & 5 deletions routines.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ func scrapers(ctx context.Context, wgScrape *sync.WaitGroup, gradesScraped chan<
go func() {
defer wgScrape.Done()

err := scrape.GetGradesAndEvents(ctx, gradesScraped, i.Username, i.Password)
err := scrape.GetGradesAndEvents(ctx, gradesScraped, i.Username, i.Password, *retries)
if err != nil {
logrus.Warnf("%v %v: %v", ErrScrapingUser, i.Username, err)
exitWithError.Store(true)
Expand Down Expand Up @@ -89,7 +89,7 @@ func msgSend(ctx context.Context, wgMsg *sync.WaitGroup, gradesMsg <-chan msgtyp
go func() {
defer wgMsg.Done()
logrus.Debug("Discord messenger started")
if err := messenger.Discord(ctx, ch, config.Discord.Token, config.Discord.UserIDs); err != nil {
if err := messenger.Discord(ctx, ch, config.Discord.Token, config.Discord.UserIDs, *retries); err != nil {
logrus.Warnf("%v: %v", ErrDiscord, err)
exitWithError.Store(true)
}
Expand All @@ -107,7 +107,7 @@ func msgSend(ctx context.Context, wgMsg *sync.WaitGroup, gradesMsg <-chan msgtyp
go func() {
defer wgMsg.Done()
logrus.Debug("Telegram messenger started")
if err := messenger.Telegram(ctx, ch, config.Telegram.Token, config.Telegram.ChatIDs); err != nil {
if err := messenger.Telegram(ctx, ch, config.Telegram.Token, config.Telegram.ChatIDs, *retries); err != nil {
logrus.Warnf("%v: %v", ErrTelegram, err)
exitWithError.Store(true)
}
Expand All @@ -125,7 +125,7 @@ func msgSend(ctx context.Context, wgMsg *sync.WaitGroup, gradesMsg <-chan msgtyp
go func() {
defer wgMsg.Done()
logrus.Debug("Slack messenger started")
if err := messenger.Slack(ctx, ch, config.Slack.Token, config.Slack.ChatIDs); err != nil {
if err := messenger.Slack(ctx, ch, config.Slack.Token, config.Slack.ChatIDs, *retries); err != nil {
logrus.Warnf("%v: %v", ErrSlack, err)
exitWithError.Store(true)
}
Expand All @@ -144,7 +144,7 @@ func msgSend(ctx context.Context, wgMsg *sync.WaitGroup, gradesMsg <-chan msgtyp
defer wgMsg.Done()
logrus.Debug("Mail messenger started")
if err := messenger.Mail(ctx, ch, config.Mail.Server, config.Mail.Port, config.Mail.Username,
config.Mail.Password, config.Mail.From, config.Mail.Subject, config.Mail.To); err != nil {
config.Mail.Password, config.Mail.From, config.Mail.Subject, config.Mail.To, *retries); err != nil {
logrus.Warnf("%v: %v", ErrMail, err)
exitWithError.Store(true)
}
Expand Down
12 changes: 4 additions & 8 deletions scrape/scrape.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,23 +23,19 @@ package scrape

import (
"context"
"time"

"github.com/dkorunic/e-dnevnik-bot/fetch"
"github.com/dkorunic/e-dnevnik-bot/msgtypes"

"github.com/avast/retry-go/v4"
)

const (
defaultScrapeTimeout = fetch.Timeout * defaultAttempts // max time permitted for all attempts
defaultAttempts = 10 // total number of full csrf/auth/fetch attempts
)

// GetGradesAndEvents initiates fetching subjects, grades and exam events from remote e-dnevnik site, sends
// individual messages to a message channel and optionally returning an error.
func GetGradesAndEvents(ctx context.Context, ch chan<- msgtypes.Message, username, password string) error {
func GetGradesAndEvents(ctx context.Context, ch chan<- msgtypes.Message, username, password string, retries uint) error {
err := func() error {
ctx, stop := context.WithTimeout(ctx, defaultScrapeTimeout)
ctx, stop := context.WithTimeout(ctx, time.Duration(retries)*fetch.Timeout)
defer stop()

client, err := fetch.NewClientWithContext(ctx, username, password)
Expand All @@ -57,7 +53,7 @@ func GetGradesAndEvents(ctx context.Context, ch chan<- msgtypes.Message, usernam

return err
},
retry.Attempts(defaultAttempts),
retry.Attempts(retries),
retry.Context(ctx),
)
if err != nil {
Expand Down

0 comments on commit fe51490

Please sign in to comment.