all repos — paprika @ db1b9b0a0c4855bc2946ef41aa87ce019a33c73e

go rewrite of taigabot

plugins/weather/weather.go (view raw)

 1
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
 100
 101
 102
 103
 104
 105
 106
 107
 108
 109
 110
 111
 112
 113
 114
 115
 116
 117
 118
 119
 120
 121
 122
 123
 124
 125
 126
 127
 128
 129
 130
 131
 132
 133
 134
 135
 136
 137
 138
 139
 140
 141
 142
 143
 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", "github.com/icyphox/paprika")

	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
}