From 1726cfd63bb23e4b0d06d1857fc86685d5aa31e6 Mon Sep 17 00:00:00 2001 From: OneTap Technologies Date: Sat, 12 Apr 2025 14:43:09 +0300 Subject: [PATCH] adding upcoming --- db/query/events.sql | 29 ++++++++ gen/db/events.sql.go | 65 ++++++++++++++++++ internal/domain/event.go | 58 ++++++++++------ internal/repository/event.go | 17 +++++ internal/services/event/service.go | 104 +++++++++++++++-------------- internal/web_server/cron.go | 15 +++-- 6 files changed, 210 insertions(+), 78 deletions(-) diff --git a/db/query/events.sql b/db/query/events.sql index dddec63..8c96d9e 100644 --- a/db/query/events.sql +++ b/db/query/events.sql @@ -33,6 +33,35 @@ ON CONFLICT (id) DO UPDATE SET is_live = EXCLUDED.is_live, status = EXCLUDED.status, fetched_at = now(); +-- name: InsertUpcomingEvent :exec +INSERT INTO events ( + id, sport_id, match_name, home_team, away_team, + home_team_id, away_team_id, home_kit_image, away_kit_image, + league_id, league_name, league_cc, start_time, + is_live, status +) VALUES ( + $1, $2, $3, $4, $5, + $6, $7, $8, $9, + $10, $11, $12, $13, + false, 'upcoming' +) +ON CONFLICT (id) DO UPDATE SET + sport_id = EXCLUDED.sport_id, + match_name = EXCLUDED.match_name, + home_team = EXCLUDED.home_team, + away_team = EXCLUDED.away_team, + home_team_id = EXCLUDED.home_team_id, + away_team_id = EXCLUDED.away_team_id, + home_kit_image = EXCLUDED.home_kit_image, + away_kit_image = EXCLUDED.away_kit_image, + league_id = EXCLUDED.league_id, + league_name = EXCLUDED.league_name, + league_cc = EXCLUDED.league_cc, + start_time = EXCLUDED.start_time, + is_live = false, + status = 'upcoming', + fetched_at = now(); + -- name: ListLiveEvents :many SELECT id FROM events WHERE is_live = true; \ No newline at end of file diff --git a/gen/db/events.sql.go b/gen/db/events.sql.go index e833d0e..8f1b423 100644 --- a/gen/db/events.sql.go +++ b/gen/db/events.sql.go @@ -97,6 +97,71 @@ func (q *Queries) InsertEvent(ctx context.Context, arg InsertEventParams) error return err } +const InsertUpcomingEvent = `-- name: InsertUpcomingEvent :exec +INSERT INTO events ( + id, sport_id, match_name, home_team, away_team, + home_team_id, away_team_id, home_kit_image, away_kit_image, + league_id, league_name, league_cc, start_time, + is_live, status +) VALUES ( + $1, $2, $3, $4, $5, + $6, $7, $8, $9, + $10, $11, $12, $13, + false, 'upcoming' +) +ON CONFLICT (id) DO UPDATE SET + sport_id = EXCLUDED.sport_id, + match_name = EXCLUDED.match_name, + home_team = EXCLUDED.home_team, + away_team = EXCLUDED.away_team, + home_team_id = EXCLUDED.home_team_id, + away_team_id = EXCLUDED.away_team_id, + home_kit_image = EXCLUDED.home_kit_image, + away_kit_image = EXCLUDED.away_kit_image, + league_id = EXCLUDED.league_id, + league_name = EXCLUDED.league_name, + league_cc = EXCLUDED.league_cc, + start_time = EXCLUDED.start_time, + is_live = false, + status = 'upcoming', + fetched_at = now() +` + +type InsertUpcomingEventParams struct { + ID string + SportID pgtype.Text + MatchName pgtype.Text + HomeTeam pgtype.Text + AwayTeam pgtype.Text + HomeTeamID pgtype.Text + AwayTeamID pgtype.Text + HomeKitImage pgtype.Text + AwayKitImage pgtype.Text + LeagueID pgtype.Text + LeagueName pgtype.Text + LeagueCc pgtype.Text + StartTime pgtype.Timestamp +} + +func (q *Queries) InsertUpcomingEvent(ctx context.Context, arg InsertUpcomingEventParams) error { + _, err := q.db.Exec(ctx, InsertUpcomingEvent, + arg.ID, + arg.SportID, + arg.MatchName, + arg.HomeTeam, + arg.AwayTeam, + arg.HomeTeamID, + arg.AwayTeamID, + arg.HomeKitImage, + arg.AwayKitImage, + arg.LeagueID, + arg.LeagueName, + arg.LeagueCc, + arg.StartTime, + ) + return err +} + const ListLiveEvents = `-- name: ListLiveEvents :many SELECT id FROM events WHERE is_live = true ` diff --git a/internal/domain/event.go b/internal/domain/event.go index fb7edf4..0a69607 100644 --- a/internal/domain/event.go +++ b/internal/domain/event.go @@ -1,23 +1,41 @@ package domain + +import "time" + type Event struct { - ID string - SportID string - MatchName string - HomeTeam string - AwayTeam string - HomeTeamID string - AwayTeamID string - HomeKitImage string - AwayKitImage string - LeagueID string - LeagueName string - LeagueCC string - StartTime string - Score string - MatchMinute int - TimerStatus string - AddedTime int - MatchPeriod int - IsLive bool - Status string + ID string + SportID string + MatchName string + HomeTeam string + AwayTeam string + HomeTeamID string + AwayTeamID string + HomeKitImage string + AwayKitImage string + LeagueID string + LeagueName string + LeagueCC string + StartTime string + Score string + MatchMinute int + TimerStatus string + AddedTime int + MatchPeriod int + IsLive bool + Status string +} +type UpcomingEvent struct { + ID string // Event ID + SportID string // Sport ID + MatchName string // Match or event name + HomeTeam string // Home team name (if available) + AwayTeam string // Away team name (can be empty/null) + HomeTeamID string // Home team ID + AwayTeamID string // Away team ID (can be empty/null) + HomeKitImage string // Kit or image for home team (optional) + AwayKitImage string // Kit or image for away team (optional) + LeagueID string // League ID + LeagueName string // League name + LeagueCC string // League country code + StartTime time.Time // Converted from "time" field in UNIX format } \ No newline at end of file diff --git a/internal/repository/event.go b/internal/repository/event.go index 1949a44..282b89c 100644 --- a/internal/repository/event.go +++ b/internal/repository/event.go @@ -38,6 +38,23 @@ func (s *Store) SaveEvent(ctx context.Context, e domain.Event) error { Status: pgtype.Text{String: e.Status, Valid: true}, }) } +func (s *Store) SaveUpcomingEvent(ctx context.Context, e domain.UpcomingEvent) error { + return s.queries.InsertUpcomingEvent(ctx, dbgen.InsertUpcomingEventParams{ + ID: e.ID, + SportID: pgtype.Text{String: e.SportID, Valid: true}, + MatchName: pgtype.Text{String: e.MatchName, Valid: true}, + HomeTeam: pgtype.Text{String: e.HomeTeam, Valid: true}, + AwayTeam: pgtype.Text{String: e.AwayTeam, Valid: true}, + HomeTeamID: pgtype.Text{String: e.HomeTeamID, Valid: true}, + AwayTeamID: pgtype.Text{String: e.AwayTeamID, Valid: true}, + HomeKitImage: pgtype.Text{String: e.HomeKitImage, Valid: true}, + AwayKitImage: pgtype.Text{String: e.AwayKitImage, Valid: true}, + LeagueID: pgtype.Text{String: e.LeagueID, Valid: true}, + LeagueName: pgtype.Text{String: e.LeagueName, Valid: true}, + LeagueCc: pgtype.Text{String: e.LeagueCC, Valid: true}, + StartTime: pgtype.Timestamp{Time: e.StartTime, Valid: true}, + }) +} func (s *Store) GetLiveEventIDs(ctx context.Context) ([]string, error) { return s.queries.ListLiveEvents(ctx) diff --git a/internal/services/event/service.go b/internal/services/event/service.go index 24207ca..f047cd2 100644 --- a/internal/services/event/service.go +++ b/internal/services/event/service.go @@ -6,6 +6,7 @@ import ( "fmt" "io" "net/http" + "strconv" "sync" "time" @@ -96,65 +97,66 @@ func (s *service) FetchLiveEvents(ctx context.Context) error { func (s *service) FetchUpcomingEvents(ctx context.Context) 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 - for _, sportID := range sportIDs { - wg.Add(1) - go func(sportID int) { - defer wg.Done() + url := fmt.Sprintf("https://api.b365api.com/v1/bet365/upcoming?sport_id=%d&token=%s", sportID, s.token) + resp, err := http.Get(url) + if err != nil { + continue + } + defer resp.Body.Close() - url := fmt.Sprintf("https://api.b365api.com/v1/bet365/upcoming?sport_id=%d&token=%s", sportID, s.token) - resp, err := http.Get(url) - if err != nil { - fmt.Printf(" Failed request for upcoming sport_id=%d: %v\n", sportID, err) - return - } - defer resp.Body.Close() + body, _ := io.ReadAll(resp.Body) + var data struct { + Success int `json:"success"` + Results []struct { + ID string `json:"id"` + SportID string `json:"sport_id"` + Time string `json:"time"` + League struct { + ID string `json:"id"` + Name string `json:"name"` + } `json:"league"` + Home struct { + ID string `json:"id"` + Name string `json:"name"` + } `json:"home"` + Away *struct { + ID string `json:"id"` + Name string `json:"name"` + } `json:"away"` + } `json:"results"` + } + if err := json.Unmarshal(body, &data); err != nil || data.Success != 1 { + continue + } - 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 upcoming sport_id=%d\nRaw: %s\n", sportID, string(body)) - return + for _, ev := range data.Results { + startUnix, _ := strconv.ParseInt(ev.Time, 10, 64) + event := domain.UpcomingEvent{ + ID: ev.ID, + SportID: ev.SportID, + MatchName: ev.Home.Name, + HomeTeam: ev.Home.Name, + AwayTeam: "", // handle nil safely + HomeTeamID: ev.Home.ID, + AwayTeamID: "", + HomeKitImage: "", + AwayKitImage: "", + LeagueID: ev.League.ID, + LeagueName: ev.League.Name, + LeagueCC: "", + StartTime: time.Unix(startUnix, 0).UTC(), } - 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"]), - HomeTeamID: getString(ev["HT"]), - AwayTeamID: getString(ev["AT"]), - HomeKitImage: getString(ev["K1"]), - AwayKitImage: getString(ev["K2"]), - LeagueID: getString(ev["C2"]), - LeagueName: getString(ev["CT"]), - LeagueCC: getString(ev["CB"]), - StartTime: time.Now().UTC().Format(time.RFC3339), - IsLive: false, - Status: "upcoming", - } - - if err := s.store.SaveEvent(ctx, event); err != nil { - fmt.Printf(" Could not store upcoming event [id=%s]: %v\n", event.ID, err) - } - } + if ev.Away != nil { + event.AwayTeam = ev.Away.Name + event.AwayTeamID = ev.Away.ID } - }(sportID) + + _ = s.store.SaveUpcomingEvent(ctx, event) + } } - wg.Wait() - fmt.Println(" All upcoming events fetched and stored.") return nil } diff --git a/internal/web_server/cron.go b/internal/web_server/cron.go index 6bae5b1..dad7d61 100644 --- a/internal/web_server/cron.go +++ b/internal/web_server/cron.go @@ -18,13 +18,14 @@ func StartDataFetchingCrons(eventService eventsvc.Service, oddsService oddssvc.S }{ { - spec: "0 0 * * * *", // Every hour - task: func() { - if err := eventService.FetchUpcomingEvents(context.Background()); err != nil { - log.Printf("FetchUpcomingEvents error: %v", err) - } - }, - }, + spec: "*/5 * * * * *", // Every 5 seconds + task: func() { + if err := eventService.FetchUpcomingEvents(context.Background()); err != nil { + log.Printf("FetchUpcomingEvents error: %v", err) + } + }, + }, + {