all repos — paprika @ 8ccd2df8f599777dfee1ef536c6c23be98f1498f

go rewrite of taigabot

Add weather plugin (#3)

* Add weather plugin

* Clean up struct

* Remove useless shit from weather desc data
Anirudh Oppiliappan x@icyphox.sh
Wed, 17 Nov 2021 22:02:06 +0530
commit

8ccd2df8f599777dfee1ef536c6c23be98f1498f

parent

7d0fbe31cbde80ead274c8b6d6700543e4c1d4bb

M go.modgo.mod

@@ -3,9 +3,9 @@

go 1.17 require ( - golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 github.com/dgraph-io/badger/v3 v3.2103.2 github.com/dustin/go-humanize v1.0.0 + golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 gopkg.in/irc.v3 v3.1.4 )

@@ -22,6 +22,5 @@ github.com/google/flatbuffers v1.12.1 // indirect

github.com/klauspost/compress v1.12.3 // indirect github.com/pkg/errors v0.9.1 // indirect go.opencensus.io v0.22.5 // indirect - golang.org/x/net v0.0.0-20201021035429-f5854403a974 // indirect - golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c // indirect -)+ golang.org/x/sys v0.0.0-20210423082822-04245dca01da // indirect +)
M go.sumgo.sum

@@ -71,13 +71,6 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=

github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 h1:CIJ76btIcR3eFI5EgSo6k1qKw9KJexJuRLI9G7Hp5wE= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=

@@ -101,8 +94,9 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=

golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20201021035429-f5854403a974 h1:IX6qOQeG5uLjB/hjjwjedwfjND0hgjPMMyO1RoIXQNI= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 h1:CIJ76btIcR3eFI5EgSo6k1qKw9KJexJuRLI9G7Hp5wE= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=

@@ -116,10 +110,15 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=

golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c h1:VwygUrnw9jn88c4u8GD3rZQbqrP/tgas88tPUbBxQrk= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da h1:b3NXsE2LusjYGGjL5bxEVZZORm/YEFFrWFjR8eFrw/c= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
A plugins/weather.go

@@ -0,0 +1,42 @@

+package plugins + +import ( + "fmt" + "strings" + + "git.icyphox.sh/taigobot/plugins/weather" + "gopkg.in/irc.v3" +) + +func init() { + Register(Weather{}) +} + +type Weather struct{} + +func (Weather) Triggers() []string { + return []string{ + ".w", + ".weather", + } +} + +func (Weather) Execute(m *irc.Message) (string, error) { + parsed := strings.SplitN(m.Trailing(), " ", 2) + if len(parsed) != 2 { + return fmt.Sprintf("Usage: %s <location>", parsed[0]), nil + } + query := parsed[1] + li, err := weather.GetLocationInfo(query) + if err != nil { + return "Error getting location info", err + } + + coordinates := li.Features[0].Geometry.Coordinates + label := li.Features[0].Properties.Geocoding.Label + info, err := weather.GetWeather(coordinates, label) + if err != nil { + return "Error getting weather data", err + } + return info, nil +}
A plugins/weather/data/weather.json

@@ -0,0 +1,43 @@

+{ + "lightsleetshowers": "Light sleet showers", + "heavyrainandthunder": "Heavy rain and thunder", + "heavyrainshowers": "Heavy rain showers", + "clearsky": "Clear sky", + "partlycloudy": "Partly cloudy", + "heavysleetshowers": "Heavy sleet showers", + "lightrainshowersandthunder": "Light rain showers and thunder", + "lightrainandthunder": "Light rain and thunder", + "snowshowersandthunder": "Snow showers and thunder", + "fog": "Fog", + "heavysleetshowersandthunder": "Heavy sleet showers and thunder", + "rain": "Rain", + "sleetandthunder": "Sleet and thunder", + "sleet": "Sleet", + "rainshowersandthunder": "Rain showers and thunder", + "heavysnowshowers": "Heavy snow showers", + "heavyrain": "Heavy rain", + "snowshowers": "Snow showers", + "lightsnowshowers": "Light snow showers", + "snow": "Snow", + "lightrainshowers": "Light rain showers", + "lightsnowandthunder": "Light snow and thunder", + "lightssleetshowersandthunder": "Light sleet showers and thunder", + "fair": "Fair", + "lightssnowshowersandthunder": "Light snow showers and thunder", + "lightsnow": "Light snow", + "sleetshowers": "Sleet showers", + "heavysleet": "Heavy sleet", + "lightsleetandthunder": "Light sleet and thunder", + "cloudy": "Cloudy", + "lightrain": "Light rain", + "heavysnowshowersandthunder": "Heavy snow showers and thunder", + "heavyrainshowersandthunder": "Heavy rain showers and thunder", + "heavysnow": "Heavy snow", + "sleetshowersandthunder": "Sleet showers and thunder", + "rainandthunder": "Rain and thunder", + "heavysleetandthunder": "Heavy sleet and thunder", + "rainshowers": "Rain showers", + "lightsleet": "Light sleet", + "heavysnowandthunder": "Heavy snow and thunder", + "snowandthunder": "Snow and thunder" +}
A plugins/weather/geocode.go

@@ -0,0 +1,35 @@

+package weather + +import ( + "encoding/json" + "fmt" + "net/http" +) + +type LocationInfo struct { + Features []struct { + Properties struct { + Geocoding struct { + Label string `json:"label"` + } `json:"geocoding"` + } `json:"properties"` + Geometry struct { + Coordinates [2]float64 `json:"coordinates"` + } `json:"geometry"` + } `json:"features"` +} + +// Get location data (lat,lon) from a given query. +func GetLocationInfo(query string) (*LocationInfo, error) { + url := "https://nominatim.openstreetmap.org/search?q=%s&format=geocodejson&limit=1" + r, err := http.Get(fmt.Sprintf(url, query)) + if err != nil { + return nil, err + } + + li := LocationInfo{} + json.NewDecoder(r.Body).Decode(&li) + defer r.Body.Close() + + return &li, nil +}
A plugins/weather/weather.go

@@ -0,0 +1,144 @@

+package weather + +import ( + _ "embed" + "encoding/json" + "fmt" + "net/http" + "strings" +) + +type WeatherData struct { + Properties struct { + Meta struct { + Units struct { + AirPressureAtSeaLevel string `json:"air_pressure_at_sea_level"` + AirTemperature string `json:"air_temperature"` + RelativeHumidity string `json:"relative_humidity"` + WindSpeed string `json:"wind_speed"` + } `json:"units"` + } `json:"meta"` + Timeseries []struct { + Data struct { + Instant struct { + Details struct { + AirPressureAtSeaLevel float64 `json:"air_pressure_at_sea_level"` + AirTemperature float64 `json:"air_temperature"` + RelativeHumidity float64 `json:"relative_humidity"` + WindSpeed float64 `json:"wind_speed"` + } `json:"details"` + } `json:"instant"` + Next12Hours struct { + Summary struct { + SymbolCode string `json:"symbol_code"` + } `json:"summary"` + } `json:"next_12_hours"` + } `json:"data"` + } `json:"timeseries"` + } `json:"properties"` +} + +func getWeatherData(lonlat [2]float64) (*WeatherData, error) { + url := "https://api.met.no/weatherapi/locationforecast/2.0/compact.json?lat=%f16&lon=%f" + url = fmt.Sprintf(url, lonlat[1], lonlat[0]) + + client := &http.Client{} + req, _ := http.NewRequest("GET", url, nil) + req.Header.Add("User-Agent", "taigobot github.com/icyphox/taigobot") + + res, err := client.Do(req) + if err != nil { + return nil, err + } + + wd := WeatherData{} + + json.NewDecoder(res.Body).Decode(&wd) + defer res.Body.Close() + + return &wd, nil +} + +func ctof(c float64) float64 { + return c*9/5 + 32 +} + +//go:embed data/weather.json +var wj []byte + +func symbolToDesc(s string) string { + m := make(map[string]string) + json.Unmarshal(wj, &m) + + // Some symbols have a _, which isn't present in + // the data. So we strip it. + s = strings.Split(s, "_")[0] + + return m[s] + ", " +} + +// Looks like Nominatim uses (lon,lat). +func GetWeather(lonlat [2]float64, location string) (string, error) { + wd, err := getWeatherData(lonlat) + if err != nil { + return "", err + } + + info := strings.Builder{} + + // Location. + fmt.Fprintf(&info, "\x02%s\x02: ", location) + + // Description of weather. + sym := wd.Properties.Timeseries[0].Data.Next12Hours.Summary.SymbolCode + desc := symbolToDesc(sym) + fmt.Fprintf(&info, "%s", desc) + + // Current temperature. + temp := wd.Properties.Timeseries[0].Data.Instant.Details.AirTemperature + var tempFmt string + if temp > 28 { + tempFmt = "\x0304%0.2f°C\x03 (\x0304%0.2f°F\x03), " + } else if temp > 18 { + tempFmt = "\x0303%0.2f°C\x03 (\x0303%0.2f°F\x03), " + } else { + tempFmt = "\x0311%0.2f°C\x03 (\x0311%0.2f°F\x03), " + } + fmt.Fprintf( + &info, + "\x02Currently:\x02 "+tempFmt, + temp, + ctof(temp), + ) + + // Humidity. + hum := wd.Properties.Timeseries[0].Data.Instant.Details.RelativeHumidity + humUnit := wd.Properties.Meta.Units.RelativeHumidity + fmt.Fprintf( + &info, + "\x02Humidity:\x02 %0.2f%s, ", + hum, + humUnit, + ) + + // Wind speed. + ws := wd.Properties.Timeseries[0].Data.Instant.Details.WindSpeed + wsUnit := wd.Properties.Meta.Units.WindSpeed + fmt.Fprintf( + &info, + "\x02Wind Speed:\x02 %0.1f %s, ", + ws, + wsUnit, + ) + + // Pressure. + ps := wd.Properties.Timeseries[0].Data.Instant.Details.AirPressureAtSeaLevel + psUnit := wd.Properties.Meta.Units.AirPressureAtSeaLevel + fmt.Fprintf(&info, + "\x02Pressure:\x02 %0.1f %s", + ps, + psUnit, + ) + + return info.String(), nil +}