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