From 09605556802f753438b00c7440fc00dbf81d47fe Mon Sep 17 00:00:00 2001 From: Asher Samuel Date: Tue, 27 May 2025 10:56:25 +0300 Subject: [PATCH] prematch fetch support for betfair --- internal/domain/event.go | 1 + internal/repository/event.go | 1 + internal/services/event/service.go | 156 ++++++++++++++++++++++------- 3 files changed, 120 insertions(+), 38 deletions(-) diff --git a/internal/domain/event.go b/internal/domain/event.go index 53dc4d9..9a463ca 100644 --- a/internal/domain/event.go +++ b/internal/domain/event.go @@ -23,6 +23,7 @@ type Event struct { MatchPeriod int IsLive bool Status string + Source string } type BetResult struct { diff --git a/internal/repository/event.go b/internal/repository/event.go index d09f3f1..337e90b 100644 --- a/internal/repository/event.go +++ b/internal/repository/event.go @@ -40,6 +40,7 @@ func (s *Store) SaveEvent(ctx context.Context, e domain.Event) error { MatchPeriod: pgtype.Int4{Int32: int32(e.MatchPeriod), Valid: true}, IsLive: pgtype.Bool{Bool: e.IsLive, Valid: true}, Status: pgtype.Text{String: e.Status, Valid: true}, + Source: pgtype.Text{String: e.Source, Valid: true}, }) } func (s *Store) SaveUpcomingEvent(ctx context.Context, e domain.UpcomingEvent) error { diff --git a/internal/services/event/service.go b/internal/services/event/service.go index 5112834..ee765bc 100644 --- a/internal/services/event/service.go +++ b/internal/services/event/service.go @@ -30,6 +30,29 @@ func New(token string, store *repository.Store) Service { } func (s *service) FetchLiveEvents(ctx context.Context) error { + var wg sync.WaitGroup + urls := []struct { + name string + source string + }{ + {"https://api.b365api.com/v1/bet365/inplay?sport_id=%d&token=%s", "bet365"}, + {"https://api.b365api.com/v1/betfair/sb/inplay?sport_id=%d&token=%s", "betfair"}, + } + + for _, url := range urls { + wg.Add(1) + + go func() { + defer wg.Done() + s.fetchLiveEvents(ctx, url.name, url.source) + }() + } + + wg.Wait() + return nil +} + +func (s *service) fetchLiveEvents(ctx context.Context, url, source string) error { sportIDs := []int{1, 13, 78, 18, 91, 16, 17, 14, 12, 3, 2, 4, 83, 15, 92, 94, 8, 19, 36, 66, 9, 75, 90, 95, 110, 107, 151, 162, 148} var wg sync.WaitGroup @@ -39,7 +62,7 @@ func (s *service) FetchLiveEvents(ctx context.Context) error { go func(sportID int) { defer wg.Done() - url := fmt.Sprintf("https://api.b365api.com/v1/bet365/inplay?sport_id=%d&token=%s", sportID, s.token) + url := fmt.Sprintf(url, sportID, s.token) resp, err := http.Get(url) if err != nil { fmt.Printf(" Failed request for sport_id=%d: %v\n", sportID, err) @@ -49,45 +72,17 @@ func (s *service) FetchLiveEvents(ctx context.Context) error { body, _ := io.ReadAll(resp.Body) - var data struct { - Success int `json:"success"` - Results [][]map[string]interface{} `json:"results"` - } - if err := json.Unmarshal(body, &data); err != nil || data.Success != 1 { - fmt.Printf(" Decode failed for sport_id=%d\nRaw: %s\n", sportID, string(body)) - return + events := []domain.Event{} + switch source { + case "bet365": + events = handleBet365prematch(body, sportID) + case "betfair": + events = handleBetfairprematch(body, sportID) } - for _, group := range data.Results { - for _, ev := range group { - if getString(ev["type"]) != "EV" { - continue - } - - event := domain.Event{ - ID: getString(ev["ID"]), - SportID: fmt.Sprintf("%d", sportID), - MatchName: getString(ev["NA"]), - Score: getString(ev["SS"]), - MatchMinute: getInt(ev["TM"]), - TimerStatus: getString(ev["TT"]), - HomeTeamID: getString(ev["HT"]), - AwayTeamID: getString(ev["AT"]), - HomeKitImage: getString(ev["K1"]), - AwayKitImage: getString(ev["K2"]), - LeagueName: getString(ev["CT"]), - LeagueID: getString(ev["C2"]), - LeagueCC: getString(ev["CB"]), - StartTime: time.Now().UTC().Format(time.RFC3339), - IsLive: true, - Status: "live", - MatchPeriod: getInt(ev["MD"]), - AddedTime: getInt(ev["TA"]), - } - - if err := s.store.SaveEvent(ctx, event); err != nil { - fmt.Printf("Could not store live event [id=%s]: %v\n", event.ID, err) - } + for _, event := range events { + if err := s.store.SaveEvent(ctx, event); err != nil { + fmt.Printf("Could not store live event [id=%s]: %v\n", event.ID, err) } } }(sportID) @@ -96,6 +91,91 @@ func (s *service) FetchLiveEvents(ctx context.Context) error { wg.Wait() fmt.Println("All live events fetched and stored.") return nil + +} + +func handleBet365prematch(body []byte, sportID int) []domain.Event { + var data struct { + Success int `json:"success"` + Results [][]map[string]interface{} `json:"results"` + } + + events := []domain.Event{} + if err := json.Unmarshal(body, &data); err != nil || data.Success != 1 { + fmt.Printf(" Decode failed for sport_id=%d\nRaw: %s\n", sportID, string(body)) + return events + } + + for _, group := range data.Results { + for _, ev := range group { + if getString(ev["type"]) != "EV" { + continue + } + + event := domain.Event{ + ID: getString(ev["ID"]), + SportID: fmt.Sprintf("%d", sportID), + MatchName: getString(ev["NA"]), + Score: getString(ev["SS"]), + MatchMinute: getInt(ev["TM"]), + TimerStatus: getString(ev["TT"]), + HomeTeamID: getString(ev["HT"]), + AwayTeamID: getString(ev["AT"]), + HomeKitImage: getString(ev["K1"]), + AwayKitImage: getString(ev["K2"]), + LeagueName: getString(ev["CT"]), + LeagueID: getString(ev["C2"]), + LeagueCC: getString(ev["CB"]), + StartTime: time.Now().UTC().Format(time.RFC3339), + IsLive: true, + Status: "live", + MatchPeriod: getInt(ev["MD"]), + AddedTime: getInt(ev["TA"]), + Source: "bet365", + } + + events = append(events, event) + } + } + + return events +} + +func handleBetfairprematch(body []byte, sportID int) []domain.Event { + var data struct { + Success int `json:"success"` + Results []map[string]interface{} `json:"results"` + } + + events := []domain.Event{} + if err := json.Unmarshal(body, &data); err != nil || data.Success != 1 { + fmt.Printf(" Decode failed for sport_id=%d\nRaw: %s\n", sportID, string(body)) + return events + } + + for _, ev := range data.Results { + homeRaw, _ := ev["home"].(map[string]interface{}) + homeId, _ := homeRaw["id"].(string) + + awayRaw, _ := ev["home"].(map[string]interface{}) + awayId, _ := awayRaw["id"].(string) + + event := domain.Event{ + ID: getString(ev["id"]), + SportID: fmt.Sprintf("%d", sportID), + TimerStatus: getString(ev["time_status"]), + HomeTeamID: homeId, + AwayTeamID: awayId, + StartTime: time.Now().UTC().Format(time.RFC3339), + IsLive: true, + Status: "live", + Source: "betfair", + } + + events = append(events, event) + } + + return events } func (s *service) FetchUpcomingEvents(ctx context.Context) error {