Temp timezone fix #11
1 changed files with 163 additions and 3 deletions
166
main.go
166
main.go
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"sort"
|
"sort"
|
||||||
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
|
@ -33,6 +34,53 @@ type NamedTimetableEntry struct {
|
||||||
ActivityType string `json:"activityType"`
|
ActivityType string `json:"activityType"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// lessonKey creates a stable key for a lesson (date + time + subject)
|
||||||
|
func lessonKey(e NamedTimetableEntry) string {
|
||||||
|
subject := "unknown"
|
||||||
|
if len(e.Su) > 0 {
|
||||||
|
subject = e.Su[0]
|
||||||
|
}
|
||||||
|
// Include date for multi-day accuracy (e.g., Monday vs Tuesday)
|
||||||
|
return e.Date + "|" + e.StartTime + "|" + subject
|
||||||
|
}
|
||||||
|
|
||||||
|
// getSubject returns the first subject or "Free period"
|
||||||
|
func getSubject(e NamedTimetableEntry) string {
|
||||||
|
if len(e.Su) == 0 {
|
||||||
|
return "Free period"
|
||||||
|
}
|
||||||
|
return e.Su[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
// formatRoom returns a readable room string
|
||||||
|
func formatRoom(rooms []string) string {
|
||||||
|
if len(rooms) == 0 {
|
||||||
|
return "no room"
|
||||||
|
}
|
||||||
|
return strings.Join(rooms, ", ")
|
||||||
|
}
|
||||||
|
|
||||||
|
// formatStatus returns a human-readable status
|
||||||
|
func formatStatus(code string) string {
|
||||||
|
if code == "" {
|
||||||
|
return "regular"
|
||||||
|
}
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
|
||||||
|
// slicesEqual checks if two string slices are equal (order-sensitive)
|
||||||
|
func slicesEqual(a, b []string) bool {
|
||||||
|
if len(a) != len(b) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for i := range a {
|
||||||
|
if a[i] != b[i] {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// init and main//
|
// init and main//
|
||||||
func init() {
|
func init() {
|
||||||
godotenv.Load(".env")
|
godotenv.Load(".env")
|
||||||
|
|
@ -72,7 +120,11 @@ func main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func isScheduledTime(now time.Time) bool {
|
func isScheduledTime(now time.Time) bool {
|
||||||
|
<<<<<<< HEAD
|
||||||
scheduled := []string{"07:45", "08:35", "09:35", "10:25", "11:25", "12:15", "13:40", "14:25", "15:20", "16:00"}
|
scheduled := []string{"07:45", "08:35", "09:35", "10:25", "11:25", "12:15", "13:40", "14:25", "15:20", "16:00"}
|
||||||
|
=======
|
||||||
|
scheduled := []string{"07:45", "08:35", "09:35", "10:25", "11:25", "12:15", "13:40", "14:25"}
|
||||||
|
>>>>>>> da5ca15 (detailed changed lessons message)
|
||||||
current := now.Format("15:04")
|
current := now.Format("15:04")
|
||||||
for _, t := range scheduled {
|
for _, t := range scheduled {
|
||||||
if t == current {
|
if t == current {
|
||||||
|
|
@ -119,11 +171,89 @@ func scheduleTimetableUpdate() {
|
||||||
log.Printf("Error reading timetable for user %s: %v", acc.Username, err)
|
log.Printf("Error reading timetable for user %s: %v", acc.Username, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
// Parse current and previous timetable data
|
||||||
|
var currentEntries, prevEntries []NamedTimetableEntry
|
||||||
|
|
||||||
if prev, ok := prevData[acc.UserID]; ok && !bytes.Equal(data, prev) {
|
if err := json.Unmarshal(data, ¤tEntries); err != nil {
|
||||||
log.Printf("Timetable has changed for user %s.", acc.Username)
|
log.Printf("Failed to parse new timetable for user %s: %v", acc.Username, err)
|
||||||
BotStart.SendDM(acc.UserID, "Your timetable has changed!")
|
prevData[acc.UserID] = data // still update to avoid repeated errors
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if prev, ok := prevData[acc.UserID]; ok {
|
||||||
|
if err := json.Unmarshal(prev, &prevEntries); err != nil {
|
||||||
|
log.Printf("Failed to parse previous timetable for user %s: %v", acc.Username, err)
|
||||||
|
prevData[acc.UserID] = data
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build maps keyed by a stable lesson identifier
|
||||||
|
currMap := make(map[string]NamedTimetableEntry)
|
||||||
|
prevMap := make(map[string]NamedTimetableEntry)
|
||||||
|
|
||||||
|
for _, e := range currentEntries {
|
||||||
|
key := lessonKey(e)
|
||||||
|
currMap[key] = e
|
||||||
|
}
|
||||||
|
for _, e := range prevEntries {
|
||||||
|
key := lessonKey(e)
|
||||||
|
prevMap[key] = e
|
||||||
|
}
|
||||||
|
|
||||||
|
var changes []string
|
||||||
|
|
||||||
|
// Check for modified or new lessons
|
||||||
|
for key, curr := range currMap {
|
||||||
|
if prev, exists := prevMap[key]; exists {
|
||||||
|
// Existing lesson — check for changes
|
||||||
|
subject := getSubject(curr)
|
||||||
|
|
||||||
|
// Room change?
|
||||||
|
if !slicesEqual(prev.Ro, curr.Ro) {
|
||||||
|
oldRoom := formatRoom(prev.Ro)
|
||||||
|
newRoom := formatRoom(curr.Ro)
|
||||||
|
changes = append(changes, fmt.Sprintf("Room changed for %s: %s → %s", subject, oldRoom, newRoom))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Code/status change?
|
||||||
|
if prev.Code != curr.Code {
|
||||||
|
oldStatus := formatStatus(prev.Code)
|
||||||
|
newStatus := formatStatus(curr.Code)
|
||||||
|
changes = append(changes, fmt.Sprintf("Status changed for %s: %s → %s", subject, oldStatus, newStatus))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// New lesson
|
||||||
|
subject := getSubject(curr)
|
||||||
|
room := formatRoom(curr.Ro)
|
||||||
|
changes = append(changes, fmt.Sprintf("New lesson: %s in %s", subject, room))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for removed lessons
|
||||||
|
for key, prev := range prevMap {
|
||||||
|
if _, exists := currMap[key]; !exists {
|
||||||
|
subject := getSubject(prev)
|
||||||
|
changes = append(changes, fmt.Sprintf("Lesson removed: %s", subject))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send notification if changes detected
|
||||||
|
if len(changes) > 0 {
|
||||||
|
log.Printf("Timetable changed for user %s: %d changes", acc.Username, len(changes))
|
||||||
|
message := "Your timetable has changed!\n\n" + strings.Join(changes, "\n")
|
||||||
|
BotStart.SendDM(acc.UserID, message)
|
||||||
|
|
||||||
|
// Optional: send to Discord webhook if configured and user is "default"
|
||||||
|
if acc.UserID == "default" && discordWebhookURL != "" {
|
||||||
|
sendUpdateDiscordWebhookWithDetails(changes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// First run — no previous data, so don't notify
|
||||||
|
log.Printf("First timetable fetch for user %s", acc.Username)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Always update prevData
|
||||||
prevData[acc.UserID] = data
|
prevData[acc.UserID] = data
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -419,6 +549,36 @@ func sendDiscordWebhook(subject string, room string, nextTime string, Status str
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func sendUpdateDiscordWebhookWithDetails(changes []string) {
|
||||||
|
log.Println("Sending detailed Discord webhook notification...")
|
||||||
|
|
||||||
|
message := "**Timetable Update**\n\n" + strings.Join(changes, "\n")
|
||||||
|
|
||||||
|
payload := DiscordWebhookPayload{
|
||||||
|
Content: message,
|
||||||
|
}
|
||||||
|
|
||||||
|
jsonData, err := json.Marshal(payload)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error marshaling webhook payload: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := http.Post(discordWebhookURL, "application/json", bytes.NewBuffer(jsonData))
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error sending Discord webhook: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode >= 200 && resp.StatusCode < 300 {
|
||||||
|
log.Println("Detailed Discord webhook sent successfully")
|
||||||
|
} else {
|
||||||
|
body, _ := ioutil.ReadAll(resp.Body)
|
||||||
|
log.Printf("Discord webhook failed with status %d: %s", resp.StatusCode, string(body))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func sendUpdateDiscordWebhook() {
|
func sendUpdateDiscordWebhook() {
|
||||||
log.Println("Sending Discord webhook notification...")
|
log.Println("Sending Discord webhook notification...")
|
||||||
// Create a rich embed message
|
// Create a rich embed message
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue