Adds initial implementation of the Telegram bot

This commit is contained in:
Miguel de la Cruz 2023-07-04 12:29:13 +02:00
parent 38e78cc02b
commit 9bc9e4f60c
7 changed files with 71 additions and 7 deletions

1
.gitignore vendored
View file

@ -1 +1,2 @@
dist
config.yml

View file

@ -38,5 +38,8 @@ $ make run
- [ ] Pass logger to the server through an option
- [ ] Create a configurable template to fill with each notification
- [ ] Create different message systems to use with the bot
- [X] Telegram
- [ ] Email
- [ ] Mattermost
- [ ] Reduce logger verbosity
- [ ] Enjoy!

1
go.mod
View file

@ -13,6 +13,7 @@ require (
github.com/charmbracelet/lipgloss v0.7.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/go-logfmt/logfmt v0.6.0 // indirect
github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/mattn/go-isatty v0.0.18 // indirect
github.com/mattn/go-runewidth v0.0.14 // indirect

2
go.sum
View file

@ -8,6 +8,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4=
github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1 h1:wG8n/XJQ07TmjbITcGiUaOtXxdrINDz1b0J1w0SzqDc=
github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1/go.mod h1:A2S0CWkNylc2phvKXWBBdD3K0iGnDBGbzRpISP2zBl8=
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98=

View file

@ -1,22 +1,52 @@
package notification
import (
"fmt"
"strconv"
"git.ctrlz.es/mgdelacroix/birthdaybot/model"
"github.com/charmbracelet/log"
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
)
type TelegramNotificationService struct {
logger *log.Logger
config *model.TelegramNotificationsConfig
bot *tgbotapi.BotAPI
}
func NewTelegramNotificationService(logger *log.Logger, config *model.TelegramNotificationsConfig) *TelegramNotificationService {
func NewTelegramNotificationService(logger *log.Logger, config *model.TelegramNotificationsConfig) (*TelegramNotificationService, error) {
bot, err := tgbotapi.NewBotAPI(config.BotToken)
if err != nil {
return nil, fmt.Errorf("cannot create bot: %w", err)
}
botUser, err := bot.GetMe()
if err != nil {
return nil, fmt.Errorf("cannot get bot information: %w", err)
}
logger.Info("telegram bot initialized", "id", botUser.ID, "username", botUser.UserName)
return &TelegramNotificationService{
logger: logger,
config: config,
}
bot: bot,
}, nil
}
func (tns *TelegramNotificationService) Notify(birthday *model.Birthday) error {
// ToDo: introduce templates here
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)
if err != nil {
return fmt.Errorf("cannot parse ChannelID: %w", err)
}
msg := tgbotapi.NewMessage(int64(chatID), msgText)
if _, err := tns.bot.Send(msg); err != nil {
return fmt.Errorf("error sending message: %w", err)
}
return nil
}

View file

@ -25,7 +25,10 @@ func createNotificationServices(logger *log.Logger, config *model.Config) ([]not
notificationServices := []notification.NotificationService{}
if config.TelegramNotifications != nil {
telegramNotificationService := notification.NewTelegramNotificationService(logger, config.TelegramNotifications)
telegramNotificationService, err := notification.NewTelegramNotificationService(logger, config.TelegramNotifications)
if err != nil {
return nil, fmt.Errorf("cannot create telegram notification service: %w", err)
}
notificationServices = append(notificationServices, telegramNotificationService)
}
@ -61,9 +64,9 @@ func New(config *model.Config) (*Server, error) {
Logger: logger,
config: config,
birthdays: birthdays,
workers: []*Worker{NewWorker(logger)},
notificationServices: notificationServices,
}
server.workers = []*Worker{NewWorker(server)}
return server, nil
}
@ -83,3 +86,22 @@ func (s *Server) Stop() {
}
s.Logger.Info("server stopped", "workers", len(s.workers))
}
func (s *Server) Notify(birthday *model.Birthday) error {
errs := []error{}
for _, service := range s.notificationServices {
err := service.Notify(birthday)
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
}

View file

@ -7,14 +7,16 @@ import (
)
type Worker struct {
server *Server
logger *log.Logger
stop chan bool
stopped chan bool
}
func NewWorker(logger *log.Logger) *Worker {
func NewWorker(server *Server) *Worker {
return &Worker{
logger: logger,
server: server,
logger: server.Logger,
stop: make(chan bool, 1),
stopped: make(chan bool, 1),
}
@ -39,6 +41,9 @@ func (w *Worker) run() {
select {
case t := <-ticker.C:
w.logger.Info("ticker ticked for worker", "tick", t)
// ToDo: improve logic here
b := w.server.Birthdays()[0]
w.server.Notify(b)
case <-w.stop:
w.logger.Info("received stop signal")
w.stopped <- true