476 lines
10 KiB
Go
476 lines
10 KiB
Go
|
|
package onexbet
|
|||
|
|
|
|||
|
|
import (
|
|||
|
|
"encoding/json"
|
|||
|
|
"fmt"
|
|||
|
|
"strconv"
|
|||
|
|
"strings"
|
|||
|
|
|
|||
|
|
webspider "gordenko.dev/dima/spider"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
// ListLiveMatchesBySport - букмекер возвращает много мусора, типа киберспорта, поэтому метод
|
|||
|
|
// champFilter фильтрует разный мусор
|
|||
|
|
func ListLiveMatchesBySport(sportID OnexbetSport) (list []LiveMatch, err error) {
|
|||
|
|
url := fmt.Sprintf(liveSportURLPattern, sportID)
|
|||
|
|
|
|||
|
|
spider, err := webspider.NewSpider(nil)
|
|||
|
|
if err != nil {
|
|||
|
|
panic(err)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
buf, err := spider.Get(url, nil)
|
|||
|
|
if err != nil {
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var rawList liveList
|
|||
|
|
|
|||
|
|
err = json.Unmarshal(buf, &rawList)
|
|||
|
|
if err != nil {
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var champFilter func(string) bool
|
|||
|
|
|
|||
|
|
switch sportID {
|
|||
|
|
case Football:
|
|||
|
|
champFilter = FootballChampFilter
|
|||
|
|
|
|||
|
|
case Handball:
|
|||
|
|
champFilter = HandballChampFilter
|
|||
|
|
|
|||
|
|
case Tennis:
|
|||
|
|
champFilter = TennisChampFilter
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
for _, m := range rawList.Value {
|
|||
|
|
m.ChampName = strings.TrimSpace(m.ChampName)
|
|||
|
|
|
|||
|
|
if champFilter != nil {
|
|||
|
|
if !champFilter(m.ChampName) {
|
|||
|
|
continue
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
scoreByPeriods := make(map[int]Score)
|
|||
|
|
|
|||
|
|
stat := m.Stat
|
|||
|
|
|
|||
|
|
for _, p := range stat.ScoreByPeriods {
|
|||
|
|
scoreByPeriods[p.Period] = Score{
|
|||
|
|
Home: p.Score.HomeScore,
|
|||
|
|
Away: p.Score.AwayScore,
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
list = append(list, LiveMatch{
|
|||
|
|
MatchID: fmt.Sprintf("%d", m.MatchID),
|
|||
|
|
Home: m.Home,
|
|||
|
|
Away: m.Away,
|
|||
|
|
StartTime: m.StartTime,
|
|||
|
|
ChampName: m.ChampName,
|
|||
|
|
SportName: m.SportName,
|
|||
|
|
StatsMatchID: m.StatsMatchID,
|
|||
|
|
CurrentPeriod: stat.CurrentPeriod,
|
|||
|
|
Score: Score{
|
|||
|
|
Home: stat.Score.HomeScore,
|
|||
|
|
Away: stat.Score.AwayScore,
|
|||
|
|
},
|
|||
|
|
ScoreByPeriods: scoreByPeriods,
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func LoadTeamLiveMatchData(matchID string) (_ TeamLiveMatchData, isMatchFinished bool, err error) {
|
|||
|
|
url := fmt.Sprintf(liveMatchURLPattern, matchID)
|
|||
|
|
|
|||
|
|
spider, err := webspider.NewSpider(nil)
|
|||
|
|
if err != nil {
|
|||
|
|
panic(err)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
buf, err := spider.Get(url, nil)
|
|||
|
|
if err != nil {
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
//fmt.Printf("buf: %s\n\n", buf)
|
|||
|
|
|
|||
|
|
var env teamLiveMatchDataEnvelope
|
|||
|
|
|
|||
|
|
err = json.Unmarshal(buf, &env)
|
|||
|
|
if err != nil {
|
|||
|
|
err = fmt.Errorf("json.Unmarshal liveMatchDataEnvelope: %s", err)
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if !env.Success {
|
|||
|
|
isMatchFinished = true
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
data := TeamLiveMatchData{
|
|||
|
|
MatchID: matchID,
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// FULL TIME
|
|||
|
|
for _, market := range env.Value.GE {
|
|||
|
|
switch market.MarketType {
|
|||
|
|
case teamMarketWinner3Way:
|
|||
|
|
if len(market.OffersList) != 3 {
|
|||
|
|
// ВАЖНО!
|
|||
|
|
// Встречается такая херня, что принимают ставки только на 1 или 2 исхода.
|
|||
|
|
fmt.Printf("1x2 market must have 3 arrays of offers (1, x, 2), not %d", len(market.OffersList))
|
|||
|
|
break
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// [
|
|||
|
|
// {
|
|||
|
|
// "C": 1.48,
|
|||
|
|
// "G": 1,
|
|||
|
|
// "T": 1 // 1
|
|||
|
|
// }
|
|||
|
|
// ],
|
|||
|
|
// [
|
|||
|
|
// {
|
|||
|
|
// "C": 6.11,
|
|||
|
|
// "G": 1,
|
|||
|
|
// "T": 2 // DRAW
|
|||
|
|
// }
|
|||
|
|
// ],
|
|||
|
|
// [
|
|||
|
|
// {
|
|||
|
|
// "C": 4.38,
|
|||
|
|
// "G": 1,
|
|||
|
|
// "T": 3 // 2
|
|||
|
|
// }
|
|||
|
|
// ]
|
|||
|
|
|
|||
|
|
winner := &Winner3Way{}
|
|||
|
|
|
|||
|
|
homeList := market.OffersList[0]
|
|||
|
|
if len(homeList) == 1 {
|
|||
|
|
winner.Home = homeList[0].Price
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
drawList := market.OffersList[1]
|
|||
|
|
if len(drawList) == 1 {
|
|||
|
|
winner.Draw = drawList[0].Price
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
awayList := market.OffersList[2]
|
|||
|
|
if len(awayList) == 1 {
|
|||
|
|
winner.Away = awayList[0].Price
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
data.Winner = winner
|
|||
|
|
|
|||
|
|
case teamMarketTotal:
|
|||
|
|
data.Total, err = getTotal(market)
|
|||
|
|
if err != nil {
|
|||
|
|
err = fmt.Errorf("Full Time total football getTotal: %s", err)
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
case teamMarketIndividualTotal1:
|
|||
|
|
data.IndividualTotalHome, err = getTotal(market)
|
|||
|
|
if err != nil {
|
|||
|
|
err = fmt.Errorf("Full Time individual total 1 football getTotal: %s", err)
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
case teamMarketIndividualTotal2:
|
|||
|
|
data.IndividualTotalAway, err = getTotal(market)
|
|||
|
|
if err != nil {
|
|||
|
|
err = fmt.Errorf("Full Time individual total 2 football getTotal: %s", err)
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Статистика
|
|||
|
|
stats := env.Value.SC
|
|||
|
|
// Текущее время матча в секундах
|
|||
|
|
data.CurrentTime = stats.CurrentTime
|
|||
|
|
data.HomeGoals = stats.FullTimeScore.HomeScore
|
|||
|
|
data.AwayGoals = stats.FullTimeScore.AwayScore
|
|||
|
|
|
|||
|
|
for _, pair := range stats.Stats {
|
|||
|
|
switch pair.Key {
|
|||
|
|
case "Attacks1":
|
|||
|
|
data.HomeAttacks, _ = strconv.Atoi(pair.Value)
|
|||
|
|
|
|||
|
|
case "Attacks2":
|
|||
|
|
data.AwayAttacks, _ = strconv.Atoi(pair.Value)
|
|||
|
|
|
|||
|
|
case "DanAttacks1":
|
|||
|
|
data.HomeDangerousAttacks, _ = strconv.Atoi(pair.Value)
|
|||
|
|
|
|||
|
|
case "DanAttacks2":
|
|||
|
|
data.AwayDangerousAttacks, _ = strconv.Atoi(pair.Value)
|
|||
|
|
|
|||
|
|
case "ShotsOn1":
|
|||
|
|
data.HomeShotsOnTarget, _ = strconv.Atoi(pair.Value)
|
|||
|
|
|
|||
|
|
case "ShotsOn2":
|
|||
|
|
data.AwayShotsOnTarget, _ = strconv.Atoi(pair.Value)
|
|||
|
|
|
|||
|
|
case "ShotsOff1":
|
|||
|
|
data.HomeShotsOffTarget, _ = strconv.Atoi(pair.Value)
|
|||
|
|
|
|||
|
|
case "ShotsOff2":
|
|||
|
|
data.AwayShotsOffTarget, _ = strconv.Atoi(pair.Value)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// SubMatchID первого и второго тайма
|
|||
|
|
//type teamSubMatchIDs struct {
|
|||
|
|
// H1 string
|
|||
|
|
// H2 string
|
|||
|
|
//}
|
|||
|
|
|
|||
|
|
var h1MatchID string
|
|||
|
|
//var h2MatchID string
|
|||
|
|
|
|||
|
|
//var subMatchIDs teamSubMatchIDs
|
|||
|
|
|
|||
|
|
if len(env.Value.BIG) > 0 {
|
|||
|
|
//fmt.Printf("\n\nBIG: %v\n\n", env.Value.BIG)
|
|||
|
|
|
|||
|
|
// Парсим 1st HALF
|
|||
|
|
for _, item := range env.Value.BIG {
|
|||
|
|
period := strings.ToLower(strings.TrimSpace(item.PeriodName))
|
|||
|
|
|
|||
|
|
switch period {
|
|||
|
|
case "1 half":
|
|||
|
|
if item.SubCategory == "" {
|
|||
|
|
h1MatchID = strconv.Itoa(item.SubMatchID)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
//case "2 half":
|
|||
|
|
// if item.SubCategory == "" {
|
|||
|
|
// h2MatchID = strconv.Itoa(item.SubMatchID)
|
|||
|
|
// }
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
//fmt.Printf("\n\nSG: %v\n\n", env.Value.SG)
|
|||
|
|
|
|||
|
|
// Парсим 1st HALF
|
|||
|
|
for _, item := range env.Value.SG {
|
|||
|
|
period := strings.ToLower(strings.TrimSpace(item.PeriodName))
|
|||
|
|
|
|||
|
|
switch period {
|
|||
|
|
case "1 half":
|
|||
|
|
if item.SubCategory == "" {
|
|||
|
|
h1MatchID = strconv.Itoa(item.SubMatchID)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
//case "2 half":
|
|||
|
|
// if item.SubCategory == "" {
|
|||
|
|
// h2MatchID = strconv.Itoa(item.SubMatchID)
|
|||
|
|
// }
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
//fmt.Printf("\n\nsubMatchIDs: %v\n\n", subMatchIDs)
|
|||
|
|
|
|||
|
|
if h1MatchID != "" {
|
|||
|
|
url = fmt.Sprintf(liveMatchURLPattern, h1MatchID)
|
|||
|
|
//fmt.Printf("Load sub url: %s", url)
|
|||
|
|
|
|||
|
|
buf, err = spider.Get(url, nil)
|
|||
|
|
if err != nil {
|
|||
|
|
// Не загрузился 1й тайм - ну и хер с ним. Просто логгируем
|
|||
|
|
fmt.Printf("Load sub: %s", err)
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var env teamLiveMatchDataEnvelope
|
|||
|
|
|
|||
|
|
err = json.Unmarshal(buf, &env)
|
|||
|
|
if err != nil {
|
|||
|
|
err = fmt.Errorf("json.Unmarshal liveMatchDataEnvelope: %s", err)
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if !env.Success {
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var half Half
|
|||
|
|
|
|||
|
|
//
|
|||
|
|
for _, market := range env.Value.GE {
|
|||
|
|
switch market.MarketType {
|
|||
|
|
case teamMarketTotal:
|
|||
|
|
//fmt.Printf("h1 total: %v\n\n", market)
|
|||
|
|
|
|||
|
|
half.Total, err = getTotal(market)
|
|||
|
|
if err != nil {
|
|||
|
|
err = fmt.Errorf("1st Half total football getTotal: %s", err)
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
case teamMarketIndividualTotal1:
|
|||
|
|
//fmt.Printf("h1 total 1: %v\n\n", market)
|
|||
|
|
|
|||
|
|
half.IndividualTotalHome, err = getTotal(market)
|
|||
|
|
if err != nil {
|
|||
|
|
err = fmt.Errorf("1st Half individual total 1 football getTotal: %s", err)
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
case teamMarketIndividualTotal2:
|
|||
|
|
//fmt.Printf("h1 total 2: %v\n\n", market)
|
|||
|
|
|
|||
|
|
half.IndividualTotalAway, err = getTotal(market)
|
|||
|
|
if err != nil {
|
|||
|
|
err = fmt.Errorf("1st Half individual total 2 football getTotal: %s", err)
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
data.H1 = half
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
//fmt.Printf("%s\n\n", buf)
|
|||
|
|
|
|||
|
|
return data, false, nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/*
|
|||
|
|
type matchPeriod struct {
|
|||
|
|
// Football, Handball: 1 - half 1, 3 - half 2
|
|||
|
|
// Tennis: 11 - 1st set, 12 - 2nd set, 13 - 3rd set
|
|||
|
|
Type int
|
|||
|
|
Score1 int
|
|||
|
|
Score2 int
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Можно получить и доп. статистику, атак, ударов, где periodType - это
|
|||
|
|
// 1 - тайм 1, 3 - тайм 2, 100 - весь матч
|
|||
|
|
type matchStats struct {
|
|||
|
|
Score1 int
|
|||
|
|
Score2 int
|
|||
|
|
Status int // 1 - line, 2 - inplay, 3 - end
|
|||
|
|
//Winner int // в inplay не 0?
|
|||
|
|
Periods []matchPeriod
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const (
|
|||
|
|
// для гандбола тоже справедливо
|
|||
|
|
footballH1 = 1
|
|||
|
|
footballH2 = 3
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
// GetTeamMatchResult - возвращает счет матча, счет по таймам. Подходит для футбола
|
|||
|
|
// и гандбола.
|
|||
|
|
func GetTeamMatchResult(statsMatchID string) (stats TeamMatchResult, err error) {
|
|||
|
|
url := fmt.Sprintf(matchStatsURLPattern, statsMatchID)
|
|||
|
|
|
|||
|
|
spider, err := webspider.NewSpider(nil)
|
|||
|
|
if err != nil {
|
|||
|
|
panic(err)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
buf, err := spider.Get(url, nil)
|
|||
|
|
if err != nil {
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
fmt.Printf("Response: %s\n", buf)
|
|||
|
|
|
|||
|
|
var raw matchStats
|
|||
|
|
|
|||
|
|
err = json.Unmarshal(buf, &raw)
|
|||
|
|
if err != nil {
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
//stats.Winner = raw.Winner
|
|||
|
|
stats.Status = raw.Status
|
|||
|
|
stats.HomeGoals = raw.Score1
|
|||
|
|
stats.AwayGoals = raw.Score2
|
|||
|
|
// tennis
|
|||
|
|
stats.Score.Home = raw.Score1
|
|||
|
|
stats.Score.Away = raw.Score2
|
|||
|
|
stats.ScoreByPeriods = make(map[int]Score)
|
|||
|
|
|
|||
|
|
for _, period := range raw.Periods {
|
|||
|
|
switch period.Type {
|
|||
|
|
case footballH1:
|
|||
|
|
// fix - позже удалить H1 и H2
|
|||
|
|
stats.H1 = &HalfStats{
|
|||
|
|
HomeGoals: period.Score1,
|
|||
|
|
AwayGoals: period.Score2,
|
|||
|
|
}
|
|||
|
|
stats.ScoreByPeriods[1] = Score{
|
|||
|
|
Home: period.Score1,
|
|||
|
|
Away: period.Score2,
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
case footballH2:
|
|||
|
|
// fix - позже удалить H1 и H2
|
|||
|
|
stats.H2 = &HalfStats{
|
|||
|
|
HomeGoals: period.Score1,
|
|||
|
|
AwayGoals: period.Score2,
|
|||
|
|
}
|
|||
|
|
stats.ScoreByPeriods[2] = Score{
|
|||
|
|
Home: period.Score1,
|
|||
|
|
Away: period.Score2,
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
func GetTeamMatchResult(statsMatchID string) (result TeamMatchResult, err error) {
|
|||
|
|
url := fmt.Sprintf(matchStatsURLPattern, statsMatchID)
|
|||
|
|
|
|||
|
|
spider, err := webspider.NewSpider(nil)
|
|||
|
|
if err != nil {
|
|||
|
|
panic(err)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
buf, err := spider.Get(url, nil)
|
|||
|
|
if err != nil {
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
//fmt.Printf("%s\n", buf)
|
|||
|
|
|
|||
|
|
var tmp matchResult
|
|||
|
|
|
|||
|
|
err = json.Unmarshal(buf, &tmp)
|
|||
|
|
if err != nil {
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
result.Status = tmp.Status
|
|||
|
|
result.ScoreByPeriods = make(map[int]PeriodScore)
|
|||
|
|
|
|||
|
|
for _, p := range tmp.Periods {
|
|||
|
|
periodNumber := periodCode2PeriodNumberMapping[p.Code]
|
|||
|
|
if periodNumber == 0 {
|
|||
|
|
continue
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
result.ScoreByPeriods[periodNumber] = PeriodScore{
|
|||
|
|
HomePoints: p.HomePoints,
|
|||
|
|
AwayPoints: p.AwayPoints,
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
result.HomePoints += p.HomePoints
|
|||
|
|
result.AwayPoints += p.AwayPoints
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return
|
|||
|
|
}
|