go-cmw/main.go
Elia el Lazkani ae431b71b5 A few code improvements, turns out I didn't need an interface after all.
* Created a proper struct for the weather
* Created methods to the weather struct to handle weather requests
2020-02-25 22:45:50 +01:00

374 lines
8.2 KiB
Go

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 (always enabled on windows)")
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()
// Windows does not have color encoding
// so let's make sure windows users are happy
term := os.Getenv("TERM")
if term == "" {
*noColors = true
}
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
}
type weather struct {
url string
}
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)
}
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
}
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)
}
}
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\n", body)
}
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)
}
// This is the main function that glues everything together.
func main() {
var params cmdParams = cmdParams{}
var domain string = "wttr.in"
flagParser(&params)
format, download, _ := params.generateParamFormat()
link, headers := generateURL(domain, params.Flags&VTWO == VTWO, params.Location, params.Language, format)
getWheather(strings.Join(link, ""), headers, download)
}