rot finished
This commit is contained in:
126
internal/services/weather/weather.go
Normal file
126
internal/services/weather/weather.go
Normal file
@@ -0,0 +1,126 @@
|
||||
package weather
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
// WeatherData contains current weather conditions
|
||||
type WeatherData struct {
|
||||
Temperature float64 `json:"temp"`
|
||||
FeelsLike float64 `json:"feels_like"`
|
||||
Humidity int `json:"humidity"`
|
||||
Pressure int `json:"pressure"`
|
||||
WindSpeed float64 `json:"wind_speed"`
|
||||
WindGust float64 `json:"wind_gust"`
|
||||
WindDeg int `json:"wind_deg"`
|
||||
Clouds int `json:"clouds"`
|
||||
Description string `json:"description"`
|
||||
Icon string `json:"icon"`
|
||||
UpdatedAt string `json:"updated"`
|
||||
}
|
||||
|
||||
// OpenWeatherMapResponse matches the API response
|
||||
type OpenWeatherMapResponse struct {
|
||||
Weather []struct {
|
||||
Description string `json:"description"`
|
||||
Icon string `json:"icon"`
|
||||
} `json:"weather"`
|
||||
Main struct {
|
||||
Temp float64 `json:"temp"`
|
||||
FeelsLike float64 `json:"feels_like"`
|
||||
Humidity int `json:"humidity"`
|
||||
Pressure int `json:"pressure"`
|
||||
} `json:"main"`
|
||||
Wind struct {
|
||||
Speed float64 `json:"speed"`
|
||||
Deg int `json:"deg"`
|
||||
Gust float64 `json:"gust"`
|
||||
} `json:"wind"`
|
||||
Clouds struct {
|
||||
All int `json:"all"`
|
||||
} `json:"clouds"`
|
||||
Dt int64 `json:"dt"`
|
||||
}
|
||||
|
||||
type Client struct {
|
||||
apiKey string
|
||||
latitude float64
|
||||
longitude float64
|
||||
httpClient *http.Client
|
||||
lastUpdate time.Time
|
||||
cachedData *WeatherData
|
||||
}
|
||||
|
||||
func New(apiKey string, latitude, longitude float64) *Client {
|
||||
return &Client{
|
||||
apiKey: apiKey,
|
||||
latitude: latitude,
|
||||
longitude: longitude,
|
||||
httpClient: &http.Client{
|
||||
Timeout: 10 * time.Second,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// GetWeatherData fetches current weather from OpenWeatherMap
|
||||
// Data is cached for 10 minutes
|
||||
func (c *Client) GetWeatherData() (*WeatherData, error) {
|
||||
// Return cached data if less than 10 minutes old
|
||||
if c.cachedData != nil && time.Since(c.lastUpdate) < 10*time.Minute {
|
||||
return c.cachedData, nil
|
||||
}
|
||||
|
||||
url := fmt.Sprintf(
|
||||
"https://api.openweathermap.org/data/2.5/weather?lat=%f&lon=%f&appid=%s&units=metric",
|
||||
c.latitude, c.longitude, c.apiKey,
|
||||
)
|
||||
|
||||
resp, err := c.httpClient.Get(url)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to fetch weather data: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
body, _ := io.ReadAll(resp.Body)
|
||||
return nil, fmt.Errorf("unexpected status code %d: %s", resp.StatusCode, string(body))
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read response: %w", err)
|
||||
}
|
||||
|
||||
var owmData OpenWeatherMapResponse
|
||||
if err := json.Unmarshal(body, &owmData); err != nil {
|
||||
return nil, fmt.Errorf("failed to parse JSON: %w", err)
|
||||
}
|
||||
|
||||
// Convert to our structure
|
||||
weatherData := &WeatherData{
|
||||
Temperature: owmData.Main.Temp,
|
||||
FeelsLike: owmData.Main.FeelsLike,
|
||||
Humidity: owmData.Main.Humidity,
|
||||
Pressure: owmData.Main.Pressure,
|
||||
WindSpeed: owmData.Wind.Speed,
|
||||
WindGust: owmData.Wind.Gust,
|
||||
WindDeg: owmData.Wind.Deg,
|
||||
Clouds: owmData.Clouds.All,
|
||||
UpdatedAt: time.Unix(owmData.Dt, 0).Format(time.RFC3339),
|
||||
}
|
||||
|
||||
if len(owmData.Weather) > 0 {
|
||||
weatherData.Description = owmData.Weather[0].Description
|
||||
weatherData.Icon = owmData.Weather[0].Icon
|
||||
}
|
||||
|
||||
// Cache the data
|
||||
c.cachedData = weatherData
|
||||
c.lastUpdate = time.Now()
|
||||
|
||||
return weatherData, nil
|
||||
}
|
||||
Reference in New Issue
Block a user