Elia el Lazkani
d43443f885
* Moved the code away from all the if statements and switches * Built a better foundation to build upon for future expension * BUGFIX: Now if windows users want to download a PNG it can be in color
256 lines
7.4 KiB
Go
256 lines
7.4 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"io/ioutil"
|
|
"log"
|
|
"net/http"
|
|
"os"
|
|
"strings"
|
|
|
|
kingpin "gopkg.in/alecthomas/kingpin.v2"
|
|
)
|
|
|
|
// VERSION The version release
|
|
const VERSION = "0.0.5"
|
|
|
|
// AUTHOR The author name
|
|
const AUTHOR = "Elia el Lazkani"
|
|
|
|
var (
|
|
switchesFlags = []string{"m", "u", "M", "0", "1", "2", "A", "F", "n", "q", "Q", "T", "p"}
|
|
)
|
|
|
|
// We create a weather struct with the url
|
|
type weather struct {
|
|
url string
|
|
}
|
|
|
|
// `weather` method to handle creating new requests with proper headers
|
|
func (w weather) newRequest(headers map[string]string) *http.Request {
|
|
req, err := http.NewRequest("GET", w.url, nil)
|
|
if err != nil {
|
|
log.Fatal("Error reading request. ", err)
|
|
}
|
|
return w.formatHeader(req, headers)
|
|
}
|
|
|
|
// `weather` method to format the headers and inject required ones
|
|
func (w weather) formatHeader(req *http.Request, headers map[string]string) *http.Request {
|
|
req.Header.Set("Content-Type", "text/plain; charset=utf-8")
|
|
// I had to read the wttr.in code to figure this one out.
|
|
// It wasn't Golang, it was 2 wasted hours that I won't be getting back.
|
|
req.Header.Set("User-Agent", "curl")
|
|
|
|
for key, value := range headers {
|
|
req.Header.Add(key, value)
|
|
}
|
|
return req
|
|
}
|
|
|
|
// `weather` method to create a client and get a weather response
|
|
// This method handles printing or downloading the weather
|
|
func (w weather) get(req *http.Request, download bool) {
|
|
client := &http.Client{}
|
|
client.Jar = nil
|
|
|
|
resp, err := client.Do(req)
|
|
if err != nil {
|
|
log.Fatal("Error reading response. ", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if download {
|
|
w.download(resp)
|
|
} else {
|
|
w.print(resp)
|
|
}
|
|
}
|
|
|
|
// `weather` method to print the weather
|
|
func (w weather) print(resp *http.Response) {
|
|
body, err := ioutil.ReadAll(resp.Body)
|
|
if err != nil {
|
|
log.Fatal("Error reading body. ", err)
|
|
}
|
|
fmt.Printf("%s", body)
|
|
}
|
|
|
|
// `weather` method to download the weather
|
|
func (w weather) download(resp *http.Response) {
|
|
splitURL := strings.Split(w.url, "/")
|
|
filename := splitURL[len(splitURL)-1]
|
|
|
|
f, err := os.Create(filename)
|
|
if err != nil {
|
|
return
|
|
}
|
|
defer f.Close()
|
|
|
|
_, err = io.Copy(f, resp.Body)
|
|
}
|
|
|
|
// Wraper function around the weather struct
|
|
func getWheather(url string, headers map[string]string, download bool) {
|
|
|
|
w := weather{url}
|
|
req := w.newRequest(headers)
|
|
w.get(req, download)
|
|
}
|
|
|
|
// Create a method to figure out the command parameters
|
|
// provided and translate them into uri parameters.
|
|
// This will figure out if we need to download the PNG image
|
|
// as well.
|
|
func generateParamFormat() (string, bool, error) {
|
|
var params []string
|
|
var prefix []string
|
|
var download bool
|
|
|
|
prefix = append(prefix, "?")
|
|
|
|
// If --format is specified, let's not bother and simply return it
|
|
if *format != "" {
|
|
return *format, false, nil
|
|
}
|
|
|
|
// If --one-liner is specified, we know the format is set so let's return it
|
|
if *oneLiner {
|
|
return "?format=3", false, nil
|
|
}
|
|
|
|
for i := range switches {
|
|
if *switches[i] {
|
|
params = append(params, switchesFlags[i])
|
|
}
|
|
}
|
|
|
|
if *png {
|
|
prefix[0] = "_"
|
|
|
|
if *transparency >= 0 && *transparency <= 100 {
|
|
params = append(params, strings.Join([]string{"_transparency=", string(*transparency)}, ""))
|
|
}
|
|
|
|
params = append(params, ".png")
|
|
download = true
|
|
}
|
|
|
|
params = append(prefix, strings.Join(params, ""))
|
|
|
|
return strings.Join(params, ""), download, nil
|
|
}
|
|
|
|
// Defining the command-line interface
|
|
var (
|
|
app = kingpin.New("go-cmw", "A small terminal wrapper around the wttr.in weather endpoint.")
|
|
switches = [13]*bool{
|
|
app.Flag("metric", "Display weather in metric").Short('m').Default("false").Bool(),
|
|
app.Flag("uscs", "Display weather in imperial").Short('u').Default("false").Bool(),
|
|
app.Flag("meter-second", "Display wind in m/s").Short('M').Default("false").Bool(),
|
|
app.Flag("zero", "Show the weather now").Short('z').Default("false").Bool(),
|
|
app.Flag("one", "Show the weather for one day").Short('o').Default("false").Bool(),
|
|
app.Flag("two", "Show the weather for two days").Short('w').Default("false").Bool(),
|
|
app.Flag("ignore-user-agent", "Request ignoring the user agent").Short('A').Default("false").Bool(),
|
|
app.Flag("follow-link", "Follow link redirect").Short('F').Default("true").Bool(),
|
|
app.Flag("narrow", "Display weather in narrow view").Short('n').Default("false").Bool(),
|
|
app.Flag("quiet", "Add the quiet flag").Short('q').Default("false").Bool(),
|
|
app.Flag("super-quiet", "Add the super quiet flag").Short('Q').Default("false").Bool(),
|
|
app.Flag("no-colors", "Disable displaying colors (always enabled on windows").Short('N').Default("false").Bool(),
|
|
app.Flag("add-frame", "Add a frame to the output").Short('p').Default("false").Bool()}
|
|
png = app.Flag("png", "Download a weather PNG image").Short('P').Default("false").Bool()
|
|
v2 = app.Flag("v2", "Use the v2 endpoint").Default("false").Bool()
|
|
transparency = app.Flag("transparency", "Set transparency level (0-100) (PNG only)").Short('t').Default("0").Int()
|
|
format = app.Flag("format", "Query format (overrides everything else)").Short('f').OverrideDefaultFromEnvar("GO_CMW_FORMAT").String()
|
|
location = app.Flag("location", "Specify explicite location").Short('L').OverrideDefaultFromEnvar("GO_CMW_LOCATION").String()
|
|
language = app.Flag("language", "Specify explicite language").Short('l').OverrideDefaultFromEnvar("GO_CMW_LANGUAGE").String()
|
|
oneLiner = app.Flag("one-liner", "One liner outpet (for the terminal multiplexer lovers out there)").Short('O').Default("false").Bool()
|
|
extraInformation = app.Flag("extra-information", "Print extra information").Default("false").Bool()
|
|
)
|
|
|
|
// Special help menu
|
|
func printExtraInformation() {
|
|
eInfo := `
|
|
Supported Location Types
|
|
------------------------
|
|
City name: Paris
|
|
Unicode name: Москва
|
|
Airport code (3 letters): muc
|
|
Domain name: @stackoverflow.com
|
|
Area code: 94107
|
|
GPS coordinates: -78.46,106.79
|
|
|
|
Special Location
|
|
----------------
|
|
Moon phase (add ,+US or
|
|
,+France for these cities): moon
|
|
Moon phase for a date: moon@2016-10-25
|
|
|
|
Supported languages
|
|
-------------------
|
|
|
|
Supported: af da de el et fr fa hu id it nb nl
|
|
pl pt-br ro ru tr uk vi
|
|
`
|
|
|
|
println(eInfo)
|
|
}
|
|
|
|
// Create a function to parse all the command line parameters
|
|
// provided and save them in the parameter struct.
|
|
func flagParser() {
|
|
|
|
app.Version(VERSION)
|
|
app.Author(AUTHOR)
|
|
|
|
kingpin.MustParse(app.Parse(os.Args[1:]))
|
|
|
|
if *extraInformation {
|
|
printExtraInformation()
|
|
os.Exit(0)
|
|
}
|
|
|
|
// Windows does not have color encoding
|
|
// so let's make sure windows users are happy
|
|
if os.Getenv("TERM") == "" && !*png {
|
|
*switches[11] = true
|
|
}
|
|
}
|
|
|
|
// Create a function to generate the url that we'll be calling shortly.
|
|
func generateURL(domain string, v2 bool, location string, lang string, affix string) ([]string, map[string]string) {
|
|
var link []string
|
|
var headers = make(map[string]string)
|
|
|
|
link = append(link, "https://")
|
|
|
|
if v2 {
|
|
link = append(link, "v2.")
|
|
}
|
|
|
|
link = append(link, domain, "/")
|
|
|
|
if location != "" {
|
|
link = append(link, location)
|
|
}
|
|
|
|
if affix != "" {
|
|
link = append(link, affix)
|
|
}
|
|
|
|
if lang != "" {
|
|
headers["Accept-Language"] = lang
|
|
}
|
|
|
|
return link, headers
|
|
}
|
|
|
|
// This is the main function that glues everything together.
|
|
func main() {
|
|
var domain string = "wttr.in"
|
|
flagParser()
|
|
affix, download, _ := generateParamFormat()
|
|
link, headers := generateURL(domain, *v2, *location, *language, affix)
|
|
getWheather(strings.Join(link, ""), headers, download)
|
|
}
|