Files
onexbet/parser.go

249 lines
6.7 KiB
Go
Raw Normal View History

2026-07-21 03:55:57 +03:00
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"`
}