package onexbet import ( "encoding/json" "fmt" "strings" webspider "gordenko.dev/dima/spider" "gordenko.dev/dima/flashscore" ) var ( // проверка на Contains bannedTennisChamps = []string{ "Setka Cup", "Russia. Masters", "Russia. League Pro", "Daily Aqua Tour", "UTR Pro Tennis Series", "VS OPEN", "Daily Pro Tour", "Mountain Bay Open", "Russian River Cup", } bannedTennisPrefixes = []string{} ) func TennisChampFilter(champName string) (_ bool) { for _, bannedChampName := range bannedHandballChamps { if strings.Contains(champName, bannedChampName) { return } } for _, bannedPrefix := range bannedHandballPrefixes { if strings.HasPrefix(champName, bannedPrefix) { return } } if flashscore.IsTennisDoublesChamp(champName) { return } fmt.Printf("CHAMP NAME IS OK: %s\n", champName) return true } /* // Поля P и St - топ поля документа // Tennis match status // Подтверждено точно "St": 2, // Tennis Match Stat // Счет по сетам "P": [ { "A": 7, "CSPP": [], "H": 5, "T": 11, "TI": "000000000000000000000000" }, { "A": 4, "CSPP": [], "H": 6, "T": 12, "TI": "000000000000000000000000" }, { "A": 4, "CSPP": [], "H": 6, "T": 13, "TI": "000000000000000000000000" }, { "A": 6, "CSPP": [], "H": 4, "T": 14, "TI": "000000000000000000000000" }, { "A": 6, "CSPP": [], "H": 2, "T": 15, "TI": "000000000000000000000000" } ], */ type matchResult struct { Periods []periodResult `json:"P"` Status int `json:"St"` } // Можно добавить поле T (код сета) для контроля. // Коды сетов: 1й - 11, 2й - 12, 3й - 13, 4й - 14, 5й - 15 type periodResult struct { Code int `json:"T"` HomePoints int `json:"H"` AwayPoints int `json:"A"` } // Для футбола и гандбола var periodCode2PeriodNumberMapping = map[int]int{ 1: 1, 3: 2, } var setCode2SetNumberMapping = map[int]int{ 11: 1, 12: 2, 13: 3, 14: 4, 15: 5, } func GetTennisMatchResult(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 _, set := range tmp.Periods { setNumber := setCode2SetNumberMapping[set.Code] if setNumber == 0 { continue } result.ScoreByPeriods[setNumber] = PeriodScore{ HomePoints: set.HomePoints, AwayPoints: set.AwayPoints, } } return } /////////// // Все структуры с маленькой буквы - это служебные структуры для парсинга // служебная структура для парсинга данных live матча type tennisLiveMatchDataEnvelope struct { Error string ErrorCode int64 Success bool // false, если матч закончен Value tennisLiveMatchData } // поле BIG со списком matchID для других периодов встречается в JSON документах // других периодов. То есть если запросить данные для 2-го сета по специальному ID, // то в поле BIG будет список ID. Если же запросить документ для Regular Time, то // поля BIG не будет, а будет SG (с коэффициентами для других периодов) type tennisLiveMatchData struct { Markets []rawMarket `json:"GE"` // рынки // текущий счет, в том числе по геймам (только в Regular Time документе) Stats statAndScore `json:"SC"` Sets []tennisSet `json:"SG"` StatsMatchID string `json:"SGI"` } type tennisSet struct { SetNumber int `json:"P"` Markets []rawMarket `json:"GE"` } /* type tennisStatsAndScore struct { CurrentSetNumber int `json:"CP"` SetScore scoreStruct `json:"FS"` // счет по сетам GameScore []periodScore `json:"PS"` } */ type statAndScore struct { Score scoreStruct `json:"FS"` ScoreByPeriods []periodScore `json:"PS"` CurrentPeriod int `json:"CP"` } // public func LoadTennisLiveMatchData(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 tennisLiveMatchDataEnvelope 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, ScoreByPeriods: make(map[int]Score), Periods: make(map[int]Period), } // FULL TIME for _, market := range env.Value.Markets { switch market.MarketType { case tennisMarketWinner: winner := &Winner2Way{} for _, offers := range market.OffersList { if len(offers) > 0 { offer := offers[0] if offer.IsBlocked { continue } if offer.Side == 1 { // T=1 winner.HasHome = true winner.Home = offer.Price } else if offer.Side == 3 { // T=3 winner.HasAway = true winner.Away = offer.Price } } } data.Winner2Way = winner case tennisMarketTotal: data.Total, err = getTotal(market) if err != nil { err = fmt.Errorf("FT getTotal (tennis): %s", err) return } case tennisMarketHandicap: data.Handicap, err = getHandicap(market) if err != nil { err = fmt.Errorf("FT getHandicap (tennis): %s", err) return } } } // BY SETS for _, p := range env.Value.Sets { set := Period{ PeriodNumber: p.SetNumber, } for _, market := range p.Markets { switch market.MarketType { case tennisMarketTotal: set.Total, err = getTotal(market) if err != nil { err = fmt.Errorf("SET %d getTotal (tennis): %s", p.SetNumber, err) return } case tennisMarketHandicap: set.Handicap, err = getHandicap(market) if err != nil { err = fmt.Errorf("SET %d getHandicap (tennis): %s", p.SetNumber, err) return } } } data.Periods[p.SetNumber] = set } // SCORE stat := env.Value.Stats if stat.CurrentPeriod == 0 { // ВАЖНО! // Если период не указан - поля нет в JSON, счета нет err = fmt.Errorf("Current score not found") return } data.CurrentPeriod = stat.CurrentPeriod data.Score.Home = stat.Score.HomeScore data.Score.Away = stat.Score.AwayScore for _, p := range stat.ScoreByPeriods { data.ScoreByPeriods[p.Period] = Score{ Home: p.Score.HomeScore, Away: p.Score.AwayScore, } } //fmt.Printf("%s\n\n", buf) return data, false, nil }