From 96ea6d603351b3e2586d4c3598179c07ad865f97 Mon Sep 17 00:00:00 2001 From: Mark Zheleznyakov Date: Fri, 17 Jan 2025 00:08:57 +0300 Subject: [PATCH] feat: Basic polling --- .env.example | 6 +++++ .gitignore | 2 ++ README.md | 1 + cmd/service/main.go | 13 +++++++++++ go.mod | 13 +++++++++++ go.sum | 10 +++++++++ internal/config/env.go | 37 +++++++++++++++++++++++++++++++ internal/schedule/api.go | 32 +++++++++++++++++++++++++++ internal/schedule/models.go | 16 ++++++++++++++ internal/schedule/poll.go | 44 +++++++++++++++++++++++++++++++++++++ 10 files changed, 174 insertions(+) create mode 100644 .env.example create mode 100644 .gitignore create mode 100644 README.md create mode 100644 cmd/service/main.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 internal/config/env.go create mode 100644 internal/schedule/api.go create mode 100644 internal/schedule/models.go create mode 100644 internal/schedule/poll.go diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..67e16cf --- /dev/null +++ b/.env.example @@ -0,0 +1,6 @@ +SCHEDULE_BASE_URL= +SCHEDULE_FACULTY_ID= +SCHEDULE_GROUP_ID= +SCHEDULE_START_DATE=2025-1-6 +SCHEDULE_FLAG_PATH=/tmp/Flagfile +SCHEDULE_CRON_PATTERN=*/10 * * * * * diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e515508 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.env +!.env.example diff --git a/README.md b/README.md new file mode 100644 index 0000000..12a8d43 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +# Schedule Poll diff --git a/cmd/service/main.go b/cmd/service/main.go new file mode 100644 index 0000000..d3280c5 --- /dev/null +++ b/cmd/service/main.go @@ -0,0 +1,13 @@ +package main + +import ( + "git.inkling.su/mrqiz/schedule-poll/internal/config" + "git.inkling.su/mrqiz/schedule-poll/internal/schedule" + "github.com/robfig/cron/v3" +) + +func main() { + c := cron.New(cron.WithSeconds()) + c.AddFunc(config.AppConfig.CronPattern, schedule.PollSchedule) + c.Run() +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..da98a89 --- /dev/null +++ b/go.mod @@ -0,0 +1,13 @@ +module git.inkling.su/mrqiz/schedule-poll + +go 1.23.3 + +require ( + github.com/go-resty/resty/v2 v2.16.3 + github.com/robfig/cron/v3 v3.0.0 +) + +require ( + github.com/joho/godotenv v1.5.1 // indirect + golang.org/x/net v0.33.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..082b9d3 --- /dev/null +++ b/go.sum @@ -0,0 +1,10 @@ +github.com/go-resty/resty/v2 v2.16.3 h1:zacNT7lt4b8M/io2Ahj6yPypL7bqx9n1iprfQuodV+E= +github.com/go-resty/resty/v2 v2.16.3/go.mod h1:hkJtXbA2iKHzJheXYvQ8snQES5ZLGKMwQ07xAwp/fiA= +github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= +github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +github.com/robfig/cron/v3 v3.0.0 h1:kQ6Cb7aHOHTSzNVNEhmp8EcWKLb4CbiMW9h9VyIhO4E= +github.com/robfig/cron/v3 v3.0.0/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= +golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= +golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= +golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= +golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= diff --git a/internal/config/env.go b/internal/config/env.go new file mode 100644 index 0000000..0ffa313 --- /dev/null +++ b/internal/config/env.go @@ -0,0 +1,37 @@ +package config + +import ( + "log" + "os" + + "github.com/joho/godotenv" +) + +type config struct { + TelegramBotToken string + Schedule struct { + BaseUrl string + FacultyId string + GroupId string + StartDate string + } + FlagfilePath string + CronPattern string +} + +var AppConfig config + +func init() { + err := godotenv.Load() + if err != nil { + log.Fatal("Error loading .env file") + } + + AppConfig.FlagfilePath = os.Getenv("SCHEDULE_FLAG_PATH") + AppConfig.Schedule.BaseUrl = os.Getenv("SCHEDULE_BASE_URL") + AppConfig.Schedule.FacultyId = os.Getenv("SCHEDULE_FACULTY_ID") + AppConfig.Schedule.GroupId = os.Getenv("SCHEDULE_GROUP_ID") + AppConfig.Schedule.StartDate = os.Getenv("SCHEDULE_START_DATE") + AppConfig.TelegramBotToken = os.Getenv("SCHEDULE_TELEGRAM_TOKEN") + AppConfig.CronPattern = os.Getenv("SCHEDULE_CRON_PATTERN") +} diff --git a/internal/schedule/api.go b/internal/schedule/api.go new file mode 100644 index 0000000..33e77f7 --- /dev/null +++ b/internal/schedule/api.go @@ -0,0 +1,32 @@ +package schedule + +import ( + "fmt" + "os" + + "github.com/go-resty/resty/v2" +) + +var ( + baseUrl string = os.Getenv("SCHEDULE_BASE_URL") +) + +func GetWeek(groupId, startDate string) (*Week, error) { + client := resty.New() + client.SetBaseURL(baseUrl) + + res := Week{} + _, err := client.R(). + SetResult(&res). + Get(fmt.Sprintf("/api/v1/ruz/scheduler/%s?date=%s", groupId, startDate)) + + return &res, err +} + +func GetScheduleUrl(facultyId, groupId, date string) string { + return fmt.Sprintf("%s/faculty/%s/groups/%s?date=%s", + baseUrl, + facultyId, + groupId, + date) +} diff --git a/internal/schedule/models.go b/internal/schedule/models.go new file mode 100644 index 0000000..47a258e --- /dev/null +++ b/internal/schedule/models.go @@ -0,0 +1,16 @@ +package schedule + +type Lesson struct { + Subject string + SubjectShort string `json:"subject_short"` +} + +type Day struct { + Weekday int + Date string + Lessons []Lesson +} + +type Week struct { + Days []Day +} diff --git a/internal/schedule/poll.go b/internal/schedule/poll.go new file mode 100644 index 0000000..5828ec8 --- /dev/null +++ b/internal/schedule/poll.go @@ -0,0 +1,44 @@ +package schedule + +import ( + "fmt" + "log" + "os" + + "git.inkling.su/mrqiz/schedule-poll/internal/config" +) + +func PollSchedule() { + flagFilePath := config.AppConfig.FlagfilePath + + if _, fileErr := os.Stat(flagFilePath); fileErr == nil { + return + } + + facultyId := config.AppConfig.Schedule.FacultyId + groupId := config.AppConfig.Schedule.GroupId + date := config.AppConfig.Schedule.StartDate + + week, err := GetWeek(groupId, date) + + if err != nil { + log.Panicln("Failed to get week data", err) + } + + days := len(week.Days) + + if days != 0 { + siteUrl := GetScheduleUrl(facultyId, groupId, date) + msg := fmt.Sprintf( + "❗ Опубликовано расписание на новый семестр!\n\n"+ + "Появились занятия на неделе с %s\n"+ + "Доступно дней: %d\n\n"+ + "%s\n", + date, + len(week.Days), + siteUrl) + + os.WriteFile(flagFilePath, []byte(msg), os.FileMode(0777)) + log.Printf("Found %d days", len(week.Days)) + } +}