darts support

This commit is contained in:
Asher Samuel 2025-05-17 19:31:44 +03:00
parent e0aaf536a0
commit ecaad893ba
7 changed files with 140 additions and 1 deletions

2
go.mod
View File

@ -18,6 +18,8 @@ require (
golang.org/x/crypto v0.36.0
)
require github.com/gorilla/websocket v1.5.3 // indirect
require (
github.com/KyleBanks/depth v1.2.1 // indirect
github.com/andybalholm/brotli v1.1.1 // indirect

2
go.sum
View File

@ -63,6 +63,8 @@ github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVI
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=

View File

@ -249,6 +249,35 @@ type VolleyballResultResponse struct {
Bet365ID string `json:"bet365_id"`
}
type DartsResultResponse 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"`
InplayCreatedAt string `json:"inplay_created_at"`
InplayUpdatedAt string `json:"inplay_updated_at"`
ConfirmedAt string `json:"confirmed_at"`
Bet365ID string `json:"bet365_id"`
}
type Score struct {
Home string `json:"home"`
Away string `json:"away"`

View File

@ -125,6 +125,23 @@ const (
VOLLEYBALL_SET_ONE_TOTAL_ODD_EVEN VolleyballMarket = 910218
)
type DartsMarket int64
const (
// Main
DARTS_MATCH_WINNER DartsMarket = 703 // match_winner
DARTS_MATCH_DOUBLE DartsMarket = 150228 // match_double
DARTS_MATCH_TREBLE DartsMarket = 150230 // match_treble
DARTS_CORRECT_LEG_SCORE DartsMarket = 150015 // correct_leg_score
DARTS_TOTAL_LEGS DartsMarket = 150117 // total_legs
DARTS_MOST_HUNDERED_EIGHTYS DartsMarket = 150030 // "most_180s"
DARTS_TOTAL_HUNDERED_EIGHTYS DartsMarket = 150012 // total_180s
DARTS_MOST_HUNDERED_EIGHTYS_HANDICAP DartsMarket = 150227 // most_180s_handicap
DARTS_PLAYER_HUNDERED_EIGHTYS DartsMarket = 150121 // player_180s
DARTS_FIRST_DART DartsMarket = 150125 // first_dart
)
// TODO: Move this into the database so that it can be modified dynamically
var SupportedMarkets = map[int64]bool{
@ -221,4 +238,17 @@ var SupportedMarkets = map[int64]bool{
int64(VOLLEYBALL_SET_ONE_LINES): false,
int64(VOLLEYBALL_SET_ONE_TO_GO_TO_EXTRA_POINTS): false,
int64(VOLLEYBALL_SET_ONE_TOTAL_ODD_EVEN): false,
// Darts Markets
int64(DARTS_MATCH_WINNER): true,
int64(DARTS_TOTAL_LEGS): true,
int64(DARTS_CORRECT_LEG_SCORE): false,
int64(DARTS_MATCH_DOUBLE): false,
int64(DARTS_MATCH_TREBLE): false,
int64(DARTS_MOST_HUNDERED_EIGHTYS): false,
int64(DARTS_TOTAL_HUNDERED_EIGHTYS): false,
int64(DARTS_MOST_HUNDERED_EIGHTYS_HANDICAP): false,
int64(DARTS_PLAYER_HUNDERED_EIGHTYS): false,
int64(DARTS_FIRST_DART): false,
}

View File

@ -297,6 +297,30 @@ func evaluateTotalOverUnder(outcome domain.BetOutcome, score struct{ Home, Away
return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("invalid odd header: %s", outcome.OddHeader)
}
func evaluateTotalLegs(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) {
total, err := strconv.ParseFloat(outcome.OddName, 64)
if err != nil {
return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("invalid : %s", outcome.OddName)
}
totalLegs := float64(score.Home + score.Away)
switch outcome.OddHeader {
case "Over":
if totalLegs > total {
return domain.OUTCOME_STATUS_WIN, nil
}
return domain.OUTCOME_STATUS_LOSS, nil
case "Under":
if totalLegs < total {
return domain.OUTCOME_STATUS_WIN, nil
}
return domain.OUTCOME_STATUS_LOSS, nil
}
return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("invalid odd header: %s", outcome.OddHeader)
}
func evaluateResultAndTotal(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) {
// The handicap will be in the format "U {float}" or "O {float}"

View File

@ -233,6 +233,12 @@ func (s *Service) FetchResult(ctx context.Context, eventID, oddID, marketID, spo
s.logger.Error("Failed to parse cricket", "event id", eventID, "market_id", marketID, "error", err)
return domain.CreateResult{}, err
}
case domain.DARTS:
result, err = s.parseDarts(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)
@ -387,6 +393,34 @@ func (s *Service) parseVolleyball(response json.RawMessage, eventID, oddID, mark
}
func (s *Service) parseDarts(response json.RawMessage, eventID, oddID, marketID int64, outcome domain.BetOutcome) (domain.CreateResult, error) {
var dartsRes domain.DartsResultResponse
if err := json.Unmarshal(response, &dartsRes); err != nil {
}
if dartsRes.TimeStatus != "3" {
s.logger.Warn("Match not yet completed", "event_id", eventID)
return domain.CreateResult{}, fmt.Errorf("match not yet completed")
}
// result for dart is limited
// only ss is given, format with 2-4
status, err := s.evaluateDartsOutcome(outcome, dartsRes)
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))
@ -605,3 +639,21 @@ func (s *Service) evaluateVolleyballOutcome(outcome domain.BetOutcome, res domai
return domain.OUTCOME_STATUS_PENDING, nil
}
func (s *Service) evaluateDartsOutcome(outcome domain.BetOutcome, res domain.DartsResultResponse) (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.DARTS_MATCH_WINNER):
return evaluateFullTimeResult(outcome, score)
case int64(domain.DARTS_TOTAL_LEGS):
return evaluateTotalLegs(outcome, score)
}
return domain.OUTCOME_STATUS_PENDING, nil
}

View File

@ -43,7 +43,7 @@ migrations/up:
.PHONY: postgres
postgres:
@echo 'Running postgres db...'
docker compose -f compose.db.yaml exec postgres psql -U root -d gh
docker compose -f docker-compose.yml exec postgres psql -U root -d gh
.PHONY: swagger
swagger: