344 lines
7.2 KiB
Go
344 lines
7.2 KiB
Go
|
|
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
|
|||
|
|
}
|