package enetpulse import ( "context" "encoding/json" "fmt" "io" "net/http" "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) FetchSports(ctx context.Context) error { url := fmt.Sprintf( "http://eapi.enetpulse.com/sport/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 fmt.Errorf("creating sport request: %w", err) } resp, err := s.httpClient.Do(req) if err != nil { return fmt.Errorf("requesting sports: %w", err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { body, _ := io.ReadAll(resp.Body) return fmt.Errorf("failed to fetch sports (status %d): %s", resp.StatusCode, string(body)) } var sportsResp domain.SportsResponse if err := json.NewDecoder(resp.Body).Decode(&sportsResp); err != nil { return fmt.Errorf("decoding sports response: %w", err) } // Example: save sports to store or process for _, sport := range sportsResp.Sports { // Insert/update in DB or cache // e.g. s.store.SaveSport(ctx, sport) fmt.Printf("Fetched sport: ID=%s Name=%s UpdatedAt=%s\n", sport.ID, sport.Name, sport.UT) } return nil } 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, // we’ll 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 }