Issues (115)

api/weather/weather.go (2 issues)

Severity
1
package weather
2
3
import (
4
	"bytes"
5
	"encoding/json"
6
	"fmt"
7
	"image/png"
8
	"net/http"
9
	"strings"
10
	"time"
11
12
	"github.com/FlameInTheDark/dtbot/api/location"
13
	"github.com/FlameInTheDark/dtbot/bot"
14
	"github.com/fogleman/gg"
15
)
16
17
// Forecast Weather forecast struct
18
type Forecast struct {
19
	Cod     string        `json:"cod"`
20
	Weather []WeatherData `json:"list"`
21
	City    CityData      `json:"city"`
22
}
23
24
// WeatherData Weather data struct
25
type WeatherData struct {
0 ignored issues
show
type name will be used as weather.WeatherData by other packages, and that stutters; consider calling this Data
Loading history...
26
	Time   int64       `json:"dt"`
27
	Main   MainData    `json:"main"`
28
	Wind   WindData    `json:"wind"`
29
	Clouds CloudsData  `json:"clouds"`
30
	WDesc  []WDescData `json:"weather"`
31
}
32
33
// TZTime returns time in specified timezone
34
func (w WeatherData) TZTime(tz int) time.Time {
35
	return time.Unix(w.Time, 0).UTC().Add(time.Hour * time.Duration(tz))
36
}
37
38
// WDescData Weather description struct
39
type WDescData struct {
40
	Id   int64  `json:"id"`
0 ignored issues
show
struct field Id should be ID
Loading history...
41
	Main string `json:"main"`
42
	Desc string `json:"description"`
43
	Icon string `json:"icon"`
44
}
45
46
// MainData Weather main data struct
47
type MainData struct {
48
	Temp     float64 `json:"temp"`
49
	Pressure float64 `json:"pressure"`
50
	TempMin  float64 `json:"temp_min"`
51
	TempMax  float64 `json:"temp_max"`
52
	Humidity int     `json:"humidity"`
53
}
54
55
// WindData Weather wind data struct
56
type WindData struct {
57
	Speed float64 `json:"speed"`
58
	Deg   float64 `json:"deg"`
59
}
60
61
// CloudsData Weather cloud data struct
62
type CloudsData struct {
63
	All int `json:"all"`
64
}
65
66
// CityData Weather city data struct
67
type CityData struct {
68
	Name string `json:"name"`
69
}
70
71
// GetWeatherImage returns buffer with weather image
72
func GetWeatherImage(ctx *bot.Context) (buf *bytes.Buffer, err error) {
73
	var (
74
		forecast Forecast
75
		city     = ctx.GetGuild().WeatherCity
76
	)
77
78
	if len(ctx.Args) > 0 {
79
		city = strings.Join(ctx.Args, "+")
80
	}
81
82
	loc, err := location.New(ctx.Conf.General.GeonamesUsername, city)
83
	if err != nil {
84
		fmt.Printf("Location API: %v", err)
85
		return
86
	}
87
88
	cityName := loc.Geonames[0].CountryName + ", " + loc.Geonames[0].Name
89
90
	// Get coordinates and get weather data
91
	newlat, newlng := loc.GetCoordinates()
92
	resp, err := http.Get(fmt.Sprintf("https://api.openweathermap.org/data/2.5/forecast?lat=%v&lon=%v&lang=%v&units=metric&appid=%v",
93
		newlat, newlng, ctx.Conf.General.Language, ctx.Conf.Weather.WeatherToken))
94
	if err != nil {
95
		fmt.Printf("Weather API: %v", err)
96
		return
97
	}
98
99
	err = json.NewDecoder(resp.Body).Decode(&forecast)
100
	if err != nil {
101
		fmt.Printf("Weather Decode: %v", err)
102
		return
103
	}
104
105
	gc := gg.NewContext(400, 650)
106
	gc.SetRGBA(0, 0, 0, 0)
107
	gc.Clear()
108
109
	// Template
110
	gc.SetRGB255(242, 97, 73)
111
	gc.DrawRoundedRectangle(0, 0, 400, 650, 10)
112
	gc.Fill()
113
114
	// Weather lines
115
	gc.SetRGB255(234, 89, 65)
116
	gc.DrawRectangle(0, 250, 400, 100)
117
	gc.DrawRectangle(0, 450, 400, 100)
118
	gc.Fill()
119
120
	gc.SetLineWidth(2)
121
	gc.SetRGBA(0, 0, 0, 0.05)
122
	gc.DrawLine(0, 250, 400, 250)
123
	gc.DrawLine(0, 349, 400, 348)
124
	gc.DrawLine(0, 450, 400, 450)
125
	gc.DrawLine(0, 549, 400, 548)
126
	gc.Stroke()
127
128
	// Text
129
	if err := gc.LoadFontFace("lato.ttf", 20); err != nil {
130
		panic(err)
131
	}
132
	// Header
133
	gc.SetRGBA(1, 1, 1, 0.7)
134
	gc.DrawStringAnchored(cityName, 10, 15, 0, 0.5)
135
	gc.SetRGBA(1, 1, 1, 0.4)
136
	gc.DrawStringAnchored(time.Now().Format("Jan 2, 2006"), 280, 15, 0, 0.5)
137
138
	// First weather data
139
	gc.SetRGBA(1, 1, 1, 0.5)
140
	if err := gc.LoadFontFace("lato.ttf", 30); err != nil {
141
		panic(err)
142
	}
143
	gc.DrawStringAnchored(fmt.Sprintf("%.2v:00", forecast.Weather[0].TZTime(ctx.Conf.General.Timezone).Hour()), 50, 200, 0.5, 0.5)
144
	gc.DrawStringAnchored(fmt.Sprintf("H:%v%%", forecast.Weather[0].Main.Humidity), 200, 200, 0.5, 0.5)
145
	gc.DrawStringAnchored(fmt.Sprintf("C:%v%%", int(forecast.Weather[0].Clouds.All)), 350, 200, 0.5, 0.5)
146
147
	gc.SetRGBA(1, 1, 1, 1)
148
	if err := gc.LoadFontFace("lato.ttf", 90); err != nil {
149
		panic(err)
150
	}
151
152
	gc.DrawStringAnchored(fmt.Sprintf("%v°", int(forecast.Weather[0].Main.TempMin)), 100, 120, 0.5, 0.5)
153
154
	if err := gc.LoadFontFace("owfont-regular.ttf", 90); err != nil {
155
		panic(err)
156
	}
157
158
	gc.DrawStringAnchored(ctx.WeatherCode(fmt.Sprintf("%v", forecast.Weather[0].WDesc[0].Id)), 250, 120, 0, 0.7)
159
160
	if err := gc.LoadFontFace("lato.ttf", 30); err != nil {
161
		panic(err)
162
	}
163
164
	// Time
165
	gc.DrawStringAnchored(fmt.Sprintf("%.2v:00", forecast.Weather[1].TZTime(ctx.Conf.General.Timezone).Hour()), 100, 285, 0, 0.5)
166
	gc.DrawStringAnchored(fmt.Sprintf("%.2v:00", forecast.Weather[2].TZTime(ctx.Conf.General.Timezone).Hour()), 100, 385, 0, 0.5)
167
	gc.DrawStringAnchored(fmt.Sprintf("%.2v:00", forecast.Weather[3].TZTime(ctx.Conf.General.Timezone).Hour()), 100, 485, 0, 0.5)
168
	gc.DrawStringAnchored(fmt.Sprintf("%.2v:00", forecast.Weather[4].TZTime(ctx.Conf.General.Timezone).Hour()), 100, 585, 0, 0.5)
169
170
	// Humidity and cloudiness
171
	if err := gc.LoadFontFace("lato.ttf", 20); err != nil {
172
		panic(err)
173
	}
174
	gc.SetRGBA(1, 1, 1, 0.5)
175
176
	gc.DrawStringAnchored(fmt.Sprintf("H:%v%%", forecast.Weather[1].Main.Humidity), 100, 315, 0, 0.5)
177
	gc.DrawStringAnchored(fmt.Sprintf("H:%v%%", forecast.Weather[2].Main.Humidity), 100, 415, 0, 0.5)
178
	gc.DrawStringAnchored(fmt.Sprintf("H:%v%%", forecast.Weather[3].Main.Humidity), 100, 515, 0, 0.5)
179
	gc.DrawStringAnchored(fmt.Sprintf("H:%v%%", forecast.Weather[4].Main.Humidity), 100, 615, 0, 0.5)
180
181
	gc.DrawStringAnchored(fmt.Sprintf("C:%v%%", int(forecast.Weather[1].Clouds.All)), 170, 315, 0, 0.5)
182
	gc.DrawStringAnchored(fmt.Sprintf("C:%v%%", int(forecast.Weather[2].Clouds.All)), 170, 415, 0, 0.5)
183
	gc.DrawStringAnchored(fmt.Sprintf("C:%v%%", int(forecast.Weather[3].Clouds.All)), 170, 515, 0, 0.5)
184
	gc.DrawStringAnchored(fmt.Sprintf("C:%v%%", int(forecast.Weather[4].Clouds.All)), 170, 615, 0, 0.5)
185
186
	gc.SetRGBA(1, 1, 1, 1)
187
	if err := gc.LoadFontFace("lato.ttf", 50); err != nil {
188
		panic(err)
189
	}
190
191
	// Temperature
192
	gc.DrawStringAnchored(fmt.Sprintf("%v°", int(forecast.Weather[1].Main.TempMin)), 320, 300, 0.5, 0.5)
193
	gc.DrawStringAnchored(fmt.Sprintf("%v°", int(forecast.Weather[2].Main.TempMin)), 320, 400, 0.5, 0.5)
194
	gc.DrawStringAnchored(fmt.Sprintf("%v°", int(forecast.Weather[3].Main.TempMin)), 320, 500, 0.5, 0.5)
195
	gc.DrawStringAnchored(fmt.Sprintf("%v°", int(forecast.Weather[4].Main.TempMin)), 320, 600, 0.5, 0.5)
196
197
	if err := gc.LoadFontFace("owfont-regular.ttf", 60); err != nil {
198
		panic(err)
199
	}
200
201
	// Weather icon
202
	gc.DrawStringAnchored(ctx.WeatherCode(fmt.Sprintf("%v", forecast.Weather[1].WDesc[0].Id)), 20, 300, 0, 0.7)
203
	gc.DrawStringAnchored(ctx.WeatherCode(fmt.Sprintf("%v", forecast.Weather[2].WDesc[0].Id)), 20, 400, 0, 0.7)
204
	gc.DrawStringAnchored(ctx.WeatherCode(fmt.Sprintf("%v", forecast.Weather[3].WDesc[0].Id)), 20, 500, 0, 0.7)
205
	gc.DrawStringAnchored(ctx.WeatherCode(fmt.Sprintf("%v", forecast.Weather[4].WDesc[0].Id)), 20, 600, 0, 0.7)
206
207
	buf = new(bytes.Buffer)
208
	pngerr := png.Encode(buf, gc.Image())
209
	if pngerr != nil {
210
		fmt.Printf("Image: %v", pngerr)
211
	}
212
	return
213
}
214