mirror of
https://github.com/dancojocaru2000/CfrTrainInfoTelegramBot.git
synced 2025-06-19 10:42:32 +03:00
Compare commits
12 commits
fa20c381a3
...
c985a6575a
Author | SHA1 | Date | |
---|---|---|---|
c985a6575a | |||
55bfd56f81 | |||
77260f973d | |||
e4f12c766e | |||
af5a3a51de | |||
24645aea81 | |||
037e7d03dc | |||
69a0cf3a01 | |||
97e6d78ce6 | |||
938edab3d6 | |||
6b3b5bb3c6 | |||
be940ffebe |
8 changed files with 311 additions and 45 deletions
25
.dockerignore
Normal file
25
.dockerignore
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
### Go template
|
||||||
|
# If you prefer the allow list template instead of the deny list, see community template:
|
||||||
|
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
|
||||||
|
#
|
||||||
|
# Binaries for programs and plugins
|
||||||
|
*.exe
|
||||||
|
*.exe~
|
||||||
|
*.dll
|
||||||
|
*.so
|
||||||
|
*.dylib
|
||||||
|
|
||||||
|
# Test binary, built with `go test -c`
|
||||||
|
*.test
|
||||||
|
|
||||||
|
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||||
|
*.out
|
||||||
|
|
||||||
|
# Dependency directories (remove the comment below to include it)
|
||||||
|
# vendor/
|
||||||
|
|
||||||
|
# Go workspace file
|
||||||
|
go.work
|
||||||
|
|
||||||
|
# Also ignore the Dockerfile
|
||||||
|
Dockerfile
|
30
.github/workflows/build-latest-image.yml
vendored
Normal file
30
.github/workflows/build-latest-image.yml
vendored
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
name: Build latest Docker image
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
jobs:
|
||||||
|
docker:
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
packages: write
|
||||||
|
runs-on:
|
||||||
|
- ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
- name: Login to GitHub Container Registry
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
- name: Build and push
|
||||||
|
uses: docker/build-push-action@v5
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
platforms: linux/amd64,linux/arm64
|
||||||
|
push: true
|
||||||
|
tags: ghcr.io/${{ github.actor }}/cfr_train_info_telegram_bot:latest
|
5
.idea/vcs.xml
generated
5
.idea/vcs.xml
generated
|
@ -9,6 +9,11 @@
|
||||||
</inspection_tool>
|
</inspection_tool>
|
||||||
</profile>
|
</profile>
|
||||||
</component>
|
</component>
|
||||||
|
<component name="GitSharedSettings">
|
||||||
|
<option name="FORCE_PUSH_PROHIBITED_PATTERNS">
|
||||||
|
<list />
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
<component name="VcsDirectoryMappings">
|
<component name="VcsDirectoryMappings">
|
||||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||||
</component>
|
</component>
|
||||||
|
|
36
Dockerfile
Normal file
36
Dockerfile
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
FROM --platform=$BUILDPLATFORM tonistiigi/xx AS xx
|
||||||
|
|
||||||
|
FROM --platform=$BUILDPLATFORM golang:1.21-alpine AS build
|
||||||
|
|
||||||
|
LABEL authors="kbruen"
|
||||||
|
LABEL org.opencontainers.image.source=https://github.com/dancojocaru2000/CfrTrainInfoTelegramBot
|
||||||
|
|
||||||
|
RUN apk add tzdata
|
||||||
|
|
||||||
|
RUN echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/testing" >> /etc/apk/repositories
|
||||||
|
RUN apk add zig@testing
|
||||||
|
COPY --from=xx / /
|
||||||
|
ARG TARGETPLATFORM
|
||||||
|
ENV CGO_ENABLED=1
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY go.mod .
|
||||||
|
COPY go.sum .
|
||||||
|
RUN go mod download
|
||||||
|
|
||||||
|
COPY main.go ./
|
||||||
|
COPY pkg ./pkg/
|
||||||
|
ARG TARGETOS
|
||||||
|
ARG TARGETARCH
|
||||||
|
RUN GOOS=$TARGETOS GOARCH=$TARGETARCH CGO_CFLAGS="-D_LARGEFILE64_SOURCE" CC="zig cc -target $(xx-info march)-$(xx-info os)-$(xx-info libc)" CXX="zig c++ -target $(xx-info march)-$(xx-info os)-$(xx-info libc)" go build -o server && xx-verify server
|
||||||
|
|
||||||
|
FROM scratch
|
||||||
|
COPY --from=build /etc/ssl/certs /etc/ssl/certs
|
||||||
|
COPY --from=build /usr/share/zoneinfo /usr/share/zoneinfo
|
||||||
|
WORKDIR /app
|
||||||
|
# COPY static ./static/
|
||||||
|
COPY --from=build /app/server ./
|
||||||
|
|
||||||
|
ENV DEBUG=false
|
||||||
|
ENTRYPOINT [ "/app/server" ]
|
19
main.go
19
main.go
|
@ -44,7 +44,7 @@ You may use ` + cancelCommand + ` to cancel any ongoing command.`
|
||||||
You may also send the date as a message in the following formats: dd.mm.yyyy, m/d/yyyy, yyyy-mm-dd, UNIX timestamp.
|
You may also send the date as a message in the following formats: dd.mm.yyyy, m/d/yyyy, yyyy-mm-dd, UNIX timestamp.
|
||||||
|
|
||||||
Keep in mind that, for night trains, this date might be yesterday.`
|
Keep in mind that, for night trains, this date might be yesterday.`
|
||||||
invalidDateMessage = "Invalid date. Please try again or us " + cancelCommand + " to cancel."
|
invalidDateMessage = "Invalid date. Please try again or use " + cancelCommand + " to cancel."
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
@ -59,7 +59,13 @@ func main() {
|
||||||
log.Fatal("ERROR: No bot token supplied; supply with CFR_BOT.TOKEN")
|
log.Fatal("ERROR: No bot token supplied; supply with CFR_BOT.TOKEN")
|
||||||
}
|
}
|
||||||
|
|
||||||
db, err := gorm.Open(sqlite.Open("bot_db.sqlite"), &gorm.Config{})
|
dbPath := os.Getenv("CFR_BOT.DB_PATH")
|
||||||
|
dbPath = strings.TrimSpace(dbPath)
|
||||||
|
if len(dbPath) == 0 {
|
||||||
|
dbPath = "bot_db.sqlite"
|
||||||
|
}
|
||||||
|
log.Printf("INFO : DB Path: %s\n", dbPath)
|
||||||
|
db, err := gorm.Open(sqlite.Open(dbPath), &gorm.Config{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
@ -257,7 +263,7 @@ func handler(ctx context.Context, b *tgBot.Bot, update *models.Update, subs *sub
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
} else {
|
} 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{
|
response = &handlers.HandlerResponse{
|
||||||
CallbackAnswer: &tgBot.AnswerCallbackQueryParams{
|
CallbackAnswer: &tgBot.AnswerCallbackQueryParams{
|
||||||
Text: fmt.Sprintf("Subscribed successfully!"),
|
Text: fmt.Sprintf("Subscribed successfully!"),
|
||||||
|
@ -287,7 +293,7 @@ func handler(ctx context.Context, b *tgBot.Bot, update *models.Update, subs *sub
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
} else {
|
} 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{
|
response = &handlers.HandlerResponse{
|
||||||
CallbackAnswer: &tgBot.AnswerCallbackQueryParams{
|
CallbackAnswer: &tgBot.AnswerCallbackQueryParams{
|
||||||
Text: fmt.Sprintf("Unsubscribed successfully!"),
|
Text: fmt.Sprintf("Unsubscribed successfully!"),
|
||||||
|
@ -301,6 +307,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])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -334,7 +343,7 @@ func handleFindTrainStages(ctx context.Context, b *tgBot.Bot, update *models.Upd
|
||||||
groupIndex := -1
|
groupIndex := -1
|
||||||
|
|
||||||
if len(commandParams) > 1 {
|
if len(commandParams) > 1 {
|
||||||
date, _ = time.Parse(time.RFC3339, commandParams[1])
|
date, _ = utils.ParseDate(commandParams[1])
|
||||||
}
|
}
|
||||||
if len(commandParams) > 2 {
|
if len(commandParams) > 2 {
|
||||||
groupIndex, _ = strconv.Atoi(commandParams[2])
|
groupIndex, _ = strconv.Atoi(commandParams[2])
|
||||||
|
|
|
@ -2,6 +2,7 @@ package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"dcdev.ro/CfrTrainInfoTelegramBot/pkg/utils"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
@ -46,6 +47,11 @@ func HandleTrainNumberCommand(ctx context.Context, trainNumber string, date time
|
||||||
Message: &bot.SendMessageParams{
|
Message: &bot.SendMessageParams{
|
||||||
Text: fmt.Sprintf("The train %s was not found.", trainNumber),
|
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
|
}, false
|
||||||
case errors.Is(err, api.ServerError):
|
case errors.Is(err, api.ServerError):
|
||||||
log.Printf("ERROR: In handle train number: %s", err.Error())
|
log.Printf("ERROR: In handle train number: %s", err.Error())
|
||||||
|
@ -53,6 +59,11 @@ func HandleTrainNumberCommand(ctx context.Context, trainNumber string, date time
|
||||||
Message: &bot.SendMessageParams{
|
Message: &bot.SendMessageParams{
|
||||||
Text: fmt.Sprintf("Unknown server error when searching for train %s.", trainNumber),
|
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
|
}, false
|
||||||
default:
|
default:
|
||||||
log.Printf("ERROR: In handle train number: %s", err.Error())
|
log.Printf("ERROR: In handle train number: %s", err.Error())
|
||||||
|
@ -63,6 +74,35 @@ func HandleTrainNumberCommand(ctx context.Context, trainNumber string, date time
|
||||||
groupIndex = 0
|
groupIndex = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
shouldUnsubscribe := func() bool {
|
||||||
|
if groupIndex == -1 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
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{}
|
message := bot.SendMessageParams{}
|
||||||
if groupIndex == -1 {
|
if groupIndex == -1 {
|
||||||
message.Text = fmt.Sprintf("Train %s %s contains multiple groups. Please choose one.", trainData.Rank, trainData.Number)
|
message.Text = fmt.Sprintf("Train %s %s contains multiple groups. Please choose one.", trainData.Rank, trainData.Number)
|
||||||
|
@ -97,6 +137,68 @@ func HandleTrainNumberCommand(ctx context.Context, trainNumber string, date time
|
||||||
|
|
||||||
messageText.WriteString(fmt.Sprintf("Date: %s\n", trainData.Date))
|
messageText.WriteString(fmt.Sprintf("Date: %s\n", trainData.Date))
|
||||||
messageText.WriteString(fmt.Sprintf("Operator: %s\n", trainData.Operator))
|
messageText.WriteString(fmt.Sprintf("Operator: %s\n", trainData.Operator))
|
||||||
|
nextStopIdx := -1
|
||||||
|
for i, station := range group.Stations {
|
||||||
|
if station.Arrival != nil && time.Now().Before(station.Arrival.ScheduleTime.Add(func() time.Duration {
|
||||||
|
if station.Arrival.Status != nil {
|
||||||
|
return time.Minute * time.Duration(station.Arrival.Status.Delay)
|
||||||
|
} else {
|
||||||
|
return time.Nanosecond * 0
|
||||||
|
}
|
||||||
|
}())) {
|
||||||
|
nextStopIdx = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if station.Departure != nil && time.Now().Before(station.Departure.ScheduleTime.Add(func() time.Duration {
|
||||||
|
if station.Departure.Status != nil {
|
||||||
|
return time.Minute * time.Duration(station.Departure.Status.Delay)
|
||||||
|
} else {
|
||||||
|
return time.Nanosecond * 0
|
||||||
|
}
|
||||||
|
}())) {
|
||||||
|
nextStopIdx = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if nextStopIdx != -1 {
|
||||||
|
nextStop := &group.Stations[nextStopIdx]
|
||||||
|
arrTime := func() *time.Time {
|
||||||
|
if nextStop.Arrival == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if nextStop.Arrival.Status != nil {
|
||||||
|
result := nextStop.Arrival.ScheduleTime.Add(time.Minute * time.Duration(nextStop.Arrival.Status.Delay))
|
||||||
|
return &result
|
||||||
|
}
|
||||||
|
return &nextStop.Arrival.ScheduleTime
|
||||||
|
}()
|
||||||
|
if arrTime != nil && time.Now().Before(*arrTime) {
|
||||||
|
arrStr := "less than 1m"
|
||||||
|
arrDiff := arrTime.Sub(time.Now())
|
||||||
|
if arrDiff/time.Hour >= 1 {
|
||||||
|
arrStr = fmt.Sprintf("%dh%dm", arrDiff/time.Hour, (arrDiff%time.Hour)/time.Minute)
|
||||||
|
} else if arrDiff/time.Minute >= 1 {
|
||||||
|
arrStr = fmt.Sprintf("%dm", arrDiff/time.Minute)
|
||||||
|
}
|
||||||
|
messageText.WriteString(fmt.Sprintf("Next stop: %s, arriving in %s at %s\n", nextStop.Name, arrStr, arrTime.In(utils.Location).Format("15:04")))
|
||||||
|
} else {
|
||||||
|
depStr := "less than 1m"
|
||||||
|
depTime := nextStop.Departure.ScheduleTime.Add(func() time.Duration {
|
||||||
|
if nextStop.Departure.Status != nil {
|
||||||
|
return time.Minute * time.Duration(nextStop.Departure.Status.Delay)
|
||||||
|
} else {
|
||||||
|
return time.Nanosecond * 0
|
||||||
|
}
|
||||||
|
}())
|
||||||
|
depDiff := depTime.Sub(time.Now())
|
||||||
|
if depDiff/time.Hour >= 1 {
|
||||||
|
depStr = fmt.Sprintf("%dh%dm", depDiff/time.Hour, (depDiff%time.Hour)/time.Minute)
|
||||||
|
} else if depDiff/time.Minute >= 1 {
|
||||||
|
depStr = fmt.Sprintf("%dm", depDiff/time.Minute)
|
||||||
|
}
|
||||||
|
messageText.WriteString(fmt.Sprintf("Currently stopped at: %s, departing in %s at %s\n", nextStop.Name, depStr, depTime.In(utils.Location).Format("15:04")))
|
||||||
|
}
|
||||||
|
}
|
||||||
if group.Status != nil {
|
if group.Status != nil {
|
||||||
messageText.WriteString("Status: ")
|
messageText.WriteString("Status: ")
|
||||||
if group.Status.Delay == 0 {
|
if group.Status.Delay == 0 {
|
||||||
|
@ -136,7 +238,9 @@ func HandleTrainNumberCommand(ctx context.Context, trainNumber string, date time
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
buttonKind := TrainInfoResponseButtonIncludeSub
|
buttonKind := TrainInfoResponseButtonIncludeSub
|
||||||
if isSubscribed {
|
if shouldUnsubscribe {
|
||||||
|
buttonKind = TrainInfoResponseButtonExcludeSub
|
||||||
|
} else if isSubscribed {
|
||||||
buttonKind = TrainInfoResponseButtonIncludeUnsub
|
buttonKind = TrainInfoResponseButtonIncludeUnsub
|
||||||
}
|
}
|
||||||
message.ReplyMarkup = GetTrainNumberCommandResponseButtons(trainData.Number, group.Stations[0].Departure.ScheduleTime, groupIndex, buttonKind)
|
message.ReplyMarkup = GetTrainNumberCommandResponseButtons(trainData.Number, group.Stations[0].Departure.ScheduleTime, groupIndex, buttonKind)
|
||||||
|
@ -154,6 +258,7 @@ func HandleTrainNumberCommand(ctx context.Context, trainNumber string, date time
|
||||||
|
|
||||||
return &HandlerResponse{
|
return &HandlerResponse{
|
||||||
Message: &message,
|
Message: &message,
|
||||||
|
ShouldUnsubscribe: shouldUnsubscribe,
|
||||||
}, true
|
}, true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@ type HandlerResponse struct {
|
||||||
CallbackAnswer *bot.AnswerCallbackQueryParams
|
CallbackAnswer *bot.AnswerCallbackQueryParams
|
||||||
MessageEdits []*bot.EditMessageTextParams
|
MessageEdits []*bot.EditMessageTextParams
|
||||||
MessageMarkupEdits []*bot.EditMessageReplyMarkupParams
|
MessageMarkupEdits []*bot.EditMessageReplyMarkupParams
|
||||||
|
ShouldUnsubscribe bool
|
||||||
Injected struct {
|
Injected struct {
|
||||||
ChatId int64
|
ChatId int64
|
||||||
MessageId int
|
MessageId int
|
||||||
|
|
|
@ -98,15 +98,14 @@ func (sub *Subscriptions) DeleteSubscription(chatId int64, messageId int) (*SubD
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var result *SubData
|
var result SubData
|
||||||
if deleteIndex != -1 {
|
if deleteIndex != -1 {
|
||||||
result = &SubData{}
|
result = datas[deleteIndex]
|
||||||
*result = datas[deleteIndex]
|
|
||||||
datas[deleteIndex] = datas[len(datas)-1]
|
datas[deleteIndex] = datas[len(datas)-1]
|
||||||
datas = datas[:len(datas)-1]
|
datas = datas[:len(datas)-1]
|
||||||
|
|
||||||
_, err := database.WriteDB(func(db *gorm.DB) (*gorm.DB, error) {
|
_, err := database.WriteDB(func(db *gorm.DB) (*gorm.DB, error) {
|
||||||
db.Delete(result)
|
db.Delete(&result)
|
||||||
return db, db.Error
|
return db, db.Error
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -120,11 +119,11 @@ func (sub *Subscriptions) DeleteSubscription(chatId int64, messageId int) (*SubD
|
||||||
} else {
|
} else {
|
||||||
sub.data[chatId] = datas
|
sub.data[chatId] = datas
|
||||||
}
|
}
|
||||||
return result, nil
|
return &result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sub *Subscriptions) CheckSubscriptions(ctx context.Context) {
|
func (sub *Subscriptions) CheckSubscriptions(ctx context.Context) {
|
||||||
ticker := time.NewTicker(time.Second * 90)
|
ticker := time.NewTicker(time.Second * 45)
|
||||||
|
|
||||||
sub.executeChecks(ctx)
|
sub.executeChecks(ctx)
|
||||||
for {
|
for {
|
||||||
|
@ -142,20 +141,29 @@ type workerData struct {
|
||||||
data SubData
|
data SubData
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type unsubscribe struct {
|
||||||
|
chatId int64
|
||||||
|
messageId int
|
||||||
|
}
|
||||||
|
|
||||||
|
type workerResponseData struct {
|
||||||
|
unsubscribe *unsubscribe
|
||||||
|
}
|
||||||
|
|
||||||
func (sub *Subscriptions) executeChecks(ctx context.Context) {
|
func (sub *Subscriptions) executeChecks(ctx context.Context) {
|
||||||
sub.mutex.RLock()
|
sub.mutex.RLock()
|
||||||
defer sub.mutex.RUnlock()
|
|
||||||
|
|
||||||
// Only allow 8 concurrent requests
|
// Only allow 8 concurrent requests
|
||||||
// TODO: Make configurable instead of hardcoded
|
// TODO: Make configurable instead of hardcoded
|
||||||
workerCount := 8
|
workerCount := 8
|
||||||
workerChan := make(chan workerData, workerCount)
|
workerChan := make(chan workerData, workerCount)
|
||||||
wg := &sync.WaitGroup{}
|
responseChan := make(chan *workerResponseData, workerCount)
|
||||||
|
defer close(responseChan)
|
||||||
for i := 0; i < workerCount; i++ {
|
for i := 0; i < workerCount; i++ {
|
||||||
wg.Add(1)
|
go checkWorker(ctx, workerChan, responseChan)
|
||||||
go checkWorker(ctx, workerChan, wg)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
for _, datas := range sub.data {
|
for _, datas := range sub.data {
|
||||||
for i := range datas {
|
for i := range datas {
|
||||||
workerChan <- workerData{
|
workerChan <- workerData{
|
||||||
|
@ -165,12 +173,42 @@ func (sub *Subscriptions) executeChecks(ctx context.Context) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
close(workerChan)
|
close(workerChan)
|
||||||
wg.Wait()
|
}()
|
||||||
|
|
||||||
|
responses := make([]*workerResponseData, 0, len(sub.data))
|
||||||
|
|
||||||
|
for _, datas := range sub.data {
|
||||||
|
for range datas {
|
||||||
|
if resp := <-responseChan; resp != nil && resp.unsubscribe != nil {
|
||||||
|
responses = append(responses, resp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub.mutex.RUnlock()
|
||||||
|
|
||||||
|
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),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkWorker(ctx context.Context, workerChan <-chan workerData, wg *sync.WaitGroup) {
|
func checkWorker(ctx context.Context, workerChan <-chan workerData, responseChan chan<- *workerResponseData) {
|
||||||
defer wg.Done()
|
|
||||||
for wData := range workerChan {
|
for wData := range workerChan {
|
||||||
|
func() {
|
||||||
|
var response *workerResponseData
|
||||||
|
defer func() {
|
||||||
|
responseChan <- response
|
||||||
|
}()
|
||||||
data := wData.data
|
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)
|
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)
|
||||||
|
|
||||||
|
@ -179,6 +217,14 @@ func checkWorker(ctx context.Context, workerChan <-chan workerData, wg *sync.Wai
|
||||||
if !ok || resp == nil || resp.Message == nil {
|
if !ok || resp == nil || resp.Message == nil {
|
||||||
// Silently discard update errors
|
// 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)
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -191,5 +237,14 @@ func checkWorker(ctx context.Context, workerChan <-chan workerData, wg *sync.Wai
|
||||||
DisableWebPagePreview: resp.Message.DisableWebPagePreview,
|
DisableWebPagePreview: resp.Message.DisableWebPagePreview,
|
||||||
ReplyMarkup: resp.Message.ReplyMarkup,
|
ReplyMarkup: resp.Message.ReplyMarkup,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
response = &workerResponseData{}
|
||||||
|
if resp.ShouldUnsubscribe {
|
||||||
|
response.unsubscribe = &unsubscribe{
|
||||||
|
chatId: data.ChatId,
|
||||||
|
messageId: data.MessageId,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue