Skip to content
This repository has been archived by the owner on Feb 20, 2023. It is now read-only.

add webhook support #24

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,7 @@ All parameters can be overwritten by ENV variable.
"path": "host=myhost port=myport user=gorm dbname=gorm password=mypassword"
},
"loglevel": "trace",
"metrics": false
}
"metrics": false,
```

- `API_ENDPOINT` marudor endpoint
Expand Down
33 changes: 23 additions & 10 deletions cmd/bot.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,16 @@ import (
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"

"github.com/pkuebler/bahn-bot/pkg/application"
"github.com/pkuebler/bahn-bot/pkg/config"
"github.com/pkuebler/bahn-bot/pkg/infrastructure/marudor"
"github.com/pkuebler/bahn-bot/pkg/infrastructure/repository"
"github.com/pkuebler/bahn-bot/pkg/infrastructure/telegramconversation"
"github.com/pkuebler/bahn-bot/pkg/interface/cron"
"github.com/pkuebler/bahn-bot/pkg/interface/telegram"
"github.com/pkuebler/bahn-bot/pkg/metrics"
trainalarmApplication "github.com/pkuebler/bahn-bot/pkg/trainalarms/application"
trainalarmRepository "github.com/pkuebler/bahn-bot/pkg/trainalarms/repository"
webhookApplication "github.com/pkuebler/bahn-bot/pkg/webhooks/application"
webhookRepository "github.com/pkuebler/bahn-bot/pkg/webhooks/repository"
)

// NewBotCmd create a command to start the bot
Expand Down Expand Up @@ -66,16 +68,19 @@ func BotCommand(ctx context.Context, cmd *cobra.Command, args []string) {
hafas := api.HafasService

// storage
// repo := repository.NewMemoryDatabase()
repo := repository.NewSQLDatabase(cfg.Database.Dialect, cfg.Database.Path)
defer repo.Close()
// repo := trainalarmRepository.NewMemoryDatabase()
trainalarmRepo := trainalarmRepository.NewSQLDatabase(cfg.Database.Dialect, cfg.Database.Path)
defer trainalarmRepo.Close()
webhookRepo := webhookRepository.NewSQLDatabase(cfg.Database.Dialect, cfg.Database.Path)
defer webhookRepo.Close()

// application
app := application.NewApplication(hafas, repo, log)
trainalarmApp := trainalarmApplication.NewApplication(hafas, trainalarmRepo, log)
webhookApp := webhookApplication.NewApplication(trainalarmRepo, webhookRepo, log)

// interfaces
service := telegram.NewTelegramService(log, repo, app, hafas)
cronService := cron.NewCronJob(log, app, cfg.EnableMetrics)
service := telegram.NewTelegramService(log, cfg, trainalarmRepo, webhookRepo, webhookApp, trainalarmApp, hafas)
cronService := cron.NewCronJob(log, trainalarmApp, cfg.EnableMetrics)

// conversationengine
router := telegramconversation.NewConversationRouter("start")
Expand All @@ -89,6 +94,14 @@ func BotCommand(ctx context.Context, cmd *cobra.Command, args []string) {
router.OnState("start", service.Start)
router.OnState("cancel", service.Cancel)

router.OnCommand("webhooks", "listwebhooks")
router.OnState("listwebhooks", service.Webhooks)
router.OnState("webhook", service.WebhookMenu)
router.OnState("deletewebhook", service.DeleteWebhook)

router.OnCommand("newwebhook", "newwebhook")
router.OnState("newwebhook", service.NewWebhook)

router.OnCommand("myalarms", "listalarms")
router.OnState("listalarms", service.ListTrainAlarms)
router.OnState("alarm", service.AlarmMenu)
Expand Down Expand Up @@ -149,7 +162,7 @@ func BotCommand(ctx context.Context, cmd *cobra.Command, args []string) {

tctx.SetMessage(update.Message.Text)

if state, payload, err := repo.GetState(ctx, tctx.ChatID(), "telegram"); err == nil {
if state, payload, err := trainalarmRepo.GetState(ctx, tctx.ChatID(), "telegram"); err == nil {
log.Tracef("[%d] Load State %s (Payload: %s)", tctx.MessageID(), state, payload)
tctx.SetStatePayload(payload)
tctx.ChangeState(state)
Expand Down Expand Up @@ -212,7 +225,7 @@ func BotCommand(ctx context.Context, cmd *cobra.Command, args []string) {
bot.DeleteMessage(tgbotapi.NewDeleteMessage(tctx.ChatID64(), tctx.DeletedMessageID()))
}

repo.UpdateState(ctx, tctx.ChatID(), "telegram", func(state string, payload string) (string, string, error) {
trainalarmRepo.UpdateState(ctx, tctx.ChatID(), "telegram", func(state string, payload string) (string, string, error) {
msgLog.Tracef("Save State %s with payload `%s` (old: %s / `%s`)", tctx.State(), tctx.StatePayload(), state, payload)
return tctx.State(), tctx.StatePayload(), nil
})
Expand Down
4 changes: 2 additions & 2 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ services:
- POSTGRES_USER=bahn-bot
- POSTGRES_PASSWORD=supersecretpassword
- POSTGRES_DB=bahn-bot
volumes:
- ./data:/var/lib/postgresql/data
# volumes:
# - ./data:/var/lib/postgresql/data

# mysql:
# image: mysql:5.7
Expand Down
18 changes: 18 additions & 0 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,20 @@ type DatabaseConfig struct {
Path string `env:"DB_PATH" json:"path"`
}

// WebhookConfig connection data
type WebhookConfig struct {
Endpoint string `env:"WEBHOOK_ENDPOINT" json:"endpoint"`
Port string `env:"WEBHOOK_PORT" json:"port"`
}

// Config contains the complete service configuration
type Config struct {
APIConfig APIConfig `json:"api"`
Database DatabaseConfig `json:"database"`
Telegram TelegramConfig `json:"telegram"`
LogLevel string `env:"LOG_LEVEL" json:"loglevel"`
EnableMetrics bool `json:"enable_metrics"`
Webhook WebhookConfig `json:"webhook"`
}

// NewTestConfig return a config object with test settings
Expand All @@ -40,6 +47,9 @@ func NewTestConfig() *Config {
APIEndpoint: "https://marudor.de/api",
},
LogLevel: "trace",
Webhook: WebhookConfig{
Endpoint: "http://localhost/hook",
},
}
}

Expand Down Expand Up @@ -73,6 +83,14 @@ func ReadConfig(file string, log *logrus.Entry) *Config {
config.APIConfig.APIEndpoint = "https://marudor.de/api"
}

if config.Webhook.Endpoint == "" {
panic("Need WEBHOOK_ENDPOINT")
}

if config.Webhook.Port == "" {
config.Webhook.Port = ":8080"
}

return &config
}

Expand Down
9 changes: 5 additions & 4 deletions pkg/interface/cron/cron.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@ import (
"fmt"
"time"

"github.com/pkuebler/bahn-bot/pkg/application"
"github.com/pkuebler/bahn-bot/pkg/domain/trainalarm"
"github.com/sirupsen/logrus"

"github.com/pkuebler/bahn-bot/pkg/infrastructure/marudor"
"github.com/pkuebler/bahn-bot/pkg/infrastructure/telegramconversation"
"github.com/sirupsen/logrus"
"github.com/pkuebler/bahn-bot/pkg/trainalarms/application"
"github.com/pkuebler/bahn-bot/pkg/trainalarms/domain"
)

// CronJob triggers applications
Expand Down Expand Up @@ -67,7 +68,7 @@ func (c *CronJob) ClearDatabase(ctx context.Context) {

// NotifyUsers about train delays
func (c *CronJob) NotifyUsers(ctx context.Context) {
c.application.NotifyUsers(ctx, func(ctx context.Context, alarm *trainalarm.TrainAlarm, train marudor.HafasTrain, diff time.Duration) error {
c.application.NotifyUsers(ctx, func(ctx context.Context, alarm *domain.TrainAlarm, train marudor.HafasTrain, diff time.Duration) error {
tctx := telegramconversation.NewTContext(alarm.GetIdentifyer())

if c.metrics != nil {
Expand Down
4 changes: 2 additions & 2 deletions pkg/interface/telegram/deletealarm.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import (
"context"
"fmt"

"github.com/pkuebler/bahn-bot/pkg/application"
"github.com/pkuebler/bahn-bot/pkg/infrastructure/telegramconversation"
"github.com/pkuebler/bahn-bot/pkg/trainalarms/application"
)

// DeleteAlarm from database
Expand All @@ -22,7 +22,7 @@ func (t *TelegramService) DeleteAlarm(ctx telegramconversation.TContext) telegra
cmd := application.DeleteTrainAlarmCmd{
AlarmID: ctx.ButtonData(),
}
alarm, _ := t.application.DeleteTrainAlarm(context.Background(), cmd)
alarm, _ := t.trainalarmApp.DeleteTrainAlarm(context.Background(), cmd)

return ctx.SendWithState(fmt.Sprintf("Alarm `%s > %s` gelöscht.", alarm.GetTrainName(), alarm.GetFinalDestinationName()), "start")
}
28 changes: 28 additions & 0 deletions pkg/interface/telegram/deletewebhook.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package telegram

import (
"context"
"fmt"

"github.com/pkuebler/bahn-bot/pkg/infrastructure/telegramconversation"
"github.com/pkuebler/bahn-bot/pkg/webhooks/application"
)

// DeleteWebhook from database
func (t *TelegramService) DeleteWebhook(ctx telegramconversation.TContext) telegramconversation.TContext {
log := ctx.LogFields(t.log)
log.Trace("DeleteWebhook()")

if !ctx.IsButtonPressed() {
return ctx.SendWithState("Irgendetwas ist schief gelaufen. :/", "start")
}

ctx.DeleteMessage(ctx.MessageID())

cmd := application.DeleteWebhookCmd{
WebhookID: ctx.ButtonData(),
}
hook, _ := t.webhookApp.DeleteWebhook(context.Background(), cmd)

return ctx.SendWithState(fmt.Sprintf("Webhook `%s: %s` gelöscht.", hook.GetProtocol(), hook.GetURLHash()), "start")
}
3 changes: 0 additions & 3 deletions pkg/interface/telegram/listtrainalarms.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,6 @@ func (t *TelegramService) ListTrainAlarms(ctx telegramconversation.TContext) tel
}

if len(alarms) == 0 {
if ctx.IsButtonPressed() {
ctx.DeleteMessage(ctx.MessageID())
}
return ctx.SendWithState("Du beobachtest noch keine Züge. /help", "start")
}

Expand Down
39 changes: 39 additions & 0 deletions pkg/interface/telegram/newwebhook.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package telegram

import (
"context"
"fmt"
"net/url"
"path"

"github.com/pkuebler/bahn-bot/pkg/infrastructure/telegramconversation"
"github.com/pkuebler/bahn-bot/pkg/webhooks/application"
"github.com/pkuebler/bahn-bot/pkg/webhooks/domain"
)

// NewWebhook generates a webhook that Travelynx uses to automatically set alarms for current trains.
func (t *TelegramService) NewWebhook(ctx telegramconversation.TContext) telegramconversation.TContext {
log := ctx.LogFields(t.log)
log.Trace("NewWebhook()")

cmd := application.AddWebhookCmd{
Identifyer: ctx.ChatID(),
Plattform: "telegram",
Protocol: string(domain.TravelynxProtocol),
}

hook, err := t.webhookApp.AddWebhook(context.Background(), cmd)
if err != nil {
log.Error(err)
return ctx.SendWithState("Irgendetwas ist schief gelaufen. :/", "start")
}

u, _ := url.Parse(t.config.Webhook.Endpoint)
u.Path = path.Join(u.Path, hook.GetURLHash())
txt := fmt.Sprintf(`Neuer Webhook angelegt.

Um automatisch Alarme angelegt zu bekommen, müssen unter https://travelynx.de/account/hooks folgende Daten hinterlegt werden:

URL: `+"`%s`", u.String())
return ctx.SendWithState(txt, "start")
}
4 changes: 2 additions & 2 deletions pkg/interface/telegram/savealarm.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import (
"context"
"fmt"

"github.com/pkuebler/bahn-bot/pkg/application"
"github.com/pkuebler/bahn-bot/pkg/infrastructure/telegramconversation"
"github.com/pkuebler/bahn-bot/pkg/trainalarms/application"
)

// SaveAlarm to database
Expand Down Expand Up @@ -36,7 +36,7 @@ func (t *TelegramService) SaveAlarm(ctx telegramconversation.TContext) telegramc
StationDate: stationDate,
}

err = t.application.AddTrainAlarm(context.Background(), cmd)
err = t.trainalarmApp.AddTrainAlarm(context.Background(), cmd)
if err != nil {
log.Error(err)
return ctx.SendWithState("Irgendetwas ist schief gelaufen. :/", "start")
Expand Down
4 changes: 2 additions & 2 deletions pkg/interface/telegram/savedelay.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import (
"strconv"
"time"

"github.com/pkuebler/bahn-bot/pkg/application"
"github.com/pkuebler/bahn-bot/pkg/infrastructure/telegramconversation"
"github.com/pkuebler/bahn-bot/pkg/trainalarms/application"
)

var (
Expand Down Expand Up @@ -52,7 +52,7 @@ func (t *TelegramService) SaveDelay(ctx telegramconversation.TContext) telegramc
ThresholdMinutes: int(thresholdMinutes.Minutes()),
}

err = t.application.UpdateTrainAlarmThreshold(context.Background(), cmd)
err = t.trainalarmApp.UpdateTrainAlarmThreshold(context.Background(), cmd)
if err != nil {
log.Error(err)
return ctx.SendWithState("Irgendetwas ist schief gelaufen. :/", "start")
Expand Down
3 changes: 3 additions & 0 deletions pkg/interface/telegram/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ func (t *TelegramService) Start(ctx telegramconversation.TContext) telegramconve
/myalarms Bearbeite deine Alarme
/newalarm Erzeuge neuen Alarm

/webhooks Bearbeite deine Webhooks
/newwebhook Verknüpfe travelynx.de

/cancel Breche aktuelle Option ab `)

ctx.Send(txt)
Expand Down
42 changes: 31 additions & 11 deletions pkg/interface/telegram/telegram.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,23 @@ import (
"strings"
"time"

"github.com/pkuebler/bahn-bot/pkg/application"
"github.com/pkuebler/bahn-bot/pkg/domain/trainalarm"
"github.com/pkuebler/bahn-bot/pkg/infrastructure/marudor"
"github.com/sirupsen/logrus"

"github.com/pkuebler/bahn-bot/pkg/config"
"github.com/pkuebler/bahn-bot/pkg/infrastructure/marudor"
trainalarmApplication "github.com/pkuebler/bahn-bot/pkg/trainalarms/application"
trainalarmDomain "github.com/pkuebler/bahn-bot/pkg/trainalarms/domain"
webhookApplication "github.com/pkuebler/bahn-bot/pkg/webhooks/application"
webhookDomain "github.com/pkuebler/bahn-bot/pkg/webhooks/domain"
)

// Application with business logic
type Application interface {
DeleteTrainAlarm(ctx context.Context, cmd application.DeleteTrainAlarmCmd) (*trainalarm.TrainAlarm, error)
AddTrainAlarm(ctx context.Context, cmd application.AddTrainAlarmCmd) error
UpdateTrainAlarmThreshold(ctx context.Context, cmd application.UpdateTrainAlarmThresholdCmd) error
DeleteTrainAlarm(ctx context.Context, cmd trainalarmApplication.DeleteTrainAlarmCmd) (*trainalarmDomain.TrainAlarm, error)
AddTrainAlarm(ctx context.Context, cmd trainalarmApplication.AddTrainAlarmCmd) error
UpdateTrainAlarmThreshold(ctx context.Context, cmd trainalarmApplication.UpdateTrainAlarmThresholdCmd) error
AddWebhook(ctx context.Context, cmd webhookApplication.AddWebhookCmd) (*webhookDomain.Webhook, error)
DeleteWebhook(ctx context.Context, cmd webhookApplication.DeleteWebhookCmd) (*webhookDomain.Webhook, error)
}

// HafasService to request train informations
Expand All @@ -28,17 +34,31 @@ type HafasService interface {
// TelegramService to handle requests
type TelegramService struct {
log *logrus.Entry
trainAlarmRepository trainalarm.Repository
application Application
config *config.Config
trainAlarmRepository trainalarmDomain.Repository
trainalarmApp *trainalarmApplication.Application
webhookRepository webhookDomain.Repository
webhookApp *webhookApplication.Application
hafas HafasService
}

// NewTelegramService to create a new service
func NewTelegramService(log *logrus.Entry, repository trainalarm.Repository, application Application, hafas HafasService) *TelegramService {
func NewTelegramService(
log *logrus.Entry,
cfg *config.Config,
trainAlarmRepository trainalarmDomain.Repository,
webhookRepository webhookDomain.Repository,
webhookApp *webhookApplication.Application,
trainalarmApp *trainalarmApplication.Application,
hafas HafasService,
) *TelegramService {
return &TelegramService{
log: log.WithField("service", "telegram"),
trainAlarmRepository: repository,
application: application,
config: cfg,
trainAlarmRepository: trainAlarmRepository,
trainalarmApp: trainalarmApp,
webhookRepository: webhookRepository,
webhookApp: webhookApp,
hafas: hafas,
}
}
Expand Down
Loading