You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

157 lines
4.5 KiB
Go

package diyanetapi
import (
"context"
"errors"
"fmt"
"time"
"github.com/imroc/req/v3"
"prayertimes/pkg/hijricalendar"
"prayertimes/pkg/prayer"
)
type Provider struct {
http *req.Client
}
func New(c *req.Client) Provider {
return Provider{
http: c.Clone().SetCommonBasicAuth("diyanet", "Q6Y3vYt5F3x2txPaaMF3uPgbK99EJhpM"),
}
}
func (d Provider) GetByCoords(ctx context.Context, coords prayer.Coordinates) (prayer.TimesResult, error) {
locationID, err := d.getLocationIDByCoords(ctx, coords)
if err != nil {
return prayer.TimesResult{}, fmt.Errorf("failed to resolve location by coordinates: %w", err)
}
result, err := d.Get(ctx, locationID)
if err != nil {
return prayer.TimesResult{}, fmt.Errorf("failed to get prayer times by coordinates: %w", err)
}
result.Location.Latitude = coords.Latitude
result.Location.Longitude = coords.Longitude
return result, nil
}
func (d Provider) getLocationIDByCoords(ctx context.Context, coords prayer.Coordinates) (string, error) {
res, err := d.http.NewRequest().
SetContext(ctx).
SetQueryParams(map[string]string{
"latitude": fmt.Sprintf("%f", coords.Latitude),
"longitude": fmt.Sprintf("%f", coords.Longitude),
}).
Get("https://namazvakti.diyanet.gov.tr/api/ilce/GetByCoordinat")
if err != nil {
return "", fmt.Errorf("failed to get location by coords: %w", err)
}
var response struct {
Success bool `json:"success"`
ResultObject []struct {
ID int `json:"cityID"`
} `json:"resultObject"`
}
if err := res.Unmarshal(&response); err != nil {
return "", fmt.Errorf("failed to unmarshal city response: %w", err)
}
if !response.Success {
return "", fmt.Errorf("failed to get location by coordinates from upstream: %w", errors.New(res.String()))
}
if len(response.ResultObject) == 0 {
return "", fmt.Errorf("failed to resolve location by coordinates: %w", errors.New("empty location result"))
}
return fmt.Sprintf("%d", response.ResultObject[0].ID), nil
}
func (d Provider) Get(ctx context.Context, locationID string) (prayer.TimesResult, error) {
res, err := d.http.NewRequest().
SetContext(ctx).
SetQueryParam("ilceId", locationID).
Get("https://namazvakti.diyanet.gov.tr/api/NamazVakti/Gunluk")
if err != nil {
return prayer.TimesResult{}, fmt.Errorf("failed to get prayer times by location id: %w", err)
}
result, err := d.parseResponse(res)
if err != nil {
return prayer.TimesResult{}, fmt.Errorf("failed to parse prayer times response: %w", err)
}
result.Location.ID = parseIntOrZero(locationID)
return result, nil
}
func (d Provider) parseResponse(res *req.Response) (prayer.TimesResult, error) {
var response struct {
Success bool `json:"success"`
ResultObject struct {
Location struct {
ID int `json:"konum_Id"`
Timezone string `json:"timezone"`
} `json:"konum"`
PrayerTimes []struct {
Date time.Time `json:"miladi_tarih_uzun_Iso8601"`
DateIslamic string `json:"hicri_tarih_uzun"`
Fajr time.Time `json:"imsak"`
Sunrise time.Time `json:"gunes"`
Dhuhr time.Time `json:"ogle"`
Asr time.Time `json:"ikindi"`
Sunset time.Time `json:"gunes_batis"`
Maghrib time.Time `json:"aksam"`
Isha time.Time `json:"yatsi"`
} `json:"namazVakti"`
} `json:"resultObject"`
}
if err := res.Unmarshal(&response); err != nil {
return prayer.TimesResult{}, fmt.Errorf("failed to unmarshal as json: %w", err)
}
if !response.Success {
return prayer.TimesResult{}, fmt.Errorf("failed to get prayer times from upstream: %w", errors.New(res.String()))
}
if len(response.ResultObject.PrayerTimes) == 0 {
return prayer.TimesResult{}, nil
}
var times []prayer.Times
today := time.Now().UTC().Truncate(time.Hour * 24)
for _, pt := range response.ResultObject.PrayerTimes {
then := pt.Date.UTC().Truncate(time.Hour * 24)
if then.Before(today) {
continue
}
times = append(times, prayer.Times{
Date: pt.Date.UTC(),
DateHijri: hijricalendar.ToISODate(pt.Date.UTC()),
Fajr: pt.Fajr.UTC(),
Sunrise: pt.Sunrise.UTC(),
Dhuhr: pt.Dhuhr.UTC(),
Asr: pt.Asr.UTC(),
Sunset: pt.Sunset.UTC(),
Maghrib: pt.Maghrib.UTC(),
Isha: pt.Isha.UTC(),
})
}
return prayer.TimesResult{
Location: prayer.Location{
ID: response.ResultObject.Location.ID,
Timezone: response.ResultObject.Location.Timezone,
},
Times: times,
}, nil
}
func parseIntOrZero(value string) int {
var out int
_, _ = fmt.Sscanf(value, "%d", &out)
return out
}