-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Setup daily notifier and adapt worker to handle multiple scheduled jo…
…bs as abstraction (#6)
- Loading branch information
Showing
7 changed files
with
200 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
package main | ||
|
||
import ( | ||
"fmt" | ||
"log" | ||
|
||
"github.com/pkg/errors" | ||
"gorm.io/gorm" | ||
) | ||
|
||
const ( | ||
DailyNotificationTemplate = `📢 *LateNightCommits Daily Notifier* 📊 | ||
Fetched a total of %d commits today\. | ||
ℹ️ Check out the [stats page](https://latenightcommits.com/api/stats) for more\.` | ||
) | ||
|
||
func runDailyNotification(db *gorm.DB, notifier Notifier) error { | ||
log.Println("📬 Running Daily Notification job...") | ||
|
||
var sentToday int64 | ||
if err := db.Raw( | ||
`SELECT COUNT(id) FROM commits WHERE created_at BETWEEN CURRENT_DATE AND (CURRENT_DATE + INTERVAL 1 DAY);`, | ||
).Scan(&sentToday).Error; err != nil { | ||
return errors.Wrap(err, "failed to retrieve daily fetched amount for notifier") | ||
} | ||
|
||
msg := fmt.Sprintf(DailyNotificationTemplate, sentToday) | ||
|
||
if err := notifier.Notify(msg); err != nil { | ||
return errors.Wrap(err, "failed to notify") | ||
} | ||
|
||
log.Println("Successfully sent daily notification!") | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
package main | ||
|
||
import ( | ||
"log" | ||
|
||
"github.com/spf13/viper" | ||
) | ||
|
||
// Notifier encapsulates the logic and infrastructure necessary to send | ||
// notifications to external systems. | ||
type Notifier interface { | ||
Notify(msg string) error | ||
} | ||
|
||
// NoopNotifier simply performs a no-op instead of notifying any external system. | ||
type NoopNotifier struct{} | ||
|
||
// Notify returns nil immediately. | ||
func (n *NoopNotifier) Notify(msg string) error { | ||
return nil | ||
} | ||
|
||
// NewNotifier builds a notifier based on the current configuration, or a NoopNotifier | ||
// if none configured. | ||
func NewNotifier() Notifier { | ||
token := viper.GetString("telegram.bot_token") | ||
chatID := viper.GetString("telegram.chat_id") | ||
|
||
if token == "" || chatID == "" { | ||
log.Println("🟨 No notifier configured, using NoopNotifier.") | ||
return &NoopNotifier{} | ||
} | ||
|
||
return NewTelegramNotifier(token, chatID) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
package main | ||
|
||
import ( | ||
"fmt" | ||
"log" | ||
|
||
"github.com/pkg/errors" | ||
"github.com/robfig/cron/v3" | ||
) | ||
|
||
// Scheduler handles running jobs on a given cron Schedule. | ||
type Scheduler struct { | ||
cron *cron.Cron | ||
} | ||
|
||
// NewScheduler returns a new Scheduler instance. | ||
func NewScheduler() *Scheduler { | ||
return &Scheduler{cron.New()} | ||
} | ||
|
||
// Run the cron scheduler, or no-op if already running. | ||
func (s *Scheduler) Run() { | ||
s.cron.Run() | ||
} | ||
|
||
// JobDefinition for working with the Scheduler. | ||
type JobDefinition struct { | ||
Name string | ||
Schedule string | ||
Run func() error | ||
} | ||
|
||
// AddJob adds a job to the Scheduler to be run on the given spec schedule and name. | ||
func (s *Scheduler) AddJob(job *JobDefinition) error { | ||
if _, err := s.cron.AddFunc(job.Schedule, func() { | ||
if err := job.Run(); err != nil { | ||
log.Printf("%s job error: %v", job.Name, err) | ||
} | ||
}); err != nil { | ||
return errors.Wrap(err, fmt.Sprintf("failed to schedule %s worker", job.Name)) | ||
} | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
package main | ||
|
||
import ( | ||
"fmt" | ||
"io" | ||
"net/http" | ||
"net/url" | ||
"strings" | ||
"time" | ||
) | ||
|
||
type TelegramNotifier struct { | ||
token string | ||
chatID string | ||
http *http.Client | ||
} | ||
|
||
func NewTelegramNotifier(token, chatID string) *TelegramNotifier { | ||
return &TelegramNotifier{ | ||
token: token, | ||
chatID: chatID, | ||
http: &http.Client{ | ||
Timeout: 5 * time.Second, | ||
}, | ||
} | ||
} | ||
|
||
func (t *TelegramNotifier) Notify(msg string) error { | ||
notifyURL := fmt.Sprintf("https://api.telegram.org/bot%s/sendMessage", t.token) | ||
form := url.Values{} | ||
form.Set("text", msg) | ||
form.Set("chat_id", t.chatID) | ||
form.Set("parse_mode", "MarkdownV2") | ||
|
||
req, err := http.NewRequest(http.MethodPost, notifyURL, strings.NewReader(form.Encode())) | ||
if err != nil { | ||
return err | ||
} | ||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded") | ||
|
||
res, err := t.http.Do(req) | ||
if err != nil { | ||
return err | ||
} | ||
defer res.Body.Close() | ||
|
||
if res.StatusCode != 200 { | ||
body, _ := io.ReadAll(res.Body) | ||
return fmt.Errorf("bad response %d from telegram: %s", res.StatusCode, string(body)) | ||
} | ||
return nil | ||
} |