package config import ( "context" "fmt" "net/http" "os" "github.com/emersion/go-ical" "github.com/emersion/go-webdav" "github.com/emersion/go-webdav/caldav" "gopkg.in/yaml.v3" ) type Config struct { Server string `yaml:"server"` Username string `yaml:"username"` Password string `yaml:"password"` Calendars []CalendarSync `yaml:"calendars"` } type CalendarSync struct { Name string `yaml:"name"` Destination string `yaml:"destination"` URL string `yaml:"url"` } func (item *CalendarSync) Sync(ctx context.Context, cfg *Config) error { webdavClient := webdav.HTTPClientWithBasicAuth(http.DefaultClient, cfg.Username, cfg.Password) cc, err := caldav.NewClient(webdavClient, cfg.Server) if err != nil { return fmt.Errorf("failed to construct a CalDAV client: %w", err) } calPath, err := CalendarPath(ctx, cc, cfg.Username, item.Destination) if err != nil { return fmt.Errorf("failed to find destination calendar path: %w", err) } resp, err := http.Get(item.URL) if err != nil { return fmt.Errorf("failed to sync %s: %w", item.Name, err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { return fmt.Errorf("source returned non-200 status code: %s", resp.Status) } calendarData, err := ical.NewDecoder(resp.Body).Decode() if err != nil { return fmt.Errorf("failed to decode source calendar data: %w", err) } // if err := ical.NewEncoder(os.Stdout).Encode(calendarData); err == nil { // os.Exit(0) // } if _, err := cc.PutCalendarObject(ctx, calPath, calendarData); err != nil { return fmt.Errorf("failed to push calendar data to destination: %w", err) } return nil } func CalendarPath(ctx context.Context, cc *caldav.Client, username, calendarName string) (string, error) { homeSet, err := cc.FindCalendarHomeSet(ctx, username) if err != nil { return "", fmt.Errorf("failed to find home set for %s: %w", username, err) } calendars, err := cc.FindCalendars(ctx, homeSet) if err != nil { return "", fmt.Errorf("failed to retrieve calendars: %w", err) } for _, cal := range calendars { if cal.Name == calendarName { return cal.Path, nil } } return "", fmt.Errorf("calendar with name %s not found", calendarName) } // Load a configuration file func Load(configFile string) (*Config, error) { cfg := new(Config) f, err := os.Open(configFile) if err != nil { return nil, fmt.Errorf("failed to open config file %s: %w", configFile, err) } defer f.Close() if err = yaml.NewDecoder(f).Decode(cfg); err != nil { return nil, fmt.Errorf("failed to decode config file %s: %w", configFile, err) } return cfg, nil }