volleyball support

This commit is contained in:
Asher Samuel 2025-05-15 01:50:59 +03:00
parent 7e1a126ead
commit 0ae3154334
5 changed files with 381 additions and 62 deletions

View File

@ -210,6 +210,45 @@ type CricketResultResponse struct {
Bet365ID string `json:"bet365_id"`
}
type VolleyballResultResponse 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"`
Scores struct {
FirstSet Score `json:"1"`
SecondSet Score `json:"2"`
ThirdSet Score `json:"3"`
FourthSet Score `json:"4"`
FivethSet Score `json:"5"`
} `json:"scores"`
Stats struct {
PointsWonOnServe []string `json:"points_won_on_serve"`
LongestStreak []string `json:"longest_streak"`
} `json:"stats"`
InplayCreatedAt string `json:"inplay_created_at"`
InplayUpdatedAt string `json:"inplay_updated_at"`
Bet365ID string `json:"bet365_id"`
}
type Score struct {
Home string `json:"home"`
Away string `json:"away"`

View File

@ -114,6 +114,17 @@ const (
CRICKET_TOP_MATCH_BOWLER CricketMarket = 30246
)
type VolleyballMarket int64
const (
VOLLEYBALL_GAME_LINES VolleyballMarket = 910000
VOLLEYBALL_CORRECT_SET_SCORE VolleyballMarket = 910201
VOLLEYBALL_MATCH_TOTAL_ODD_EVEN VolleyballMarket = 910217
VOLLEYBALL_SET_ONE_LINES VolleyballMarket = 910204
VOLLEYBALL_SET_ONE_TO_GO_TO_EXTRA_POINTS VolleyballMarket = 910209
VOLLEYBALL_SET_ONE_TOTAL_ODD_EVEN VolleyballMarket = 910218
)
// TODO: Move this into the database so that it can be modified dynamically
var SupportedMarkets = map[int64]bool{
@ -189,16 +200,26 @@ var SupportedMarkets = map[int64]bool{
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_TO_WIN_THE_MATCH): 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,
int64(CRICKET_FIRST_OVER_TOTAL_RUNS_Odd_Even): false,
int64(CRICKET_FIRST_INNINIGS_SCORE): false,
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,
// Volleyball Markets
int64(VOLLEYBALL_GAME_LINES): false,
int64(VOLLEYBALL_CORRECT_SET_SCORE): true,
int64(VOLLEYBALL_MATCH_TOTAL_ODD_EVEN): true,
int64(VOLLEYBALL_SET_ONE_LINES): false,
int64(VOLLEYBALL_SET_ONE_TO_GO_TO_EXTRA_POINTS): false,
int64(VOLLEYBALL_SET_ONE_TOTAL_ODD_EVEN): false,
}

View File

@ -708,47 +708,3 @@ 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

@ -226,6 +226,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.VOLLEYBALL:
result, err = s.parseVolleyball(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)
@ -271,9 +277,6 @@ 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
@ -355,6 +358,34 @@ func (s *Service) parseCricket(response json.RawMessage, eventID, oddID, marketI
}, nil
}
func (s *Service) parseVolleyball(response json.RawMessage, eventID, oddID, marketID int64, outcome domain.BetOutcome) (domain.CreateResult, error) {
var volleyballRes domain.VolleyballResultResponse
if err := json.Unmarshal(response, &volleyballRes); err != nil {
s.logger.Error("Failed to unmarshal football result", "event_id", eventID, "error", err)
return domain.CreateResult{}, err
}
if volleyballRes.TimeStatus != "3" {
s.logger.Warn("Match not yet completed", "event_id", eventID)
return domain.CreateResult{}, fmt.Errorf("match not yet completed")
}
status, err := s.evaluateVolleyballOutcome(outcome, volleyballRes)
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))
@ -537,10 +568,26 @@ func (s *Service) evaluateCricketOutcome(outcome domain.BetOutcome, res domain.C
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
}
func (s *Service) evaluateVolleyballOutcome(outcome domain.BetOutcome, res domain.VolleyballResultResponse) (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 {
// TODO: new Game Lines for volleyball
case int64(domain.VOLLEYBALL_CORRECT_SET_SCORE):
return evaluateCorrectScore(outcome, score)
case int64(domain.VOLLEYBALL_MATCH_TOTAL_ODD_EVEN):
return evaluateGoalsOddEven(outcome, score)
}
return domain.OUTCOME_STATUS_PENDING, nil

256
q Normal file
View File

@ -0,0 +1,256 @@
diff --git a/internal/domain/result.go b/internal/domain/result.go
index e43476b..def8719 100644
--- a/internal/domain/result.go
+++ b/internal/domain/result.go
@@ -210,6 +210,45 @@ type CricketResultResponse struct {
Bet365ID string `json:"bet365_id"`
}

+type VolleyballResultResponse 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"`
+ Scores struct {
+ FirstSet Score `json:"1"`
+ SecondSet Score `json:"2"`
+ ThirdSet Score `json:"3"`
+ FourthSet Score `json:"4"`
+ FivethSet Score `json:"5"`
+ } `json:"scores"`
+ Stats struct {
+ PointsWonOnServe []string `json:"points_won_on_serve"`
+ LongestStreak []string `json:"longest_streak"`
+ } `json:"stats"`
+ InplayCreatedAt string `json:"inplay_created_at"`
+ InplayUpdatedAt string `json:"inplay_updated_at"`
+ Bet365ID string `json:"bet365_id"`
+}
+
type Score struct {
Home string `json:"home"`
Away string `json:"away"`
diff --git a/internal/domain/sportmarket.go b/internal/domain/sportmarket.go
index f442f9c..fc25e36 100644
--- a/internal/domain/sportmarket.go
+++ b/internal/domain/sportmarket.go
@@ -114,6 +114,17 @@ const (
CRICKET_TOP_MATCH_BOWLER CricketMarket = 30246
)

+type VolleyballMarket int64
+
+const (
+ VOLLEYBALL_GAME_LINES VolleyballMarket = 910000
+ VOLLEYBALL_CORRECT_SET_SCORE VolleyballMarket = 910201
+ VOLLEYBALL_MATCH_TOTAL_ODD_EVEN VolleyballMarket = 910217
+ VOLLEYBALL_SET_ONE_LINES VolleyballMarket = 910204
+ VOLLEYBALL_SET_ONE_TO_GO_TO_EXTRA_POINTS VolleyballMarket = 910209
+ VOLLEYBALL_SET_ONE_TOTAL_ODD_EVEN VolleyballMarket = 910218
+)
+
// TODO: Move this into the database so that it can be modified dynamically

var SupportedMarkets = map[int64]bool{
@@ -189,16 +200,26 @@ var SupportedMarkets = map[int64]bool{
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,
+ int64(CRICKET_TO_WIN_THE_MATCH): true,
+
+ int64(CRICKET_FIRST_OVER_TOTAL_RUNS_Odd_Even): false,
+ int64(CRICKET_FIRST_INNINIGS_SCORE): false,
+ 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,
+
+ // Volleyball Markets
+ int64(VOLLEYBALL_GAME_LINES): false,
+
+ int64(VOLLEYBALL_CORRECT_SET_SCORE): true,
+ int64(VOLLEYBALL_MATCH_TOTAL_ODD_EVEN): true,
+
+ int64(VOLLEYBALL_SET_ONE_LINES): false,
+ int64(VOLLEYBALL_SET_ONE_TO_GO_TO_EXTRA_POINTS): false,
+ int64(VOLLEYBALL_SET_ONE_TOTAL_ODD_EVEN): false,
}
diff --git a/internal/services/result/eval.go b/internal/services/result/eval.go
index 8c21390..5d7cd66 100644
--- a/internal/services/result/eval.go
+++ b/internal/services/result/eval.go
@@ -708,47 +708,3 @@ 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)
- }
-}
diff --git a/internal/services/result/service.go b/internal/services/result/service.go
index fcbff6b..92886c3 100644
--- a/internal/services/result/service.go
+++ b/internal/services/result/service.go
@@ -226,6 +226,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.VOLLEYBALL:
+ result, err = s.parseVolleyball(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)
@@ -271,9 +277,6 @@ 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
@@ -355,6 +358,34 @@ func (s *Service) parseCricket(response json.RawMessage, eventID, oddID, marketI
}, nil
}

+func (s *Service) parseVolleyball(response json.RawMessage, eventID, oddID, marketID int64, outcome domain.BetOutcome) (domain.CreateResult, error) {
+ var volleyballRes domain.VolleyballResultResponse
+
+ if err := json.Unmarshal(response, &volleyballRes); err != nil {
+ s.logger.Error("Failed to unmarshal football result", "event_id", eventID, "error", err)
+ return domain.CreateResult{}, err
+ }
+ if volleyballRes.TimeStatus != "3" {
+ s.logger.Warn("Match not yet completed", "event_id", eventID)
+ return domain.CreateResult{}, fmt.Errorf("match not yet completed")
+ }
+
+ status, err := s.evaluateVolleyballOutcome(outcome, volleyballRes)
+ 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))
@@ -537,10 +568,27 @@ func (s *Service) evaluateCricketOutcome(outcome domain.BetOutcome, res domain.C
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 domain.OUTCOME_STATUS_PENDING, nil
+}
+
+func (s *Service) evaluateVolleyballOutcome(outcome domain.BetOutcome, res domain.VolleyballResultResponse) (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 {
+ // new Game line since its different from basket ball
+ case int64(domain.VOLLEYBALL_GAME_LINES):
+ return evaluateGameLines(outcome, score)
+ case int64(domain.VOLLEYBALL_CORRECT_SET_SCORE):
+ return evaluateCorrectScore(outcome, score)
+ case int64(domain.VOLLEYBALL_MATCH_TOTAL_ODD_EVEN):
return evaluateGoalsOddEven(outcome, score)
- case int64(domain.CRICKET_FIRST_INNINIGS_SCORE):
- return evaluateInningScore(outcome, score)
}

return domain.OUTCOME_STATUS_PENDING, nil