Adapting the calendar quickstart to "wherewhen"
This commit is contained in:
parent
38e96aa0d0
commit
460a0200d4
209
wherewhen/cmd/wherewhen/main.go
Normal file
209
wherewhen/cmd/wherewhen/main.go
Normal file
@ -0,0 +1,209 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
"golang.org/x/oauth2"
|
||||
"golang.org/x/oauth2/google"
|
||||
"google.golang.org/api/calendar/v3"
|
||||
)
|
||||
|
||||
var (
|
||||
nowTime = time.Now()
|
||||
oneYear, _ = time.ParseDuration("365d")
|
||||
clientSecretFlag = flag.String("s", "client_secret.json", "client secret json file")
|
||||
calendarIDFlag = flag.String("i", "primary", "calendar ID to search")
|
||||
endTimeFlag = flag.String("e", nowTime.Format(time.RFC3339), "end time for search")
|
||||
timeWindowFlag = flag.Duration("t", oneYear, "time window duration from before end time")
|
||||
defaultLocationFlag = flag.String("l", "notset", "default location for where")
|
||||
|
||||
locationAliases = map[string]string{
|
||||
"pittsburgh": "dan",
|
||||
"kennywood": "dan",
|
||||
"akron": "sarah",
|
||||
}
|
||||
)
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
b, err := ioutil.ReadFile(*clientSecretFlag)
|
||||
if err != nil {
|
||||
log.Fatalf("Unable to read client secret file: %v", err)
|
||||
}
|
||||
|
||||
config, err := google.ConfigFromJSON(b, calendar.CalendarReadonlyScope)
|
||||
if err != nil {
|
||||
log.Fatalf("Unable to parse client secret file to config: %v", err)
|
||||
}
|
||||
client := getClient(ctx, config)
|
||||
|
||||
srv, err := calendar.New(client)
|
||||
if err != nil {
|
||||
log.Fatalf("Unable to retrieve calendar Client %v", err)
|
||||
}
|
||||
|
||||
endT, err := time.Parse(time.RFC3339, *endTimeFlag)
|
||||
if err != nil {
|
||||
log.Fatalf("Unable to parse end time %q: %v", *endTimeFlag, err)
|
||||
}
|
||||
|
||||
maxResults := int64(2500)
|
||||
startT := endT.Add(-*timeWindowFlag)
|
||||
t := startT.Format(time.RFC3339)
|
||||
events, err := srv.Events.List(*calendarIDFlag).ShowDeleted(false).
|
||||
SingleEvents(true).TimeMin(t).TimeMax(endT.Format(time.RFC3339)).
|
||||
MaxResults(maxResults).OrderBy("startTime").Do()
|
||||
if err != nil {
|
||||
log.Fatalf("Unable to retrieve next %d events. %v", maxResults, err)
|
||||
}
|
||||
|
||||
// days := generateDays(startT, endT)
|
||||
found := []*miniEvent{}
|
||||
defaultLocation := *defaultLocationFlag
|
||||
|
||||
if len(events.Items) > 0 {
|
||||
for _, i := range events.Items {
|
||||
var (
|
||||
startTime string
|
||||
endTime string
|
||||
)
|
||||
if i.Start.DateTime != "" {
|
||||
startTime = i.Start.DateTime
|
||||
endTime = i.End.DateTime
|
||||
} else {
|
||||
startTime = i.Start.Date
|
||||
endTime = i.End.Date
|
||||
}
|
||||
|
||||
evt := &miniEvent{
|
||||
Start: startTime,
|
||||
End: endTime,
|
||||
Location: defaultLocation,
|
||||
Summary: strings.ToLower(strings.TrimSpace(i.Summary)),
|
||||
}
|
||||
|
||||
summ := strings.ToLower(i.Summary)
|
||||
if strings.Contains(summ, "possible") {
|
||||
continue
|
||||
}
|
||||
|
||||
parts := strings.Split(summ, " ")
|
||||
locIdx := 0
|
||||
for i, part := range parts {
|
||||
if part == "in" || part == "with" {
|
||||
locIdx = i + 1
|
||||
}
|
||||
}
|
||||
loc := strings.TrimSpace(parts[locIdx])
|
||||
if locAlias, ok := locationAliases[loc]; ok {
|
||||
loc = locAlias
|
||||
}
|
||||
evt.Location = loc
|
||||
|
||||
found = append(found, evt)
|
||||
}
|
||||
} else {
|
||||
fmt.Fprintf(os.Stderr, "No events found.\n")
|
||||
}
|
||||
|
||||
// TODO: sort found events
|
||||
|
||||
for _, evt := range found {
|
||||
asJson, err := json.Marshal(evt)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "ERROR: %v\n", err)
|
||||
continue
|
||||
}
|
||||
fmt.Printf("%s\n", string(asJson))
|
||||
}
|
||||
}
|
||||
|
||||
func getClient(ctx context.Context, config *oauth2.Config) *http.Client {
|
||||
cacheFile, err := tokenCacheFile()
|
||||
if err != nil {
|
||||
log.Fatalf("Unable to get path to cached credential file. %v", err)
|
||||
}
|
||||
tok, err := tokenFromFile(cacheFile)
|
||||
if err != nil {
|
||||
tok = getTokenFromWeb(config)
|
||||
saveToken(cacheFile, tok)
|
||||
}
|
||||
return config.Client(ctx, tok)
|
||||
}
|
||||
|
||||
func getTokenFromWeb(config *oauth2.Config) *oauth2.Token {
|
||||
authURL := config.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)
|
||||
}
|
||||
|
||||
tok, err := config.Exchange(oauth2.NoContext, code)
|
||||
if err != nil {
|
||||
log.Fatalf("Unable to retrieve token from web %v", err)
|
||||
}
|
||||
return tok
|
||||
}
|
||||
|
||||
func tokenCacheFile() (string, error) {
|
||||
usr, err := user.Current()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
tokenCacheDir := filepath.Join(usr.HomeDir, ".credentials")
|
||||
os.MkdirAll(tokenCacheDir, 0700)
|
||||
return filepath.Join(tokenCacheDir,
|
||||
url.QueryEscape("wherewhen.json")), err
|
||||
}
|
||||
|
||||
func tokenFromFile(file string) (*oauth2.Token, error) {
|
||||
f, err := os.Open(file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
t := &oauth2.Token{}
|
||||
err = json.NewDecoder(f).Decode(t)
|
||||
defer f.Close()
|
||||
return t, err
|
||||
}
|
||||
|
||||
func saveToken(file string, token *oauth2.Token) {
|
||||
fmt.Printf("Saving credential file to: %s\n", file)
|
||||
f, err := os.OpenFile(file, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
|
||||
if err != nil {
|
||||
log.Fatalf("Unable to cache oauth token: %v", err)
|
||||
}
|
||||
defer f.Close()
|
||||
json.NewEncoder(f).Encode(token)
|
||||
}
|
||||
|
||||
type miniEvent struct {
|
||||
Start string `json:"start"`
|
||||
StartDate string `json:"start_date"`
|
||||
End string `json:"end"`
|
||||
EndDate string `json:"end_date"`
|
||||
Location string `json:"loc"`
|
||||
Summary string `json:"summ"`
|
||||
}
|
||||
|
||||
func generateDays(startT, endT time.Time) []string {
|
||||
return []string{}
|
||||
}
|
@ -1,137 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
"golang.org/x/oauth2"
|
||||
"golang.org/x/oauth2/google"
|
||||
"google.golang.org/api/calendar/v3"
|
||||
)
|
||||
|
||||
// getClient uses a Context and Config to retrieve a Token
|
||||
// then generate a Client. It returns the generated Client.
|
||||
func getClient(ctx context.Context, config *oauth2.Config) *http.Client {
|
||||
cacheFile, err := tokenCacheFile()
|
||||
if err != nil {
|
||||
log.Fatalf("Unable to get path to cached credential file. %v", err)
|
||||
}
|
||||
tok, err := tokenFromFile(cacheFile)
|
||||
if err != nil {
|
||||
tok = getTokenFromWeb(config)
|
||||
saveToken(cacheFile, tok)
|
||||
}
|
||||
return config.Client(ctx, tok)
|
||||
}
|
||||
|
||||
// getTokenFromWeb uses Config to request a Token.
|
||||
// It returns the retrieved Token.
|
||||
func getTokenFromWeb(config *oauth2.Config) *oauth2.Token {
|
||||
authURL := config.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)
|
||||
}
|
||||
|
||||
tok, err := config.Exchange(oauth2.NoContext, code)
|
||||
if err != nil {
|
||||
log.Fatalf("Unable to retrieve token from web %v", err)
|
||||
}
|
||||
return tok
|
||||
}
|
||||
|
||||
// tokenCacheFile generates credential file path/filename.
|
||||
// It returns the generated credential path/filename.
|
||||
func tokenCacheFile() (string, error) {
|
||||
usr, err := user.Current()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
tokenCacheDir := filepath.Join(usr.HomeDir, ".credentials")
|
||||
os.MkdirAll(tokenCacheDir, 0700)
|
||||
return filepath.Join(tokenCacheDir,
|
||||
url.QueryEscape("calendar-go-quickstart.json")), err
|
||||
}
|
||||
|
||||
// tokenFromFile retrieves a Token from a given file path.
|
||||
// It returns the retrieved Token and any read error encountered.
|
||||
func tokenFromFile(file string) (*oauth2.Token, error) {
|
||||
f, err := os.Open(file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
t := &oauth2.Token{}
|
||||
err = json.NewDecoder(f).Decode(t)
|
||||
defer f.Close()
|
||||
return t, err
|
||||
}
|
||||
|
||||
// saveToken uses a file path to create a file and store the
|
||||
// token in it.
|
||||
func saveToken(file string, token *oauth2.Token) {
|
||||
fmt.Printf("Saving credential file to: %s\n", file)
|
||||
f, err := os.OpenFile(file, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
|
||||
if err != nil {
|
||||
log.Fatalf("Unable to cache oauth token: %v", err)
|
||||
}
|
||||
defer f.Close()
|
||||
json.NewEncoder(f).Encode(token)
|
||||
}
|
||||
|
||||
func main() {
|
||||
ctx := context.Background()
|
||||
|
||||
b, err := ioutil.ReadFile("client_secret.json")
|
||||
if err != nil {
|
||||
log.Fatalf("Unable to read client secret file: %v", err)
|
||||
}
|
||||
|
||||
// If modifying these scopes, delete your previously saved credentials
|
||||
// at ~/.credentials/calendar-go-quickstart.json
|
||||
config, err := google.ConfigFromJSON(b, calendar.CalendarReadonlyScope)
|
||||
if err != nil {
|
||||
log.Fatalf("Unable to parse client secret file to config: %v", err)
|
||||
}
|
||||
client := getClient(ctx, config)
|
||||
|
||||
srv, err := calendar.New(client)
|
||||
if err != nil {
|
||||
log.Fatalf("Unable to retrieve calendar Client %v", err)
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user