package main import ( "encoding/json" "fmt" "io/ioutil" "log" "os" "sort" "strings" "time" "github.com/br0xen/user-config" "github.com/jroimartin/gocui" ) const ( AppName = "gocal" AppVersion = 1 ) type AppState struct { Name string Version int ClientSecret []byte cfg *userConfig.Config account *Account defaultCalendars []string StatusMsg string } var state *AppState func main() { var err error var op string state = &AppState{Name: AppName, Version: AppVersion} state.cfg, err = userConfig.NewConfig(state.Name) if err != nil { panic(err) } if len(os.Args) > 1 { op = os.Args[1] } else { op = "today" } DoVersionCheck() switch op { case "--reinit": // Reset all config for _, v := range state.cfg.GetKeyList() { fmt.Println("Deleting Key: " + v) state.cfg.DeleteKey(v) } case "today": // Show everything on the calendar for today InitComm() // TODO: Get calendars flagged as default list := state.account.GetCalendarList() var todayEvents []Event if len(list) > 0 { for defIdx := range state.defaultCalendars { for i := range list { if list[i].Deleted { // Deleted calendar, next please continue } if state.defaultCalendars[defIdx] == list[i].Id { todayEvents = append(todayEvents, list[i].GetTodaysEvents()...) } } } sort.Sort(ByStartTime(todayEvents)) for _, e := range todayEvents { if e.GetStartTime() == "00:00:00" { fmt.Println("[All Day ] " + e.Summary) } else { fmt.Println("[" + e.GetStartTime() + "] " + e.Summary) } } } else { fmt.Printf("No calendars found.\n") } case "config": InitComm() DoConfig() case "defaults": // Show Defaults InitComm() case "add": // Quick event add to primary calendar quickAddText := strings.Join(os.Args[2:], " ") e, err := state.account.GetDefaultCalendar().QuickAdd(quickAddText) if err != nil { fmt.Println(err.Error()) } else { fmt.Println(e.ToCLIString()) } case "bail": // Just initialize communications and bail InitComm() } // All done, save the account state saveAccountState() } func InitComm() { var err error sec := state.cfg.GetBytes("ClientSecret") tkn := state.cfg.GetBytes("Token") state.account, err = GetAccount(sec, tkn) if err != nil { log.Fatalf("Unable to get Account: %v", err) } // Save the Raw Token state.cfg.SetBytes("Token", state.account.CC.GetRawToken()) // If the 'defaultCalendars' cfg is set to 'primary' (or not at all) // we need to actually but the primary cal id in there var defCal []string defCal, err = state.cfg.GetArray("defaultCalendars") if len(defCal) == 0 || (len(defCal) == 1 && defCal[0] == "primary") { gCal, err := state.account.Service.CalendarList.Get("primary").Do() if err == nil { defCal = []string{gCal.Id} state.cfg.SetArray("defaultCalendars", defCal) } } state.defaultCalendars = defCal loadAccountState() } func DoVersionCheck() { confVer, _ := state.cfg.GetInt("version") for confVer < state.Version { // Update from confVer to state.Version switch confVer { case 0: // Initializing the app fmt.Println("Initializing gocal") for { fmt.Println("Client Secret Filename (Ctrl-C to exit): ") var fn string if _, err := fmt.Scan(&fn); err == nil { var b []byte b, err = ioutil.ReadFile(fn) if len(b) > 0 && err == nil { state.cfg.SetBytes("ClientSecret", b) break } } } state.cfg.SetInt("version", 1) state.cfg.SetArray("defaultCalendars", []string{"primary"}) } state.ClientSecret = state.cfg.GetBytes("ClientSecret") // Refetch the version from the config confVer, _ = state.cfg.GetInt("version") } } // saveAccountState saves all the bits of cache from the account // into our config func saveAccountState() { var err error var calListJson []byte if calListJson, err = json.Marshal(state.account.CalendarList); err == nil { if err = state.cfg.SetBytes("calendarList", calListJson); err != nil { fmt.Println("saveAccountState:calendarList error:", err) } if err = state.cfg.SetDateTime("calListUseBy", state.account.CalListUseBy); err != nil { fmt.Println("saveAccountState:calListUseBy error:", err) } } if err = state.cfg.SetArray("defaultCalendars", state.defaultCalendars); err != nil { fmt.Println("saveAccountState:defaultCalendars error:", err) } } // loadAccountState loads all the bits of cache from the config // into the account func loadAccountState() { var err error var calListJson []byte calListJson = state.cfg.GetBytes("calendarList") if err = json.Unmarshal(calListJson, &state.account.CalendarList); err == nil { if len(state.account.CalendarList) > 0 { if err != nil { fmt.Println("error: ", err) } if state.account.CalListUseBy, err = state.cfg.GetDateTime("calListUseBy"); err != nil { fmt.Println("error: ", err) } } } else { fmt.Println("error: ", err) } } // BEGIN CONFIG SCREEN FUNCTIONS // func DoConfig() { g, err := gocui.NewGui(gocui.Output256) if err != nil { log.Panicln(err) } defer g.Close() g.Cursor = true g.SetManagerFunc(configLayout) // Set up some keybindings if err := g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quit); err != nil { log.Panicln(err) } if err := g.SetKeybinding("default_calendars", gocui.KeyArrowUp, gocui.ModNone, cursorUp); err != nil { log.Panicln(err) } if err := g.SetKeybinding("default_calendars", 'k', gocui.ModNone, cursorUp); err != nil { log.Panicln(err) } if err := g.SetKeybinding("default_calendars", gocui.KeyArrowDown, gocui.ModNone, cursorDown); err != nil { log.Panicln(err) } if err := g.SetKeybinding("default_calendars", 'j', gocui.ModNone, cursorDown); err != nil { log.Panicln(err) } if err := g.SetKeybinding("default_calendars", gocui.KeySpace, gocui.ModNone, toggleCalendar); err != nil { log.Panicln(err) } if err := g.SetKeybinding("default_calendars", gocui.KeyEnter, gocui.ModNone, toggleCalendar); err != nil { log.Panicln(err) } // Kick off the main loop if err := g.MainLoop(); err != nil && err != gocui.ErrQuit { log.Panicln(err) } } func configLayout(g *gocui.Gui) error { list := state.account.GetCalendarList() height := len(list) + 4 width, _ := g.Size() if v, err := g.SetView("default_calendars", 0, 0, width-1, height); err != nil { v.Highlight = true v.SelBgColor = gocui.ColorGreen v.SelFgColor = gocui.ColorBlack if err != gocui.ErrUnknownView { return err } drawCalList(g, v) if _, err := g.SetCurrentView("default_calendars"); err != nil { return err } } return nil } func quit(g *gocui.Gui, v *gocui.View) error { return gocui.ErrQuit } func toggleCalendar(g *gocui.Gui, v *gocui.View) error { _, cy := v.Cursor() calList := state.account.GetCalendarList() if v != nil { for i := range state.defaultCalendars { state.StatusMsg = time.Now().Format(time.RFC3339) if state.defaultCalendars[i] == calList[cy].Id { } } } drawCalList(g, v) return nil } func cursorUp(g *gocui.Gui, v *gocui.View) error { if v != nil { ox, oy := v.Origin() cx, cy := v.Cursor() if err := v.SetCursor(cx, cy-1); err != nil && oy > 0 { if err := v.SetOrigin(ox, oy-1); err != nil { return err } } } return nil } func cursorDown(g *gocui.Gui, v *gocui.View) error { if v != nil { cx, cy := v.Cursor() if err := v.SetCursor(cx, cy+1); err != nil { ox, oy := v.Origin() if err := v.SetOrigin(ox, oy+1); err != nil { return err } } } return nil } func drawCalList(g *gocui.Gui, v *gocui.View) error { width, _ := g.Size() list := state.account.GetCalendarList() for i := range list { isDef := false for defIdx := range state.defaultCalendars { if list[i].Id == state.defaultCalendars[defIdx] { isDef = true break } } calSumTxt := "[" if isDef { calSumTxt += "*" } else { calSumTxt += " " } calSumTxt += "] " + list[i].Summary spc := strings.Repeat(" ", width-len(calSumTxt)-2) fmt.Fprintln(v, calSumTxt+spc) } fmt.Fprintln(v, "Status: "+state.StatusMsg) return nil } // END CONFIG SCREEN FUNCTIONS //