adding upcoming

This commit is contained in:
OneTap Technologies 2025-04-12 14:43:09 +03:00
parent ed0d107f1a
commit 1726cfd63b
6 changed files with 210 additions and 78 deletions

View File

@ -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;

View File

@ -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
`

View File

@ -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
}

View File

@ -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)

View File

@ -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
}

View File

@ -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)
}
},
},
{