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]interface{})
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 // lmao
78 return m[s].(map[string]interface{})["desc"].(string) + ", "
79}
80
81// Looks like Nominatim uses (lon,lat).
82func GetWeather(lonlat [2]float64, location string) (string, error) {
83 wd, err := getWeatherData(lonlat)
84 if err != nil {
85 return "", err
86 }
87
88 info := strings.Builder{}
89
90 // Location.
91 fmt.Fprintf(&info, "\x02%s\x02: ", location)
92
93 // Description of weather.
94 sym := wd.Properties.Timeseries[0].Data.Next12Hours.Summary.SymbolCode
95 desc := symbolToDesc(sym)
96 fmt.Fprintf(&info, "%s", desc)
97
98 // Current temperature.
99 temp := wd.Properties.Timeseries[0].Data.Instant.Details.AirTemperature
100 var tempFmt string
101 if temp > 28 {
102 tempFmt = "\x0304%0.2f°C\x03 (\x0304%0.2f°F\x03), "
103 } else if temp > 18 {
104 tempFmt = "\x0303%0.2f°C\x03 (\x0303%0.2f°F\x03), "
105 } else {
106 tempFmt = "\x0311%0.2f°C\x03 (\x0311%0.2f°F\x03), "
107 }
108 fmt.Fprintf(
109 &info,
110 "\x02Currently:\x02 "+tempFmt,
111 temp,
112 ctof(temp),
113 )
114
115 // Humidity.
116 hum := wd.Properties.Timeseries[0].Data.Instant.Details.RelativeHumidity
117 humUnit := wd.Properties.Meta.Units.RelativeHumidity
118 fmt.Fprintf(
119 &info,
120 "\x02Humidity:\x02 %0.2f%s, ",
121 hum,
122 humUnit,
123 )
124
125 // Wind speed.
126 ws := wd.Properties.Timeseries[0].Data.Instant.Details.WindSpeed
127 wsUnit := wd.Properties.Meta.Units.WindSpeed
128 fmt.Fprintf(
129 &info,
130 "\x02Wind Speed:\x02 %0.1f%s, ",
131 ws,
132 wsUnit,
133 )
134
135 // Pressure.
136 ps := wd.Properties.Timeseries[0].Data.Instant.Details.AirPressureAtSeaLevel
137 psUnit := wd.Properties.Meta.Units.AirPressureAtSeaLevel
138 fmt.Fprintf(&info,
139 "\x02Pressure:\x02 %0.1f%s",
140 ps,
141 psUnit,
142 )
143
144 return info.String(), nil
145}