2023-06-29 22:46:17 +01:00
|
|
|
package server
|
|
|
|
|
2023-06-29 23:27:38 +01:00
|
|
|
import (
|
2023-07-01 17:00:48 +01:00
|
|
|
"errors"
|
2023-06-30 09:33:25 +01:00
|
|
|
"fmt"
|
2023-07-11 09:10:56 +01:00
|
|
|
"path"
|
2023-07-10 19:46:12 +01:00
|
|
|
"text/template"
|
2023-07-10 22:27:18 +01:00
|
|
|
"time"
|
2023-06-30 09:33:25 +01:00
|
|
|
|
2023-06-29 23:27:38 +01:00
|
|
|
"git.ctrlz.es/mgdelacroix/birthdaybot/model"
|
2023-07-01 17:00:48 +01:00
|
|
|
"git.ctrlz.es/mgdelacroix/birthdaybot/notification"
|
2023-06-30 09:33:25 +01:00
|
|
|
"git.ctrlz.es/mgdelacroix/birthdaybot/parser"
|
2023-06-30 09:38:29 +01:00
|
|
|
"github.com/charmbracelet/log"
|
2023-06-29 23:27:38 +01:00
|
|
|
)
|
|
|
|
|
2023-07-10 12:39:25 +01:00
|
|
|
var (
|
|
|
|
ErrNoNotificationServices = errors.New("there are no notification services configured")
|
|
|
|
ErrNoLogger = errors.New("there is no logger configured")
|
|
|
|
ErrNoConfig = errors.New("configuration is required to create a server")
|
|
|
|
)
|
2023-07-01 17:00:48 +01:00
|
|
|
|
2023-06-30 09:33:25 +01:00
|
|
|
type Server struct {
|
2023-07-01 17:00:48 +01:00
|
|
|
Logger *log.Logger
|
2023-07-10 20:08:05 +01:00
|
|
|
Config *model.Config
|
2023-07-11 11:37:56 +01:00
|
|
|
WebServer *WebServer
|
2023-07-10 12:39:25 +01:00
|
|
|
workers []Worker
|
2023-07-01 17:00:48 +01:00
|
|
|
birthdays []*model.Birthday
|
|
|
|
notificationServices []notification.NotificationService
|
2023-07-10 19:46:12 +01:00
|
|
|
tmpl *template.Template
|
2023-07-01 17:00:48 +01:00
|
|
|
}
|
|
|
|
|
2023-07-10 20:08:05 +01:00
|
|
|
func (s *Server) createNotificationServices() ([]notification.NotificationService, error) {
|
2023-07-01 17:00:48 +01:00
|
|
|
notificationServices := []notification.NotificationService{}
|
|
|
|
|
2023-07-10 20:08:05 +01:00
|
|
|
if s.Config.TelegramNotifications != nil {
|
|
|
|
telegramNotificationService, err := notification.NewTelegramNotificationService(s.Logger, s.Config)
|
2023-07-04 11:29:13 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("cannot create telegram notification service: %w", err)
|
|
|
|
}
|
2023-07-01 17:00:48 +01:00
|
|
|
notificationServices = append(notificationServices, telegramNotificationService)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(notificationServices) == 0 {
|
|
|
|
return nil, ErrNoNotificationServices
|
|
|
|
}
|
|
|
|
|
|
|
|
return notificationServices, nil
|
2023-06-30 09:33:25 +01:00
|
|
|
}
|
2023-06-29 23:27:38 +01:00
|
|
|
|
2023-07-04 11:48:16 +01:00
|
|
|
func New(options ...Option) (*Server, error) {
|
|
|
|
srv := &Server{}
|
|
|
|
for _, option := range options {
|
|
|
|
srv = option(srv)
|
|
|
|
}
|
2023-06-30 09:38:29 +01:00
|
|
|
|
2023-07-10 12:39:25 +01:00
|
|
|
if srv.Logger == nil {
|
|
|
|
return nil, ErrNoLogger
|
|
|
|
}
|
2023-06-30 09:38:29 +01:00
|
|
|
|
2023-07-10 20:08:05 +01:00
|
|
|
if srv.Config == nil {
|
2023-07-10 12:39:25 +01:00
|
|
|
return nil, ErrNoConfig
|
2023-06-30 09:33:25 +01:00
|
|
|
}
|
|
|
|
|
2023-07-10 12:39:25 +01:00
|
|
|
if len(srv.birthdays) == 0 {
|
2023-07-10 20:08:05 +01:00
|
|
|
srv.Logger.Debug("parsing CSV file", "birthdayFile", srv.Config.Birthdays.File)
|
2023-07-01 17:00:48 +01:00
|
|
|
|
2023-07-10 12:39:25 +01:00
|
|
|
var err error
|
2023-07-10 20:08:05 +01:00
|
|
|
srv.birthdays, err = parser.ParseCSV(srv.Config.Birthdays.File)
|
2023-07-10 12:39:25 +01:00
|
|
|
if err != nil {
|
2023-07-10 20:08:05 +01:00
|
|
|
srv.Logger.Error("cannot parse CSV file", "birthdayFile", srv.Config.Birthdays.File, "error", err)
|
|
|
|
return nil, fmt.Errorf("cannot parse CSV file %s: %w", srv.Config.Birthdays.File, err)
|
2023-07-10 12:39:25 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(srv.notificationServices) == 0 {
|
|
|
|
srv.Logger.Debug("creating notification services from config")
|
|
|
|
|
|
|
|
var err error
|
2023-07-10 20:08:05 +01:00
|
|
|
srv.notificationServices, err = srv.createNotificationServices()
|
2023-07-10 12:39:25 +01:00
|
|
|
if err != nil {
|
|
|
|
srv.Logger.Error("error creating notification services", "error", err)
|
|
|
|
return nil, err
|
|
|
|
}
|
2023-07-01 17:00:48 +01:00
|
|
|
}
|
|
|
|
|
2023-07-10 12:39:25 +01:00
|
|
|
if len(srv.workers) == 0 {
|
2023-07-10 18:58:55 +01:00
|
|
|
srv.Logger.Debug("creating server workers")
|
2023-06-30 09:38:29 +01:00
|
|
|
|
2023-07-10 12:39:25 +01:00
|
|
|
srv.workers = []Worker{NewSimpleWorker(srv)}
|
2023-06-30 09:33:25 +01:00
|
|
|
}
|
|
|
|
|
2023-07-10 20:08:05 +01:00
|
|
|
if srv.Config.Birthdays.Template != "" {
|
|
|
|
srv.Logger.Debug("parsing birthday template", "file", srv.Config.Birthdays.Template)
|
2023-07-10 19:46:12 +01:00
|
|
|
|
2023-07-11 09:10:56 +01:00
|
|
|
funcs := template.FuncMap{
|
|
|
|
"getYearsOld": func(yearOfBirth int) int {
|
|
|
|
return time.Now().Year() - yearOfBirth
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2023-07-10 19:46:12 +01:00
|
|
|
var err error
|
2023-07-11 09:10:56 +01:00
|
|
|
srv.tmpl, err = template.
|
|
|
|
New(path.Base(srv.Config.Birthdays.Template)).
|
|
|
|
Funcs(funcs).
|
|
|
|
ParseFiles(srv.Config.Birthdays.Template)
|
2023-07-10 19:46:12 +01:00
|
|
|
if err != nil {
|
2023-07-10 20:08:05 +01:00
|
|
|
return nil, fmt.Errorf("cannot parse template file %q: %w", srv.Config.Birthdays.Template, err)
|
2023-07-10 19:46:12 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-11 11:37:56 +01:00
|
|
|
if srv.WebServer == nil {
|
|
|
|
srv.Logger.Debug("creating web server")
|
|
|
|
|
|
|
|
srv.WebServer = NewWebServer(srv)
|
|
|
|
}
|
|
|
|
|
2023-07-10 12:39:25 +01:00
|
|
|
return srv, nil
|
2023-06-29 22:46:17 +01:00
|
|
|
}
|
2023-07-01 16:23:19 +01:00
|
|
|
|
2023-07-11 11:37:56 +01:00
|
|
|
func (s *Server) Start() error {
|
2023-07-01 16:31:09 +01:00
|
|
|
s.Logger.Info("starting server")
|
2023-07-11 11:37:56 +01:00
|
|
|
|
|
|
|
if err := s.WebServer.Start(); err != nil {
|
|
|
|
return fmt.Errorf("cannot start web server: %w", err)
|
|
|
|
}
|
|
|
|
|
2023-07-01 16:23:19 +01:00
|
|
|
for _, worker := range s.workers {
|
|
|
|
worker.Start()
|
|
|
|
}
|
2023-07-11 11:37:56 +01:00
|
|
|
|
2023-07-10 18:58:55 +01:00
|
|
|
s.Logger.Debug("server started", "workers", len(s.workers))
|
2023-07-11 11:37:56 +01:00
|
|
|
|
|
|
|
return nil
|
2023-07-01 16:23:19 +01:00
|
|
|
}
|
|
|
|
|
2023-07-11 11:37:56 +01:00
|
|
|
func (s *Server) Stop() error {
|
2023-07-01 16:31:09 +01:00
|
|
|
s.Logger.Info("stopping server")
|
2023-07-11 11:37:56 +01:00
|
|
|
|
|
|
|
if err := s.WebServer.Stop(); err != nil {
|
|
|
|
return fmt.Errorf("cannot stop web server: %w", err)
|
|
|
|
}
|
|
|
|
|
2023-07-01 16:23:19 +01:00
|
|
|
for _, worker := range s.workers {
|
|
|
|
worker.Stop()
|
|
|
|
}
|
2023-07-11 11:37:56 +01:00
|
|
|
|
2023-07-10 18:58:55 +01:00
|
|
|
s.Logger.Debug("server stopped", "workers", len(s.workers))
|
2023-07-11 11:37:56 +01:00
|
|
|
|
|
|
|
return nil
|
2023-07-01 16:23:19 +01:00
|
|
|
}
|
2023-07-04 11:29:13 +01:00
|
|
|
|
|
|
|
func (s *Server) Notify(birthday *model.Birthday) error {
|
|
|
|
errs := []error{}
|
|
|
|
for _, service := range s.notificationServices {
|
2023-07-10 19:46:12 +01:00
|
|
|
err := service.Notify(birthday, s.tmpl)
|
2023-07-04 11:29:13 +01:00
|
|
|
if err != nil {
|
|
|
|
errs = append(errs, err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(errs) == 0 {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return errors.Join(errs...)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Server) Birthdays() []*model.Birthday {
|
|
|
|
return s.birthdays
|
|
|
|
}
|