cricket support

This commit is contained in:
Asher Samuel 2025-05-14 00:52:05 +03:00
parent 252bf04b1e
commit 7e1a126ead
5 changed files with 179 additions and 1 deletions

View File

@ -175,6 +175,41 @@ type IceHockeyResultResponse struct {
Bet365ID string `json:"bet365_id"`
}
type CricketResultResponse struct {
ID string `json:"id"`
SportID string `json:"sport_id"`
Time string `json:"time"`
TimeStatus string `json:"time_status"`
League struct {
ID string `json:"id"`
Name string `json:"name"`
CC string `json:"cc"`
} `json:"league"`
Home struct {
ID string `json:"id"`
Name string `json:"name"`
ImageID string `json:"image_id"`
CC string `json:"cc"`
} `json:"home"`
Away struct {
ID string `json:"id"`
Name string `json:"name"`
ImageID string `json:"image_id"`
CC string `json:"cc"`
} `json:"away"`
SS string `json:"ss"`
Extra struct {
HomePos string `json:"home_pos"`
AwayPos string `json:"away_pos"`
NumberOfPeriods string `json:"numberofperiods"`
PeriodLength string `json:"periodlength"`
StadiumData map[string]string `json:"stadium_data"`
} `json:"extra"`
HasLineup int `json:"has_lineup"`
ConfirmedAt string `json:"confirmed_at"`
Bet365ID string `json:"bet365_id"`
}
type Score struct {
Home string `json:"home"`
Away string `json:"away"`

View File

@ -91,6 +91,29 @@ const (
ICE_HOCKEY_ALTERNATIVE_TOTAL_TWO_WAY IceHockeyMarket = 170240
)
type CricketMarket int64
const (
// Main
CRICKET_TO_WIN_THE_MATCH CricketMarket = 1246
CRICKET_TEAM_TOP_BATTER CricketMarket = 1241
CRICKET_TEAM_TOP_BOWLE CricketMarket = 1242
CRICKET_PLAYER_OF_THE_MATCH CricketMarket = 346
CRICKET_FIRST_WICKET_METHOD CricketMarket = 30205
// First Over
CRICKET_FIRST_OVER_TOTAL_RUNS CricketMarket = 300336
CRICKET_FIRST_OVER_TOTAL_RUNS_Odd_Even CricketMarket = 300118
// Inninigs 1
CRICKET_FIRST_INNINIGS_SCORE CricketMarket = 300338
CRICKET_INNINGS_OF_MATCH_BOWLED_OUT CricketMarket = 300108
// Match
CRICKET_TOP_MATCH_BATTER CricketMarket = 30245
CRICKET_TOP_MATCH_BOWLER CricketMarket = 30246
)
// TODO: Move this into the database so that it can be modified dynamically
var SupportedMarkets = map[int64]bool{
@ -164,4 +187,18 @@ var SupportedMarkets = map[int64]bool{
int64(ICE_HOCKEY_ALTERNATIVE_PUCK_LINE_TWO_WAY): false,
int64(ICE_HOCKEY_ALTERNATIVE_TOTAL_TWO_WAY): false,
// Cricket Markets
int64(CRICKET_TO_WIN_THE_MATCH): true,
int64(CRICKET_FIRST_OVER_TOTAL_RUNS_Odd_Even): true,
int64(CRICKET_FIRST_INNINIGS_SCORE): true,
int64(CRICKET_INNINGS_OF_MATCH_BOWLED_OUT): false,
int64(CRICKET_FIRST_OVER_TOTAL_RUNS): false,
int64(CRICKET_TEAM_TOP_BATTER): false,
int64(CRICKET_TEAM_TOP_BOWLE): false,
int64(CRICKET_PLAYER_OF_THE_MATCH): false,
int64(CRICKET_FIRST_WICKET_METHOD): false,
int64(CRICKET_TOP_MATCH_BATTER): false,
int64(CRICKET_TOP_MATCH_BOWLER): false,
}

View File

@ -709,3 +709,46 @@ func evaluateTiedAfterRegulation(outcome domain.BetOutcome, scores []struct{ Hom
return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("invalid oddname: %s", outcome.OddName)
}
// Cricket evalations
func evaluateInningScore(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) {
var inningsScore int
if outcome.OddName == "1" {
inningsScore = score.Home
} else if outcome.OddName == "2" {
inningsScore = score.Away
} else {
return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("invalid odd header: %s", outcome.OddHeader)
}
parts := strings.Fields(outcome.OddHeader)
if len(parts) != 2 {
return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("invalid odd format: %s", outcome.OddHeader)
}
evalType := parts[0]
threshold, err := strconv.ParseFloat(parts[1], 64)
if err != nil {
return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("invalid threshold value: %s", parts[1])
}
switch evalType {
case "Over":
if float64(inningsScore) > threshold {
return domain.OUTCOME_STATUS_WIN, nil
}
return domain.OUTCOME_STATUS_LOSS, nil
case "Under":
if float64(inningsScore) < threshold {
return domain.OUTCOME_STATUS_WIN, nil
}
return domain.OUTCOME_STATUS_LOSS, nil
case "Exactly":
if float64(inningsScore) == threshold {
return domain.OUTCOME_STATUS_WIN, nil
}
return domain.OUTCOME_STATUS_LOSS, nil
default:
return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("invalid comparison operator: %s", evalType)
}
}

View File

@ -168,7 +168,8 @@ func (s *Service) FetchAndProcessResults(ctx context.Context) error {
// }
func (s *Service) fetchResult(ctx context.Context, eventID, oddID, marketID, sportID int64, outcome domain.BetOutcome) (domain.CreateResult, error) {
url := fmt.Sprintf("https://api.b365api.com/v1/bet365/result?token=%s&event_id=%d", s.config.Bet365Token, eventID)
// url := fmt.Sprintf("https://api.b365api.com/v1/bet365/result?token=%s&event_id=%d", s.config.Bet365Token, eventID)
url := fmt.Sprintf("https://api.b365api.com/v1/event/view?token=%s&event_id=%d", s.config.Bet365Token, eventID)
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
s.logger.Error("Failed to create request", "event_id", eventID, "error", err)
@ -219,6 +220,12 @@ func (s *Service) fetchResult(ctx context.Context, eventID, oddID, marketID, spo
s.logger.Error("Failed to parse ice hockey", "event id", eventID, "market_id", marketID, "error", err)
return domain.CreateResult{}, err
}
case domain.CRICKET:
result, err = s.parseCricket(resultResp.Results[0], eventID, oddID, marketID, outcome)
if err != nil {
s.logger.Error("Failed to parse cricket", "event id", eventID, "market_id", marketID, "error", err)
return domain.CreateResult{}, err
}
default:
s.logger.Error("Unsupported sport", "sport", sportID)
return domain.CreateResult{}, fmt.Errorf("unsupported sport: %v", sportID)
@ -263,6 +270,10 @@ func (s *Service) parseFootball(resultRes json.RawMessage, eventID, oddID, marke
func (s *Service) parseBasketball(response json.RawMessage, eventID, oddID, marketID int64, outcome domain.BetOutcome) (domain.CreateResult, error) {
var basketBallRes domain.BasketballResultResponse
// TODO: here !!
fmt.Println(string(response))
if err := json.Unmarshal(response, &basketBallRes); err != nil {
s.logger.Error("Failed to unmarshal football result", "event_id", eventID, "error", err)
return domain.CreateResult{}, err
@ -317,6 +328,33 @@ func (s *Service) parseIceHockey(response json.RawMessage, eventID, oddID, marke
}
func (s *Service) parseCricket(response json.RawMessage, eventID, oddID, marketID int64, outcome domain.BetOutcome) (domain.CreateResult, error) {
var cricketRes domain.CricketResultResponse
if err := json.Unmarshal(response, &cricketRes); err != nil {
s.logger.Error("Failed to unmarshal football result", "event_id", eventID, "error", err)
return domain.CreateResult{}, err
}
if cricketRes.TimeStatus != "3" {
s.logger.Warn("Match not yet completed", "event_id", eventID)
return domain.CreateResult{}, fmt.Errorf("match not yet completed")
}
status, err := s.evaluateCricketOutcome(outcome, cricketRes)
if err != nil {
s.logger.Error("Failed to evaluate outcome", "event_id", eventID, "market_id", marketID, "error", err)
return domain.CreateResult{}, err
}
return domain.CreateResult{
BetOutcomeID: 0,
EventID: eventID,
OddID: oddID,
MarketID: marketID,
Status: status,
}, nil
}
func parseScore(home string, away string) struct{ Home, Away int } {
homeVal, _ := strconv.Atoi(strings.TrimSpace(home))
awaVal, _ := strconv.Atoi(strings.TrimSpace(away))
@ -487,3 +525,23 @@ func (s *Service) evaluateIceHockeyOutcome(outcome domain.BetOutcome, res domain
return domain.OUTCOME_STATUS_PENDING, nil
}
func (s *Service) evaluateCricketOutcome(outcome domain.BetOutcome, res domain.CricketResultResponse) (domain.OutcomeStatus, error) {
if !domain.SupportedMarkets[outcome.MarketID] {
s.logger.Warn("Unsupported market type", "market_name", outcome.MarketName)
return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("unsupported market type: %s", outcome.MarketName)
}
score := parseSS(res.SS)
switch outcome.MarketID {
case int64(domain.CRICKET_TO_WIN_THE_MATCH):
return evaluateFullTimeResult(outcome, score)
case int64(domain.CRICKET_FIRST_OVER_TOTAL_RUNS_Odd_Even):
return evaluateGoalsOddEven(outcome, score)
case int64(domain.CRICKET_FIRST_INNINIGS_SCORE):
return evaluateInningScore(outcome, score)
}
return domain.OUTCOME_STATUS_PENDING, nil
}

View File

@ -27,6 +27,11 @@ migrations/up:
@echo 'Running up migrations...'
@migrate -path ./db/migrations -database $(DB_URL) up
.PHONY: postgres
postgres:
@echo 'Running postgres db...'
docker compose -f compose.db.yaml exec postgres psql -U root -d gh
.PHONY: swagger
swagger:
@swag init -g cmd/main.go