From a9d880a4864a734319a5bf19c26b9f387d582437 Mon Sep 17 00:00:00 2001 From: Brian Buller Date: Thu, 2 Feb 2017 11:03:02 -0600 Subject: [PATCH] Initial Commit --- .gitignore | 2 + calclient.go | 84 ++++++++++++++++++++++++++++++++ main.go | 135 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 221 insertions(+) create mode 100644 .gitignore create mode 100644 calclient.go create mode 100644 main.go diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ad87cff --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +client_secret.json +gocal diff --git a/calclient.go b/calclient.go new file mode 100644 index 0000000..ce0f617 --- /dev/null +++ b/calclient.go @@ -0,0 +1,84 @@ +package main + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "log" + "net/http" + + calendar "google.golang.org/api/calendar/v3" + + "golang.org/x/oauth2" + "golang.org/x/oauth2/google" +) + +type CalClient struct { + rawClientSecret []byte + rawToken []byte + ctx context.Context + cfg *oauth2.Config + tkn *oauth2.Token + client *http.Client +} + +func CreateClient(secret, tkn []byte) *CalClient { + var err error + c := CalClient{ + rawClientSecret: []byte(secret), + rawToken: []byte(tkn), + } + + c.ctx = context.Background() + c.cfg, err = google.ConfigFromJSON(c.rawClientSecret, calendar.CalendarScope) + + if err != nil { + log.Fatalf("unable to parse client secret file to config: %v", err) + } + + // Try to create the client with the passed token + c.client, err = c.GetClient() + if err != nil { + log.Fatalf("unable to create client: %v", err) + } + return &c +} + +func (c *CalClient) GetClient() (*http.Client, error) { + err := c.tokenFromRaw() + if err != nil { + err = c.tokenFromWeb() + if err != nil { + return nil, err + } + } + return c.cfg.Client(c.ctx, c.tkn), nil +} + +func (c *CalClient) tokenFromRaw() error { + c.tkn = &oauth2.Token{} + tmpToken := bytes.NewBuffer(c.rawToken) + err := json.NewDecoder(tmpToken).Decode(c.tkn) + return err +} + +func (c *CalClient) tokenFromWeb() error { + var err error + authURL := c.cfg.AuthCodeURL("state-token", oauth2.AccessTypeOffline) + fmt.Printf("Go to the following link in your browser then type the authorization code:\n%v\n", authURL) + var code string + if _, err := fmt.Scan(&code); err != nil { + log.Fatalf("Unable to read authorization code %v", err) + } + + c.tkn, err = c.cfg.Exchange(oauth2.NoContext, code) + return err +} + +func (c *CalClient) TokenToRaw() { + var tmpToken bytes.Buffer + t := c.tkn + json.NewEncoder(&tmpToken).Encode(t) + c.rawToken = tmpToken.Bytes() +} diff --git a/main.go b/main.go new file mode 100644 index 0000000..366b043 --- /dev/null +++ b/main.go @@ -0,0 +1,135 @@ +package main + +import ( + "fmt" + "io/ioutil" + "log" + "os" + "time" + + calendar "google.golang.org/api/calendar/v3" + + "github.com/br0xen/user-config" +) + +const ( + AppName = "gocal" + AppVersion = 1 +) + +type AppState struct { + Name string + Version int + ClientSecret []byte + cfg *userConfig.Config +} + +var state *AppState + +func main() { + var err error + state = &AppState{Name: AppName, Version: AppVersion} + state.cfg, err = userConfig.NewConfig(state.Name) + + if err != nil { + panic(err) + } + + for i := range os.Args { + switch { + case os.Args[i] == "--reinit": + // Reset all config + for _, v := range state.cfg.GetKeyList() { + state.cfg.DeleteKey(v) + } + } + } + + DoVersionCheck() + + sec := state.cfg.GetBytes("ClientSecret") + tkn := state.cfg.GetBytes("Token") + + c := CreateClient(sec, tkn) + + c.TokenToRaw() + state.cfg.SetBytes("Token", c.rawToken) + + srv, err := calendar.New(c.client) + + if err != nil { + log.Fatalf("unable to retrieve calendar Client %v", err) + } + + listSvc := srv.CalendarList.List() + list, err := listSvc.Do() + if err != nil { + log.Fatalf("unable to retrieve calendar list %v", err) + } + if len(list.Items) > 0 { + for i := range list.Items { + if list.Items[i].Deleted { + // Deleted calendar, next please + continue + } + fmt.Print("(" + list.Items[i].Id + ") ") + fmt.Print(list.Items[i].Summary + "; ") + fmt.Println(list.Items[i].Description) + } + } else { + fmt.Printf("No calendars found.\n") + } + + fmt.Println("\n-====-\n") + + t := time.Now().Format(time.RFC3339) + events, err := srv.Events.List("primary").ShowDeleted(false). + SingleEvents(true).TimeMin(t).MaxResults(10).OrderBy("startTime").Do() + if err != nil { + log.Fatalf("Unable to retrieve next ten of the user's events. %v", err) + } + + fmt.Println("Upcoming events:") + if len(events.Items) > 0 { + for _, i := range events.Items { + var when string + // If the DateTime is an empty string the Event is an all-day Event. + // So only Date is available. + if i.Start.DateTime != "" { + when = i.Start.DateTime + } else { + when = i.Start.Date + } + fmt.Printf("%s (%s)\n", i.Summary, when) + } + } else { + fmt.Printf("No upcoming events found.\n") + } +} + +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.ClientSecret = state.cfg.GetBytes("ClientSecret") + // Refetch the version from the config + confVer, _ = state.cfg.GetInt("version") + } +}