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 }