strava bitbar plugin
package main
/*
# <bitbar.title>Strava</bitbar.title>
# <bitbar.version>v0.0.1</bitbar.version>
# <bitbar.author>Daniel Cook</bitbar.author>
# <bitbar.author.github>danielecook</bitbar.author.github>
# <bitbar.desc>Bitbar Strava tracker</bitbar.desc>
*/
import (
"fmt"
"io/ioutil"
"log"
"net/http"
"os"
"os/exec"
"time"
keychain "github.com/keybase/go-keychain"
"github.com/tidwall/gjson"
)
const service = "Strava-API-BitBar"
const account = "access_token"
const access_group = "strava.api.bitbar"
var client = &http.Client{Timeout: 10 * time.Second}
type athlete struct {
Bar string
}
func main() {
args := os.Args
/*
Set Access Token
*/
if len(args) > 1 && args[1] == "set_api_key" {
fmt.Printf("---\n")
cmd := exec.Command("osascript", "-e", "display dialog \"Enter Strava-API access_token\" default answer \" \" buttons {\"ok\"} with title \"Enter Strava access_token\" \n set a to the text returned of the result\n")
out, err := cmd.CombinedOutput()
store_api_key(out)
if err != nil {
log.Fatal(err)
}
fmt.Printf("%s\n", out)
os.Exit(0)
}
var access_token = fetch_api_key()
if access_token == "" {
fmt.Println("Set Strava Access Token")
fmt.Println("---")
access_token_line()
os.Exit(0)
}
// Fetch urls
var athlete_url = fmt.Sprintf("https://www.strava.com/api/v3/athlete?access_token=%s", access_token)
athlete := getUrl(athlete_url)
user_id := gjson.Get(athlete, "id").String()
url := fmt.Sprintf("https://www.strava.com/api/v3/athletes/%s/stats?access_token=%s", user_id, access_token)
stats := getUrl(url)
fmt.Printf("🏃♂️ %.0f ", meters_to_miles(gjson.Get(stats, "all_run_totals.distance").Float()))
fmt.Printf("🚲 %.0f\n", meters_to_miles(gjson.Get(stats, "all_ride_totals.distance").Float()))
fmt.Println("---")
fmt.Printf("%s | href=https://www.strava.com/athletes/%s\n", gjson.Get(athlete, "username").String(), user_id)
/* This Year */
fmt.Println("Year to date")
fmt.Printf("🏃♂️ %.0f miles (%v runs) | href=https://www.strava.com/athletes/%v/training/log?sport=Run \n",
meters_to_miles(gjson.Get(stats, "ytd_run_totals.distance").Float()),
gjson.Get(stats, "ytd_run_totals.count").Int(),
user_id)
fmt.Printf("🚲 %.0f miles (%v rides) | href=https://www.strava.com/athletes/%v/training/log?sport=Ride \n",
meters_to_miles(gjson.Get(stats, "ytd_ride_totals.distance").Float()),
gjson.Get(stats, "ytd_ride_totals.count").Int(),
user_id)
/* Last Four Weeks */
fmt.Println("4 weeks")
fmt.Printf("🏃♂️ %.0f miles (%v runs) | href=https://www.strava.com/athletes/%v/training/log?sport=Run \n",
meters_to_miles(gjson.Get(stats, "recent_run_totals.distance").Float()),
gjson.Get(stats, "recent_run_totals.count").Int(),
user_id)
fmt.Printf("🚲 %.0f miles (%v rides) | href=https://www.strava.com/athletes/%v/training/log?sport=Ride \n",
meters_to_miles(gjson.Get(stats, "recent_ride_totals.distance").Float()),
gjson.Get(stats, "recent_ride_totals.count").Int(),
user_id)
/* Access Token */
fmt.Println("---")
access_token_line()
os.Exit(0)
}
func getUrl(url string) string {
resp, err := client.Get(url)
if err != nil {
fmt.Println("API Error")
fmt.Println("---")
access_token_line()
os.Exit(0)
}
b, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Println("API Error")
fmt.Println("---")
access_token_line()
os.Exit(0)
}
return string(b)
}
func store_api_key(api_key []byte) {
/*
Stores the API key
*/
item := keychain.NewItem()
item.SetSecClass(keychain.SecClassGenericPassword)
item.SetService(service)
item.SetAccount(account)
item.SetLabel("Strava API Key for BitBar")
item.SetAccessGroup(access_group)
item.SetData(api_key)
item.SetSynchronizable(keychain.SynchronizableNo)
item.SetAccessible(keychain.AccessibleWhenUnlocked)
err := keychain.AddItem(item)
if err == keychain.ErrorDuplicateItem {
// If duplicate -- overwrite
delete_api_key()
keychain.AddItem(item)
} else {
log.Fatal(err)
}
}
func delete_api_key() {
/*
Deletes the record for the API key
*/
item := keychain.NewItem()
item.SetSecClass(keychain.SecClassGenericPassword)
item.SetService(service)
item.SetAccount(account)
keychain.DeleteItem(item)
}
func fetch_api_key() string {
/*
Fetches the user API key from the keychain
*/
var password string
query := keychain.NewItem()
query.SetSecClass(keychain.SecClassGenericPassword)
query.SetService(service)
query.SetAccount(account)
query.SetAccessGroup(access_group)
query.SetMatchLimit(keychain.MatchLimitOne)
query.SetReturnData(true)
results, err := keychain.QueryItem(query)
if err != nil {
// Error
} else if len(results) != 1 {
// Not found
} else {
password = string(results[0].Data)
}
return password
}
func access_token_line() {
args := os.Args
fmt.Println("Set Access Token | bash=" + args[0] + " param1=set_api_key terminal=false refresh=true")
}
func meters_to_miles(meters float64) float64 {
return meters * 0.0006213712
}