diff --git a/.idea/vcs.xml b/.idea/vcs.xml
index 0b0005f..7e9c2e8 100644
--- a/.idea/vcs.xml
+++ b/.idea/vcs.xml
@@ -9,6 +9,11 @@
+
+
+
diff --git a/main.go b/main.go
index 0a34fff..b71d204 100644
--- a/main.go
+++ b/main.go
@@ -257,7 +257,7 @@ func handler(ctx context.Context, b *tgBot.Bot, update *models.Update, subs *sub
},
}
} else {
- // TODO: Update message to contain unsubscribe button
+ log.Printf("DEBUG: Subscribed: chatID %d, trainNumber %s, date %s, groupIndex %d", update.CallbackQuery.Message.Chat.ID, trainNumber, date.Format("2006-01-02"), groupIndex)
response = &handlers.HandlerResponse{
CallbackAnswer: &tgBot.AnswerCallbackQueryParams{
Text: fmt.Sprintf("Subscribed successfully!"),
@@ -287,7 +287,7 @@ func handler(ctx context.Context, b *tgBot.Bot, update *models.Update, subs *sub
},
}
} else {
- // TODO: Update message to contain unsubscribe button
+ log.Printf("DEBUG: Unsubscribed: chatID %d, trainNumber %s, date %s, groupIndex %d", update.CallbackQuery.Message.Chat.ID, trainNumber, date.Format("2006-01-02"), groupIndex)
response = &handlers.HandlerResponse{
CallbackAnswer: &tgBot.AnswerCallbackQueryParams{
Text: fmt.Sprintf("Unsubscribed successfully!"),
@@ -301,6 +301,9 @@ func handler(ctx context.Context, b *tgBot.Bot, update *models.Update, subs *sub
},
}
}
+
+ default:
+ log.Printf("WARN : Unknown callback query method: %s", splitted[0])
}
}
}
diff --git a/pkg/handlers/findTrain.go b/pkg/handlers/findTrain.go
index 165a392..aa9fad8 100644
--- a/pkg/handlers/findTrain.go
+++ b/pkg/handlers/findTrain.go
@@ -46,6 +46,11 @@ func HandleTrainNumberCommand(ctx context.Context, trainNumber string, date time
Message: &bot.SendMessageParams{
Text: fmt.Sprintf("The train %s was not found.", trainNumber),
},
+ ShouldUnsubscribe: func() bool {
+ now := time.Now().In(utils.Location)
+ midnightYesterday := time.Date(now.Year(), now.Month(), now.Day()-1, 0, 0, 0, 0, utils.Location)
+ return date.Before(midnightYesterday)
+ }(),
}, false
case errors.Is(err, api.ServerError):
log.Printf("ERROR: In handle train number: %s", err.Error())
@@ -53,6 +58,11 @@ func HandleTrainNumberCommand(ctx context.Context, trainNumber string, date time
Message: &bot.SendMessageParams{
Text: fmt.Sprintf("Unknown server error when searching for train %s.", trainNumber),
},
+ ShouldUnsubscribe: func() bool {
+ now := time.Now().In(utils.Location)
+ midnightYesterday := time.Date(now.Year(), now.Month(), now.Day()-1, 0, 0, 0, 0, utils.Location)
+ return date.Before(midnightYesterday)
+ }(),
}, false
default:
log.Printf("ERROR: In handle train number: %s", err.Error())
@@ -63,6 +73,32 @@ func HandleTrainNumberCommand(ctx context.Context, trainNumber string, date time
groupIndex = 0
}
+ shouldUnsubscribe := func() bool {
+ if len(trainData.Groups) <= groupIndex {
+ groupIndex = 0
+ }
+ now := time.Now().In(utils.Location)
+ midnightYesterday := time.Date(now.Year(), now.Month(), now.Day()-1, 0, 0, 0, 0, utils.Location)
+ lastStation := trainData.Groups[groupIndex].
+ Stations[len(trainData.Groups[groupIndex].Stations)-1]
+ if now.After(lastStation.Arrival.
+ ScheduleTime.Add(time.Hour * 6)) {
+ return true
+ }
+ if trainData.Groups[groupIndex].
+ Status != nil && trainData.Groups[groupIndex].
+ Status.Station == lastStation.Name &&
+ trainData.Groups[groupIndex].Status.
+ State == "arrival" {
+ return true
+ }
+ if date.Before(midnightYesterday) {
+ return true
+ }
+
+ return false
+ }()
+
message := bot.SendMessageParams{}
if groupIndex == -1 {
message.Text = fmt.Sprintf("Train %s %s contains multiple groups. Please choose one.", trainData.Rank, trainData.Number)
@@ -136,7 +172,9 @@ func HandleTrainNumberCommand(ctx context.Context, trainNumber string, date time
},
}
buttonKind := TrainInfoResponseButtonIncludeSub
- if isSubscribed {
+ if shouldUnsubscribe {
+ buttonKind = TrainInfoResponseButtonExcludeSub
+ } else if isSubscribed {
buttonKind = TrainInfoResponseButtonIncludeUnsub
}
message.ReplyMarkup = GetTrainNumberCommandResponseButtons(trainData.Number, group.Stations[0].Departure.ScheduleTime, groupIndex, buttonKind)
@@ -153,7 +191,8 @@ func HandleTrainNumberCommand(ctx context.Context, trainNumber string, date time
}
return &HandlerResponse{
- Message: &message,
+ Message: &message,
+ ShouldUnsubscribe: shouldUnsubscribe,
}, true
}
diff --git a/pkg/handlers/response.go b/pkg/handlers/response.go
index 4554ae9..8889100 100644
--- a/pkg/handlers/response.go
+++ b/pkg/handlers/response.go
@@ -8,6 +8,7 @@ type HandlerResponse struct {
CallbackAnswer *bot.AnswerCallbackQueryParams
MessageEdits []*bot.EditMessageTextParams
MessageMarkupEdits []*bot.EditMessageReplyMarkupParams
+ ShouldUnsubscribe bool
Injected struct {
ChatId int64
MessageId int
diff --git a/pkg/subscriptions/subscriptions.go b/pkg/subscriptions/subscriptions.go
index 249f44c..d437097 100644
--- a/pkg/subscriptions/subscriptions.go
+++ b/pkg/subscriptions/subscriptions.go
@@ -98,15 +98,14 @@ func (sub *Subscriptions) DeleteSubscription(chatId int64, messageId int) (*SubD
break
}
}
- var result *SubData
+ var result SubData
if deleteIndex != -1 {
- result = &SubData{}
- *result = datas[deleteIndex]
+ result = datas[deleteIndex]
datas[deleteIndex] = datas[len(datas)-1]
datas = datas[:len(datas)-1]
_, err := database.WriteDB(func(db *gorm.DB) (*gorm.DB, error) {
- db.Delete(result)
+ db.Delete(&result)
return db, db.Error
})
if err != nil {
@@ -120,7 +119,7 @@ func (sub *Subscriptions) DeleteSubscription(chatId int64, messageId int) (*SubD
} else {
sub.data[chatId] = datas
}
- return result, nil
+ return &result, nil
}
func (sub *Subscriptions) CheckSubscriptions(ctx context.Context) {
@@ -142,54 +141,110 @@ type workerData struct {
data SubData
}
+type unsubscribe struct {
+ chatId int64
+ messageId int
+}
+
+type workerResponseData struct {
+ unsubscribe *unsubscribe
+}
+
func (sub *Subscriptions) executeChecks(ctx context.Context) {
sub.mutex.RLock()
- defer sub.mutex.RUnlock()
// Only allow 8 concurrent requests
// TODO: Make configurable instead of hardcoded
workerCount := 8
workerChan := make(chan workerData, workerCount)
- wg := &sync.WaitGroup{}
+ responseChan := make(chan *workerResponseData, workerCount)
+ defer close(responseChan)
for i := 0; i < workerCount; i++ {
- wg.Add(1)
- go checkWorker(ctx, workerChan, wg)
+ go checkWorker(ctx, workerChan, responseChan)
}
+ go func() {
+ for _, datas := range sub.data {
+ for i := range datas {
+ workerChan <- workerData{
+ tgBot: sub.tgBot,
+ data: datas[i],
+ }
+ }
+ }
+ close(workerChan)
+ }()
+
+ responses := make([]*workerResponseData, 0, len(sub.data))
+
for _, datas := range sub.data {
- for i := range datas {
- workerChan <- workerData{
- tgBot: sub.tgBot,
- data: datas[i],
+ for range datas {
+ if resp := <-responseChan; resp != nil && resp.unsubscribe != nil {
+ responses = append(responses, resp)
}
}
}
- close(workerChan)
- wg.Wait()
-}
-func checkWorker(ctx context.Context, workerChan <-chan workerData, wg *sync.WaitGroup) {
- defer wg.Done()
- for wData := range workerChan {
- data := wData.data
- log.Printf("DEBUG: Timer tick, update for chat %d, train %s, date %s, group %d", data.ChatId, data.TrainNumber, data.Date.Format("2006-01-02"), data.GroupIndex)
+ sub.mutex.RUnlock()
- resp, ok := handlers.HandleTrainNumberCommand(ctx, data.TrainNumber, data.Date, data.GroupIndex, true)
-
- if !ok || resp == nil || resp.Message == nil {
- // Silently discard update errors
- log.Printf("DEBUG: Error when updating chat %d, train %s, date %s, group %d", data.ChatId, data.TrainNumber, data.Date.Format("2006-01-02"), data.GroupIndex)
- return
+ for i := range responses {
+ if responses[i].unsubscribe != nil {
+ // Ignore error since this is optional optimisation
+ deletedSub, err := sub.DeleteSubscription(responses[i].unsubscribe.chatId, responses[i].unsubscribe.messageId)
+ if err == nil && deletedSub != nil {
+ _, _ = sub.tgBot.EditMessageReplyMarkup(ctx, &bot.EditMessageReplyMarkupParams{
+ ChatID: responses[i].unsubscribe.chatId,
+ MessageID: responses[i].unsubscribe.messageId,
+ ReplyMarkup: handlers.GetTrainNumberCommandResponseButtons(deletedSub.TrainNumber, deletedSub.Date, deletedSub.GroupIndex, handlers.TrainInfoResponseButtonExcludeSub),
+ })
+ }
}
-
- _, _ = wData.tgBot.EditMessageText(ctx, &bot.EditMessageTextParams{
- ChatID: data.ChatId,
- MessageID: data.MessageId,
- Text: resp.Message.Text,
- ParseMode: resp.Message.ParseMode,
- Entities: resp.Message.Entities,
- DisableWebPagePreview: resp.Message.DisableWebPagePreview,
- ReplyMarkup: resp.Message.ReplyMarkup,
- })
+ }
+}
+
+func checkWorker(ctx context.Context, workerChan <-chan workerData, responseChan chan<- *workerResponseData) {
+ for wData := range workerChan {
+ func() {
+ var response *workerResponseData
+ defer func() {
+ responseChan <- response
+ }()
+ data := wData.data
+ log.Printf("DEBUG: Timer tick, update for chat %d, train %s, date %s, group %d", data.ChatId, data.TrainNumber, data.Date.Format("2006-01-02"), data.GroupIndex)
+
+ resp, ok := handlers.HandleTrainNumberCommand(ctx, data.TrainNumber, data.Date, data.GroupIndex, true)
+
+ if !ok || resp == nil || resp.Message == nil {
+ // Silently discard update errors
+ log.Printf("DEBUG: Error when updating chat %d, train %s, date %s, group %d", data.ChatId, data.TrainNumber, data.Date.Format("2006-01-02"), data.GroupIndex)
+ if resp != nil && resp.ShouldUnsubscribe {
+ response = &workerResponseData{
+ unsubscribe: &unsubscribe{
+ chatId: data.ChatId,
+ messageId: data.MessageId,
+ },
+ }
+ }
+ return
+ }
+
+ _, _ = wData.tgBot.EditMessageText(ctx, &bot.EditMessageTextParams{
+ ChatID: data.ChatId,
+ MessageID: data.MessageId,
+ Text: resp.Message.Text,
+ ParseMode: resp.Message.ParseMode,
+ Entities: resp.Message.Entities,
+ DisableWebPagePreview: resp.Message.DisableWebPagePreview,
+ ReplyMarkup: resp.Message.ReplyMarkup,
+ })
+
+ response = &workerResponseData{}
+ if resp.ShouldUnsubscribe {
+ response.unsubscribe = &unsubscribe{
+ chatId: data.ChatId,
+ messageId: data.MessageId,
+ }
+ }
+ }()
}
}