From 9bc9e4f60c1df7b611c0f9e802daa1c11fa67fa7 Mon Sep 17 00:00:00 2001 From: Miguel de la Cruz Date: Tue, 4 Jul 2023 12:29:13 +0200 Subject: [PATCH] Adds initial implementation of the Telegram bot --- .gitignore | 3 ++- README.md | 3 +++ go.mod | 1 + go.sum | 2 ++ notification/service_telegram.go | 34 ++++++++++++++++++++++++++++++-- server/server.go | 26 ++++++++++++++++++++++-- server/worker.go | 9 +++++++-- 7 files changed, 71 insertions(+), 7 deletions(-) diff --git a/.gitignore b/.gitignore index 53c37a1..fc2e460 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -dist \ No newline at end of file +dist +config.yml \ No newline at end of file diff --git a/README.md b/README.md index e63c643..049e241 100644 --- a/README.md +++ b/README.md @@ -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! diff --git a/go.mod b/go.mod index 67604fe..83e1bd2 100644 --- a/go.mod +++ b/go.mod @@ -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 diff --git a/go.sum b/go.sum index e358a0a..69b7a3d 100644 --- a/go.sum +++ b/go.sum @@ -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= diff --git a/notification/service_telegram.go b/notification/service_telegram.go index 1b98c6e..77d7a68 100644 --- a/notification/service_telegram.go +++ b/notification/service_telegram.go @@ -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 } diff --git a/server/server.go b/server/server.go index 174c479..8074e40 100644 --- a/server/server.go +++ b/server/server.go @@ -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 +} diff --git a/server/worker.go b/server/worker.go index 805f86c..20ec18c 100644 --- a/server/worker.go +++ b/server/worker.go @@ -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