Yimaru-BackEnd/internal/services/enet_pulse/service.go
2025-10-14 14:21:27 +03:00

2263 lines
71 KiB
Go
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package enetpulse
import (
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"strconv"
"strings"
"time"
"github.com/SamuelTariku/FortuneBet-Backend/internal/config"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
"github.com/SamuelTariku/FortuneBet-Backend/internal/repository"
)
type Service struct {
// username string
// token string
cfg config.Config
store *repository.Store
// mongoLogger *zap.Logger
httpClient *http.Client
}
func New(cfg config.Config, store *repository.Store) *Service {
return &Service{
cfg: cfg,
store: store,
// mongoLogger: mongoLogger,
httpClient: &http.Client{
Timeout: 15 * time.Second,
},
}
}
func (s *Service) FetchAndStoreSports(ctx context.Context) error {
// 1⃣ Compose URL with credentials
url := fmt.Sprintf(
"http://eapi.enetpulse.com/sport/list/?username=%s&token=%s",
s.cfg.EnetPulseConfig.UserName, s.cfg.EnetPulseConfig.Token,
)
// 2⃣ Create HTTP request with context
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
return fmt.Errorf("creating sport request: %w", err)
}
// 3⃣ Execute request
resp, err := s.httpClient.Do(req)
if err != nil {
return fmt.Errorf("requesting sports: %w", err)
}
defer resp.Body.Close()
// 4⃣ Check response status
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
return fmt.Errorf("failed to fetch sports (status %d): %s", resp.StatusCode, string(body))
}
// 5⃣ Decode JSON response
var sportsResp struct {
Sports map[string]struct {
ID string `json:"id"`
N string `json:"n"` // updates count
Name string `json:"name"`
UT string `json:"ut"` // timestamp string
} `json:"sports"`
}
if err := json.NewDecoder(resp.Body).Decode(&sportsResp); err != nil {
return fmt.Errorf("decoding sports response: %w", err)
}
// 6⃣ Iterate and store each sport
for _, sport := range sportsResp.Sports {
// Parse updates count
updatesCount := 0
if sport.N != "" {
if n, err := strconv.Atoi(sport.N); err == nil {
updatesCount = n
}
}
// Parse timestamp
lastUpdatedAt, err := time.Parse(time.RFC3339, sport.UT)
if err != nil {
// Fallback to zero time if parsing fails
lastUpdatedAt = time.Time{}
}
// Build domain object
createSport := domain.CreateEnetpulseSport{
SportID: sport.ID,
Name: sport.Name,
UpdatesCount: updatesCount,
LastUpdatedAt: lastUpdatedAt,
Status: 1, // default active
}
// Insert or update in DB
if _, err := s.store.CreateEnetpulseSport(ctx, createSport); err != nil {
// Log error but continue
// s.logger.Error("failed to store sport", zap.String("sport_id", sport.ID), zap.Error(err))
continue
}
}
// s.logger.Info("Successfully fetched and stored sports", zap.Int("count", len(sportsResp.Sports)))
return nil
}
func (s *Service) GetAllSports(ctx context.Context) ([]domain.EnetpulseSport, error) {
// 1⃣ Fetch all sports from the store (database)
sports, err := s.store.GetAllEnetpulseSports(ctx)
if err != nil {
return nil, fmt.Errorf("failed to fetch sports from DB: %w", err)
}
// 2⃣ Optionally, you can log the count or perform other transformations
// s.logger.Info("Fetched sports from DB", zap.Int("count", len(sports)))
return sports, nil
}
func (s *Service) FetchAndStoreTournamentTemplates(ctx context.Context) error {
// 1⃣ Fetch all sports from the database
sports, err := s.store.GetAllEnetpulseSports(ctx)
if err != nil {
return fmt.Errorf("failed to fetch sports from DB: %w", err)
}
// Template struct
type TournamentTemplate struct {
ID string `json:"id"`
Name string `json:"name"`
SportFK string `json:"sportFK"`
Gender string `json:"gender"`
N string `json:"n"`
UT string `json:"ut"`
}
for _, sport := range sports {
if sport.SportID != "1" {
continue
} else {
// 2⃣ Compose URL for each sport using its Enetpulse sportFK
url := fmt.Sprintf(
"http://eapi.enetpulse.com/tournament_template/list/?sportFK=%s&username=%s&token=%s",
sport.SportID, // must be Enetpulse sportFK
s.cfg.EnetPulseConfig.UserName,
s.cfg.EnetPulseConfig.Token,
)
fmt.Println("Fetching tournament templates:", url)
// 3⃣ Create HTTP request
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
return fmt.Errorf("creating tournament template request for sport %s: %w", sport.SportID, err)
}
// 4⃣ Execute request
resp, err := s.httpClient.Do(req)
if err != nil {
return fmt.Errorf("requesting tournament templates for sport %s: %w", sport.SportID, err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
return fmt.Errorf("failed to fetch tournament templates for sport %s (status %d): %s",
sport.SportID, resp.StatusCode, string(body))
}
// 5⃣ Decode JSON response flexibly
var raw struct {
TournamentTemplates json.RawMessage `json:"tournament_templates"`
}
bodyBytes, err := io.ReadAll(resp.Body)
if err != nil {
return fmt.Errorf("reading tournament templates response for sport %s: %w", sport.SportID, err)
}
if err := json.Unmarshal(bodyBytes, &raw); err != nil {
return fmt.Errorf("unmarshalling raw tournament templates for sport %s: %w", sport.SportID, err)
}
// 6⃣ Parse depending on object or array
templates := map[string]TournamentTemplate{}
if len(raw.TournamentTemplates) > 0 && raw.TournamentTemplates[0] == '{' {
// Object (normal case)
if err := json.Unmarshal(raw.TournamentTemplates, &templates); err != nil {
return fmt.Errorf("decoding tournament templates (object) for sport %s: %w", sport.SportID, err)
}
} else {
// Array or empty → skip safely
fmt.Printf("No tournament templates found for sport %s\n", sport.SportID)
continue
}
// 7⃣ Iterate and store each tournament template
for _, tmpl := range templates {
updatesCount := 0
if tmpl.N != "" {
if n, err := strconv.Atoi(tmpl.N); err == nil {
updatesCount = n
}
}
lastUpdatedAt, err := time.Parse(time.RFC3339, tmpl.UT)
if err != nil {
lastUpdatedAt = time.Time{}
}
// Convert sport.SportID from string to int64
sportFK, err := strconv.ParseInt(sport.SportID, 10, 64)
if err != nil {
fmt.Printf("failed to convert sport.SportID '%s' to int64: %v\n", sport.SportID, err)
continue
}
createTemplate := domain.CreateEnetpulseTournamentTemplate{
TemplateID: tmpl.ID,
Name: tmpl.Name,
SportFK: sportFK, // use DB sport ID internally
Gender: tmpl.Gender,
UpdatesCount: updatesCount,
LastUpdatedAt: lastUpdatedAt,
Status: 1, // default active
}
if _, err := s.store.CreateEnetpulseTournamentTemplate(ctx, createTemplate); err != nil {
fmt.Printf("failed to store tournament template %s: %v\n", tmpl.ID, err)
continue
}
}
break
}
}
fmt.Println("✅ Successfully fetched and stored all tournament templates")
return nil
}
func (s *Service) GetAllTournamentTemplates(ctx context.Context) ([]domain.EnetpulseTournamentTemplate, error) {
// 1⃣ Fetch all tournament templates from the store (database)
templates, err := s.store.GetAllEnetpulseTournamentTemplates(ctx)
if err != nil {
return nil, fmt.Errorf("failed to fetch tournament templates from DB: %w", err)
}
// 2⃣ Optionally, you can log the count or perform other transformations
// s.logger.Info("Fetched tournament templates from DB", zap.Int("count", len(templates)))
return templates, nil
}
func (s *Service) FetchAndStoreTournaments(ctx context.Context) error {
// 1⃣ Fetch all tournament templates from the database
templates, err := s.store.GetAllEnetpulseTournamentTemplates(ctx)
if err != nil {
return fmt.Errorf("failed to fetch tournament templates from DB: %w", err)
}
for _, tmpl := range templates {
// 2⃣ Compose URL for each tournament template
url := fmt.Sprintf(
"http://eapi.enetpulse.com/tournament/list/?tournament_templateFK=%s&username=%s&token=%s",
tmpl.TemplateID,
s.cfg.EnetPulseConfig.UserName,
s.cfg.EnetPulseConfig.Token,
)
// 3⃣ Create HTTP request
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
return fmt.Errorf("creating tournament request for template %s: %w", tmpl.TemplateID, err)
}
// 4⃣ Execute request
resp, err := s.httpClient.Do(req)
if err != nil {
return fmt.Errorf("requesting tournaments for template %s: %w", tmpl.TemplateID, err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
return fmt.Errorf("failed to fetch tournaments for template %s (status %d): %s",
tmpl.TemplateID, resp.StatusCode, string(body))
}
// 5⃣ Decode JSON response
var tournamentsResp struct {
Tournaments map[string]struct {
ID string `json:"id"`
Name string `json:"name"`
TournamentTemplateFK string `json:"tournament_templateFK"`
N string `json:"n"` // updates count
UT string `json:"ut"` // timestamp
} `json:"tournaments"`
}
if err := json.NewDecoder(resp.Body).Decode(&tournamentsResp); err != nil {
return fmt.Errorf("decoding tournaments for template %s: %w", tmpl.TemplateID, err)
}
// 6⃣ Iterate and store each tournament
for _, t := range tournamentsResp.Tournaments {
updatesCount := 0
if t.N != "" {
if n, err := strconv.Atoi(t.N); err == nil {
updatesCount = n
}
}
lastUpdatedAt, err := time.Parse(time.RFC3339, t.UT)
if err != nil {
lastUpdatedAt = time.Time{}
}
createTournament := domain.CreateEnetpulseTournament{
TournamentID: t.ID,
Name: t.Name,
TournamentTemplateFK: tmpl.TemplateID, // DB ID of template
UpdatesCount: updatesCount,
LastUpdatedAt: lastUpdatedAt,
Status: 1, // default active
}
// Insert into DB
if _, err := s.store.CreateEnetpulseTournament(ctx, createTournament); err != nil {
// Log error but continue
// s.logger.Error("failed to store tournament", zap.String("tournament_id", t.ID), zap.Error(err))
continue
}
}
}
// s.logger.Info("Successfully fetched and stored all tournaments")
return nil
}
func (s *Service) GetAllTournaments(ctx context.Context) ([]domain.EnetpulseTournament, error) {
// 1⃣ Fetch all tournaments from the store (database)
tournaments, err := s.store.GetAllEnetpulseTournaments(ctx)
if err != nil {
return nil, fmt.Errorf("failed to fetch tournaments from DB: %w", err)
}
// 2⃣ Optionally log count or do transformations
// s.logger.Info("Fetched tournaments from DB", zap.Int("count", len(tournaments)))
return tournaments, nil
}
func (s *Service) FetchAndStoreTournamentStages(ctx context.Context) error {
// 1⃣ Get all tournaments from DB
tournaments, err := s.store.GetAllEnetpulseTournaments(ctx)
if err != nil {
return fmt.Errorf("failed to fetch tournaments from DB: %w", err)
}
// 2⃣ Loop through each tournament
for _, t := range tournaments {
// Compose URL for each tournament
url := fmt.Sprintf(
"http://eapi.enetpulse.com/tournament_stage/list/?language_typeFK=3&tz=Europe/Sofia&tournamentFK=%s&username=%s&token=%s",
t.TournamentID,
s.cfg.EnetPulseConfig.UserName,
s.cfg.EnetPulseConfig.Token,
)
// 3⃣ Create HTTP request
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
// log and skip
// s.logger.Error("creating tournament stages request", zap.String("tournament_id", t.TournamentID), zap.Error(err))
continue
}
// 4⃣ Execute request
resp, err := s.httpClient.Do(req)
if err != nil {
// s.logger.Error("requesting tournament stages", zap.String("tournament_id", t.TournamentID), zap.Error(err))
continue
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
// s.logger.Error("unexpected status code fetching stages", zap.String("tournament_id", t.TournamentID), zap.Int("status", resp.StatusCode))
_ = body
continue
}
// 5⃣ Decode JSON response (based on EnetPulse response format)
var stagesResp struct {
Stages map[string]struct {
ID string `json:"id"`
Name string `json:"name"`
TournamentFK string `json:"tournamentFK"`
N string `json:"n"` // updates count
UT string `json:"ut"` // timestamp
} `json:"tournament_stages"`
}
if err := json.NewDecoder(resp.Body).Decode(&stagesResp); err != nil {
// s.logger.Error("decoding tournament stages", zap.String("tournament_id", t.TournamentID), zap.Error(err))
continue
}
// 6⃣ Iterate and store each stage
for _, st := range stagesResp.Stages {
updatesCount := 0
if st.N != "" {
if n, err := strconv.Atoi(st.N); err == nil {
updatesCount = n
}
}
lastUpdatedAt, err := time.Parse(time.RFC3339, st.UT)
if err != nil {
lastUpdatedAt = time.Time{}
}
createStage := domain.CreateEnetpulseTournamentStage{
StageID: st.ID,
Name: st.Name,
TournamentFK: t.TournamentID, // DB ID of the tournament
UpdatesCount: updatesCount,
LastUpdatedAt: lastUpdatedAt,
Status: 1,
}
// Insert into DB
if _, err := s.store.CreateEnetpulseTournamentStage(ctx, createStage); err != nil {
// s.logger.Error("failed to store tournament stage", zap.String("stage_id", st.ID), zap.Error(err))
continue
}
}
}
// s.logger.Info("Successfully fetched and stored all tournament stages for all tournaments")
return nil
}
func (s *Service) GetAllTournamentStages(ctx context.Context) ([]domain.EnetpulseTournamentStage, error) {
// 1⃣ Fetch all tournament stages from the store (database)
stages, err := s.store.GetAllEnetpulseTournamentStages(ctx)
if err != nil {
return nil, fmt.Errorf("failed to fetch tournament stages from DB: %w", err)
}
// 2⃣ Optionally log count or perform transformations
// s.logger.Info("Fetched tournament stages from DB", zap.Int("count", len(stages)))
return stages, nil
}
func (s *Service) FetchAndStoreFixtures(ctx context.Context, date string) error {
// 1⃣ Fetch all sports from the database
sports, err := s.store.GetAllEnetpulseSports(ctx)
if err != nil {
return fmt.Errorf("failed to fetch sports from DB: %w", err)
}
// Struct for decoding each fixture from API
type Fixture struct {
FixtureID string `json:"id"`
Name string `json:"name"`
SportFK string `json:"sportFK"`
TournamentFK string `json:"tournamentFK"`
TournamentName string `json:"tournament_name"`
StartDate string `json:"startdate"`
StatusType string `json:"status_type"`
HomeTeam string `json:"home_team"`
AwayTeam string `json:"away_team"`
HomeTeamID string `json:"home_team_id"`
AwayTeamID string `json:"away_team_id"`
HomeKitImage string `json:"home_kit_image"`
AwayKitImage string `json:"away_kit_image"`
}
// 2⃣ Loop through each sport
for _, sport := range sports {
if sport.SportID != "1" {
continue
}
url := fmt.Sprintf(
"http://eapi.enetpulse.com/event/fixtures/?username=%s&token=%s&sportFK=%s&language_typeFK=3&date=%s",
s.cfg.EnetPulseConfig.UserName,
s.cfg.EnetPulseConfig.Token,
sport.SportID,
date,
)
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
fmt.Printf("creating fixtures request for sport %s: %v\n", sport.SportID, err)
continue
}
resp, err := s.httpClient.Do(req)
if err != nil {
fmt.Printf("requesting fixtures for sport %s: %v\n", sport.SportID, err)
continue
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
fmt.Printf("failed to fetch fixtures for sport %s (status %d): %s\n",
sport.SportID, resp.StatusCode, string(body))
continue
}
// 3⃣ Decode API response
var fixturesResp struct {
Events map[string]Fixture `json:"events"`
}
if err := json.NewDecoder(resp.Body).Decode(&fixturesResp); err != nil {
fmt.Printf("decoding fixtures response for sport %s: %v\n", sport.SportID, err)
continue
}
// 4⃣ Iterate over fixtures and store as events
for _, fx := range fixturesResp.Events {
// Conversions
sportID, _ := strconv.Atoi(fx.SportFK)
homeTeamID, _ := strconv.ParseInt(fx.HomeTeamID, 10, 64)
awayTeamID, _ := strconv.ParseInt(fx.AwayTeamID, 10, 64)
leagueID, _ := strconv.ParseInt(fx.TournamentFK, 10, 64)
startDate, _ := time.Parse("2006-01-02 15:04:05", fx.StartDate)
event := domain.CreateEvent{
SourceEventID: fx.FixtureID,
SportID: int32(sportID),
MatchName: fx.Name,
HomeTeam: fx.HomeTeam,
AwayTeam: fx.AwayTeam,
HomeTeamID: homeTeamID,
AwayTeamID: awayTeamID,
HomeTeamImage: fx.HomeKitImage,
AwayTeamImage: fx.AwayKitImage,
LeagueID: leagueID,
LeagueName: fx.TournamentName,
StartTime: startDate,
IsLive: false, // default, can update later from live feed
Status: domain.STATUS_PENDING, // map to enum if needed
Source: "EnetPulse", // custom enum constant
DefaultWinningUpperLimit: 0, // default, can adjust
}
// 5⃣ Save event in DB (UPSERT)
if err := s.store.SaveEvent(ctx, event); err != nil {
fmt.Printf("failed storing event %s: %v\n", fx.FixtureID, err)
continue
}
}
fmt.Printf("✅ Successfully fetched and stored events for sport %s\n", sport.SportID)
break
}
fmt.Println("✅ Completed fetching and storing events for all sports")
return nil
}
func (s *Service) FetchFixtures(ctx context.Context, date string) ([]domain.EnetpulseFixture, error) {
var allFixtures []domain.EnetpulseFixture
// 1⃣ Fetch all sports from the database
sports, err := s.store.GetAllEnetpulseSports(ctx)
if err != nil {
return nil, fmt.Errorf("failed to fetch sports from DB: %w", err)
}
// Struct for decoding each fixture from API
type Fixture struct {
FixtureID string `json:"id"`
Name string `json:"name"`
SportFK string `json:"sportFK"`
TournamentFK string `json:"tournamentFK"`
TournamentTemplateFK string `json:"tournament_templateFK"`
TournamentStageFK string `json:"tournament_stageFK"`
TournamentStageName string `json:"tournament_stage_name"`
TournamentName string `json:"tournament_name"`
TournamentTemplateName string `json:"tournament_template_name"`
SportName string `json:"sport_name"`
Gender string `json:"gender"`
StartDate string `json:"startdate"`
StatusType string `json:"status_type"`
StatusDescFK string `json:"status_descFK"`
RoundTypeFK string `json:"round_typeFK"`
UpdatesCount string `json:"n"`
LastUpdatedAt string `json:"ut"`
}
// 2⃣ Loop through each sport
for _, sport := range sports {
// Only fetch for sport "1" (Football)
if sport.SportID != "1" {
continue
}
url := fmt.Sprintf(
"http://eapi.enetpulse.com/event/fixtures/?username=%s&token=%s&sportFK=%s&language_typeFK=3&date=%s",
s.cfg.EnetPulseConfig.UserName,
s.cfg.EnetPulseConfig.Token,
sport.SportID,
date,
)
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
fmt.Printf("creating fixtures request for sport %s: %v\n", sport.SportID, err)
continue
}
resp, err := s.httpClient.Do(req)
if err != nil {
fmt.Printf("requesting fixtures for sport %s: %v\n", sport.SportID, err)
continue
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
fmt.Printf("failed to fetch fixtures for sport %s (status %d): %s\n",
sport.SportID, resp.StatusCode, string(body))
continue
}
// 3⃣ Decode API response
var fixturesResp struct {
Events map[string]Fixture `json:"events"`
}
if err := json.NewDecoder(resp.Body).Decode(&fixturesResp); err != nil {
fmt.Printf("decoding fixtures response for sport %s: %v\n", sport.SportID, err)
continue
}
// 4⃣ Iterate over fixtures and store them
for _, fx := range fixturesResp.Events {
tournamentFK, _ := strconv.Atoi(fx.TournamentFK)
tournamentTemplateFK, _ := strconv.Atoi(fx.TournamentTemplateFK)
tournamentStageFK, _ := strconv.Atoi(fx.TournamentStageFK)
statusDescFK, _ := strconv.Atoi(fx.StatusDescFK)
roundTypeFK, _ := strconv.Atoi(fx.RoundTypeFK)
updatesCount, _ := strconv.Atoi(fx.UpdatesCount)
startDate, _ := time.Parse(time.RFC3339, fx.StartDate)
lastUpdatedAt, _ := time.Parse(time.RFC3339, fx.LastUpdatedAt)
createFixture := domain.CreateEnetpulseFixture{
FixtureID: fx.FixtureID,
Name: fx.Name,
SportFK: fx.SportFK,
TournamentFK: strconv.Itoa(tournamentFK),
TournamentTemplateFK: strconv.Itoa(tournamentTemplateFK),
TournamentStageFK: strconv.Itoa(tournamentStageFK),
TournamentStageName: fx.TournamentStageName,
TournamentName: fx.TournamentName,
TournamentTemplateName: fx.TournamentTemplateName,
SportName: fx.SportName,
Gender: fx.Gender,
StartDate: startDate,
StatusType: fx.StatusType,
StatusDescFK: strconv.Itoa(statusDescFK),
RoundTypeFK: strconv.Itoa(roundTypeFK),
UpdatesCount: updatesCount,
LastUpdatedAt: lastUpdatedAt,
}
dbFixture, err := s.store.CreateEnetpulseFixture(ctx, createFixture)
if err != nil {
fmt.Printf("failed storing fixture %s: %v\n", fx.FixtureID, err)
continue
}
allFixtures = append(allFixtures, dbFixture)
}
fmt.Printf("✅ Successfully fetched and stored fixtures for sport %s\n", sport.SportID)
break // stop after first relevant sport
}
fmt.Println("✅ Completed fetching and storing fixtures for all sports")
return allFixtures, nil
}
func (s *Service) GetAllFixtures(ctx context.Context) ([]domain.EnetpulseFixture, error) {
// 1⃣ Fetch all from store
fixtures, err := s.store.GetAllEnetpulseFixtures(ctx)
if err != nil {
return nil, fmt.Errorf("failed to fetch fixtures from DB: %w", err)
}
return fixtures, nil
}
func (s *Service) FetchAndStoreResults(ctx context.Context) error {
// 1⃣ Fetch all sports (if you want to limit to one, adjust the loop as in your template fetcher)
sports, err := s.store.GetAllEnetpulseSports(ctx)
if err != nil {
return fmt.Errorf("failed to fetch sports from DB: %w", err)
}
type Result struct {
ID string `json:"id"`
Name string `json:"name"`
SportFK string `json:"sportFK"`
TournamentFK string `json:"tournamentFK"`
TournamentTemplateFK string `json:"tournament_templateFK"`
TournamentStageFK string `json:"tournament_stageFK"`
TournamentStageName string `json:"tournament_stage_name"`
TournamentName string `json:"tournament_name"`
TournamentTemplateName string `json:"tournament_template_name"`
SportName string `json:"sport_name"`
StartDate string `json:"startdate"`
StatusType string `json:"status_type"`
StatusDescFK string `json:"status_descFK"`
RoundTypeFK string `json:"round_typeFK"`
N string `json:"n"`
UT string `json:"ut"`
Round string `json:"round"`
Live string `json:"live"`
VenueName string `json:"venue_name"`
LivestatsPlus string `json:"livestats_plus"`
LivestatsType string `json:"livestats_type"`
Commentary string `json:"commentary"`
LineupConfirmed bool `json:"lineup_confirmed"`
Verified bool `json:"verified"`
Spectators int32 `json:"spectators"`
GameStarted string `json:"game_started"`
FirstHalfEnded string `json:"first_half_ended"`
SecondHalfStarted string `json:"second_half_started"`
SecondHalfEnded string `json:"second_half_ended"`
GameEnded string `json:"game_ended"`
}
for _, sport := range sports {
if sport.SportID != "1" { // ⚽ Example: Only Football
continue
}
url := fmt.Sprintf(
// "https://eapi.enetpulse.com/event/results/?username=kirubelapiusr&token=b1d35ee5fb8371938c6ca1b4fd6c75cc&sportFK=1&language_typeFK=3&date=2025-10-12"
"http://eapi.enetpulse.com/event/results/?sportFK=%s&date=%s&username=%s&token=%s",
sport.SportID,
time.DateOnly,
s.cfg.EnetPulseConfig.UserName,
s.cfg.EnetPulseConfig.Token,
)
fmt.Println("Fetching results:", url)
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
return fmt.Errorf("creating results request for sport %s: %w", sport.SportID, err)
}
resp, err := s.httpClient.Do(req)
if err != nil {
return fmt.Errorf("requesting results for sport %s: %w", sport.SportID, err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
return fmt.Errorf("failed to fetch results for sport %s (status %d): %s",
sport.SportID, resp.StatusCode, string(body))
}
var raw struct {
EventResults json.RawMessage `json:"results"`
}
bodyBytes, err := io.ReadAll(resp.Body)
if err != nil {
return fmt.Errorf("reading results response for sport %s: %w", sport.SportID, err)
}
if err := json.Unmarshal(bodyBytes, &raw); err != nil {
return fmt.Errorf("unmarshalling raw results for sport %s: %w", sport.SportID, err)
}
results := map[string]Result{}
if len(raw.EventResults) > 0 && raw.EventResults[0] == '{' {
if err := json.Unmarshal(raw.EventResults, &results); err != nil {
return fmt.Errorf("decoding results (object) for sport %s: %w", sport.SportID, err)
}
} else {
fmt.Printf("No results found for sport %s\n", sport.SportID)
continue
}
for _, r := range results {
updatesCount := 0
if r.N != "" {
if n, err := strconv.Atoi(r.N); err == nil {
updatesCount = n
}
}
lastUpdatedAt, _ := time.Parse(time.RFC3339, r.UT)
startDate, _ := time.Parse(time.RFC3339, r.StartDate)
gameStarted, _ := time.Parse(time.RFC3339, r.GameStarted)
firstHalfEnded, _ := time.Parse(time.RFC3339, r.FirstHalfEnded)
secondHalfStarted, _ := time.Parse(time.RFC3339, r.SecondHalfStarted)
secondHalfEnded, _ := time.Parse(time.RFC3339, r.SecondHalfEnded)
gameEnded, _ := time.Parse(time.RFC3339, r.GameEnded)
createResult := domain.CreateEnetpulseResult{
ResultID: r.ID,
Name: r.Name,
SportFK: r.SportFK,
TournamentFK: r.TournamentFK,
TournamentTemplateFK: r.TournamentTemplateFK,
TournamentStageFK: r.TournamentStageFK,
TournamentStageName: r.TournamentStageName,
TournamentName: r.TournamentName,
TournamentTemplateName: r.TournamentTemplateName,
SportName: r.SportName,
StartDate: startDate,
StatusType: r.StatusType,
StatusDescFK: r.StatusDescFK,
RoundTypeFK: r.RoundTypeFK,
UpdatesCount: int32(updatesCount),
LastUpdatedAt: lastUpdatedAt,
Round: r.Round,
Live: r.Live,
VenueName: r.VenueName,
LivestatsPlus: r.LivestatsPlus,
LivestatsType: r.LivestatsType,
Commentary: r.Commentary,
LineupConfirmed: r.LineupConfirmed,
Verified: r.Verified,
Spectators: r.Spectators,
GameStarted: &gameStarted,
FirstHalfEnded: &firstHalfEnded,
SecondHalfStarted: &secondHalfStarted,
SecondHalfEnded: &secondHalfEnded,
GameEnded: &gameEnded,
}
if _, err := s.store.CreateEnetpulseResult(ctx, createResult); err != nil {
fmt.Printf("❌ failed to store result %s: %v\n", r.ID, err)
continue
}
}
break // limit to one sport if necessary
}
fmt.Println("✅ Successfully fetched and stored EnetPulse results")
return nil
}
func (s *Service) GetAllResults(ctx context.Context) ([]domain.EnetpulseResult, error) {
results, err := s.store.GetAllEnetpulseResults(ctx)
if err != nil {
return nil, fmt.Errorf("failed to fetch results from DB: %w", err)
}
fmt.Printf("✅ Retrieved %d results from DB\n", len(results))
return results, nil
}
// FetchAndStoreOutcomeTypes fetches outcome types from EnetPulse API and stores them in the DB.
func (s *Service) FetchAndStoreOutcomeTypes(ctx context.Context) error {
// 1⃣ Compose EnetPulse API URL
url := fmt.Sprintf(
"http://eapi.enetpulse.com/static/outcome_type/?language_typeFK=3&username=%s&token=%s",
s.cfg.EnetPulseConfig.UserName,
s.cfg.EnetPulseConfig.Token,
)
// 2⃣ Create HTTP request
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
return fmt.Errorf("failed to create outcome types request: %w", err)
}
// 3⃣ Execute request
resp, err := s.httpClient.Do(req)
if err != nil {
return fmt.Errorf("failed to call EnetPulse outcome_type API: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
return fmt.Errorf("unexpected status %d fetching outcome types: %s", resp.StatusCode, string(body))
}
// 4⃣ Decode JSON response
var outcomeResp struct {
OutcomeTypes map[string]struct {
ID string `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
N string `json:"n"`
UT string `json:"ut"`
} `json:"outcome_type"`
}
if err := json.NewDecoder(resp.Body).Decode(&outcomeResp); err != nil {
return fmt.Errorf("failed to decode outcome types JSON: %w", err)
}
// 5⃣ Iterate and store each outcome type
for _, ot := range outcomeResp.OutcomeTypes {
updatesCount := 0
if ot.N != "" {
if n, err := strconv.Atoi(ot.N); err == nil {
updatesCount = n
}
}
lastUpdatedAt, err := time.Parse(time.RFC3339, ot.UT)
if err != nil {
lastUpdatedAt = time.Time{}
}
createOutcome := domain.CreateEnetpulseOutcomeType{
OutcomeTypeID: ot.ID,
Name: ot.Name,
Description: ot.Description,
UpdatesCount: int32(updatesCount),
LastUpdatedAt: lastUpdatedAt,
}
// 6⃣ Save to DB (upsert)
if _, err := s.store.CreateEnetpulseOutcomeType(ctx, createOutcome); err != nil {
// Optionally log the failure, continue to next
continue
}
}
// s.logger.Info("✅ Successfully fetched and stored all EnetPulse outcome types")
return nil
}
// GetAllOutcomeTypes retrieves all stored outcome types from the DB.
func (s *Service) GetAllOutcomeTypes(ctx context.Context) ([]domain.EnetpulseOutcomeType, error) {
outcomes, err := s.store.GetAllEnetpulseOutcomeTypes(ctx)
if err != nil {
return nil, fmt.Errorf("failed to fetch outcome types from DB: %w", err)
}
// s.logger.Info("✅ Fetched outcome types from DB", zap.Int("count", len(outcomes)))
return outcomes, nil
}
func (s *Service) FetchAndStorePreodds(ctx context.Context) error {
// 1⃣ Fetch all events from DB
fixtures, err := s.store.GetAllEnetpulseFixtures(ctx)
if err != nil {
return fmt.Errorf("failed to fetch fixtures: %w", err)
}
// providerIDStr := strconv.Itoa(int(s.cfg.EnetPulseConfig.ProviderID))
// 2⃣ Loop through each fixture/event
for _, fixture := range fixtures {
url := fmt.Sprintf(
"http://eapi.enetpulse.com/preodds/event/?objectFK=%s&odds_providerFK=%s&username=%s&token=%s",
fixture.FixtureID,
s.cfg.EnetPulseConfig.ProviderID,
s.cfg.EnetPulseConfig.UserName,
s.cfg.EnetPulseConfig.Token,
)
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
// optionally log error and continue to next fixture
continue
}
resp, err := s.httpClient.Do(req)
if err != nil {
continue
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
continue
}
// Decode API response
var preoddsResp struct {
Preodds map[string]struct {
ID string `json:"id"`
OutcomeTypeFK string `json:"outcome_typeFK"`
OutcomeScopeFK string `json:"outcome_scopeFK"`
OutcomeSubtypeFK string `json:"outcome_subtypeFK"`
EventParticipantNumber string `json:"event_participant_number"`
Iparam string `json:"iparam"`
Iparam2 string `json:"iparam2"`
Dparam string `json:"dparam"`
Dparam2 string `json:"dparam2"`
Sparam string `json:"sparam"`
N string `json:"n"`
UT string `json:"ut"`
BettingOffers []struct {
ID string `json:"id"`
BettingOfferStatusFK int32 `json:"bettingoffer_status_fk"`
OddsProviderFK int32 `json:"odds_provider_fk"`
Odds float64 `json:"odds"`
OddsOld float64 `json:"odds_old"`
Active string `json:"active"`
CouponKey string `json:"coupon_key"`
N string `json:"n"`
UT string `json:"ut"`
} `json:"bettingoffers"`
} `json:"preodds"`
}
if err := json.NewDecoder(resp.Body).Decode(&preoddsResp); err != nil {
continue
}
// Iterate and store preodds and nested betting offers
for _, p := range preoddsResp.Preodds {
updatesCount := 0
if p.N != "" {
if n, err := strconv.Atoi(p.N); err == nil {
updatesCount = n
}
}
lastUpdatedAt, _ := time.Parse(time.RFC3339, p.UT)
eventParticipantNumber := int32(0)
if p.EventParticipantNumber != "" {
if epn, err := strconv.Atoi(p.EventParticipantNumber); err == nil {
eventParticipantNumber = int32(epn)
}
}
createPreodds := domain.CreateEnetpulsePreodds{
PreoddsID: p.ID,
EventFK: fixture.FixtureID,
OutcomeTypeFK: string(p.OutcomeTypeFK),
OutcomeScopeFK: string(p.OutcomeScopeFK),
OutcomeSubtypeFK: string(p.OutcomeSubtypeFK),
EventParticipantNumber: int(eventParticipantNumber),
IParam: p.Iparam,
IParam2: p.Iparam2,
DParam: p.Dparam,
DParam2: p.Dparam2,
SParam: p.Sparam,
UpdatesCount: int(updatesCount),
LastUpdatedAt: lastUpdatedAt,
}
storedPreodds, err := s.store.CreateEnetpulsePreodds(ctx, createPreodds)
if err != nil {
continue
}
for _, o := range p.BettingOffers {
bettingUpdates := 0
if o.N != "" {
if n, err := strconv.Atoi(o.N); err == nil {
bettingUpdates = n
}
}
bettingLastUpdatedAt, _ := time.Parse(time.RFC3339, o.UT)
createOffer := domain.CreateEnetpulsePreoddsBettingOffer{
BettingOfferID: o.ID,
PreoddsFK: storedPreodds.PreoddsID,
BettingOfferStatusFK: o.BettingOfferStatusFK,
OddsProviderFK: o.OddsProviderFK,
Odds: o.Odds,
OddsOld: o.OddsOld,
Active: o.Active,
CouponKey: o.CouponKey,
UpdatesCount: int(bettingUpdates),
LastUpdatedAt: bettingLastUpdatedAt,
}
_, _ = s.store.CreateEnetpulsePreoddsBettingOffer(ctx, createOffer)
}
}
}
return nil
}
// helper function to parse string to int32 safely
func ParseStringToInt32(s string) int32 {
if s == "" {
return 0
}
i, _ := strconv.Atoi(s)
return int32(i)
}
func (s *Service) GetAllPreodds(ctx context.Context) ([]domain.EnetpulsePreodds, error) {
preodds, err := s.store.GetAllEnetpulsePreodds(ctx)
if err != nil {
return nil, fmt.Errorf("failed to fetch preodds from DB: %w", err)
}
return preodds, nil
}
// FetchAndStoreBettingOffers fetches betting offers from EnetPulse API and stores them in the DB.
func (s *Service) StoreBettingOffers(ctx context.Context, preoddsID string, oddsProviderIDs []int32) error {
// 1⃣ Compose API URL
providers := make([]string, len(oddsProviderIDs))
for i, p := range oddsProviderIDs {
providers[i] = strconv.Itoa(int(p))
}
url := fmt.Sprintf(
"http://eapi.enetpulse.com/preodds_bettingoffer/?preoddsFK=%s&odds_providerFK=%s&username=%s&token=%s",
preoddsID,
strings.Join(providers, ","),
s.cfg.EnetPulseConfig.UserName,
s.cfg.EnetPulseConfig.Token,
)
// 2⃣ Create HTTP request
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
return fmt.Errorf("failed to create betting offer request: %w", err)
}
// 3⃣ Execute request
resp, err := s.httpClient.Do(req)
if err != nil {
return fmt.Errorf("failed to call EnetPulse betting offer API: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
return fmt.Errorf("unexpected status %d fetching betting offers: %s", resp.StatusCode, string(body))
}
// 4⃣ Decode JSON response
var offerResp struct {
BettingOffers map[string]struct {
ID string `json:"id"`
PreoddsFK string `json:"preodds_fk"`
BettingOfferStatusFK int32 `json:"bettingoffer_status_fk"`
OddsProviderFK int32 `json:"odds_provider_fk"`
Odds float64 `json:"odds"`
OddsOld float64 `json:"odds_old"`
Active string `json:"active"`
CouponKey string `json:"coupon_key"`
N string `json:"n"`
UT string `json:"ut"`
} `json:"bettingoffer"`
}
if err := json.NewDecoder(resp.Body).Decode(&offerResp); err != nil {
return fmt.Errorf("failed to decode betting offers JSON: %w", err)
}
// 5⃣ Iterate and store each betting offer
for _, o := range offerResp.BettingOffers {
updatesCount := 0
if o.N != "" {
if n, err := strconv.Atoi(o.N); err == nil {
updatesCount = n
}
}
lastUpdatedAt, err := time.Parse(time.RFC3339, o.UT)
if err != nil {
lastUpdatedAt = time.Time{}
}
createOffer := domain.CreateEnetpulsePreoddsBettingOffer{
BettingOfferID: o.ID,
PreoddsFK: preoddsID,
BettingOfferStatusFK: o.BettingOfferStatusFK,
OddsProviderFK: o.OddsProviderFK,
Odds: o.Odds,
OddsOld: o.OddsOld,
Active: o.Active,
CouponKey: o.CouponKey,
UpdatesCount: int(updatesCount),
LastUpdatedAt: lastUpdatedAt,
}
// 6⃣ Save to DB
if _, err := s.store.CreateEnetpulsePreoddsBettingOffer(ctx, createOffer); err != nil {
// optionally log the failure and continue
continue
}
}
return nil
}
// GetAllBettingOffers retrieves all stored betting offers from the DB.
func (s *Service) GetAllBettingOffers(ctx context.Context) ([]domain.EnetpulsePreoddsBettingOffer, error) {
offers, err := s.store.GetAllEnetpulsePreoddsBettingOffers(ctx)
if err != nil {
return nil, fmt.Errorf("failed to fetch betting offers from DB: %w", err)
}
return offers, nil
}
// helper to safely parse string to int32
// func parseStringToInt32(s string) int32 {
// if s == "" {
// return 0
// }
// i, err := strconv.Atoi(s)
// if err != nil {
// return 0
// }
// return int32(i)
// }
func (s *Service) FetchTournamentTemplates(ctx context.Context) (*domain.TournamentTemplatesResponse, error) {
url := fmt.Sprintf(
"http://eapi.enetpulse.com/tournamenttemplate/list/?username=%s&token=%s",
s.cfg.EnetPulseConfig.UserName, s.cfg.EnetPulseConfig.Token,
)
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
return nil, fmt.Errorf("creating tournament template request: %w", err)
}
resp, err := s.httpClient.Do(req)
if err != nil {
return nil, fmt.Errorf("requesting tournament templates: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
return nil, fmt.Errorf("failed to fetch tournament templates: %s, body: %s", resp.Status, string(body))
}
var templatesResp domain.TournamentTemplatesResponse
if err := json.NewDecoder(resp.Body).Decode(&templatesResp); err != nil {
return nil, fmt.Errorf("decoding tournament templates response: %w", err)
}
// Optionally save to DB or cache
return &templatesResp, nil
}
func (s *Service) FetchTournamentTemplateParticipants(ctx context.Context, templateID string, opts domain.ParticipantsOptions) (*domain.TournamentTemplateParticipantsResponse, error) {
// Build URL with optional parameters
url := fmt.Sprintf(
"http://eapi.enetpulse.com/tournamenttemplate/participants/?username=%s&token=%s&id=%s",
s.cfg.EnetPulseConfig.UserName, s.cfg.EnetPulseConfig.Token, templateID,
)
// Append optional params if set
if opts.IncludeProperties {
url += "&includeProperties=yes"
}
if opts.IncludeParticipantProperties {
url += "&includeParticipantProperties=yes"
}
if opts.IncludeParticipantSports {
url += "&includeParticipantSports=yes"
}
if opts.IncludeCountries {
url += "&includeCountries=yes"
}
if opts.IncludeCountryCodes {
url += "&includeCountryCodes=yes"
}
if opts.ParticipantType != "" {
url += "&participantType=" + opts.ParticipantType
}
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
return nil, fmt.Errorf("creating tournament participants request: %w", err)
}
resp, err := s.httpClient.Do(req)
if err != nil {
return nil, fmt.Errorf("requesting tournament participants: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
return nil, fmt.Errorf("failed to fetch tournament participants: %s, body: %s", resp.Status, string(body))
}
var participantsResp domain.TournamentTemplateParticipantsResponse
if err := json.NewDecoder(resp.Body).Decode(&participantsResp); err != nil {
return nil, fmt.Errorf("decoding tournament participants response: %w", err)
}
// Optionally save to DB or cache
return &participantsResp, nil
}
func (s *Service) FetchTournaments(ctx context.Context, templateID string) error {
url := fmt.Sprintf(
"http://eapi.enetpulse.com/tournament/list/?tournament_templateFK=%s&username=%s&token=%s",
templateID, s.cfg.EnetPulseConfig.UserName, s.cfg.EnetPulseConfig.Token,
)
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
return fmt.Errorf("creating tournament request: %w", err)
}
resp, err := s.httpClient.Do(req)
if err != nil {
return fmt.Errorf("requesting tournaments: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
return fmt.Errorf("failed to fetch tournaments (status %d): %s",
resp.StatusCode, string(body))
}
var tournamentsResp domain.TournamentsResponse
if err := json.NewDecoder(resp.Body).Decode(&tournamentsResp); err != nil {
return fmt.Errorf("decoding tournaments response: %w", err)
}
// Example: save tournaments to store or log them
for _, tournament := range tournamentsResp.Tournaments {
fmt.Printf("Tournament ID=%s Name=%s TemplateFK=%s UpdatedAt=%s\n",
tournament.ID, tournament.Name, tournament.TournamentTemplateFK, tournament.UT)
// e.g. s.store.SaveTournament(ctx, tournament)
}
return nil
}
func (s *Service) FetchTournamentParticipants(ctx context.Context, tournamentID string) error {
url := fmt.Sprintf(
"http://eapi.enetpulse.com/tournament_stage/participants/?id=%s&participantType=team&username=%s&token=%s",
tournamentID, s.cfg.EnetPulseConfig.UserName, s.cfg.EnetPulseConfig.Token,
)
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
return fmt.Errorf("creating tournament participants request: %w", err)
}
resp, err := s.httpClient.Do(req)
if err != nil {
return fmt.Errorf("requesting tournament participants: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
return fmt.Errorf("failed to fetch tournament participants (status %d): %s",
resp.StatusCode, string(body))
}
var participantsResp domain.TournamentParticipantsResponse
if err := json.NewDecoder(resp.Body).Decode(&participantsResp); err != nil {
return fmt.Errorf("decoding tournament participants response: %w", err)
}
// Example: loop participants
for tid, t := range participantsResp.Tournaments {
fmt.Printf("Tournament ID=%s Name=%s has %d participants\n",
tid, t.Name, len(t.Participants))
// You can loop deeper into t.Participants and store
}
return nil
}
func (s *Service) FetchTournamentStages(ctx context.Context, tournamentFK string) (*domain.TournamentStagesResponse, error) {
url := fmt.Sprintf(
"http://eapi.enetpulse.com/tournament_stage/list/?language_typeFK=3&tz=Europe/Sofia&tournamentFK=%s&username=%s&token=%s",
tournamentFK, s.cfg.EnetPulseConfig.UserName, s.cfg.EnetPulseConfig.Token,
)
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
return nil, fmt.Errorf("creating tournament stages request: %w", err)
}
resp, err := s.httpClient.Do(req)
if err != nil {
return nil, fmt.Errorf("requesting tournament stages: %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))
}
var stagesResp domain.TournamentStagesResponse
if err := json.NewDecoder(resp.Body).Decode(&stagesResp); err != nil {
return nil, fmt.Errorf("decoding tournament stages response: %w", err)
}
// optionally save to DB or cache here
return &stagesResp, nil
}
func (s *Service) FetchTournamentStageParticipants(
ctx context.Context,
stageID string,
includeProps, includeParticipantProps, includeCountries, includeCountryCodes bool,
) (*domain.TournamentStageParticipantsResponse, error) {
url := fmt.Sprintf(
"http://eapi.enetpulse.com/tournament_stage/participants/?id=%s&includeProperties=%s&includeParticipantProperties=%s&includeCountries=%s&includeCountryCodes=%s&username=%s&token=%s",
stageID,
boolToYesNo(includeProps),
boolToYesNo(includeParticipantProps),
boolToYesNo(includeCountries),
boolToYesNo(includeCountryCodes),
s.cfg.EnetPulseConfig.UserName, s.cfg.EnetPulseConfig.Token,
)
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
return nil, fmt.Errorf("creating participants request: %w", err)
}
resp, err := s.httpClient.Do(req)
if err != nil {
return nil, fmt.Errorf("requesting participants: %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))
}
var participantsResp domain.TournamentStageParticipantsResponse
if err := json.NewDecoder(resp.Body).Decode(&participantsResp); err != nil {
return nil, fmt.Errorf("decoding participants response: %w", err)
}
// optionally save to DB or cache here
return &participantsResp, nil
}
// helper function to convert bool to yes/no
func boolToYesNo(v bool) string {
if v {
return "yes"
}
return "no"
}
func (s *Service) FetchDailyEvents(ctx context.Context, req domain.DailyEventsRequest) (*domain.DailyEventsResponse, error) {
baseURL := "http://eapi.enetpulse.com/event/daily/"
query := fmt.Sprintf("?username=%s&token=%s&language_typeFK=3", s.cfg.EnetPulseConfig.UserName, s.cfg.EnetPulseConfig.Token)
// Required at least one of sportFK / tournament_templateFK / tournament_stageFK
if req.SportFK != 0 {
query += fmt.Sprintf("&sportFK=%d", req.SportFK)
}
if req.TournamentTemplateFK != 0 {
query += fmt.Sprintf("&tournament_templateFK=%d", req.TournamentTemplateFK)
}
if req.TournamentStageFK != 0 {
query += fmt.Sprintf("&tournament_stageFK=%d", req.TournamentStageFK)
}
// Optionals
if req.Date != "" {
query += fmt.Sprintf("&date=%s", req.Date)
}
if req.Live != "" {
query += fmt.Sprintf("&live=%s", req.Live)
}
if req.IncludeVenue != "" {
query += fmt.Sprintf("&includeVenue=%s", req.IncludeVenue)
}
if req.StatusType != "" {
query += fmt.Sprintf("&status_type=%s", req.StatusType)
}
if req.IncludeEventProperties != "" {
query += fmt.Sprintf("&includeEventProperties=%s", req.IncludeEventProperties)
}
if req.IncludeCountryCodes != "" {
query += fmt.Sprintf("&includeCountryCodes=%s", req.IncludeCountryCodes)
}
if req.IncludeFirstLastName != "" {
query += fmt.Sprintf("&includeFirstLastName=%s", req.IncludeFirstLastName)
}
if req.IncludeDeleted != "" {
query += fmt.Sprintf("&includeDeleted=%s", req.IncludeDeleted)
}
fullURL := baseURL + query
httpReq, err := http.NewRequestWithContext(ctx, http.MethodGet, fullURL, nil)
if err != nil {
return nil, err
}
resp, err := s.httpClient.Do(httpReq)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
return nil, fmt.Errorf("failed to fetch daily events: status %d body: %s", resp.StatusCode, string(body))
}
var dailyResp domain.DailyEventsResponse
if err := json.NewDecoder(resp.Body).Decode(&dailyResp); err != nil {
return nil, err
}
return &dailyResp, nil
}
// func (s *Service) FetchFixtures(ctx context.Context, params domain.FixturesRequest) (*domain.FixturesResponse, error) {
// // Build base URL
// url := fmt.Sprintf("http://eapi.enetpulse.com/event/fixtures/?username=%s&token=%s",
// s.cfg.EnetPulseConfig.UserName, s.cfg.EnetPulseConfig.Token)
// // Required filter: one of sportFK | tournament_templateFK | tournament_stageFK
// if params.SportFK != 0 {
// url += fmt.Sprintf("&sportFK=%d", params.SportFK)
// }
// if params.TournamentTemplateFK != 0 {
// url += fmt.Sprintf("&tournament_templateFK=%d", params.TournamentTemplateFK)
// }
// if params.TournamentStageFK != 0 {
// url += fmt.Sprintf("&tournament_stageFK=%d", params.TournamentStageFK)
// }
// // Optional filters
// if params.LanguageTypeFK != 0 {
// url += fmt.Sprintf("&language_typeFK=%d", params.LanguageTypeFK)
// } else {
// url += "&language_typeFK=3" // default to English
// }
// if params.Date != "" {
// url += fmt.Sprintf("&date=%s", params.Date)
// }
// if params.Live != "" {
// url += fmt.Sprintf("&live=%s", params.Live)
// }
// if params.IncludeVenue {
// url += "&includeVenue=yes"
// }
// if !params.IncludeEventProperties {
// url += "&includeEventProperties=no"
// }
// if params.IncludeCountryCodes {
// url += "&includeCountryCodes=yes"
// }
// if params.IncludeFirstLastName {
// url += "&includeFirstLastName=yes"
// }
// // Make request
// req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
// if err != nil {
// return nil, fmt.Errorf("creating fixtures request: %w", err)
// }
// resp, err := s.httpClient.Do(req)
// if err != nil {
// return nil, fmt.Errorf("requesting fixtures: %w", err)
// }
// defer resp.Body.Close()
// if resp.StatusCode != http.StatusOK {
// body, _ := io.ReadAll(resp.Body)
// return nil, fmt.Errorf("unexpected status %d: %s", resp.StatusCode, string(body))
// }
// // Decode response
// var fixturesResp domain.FixturesResponse
// if err := json.NewDecoder(resp.Body).Decode(&fixturesResp); err != nil {
// return nil, fmt.Errorf("decoding fixtures response: %w", err)
// }
// return &fixturesResp, nil
// }
func (s *Service) FetchResults(ctx context.Context, params domain.ResultsRequest) (*domain.ResultsResponse, error) {
// Build base URL
url := fmt.Sprintf("http://eapi.enetpulse.com/event/results/?username=%s&token=%s",
s.cfg.EnetPulseConfig.UserName, s.cfg.EnetPulseConfig.Token)
// Required filters (at least one of them)
if params.SportFK != 0 {
url += fmt.Sprintf("&sportFK=%d", params.SportFK)
}
if params.TournamentTemplateFK != 0 {
url += fmt.Sprintf("&tournament_templateFK=%d", params.TournamentTemplateFK)
}
if params.TournamentStageFK != 0 {
url += fmt.Sprintf("&tournament_stageFK=%d", params.TournamentStageFK)
}
// Optional filters
if params.LanguageTypeFK != 0 {
url += fmt.Sprintf("&language_typeFK=%d", params.LanguageTypeFK)
} else {
url += "&language_typeFK=3" // default English
}
if params.Date != "" {
url += fmt.Sprintf("&date=%s", params.Date)
}
if params.Live != "" {
url += fmt.Sprintf("&live=%s", params.Live)
}
if params.IncludeVenue {
url += "&includeVenue=yes"
}
if !params.IncludeEventProperties {
url += "&includeEventProperties=no"
}
if params.IncludeCountryCodes {
url += "&includeCountryCodes=yes"
}
if params.IncludeFirstLastName {
url += "&includeFirstLastName=yes"
}
if params.Limit > 0 {
url += fmt.Sprintf("&limit=%d", params.Limit)
}
if params.Offset > 0 {
url += fmt.Sprintf("&offset=%d", params.Offset)
}
// Make request
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
return nil, fmt.Errorf("creating results request: %w", err)
}
resp, err := s.httpClient.Do(req)
if err != nil {
return nil, fmt.Errorf("requesting results: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
return nil, fmt.Errorf("unexpected status %d: %s", resp.StatusCode, string(body))
}
// Decode response
var resultsResp domain.ResultsResponse
if err := json.NewDecoder(resp.Body).Decode(&resultsResp); err != nil {
return nil, fmt.Errorf("decoding results response: %w", err)
}
return &resultsResp, nil
}
func (s *Service) FetchEventDetails(ctx context.Context, params domain.EventDetailsRequest) (*domain.EventDetailsResponse, error) {
// Base URL
url := fmt.Sprintf("http://eapi.enetpulse.com/event/details/?username=%s&token=%s&id=%d",
s.cfg.EnetPulseConfig.UserName, s.cfg.EnetPulseConfig.Token, params.ID)
// Optional flags
if params.IncludeLineups {
url += "&includeLineups=yes"
}
if params.IncludeIncidents {
url += "&includeIncidents=yes"
}
if !params.IncludeExtendedResults {
url += "&includeExtendedResults=no"
}
if params.IncludeProperties {
url += "&includeProperties=yes"
}
if params.IncludeLivestats {
url += "&includeLivestats=yes"
}
if params.IncludeVenue {
url += "&includeVenue=yes"
}
if params.IncludeCountryCodes {
url += "&includeCountryCodes=yes"
}
if params.IncludeFirstLastName {
url += "&includeFirstLastName=yes"
}
if params.IncludeDeleted {
url += "&includeDeleted=yes"
}
if params.IncludeReference {
url += "&includeReference=yes"
}
if params.IncludeObjectParticipants {
url += "&includeObjectParticipants=yes"
}
if params.IncludeEventIncidentRelation {
url += "&includeEventIncidentRelation=yes"
}
if params.IncludeTeamProperties {
url += "&includeTeamProperties=yes"
}
if params.IncludeObjectRound {
url += "&includeObjectRound=yes"
}
// Make request
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
return nil, fmt.Errorf("creating event details request: %w", err)
}
resp, err := s.httpClient.Do(req)
if err != nil {
return nil, fmt.Errorf("requesting event details: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
return nil, fmt.Errorf("unexpected status %d: %s", resp.StatusCode, string(body))
}
// Decode response
var detailsResp domain.EventDetailsResponse
if err := json.NewDecoder(resp.Body).Decode(&detailsResp); err != nil {
return nil, fmt.Errorf("decoding event details response: %w", err)
}
return &detailsResp, nil
}
func (s *Service) FetchEventList(ctx context.Context, params domain.EventListRequest) (*domain.EventListResponse, error) {
// You must provide either TournamentFK or TournamentStageFK
if params.TournamentFK == 0 && params.TournamentStageFK == 0 {
return nil, fmt.Errorf("either TournamentFK or TournamentStageFK is required")
}
// Base URL
url := fmt.Sprintf("http://eapi.enetpulse.com/event/list/?username=%s&token=%s",
s.cfg.EnetPulseConfig.UserName, s.cfg.EnetPulseConfig.Token)
// Mandatory one of
if params.TournamentFK != 0 {
url += fmt.Sprintf("&tournamentFK=%d", params.TournamentFK)
}
if params.TournamentStageFK != 0 {
url += fmt.Sprintf("&tournament_stageFK=%d", params.TournamentStageFK)
}
// Optional parameters
if !params.IncludeEventProperties {
url += "&includeEventProperties=no"
}
if params.StatusType != "" {
url += "&status_type=" + params.StatusType
}
if params.IncludeVenue {
url += "&includeVenue=yes"
}
if params.IncludeDeleted {
url += "&includeDeleted=yes"
}
if params.Limit > 0 {
url += fmt.Sprintf("&limit=%d", params.Limit)
}
if params.Offset > 0 {
url += fmt.Sprintf("&offset=%d", params.Offset)
}
// Make request
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
return nil, fmt.Errorf("creating event list request: %w", err)
}
resp, err := s.httpClient.Do(req)
if err != nil {
return nil, fmt.Errorf("requesting event list: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
return nil, fmt.Errorf("unexpected status %d: %s", resp.StatusCode, string(body))
}
// Decode response
var eventListResp domain.EventListResponse
if err := json.NewDecoder(resp.Body).Decode(&eventListResp); err != nil {
return nil, fmt.Errorf("decoding event list response: %w", err)
}
return &eventListResp, nil
}
func (s *Service) FetchParticipantFixtures(ctx context.Context, params domain.ParticipantFixturesRequest) (*domain.ParticipantFixturesResponse, error) {
// You must provide ParticipantFK
if params.ParticipantFK == 0 {
return nil, fmt.Errorf("ParticipantFK is required")
}
// Base URL
url := fmt.Sprintf("http://eapi.enetpulse.com/event/participant_fixtures/?username=%s&token=%s",
s.cfg.EnetPulseConfig.UserName, s.cfg.EnetPulseConfig.Token)
// Mandatory param
url += fmt.Sprintf("&participantFK=%d", params.ParticipantFK)
// Optionals
if params.SportFK != 0 {
url += fmt.Sprintf("&sportFK=%d", params.SportFK)
}
if params.TournamentFK != 0 {
url += fmt.Sprintf("&tournamentFK=%d", params.TournamentFK)
}
if params.TournamentTemplateFK != 0 {
url += fmt.Sprintf("&tournament_templateFK=%d", params.TournamentTemplateFK)
}
if params.TournamentStageFK != 0 {
url += fmt.Sprintf("&tournament_stageFK=%d", params.TournamentStageFK)
}
if params.Date != "" {
url += "&date=" + params.Date
}
if params.Live != "" {
url += "&live=" + params.Live
}
if params.Limit > 0 {
url += fmt.Sprintf("&limit=%d", params.Limit)
}
if params.Offset > 0 {
url += fmt.Sprintf("&offset=%d", params.Offset)
}
if params.IncludeVenue {
url += "&includeVenue=yes"
}
if params.IncludeCountryCodes {
url += "&includeCountryCodes=yes"
}
if params.IncludeFirstLastName {
url += "&includeFirstLastName=yes"
}
if !params.IncludeEventProperties {
// default yes → only append when false
url += "&includeEventProperties=no"
}
if params.LanguageTypeFK != 0 {
url += fmt.Sprintf("&language_typeFK=%d", params.LanguageTypeFK)
}
// Make request
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
return nil, fmt.Errorf("creating participant fixtures request: %w", err)
}
resp, err := s.httpClient.Do(req)
if err != nil {
return nil, fmt.Errorf("requesting participant fixtures: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
return nil, fmt.Errorf("unexpected status %d: %s", resp.StatusCode, string(body))
}
// Decode response
var fixturesResp domain.ParticipantFixturesResponse
if err := json.NewDecoder(resp.Body).Decode(&fixturesResp); err != nil {
return nil, fmt.Errorf("decoding participant fixtures response: %w", err)
}
return &fixturesResp, nil
}
func (s *Service) FetchParticipantResults(ctx context.Context, params domain.ParticipantResultsRequest) (*domain.ParticipantResultsResponse, error) {
// Required param
if params.ParticipantFK == 0 {
return nil, fmt.Errorf("participantFK is required")
}
// Base URL
url := fmt.Sprintf("http://eapi.enetpulse.com/event/participant_results/?username=%s&token=%s&participantFK=%d",
s.cfg.EnetPulseConfig.UserName, s.cfg.EnetPulseConfig.Token, params.ParticipantFK)
// Optional parameters
if params.Limit > 0 {
url += fmt.Sprintf("&limit=%d", params.Limit)
}
if params.Offset > 0 {
url += fmt.Sprintf("&offset=%d", params.Offset)
}
if params.IncludeDeleted {
url += "&includeDeleted=yes"
}
if params.IncludeVenue {
url += "&includeVenue=yes"
}
// Make request
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
return nil, fmt.Errorf("creating participant_results request: %w", err)
}
resp, err := s.httpClient.Do(req)
if err != nil {
return nil, fmt.Errorf("requesting participant_results: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
return nil, fmt.Errorf("unexpected status %d: %s", resp.StatusCode, string(body))
}
// Decode response
var prResp domain.ParticipantResultsResponse
if err := json.NewDecoder(resp.Body).Decode(&prResp); err != nil {
return nil, fmt.Errorf("decoding participant_results response: %w", err)
}
return &prResp, nil
}
func (s *Service) FetchTeamLogo(ctx context.Context, teamFK int64) (*domain.TeamLogoResponse, error) {
if teamFK == 0 {
return nil, fmt.Errorf("teamFK is required")
}
// Build URL
url := fmt.Sprintf("http://eapi.enetpulse.com/image/team_logo/?teamFK=%d&username=%s&token=%s",
teamFK, s.cfg.EnetPulseConfig.UserName, s.cfg.EnetPulseConfig.Token)
// Make request
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
return nil, fmt.Errorf("creating team logo request: %w", err)
}
resp, err := s.httpClient.Do(req)
if err != nil {
return nil, fmt.Errorf("requesting team logo: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
return nil, fmt.Errorf("unexpected status %d: %s", resp.StatusCode, string(body))
}
// Read image bytes
data, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("reading team logo body: %w", err)
}
// Build response
return &domain.TeamLogoResponse{
ContentType: resp.Header.Get("Content-Type"),
Data: data,
URL: url, // optional: you can also return the URL for reference
}, nil
}
func (s *Service) FetchTeamShirts(ctx context.Context, teamFK int64) (*domain.TeamShirtsResponse, error) {
if teamFK == 0 {
return nil, fmt.Errorf("teamFK is required")
}
// Build URL
url := fmt.Sprintf("http://eapi.enetpulse.com/image/team_shirt/?teamFK=%d&username=%s&token=%s",
teamFK, s.cfg.EnetPulseConfig.UserName, s.cfg.EnetPulseConfig.Token)
// Make request
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
return nil, fmt.Errorf("creating team shirts request: %w", err)
}
resp, err := s.httpClient.Do(req)
if err != nil {
return nil, fmt.Errorf("requesting team shirts: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
return nil, fmt.Errorf("unexpected status %d: %s", resp.StatusCode, string(body))
}
// Read response bytes — because shirts may come back as JSON list of URLs *or* image bytes
data, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("reading team shirts body: %w", err)
}
// Build response — since Enetpulse typically returns a list of image URLs,
// well return raw bytes if only one image, or JSON if multiple images.
return &domain.TeamShirtsResponse{
ContentType: resp.Header.Get("Content-Type"),
RawData: data,
URL: url,
}, nil
}
func (s *Service) FetchCountryFlag(ctx context.Context, countryFK int64) (*domain.ImageResponse, error) {
if countryFK == 0 {
return nil, fmt.Errorf("countryFK is required")
}
// Build URL
url := fmt.Sprintf("http://eapi.enetpulse.com/image/country_flag/?countryFK=%d&username=%s&token=%s",
countryFK, s.cfg.EnetPulseConfig.UserName, s.cfg.EnetPulseConfig.Token)
// Create HTTP request
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
return nil, fmt.Errorf("creating country flag request: %w", err)
}
// Execute request
resp, err := s.httpClient.Do(req)
if err != nil {
return nil, fmt.Errorf("requesting country flag: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
return nil, fmt.Errorf("unexpected status %d: %s", resp.StatusCode, string(body))
}
// Read image bytes
data, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("reading country flag body: %w", err)
}
// Return response
return &domain.ImageResponse{
ContentType: resp.Header.Get("Content-Type"),
RawData: data,
URL: url,
}, nil
}
func (s *Service) FetchPreMatchOdds(ctx context.Context, params domain.PreMatchOddsRequest) (*domain.PreMatchOddsResponse, error) {
// Mandatory parameter check
if params.ObjectFK == 0 {
return nil, fmt.Errorf("objectFK (event ID) is required")
}
// Base URL
url := fmt.Sprintf("http://eapi.enetpulse.com/preodds/event/?username=%s&token=%s",
s.cfg.EnetPulseConfig.UserName, s.cfg.EnetPulseConfig.Token)
// Mandatory parameters
url += fmt.Sprintf("&objectFK=%d", params.ObjectFK)
if len(params.OddsProviderFK) > 0 {
url += "&odds_providerFK=" + joinIntSlice(params.OddsProviderFK)
}
if params.OutcomeTypeFK != 0 {
url += fmt.Sprintf("&outcome_typeFK=%d", params.OutcomeTypeFK)
}
if params.OutcomeScopeFK != 0 {
url += fmt.Sprintf("&outcome_scopeFK=%d", params.OutcomeScopeFK)
}
if params.OutcomeSubtypeFK != 0 {
url += fmt.Sprintf("&outcome_subtypeFK=%d", params.OutcomeSubtypeFK)
}
// Optional parameters
if params.Limit > 0 {
url += fmt.Sprintf("&limit=%d", params.Limit)
}
if params.Offset > 0 {
url += fmt.Sprintf("&offset=%d", params.Offset)
}
if params.LanguageTypeFK != 0 {
url += fmt.Sprintf("&language_typeFK=%d", params.LanguageTypeFK)
}
// Create HTTP request
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
return nil, fmt.Errorf("creating pre-match odds request: %w", err)
}
// Execute request
resp, err := s.httpClient.Do(req)
if err != nil {
return nil, fmt.Errorf("requesting pre-match odds: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
return nil, fmt.Errorf("unexpected status %d: %s", resp.StatusCode, string(body))
}
// Decode response
var oddsResp domain.PreMatchOddsResponse
if err := json.NewDecoder(resp.Body).Decode(&oddsResp); err != nil {
return nil, fmt.Errorf("decoding pre-match odds response: %w", err)
}
return &oddsResp, nil
}
// Helper function to join int slice with comma
func joinIntSlice(slice []int64) string {
result := ""
for i, v := range slice {
if i > 0 {
result += ","
}
result += fmt.Sprintf("%d", v)
}
return result
}
func (s *Service) FetchTournamentStageOdds(ctx context.Context, params domain.TournamentStageOddsRequest) (*domain.TournamentStageOddsResponse, error) {
// Mandatory parameter check
if params.ObjectFK == 0 {
return nil, fmt.Errorf("objectFK (tournament stage ID) is required")
}
if params.OutcomeTypeFK == 0 {
return nil, fmt.Errorf("outcomeTypeFK is required")
}
// Base URL
url := fmt.Sprintf("http://eapi.enetpulse.com/preodds/tournament_stage/?username=%s&token=%s",
s.cfg.EnetPulseConfig.UserName, s.cfg.EnetPulseConfig.Token)
// Mandatory parameters
url += fmt.Sprintf("&objectFK=%d", params.ObjectFK)
url += fmt.Sprintf("&outcome_typeFK=%d", params.OutcomeTypeFK)
if params.OddsProviderFK != 0 {
url += fmt.Sprintf("&odds_providerFK=%d", params.OddsProviderFK)
}
if params.OutcomeScopeFK != 0 {
url += fmt.Sprintf("&outcome_scopeFK=%d", params.OutcomeScopeFK)
}
if params.OutcomeSubtypeFK != 0 {
url += fmt.Sprintf("&outcome_subtypeFK=%d", params.OutcomeSubtypeFK)
}
// Optional parameters
if params.Limit > 0 {
url += fmt.Sprintf("&limit=%d", params.Limit)
}
if params.Offset > 0 {
url += fmt.Sprintf("&offset=%d", params.Offset)
}
if params.LanguageTypeFK != 0 {
url += fmt.Sprintf("&language_typeFK=%d", params.LanguageTypeFK)
}
// Create HTTP request
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
return nil, fmt.Errorf("creating tournament stage odds request: %w", err)
}
// Execute request
resp, err := s.httpClient.Do(req)
if err != nil {
return nil, fmt.Errorf("requesting tournament stage odds: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
return nil, fmt.Errorf("unexpected status %d: %s", resp.StatusCode, string(body))
}
// Decode response
var oddsResp domain.TournamentStageOddsResponse
if err := json.NewDecoder(resp.Body).Decode(&oddsResp); err != nil {
return nil, fmt.Errorf("decoding tournament stage odds response: %w", err)
}
return &oddsResp, nil
}
func (s *Service) FetchTournamentOdds(ctx context.Context, params domain.TournamentOddsRequest) (*domain.TournamentOddsResponse, error) {
// Mandatory parameter check
if params.ObjectFK == 0 {
return nil, fmt.Errorf("objectFK (tournament ID) is required")
}
if params.OutcomeTypeFK == 0 {
return nil, fmt.Errorf("outcomeTypeFK is required")
}
// Base URL
url := fmt.Sprintf("http://eapi.enetpulse.com/preodds/tournament/?username=%s&token=%s",
s.cfg.EnetPulseConfig.UserName, s.cfg.EnetPulseConfig.Token)
// Mandatory parameters
url += fmt.Sprintf("&objectFK=%d", params.ObjectFK)
url += fmt.Sprintf("&outcome_typeFK=%d", params.OutcomeTypeFK)
if params.OddsProviderFK != 0 {
url += fmt.Sprintf("&odds_providerFK=%d", params.OddsProviderFK)
}
if params.OutcomeScopeFK != 0 {
url += fmt.Sprintf("&outcome_scopeFK=%d", params.OutcomeScopeFK)
}
if params.OutcomeSubtypeFK != 0 {
url += fmt.Sprintf("&outcome_subtypeFK=%d", params.OutcomeSubtypeFK)
}
// Optional parameters
if params.Limit > 0 {
url += fmt.Sprintf("&limit=%d", params.Limit)
}
if params.Offset > 0 {
url += fmt.Sprintf("&offset=%d", params.Offset)
}
if params.LanguageTypeFK != 0 {
url += fmt.Sprintf("&language_typeFK=%d", params.LanguageTypeFK)
}
// Create HTTP request
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
return nil, fmt.Errorf("creating tournament odds request: %w", err)
}
// Execute request
resp, err := s.httpClient.Do(req)
if err != nil {
return nil, fmt.Errorf("requesting tournament odds: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
return nil, fmt.Errorf("unexpected status %d: %s", resp.StatusCode, string(body))
}
// Decode response
var oddsResp domain.TournamentOddsResponse
if err := json.NewDecoder(resp.Body).Decode(&oddsResp); err != nil {
return nil, fmt.Errorf("decoding tournament odds response: %w", err)
}
return &oddsResp, nil
}