249 lines
6.7 KiB
Go
249 lines
6.7 KiB
Go
|
|
package onexbet
|
|||
|
|
|
|||
|
|
import (
|
|||
|
|
"fmt"
|
|||
|
|
"strings"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
type organizer struct {
|
|||
|
|
Name string `json:"name"`
|
|||
|
|
Sport string `json:"sport"`
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
type teamName struct {
|
|||
|
|
Name string `json:"name"`
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// служебная структура для парсинга списка live матчей
|
|||
|
|
type liveMatch struct {
|
|||
|
|
StartDate string `json:"startDate"`
|
|||
|
|
URL string `json:"url"`
|
|||
|
|
Organizer organizer `json:"organizer"`
|
|||
|
|
HomeTeam teamName `json:"homeTeam"`
|
|||
|
|
AwayTeam teamName `json:"awayTeam"`
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// служебная структура для статистики: удары, угловые, атаки...
|
|||
|
|
type keyValue struct {
|
|||
|
|
Key string
|
|||
|
|
Value string
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// служебная структура для коэффициентов/параметров
|
|||
|
|
type rawMarket struct {
|
|||
|
|
OffersList [][]rawOffer `json:"E"` // Массив массивов!
|
|||
|
|
MarketType int `json:"G"`
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
type rawOffer struct {
|
|||
|
|
Price float64 `json:"C"`
|
|||
|
|
Param float64 `json:"P"` // 2.5
|
|||
|
|
Side int `json:"T"` // 1 X 2, over/under
|
|||
|
|
IsBlocked bool `json:"B"` // означает что цена заблокирована (иконка замка)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func getMatchIDFromURL(url string) string {
|
|||
|
|
// формат ссылки live/Football/2084034-FIFA-20-Italian-Cup/240767941-Fiorentina-Paulo-Milan-Arta/
|
|||
|
|
// matchID - 240767941
|
|||
|
|
// Алгоритм:
|
|||
|
|
// - разбиваем ссылки на секции по символу '/'
|
|||
|
|
// - разбиваем последнюю НЕпустую секцию на части по символу '-'
|
|||
|
|
// - первый элемент и будет matchID
|
|||
|
|
|
|||
|
|
url = strings.TrimSpace(url)
|
|||
|
|
url = strings.TrimSuffix(url, "/")
|
|||
|
|
|
|||
|
|
sections := strings.Split(url, "/")
|
|||
|
|
|
|||
|
|
last := sections[len(sections)-1]
|
|||
|
|
|
|||
|
|
parts := strings.Split(last, "-")
|
|||
|
|
|
|||
|
|
return parts[0]
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
type rawMatchCandidate struct {
|
|||
|
|
Sport string `json:"SN"`
|
|||
|
|
ChampName string `json:"L"`
|
|||
|
|
Home string `json:"O1"`
|
|||
|
|
Away string `json:"O2"`
|
|||
|
|
StartTime int64 `json:"S"`
|
|||
|
|
MatchID string `json:"SGI"`
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// служебная структура для парсинга данных live матча
|
|||
|
|
type searchResponse struct {
|
|||
|
|
Error string
|
|||
|
|
ErrorCode int64
|
|||
|
|
Success bool // false, если матч закончен
|
|||
|
|
Value []rawMatchCandidate
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Список live матчей
|
|||
|
|
|
|||
|
|
/*
|
|||
|
|
Live матч. Коэффициенты и т.д.
|
|||
|
|
|
|||
|
|
https://1x-bet-ua.com/LiveFeed/GetGameZip?id=240704765&lng=en&cfview=0&isSubGames=true&GroupEvents=true&allEventsGroupSubGames=true&countevents=250&partner=25&marketType=1
|
|||
|
|
|
|||
|
|
|
|||
|
|
Live матч завершен. Ответ:
|
|||
|
|
|
|||
|
|
{"Error":null,"ErrorCode":0,"Guid":"","Id":0,"Success":false,"Value":null}
|
|||
|
|
|
|||
|
|
<a href="live/Football/2084034-FIFA-20-Italian-Cup/240767941-Fiorentina-Paulo-Milan-Arta/" class="c-events__name">
|
|||
|
|
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
// https://ua-1x-bet.com/en/live/Football/88637-England-Premier-League/242638344-Leicester-City-Brighton--Hove-Albion/
|
|||
|
|
|
|||
|
|
type liveItem struct {
|
|||
|
|
SportName string `json:"SN"`
|
|||
|
|
Home string `json:"O1"`
|
|||
|
|
Away string `json:"O2"`
|
|||
|
|
MatchID int64 `json:"I"`
|
|||
|
|
StartTime int64 `json:"S"`
|
|||
|
|
ChampName string `json:"L"` // вместе с зоной
|
|||
|
|
ZoneName string `json:"CN"`
|
|||
|
|
StatsMatchID string `json:"SGI"` // вида 5d02144c286ef2e7bff3015b
|
|||
|
|
Stat statAndScore `json:"SC"`
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
type liveList struct {
|
|||
|
|
Success bool
|
|||
|
|
Value []liveItem
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func getTotal(market rawMarket) (_ Total, err error) {
|
|||
|
|
if len(market.OffersList) != 2 {
|
|||
|
|
err = fmt.Errorf("Total market must have 2 arrays of offers (Over and Under), not %d", len(market.OffersList))
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
// OVER
|
|||
|
|
var over []ParamOffer
|
|||
|
|
for _, offer := range market.OffersList[0] {
|
|||
|
|
if offer.IsBlocked {
|
|||
|
|
continue
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
over = append(over, ParamOffer{
|
|||
|
|
Price: offer.Price,
|
|||
|
|
Param: fmt.Sprintf("%.1f", offer.Param),
|
|||
|
|
ParamFloat: offer.Param,
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// UNDER
|
|||
|
|
var under []ParamOffer
|
|||
|
|
for _, offer := range market.OffersList[1] {
|
|||
|
|
if offer.IsBlocked {
|
|||
|
|
continue
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
under = append(under, ParamOffer{
|
|||
|
|
Price: offer.Price,
|
|||
|
|
Param: fmt.Sprintf("%.1f", offer.Param),
|
|||
|
|
ParamFloat: offer.Param,
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return Total{
|
|||
|
|
Over: over,
|
|||
|
|
Under: under,
|
|||
|
|
}, nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func getHandicap(market rawMarket) (_ Handicap, err error) {
|
|||
|
|
if len(market.OffersList) != 2 {
|
|||
|
|
err = fmt.Errorf("Handicap market must have 2 arrays of offers (Home and Away), not %d", len(market.OffersList))
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
// home
|
|||
|
|
var home []ParamOffer
|
|||
|
|
for _, offer := range market.OffersList[0] {
|
|||
|
|
if offer.IsBlocked {
|
|||
|
|
continue
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
home = append(home, ParamOffer{
|
|||
|
|
Price: offer.Price,
|
|||
|
|
Param: fmt.Sprintf("%.1f", offer.Param),
|
|||
|
|
ParamFloat: offer.Param,
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// away
|
|||
|
|
var away []ParamOffer
|
|||
|
|
for _, offer := range market.OffersList[1] {
|
|||
|
|
if offer.IsBlocked {
|
|||
|
|
continue
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
away = append(away, ParamOffer{
|
|||
|
|
Price: offer.Price,
|
|||
|
|
Param: fmt.Sprintf("%.1f", offer.Param),
|
|||
|
|
ParamFloat: offer.Param,
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return Handicap{
|
|||
|
|
Home: home,
|
|||
|
|
Away: away,
|
|||
|
|
}, nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// live match data
|
|||
|
|
|
|||
|
|
// служебная структура для парсинга данных live матча
|
|||
|
|
type teamLiveMatchDataEnvelope struct {
|
|||
|
|
Error string
|
|||
|
|
ErrorCode int64
|
|||
|
|
Success bool // false, если матч закончен
|
|||
|
|
Value teamLiveMatchData
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// half 1, half 2
|
|||
|
|
type teamSG struct {
|
|||
|
|
SubMatchID int `json:"I"`
|
|||
|
|
Period int `json:"P"`
|
|||
|
|
PeriodName string `json:"PN"`
|
|||
|
|
// Опциональное поле (у тенниса нет)
|
|||
|
|
SubCategory string `json:"TG"` // yellow cards, etc...
|
|||
|
|
// В "Austria. Erste Liga" у матчей half 1 кодируется в нижеуказанных полях
|
|||
|
|
// Требует уточнения.
|
|||
|
|
// EC int
|
|||
|
|
// EGC int
|
|||
|
|
// GE []rawMarket
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
type teamLiveMatchData struct {
|
|||
|
|
// список subMatchID для half 1, half 2...
|
|||
|
|
// поле есть до лайва вместо SG?
|
|||
|
|
BIG []teamSG
|
|||
|
|
GE []rawMarket // рынки
|
|||
|
|
// периоды и голы; текущее время матча; статистика по ударам, атакам...
|
|||
|
|
SC teamStatsAndScore
|
|||
|
|
// список subMatchID для half 1, half 2...
|
|||
|
|
// коэффициенты можно получить отдельным HTTP-запросом
|
|||
|
|
SG []teamSG
|
|||
|
|
//StatsMatchID string `json:""SGI`
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
type teamStatsAndScore struct {
|
|||
|
|
CurrentPeriod int `json:"CP"`
|
|||
|
|
FullTimeScore scoreStruct `json:"FS"`
|
|||
|
|
ScoreByPeriods []periodScore `json:"PS"`
|
|||
|
|
CurrentTime int `json:"TS"` // в секундах с начала матча!
|
|||
|
|
Stats []keyValue `json:"S"` // статистика удары, угловые, атаки и т.д.
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
type scoreStruct struct {
|
|||
|
|
HomeScore int `json:"S1"`
|
|||
|
|
AwayScore int `json:"S2"`
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
type periodScore struct {
|
|||
|
|
Period int `json:"Key"`
|
|||
|
|
Score scoreStruct `json:"Value"`
|
|||
|
|
}
|