Adds templates to birthday message

This commit is contained in:
Miguel de la Cruz 2023-07-10 20:46:12 +02:00
parent 7a88cf62ac
commit c7399eb9da
9 changed files with 64 additions and 11 deletions

View file

@ -11,6 +11,19 @@ There is an [example configuration file](./example-config.yml) that
can be used as a base to create your own configuration, and there is a can be used as a base to create your own configuration, and there is a
[birthdays example CSV file](./birthdays.csv) to load the data. [birthdays example CSV file](./birthdays.csv) to load the data.
## Template file
The template can be configured using the `birthday_template` config
parameter to point to the template file. The template uses the Go
Template Format, and has the following properties available:
- `.Name`: the name of the person whose birthday we're notifying.
- `.Email`: the email of the person.
- `.Phone`: the phone of the person, as a string.
- `.YearOfBirth`: the year that the person was born, as number.
- `.MonthOfBirth`: the month that the person was born, as number.
- `.DayOfBirth`: the day that the person was born, as number.
## Run the bot ## Run the bot
To get help for the bot command, run: To get help for the bot command, run:
@ -39,7 +52,7 @@ $ make run
- [X] Configure logger through config (levels and such) - [X] Configure logger through config (levels and such)
- [X] Reduce logger verbosity (through levels) - [X] Reduce logger verbosity (through levels)
- [ ] Add pictures to birthday notifications - [ ] Add pictures to birthday notifications
- [ ] Create a configurable template to fill with each notification - [X] Create a configurable template to fill with each notification
- [ ] Create different message systems to use with the bot - [ ] Create different message systems to use with the bot
- [X] Telegram - [X] Telegram
- [ ] Email - [ ] Email

View file

@ -1,5 +1,6 @@
--- ---
birthday_file: birthdays.csv birthday_file: birthdays.csv
birthday_template: ./birthday_message.tmpl
logger: logger:
level: debug level: debug

View file

@ -15,6 +15,17 @@ type Birthday struct {
DayOfBirth int DayOfBirth int
} }
func (b *Birthday) ToMap() map[string]any {
return map[string]any{
"Name": b.Name,
"Email": b.Email,
"Phone": b.Phone,
"YearOfBirth": b.YearOfBirth,
"MonthOfBirth": b.MonthOfBirth,
"DayOfBirth": b.DayOfBirth,
}
}
func NewBirthdayFromRecord(record []string) (*Birthday, error) { func NewBirthdayFromRecord(record []string) (*Birthday, error) {
if len(record) != 4 { if len(record) != 4 {
return nil, fmt.Errorf("invalid length %d for record", len(record)) return nil, fmt.Errorf("invalid length %d for record", len(record))

View file

@ -18,11 +18,11 @@ var (
type Config struct { type Config struct {
BirthdayFile string `yaml:"birthday_file"` BirthdayFile string `yaml:"birthday_file"`
BirthdayTemplate string `yaml:"birthday_template"`
Logger *LoggerConfig `yaml:"logger"` Logger *LoggerConfig `yaml:"logger"`
TelegramNotifications *TelegramNotificationsConfig `yaml:"telegram_notifications"` TelegramNotifications *TelegramNotificationsConfig `yaml:"telegram_notifications"`
} }
// ToDo: to be implemented
func (c *Config) IsValid() error { func (c *Config) IsValid() error {
if c.BirthdayFile == "" { if c.BirthdayFile == "" {
return ErrConfigBirthdayFileEmpty return ErrConfigBirthdayFileEmpty

View file

@ -6,6 +6,7 @@ package mocks
import ( import (
reflect "reflect" reflect "reflect"
template "text/template"
model "git.ctrlz.es/mgdelacroix/birthdaybot/model" model "git.ctrlz.es/mgdelacroix/birthdaybot/model"
gomock "github.com/golang/mock/gomock" gomock "github.com/golang/mock/gomock"
@ -35,15 +36,15 @@ func (m *MockNotificationService) EXPECT() *MockNotificationServiceMockRecorder
} }
// Notify mocks base method. // Notify mocks base method.
func (m *MockNotificationService) Notify(arg0 *model.Birthday) error { func (m *MockNotificationService) Notify(arg0 *model.Birthday, arg1 *template.Template) error {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Notify", arg0) ret := m.ctrl.Call(m, "Notify", arg0, arg1)
ret0, _ := ret[0].(error) ret0, _ := ret[0].(error)
return ret0 return ret0
} }
// Notify indicates an expected call of Notify. // Notify indicates an expected call of Notify.
func (mr *MockNotificationServiceMockRecorder) Notify(arg0 interface{}) *gomock.Call { func (mr *MockNotificationServiceMockRecorder) Notify(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Notify", reflect.TypeOf((*MockNotificationService)(nil).Notify), arg0) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Notify", reflect.TypeOf((*MockNotificationService)(nil).Notify), arg0, arg1)
} }

View file

@ -2,9 +2,11 @@
package notification package notification
import ( import (
"text/template"
"git.ctrlz.es/mgdelacroix/birthdaybot/model" "git.ctrlz.es/mgdelacroix/birthdaybot/model"
) )
type NotificationService interface { type NotificationService interface {
Notify(*model.Birthday) error Notify(*model.Birthday, *template.Template) error
} }

View file

@ -1,8 +1,10 @@
package notification package notification
import ( import (
"bytes"
"fmt" "fmt"
"strconv" "strconv"
"text/template"
"git.ctrlz.es/mgdelacroix/birthdaybot/model" "git.ctrlz.es/mgdelacroix/birthdaybot/model"
"github.com/charmbracelet/log" "github.com/charmbracelet/log"
@ -35,9 +37,19 @@ func NewTelegramNotificationService(logger *log.Logger, config *model.TelegramNo
}, nil }, nil
} }
func (tns *TelegramNotificationService) Notify(birthday *model.Birthday) error { func (tns *TelegramNotificationService) Notify(birthday *model.Birthday, template *template.Template) error {
// ToDo: introduce templates here var msgText string
msgText := fmt.Sprintf("It's %s's birthday! You can reach them out at %s or %s", birthday.Name, birthday.Email, birthday.Phone) if template != nil {
var stringBuffer bytes.Buffer
if err := template.Execute(&stringBuffer, birthday.ToMap()); err != nil {
return fmt.Errorf("cannot execute template for birthday: %w", err)
}
msgText = stringBuffer.String()
} else {
msgText = fmt.Sprintf("It's %s's birthday! You can reach them out at %s or %s", birthday.Name, birthday.Email, birthday.Phone)
}
chatID, err := strconv.Atoi(tns.config.ChannelID) chatID, err := strconv.Atoi(tns.config.ChannelID)
if err != nil { if err != nil {
return fmt.Errorf("cannot parse ChannelID: %w", err) return fmt.Errorf("cannot parse ChannelID: %w", err)

1
sample.tmpl Normal file
View file

@ -0,0 +1 @@
¡Mañana es el cumpleaños de {{.Name}}! Puedes felicitarle o bien escribiendo a {{.Email}} o bien llamando al número {{.Phone}}

View file

@ -3,6 +3,7 @@ package server
import ( import (
"errors" "errors"
"fmt" "fmt"
"text/template"
"git.ctrlz.es/mgdelacroix/birthdaybot/model" "git.ctrlz.es/mgdelacroix/birthdaybot/model"
"git.ctrlz.es/mgdelacroix/birthdaybot/notification" "git.ctrlz.es/mgdelacroix/birthdaybot/notification"
@ -22,6 +23,7 @@ type Server struct {
workers []Worker workers []Worker
birthdays []*model.Birthday birthdays []*model.Birthday
notificationServices []notification.NotificationService notificationServices []notification.NotificationService
tmpl *template.Template
} }
func createNotificationServices(logger *log.Logger, config *model.Config) ([]notification.NotificationService, error) { func createNotificationServices(logger *log.Logger, config *model.Config) ([]notification.NotificationService, error) {
@ -84,6 +86,16 @@ func New(options ...Option) (*Server, error) {
srv.workers = []Worker{NewSimpleWorker(srv)} srv.workers = []Worker{NewSimpleWorker(srv)}
} }
if srv.config.BirthdayTemplate != "" {
srv.Logger.Debug("parsing birthday template", "file", srv.config.BirthdayTemplate)
var err error
srv.tmpl, err = template.ParseFiles(srv.config.BirthdayTemplate)
if err != nil {
return nil, fmt.Errorf("cannot parse template file %q: %w", srv.config.BirthdayTemplate, err)
}
}
return srv, nil return srv, nil
} }
@ -106,7 +118,7 @@ func (s *Server) Stop() {
func (s *Server) Notify(birthday *model.Birthday) error { func (s *Server) Notify(birthday *model.Birthday) error {
errs := []error{} errs := []error{}
for _, service := range s.notificationServices { for _, service := range s.notificationServices {
err := service.Notify(birthday) err := service.Notify(birthday, s.tmpl)
if err != nil { if err != nil {
errs = append(errs, err) errs = append(errs, err)
} }