Working timetable
This commit is contained in:
parent
1bd4de6bf4
commit
58415419bc
7 changed files with 420 additions and 50 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -3,3 +3,6 @@ rooms.json
|
||||||
classes.json
|
classes.json
|
||||||
subjects.json
|
subjects.json
|
||||||
teachers.json
|
teachers.json
|
||||||
|
login.json
|
||||||
|
timetable.json
|
||||||
|
timetableFilled.json
|
||||||
|
|
@ -9,7 +9,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
type classesResponse struct {
|
type ClassesResponse struct {
|
||||||
Jsonrpc string `json:"jsonrpc"`
|
Jsonrpc string `json:"jsonrpc"`
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
Result []classes `json:"result"`
|
Result []classes `json:"result"`
|
||||||
|
|
@ -69,7 +69,7 @@ func Classes(cookies []*http.Cookie) {
|
||||||
}
|
}
|
||||||
//responseString := string(response)
|
//responseString := string(response)
|
||||||
//log.Println("Repsonse ", responseString)
|
//log.Println("Repsonse ", responseString)
|
||||||
var Response classesResponse
|
var Response ClassesResponse
|
||||||
err = json.Unmarshal(response, &Response)
|
err = json.Unmarshal(response, &Response)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Error unmarshaling response: %v", err)
|
log.Fatalf("Error unmarshaling response: %v", err)
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
type subjectsResponse struct {
|
type SubjectsResponse struct {
|
||||||
Jsonrpc string `json:"jsonrpc"`
|
Jsonrpc string `json:"jsonrpc"`
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
Result []subjects `json:"result"`
|
Result []subjects `json:"result"`
|
||||||
|
|
@ -69,7 +69,7 @@ func Subjects(cookies []*http.Cookie) {
|
||||||
}
|
}
|
||||||
//responseString := string(response)
|
//responseString := string(response)
|
||||||
//log.Println("Repsonse ", responseString)
|
//log.Println("Repsonse ", responseString)
|
||||||
var Response subjectsResponse
|
var Response SubjectsResponse
|
||||||
err = json.Unmarshal(response, &Response)
|
err = json.Unmarshal(response, &Response)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Error unmarshaling response: %v", err)
|
log.Fatalf("Error unmarshaling response: %v", err)
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
type teachersResponse struct {
|
type TeachersResponse struct {
|
||||||
Jsonrpc string `json:"jsonrpc"`
|
Jsonrpc string `json:"jsonrpc"`
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
Result []teachers `json:"result"`
|
Result []teachers `json:"result"`
|
||||||
|
|
@ -69,7 +69,7 @@ func Teachers(cookies []*http.Cookie) {
|
||||||
}
|
}
|
||||||
//responseString := string(response)
|
//responseString := string(response)
|
||||||
//log.Println("Repsonse ", responseString)
|
//log.Println("Repsonse ", responseString)
|
||||||
var Response teachersResponse
|
var Response TeachersResponse
|
||||||
err = json.Unmarshal(response, &Response)
|
err = json.Unmarshal(response, &Response)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Error unmarshaling response: %v", err)
|
log.Fatalf("Error unmarshaling response: %v", err)
|
||||||
|
|
|
||||||
228
Bot/Timetable.go
Normal file
228
Bot/Timetable.go
Normal file
|
|
@ -0,0 +1,228 @@
|
||||||
|
package Untis
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TimetableResponse struct {
|
||||||
|
Jsonrpc string `json:"jsonrpc"`
|
||||||
|
ID string `json:"id"`
|
||||||
|
Result []timetable `json:"result"`
|
||||||
|
}
|
||||||
|
type NamedTimetableEntry struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
Date string `json:"date"`
|
||||||
|
StartTime string `json:"startTime"`
|
||||||
|
EndTime string `json:"endTime"`
|
||||||
|
Code string `json:"code,omitempty"`
|
||||||
|
Statflags string `json:"statflags,omitempty"`
|
||||||
|
Kl []string `json:"kl"`
|
||||||
|
Su []string `json:"su"`
|
||||||
|
Ro []string `json:"ro"`
|
||||||
|
ActivityType string `json:"activityType"`
|
||||||
|
}
|
||||||
|
type timetable struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
Date int `json:"date"`
|
||||||
|
StartTime int `json:"startTime"`
|
||||||
|
EndTime int `json:"endTime"`
|
||||||
|
Code string `json:"code,omitempty"`
|
||||||
|
Statflags string `json:"statflags,omitempty"`
|
||||||
|
Kl []IDObj `json:"kl"`
|
||||||
|
Su []IDObj `json:"su"`
|
||||||
|
Ro []IDObj `json:"ro"`
|
||||||
|
ActivityType string `json:"activityType"`
|
||||||
|
}
|
||||||
|
type IDObj struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
}
|
||||||
|
type TimetableEntry struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
Date int `json:"date"`
|
||||||
|
StartTime int `json:"startTime"`
|
||||||
|
EndTime int `json:"endTime"`
|
||||||
|
Code string `json:"code,omitempty"`
|
||||||
|
Statflags string `json:"statflags,omitempty"`
|
||||||
|
Kl []IDObj `json:"kl"`
|
||||||
|
Su []IDObj `json:"su"`
|
||||||
|
Ro []IDObj `json:"ro"`
|
||||||
|
ActivityType string `json:"activityType"`
|
||||||
|
}
|
||||||
|
type NamedObj struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
type getTimetable struct {
|
||||||
|
Id string `json:"id"`
|
||||||
|
Method string `json:"method"`
|
||||||
|
Params params `json:"params"`
|
||||||
|
Jsonrpc string `json:"jsonrpc"`
|
||||||
|
}
|
||||||
|
type params struct {
|
||||||
|
StartDate string `json:"startDate"`
|
||||||
|
EndDate string `json:"endDate"`
|
||||||
|
Id int `json:"id"`
|
||||||
|
Type int `json:"type"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReadLoginResultFromFile(path string) (Loginresult, error) {
|
||||||
|
var result Loginresult
|
||||||
|
data, err := os.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
err = json.Unmarshal(data, &result)
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func Timetable(cookies []*http.Cookie) {
|
||||||
|
loginResult, err := ReadLoginResultFromFile("login.json")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
today := time.Now().Format("20060102")
|
||||||
|
g := getTimetable{"2023-05-06 15:44:22.215292", "getTimetable", params{today, today, loginResult.PersonID, loginResult.PersonType}, "2.0"}
|
||||||
|
TimetablesJson, err := json.Marshal(g)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Error marshaling login data: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
timetable := bytes.NewReader(TimetablesJson)
|
||||||
|
|
||||||
|
prompt, err := http.NewRequest("GET", Url, timetable)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Error creatingrequest: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
//log.Println("prompt without extra header or cookie ", prompt)
|
||||||
|
//log.Println("Cookie: ", cookies)
|
||||||
|
|
||||||
|
prompt.Header.Set("Content-Type", "application/json")
|
||||||
|
prompt.Header.Set("User-Agent", "Webuntis Test")
|
||||||
|
|
||||||
|
for _, cookie := range cookies {
|
||||||
|
//if cookie.Name == "JSESSIONID" {
|
||||||
|
prompt.AddCookie(cookie)
|
||||||
|
//log.Printf("Added JSESSIONID cookie: %s=%s", cookie.Name, cookie.Value)
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
//log.Println("Request JSON:", string(TeachersJson))
|
||||||
|
out, err := http.DefaultClient.Do(prompt)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error during request: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer out.Body.Close()
|
||||||
|
//log.Println(out.Status)
|
||||||
|
response, err := io.ReadAll(out.Body)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error reading response body: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
//responseString := string(response)
|
||||||
|
//log.Println("Repsonse ", responseString)
|
||||||
|
var Response TimetableResponse
|
||||||
|
err = json.Unmarshal(response, &Response)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Error unmarshaling response: %v", err)
|
||||||
|
}
|
||||||
|
data, err := json.MarshalIndent(Response.Result, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
err = os.WriteFile("timetable.json", data, 0644)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
log.Println("Updated Timetable")
|
||||||
|
setTimetable()
|
||||||
|
}
|
||||||
|
|
||||||
|
func LoadIDMap(path string) (map[int]string, error) {
|
||||||
|
data, err := os.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var objs []NamedObj
|
||||||
|
if err := json.Unmarshal(data, &objs); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
m := make(map[int]string)
|
||||||
|
for _, obj := range objs {
|
||||||
|
m[obj.ID] = obj.Name
|
||||||
|
}
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
func LoadTimetable(path string) ([]TimetableEntry, error) {
|
||||||
|
data, err := os.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var entries []TimetableEntry
|
||||||
|
if err := json.Unmarshal(data, &entries); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return entries, nil
|
||||||
|
}
|
||||||
|
func formatTime(t int) string {
|
||||||
|
h := t / 100
|
||||||
|
m := t % 100
|
||||||
|
return fmt.Sprintf("%02d:%02d", h, m)
|
||||||
|
}
|
||||||
|
func formatDate(date int) string {
|
||||||
|
s := fmt.Sprintf("%08d", date) // ensures leading zeros
|
||||||
|
year := s[:4]
|
||||||
|
month := s[4:6]
|
||||||
|
day := s[6:8]
|
||||||
|
return fmt.Sprintf("%s-%s-%s", day, month, year)
|
||||||
|
}
|
||||||
|
func setTimetable() {
|
||||||
|
subjects, _ := LoadIDMap("subjects.json")
|
||||||
|
rooms, _ := LoadIDMap("rooms.json")
|
||||||
|
classes, _ := LoadIDMap("classes.json")
|
||||||
|
timetable, _ := LoadTimetable("timetable.json")
|
||||||
|
|
||||||
|
var namedTimetable []NamedTimetableEntry
|
||||||
|
|
||||||
|
for _, lesson := range timetable {
|
||||||
|
var klNames, suNames, roNames []string
|
||||||
|
for _, kl := range lesson.Kl {
|
||||||
|
klNames = append(klNames, classes[kl.ID])
|
||||||
|
}
|
||||||
|
for _, su := range lesson.Su {
|
||||||
|
suNames = append(suNames, subjects[su.ID])
|
||||||
|
}
|
||||||
|
for _, ro := range lesson.Ro {
|
||||||
|
roNames = append(roNames, rooms[ro.ID])
|
||||||
|
}
|
||||||
|
namedTimetable = append(namedTimetable, NamedTimetableEntry{
|
||||||
|
ID: lesson.ID,
|
||||||
|
Date: formatDate(lesson.Date),
|
||||||
|
StartTime: formatTime(lesson.StartTime),
|
||||||
|
EndTime: formatTime(lesson.EndTime),
|
||||||
|
Code: lesson.Code,
|
||||||
|
Statflags: lesson.Statflags,
|
||||||
|
Kl: klNames,
|
||||||
|
Su: suNames,
|
||||||
|
Ro: roNames,
|
||||||
|
ActivityType: lesson.ActivityType,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := json.MarshalIndent(namedTimetable, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
err = os.WriteFile("timetableFilled.json", data, 0644)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
log.Println("Filled Timetable")
|
||||||
|
}
|
||||||
33
Bot/Untis.go
33
Bot/Untis.go
|
|
@ -3,6 +3,7 @@ package Untis
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
|
@ -23,6 +24,17 @@ type Login struct {
|
||||||
|
|
||||||
Jsonrpc string `json:"jsonrpc"`
|
Jsonrpc string `json:"jsonrpc"`
|
||||||
}
|
}
|
||||||
|
type LoginResponse struct {
|
||||||
|
Jsonrpc string `json:"jsonrpc"`
|
||||||
|
ID string `json:"id"`
|
||||||
|
Result Loginresult `json:"result"`
|
||||||
|
}
|
||||||
|
type Loginresult struct {
|
||||||
|
SessionID string `json:"sessionId"`
|
||||||
|
PersonType int `json:"personType"`
|
||||||
|
PersonID int `json:"personId"`
|
||||||
|
KlasseID int `json:"klasseId"`
|
||||||
|
}
|
||||||
|
|
||||||
var Url = "https://thalia.webuntis.com/WebUntis/jsonrpc.do?school=Mons_Tabor"
|
var Url = "https://thalia.webuntis.com/WebUntis/jsonrpc.do?school=Mons_Tabor"
|
||||||
var Password = os.Getenv("UNTIS_PASSWORD")
|
var Password = os.Getenv("UNTIS_PASSWORD")
|
||||||
|
|
@ -43,6 +55,7 @@ func Main() {
|
||||||
|
|
||||||
//getTeachers sends empty response -> commented out
|
//getTeachers sends empty response -> commented out
|
||||||
//Teachers(cookies)
|
//Teachers(cookies)
|
||||||
|
Timetable(cookies)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Auth() ([]*http.Cookie, error) {
|
func Auth() ([]*http.Cookie, error) {
|
||||||
|
|
@ -68,7 +81,25 @@ func Auth() ([]*http.Cookie, error) {
|
||||||
//log.Println("Set-Cookie headers:", LoginOut.Header["Set-Cookie"])
|
//log.Println("Set-Cookie headers:", LoginOut.Header["Set-Cookie"])
|
||||||
//loginRespBody, _ := io.ReadAll(LoginOut.Body)
|
//loginRespBody, _ := io.ReadAll(LoginOut.Body)
|
||||||
//log.Println("Login response body:", string(loginRespBody))
|
//log.Println("Login response body:", string(loginRespBody))
|
||||||
log.Println("Login successful")
|
//log.Println("Login successful")
|
||||||
|
response, err := io.ReadAll(LoginOut.Body)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Error reading response body: %v", err)
|
||||||
|
}
|
||||||
|
var Response LoginResponse
|
||||||
|
err = json.Unmarshal(response, &Response)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Error unmarshaling response: %v", err)
|
||||||
|
}
|
||||||
|
data, err := json.MarshalIndent(Response.Result, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
err = os.WriteFile("login.json", data, 0644)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
return cookies, nil
|
return cookies, nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
194
main.go
194
main.go
|
|
@ -8,8 +8,8 @@ import (
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"os/signal"
|
"os/signal"
|
||||||
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
Untis "untislogger/Bot"
|
Untis "untislogger/Bot"
|
||||||
|
|
@ -17,12 +17,20 @@ import (
|
||||||
"github.com/joho/godotenv"
|
"github.com/joho/godotenv"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Global variable to store the current public IP
|
type NamedTimetableEntry struct {
|
||||||
var currentPubIP string
|
ID int `json:"id"`
|
||||||
|
Date string `json:"date"`
|
||||||
// Discord webhook configuration
|
StartTime string `json:"startTime"`
|
||||||
var discordWebhookURL string // Webhook URL from environment variable
|
EndTime string `json:"endTime"`
|
||||||
|
Code string `json:"code,omitempty"`
|
||||||
|
Statflags string `json:"statflags,omitempty"`
|
||||||
|
Kl []string `json:"kl"`
|
||||||
|
Su []string `json:"su"`
|
||||||
|
Ro []string `json:"ro"`
|
||||||
|
ActivityType string `json:"activityType"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// init and main//
|
||||||
func init() {
|
func init() {
|
||||||
err := godotenv.Load()
|
err := godotenv.Load()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -38,12 +46,14 @@ func init() {
|
||||||
} else {
|
} else {
|
||||||
log.Println("No Discord webhook provided, Discord notifications will be disabled")
|
log.Println("No Discord webhook provided, Discord notifications will be disabled")
|
||||||
}
|
}
|
||||||
|
|
||||||
// scheduleIPCheck() // Start the periodic IP check
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
go Untis.Main() //starting API calls function
|
go Untis.Main() //starting API calls function
|
||||||
|
//checking for time
|
||||||
|
log.Println("starting Timetable checks")
|
||||||
|
go checkTimetableChanges()
|
||||||
|
go notifyUpcomingLessons()
|
||||||
// Create a channel to receive OS signals
|
// Create a channel to receive OS signals
|
||||||
sigChan := make(chan os.Signal, 1)
|
sigChan := make(chan os.Signal, 1)
|
||||||
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
|
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
|
||||||
|
|
@ -55,52 +65,102 @@ func main() {
|
||||||
log.Println("Shutting down...")
|
log.Println("Shutting down...")
|
||||||
}
|
}
|
||||||
|
|
||||||
func testPubIP() {
|
func loadTimetableFilled(path string) ([]NamedTimetableEntry, error) {
|
||||||
resp, err := http.Get("https://api.ipify.org")
|
data, err := os.ReadFile(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Error getting public IP: %v", err)
|
return nil, err
|
||||||
return
|
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
var entries []NamedTimetableEntry
|
||||||
|
if err := json.Unmarshal(data, &entries); err != nil {
|
||||||
ip, err := ioutil.ReadAll(resp.Body)
|
return nil, err
|
||||||
if err != nil {
|
|
||||||
log.Printf("Error reading response: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Printf("Current public IP: %s", ip)
|
|
||||||
if string(ip) != currentPubIP {
|
|
||||||
log.Printf("IP changed sending notifications for new IP: %s", ip)
|
|
||||||
currentPubIP = string(ip)
|
|
||||||
notifyNewPubIP(currentPubIP)
|
|
||||||
}
|
}
|
||||||
|
return entries, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func scheduleIPCheck() {
|
func checkTimetableChanges() {
|
||||||
// Run once immediately
|
var lastTimetable []NamedTimetableEntry
|
||||||
testPubIP()
|
|
||||||
|
|
||||||
// Then schedule to run every hour
|
for {
|
||||||
ticker := time.NewTicker(1 * time.Hour)
|
current, err := loadTimetableFilled("timetableFilled.json")
|
||||||
go func() {
|
if err != nil {
|
||||||
for range ticker.C {
|
log.Println("Error loading timetable:", err)
|
||||||
testPubIP()
|
time.Sleep(time.Hour)
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
||||||
func notifyNewPubIP(ip string) {
|
// Compare with last timetable (simple string comparison)
|
||||||
log.Printf("Notifying about new IP: %s", ip)
|
log.Println("Checking if something changed")
|
||||||
|
if !equalTimetables(lastTimetable, current) && len(lastTimetable) > 0 {
|
||||||
// Send Discord notification if webhook is configured
|
// Find and send changes
|
||||||
if discordWebhookURL != "" {
|
changes := diffTimetables(lastTimetable, current)
|
||||||
sendDiscordWebhook(ip)
|
oldMap := make(map[int]NamedTimetableEntry)
|
||||||
} else {
|
for _, e := range lastTimetable {
|
||||||
log.Println("Discord webhook not configured, skipping Discord notification")
|
oldMap[e.ID] = e
|
||||||
|
}
|
||||||
|
for _, newLesson := range changes {
|
||||||
|
oldLesson, found := oldMap[newLesson.ID]
|
||||||
|
if found {
|
||||||
|
sendTimetableChangeWebhook(oldLesson, newLesson)
|
||||||
|
} else {
|
||||||
|
// If it's a new lesson, you can send a simple notification or skip
|
||||||
|
sendTimetableChangeWebhook(NamedTimetableEntry{}, newLesson)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lastTimetable = current
|
||||||
|
time.Sleep(time.Hour)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func equalTimetables(a, b []NamedTimetableEntry) bool {
|
||||||
|
|
||||||
|
aj, _ := json.Marshal(a)
|
||||||
|
bj, _ := json.Marshal(b)
|
||||||
|
return bytes.Equal(aj, bj)
|
||||||
|
}
|
||||||
|
|
||||||
|
func diffTimetables(old, new []NamedTimetableEntry) []NamedTimetableEntry {
|
||||||
|
// Simple diff: return lessons in new that are not in old
|
||||||
|
var diff []NamedTimetableEntry
|
||||||
|
oldMap := make(map[int]NamedTimetableEntry)
|
||||||
|
for _, e := range old {
|
||||||
|
oldMap[e.ID] = e
|
||||||
|
}
|
||||||
|
for _, e := range new {
|
||||||
|
if _, found := oldMap[e.ID]; !found {
|
||||||
|
diff = append(diff, e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return diff
|
||||||
|
}
|
||||||
|
|
||||||
|
func notifyUpcomingLessons() {
|
||||||
|
log.Println("checking for new Lesson")
|
||||||
|
for {
|
||||||
|
entries, err := loadTimetableFilled("timetableFilled.json")
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Error loading timetable:", err)
|
||||||
|
time.Sleep(time.Minute)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
now := time.Now()
|
||||||
|
for _, lesson := range entries {
|
||||||
|
// Parse date and time
|
||||||
|
lessonTime, err := time.Parse("02-01-2006 15:04", lesson.Date+" "+lesson.StartTime)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if lessonTime.Sub(now) > 4*time.Minute && lessonTime.Sub(now) < 6*time.Minute {
|
||||||
|
sendNextLessonWebhook(lesson)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
time.Sleep(time.Minute)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Discord webhook configuration
|
||||||
|
var discordWebhookURL string // Webhook URL from environment variable
|
||||||
|
|
||||||
// DiscordWebhookPayload represents the structure for Discord webhook messages
|
// DiscordWebhookPayload represents the structure for Discord webhook messages
|
||||||
type DiscordWebhookPayload struct {
|
type DiscordWebhookPayload struct {
|
||||||
Content string `json:"content"`
|
Content string `json:"content"`
|
||||||
|
|
@ -121,6 +181,54 @@ type Field struct {
|
||||||
Inline bool `json:"inline"`
|
Inline bool `json:"inline"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func sendTimetableChangeWebhook(oldLesson, newLesson NamedTimetableEntry) {
|
||||||
|
var changes []string
|
||||||
|
if !equalStringSlices(oldLesson.Su, newLesson.Su) {
|
||||||
|
changes = append(changes, fmt.Sprintf("Subject: %v → %v", strings.Join(oldLesson.Su, ", "), strings.Join(newLesson.Su, ", ")))
|
||||||
|
}
|
||||||
|
if !equalStringSlices(oldLesson.Ro, newLesson.Ro) {
|
||||||
|
changes = append(changes, fmt.Sprintf("Room: %v → %v", strings.Join(oldLesson.Ro, ", "), strings.Join(newLesson.Ro, ", ")))
|
||||||
|
}
|
||||||
|
if oldLesson.StartTime != newLesson.StartTime {
|
||||||
|
changes = append(changes, fmt.Sprintf("Time: %s → %s", oldLesson.StartTime, newLesson.StartTime))
|
||||||
|
}
|
||||||
|
if oldLesson.Code != newLesson.Code {
|
||||||
|
changes = append(changes, fmt.Sprintf("Code: %s → %s", oldLesson.Code, newLesson.Code))
|
||||||
|
}
|
||||||
|
if len(changes) == 0 {
|
||||||
|
changes = append(changes, "Other details changed")
|
||||||
|
}
|
||||||
|
|
||||||
|
content := fmt.Sprintf("**Timetable Change**\nLesson: %v\nTime: %s\nChanged: %s",
|
||||||
|
strings.Join(newLesson.Su, ", "),
|
||||||
|
newLesson.StartTime,
|
||||||
|
strings.Join(changes, "; "),
|
||||||
|
)
|
||||||
|
|
||||||
|
sendDiscordWebhook(content)
|
||||||
|
}
|
||||||
|
|
||||||
|
func sendNextLessonWebhook(lesson NamedTimetableEntry) {
|
||||||
|
content := fmt.Sprintf(
|
||||||
|
"**Next Lesson Reminder**\nLesson: %v\nTime: %s\nRoom: %v",
|
||||||
|
strings.Join(lesson.Su, ", "),
|
||||||
|
lesson.StartTime,
|
||||||
|
strings.Join(lesson.Ro, ", "),
|
||||||
|
)
|
||||||
|
sendDiscordWebhook(content)
|
||||||
|
}
|
||||||
|
|
||||||
|
func equalStringSlices(a, b []string) bool {
|
||||||
|
if len(a) != len(b) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for i := range a {
|
||||||
|
if a[i] != b[i] {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
func sendDiscordWebhook(ip string) {
|
func sendDiscordWebhook(ip string) {
|
||||||
log.Println("Sending Discord webhook notification...")
|
log.Println("Sending Discord webhook notification...")
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue