plugins/weather/weather.go (view raw)
1package weather
2
3import (
4 _ "embed"
5 "encoding/json"
6 "fmt"
7 "net/http"
8 "strings"
9)
10
11type WeatherData struct {
12 Properties struct {
13 Meta struct {
14 Units struct {
15 AirPressureAtSeaLevel string `json:"air_pressure_at_sea_level"`
16 AirTemperature string `json:"air_temperature"`
17 RelativeHumidity string `json:"relative_humidity"`
18 WindSpeed string `json:"wind_speed"`
19 } `json:"units"`
20 } `json:"meta"`
21 Timeseries []struct {
22 Data struct {
23 Instant struct {
24 Details struct {
25 AirPressureAtSeaLevel float64 `json:"air_pressure_at_sea_level"`
26 AirTemperature float64 `json:"air_temperature"`
27 RelativeHumidity float64 `json:"relative_humidity"`
28 WindSpeed float64 `json:"wind_speed"`
29 } `json:"details"`
30 } `json:"instant"`
31 Next12Hours struct {
32 Summary struct {
33 SymbolCode string `json:"symbol_code"`
34 } `json:"summary"`
35 } `json:"next_12_hours"`
36 } `json:"data"`
37 } `json:"timeseries"`
38 } `json:"properties"`
39}
40
41func getWeatherData(lonlat [2]float64) (*WeatherData, error) {
42 url := "https://api.met.no/weatherapi/locationforecast/2.0/compact.json?lat=%f16&lon=%f"
43 url = fmt.Sprintf(url, lonlat[1], lonlat[0])
44
45 client := &http.Client{}
46 req, _ := http.NewRequest("GET", url, nil)
47 req.Header.Add("User-Agent", "taigobot github.com/icyphox/taigobot")
48
49 res, err := client.Do(req)
50 if err != nil {
51 return nil, err
52 }
53
54 wd := WeatherData{}
55
56 json.NewDecoder(res.Body).Decode(&wd)
57 defer res.Body.Close()
58
59 return &wd, nil
60}
61
62func ctof(c float64) float64 {
63 return c*9/5 + 32
64}
65
66//go:embed data/weather.json
67var wj []byte
68
69func symbolToDesc(s string) string {
70 m := make(map[string]string)
71 json.Unmarshal(wj, &m)
72
73 // Some symbols have a _, which isn't present in
74 // the data. So we strip it.
75 s = strings.Split(s, "_")[0]
76
77 return m[s] + ", "
78}
79
80// Looks like Nominatim uses (lon,lat).
81func GetWeather(lonlat [2]float64, location string) (string, error) {
82 wd, err := getWeatherData(lonlat)
83 if err != nil {
84 return "", err
85 }
86
87 info := strings.Builder{}
88
89 // Location.
90 fmt.Fprintf(&info, "\x02%s\x02: ", location)
91
92 // Description of weather.
93 sym := wd.Properties.Timeseries[0].Data.Next12Hours.Summary.SymbolCode
94 desc := symbolToDesc(sym)
95 fmt.Fprintf(&info, "%s", desc)
96
97 // Current temperature.
98 temp := wd.Properties.Timeseries[0].Data.Instant.Details.AirTemperature
99 var tempFmt string
100 if temp > 28 {
101 tempFmt = "\x0304%0.2f°C\x03 (\x0304%0.2f°F\x03), "
102 } else if temp > 18 {
103 tempFmt = "\x0303%0.2f°C\x03 (\x0303%0.2f°F\x03), "
104 } else {
105 tempFmt = "\x0311%0.2f°C\x03 (\x0311%0.2f°F\x03), "
106 }
107 fmt.Fprintf(
108 &info,
109 "\x02Currently:\x02 "+tempFmt,
110 temp,
111 ctof(temp),
112 )
113
114 // Humidity.
115 hum := wd.Properties.Timeseries[0].Data.Instant.Details.RelativeHumidity
116 humUnit := wd.Properties.Meta.Units.RelativeHumidity
117 fmt.Fprintf(
118 &info,
119 "\x02Humidity:\x02 %0.2f%s, ",
120 hum,
121 humUnit,
122 )
123
124 // Wind speed.
125 ws := wd.Properties.Timeseries[0].Data.Instant.Details.WindSpeed
126 wsUnit := wd.Properties.Meta.Units.WindSpeed
127 fmt.Fprintf(
128 &info,
129 "\x02Wind Speed:\x02 %0.1f %s, ",
130 ws,
131 wsUnit,
132 )
133
134 // Pressure.
135 ps := wd.Properties.Timeseries[0].Data.Instant.Details.AirPressureAtSeaLevel
136 psUnit := wd.Properties.Meta.Units.AirPressureAtSeaLevel
137 fmt.Fprintf(&info,
138 "\x02Pressure:\x02 %0.1f %s",
139 ps,
140 psUnit,
141 )
142
143 return info.String(), nil
144}