diff options
-rw-r--r-- | README.org | 31 | ||||
-rw-r--r-- | cmd/cetus-nasa/cetus-nasa.go | 105 | ||||
-rw-r--r-- | pkg/nasa/apod.go | 109 |
3 files changed, 245 insertions, 0 deletions
diff --git a/README.org b/README.org index 9363c21..9985136 100644 --- a/README.org +++ b/README.org @@ -7,6 +7,37 @@ sources for fetching the background. *Dependency*: [[https://feh.finalrewind.org/][feh]] +* NASA Astronomy Picture of the Day +cetus-nasa uses NASA's API to get the Astronomy Picture of the Day. +** Features +- set APOD as background +- fetch information on APOD +- choose custom date +- choose date randomly +** Examples +#+BEGIN_SRC sh +# set currently APOD as background +cetus-nasa + +# set date randomly +cetus-nasa -random + +# set 2020-01-05 APOD as background +cetus-nasa -date 2020-01-05 + +# change api endpoint & api key +cetus-nasa -api https://api.nasa.gov/planetary/apod \ + -api-key DEMO_KEY + +# don't set background, just fetch information +cetus-nasa -fetch-only + +# don't output anything +cetus-nasa -quiet + +# don't set background, just fetch & don't output anything +cetus-nasa -quiet -fetch-only # why would anyone do this? +#+END_SRC * Demo I just run some cetus commands on my computer, nothing fancy. I'll make better demo videos someday. diff --git a/cmd/cetus-nasa/cetus-nasa.go b/cmd/cetus-nasa/cetus-nasa.go new file mode 100644 index 0000000..96b83fc --- /dev/null +++ b/cmd/cetus-nasa/cetus-nasa.go @@ -0,0 +1,105 @@ +// Copyright (c) 2020, Andinus <andinus@inventati.org> + +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. + +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +package main + +import ( + "flag" + "fmt" + "log" + "time" + + "framagit.org/andinus/cetus/pkg" + "framagit.org/andinus/cetus/pkg/nasa" +) + +var ( + quiet bool + fetchOnly bool + + api string + apiKey string + date string + random bool + dateHelp string + dateDefault string + timeout time.Duration +) + +func main() { + var ( + picturePath string + apodRes nasa.APOD + err error + apodInfo map[string]string + ) + + dateHelp = fmt.Sprintf("Choose a random date between 1995-06-16 & %s", + time.Now().UTC().Format("2006-01-02")) + dateDefault = time.Now().UTC().Format("2006-01-02") + + parseFlags() + + // Convert timeout to seconds + timeout = timeout * time.Second + + if random { + date = nasa.RandDate() + } + + apodInfo = make(map[string]string) + apodInfo["api"] = api + apodInfo["apiKey"] = apiKey + apodInfo["date"] = date + + apodRes, err = nasa.APODPath(apodInfo, timeout) + if err != nil { + if len(apodRes.Msg) != 0 { + log.Println("Message: ", apodRes.Msg) + } + log.Fatal(err) + } + + if !quiet { + fmt.Printf("Title: %s\n\n", apodRes.Title) + fmt.Printf("Copyright: %s\n", apodRes.Copyright) + fmt.Printf("Date: %s\n\n", apodRes.Date) + fmt.Printf("URL: %s\n\n", apodRes.HDURL) + fmt.Printf("Explanation: %s\n", apodRes.Explanation) + } + + // if fetchOnly is true then don't set background + if fetchOnly { + return + } + picturePath = apodRes.HDURL + err = background.Set(picturePath) + if err != nil { + log.Fatal(err) + } +} + +func parseFlags() { + flag.BoolVar(&quiet, "quiet", false, "No output") + flag.BoolVar(&fetchOnly, "fetch-only", false, "Don't set background, only fetch info") + flag.BoolVar(&random, "random", false, dateHelp) + + flag.StringVar(&api, "api", "https://api.nasa.gov/planetary/apod", "APOD API URL") + flag.StringVar(&apiKey, "api-key", "DEMO_KEY", "api.nasa.gov key for expanded usage") + flag.StringVar(&date, "date", dateDefault, "Date of the APOD image to retrieve") + + flag.DurationVar(&timeout, "timeout", 32*time.Second, "Timeout for http client in seconds") + flag.Parse() + +} diff --git a/pkg/nasa/apod.go b/pkg/nasa/apod.go new file mode 100644 index 0000000..62eea96 --- /dev/null +++ b/pkg/nasa/apod.go @@ -0,0 +1,109 @@ +// Copyright (c) 2020, Andinus <andinus@inventati.org> + +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. + +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +package nasa + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "math/rand" + "net/http" + "regexp" + "time" +) + +// APOD holds responses +type APOD struct { + Copyright string `json:"copyright"` + Date string `json:"date"` + Explanation string `json:"explanation"` + HDURL string `json:"hdurl"` + MediaType string `json:"media_type"` + ServiceVersion string `json:"service_version"` + Title string `json:"title"` + URL string `json:"url"` + + Code int `json:"code"` + Msg string `json:"msg"` +} + +// RandDate returns a random date between 1995-06-16 & today +func RandDate() string { + var ( + min int64 + max int64 + sec int64 + delta int64 + date string + ) + min = time.Date(1995, 6, 16, 0, 0, 0, 0, time.UTC).Unix() + max = time.Now().UTC().Unix() + delta = max - min + + sec = rand.Int63n(delta) + min + date = time.Unix(sec, 0).Format("2006-01-02") + + return date +} + +// APODPath returns Astronomy Picture of the Day path +func APODPath(apodInfo map[string]string, timeout time.Duration) (APOD, error) { + var err error + apodRes := APOD{} + + // validate date + re := regexp.MustCompile("((19|20)\\d\\d)-(0?[1-9]|1[012])-(0?[1-9]|[12][0-9]|3[01])") + if !re.MatchString(apodInfo["date"]) { + return apodRes, fmt.Errorf("%s does not match format 'YYYY-MM-DD'", apodInfo["date"]) + } + + client := http.Client{ + Timeout: time.Second * timeout, + } + + req, err := http.NewRequest(http.MethodGet, apodInfo["api"], nil) + if err != nil { + return apodRes, err + } + q := req.URL.Query() + q.Add("api_key", apodInfo["apiKey"]) + q.Add("date", apodInfo["date"]) + req.URL.RawQuery = q.Encode() + + res, err := client.Do(req) + + if err != nil { + fmt.Printf("Error: GET %s\n", apodInfo["api"]) + return apodRes, err + } + defer res.Body.Close() + + resBody, err := ioutil.ReadAll(res.Body) + if err != nil { + return apodRes, err + } + + err = json.Unmarshal([]byte(resBody), &apodRes) + if err != nil { + return apodRes, err + } + + if res.StatusCode != 200 { + return apodRes, fmt.Errorf("Unexpected response status code received: %d %s", + res.StatusCode, http.StatusText(res.StatusCode)) + } + + return apodRes, err +} |