Yimaru-BackEnd/internal/services/enet_pulse/service.go

1241 lines
37 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"
"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) 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)
}
for _, sport := range sports {
// 2⃣ Compose URL for each sport using its sportID
url := fmt.Sprintf(
"http://eapi.enetpulse.com/tournament_template/list/?sportFK=%s&username=%s&token=%s",
sport.SportID,
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 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
var templatesResp struct {
TournamentTemplates map[string]struct {
ID string `json:"id"`
Name string `json:"name"`
SportFK string `json:"sportFK"`
Gender string `json:"gender"`
N string `json:"n"` // updates count
UT string `json:"ut"` // timestamp
} `json:"tournament_templates"`
}
if err := json.NewDecoder(resp.Body).Decode(&templatesResp); err != nil {
return fmt.Errorf("decoding tournament templates for sport %s: %w", sport.SportID, err)
}
// 6⃣ Iterate and store each tournament template
for _, tmpl := range templatesResp.TournamentTemplates {
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{}
}
createTemplate := domain.CreateEnetpulseTournamentTemplate{
TemplateID: tmpl.ID,
Name: tmpl.Name,
SportFK: sport.ID, // use DB sport ID
Gender: tmpl.Gender,
UpdatesCount: updatesCount,
LastUpdatedAt: lastUpdatedAt,
Status: 1, // default active
}
// Insert into DB
if _, err := s.store.CreateEnetpulseTournamentTemplate(ctx, createTemplate); err != nil {
// Log error but continue
// s.logger.Error("failed to store tournament template", zap.String("template_id", tmpl.ID), zap.Error(err))
continue
}
}
}
// s.logger.Info("Successfully fetched and stored all tournament templates")
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,
// 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
}