From 3e560032e44ba2bde6e8bf66cc07998b27742fb1 Mon Sep 17 00:00:00 2001 From: Elia el Lazkani Date: Sun, 16 Feb 2020 21:06:37 +0000 Subject: [PATCH] Adding the initial code --- LICENSE | 25 ++++ main.go | 348 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 373 insertions(+) create mode 100644 LICENSE create mode 100644 main.go diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..9479ead --- /dev/null +++ b/LICENSE @@ -0,0 +1,25 @@ +BSD 2-Clause License + +Copyright (c) 2020, Elia el Lazkani +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/main.go b/main.go new file mode 100644 index 0000000..3ed971a --- /dev/null +++ b/main.go @@ -0,0 +1,348 @@ +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) +}