assign-notify/main.go
2023-11-28 19:47:02 -08:00

129 lines
3.0 KiB
Go

package main
import (
"errors"
"fmt"
"io/fs"
"log"
"net/http"
"os"
"time"
"github.com/BurntSushi/toml"
"github.com/NickyBoy89/assign-notify/calendar"
"github.com/NickyBoy89/assign-notify/selection"
"github.com/NickyBoy89/assign-notify/timeline"
ics "github.com/arran4/golang-ical"
"github.com/mitchellh/go-homedir"
)
func ConstructCalendarUrl(
moodleBase, userId, authToken string,
eventTypes selection.CalendarEventType,
when timeline.CalendarEventHorizon,
) string {
return fmt.Sprintf(
"%s/calendar/export_execute.php?userid=%s&authtoken=%s&preset_what=%s&preset_time=%s",
moodleBase,
userId,
authToken,
eventTypes,
when,
)
}
// ConfigFileLocations is a list of every place that the config file could be located
// The items are explored in-order, and tildes are expanded to the user's home directory
var ConfigFileLocations = []string{
"~/.config/assign-notify/config.toml",
}
type Config struct {
MoodleRoot string `toml:"moodle_root"`
UserId string `toml:"user_id"`
AuthToken string `toml:"auth_token"`
}
func main() {
// Load the config
var configFileLocation string
for _, path := range ConfigFileLocations {
location, err := homedir.Expand(path)
if err != nil {
log.Fatalf("Unable to get home directory: %s", err)
}
if _, err := os.Stat(location); err == nil {
configFileLocation = location
} else {
// If there actually was an error, and the file wasn't just missing
// Otherwise, check the next file in the list
if !errors.Is(err, fs.ErrNotExist) {
log.Fatalf("Error accessing config file at %s: %s", location, err)
}
}
}
// No configuration was found
if configFileLocation == "" {
log.Fatalf("No configuration file found, looked in: %s", ConfigFileLocations)
}
var conf Config
confFile, err := os.Open(configFileLocation)
if err != nil {
log.Fatalf("Unable to open config %s: %s", configFileLocation, err)
}
if _, err := toml.NewDecoder(confFile).Decode(&conf); err != nil {
log.Fatalf("Error decoding %s: %s", configFileLocation, err)
}
confFile.Close()
resp, err := http.Get(ConstructCalendarUrl(
conf.MoodleRoot,
conf.UserId,
conf.AuthToken,
selection.AllEvents,
timeline.RecentAndUpcoming,
))
if err != nil {
log.Fatalf("Error requesting calendar: %s", err)
}
defer resp.Body.Close()
cal, err := ics.ParseCalendar(resp.Body)
if err != nil {
log.Fatalf("Error parsing ICAL: %s", err)
}
// Sort the events into when their due dates are
var past, upcoming []calendar.CalendarEvent
for _, event := range cal.Events() {
calEvent, err := calendar.FromVEvent(event)
if err != nil {
panic(err)
}
if calEvent.EndTime.Before(time.Now()) {
past = append(past, calEvent)
} else {
upcoming = append(upcoming, calEvent)
}
}
for _, event := range upcoming {
due := time.Until(event.EndTime)
days := int(due.Hours()) / 24
hours := int(due.Hours())
fmt.Printf(
"%s: %s: due in %v days %v hours %.0f minutes\n",
event.Category.ClassName,
event.Summary,
days,
hours-(days*24),
due.Minutes()-float64(hours*60),
)
}
}