mirror of https://github.com/chubin/wttr.in
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
173 lines
4.2 KiB
173 lines
4.2 KiB
// This code represents wttr.in view v1.
|
|
// It is based on wego (github.com/schachmat/wego) from which it diverged back in 2016.
|
|
|
|
//nolint:forbidigo,funlen,gocognit,cyclop
|
|
package v1
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"flag"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"log"
|
|
"os"
|
|
"os/user"
|
|
"path"
|
|
"regexp"
|
|
"strings"
|
|
|
|
"github.com/mattn/go-colorable"
|
|
"github.com/mattn/go-runewidth"
|
|
)
|
|
|
|
type Configuration struct {
|
|
APIKey string
|
|
City string
|
|
Numdays int
|
|
Imperial bool
|
|
WindUnit bool
|
|
Inverse bool
|
|
Lang string
|
|
Narrow bool
|
|
LocationName string
|
|
WindMS bool
|
|
RightToLeft bool
|
|
}
|
|
|
|
type global struct {
|
|
ansiEsc *regexp.Regexp
|
|
config Configuration
|
|
configpath string
|
|
debug bool
|
|
}
|
|
|
|
const (
|
|
wuri = "http://127.0.0.1:5001/premium/v1/weather.ashx?"
|
|
suri = "http://127.0.0.1:5001/premium/v1/search.ashx?"
|
|
slotcount = 4
|
|
)
|
|
|
|
func (g *global) configload() error {
|
|
b, err := ioutil.ReadFile(g.configpath)
|
|
if err == nil {
|
|
return json.Unmarshal(b, &g.config)
|
|
}
|
|
|
|
return err
|
|
}
|
|
|
|
func (g *global) configsave() error {
|
|
j, err := json.MarshalIndent(g.config, "", "\t")
|
|
if err == nil {
|
|
return ioutil.WriteFile(g.configpath, j, 0o600)
|
|
}
|
|
|
|
return err
|
|
}
|
|
|
|
func (g *global) init() {
|
|
flag.IntVar(&g.config.Numdays, "days", 3, "Number of days of weather forecast to be displayed")
|
|
flag.StringVar(&g.config.Lang, "lang", "en", "Language of the report")
|
|
flag.StringVar(&g.config.City, "city", "New York", "City to be queried")
|
|
flag.BoolVar(&g.debug, "debug", false, "Print out raw json response for debugging purposes")
|
|
flag.BoolVar(&g.config.Imperial, "imperial", false, "Use imperial units")
|
|
flag.BoolVar(&g.config.Inverse, "inverse", false, "Use inverted colors")
|
|
flag.BoolVar(&g.config.Narrow, "narrow", false, "Narrow output (two columns)")
|
|
flag.StringVar(&g.config.LocationName, "location_name", "", "Location name (used in the caption)")
|
|
flag.BoolVar(&g.config.WindMS, "wind_in_ms", false, "Show wind speed in m/s")
|
|
flag.BoolVar(&g.config.RightToLeft, "right_to_left", false, "Right to left script")
|
|
g.configpath = os.Getenv("WEGORC")
|
|
if g.configpath == "" {
|
|
usr, err := user.Current()
|
|
if err != nil {
|
|
log.Fatalf("%v\nYou can set the environment variable WEGORC to point to your config file as a workaround.", err)
|
|
}
|
|
g.configpath = path.Join(usr.HomeDir, ".wegorc")
|
|
}
|
|
g.config.APIKey = ""
|
|
g.config.Imperial = false
|
|
g.config.Lang = "en"
|
|
err := g.configload()
|
|
var pathError *os.PathError
|
|
if errors.Is(err, pathError) {
|
|
log.Printf("No config file found. Creating %s ...", g.configpath)
|
|
if err2 := g.configsave(); err2 != nil {
|
|
log.Fatal(err2)
|
|
}
|
|
} else if err != nil {
|
|
log.Fatalf("could not parse %v: %v", g.configpath, err)
|
|
}
|
|
|
|
g.ansiEsc = regexp.MustCompile("\033.*?m")
|
|
}
|
|
|
|
func Cmd() error {
|
|
g := global{}
|
|
g.init()
|
|
|
|
flag.Parse()
|
|
|
|
r, err := g.getDataFromAPI()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if r.Data.Req == nil || len(r.Data.Req) < 1 {
|
|
if r.Data.Err != nil && len(r.Data.Err) >= 1 {
|
|
log.Fatal(r.Data.Err[0].Msg)
|
|
}
|
|
log.Fatal("Malformed response.")
|
|
}
|
|
locationName := r.Data.Req[0].Query
|
|
if g.config.LocationName != "" {
|
|
locationName = g.config.LocationName
|
|
}
|
|
if g.config.Lang == "he" || g.config.Lang == "ar" || g.config.Lang == "fa" {
|
|
g.config.RightToLeft = true
|
|
}
|
|
if caption, ok := localizedCaption()[g.config.Lang]; !ok {
|
|
fmt.Printf("Weather report: %s\n\n", locationName)
|
|
} else {
|
|
if g.config.RightToLeft {
|
|
caption = locationName + " " + caption
|
|
space := strings.Repeat(" ", 125-runewidth.StringWidth(caption))
|
|
fmt.Printf("%s%s\n\n", space, caption)
|
|
} else {
|
|
fmt.Printf("%s %s\n\n", caption, locationName)
|
|
}
|
|
}
|
|
stdout := colorable.NewColorableStdout()
|
|
|
|
if r.Data.Cur == nil || len(r.Data.Cur) < 1 {
|
|
log.Fatal("No weather data available.")
|
|
}
|
|
out := g.formatCond(make([]string, 5), r.Data.Cur[0], true)
|
|
for _, val := range out {
|
|
if g.config.RightToLeft {
|
|
fmt.Fprint(stdout, strings.Repeat(" ", 94))
|
|
} else {
|
|
fmt.Fprint(stdout, " ")
|
|
}
|
|
fmt.Fprintln(stdout, val)
|
|
}
|
|
|
|
if g.config.Numdays == 0 {
|
|
return nil
|
|
}
|
|
if r.Data.Weather == nil {
|
|
log.Fatal("No detailed weather forecast available.")
|
|
}
|
|
for _, d := range r.Data.Weather {
|
|
lines, err := g.printDay(d)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for _, val := range lines {
|
|
fmt.Fprintln(stdout, val)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|