package main import ( "fmt" "io" "io/ioutil" "log" "net/http" "os" "strings" flag "github.com/spf13/pflag" ) // Create a list of constants to figure out // the command line flags and translate them // into uri flags. const ( METRIC = 1 << iota USCS METERSECOND ZERO ONE TWO IGNOREUSERAGENT FOLLOWLINK NARROW QUIET SUPERQUIET NOCOLORS ADDFRAME MIDTRANSPARENCY TRANSPARENCY LOCATION LANGUAGE PNG VTWO ) // Create a list of constants to define // uri switches by flag. const ( MetricSwitch = "m" UscsSwitch = "u" MeterSecondSwitch = "M" ZeroSwitch = "0" OneSwitch = "1" TwoSwitch = "2" IgnoreUserAgentSwitch = "A" FollowLinkSwitch = "F" NarrowSwitch = "n" QuietSwitch = "q" SuperQuietSwitch = "Q" NoColorsSwitch = "T" AddFrameSwitch = "p" MidTransparencySwitch = "T" ) // Define a command parameters struct. // This will hold all the configurations // set by the command line parameters provided. type cmdParams struct { Flags int TransparencyLevel int Location string Language string } // 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 (cmdP *cmdParams) generateParamFormat() (string, bool, error) { var params []string var prefix []string var download bool prefix = append(prefix, "?") if cmdP.Flags&METRIC == METRIC { params = append(params, MetricSwitch) } if cmdP.Flags&USCS == USCS { params = append(params, UscsSwitch) } if cmdP.Flags&METERSECOND == METERSECOND { params = append(params, MeterSecondSwitch) } if cmdP.Flags&ZERO == ZERO { params = append(params, ZeroSwitch) } if cmdP.Flags&ONE == ONE { params = append(params, OneSwitch) } if cmdP.Flags&TWO == TWO { params = append(params, TwoSwitch) } if cmdP.Flags&IGNOREUSERAGENT == IGNOREUSERAGENT { params = append(params, IgnoreUserAgentSwitch) } if cmdP.Flags&FOLLOWLINK == FOLLOWLINK { params = append(params, FollowLinkSwitch) } if cmdP.Flags&NARROW == NARROW { params = append(params, NarrowSwitch) } if cmdP.Flags&QUIET == QUIET { params = append(params, QuietSwitch) } if cmdP.Flags&SUPERQUIET == SUPERQUIET { params = append(params, SuperQuietSwitch) } if cmdP.Flags&NOCOLORS == NOCOLORS { params = append(params, NoColorsSwitch) } if cmdP.Flags&ADDFRAME == ADDFRAME { params = append(params, AddFrameSwitch) } if cmdP.Flags&MIDTRANSPARENCY == MIDTRANSPARENCY { params = append(params, MidTransparencySwitch) } if cmdP.Flags&PNG == PNG { prefix[0] = "_" if cmdP.Flags&TRANSPARENCY == TRANSPARENCY && cmdP.TransparencyLevel >= 0 && cmdP.TransparencyLevel <= 100 { params = append(params, strings.Join([]string{"_transparency=", string(cmdP.TransparencyLevel)}, "")) } params = append(params, ".png") download = true } params = append(prefix, strings.Join(params, "")) return strings.Join(params, ""), download, nil } // TODO: // * Fix the description for help // // Create a function to parse all the command line parameters // provided and save them in the parameter struct. func flagParser(cmdP *cmdParams) { metric := flag.Bool("metric", false, "Display weather in metric") uscs := flag.Bool("uscs", false, "Display weather Imperial") meterSecond := flag.Bool("meter-second", false, "Display wind in m/s") zero := flag.Bool("zero", false, "Show the weather now") one := flag.Bool("one", false, "Show the weather for one day") two := flag.Bool("two", false, "Show the weather for two days") ignoreUserAgent := flag.Bool("ignore-user-agent", false, "Request ignoring the user agent") followLink := flag.Bool("follow-link", true, "Follow link redirect") narrow := flag.Bool("narrow", false, "Display weather in narrow view") quiet := flag.Bool("quiet", false, "Add the quiet flag") superQuiet := flag.Bool("super-quiet", false, "Add the super quiet flag") noColors := flag.Bool("no-colors", false, "Disable displaying colors") addFrame := flag.Bool("add-frame", false, "Add a frame to the output") midTransparency := flag.Bool("mid-transparency", false, "Enable mid-transparency (PNG only)") transparency := flag.Bool("transparency", false, "Enable transparency (PNG only)") png := flag.Bool("png", false, "Download a weather PNG image") v2 := flag.Bool("v2", false, "Use the v2 endpoint") transparencyLevel := flag.Int("transparency-level", 0, "Set transparency level between 0 and 100 ") location := flag.String("location", "", "Specify explicite location") language := flag.String("language", "", "Speficy explicite language") flag.Parse() if *metric { cmdP.Flags += METRIC } if *uscs { cmdP.Flags += USCS } if *meterSecond { cmdP.Flags += METERSECOND } if *zero { cmdP.Flags += ZERO } if *one { cmdP.Flags += ONE } if *two { cmdP.Flags += TWO } if *ignoreUserAgent { cmdP.Flags += IGNOREUSERAGENT } if *followLink { cmdP.Flags += FOLLOWLINK } if *narrow { cmdP.Flags += NARROW } if *quiet { cmdP.Flags += QUIET } if *superQuiet { cmdP.Flags += SUPERQUIET } if *noColors { cmdP.Flags += NOCOLORS } if *addFrame { cmdP.Flags += ADDFRAME } if *midTransparency { cmdP.Flags += MIDTRANSPARENCY } if *transparency { cmdP.Flags += TRANSPARENCY cmdP.TransparencyLevel = *transparencyLevel } if *png { cmdP.Flags += PNG } if *v2 { cmdP.Flags += VTWO } if *location != "" { cmdP.Flags += LOCATION cmdP.Location = *location } if *language != "" { cmdP.Flags += LANGUAGE cmdP.Language = *language } } // Create a function to generate the url that we'll be calling shortly. func generateURL(domain string, v2 bool, location string, lang string, format 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 format != "" { link = append(link, format) } if lang != "" { headers["Accept-Language"] = lang } return link, headers } // TODO: // * Create interfaces to print and save and migrate code to them // // Figure out if we need to print or download a PNG image // and call the server accordingly. func getWheather(url string, headers map[string]string, download bool) { req, err := http.NewRequest("GET", url, nil) if err != nil { log.Fatal("Error reading request. ", err) } 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) } 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 { splitURL := strings.Split(url, "/") filename := splitURL[len(splitURL)-1] f, err := os.Create(filename) if err != nil { return } defer f.Close() _, err = io.Copy(f, resp.Body) } else { body, err := ioutil.ReadAll(resp.Body) if err != nil { log.Fatal("Error reading body. ", err) } fmt.Printf("%s\n", body) } } // This is the main function that glues everything together. func main() { var params cmdParams = cmdParams{} var domain string = "wttr.in" flagParser(¶ms) format, download, _ := params.generateParamFormat() link, headers := generateURL(domain, params.Flags&VTWO == VTWO, params.Location, params.Language, format) getWheather(strings.Join(link, ""), headers, download) }