This commit is contained in:
2026-07-21 03:55:57 +03:00
commit 15b428efec
39 changed files with 136724 additions and 0 deletions

218
api.go Normal file
View File

@@ -0,0 +1,218 @@
package onexbet
import (
"encoding/json"
"fmt"
"net/http"
"net/url"
"strings"
"time"
"gordenko.dev/dima/httpreq"
)
// champFilter - на входе имя чемпионата, на выходе true - если чемпионат удовлетворяет,
// false - если игнорируем
func SearchMatchCandidates(home, away string, sportID OnexbetSport) (candidates []MatchCandidate, err error) {
var (
lineSearchURL string
liveSearchURL string
requiredSportName string
champFilter func(string) bool
)
switch sportID {
case Football:
lineSearchURL = stdLineSearchURL
liveSearchURL = stdLiveSearchURL
requiredSportName = "football"
champFilter = FootballChampFilter
case Handball:
lineSearchURL = stdLineSearchURL
liveSearchURL = stdLiveSearchURL
requiredSportName = "handball"
champFilter = HandballChampFilter
case Tennis:
lineSearchURL = tennisLineSearchURL
liveSearchURL = tennisLiveSearchURL
requiredSportName = "tennis"
champFilter = TennisChampFilter
}
// 2 запроса к Line и Live матчам
homeLineMatches, err := SearchTeamMatches(lineSearchURL, home)
if err != nil {
// log
}
homeLiveMatches, err := SearchTeamMatches(liveSearchURL, home)
if err != nil {
// log
}
awayLineMatches, err := SearchTeamMatches(lineSearchURL, away)
if err != nil {
// log
}
awayLiveMatches, err := SearchTeamMatches(liveSearchURL, away)
if err != nil {
// log
}
lists := [][]MatchCandidate{
homeLineMatches,
homeLiveMatches,
awayLineMatches,
awayLiveMatches,
}
requiredSportName = strings.ToLower(requiredSportName)
candidates = filterMatchesBySportAndChamps(lists, requiredSportName, champFilter)
return
}
// champFilter - на входе имя чемпионата, на выходе true - если чемпионат удовлетворяет,
// false - если игнорируем
func SearchTeamCandidates(team string, sportID OnexbetSport) (candidates []MatchCandidate, err error) {
var (
lineSearchURL string
liveSearchURL string
requiredSportName string
champFilter func(string) bool
)
switch sportID {
case Football:
lineSearchURL = stdLineSearchURL
liveSearchURL = stdLiveSearchURL
requiredSportName = "football"
champFilter = FootballChampFilter
case Handball:
lineSearchURL = stdLineSearchURL
liveSearchURL = stdLiveSearchURL
requiredSportName = "handball"
champFilter = HandballChampFilter
case Tennis:
lineSearchURL = tennisLineSearchURL
liveSearchURL = tennisLiveSearchURL
requiredSportName = "tennis"
champFilter = TennisChampFilter
}
// 2 запроса к Line и Live матчам
teamLineMatches, err := SearchTeamMatches(lineSearchURL, team)
if err != nil {
// log
}
teamLiveMatches, err := SearchTeamMatches(liveSearchURL, team)
if err != nil {
// log
}
lists := [][]MatchCandidate{
teamLineMatches,
teamLiveMatches,
}
candidates = filterMatchesBySportAndChamps(lists, requiredSportName, champFilter)
return
}
func filterMatchesBySportAndChamps(lists [][]MatchCandidate, requiredSportName string, champFilter func(string) bool) (candidates []MatchCandidate) {
// Для отсева матчей дубликатов
matchIDs := make(map[string]bool)
requiredSportName = strings.ToLower(requiredSportName)
for _, list := range lists {
for _, match := range list {
if !matchIDs[match.MatchID] {
sportName := strings.ToLower(match.Sport)
if !strings.Contains(sportName, requiredSportName) {
continue
}
if requiredSportName == "football" {
// Доп проверка на Американский футбол
if strings.Contains(sportName, "american football") {
continue
}
}
if champFilter != nil {
if !champFilter(match.ChampName) {
continue
}
}
// FIX - добавить проверку для тенниса по / и ()
candidates = append(candidates, match)
matchIDs[match.MatchID] = true
}
}
}
return
}
func SearchTeamMatches(searchURL string, teamName string) (matches []MatchCandidate, err error) {
if teamName == "" {
err = fmt.Errorf("Empty teamName")
return
}
if searchURL == "" {
err = fmt.Errorf("Empty searchURL")
return
}
// 2 запроса к Line и Live матчам
params := url.Values{}
params.Set("text", teamName)
params.Set("limit", "50")
params.Set("mode", "4")
params.Set("partner", defaultPartnerID)
params.Set("lng", "en")
params.Set("userId", "0")
queryString := params.Encode()
req, err := http.NewRequest("GET", searchURL+"?"+queryString, nil)
if err != nil {
return
}
resp, err := httpreq.Send(req, 10*time.Second)
if err != nil {
err = fmt.Errorf("httpreq.Send: %s", err)
return
}
var result searchResponse
err = json.Unmarshal(resp.Body, &result)
if err != nil {
return
}
for _, match := range result.Value {
matches = append(matches, MatchCandidate{
Sport: match.Sport,
ChampName: match.ChampName,
StartTime: match.StartTime,
Home: match.Home,
Away: match.Away,
MatchID: match.MatchID,
})
}
return
}

File diff suppressed because it is too large Load Diff

2200
basketball/live-match.json Executable file

File diff suppressed because it is too large Load Diff

1502
basketball/live-q4.json Executable file

File diff suppressed because it is too large Load Diff

45
cmd/Notes Executable file
View File

@@ -0,0 +1,45 @@
В обычном матче рынки типа Half 1, Half 2, corners попадают в поле SG. У каждой группы есть свой EC и EGC код.
Но некоторые матчи для Half 1, Half 2 заводят новые matchID. Опознать такие матчи можно по ответу:
{
"Error": "",
"ErrorCode": 0,
"Guid": "",
"Id": 0,
"Success": true,
"Value": {
"BIG": [
{
"I": 241207820,
"PN": "1 Half"
},
{
"I": 241207823,
"PN": "2 Half"
},
{
"I": 241207821,
"TG": "Corners"
},
{
"I": 241207822,
"PN": "1 Half",
"TG": "Corners"
},
{
"I": 241209140,
"TG": "Accumulator Outcomes"
},
{
"I": 241207815,
"TG": "Quick events"
},
{
"I": 241207816,
"TG": "Result + Total"
}
],
Если есть поле BIG - это комплексный матч. Поле PN - это название группы, I - это matchID.
Загрузив данные для Half1 следует искать все нужные рынки на топ уровне Value.GE, а не в массиве Value.SG

92
cmd/main.go Executable file
View File

@@ -0,0 +1,92 @@
package main
/*
Отписка от матча:
- метод Unwatch
- когда матч исчезнет из лайва
*/
import (
"log"
"os"
"os/signal"
"syscall"
"time"
"gordenko.dev/dima/onexbet"
"gordenko.dev/dima/onexbet/daemon"
"gordenko.dev/dima/web"
"gordenko.dev/dima/web/api"
"gordenko.dev/dima/web/router"
"gordenko.dev/dima/ws"
"gordenko.dev/dima/ws/publisher"
)
const (
port = 7772
dsn = "user:password@/tipper"
)
func main() {
wsServer, err := ws.NewPublicServer(ws.PublicServerOptions{
Logger: log.New(os.Stdout, "ws: ", log.LstdFlags),
LostConnectionTimeout: 1 * time.Minute,
Ping: true,
})
if err != nil {
log.Fatalf("ws.NewPublicServer: %s\n", err)
}
wsPublisher, err := publisher.NewPublicPublisher(publisher.PublicPublisherOptions{
WsPublicServer: wsServer,
})
if err != nil {
log.Fatalf("ws.NewPublicPublisher: %s\n", err)
}
//
logger := log.New(os.Stdout, "", log.LstdFlags)
d := daemon.New(daemon.Options{
Logger: logger,
SportIDs: []onexbet.OnexbetSport{
onexbet.Handball,
onexbet.Football,
onexbet.Tennis,
},
WsPublisher: wsPublisher,
})
wsServer.OnConnected(d.OnWsClientConnected)
publicAPI, err := api.NewAPI(api.Options{
Logger: logger,
})
publicAPI.PublicFunc("watch", d.Watch)
publicAPI.PublicFunc("unwatch", d.Unwatch)
//publicAPI.Handle("searchMatchCandidates", s.SearchMatchCandidates)
r := router.New()
r.PrefixHandle("/api", publicAPI)
r.Handle("/ws", wsServer)
app, err := web.NewApp(web.AppOptions{
Port: port,
Router: r,
})
if err != nil {
log.Fatalf("web.NewApp: %s\n", err)
}
go app.Run()
go d.Run()
sigs := make(chan os.Signal, 1)
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
<-sigs
logger.Println("daemon stopped.")
}

270
daemon/daemon.go Normal file
View File

@@ -0,0 +1,270 @@
package daemon
/*
Отписка от матча:
- метод Unwatch
- когда матч исчезнет из лайва
*/
import (
"fmt"
"log"
"sync"
"time"
"gordenko.dev/dima/onexbet"
"gordenko.dev/dima/ws"
"gordenko.dev/dima/ws/publisher"
)
const (
defaultLiveMatchesLoadInterval = 60 * time.Second
defaultPollingInterval = 5 * time.Second
)
type Options struct {
Logger *log.Logger
LiveMatchesLoadInterval int
PollingInterval int
SportIDs []onexbet.OnexbetSport
WsPublisher *publisher.PublicPublisher
}
type Daemon struct {
mutex sync.Mutex
publisher *publisher.PublicPublisher
port int
liveMatchesLoadInterval time.Duration
logger *log.Logger
//liveMatches map[string]onexbet.LiveMatch
teamLiveMatches map[onexbet.OnexbetSport][]onexbet.LiveMatch
// matchID => isClosed flag
pollingMatches map[onexbet.OnexbetSport]map[string]*bool
pollingInterval time.Duration
sportIDs []onexbet.OnexbetSport
}
func New(opt Options) *Daemon {
if opt.Logger == nil {
panic("Logger option is required")
}
s := &Daemon{
logger: opt.Logger,
publisher: opt.WsPublisher,
pollingMatches: make(map[onexbet.OnexbetSport]map[string]*bool),
teamLiveMatches: make(map[onexbet.OnexbetSport][]onexbet.LiveMatch),
sportIDs: opt.SportIDs,
}
if opt.LiveMatchesLoadInterval <= 0 {
s.liveMatchesLoadInterval = defaultLiveMatchesLoadInterval
} else {
s.liveMatchesLoadInterval = time.Duration(opt.LiveMatchesLoadInterval) * time.Second
}
if opt.PollingInterval <= 0 {
s.pollingInterval = defaultPollingInterval
} else {
s.pollingInterval = time.Duration(opt.PollingInterval) * time.Second
}
for _, sportID := range s.sportIDs {
s.pollingMatches[sportID] = make(map[string]*bool)
}
return s
}
func (s *Daemon) OnWsClientConnected(conn *ws.Conn) {
s.mutex.Lock()
defer s.mutex.Unlock()
for _, sportID := range s.sportIDs {
conn.Send(onexbet.WsMsgLiveMatches, s.getLiveMatchesMessage(sportID))
}
s.publisher.SubscribeTo(conn, onexbet.TopicLive)
}
func (s *Daemon) getLiveMatchesMessage(sportID onexbet.OnexbetSport) onexbet.LiveMatchesMessage {
var watching []string
for matchID := range s.pollingMatches[sportID] {
watching = append(watching, matchID)
}
return onexbet.LiveMatchesMessage{
SportID: sportID,
Matches: s.teamLiveMatches[sportID],
Watching: watching,
}
}
func (s *Daemon) loadLiveMatches(sportID onexbet.OnexbetSport) {
s.logger.Printf("Load live matches\n")
list, err := onexbet.ListLiveMatchesBySport(sportID)
if err != nil {
s.logger.Printf("ListLiveMatchesBySport(sportID=%d): %s\n", sportID, err)
return
}
//s.logger.Printf("NewMatches: %# v\n", pretty.Formatter(newMatches))
//s.logger.Printf("NewMatches: %d\n", len(newMatchesList))
// sync current live matches with new matches
s.mutex.Lock()
defer s.mutex.Unlock()
s.teamLiveMatches[sportID] = list
s.publisher.Publish(onexbet.TopicLive, onexbet.WsMsgLiveMatches, s.getLiveMatchesMessage(sportID))
}
// Метод может прислать обновление для матча после закрытия. Это не проблема.
func (s *Daemon) pollTeamMatch(m map[string]*bool, req onexbet.WatchReq, isClosedPtr *bool) { //closeCh chan struct{}) {
s.logger.Printf("poll team match %s\n", req.MatchID)
for {
if *isClosedPtr {
s.logger.Printf("match %s polling is closed by command\n", req.MatchID)
return
}
data, isMatchFinished, err := onexbet.LoadTeamLiveMatchData(req.MatchID)
if err != nil {
s.logger.Printf("LoadTeamLiveMatchData: %s; matchID=%s", err, req.MatchID)
} else {
//s.sendLiveMatchData(data)
//s.logger.Printf("match update: %v\n\n", data)
// Мутекс нужен, чтобы защитить Publisher, ибо он НЕ ThreadSafe!
if isMatchFinished {
// Отправлять сообщение не нужно, ибо Tipper обнаружит что матч исчез из
// polling и запросит для него результат
//s.mutex.Lock()
//delete(m, req.MatchID)
//s.publisher.Publish(ChannelLive, onexbet.WSMessageMatchFinished, req)
//s.mutex.Unlock()
// выходим
s.logger.Printf("match %s polling is finished\n", req.MatchID)
return
}
s.mutex.Lock()
s.publisher.Publish(onexbet.TopicLive, onexbet.WsMsgTeamLiveMatchData, data)
s.mutex.Unlock()
}
time.Sleep(s.pollingInterval)
}
}
// Метод может прислать обновление для матча после закрытия. Это не проблема.
func (s *Daemon) pollTennisMatch(m map[string]*bool, req onexbet.WatchReq, isClosedPtr *bool) { //closeCh chan struct{}) {
s.logger.Printf("poll tennis match %s\n", req.MatchID)
for {
if *isClosedPtr {
s.logger.Printf("match %s polling is closed by command\n", req.MatchID)
return
}
data, isMatchFinished, err := onexbet.LoadTennisLiveMatchData(req.MatchID)
if err != nil {
s.logger.Printf("LoadTennisLiveMatchData: %s; matchID=%s", err, req.MatchID)
} else {
//s.sendLiveMatchData(data)
//s.logger.Printf("match update: %v\n\n", data)
// Мутекс нужен, чтобы защитить Publisher, ибо он НЕ ThreadSafe!
if isMatchFinished {
// Отправлять сообщение не нужно, ибо Tipper обнаружит что матч исчез из
// polling и запросит для него результат
//s.mutex.Lock()
//delete(m, req.MatchID)
//s.publisher.Publish(ChannelLive, onexbet.WSMessageMatchFinished, req)
//s.mutex.Unlock()
// выходим
s.logger.Printf("match %s polling is finished\n", req.MatchID)
return
}
s.mutex.Lock()
s.publisher.Publish(onexbet.TopicLive, onexbet.WsMsgTennisLiveMatchData, data)
s.mutex.Unlock()
}
time.Sleep(s.pollingInterval)
}
}
func (s *Daemon) Watch(req onexbet.WatchReq) (err error) {
s.logger.Printf("Watch: sport=%d, matchID=%s\n", req.SportID, req.MatchID)
s.mutex.Lock()
defer s.mutex.Unlock()
m, ok := s.pollingMatches[req.SportID]
if !ok {
err = fmt.Errorf("Unknown sportID: %d", req.SportID)
return
}
_, ok = m[req.MatchID]
if ok {
// already polling
s.logger.Printf("match %s already polling\n", req.MatchID)
return
}
var isClosed bool
isClosedPtr := &isClosed
m[req.MatchID] = isClosedPtr //closeCh
if req.SportID == onexbet.Tennis {
go s.pollTennisMatch(m, req, isClosedPtr)
} else {
go s.pollTeamMatch(m, req, isClosedPtr)
}
return
}
func (s *Daemon) Unwatch(req onexbet.WatchReq) (err error) {
s.logger.Printf("Unwatch: sport=%d, matchID=%s\n", req.SportID, req.MatchID)
s.mutex.Lock()
defer s.mutex.Unlock()
m, ok := s.pollingMatches[req.SportID]
if !ok {
err = fmt.Errorf("Unknown sportID: %d", req.SportID)
return
}
isClosedPtr, ok := m[req.MatchID]
if !ok {
return
}
// Сигнализируем горутине чтобы она закрылась
*isClosedPtr = true
delete(m, req.MatchID)
return
}
func (s *Daemon) Run() {
for _, sportID := range s.sportIDs {
s.loadLiveMatches(sportID)
s.logger.Println("load end")
}
s.logger.Printf("liveMatchesLoadInterval: %d\n", int(s.liveMatchesLoadInterval.Seconds()))
ticker := time.NewTicker(s.liveMatchesLoadInterval)
for {
s.logger.Println("new loop")
select {
case <-ticker.C:
for _, sportID := range s.sportIDs {
s.loadLiveMatches(sportID)
s.logger.Println("load end")
}
}
}
}

3832
examples/handball-ge.json Normal file

File diff suppressed because it is too large Load Diff

799
examples/handball-men.json Normal file
View File

@@ -0,0 +1,799 @@
{
"Error": "",
"ErrorCode": 0,
"Guid": "",
"Id": 0,
"Success": true,
"Value": {
"CN": "Qatar",
"CO": 10,
"COI": 86,
"EC": 81,
"EGC": 12,
"GE": [
{
"E": [
[
{
"C": 1.23,
"G": 17,
"P": 52.5,
"T": 9
},
{
"C": 1.47,
"G": 17,
"P": 53.5,
"T": 9
},
{
"C": 1.625,
"G": 17,
"P": 54,
"T": 9
},
{
"C": 1.95,
"CE": 1,
"G": 17,
"P": 54.5,
"T": 9
},
{
"C": 2.235,
"G": 17,
"P": 55,
"T": 9
},
{
"C": 2.55,
"G": 17,
"P": 55.5,
"T": 9
},
{
"C": 3.49,
"G": 17,
"P": 56.5,
"T": 9
}
],
[
{
"C": 3.42,
"G": 17,
"P": 52.5,
"T": 10
},
{
"C": 2.42,
"G": 17,
"P": 53.5,
"T": 10
},
{
"C": 2.096,
"G": 17,
"P": 54,
"T": 10
},
{
"C": 1.85,
"CE": 1,
"G": 17,
"P": 54.5,
"T": 10
},
{
"C": 1.55,
"G": 17,
"P": 55,
"T": 10
},
{
"C": 1.43,
"G": 17,
"P": 55.5,
"T": 10
},
{
"C": 1.22,
"G": 17,
"P": 56.5,
"T": 10
}
]
],
"G": 17
},
{
"E": [
[
{
"C": 3.64,
"G": 2,
"P": -7.5,
"T": 7
},
{
"C": 2.57,
"G": 2,
"P": -6.5,
"T": 7
},
{
"C": 1.95,
"CE": 1,
"G": 2,
"P": -5.5,
"T": 7
},
{
"C": 1.47,
"G": 2,
"P": -4.5,
"T": 7
},
{
"C": 1.24,
"G": 2,
"P": -3.5,
"T": 7
}
],
[
{
"C": 1.2,
"G": 2,
"P": 7.5,
"T": 8
},
{
"C": 1.42,
"G": 2,
"P": 6.5,
"T": 8
},
{
"C": 1.85,
"CE": 1,
"G": 2,
"P": 5.5,
"T": 8
},
{
"C": 2.43,
"G": 2,
"P": 4.5,
"T": 8
},
{
"C": 3.36,
"G": 2,
"P": 3.5,
"T": 8
}
]
],
"G": 2
},
{
"E": [
[
{
"C": 1.22,
"G": 15,
"P": 28.5,
"T": 11
},
{
"C": 1.31,
"G": 15,
"P": 29,
"T": 11
},
{
"C": 1.6,
"G": 15,
"P": 29.5,
"T": 11
},
{
"C": 1.97,
"CE": 1,
"G": 15,
"P": 30,
"T": 11
},
{
"C": 2.44,
"G": 15,
"P": 30.5,
"T": 11
},
{
"C": 3.38,
"G": 15,
"P": 31,
"T": 11
},
{
"C": 3.94,
"G": 15,
"P": 31.5,
"T": 11
}
],
[
{
"C": 3.5,
"G": 15,
"P": 28.5,
"T": 12
},
{
"C": 2.89,
"G": 15,
"P": 29,
"T": 12
},
{
"C": 2.06,
"G": 15,
"P": 29.5,
"T": 12
},
{
"C": 1.66,
"CE": 1,
"G": 15,
"P": 30,
"T": 12
},
{
"C": 1.42,
"G": 15,
"P": 30.5,
"T": 12
},
{
"C": 1.23,
"G": 15,
"P": 31,
"T": 12
},
{
"C": 1.17,
"G": 15,
"P": 31.5,
"T": 12
}
]
],
"G": 15
},
{
"E": [
[
{
"C": 1.35,
"G": 62,
"P": 23.5,
"T": 13
},
{
"C": 1.51,
"G": 62,
"P": 24,
"T": 13
},
{
"C": 1.86,
"CE": 1,
"G": 62,
"P": 24.5,
"T": 13
},
{
"C": 2.39,
"G": 62,
"P": 25,
"T": 13
},
{
"C": 2.79,
"G": 62,
"P": 25.5,
"T": 13
}
],
[
{
"C": 2.69,
"G": 62,
"P": 23.5,
"T": 14
},
{
"C": 2.23,
"G": 62,
"P": 24,
"T": 14
},
{
"C": 1.75,
"CE": 1,
"G": 62,
"P": 24.5,
"T": 14
},
{
"C": 1.45,
"G": 62,
"P": 25,
"T": 14
},
{
"C": 1.33,
"G": 62,
"P": 25.5,
"T": 14
}
]
],
"G": 62
},
{
"E": [
[
{
"C": 1.9,
"G": 14,
"T": 182
}
],
[
{
"C": 1.9,
"G": 14,
"T": 183
}
]
],
"G": 14
},
{
"E": [
[
{
"C": 1.9,
"G": 91,
"T": 755
}
],
[
{
"C": 1.9,
"G": 91,
"T": 757
}
]
],
"G": 91
},
{
"E": [
[
{
"C": 1.9,
"G": 92,
"T": 766
}
],
[
{
"C": 1.9,
"G": 92,
"T": 767
}
]
],
"G": 92
},
{
"E": [
[
{
"C": 3.86,
"G": 1144,
"P": 1.003,
"T": 2314
},
{
"C": 1.048,
"G": 1144,
"P": 3.009,
"T": 2314
},
{
"C": 6.02,
"G": 1144,
"P": 9.012,
"T": 2314
},
{
"C": 47,
"G": 1144,
"P": 12.016,
"T": 2314
},
{
"C": 50,
"G": 1144,
"P": 16.02,
"T": 2314
},
{
"C": 30,
"G": 1144,
"P": 21,
"T": 2316
}
],
[
{
"C": 10,
"G": 1144,
"T": 2318
},
{
"C": 38,
"G": 1144,
"P": 1.003,
"T": 2315
},
{
"C": 50,
"G": 1144,
"P": 3.009,
"T": 2315
},
{
"C": 50,
"G": 1144,
"P": 9.012,
"T": 2315
},
{
"C": 50,
"G": 1144,
"P": 12.016,
"T": 2315
},
{
"C": 50,
"G": 1144,
"P": 16.02,
"T": 2315
},
{
"C": 30,
"G": 1144,
"P": 21,
"T": 2317
}
]
],
"G": 1144
},
{
"E": [
[
{
"C": 2.42,
"G": 3321,
"P": 53,
"T": 4597
},
{
"C": 2.4,
"G": 3321,
"P": 54.056,
"T": 4598
},
{
"C": 3.49,
"G": 3321,
"P": 57,
"T": 4599
}
]
],
"G": 3321
},
{
"E": [
[
{
"C": 3.42,
"G": 3323,
"P": 52,
"T": 4600
},
{
"C": 3.43,
"G": 3323,
"P": 53.054,
"T": 4601
},
{
"C": 3.75,
"G": 3323,
"P": 55.056,
"T": 4601
},
{
"C": 6.14,
"G": 3323,
"P": 57.058,
"T": 4601
},
{
"C": 11.5,
"G": 3323,
"P": 59,
"T": 4602
}
]
],
"G": 3323
},
{
"E": [
[
{
"C": 2.06,
"G": 3327,
"P": 29,
"T": 4606
},
{
"C": 2.59,
"G": 3327,
"P": 30.031,
"T": 4607
},
{
"C": 3.94,
"G": 3327,
"P": 32,
"T": 4608
}
]
],
"G": 3327
},
{
"E": [
[
{
"C": 1.75,
"G": 3331,
"P": 24,
"T": 4612
},
{
"C": 2.99,
"G": 3331,
"P": 25.026,
"T": 4613
},
{
"C": 5.43,
"G": 3331,
"P": 27,
"T": 4614
}
]
],
"G": 3331
}
],
"HSI": true,
"I": 264581794,
"KI": 1,
"L": "Qatar Championship",
"LI": 179407,
"LR": "Чемпионат Катара",
"MEC": [
{
"EC": 48,
"MT": 2
},
{
"EC": 38,
"MT": 3
},
{
"EC": 10,
"MT": 4
},
{
"EC": 0,
"MT": 5
},
{
"EC": 0,
"MT": 6
},
{
"EC": 0,
"MT": 7
},
{
"EC": 0,
"MT": 8
},
{
"EC": 0,
"MT": 9
},
{
"EC": 0,
"MT": 10
},
{
"EC": 0,
"MT": 11
},
{
"EC": 0,
"MT": 12
},
{
"EC": 0,
"MT": 13
},
{
"EC": 81,
"MT": 1
}
],
"MIO": {
"Loc": "Duhail Handball Sports Hall (Doha)"
},
"MIS": [
{
"K": 11,
"V": "Qatar"
},
{
"K": 2,
"V": "Duhail Handball Sports Hall (Doha)"
}
],
"MS": [
0
],
"N": 9328,
"O1": "Al Arabi Doha",
"O1C": 86,
"O1I": 55149,
"O1IMG": [
"55149.png"
],
"O1IS": [
55149
],
"O1R": "Аль Араби Доха",
"O2": "Al Reyyan",
"O2C": 86,
"O2I": 57115,
"O2IMG": [
"57115.png"
],
"O2IS": [
57115
],
"O2R": "Аль Рэйан",
"S": 1604322000,
"SI": 8,
"SN": "Handball",
"SR": "Гандбол",
"T": 6,
"TN": "Half",
"HMH": 1,
"R": 6,
"SC": {
"CP": 2,
"CPS": "2 Half",
"FS": {
"S1": 26,
"S2": 20
},
"HC": 1,
"I": "(5x5)",
"PS": [
{
"Key": 1,
"Value": {
"S1": 15,
"S2": 14
}
},
{
"Key": 2,
"Value": {
"S1": 11,
"S2": 6
}
}
],
"S": [
{
"Key": "Stat",
"Value": "4;52-3;48"
},
{
"Key": "Stat1",
"Value": "{\"Remtime\":\"3062\"}"
},
{
"Key": "Stat2",
"Value": "{\"Remtime\":\"2959\"}"
}
],
"ST": [
{
"Key": 0,
"Value": [
{
"ID": 29,
"N": null,
"S1": "52",
"S2": "48"
},
{
"ID": 21,
"N": null,
"S1": "7/7",
"S2": "3/5"
},
{
"ID": 22,
"N": null,
"S1": "4",
"S2": "6"
},
{
"ID": 24,
"N": null,
"S1": "8",
"S2": "3"
},
{
"ID": 25,
"N": null,
"S1": "4",
"S2": "3"
},
{
"ID": 28,
"N": null,
"S1": "4",
"S2": "3"
},
{
"ID": 46,
"N": null,
"S1": "30",
"S2": "22"
}
]
}
],
"TS": 3063
},
"ZP": 264579984
}
}

1595
examples/handball2.json Normal file

File diff suppressed because it is too large Load Diff

949
examples/lineups.json Executable file
View File

@@ -0,0 +1,949 @@
{
"LineupsHome": [
{
"PlayerId": "5abbaa62494765f3caa6caad",
"Name": "Yeray Alvarez",
"ShortName": "Yeray Alvarez",
"Line": 1,
"Position": 1,
"CountryId": 78,
"CountryTitle": "Spain",
"BirthDate": 790905600,
"TeamTitle": "Атлетик (Исп)",
"Num": 5,
"Type": 1,
"XbetId": 575301,
"Image": "575301.png",
"Events": [],
"PositionName": null
},
{
"PlayerId": "5abc9c69494765f3cac4aacf",
"Name": "Yuri Berchiche",
"ShortName": "Yuri Berchiche",
"Line": 1,
"Position": 3,
"CountryId": 78,
"CountryTitle": "Spain",
"BirthDate": 634608000,
"TeamTitle": "Атлетик (Исп)",
"Num": 17,
"Type": 1,
"XbetId": 459485,
"Image": "459485.png",
"Events": [],
"PositionName": null
},
{
"PlayerId": "5abf4ec7494765f3ca084114",
"Name": "Dani García",
"ShortName": "Dani García",
"Line": 2,
"Position": 0,
"CountryId": 78,
"CountryTitle": "Spain",
"BirthDate": 643507200,
"TeamTitle": "Атлетик (Исп)",
"Num": 14,
"Type": 1,
"XbetId": 453169,
"Image": "453169.png",
"Events": [],
"PositionName": null
},
{
"PlayerId": "5ab559a4494765f3ca57d103",
"Name": "Raúl García",
"ShortName": "Raúl García",
"Line": 4,
"Position": 0,
"CountryId": 78,
"CountryTitle": "Spain",
"BirthDate": 521424000,
"TeamTitle": "Атлетик (Исп)",
"Num": 22,
"Type": 1,
"XbetId": 466309,
"Image": "466309.png",
"Events": [],
"PositionName": null
},
{
"PlayerId": "5abd226b494765f3cae2d2ad",
"Name": "Ander Capa",
"ShortName": "Ander Capa",
"Line": 1,
"Position": 0,
"CountryId": 78,
"CountryTitle": "Spain",
"BirthDate": 705801600,
"TeamTitle": "Атлетик (Исп)",
"Num": 21,
"Type": 1,
"XbetId": 453177,
"Image": "ef3000952fd66572a753ac7516d1fdc9.png",
"Events": [],
"PositionName": null
},
{
"PlayerId": "5ab4079f494765f3ca479f08",
"Name": "Inigo Cordoba",
"ShortName": "Inigo Cordoba",
"Line": 3,
"Position": 2,
"CountryId": 78,
"CountryTitle": "Spain",
"BirthDate": 858211200,
"TeamTitle": "Атлетик (Исп)",
"Num": 11,
"Type": 1,
"XbetId": 2929287,
"Image": "f8501f704db692c22c9103775fd17224.png",
"Events": [],
"PositionName": null
},
{
"PlayerId": "5ab2b7d2494765f3ca38d5d9",
"Name": "Unai Lopez",
"ShortName": "Unai Lopez",
"Line": 2,
"Position": 1,
"CountryId": 78,
"CountryTitle": "Spain",
"BirthDate": 815011200,
"TeamTitle": "Атлетик (Исп)",
"Num": 8,
"Type": 1,
"XbetId": 852707,
"Image": "852707.png",
"Events": [],
"PositionName": null
},
{
"PlayerId": "5abbaa62494765f3caa6cab4",
"Name": "Inigo Martínez",
"ShortName": "Inigo Martínez",
"Line": 1,
"Position": 2,
"CountryId": 78,
"CountryTitle": "Spain",
"BirthDate": 674438400,
"TeamTitle": "Атлетик (Исп)",
"Num": 4,
"Type": 1,
"XbetId": 459487,
"Image": "459487.png",
"Events": [],
"PositionName": null
},
{
"PlayerId": "5abbaa63494765f3caa6cad6",
"Name": "Iker Muniain",
"ShortName": "Iker Muniain",
"Line": 3,
"Position": 1,
"CountryId": 78,
"CountryTitle": "Spain",
"BirthDate": 724723200,
"TeamTitle": "Атлетик (Исп)",
"Num": 10,
"Type": 1,
"XbetId": 433067,
"Image": "433067.png",
"Events": [
{
"Minute": "6",
"Type": 3,
"Player": "Iker Muniain",
"PlayerId": "5abbaa63494765f3caa6cad6",
"PlayerXbetId": 433067,
"PlayerImage": "433067.png",
"Assistant": null,
"AssistantId": "000000000000000000000000",
"AssistantXbetId": 0,
"AssistantImage": null,
"Note": null,
"TeamId": "5abbaa62494765f3caa6ca54",
"TeamLogoId": 0,
"PeriodType": 1,
"Images": null
},
{
"Minute": "37",
"Type": 1,
"Player": "Iker Muniain",
"PlayerId": "5abbaa63494765f3caa6cad6",
"PlayerXbetId": 433067,
"PlayerImage": "433067.png",
"Assistant": "Yuri Berchiche",
"AssistantId": "5abc9c69494765f3cac4aacf",
"AssistantXbetId": 459485,
"AssistantImage": "459485.png",
"Note": null,
"TeamId": "5abbaa62494765f3caa6ca54",
"TeamLogoId": 0,
"PeriodType": 1,
"Images": null
}
],
"PositionName": null
},
{
"PlayerId": "5ab3e79f494765f3ca45b39a",
"Name": "Unai Simon",
"ShortName": "Unai Simon",
"Line": 0,
"Position": 0,
"CountryId": 78,
"CountryTitle": "Spain",
"BirthDate": 868579200,
"TeamTitle": "Атлетик (Исп)",
"Num": 1,
"Type": 1,
"XbetId": 1055451,
"Image": "1055451.png",
"Events": [],
"PositionName": null
},
{
"PlayerId": "5ab3c555494765f3ca4368bd",
"Name": "Inaki Williams",
"ShortName": "Inaki Williams",
"Line": 3,
"Position": 0,
"CountryId": 78,
"CountryTitle": "Spain",
"BirthDate": 771638400,
"TeamTitle": "Атлетик (Исп)",
"Num": 9,
"Type": 1,
"XbetId": 466315,
"Image": "466315.png",
"Events": [
{
"Minute": "45+1",
"Type": 3,
"Player": "Inaki Williams",
"PlayerId": "5ab3c555494765f3ca4368bd",
"PlayerXbetId": 466315,
"PlayerImage": "466315.png",
"Assistant": null,
"AssistantId": "000000000000000000000000",
"AssistantXbetId": 0,
"AssistantImage": null,
"Note": null,
"TeamId": "5abbaa62494765f3caa6ca54",
"TeamLogoId": 0,
"PeriodType": 1,
"Images": null
}
],
"PositionName": null
},
{
"PlayerId": "5abbaa62494765f3caa6cab0",
"Name": "Баленсиага Микел",
"ShortName": "Баленсиага Микел",
"Line": 0,
"Position": 0,
"CountryId": 78,
"CountryTitle": "Spain",
"BirthDate": 573091200,
"TeamTitle": "Атлетик (Исп)",
"Num": 24,
"Type": 2,
"XbetId": 218529,
"Image": "218529.png",
"Events": [],
"PositionName": null
},
{
"PlayerId": "5abbaa62494765f3caa6cac6",
"Name": "Mikel Vesga",
"ShortName": "Mikel Vesga",
"Line": 0,
"Position": 0,
"CountryId": 78,
"CountryTitle": "Spain",
"BirthDate": 732672000,
"TeamTitle": "Атлетик (Исп)",
"Num": 16,
"Type": 2,
"XbetId": 568945,
"Image": "568945.png",
"Events": [],
"PositionName": null
},
{
"PlayerId": "5abbaa62494765f3caa6cab2",
"Name": "Oscar de Marcos",
"ShortName": "Oscar de Marcos",
"Line": 0,
"Position": 0,
"CountryId": 78,
"CountryTitle": "Spain",
"BirthDate": 608515200,
"TeamTitle": "Атлетик (Исп)",
"Num": 18,
"Type": 2,
"XbetId": 412139,
"Image": "412139.png",
"Events": [],
"PositionName": null
},
{
"PlayerId": "5abc9e1e494765f3cacf9116",
"Name": "Ibai Gomez",
"ShortName": "Ibai Gomez",
"Line": 0,
"Position": 0,
"CountryId": 78,
"CountryTitle": "Spain",
"BirthDate": 626745600,
"TeamTitle": "Атлетик (Исп)",
"Num": 19,
"Type": 2,
"XbetId": 493841,
"Image": "493841.png",
"Events": [],
"PositionName": null
},
{
"PlayerId": "5ab4b527494765f3ca4c7487",
"Name": "Kenan Kodro",
"ShortName": "Kenan Kodro",
"Line": 0,
"Position": 0,
"CountryId": 29,
"CountryTitle": "Bosnia and Herzegovina",
"BirthDate": 745718400,
"TeamTitle": "Атлетик (Исп)",
"Num": 23,
"Type": 2,
"XbetId": 403577,
"Image": "403577.png",
"Events": [],
"PositionName": null
},
{
"PlayerId": "5ab66fee494765f3ca64ac36",
"Name": "Gaizka Larrazabal",
"ShortName": "Gaizka Larrazabal",
"Line": 0,
"Position": 0,
"CountryId": 78,
"CountryTitle": "Spain",
"BirthDate": 882316800,
"TeamTitle": "Атлетик (Исп)",
"Num": 12,
"Type": 2,
"XbetId": 3023431,
"Image": "9d2ae3aaf86053bd6c6be05b9c4d80ee.png",
"Events": [],
"PositionName": null
},
{
"PlayerId": "5abbaa62494765f3caa6cacc",
"Name": "Lekue",
"ShortName": "Lekue",
"Line": 0,
"Position": 0,
"CountryId": 78,
"CountryTitle": "Spain",
"BirthDate": 736473600,
"TeamTitle": "Атлетик (Исп)",
"Num": 15,
"Type": 2,
"XbetId": 218899,
"Image": "218899.png",
"Events": [],
"PositionName": null
},
{
"PlayerId": "5ab3e79f494765f3ca45b38c",
"Name": "Unai Nunez",
"ShortName": "Unai Nunez",
"Line": 0,
"Position": 0,
"CountryId": 78,
"CountryTitle": "Spain",
"BirthDate": 854582400,
"TeamTitle": "Атлетик (Исп)",
"Num": 3,
"Type": 2,
"XbetId": 852675,
"Image": "852675.png",
"Events": [],
"PositionName": null
},
{
"PlayerId": "5abbaa62494765f3caa6caba",
"Name": "San Jose M.",
"ShortName": "San Jose M.",
"Line": 0,
"Position": 0,
"CountryId": 78,
"CountryTitle": "Spain",
"BirthDate": 612489600,
"TeamTitle": "Атлетик (Исп)",
"Num": 6,
"Type": 2,
"XbetId": 466303,
"Image": "466303.png",
"Events": [],
"PositionName": null
},
{
"PlayerId": "5b375c0db3cf6d8f7f7503c6",
"Name": "Oihan Sancet",
"ShortName": "Oihan Sancet",
"Line": 0,
"Position": 0,
"CountryId": 78,
"CountryTitle": "Spain",
"BirthDate": 956620800,
"TeamTitle": "Атлетик (Исп)",
"Num": 34,
"Type": 2,
"XbetId": 2894965,
"Image": "1efc11ce6d8913ebffa2318b98be6cfb.png",
"Events": [],
"PositionName": null
},
{
"PlayerId": "5abbaa62494765f3caa6caa4",
"Name": "Iago Herrerin",
"ShortName": "Iago Herrerin",
"Line": 0,
"Position": 0,
"CountryId": 78,
"CountryTitle": "Spain",
"BirthDate": 570067200,
"TeamTitle": "Атлетик (Исп)",
"Num": 13,
"Type": 2,
"XbetId": 634267,
"Image": "634267.png",
"Events": [],
"PositionName": null
},
{
"PlayerId": "5abbaa62494765f3caa6cac4",
"Name": "Benat Etxebarria",
"ShortName": "Benat Etxebarria",
"Line": 0,
"Position": 0,
"CountryId": 78,
"CountryTitle": "Spain",
"BirthDate": 540691200,
"TeamTitle": "Атлетик (Исп)",
"Num": 7,
"Type": 2,
"XbetId": 537509,
"Image": "537509.png",
"Events": [],
"PositionName": null
},
{
"PlayerId": "5abf4ec2494765f3ca081a15",
"Name": "Gaizka Garitano",
"ShortName": "Gaizka Garitano",
"Line": 0,
"Position": 0,
"CountryId": 78,
"CountryTitle": "Spain",
"BirthDate": 174096000,
"TeamTitle": "Атлетик (Исп)",
"Num": 0,
"Type": 3,
"XbetId": 0,
"Image": "",
"Events": [],
"PositionName": null
}
],
"MissingHome": [
{
"PlayerId": "5ab64029494765f3ca608d87",
"Name": "Asier Villalibre",
"ShortName": "Asier Villalibre",
"CountryId": 78,
"CountryTitle": "Spain",
"BirthDate": 875577600,
"TeamTitle": "Атлетик (Исп)",
"Status": null,
"Reason": 21,
"XbetId": 679221,
"Image": "c9227d43b91bc763e12d6c45acc296d9.png"
}
],
"LineupsAway": [
{
"PlayerId": "5ab53381494765f3ca552586",
"Name": "Yannick Ferreira Carrasco",
"ShortName": "Yannick Ferreira Carrasco",
"Line": 3,
"Position": 2,
"CountryId": 24,
"CountryTitle": "Belgium",
"BirthDate": 747100800,
"TeamTitle": "",
"Num": 21,
"Type": 1,
"XbetId": 0,
"Image": "",
"Events": [],
"PositionName": null
},
{
"PlayerId": "5ab52491494765f3ca53f776",
"Name": "Koke",
"ShortName": "Koke",
"Line": 3,
"Position": 0,
"CountryId": 78,
"CountryTitle": "Spain",
"BirthDate": 694828800,
"TeamTitle": "",
"Num": 6,
"Type": 1,
"XbetId": 2233673,
"Image": "68c28c9594a8367beba19c574d63e780.png",
"Events": [],
"PositionName": null
},
{
"PlayerId": "5ab5247c494765f3ca53f284",
"Name": "Коста Диего",
"ShortName": "Коста Диего",
"Line": 4,
"Position": 0,
"CountryId": 78,
"CountryTitle": "Spain",
"BirthDate": 592185600,
"TeamTitle": "",
"Num": 19,
"Type": 1,
"XbetId": 452297,
"Image": "5fe3cff41b9f941ad3a0a8c760176d27.png",
"Events": [
{
"Minute": "39",
"Type": 1,
"Player": "Коста Диего",
"PlayerId": "5ab5247c494765f3ca53f284",
"PlayerXbetId": 452297,
"PlayerImage": "5fe3cff41b9f941ad3a0a8c760176d27.png",
"Assistant": "Koke",
"AssistantId": "5ab52491494765f3ca53f776",
"AssistantXbetId": 2233673,
"AssistantImage": "68c28c9594a8367beba19c574d63e780.png",
"Note": null,
"TeamId": "5abc9b86494765f3cabea3af",
"TeamLogoId": 0,
"PeriodType": 1,
"Images": null
}
],
"PositionName": null
},
{
"PlayerId": "5ab3c555494765f3ca4368b7",
"Name": "Marcos Llorente",
"ShortName": "Marcos Llorente",
"Line": 3,
"Position": 1,
"CountryId": 78,
"CountryTitle": "Spain",
"BirthDate": 791424000,
"TeamTitle": "Атлетико (Исп)",
"Num": 14,
"Type": 1,
"XbetId": 464855,
"Image": "fb790ca34dc72266d5fa1ac506e77f94.png",
"Events": [],
"PositionName": null
},
{
"PlayerId": "5ab3e7a3494765f3ca45b4ba",
"Name": "Saul Niguez",
"ShortName": "Saul Niguez",
"Line": 2,
"Position": 1,
"CountryId": 78,
"CountryTitle": "Spain",
"BirthDate": 785376000,
"TeamTitle": "Атлетико (Исп)",
"Num": 8,
"Type": 1,
"XbetId": 334173,
"Image": "24e96150526bd4b05ebc9cddad1d80b3.png",
"Events": [],
"PositionName": null
},
{
"PlayerId": "5ab3751e494765f3ca3e5e8f",
"Name": "Jan Oblak",
"ShortName": "Jan Oblak",
"Line": 0,
"Position": 0,
"CountryId": 172,
"CountryTitle": "Slovenia",
"BirthDate": 726364800,
"TeamTitle": "Атлетико (Исп)",
"Num": 13,
"Type": 1,
"XbetId": 334167,
"Image": "140fcbcbfd099f9e30a4e1bdf7a8a5d8.png",
"Events": [],
"PositionName": null
},
{
"PlayerId": "5ab132a4494765f3ca259a07",
"Name": "Renan Augusto Lodi dos Santos",
"ShortName": "Renan Augusto Lodi dos Santos",
"Line": 1,
"Position": 3,
"CountryId": 31,
"CountryTitle": "Brazil",
"BirthDate": 902534400,
"TeamTitle": "Clube Atletico Paranaense",
"Num": 12,
"Type": 1,
"XbetId": 1525815,
"Image": "1f2b5d4393f60fba30fea21d6ab47fdd.png",
"Events": [],
"PositionName": null
},
{
"PlayerId": "5ab3fe7c494765f3ca471887",
"Name": "Stefan Savic",
"ShortName": "Stefan Savic",
"Line": 1,
"Position": 2,
"CountryId": 203,
"CountryTitle": "Montenegro",
"BirthDate": 663292800,
"TeamTitle": "Атлетико (Исп)",
"Num": 15,
"Type": 1,
"XbetId": 455355,
"Image": "606c7e7ece90cf5cd57e1ccb7828158d.png",
"Events": [],
"PositionName": null
},
{
"PlayerId": "5abc9c69494765f3cac4a9f1",
"Name": "Thomas Partey",
"ShortName": "Thomas Partey",
"Line": 2,
"Position": 0,
"CountryId": 48,
"CountryTitle": "Ghana",
"BirthDate": 739929600,
"TeamTitle": "Атлетико (Исп)",
"Num": 5,
"Type": 1,
"XbetId": 646425,
"Image": "d27fe03eb47da099b98ae418d05075f1.png",
"Events": [],
"PositionName": null
},
{
"PlayerId": "5ab132b5494765f3ca259ea8",
"Name": "Kieran Trippier",
"ShortName": "Kieran Trippier",
"Line": 1,
"Position": 0,
"CountryId": 231,
"CountryTitle": "England",
"BirthDate": 653702400,
"TeamTitle": "Атлетико (Исп)",
"Num": 23,
"Type": 1,
"XbetId": 383039,
"Image": "d58819ad0dcfc9e27e156e2bfff8bed8.png",
"Events": [],
"PositionName": null
},
{
"PlayerId": "5ab4d6a9494765f3ca4ea955",
"Name": "Jose Gimenez",
"ShortName": "Jose Gimenez",
"Line": 1,
"Position": 1,
"CountryId": 193,
"CountryTitle": "Uruguay",
"BirthDate": 790560000,
"TeamTitle": "",
"Num": 2,
"Type": 1,
"XbetId": 549505,
"Image": "549505.png",
"Events": [],
"PositionName": null
},
{
"PlayerId": "5abc9d62494765f3cacb85c1",
"Name": "Адан А. (В) (C",
"ShortName": "Адан А. (В) (C",
"Line": 0,
"Position": 0,
"CountryId": 78,
"CountryTitle": "Spain",
"BirthDate": 547862400,
"TeamTitle": "Атлетико (Исп)",
"Num": 1,
"Type": 2,
"XbetId": 456257,
"Image": "77813ea0d7edc183c819fa44ebec8c8f.png",
"Events": [],
"PositionName": null
},
{
"PlayerId": "5ab4b528494765f3ca4c7d9f",
"Name": "Santiago Arias",
"ShortName": "Santiago Arias",
"Line": 0,
"Position": 0,
"CountryId": 91,
"CountryTitle": "Colombia",
"BirthDate": 695260800,
"TeamTitle": "",
"Num": 4,
"Type": 2,
"XbetId": 468061,
"Image": "74db1420ef48a66b27589402d45ec069.png",
"Events": [],
"PositionName": null
},
{
"PlayerId": "5ab25850494765f3ca326157",
"Name": "Sergio Camello",
"ShortName": "Sergio Camello",
"Line": 0,
"Position": 0,
"CountryId": 78,
"CountryTitle": "Spain",
"BirthDate": 981763200,
"TeamTitle": "Атлетико (Исп)",
"Num": 34,
"Type": 2,
"XbetId": 1004059,
"Image": "1004059.png",
"Events": [],
"PositionName": null
},
{
"PlayerId": "5ab4e64f494765f3ca4fa6e3",
"Name": "Angel Correa",
"ShortName": "Angel Correa",
"Line": 0,
"Position": 0,
"CountryId": 14,
"CountryTitle": "Argentina",
"BirthDate": 794707200,
"TeamTitle": "Атлетико (Исп)",
"Num": 10,
"Type": 2,
"XbetId": 549503,
"Image": "8f09e835b731fb4d5351866967b7d40e.png",
"Events": [],
"PositionName": null
},
{
"PlayerId": "5ab3e13f494765f3ca4541a0",
"Name": "Thomas Lemar",
"ShortName": "Thomas Lemar",
"Line": 0,
"Position": 0,
"CountryId": 198,
"CountryTitle": "France",
"BirthDate": 816134400,
"TeamTitle": "Атлетико (Исп)",
"Num": 11,
"Type": 2,
"XbetId": 397403,
"Image": "d470efe134fb2f6e76a9c865c11c6cdf.png",
"Events": [],
"PositionName": null
},
{
"PlayerId": "5abd3ae3494765f3cae4b18a",
"Name": "Toni Moya",
"ShortName": "Toni Moya",
"Line": 0,
"Position": 0,
"CountryId": 78,
"CountryTitle": "Spain",
"BirthDate": 890352000,
"TeamTitle": "Атлетико (Исп)",
"Num": 38,
"Type": 2,
"XbetId": 1843345,
"Image": "1843345.png",
"Events": [],
"PositionName": null
},
{
"PlayerId": "5ab2a7af494765f3ca37e56d",
"Name": "Alvaro Morata",
"ShortName": "Alvaro Morata",
"Line": 0,
"Position": 0,
"CountryId": 78,
"CountryTitle": "Spain",
"BirthDate": 719798400,
"TeamTitle": "Атлетико (Исп)",
"Num": 9,
"Type": 2,
"XbetId": 331073,
"Image": "f05a91c0c34553d126bb342e4ec8ca5a.png",
"Events": [],
"PositionName": null
},
{
"PlayerId": "5abc9b36494765f3cabcd5b4",
"Name": "Rodrigo Riquelme",
"ShortName": "Rodrigo Riquelme",
"Line": 0,
"Position": 0,
"CountryId": 78,
"CountryTitle": "Spain",
"BirthDate": 954633600,
"TeamTitle": "Атлетико (Исп)",
"Num": 32,
"Type": 2,
"XbetId": 1993053,
"Image": "1993053.png",
"Events": [],
"PositionName": null
},
{
"PlayerId": "5ba0e52db3cf6d8f7f21bb67",
"Name": "Manuel Sanchez",
"ShortName": "Manuel Sanchez",
"Line": 0,
"Position": 0,
"CountryId": 78,
"CountryTitle": "Spain",
"BirthDate": 967075200,
"TeamTitle": "Атлетико (Исп)",
"Num": 35,
"Type": 2,
"XbetId": 2156443,
"Image": "2156443.png",
"Events": [],
"PositionName": null
},
{
"PlayerId": "5ab4fa7a494765f3ca51059f",
"Name": "Ivan Saponjic",
"ShortName": "Ivan Saponjic",
"Line": 0,
"Position": 0,
"CountryId": 168,
"CountryTitle": "Serbia",
"BirthDate": 870480000,
"TeamTitle": "Атлетико (Исп)",
"Num": 17,
"Type": 2,
"XbetId": 797333,
"Image": "797333.png",
"Events": [],
"PositionName": null
},
{
"PlayerId": "5ab4b528494765f3ca4c7ad2",
"Name": "Mario Hermoso",
"ShortName": "Mario Hermoso",
"Line": 0,
"Position": 0,
"CountryId": 78,
"CountryTitle": "Spain",
"BirthDate": 803433600,
"TeamTitle": "Атлетико (Исп)",
"Num": 22,
"Type": 2,
"XbetId": 846783,
"Image": "603db8f5c1c8fb5d04f32eb49d30cc95.png",
"Events": [],
"PositionName": null
},
{
"PlayerId": "5ab5247a494765f3ca53f1d5",
"Name": "Hector Herrera",
"ShortName": "Hector Herrera",
"Line": 0,
"Position": 0,
"CountryId": 120,
"CountryTitle": "Mexico",
"BirthDate": 640483200,
"TeamTitle": "",
"Num": 16,
"Type": 2,
"XbetId": 454633,
"Image": "dc66f90fee3726ffd99a70f4a6ff6dec.png",
"Events": [],
"PositionName": null
},
{
"PlayerId": "5abc9e75494765f3cad121b5",
"Name": "Diego Simeone",
"ShortName": "Diego Simeone",
"Line": 0,
"Position": 0,
"CountryId": 14,
"CountryTitle": "Argentina",
"BirthDate": 10108800,
"TeamTitle": "Атлетико (Исп)",
"Num": 0,
"Type": 3,
"XbetId": 1843335,
"Image": "1843335.png",
"Events": [],
"PositionName": null
}
],
"MissingAway": [
{
"PlayerId": "5ab52477494765f3ca53f0b7",
"Name": "Vitolo",
"ShortName": "Vitolo",
"CountryId": 78,
"CountryTitle": "Spain",
"BirthDate": 625968000,
"TeamTitle": "Атлетико (Исп)",
"Status": null,
"Reason": 17,
"XbetId": 92195,
"Image": "c3ec74cc2d1cca0a6af3f97f7f57854c.png"
},
{
"PlayerId": "5ab52491494765f3ca53f727",
"Name": "Sime Vrsaljko",
"ShortName": "Sime Vrsaljko",
"CountryId": 201,
"CountryTitle": "Croatia",
"BirthDate": 695001600,
"TeamTitle": "",
"Status": null,
"Reason": 9,
"XbetId": 331043,
"Image": "74c623500f1ef90fe9fee698cd37a186.png"
},
{
"PlayerId": "5ab26523494765f3ca333768",
"Name": "Joao Felix Sequeira",
"ShortName": "Joao Felix Sequeira",
"CountryId": 148,
"CountryTitle": "Portugal",
"BirthDate": 0,
"TeamTitle": "Martinkevich Z. (Блр)",
"Status": null,
"Reason": 9,
"XbetId": 662845,
"Image": "4b0b2aaf191a6fab7360076d46faaf7e.png"
}
]
}

1274
examples/live-match-sg-data.json Executable file

File diff suppressed because it is too large Load Diff

2291
examples/live-match-sg-links.json Executable file

File diff suppressed because it is too large Load Diff

2427
examples/live-match.json Executable file

File diff suppressed because it is too large Load Diff

2160
examples/live-math-sg-links2.json Executable file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,721 @@
{
"Error": "",
"ErrorCode": 0,
"Guid": "",
"Id": 0,
"Success": true,
"Value": {
"BIG": [
{
"I": 295536788,
"PN": "3 Set"
}
],
"CHIMG": "251ff8af4272c6b5988404b5d39bb8de.png",
"CID": 1,
"CN": "World",
"CO": 50,
"COI": 225,
"EC": 73,
"EGC": 11,
"GE": [
{
"E": [
[
{
"C": 7.1,
"G": 1,
"T": 1
}
],
[
{
"C": 1.07,
"G": 1,
"T": 3
}
]
],
"G": 1
},
{
"E": [
[
{
"C": 1.064,
"G": 17,
"P": 6.5,
"T": 9
},
{
"C": 1.26,
"G": 17,
"P": 7.5,
"T": 9
},
{
"C": 1.98,
"CE": 1,
"G": 17,
"P": 8.5,
"T": 9
},
{
"C": 2.595,
"G": 17,
"P": 9.5,
"T": 9
},
{
"C": 5.99,
"G": 17,
"P": 10.5,
"T": 9
},
{
"C": 10.8,
"G": 17,
"P": 12.5,
"T": 9
}
],
[
{
"C": 7.02,
"G": 17,
"P": 6.5,
"T": 10
},
{
"C": 3.46,
"G": 17,
"P": 7.5,
"T": 10
},
{
"C": 1.755,
"CE": 1,
"G": 17,
"P": 8.5,
"T": 10
},
{
"C": 1.435,
"G": 17,
"P": 9.5,
"T": 10
},
{
"C": 1.096,
"G": 17,
"P": 10.5,
"T": 10
},
{
"C": 1.01,
"G": 17,
"P": 12.5,
"T": 10
}
]
],
"G": 17
},
{
"E": [
[
{
"C": 5.39,
"G": 2,
"P": 1.5,
"T": 7
},
{
"C": 2.496,
"G": 2,
"P": 2.5,
"T": 7
},
{
"C": 1.98,
"CE": 1,
"G": 2,
"P": 3.5,
"T": 7
},
{
"C": 1.26,
"G": 2,
"P": 4.5,
"T": 7
},
{
"C": 1.064,
"G": 2,
"P": 5.5,
"T": 7
}
],
[
{
"C": 1.115,
"G": 2,
"P": -1.5,
"T": 8
},
{
"C": 1.47,
"G": 2,
"P": -2.5,
"T": 8
},
{
"C": 1.755,
"CE": 1,
"G": 2,
"P": -3.5,
"T": 8
},
{
"C": 3.46,
"G": 2,
"P": -4.5,
"T": 8
},
{
"C": 7.02,
"G": 2,
"P": -5.5,
"T": 8
}
]
],
"G": 2
},
{
"E": [
[
{
"C": 1.968,
"CE": 1,
"G": 15,
"P": 2.5,
"T": 11
},
{
"C": 2.496,
"G": 15,
"P": 3.5,
"T": 11
}
],
[
{
"C": 1.744,
"CE": 1,
"G": 15,
"P": 2.5,
"T": 12
},
{
"C": 1.47,
"G": 15,
"P": 3.5,
"T": 12
}
]
],
"G": 15
},
{
"E": [
[
{
"C": 15.8,
"G": 136,
"P": 6.003,
"T": 731
},
{
"C": 15.8,
"G": 136,
"P": 6.004,
"T": 731
},
{
"C": 15.8,
"G": 136,
"P": 7.005,
"T": 731
},
{
"C": 15.8,
"G": 136,
"P": 7.006,
"T": 731
}
],
[
{
"C": 5.74,
"G": 136,
"P": 0.006,
"T": 731
},
{
"C": 5.58,
"G": 136,
"P": 1.006,
"T": 731
},
{
"C": 2.925,
"G": 136,
"P": 2.006,
"T": 731
},
{
"C": 7.54,
"G": 136,
"P": 3.006,
"T": 731
},
{
"C": 4.53,
"G": 136,
"P": 4.006,
"T": 731
},
{
"C": 15.8,
"G": 136,
"P": 5.007,
"T": 731
},
{
"C": 15.8,
"G": 136,
"P": 6.007,
"T": 731
}
]
],
"G": 136
},
{
"E": [
[
{
"C": 1.39,
"G": 14,
"T": 182
}
],
[
{
"C": 2.76,
"G": 14,
"T": 183
}
]
],
"G": 14
},
{
"E": [
[
{
"C": 2.62,
"G": 22,
"P": 4,
"T": 50
},
{
"C": 1.35,
"G": 22,
"P": 5,
"T": 50
}
],
[
{
"C": 1.43,
"G": 22,
"P": 4,
"T": 51
},
{
"C": 2.94,
"G": 22,
"P": 5,
"T": 51
}
]
],
"G": 22
},
{
"E": [
[
{
"C": 3.01,
"G": 57,
"P": 4,
"T": 538
},
{
"C": 3.095,
"G": 57,
"P": 5,
"T": 538
}
],
[
{
"C": 1.285,
"G": 57,
"P": 4,
"T": 539
},
{
"C": 1.27,
"G": 57,
"P": 5,
"T": 539
}
]
],
"G": 57
},
{
"E": [
[
{
"C": 2.112,
"G": 135,
"P": 2.004,
"T": 1794
},
{
"C": 2.112,
"G": 135,
"P": 3.004,
"T": 1794
},
{
"C": 2.112,
"G": 135,
"P": 4.004,
"T": 1794
},
{
"C": 1.615,
"G": 135,
"P": 1.005,
"T": 1794
},
{
"C": 1.615,
"G": 135,
"P": 2.005,
"T": 1794
},
{
"C": 1.615,
"G": 135,
"P": 3.005,
"T": 1794
},
{
"C": 1.615,
"G": 135,
"P": 4.005,
"T": 1794
}
],
[
{
"C": 1.66,
"G": 135,
"P": 2.004,
"T": 1795
},
{
"C": 1.66,
"G": 135,
"P": 3.004,
"T": 1795
},
{
"C": 1.66,
"G": 135,
"P": 4.004,
"T": 1795
},
{
"C": 2.195,
"G": 135,
"P": 1.005,
"T": 1795
},
{
"C": 2.195,
"G": 135,
"P": 2.005,
"T": 1795
},
{
"C": 2.195,
"G": 135,
"P": 3.005,
"T": 1795
},
{
"C": 2.195,
"G": 135,
"P": 4.005,
"T": 1795
}
]
],
"G": 135
},
{
"E": [
[
{
"C": 1.21,
"G": 3213,
"P": 4,
"T": 4393
},
{
"C": 1.22,
"G": 3213,
"P": 5,
"T": 4393
}
],
[
{
"C": 3.5,
"G": 3213,
"P": 4,
"T": 4394
},
{
"C": 3.44,
"G": 3213,
"P": 5,
"T": 4394
}
]
],
"G": 3213
},
{
"E": [
[
{
"C": 1.26,
"G": 10447,
"P": 2,
"T": 13780
},
{
"C": 1.968,
"G": 10447,
"P": 3,
"T": 13780
},
{
"C": 2.595,
"G": 10447,
"P": 4,
"T": 13780
}
],
[
{
"C": 3.46,
"G": 10447,
"P": 2,
"T": 13781
},
{
"C": 1.744,
"G": 10447,
"P": 3,
"T": 13781
},
{
"C": 1.435,
"G": 10447,
"P": 4,
"T": 13781
}
]
],
"G": 10447
}
],
"I": 295536788,
"KI": 1,
"L": "Russia. Masters",
"LE": "Russia. Masters",
"LI": 1877467,
"LR": "Россия. Мастерс",
"MEC": [
{
"EC": 0,
"MT": 2
},
{
"EC": 18,
"MT": 3
},
{
"EC": 0,
"MT": 4
},
{
"EC": 0,
"MT": 5
},
{
"EC": 0,
"MT": 6
},
{
"EC": 0,
"MT": 7
},
{
"EC": 0,
"MT": 8
},
{
"EC": 0,
"MT": 9
},
{
"EC": 0,
"MT": 10
},
{
"EC": 8,
"MT": 11
},
{
"EC": 0,
"MT": 12
},
{
"EC": 0,
"MT": 13
},
{
"EC": 73,
"MT": 1
}
],
"MG": 295524958,
"MIO": {
"MaF": "5 sets"
},
"MIS": [
{
"K": 3,
"V": "5 sets"
},
{
"K": 10,
"V": "Hard"
}
],
"MS": [
0
],
"N": 26258,
"O1": "Dmitriy Khalyapin",
"O1C": 1,
"O1E": "Dmitriy Khalyapin",
"O1I": 4808537,
"O1IMG": [
"033b86b04b4e386f59d8afab8b1874de.png"
],
"O1IS": [
4808537
],
"O1R": "Дмитрий Халяпин",
"O2": "Vladimir Gunko",
"O2C": 1,
"O2E": "Vladimir Gunko",
"O2I": 4702045,
"O2IMG": [
"e011b488505d0e9bb1bb7d2b98d3b043.png"
],
"O2IS": [
4702045
],
"O2R": "Владимир Гунько",
"P": 3,
"PN": "3 Set",
"S": 1618616754,
"SE": "Tennis",
"SGI": "607a2235f75a663f69fdaeb5",
"SI": 4,
"SN": "Tennis",
"SR": "Теннис",
"SS": 3,
"STI": "5ff0075bf75a663f69dc1194",
"T": 99,
"TN": "Set",
"AM": true,
"HMH": 1,
"OuR": true,
"R": 99,
"SC": {
"CP": 3,
"CPS": "3 Set",
"FS": {
"S1": 1,
"S2": 1
},
"P": 2,
"PS": [
{
"Key": 1,
"Value": {
"S1": 5,
"S2": 7
}
},
{
"Key": 2,
"Value": {
"S1": 6,
"S2": 4
}
},
{
"Key": 3,
"Value": {
"S2": 3
}
}
],
"S": [],
"SS": {
"S1": "0",
"S2": "0"
},
"TD": -1,
"TR": -1
},
"SVoAP": true,
"VA": 1,
"VI": "5305152",
"ZP": 547307
}
}

View File

@@ -0,0 +1,945 @@
{
"Error": "",
"ErrorCode": 0,
"Guid": "",
"Id": 0,
"Success": true,
"Value": {
"CHIMG": "251ff8af4272c6b5988404b5d39bb8de.png",
"CID": 1,
"CN": "World",
"CO": 50,
"COI": 225,
"EC": 18,
"EGC": 4,
"GE": [
{
"E": [
[
{
"C": 2.225,
"G": 1,
"T": 1
}
],
[
{
"C": 1.57,
"G": 1,
"T": 3
}
]
],
"G": 1
},
{
"E": [
[
{
"C": 1.77,
"G": 17,
"P": 43.5,
"T": 9
},
{
"C": 1.875,
"CE": 1,
"G": 17,
"P": 44.5,
"T": 9
}
],
[
{
"C": 1.98,
"G": 17,
"P": 43.5,
"T": 10
},
{
"C": 1.845,
"CE": 1,
"G": 17,
"P": 44.5,
"T": 10
}
]
],
"G": 17
},
{
"E": [
[
{
"C": 2.24,
"G": 2,
"P": 0.5,
"T": 7
},
{
"C": 2.01,
"G": 2,
"P": 1.5,
"T": 7
},
{
"C": 1.8,
"CE": 1,
"G": 2,
"P": 2.5,
"T": 7
}
],
[
{
"C": 1.605,
"G": 2,
"P": -0.5,
"T": 8
},
{
"C": 1.75,
"G": 2,
"P": -1.5,
"T": 8
},
{
"C": 1.925,
"CE": 1,
"G": 2,
"P": -2.5,
"T": 8
}
]
],
"G": 2
},
{
"E": [
[
{
"B": true,
"C": 12,
"G": 952,
"P": 0.03,
"T": 1964
},
{
"C": 2.55,
"G": 952,
"P": 100.03,
"T": 1964
},
{
"C": 3.72,
"G": 952,
"P": 200.03,
"T": 1964
},
{
"B": true,
"C": 12,
"G": 952,
"P": 300,
"T": 1964
},
{
"C": 6.92,
"G": 952,
"P": 300.01,
"T": 1964
},
{
"C": 3.025,
"G": 952,
"P": 300.02,
"T": 1964
}
]
],
"G": 952
}
],
"I": 295524958,
"KI": 1,
"L": "Russia. Masters",
"LE": "Russia. Masters",
"LI": 1877467,
"LR": "Россия. Мастерс",
"MEC": [
{
"EC": 0,
"MT": 2
},
{
"EC": 4,
"MT": 3
},
{
"EC": 0,
"MT": 4
},
{
"EC": 0,
"MT": 5
},
{
"EC": 0,
"MT": 6
},
{
"EC": 0,
"MT": 7
},
{
"EC": 0,
"MT": 8
},
{
"EC": 0,
"MT": 9
},
{
"EC": 0,
"MT": 10
},
{
"EC": 0,
"MT": 11
},
{
"EC": 0,
"MT": 12
},
{
"EC": 0,
"MT": 13
},
{
"EC": 18,
"MT": 1
}
],
"MIO": {
"MaF": "5 sets"
},
"MIS": [
{
"K": 3,
"V": "5 sets"
},
{
"K": 10,
"V": "Hard"
}
],
"MS": [
0
],
"N": 26255,
"O1": "Dmitriy Khalyapin",
"O1C": 1,
"O1E": "Dmitriy Khalyapin",
"O1I": 4808537,
"O1IMG": [
"033b86b04b4e386f59d8afab8b1874de.png"
],
"O1IS": [
4808537
],
"O1R": "Дмитрий Халяпин",
"O2": "Vladimir Gunko",
"O2C": 1,
"O2E": "Vladimir Gunko",
"O2I": 4702045,
"O2IMG": [
"e011b488505d0e9bb1bb7d2b98d3b043.png"
],
"O2IS": [
4702045
],
"O2R": "Владимир Гунько",
"S": 1618616754,
"SE": "Tennis",
"SGI": "607a2235f75a663f69fdaeb5",
"SI": 4,
"SN": "Tennis",
"SR": "Теннис",
"SS": 3,
"STI": "5ff0075bf75a663f69dc1194",
"T": 99,
"TN": "Set",
"AM": true,
"HMH": 1,
"OuR": true,
"R": 99,
"SC": {
"CP": 3,
"CPS": "3 Set",
"FS": {
"S1": 1,
"S2": 1
},
"P": 1,
"PS": [
{
"Key": 1,
"Value": {
"S1": 5,
"S2": 7
}
},
{
"Key": 2,
"Value": {
"S1": 6,
"S2": 4
}
},
{
"Key": 3,
"Value": {
"S2": 2
}
}
],
"S": [],
"SS": {
"S1": "15",
"S2": "30"
},
"TR": -1
},
"SG": [
{
"EC": 74,
"EGC": 12,
"GE": [
{
"E": [
[
{
"C": 3.95,
"G": 1,
"T": 1
}
],
[
{
"C": 1.195,
"G": 1,
"T": 3
}
]
],
"G": 1
},
{
"E": [
[
{
"C": 1.001,
"G": 17,
"P": 6.5,
"T": 9
},
{
"C": 1.104,
"G": 17,
"P": 7.5,
"T": 9
},
{
"C": 1.49,
"G": 17,
"P": 8.5,
"T": 9
},
{
"C": 1.992,
"CE": 1,
"G": 17,
"P": 9.5,
"T": 9
},
{
"C": 4.29,
"G": 17,
"P": 10.5,
"T": 9
},
{
"C": 7.74,
"G": 17,
"P": 12.5,
"T": 9
}
],
[
{
"C": 13.2,
"G": 17,
"P": 6.5,
"T": 10
},
{
"C": 5.71,
"G": 17,
"P": 7.5,
"T": 10
},
{
"C": 2.435,
"G": 17,
"P": 8.5,
"T": 10
},
{
"C": 1.744,
"CE": 1,
"G": 17,
"P": 9.5,
"T": 10
},
{
"C": 1.18,
"G": 17,
"P": 10.5,
"T": 10
},
{
"C": 1.05,
"G": 17,
"P": 12.5,
"T": 10
}
]
],
"G": 17
},
{
"E": [
[
{
"C": 5.36,
"G": 2,
"P": -1.5,
"T": 7
},
{
"C": 3.168,
"G": 2,
"P": 1.5,
"T": 7
},
{
"C": 1.768,
"CE": 1,
"G": 2,
"P": 2.5,
"T": 7
},
{
"C": 1.47,
"G": 2,
"P": 3.5,
"T": 7
},
{
"C": 1.104,
"G": 2,
"P": 4.5,
"T": 7
}
],
[
{
"C": 1.12,
"G": 2,
"P": 1.5,
"T": 8
},
{
"C": 1.305,
"G": 2,
"P": -1.5,
"T": 8
},
{
"C": 1.965,
"CE": 1,
"G": 2,
"P": -2.5,
"T": 8
},
{
"C": 2.5,
"G": 2,
"P": -3.5,
"T": 8
},
{
"C": 5.71,
"G": 2,
"P": -4.5,
"T": 8
}
]
],
"G": 2
},
{
"E": [
[
{
"C": 1.47,
"G": 15,
"P": 2.5,
"T": 11
},
{
"C": 1.76,
"CE": 1,
"G": 15,
"P": 3.5,
"T": 11
}
],
[
{
"C": 2.5,
"G": 15,
"P": 2.5,
"T": 12
},
{
"C": 1.952,
"CE": 1,
"G": 15,
"P": 3.5,
"T": 12
}
]
],
"G": 15
},
{
"E": [
[
{
"C": 1.05,
"G": 62,
"P": 4.5,
"T": 13
},
{
"C": 1.12,
"CE": 1,
"G": 62,
"P": 5.5,
"T": 13
}
],
[
{
"C": 7.67,
"G": 62,
"P": 4.5,
"T": 14
},
{
"C": 5.36,
"CE": 1,
"G": 62,
"P": 5.5,
"T": 14
}
]
],
"G": 62
},
{
"E": [
[
{
"C": 15.8,
"G": 136,
"P": 6.002,
"T": 731
},
{
"C": 15,
"G": 136,
"P": 6.003,
"T": 731
},
{
"C": 12.3,
"G": 136,
"P": 6.004,
"T": 731
},
{
"C": 14.3,
"G": 136,
"P": 7.005,
"T": 731
},
{
"C": 11.9,
"G": 136,
"P": 7.006,
"T": 731
}
],
[
{
"C": 10.7,
"G": 136,
"P": 0.006,
"T": 731
},
{
"C": 8.2,
"G": 136,
"P": 1.006,
"T": 731
},
{
"C": 3.66,
"G": 136,
"P": 2.006,
"T": 731
},
{
"C": 7.27,
"G": 136,
"P": 3.006,
"T": 731
},
{
"C": 4,
"G": 136,
"P": 4.006,
"T": 731
},
{
"C": 15.8,
"G": 136,
"P": 5.007,
"T": 731
},
{
"C": 13.2,
"G": 136,
"P": 6.007,
"T": 731
}
]
],
"G": 136
},
{
"E": [
[
{
"C": 1.456,
"G": 14,
"T": 182
}
],
[
{
"C": 2.536,
"G": 14,
"T": 183
}
]
],
"G": 14
},
{
"E": [
[
{
"C": 1.984,
"G": 22,
"P": 3,
"T": 50
},
{
"C": 2.62,
"G": 22,
"P": 4,
"T": 50
}
],
[
{
"C": 1.735,
"G": 22,
"P": 3,
"T": 51
},
{
"C": 1.43,
"G": 22,
"P": 4,
"T": 51
}
]
],
"G": 22
},
{
"E": [
[
{
"C": 2.13,
"G": 57,
"P": 3,
"T": 538
},
{
"C": 3.01,
"G": 57,
"P": 4,
"T": 538
}
],
[
{
"C": 1.56,
"G": 57,
"P": 3,
"T": 539
},
{
"C": 1.285,
"G": 57,
"P": 4,
"T": 539
}
]
],
"G": 57
},
{
"E": [
[
{
"C": 1.615,
"G": 135,
"P": 5.003,
"T": 1794
},
{
"C": 2.112,
"G": 135,
"P": 1.004,
"T": 1794
},
{
"C": 2.112,
"G": 135,
"P": 2.004,
"T": 1794
},
{
"C": 2.112,
"G": 135,
"P": 3.004,
"T": 1794
},
{
"C": 2.112,
"G": 135,
"P": 4.004,
"T": 1794
}
],
[
{
"C": 2.195,
"G": 135,
"P": 5.003,
"T": 1795
},
{
"C": 1.66,
"G": 135,
"P": 1.004,
"T": 1795
},
{
"C": 1.66,
"G": 135,
"P": 2.004,
"T": 1795
},
{
"C": 1.66,
"G": 135,
"P": 3.004,
"T": 1795
},
{
"C": 1.66,
"G": 135,
"P": 4.004,
"T": 1795
}
]
],
"G": 135
},
{
"E": [
[
{
"C": 1.096,
"G": 3213,
"P": 3,
"T": 4393
},
{
"C": 1.21,
"G": 3213,
"P": 4,
"T": 4393
}
],
[
{
"C": 5.02,
"G": 3213,
"P": 3,
"T": 4394
},
{
"C": 3.5,
"G": 3213,
"P": 4,
"T": 4394
}
]
],
"G": 3213
},
{
"E": [
[
{
"C": 1.104,
"G": 10447,
"P": 2,
"T": 13780
},
{
"C": 1.49,
"G": 10447,
"P": 3,
"T": 13780
},
{
"C": 1.98,
"G": 10447,
"P": 4,
"T": 13780
}
],
[
{
"C": 5.71,
"G": 10447,
"P": 2,
"T": 13781
},
{
"C": 2.435,
"G": 10447,
"P": 3,
"T": 13781
},
{
"C": 1.736,
"G": 10447,
"P": 4,
"T": 13781
}
]
],
"G": 10447
}
],
"I": 295536788,
"MEC": [
{
"EC": 0,
"MT": 2
},
{
"EC": 22,
"MT": 3
},
{
"EC": 0,
"MT": 4
},
{
"EC": 0,
"MT": 5
},
{
"EC": 0,
"MT": 6
},
{
"EC": 0,
"MT": 7
},
{
"EC": 0,
"MT": 8
},
{
"EC": 0,
"MT": 9
},
{
"EC": 0,
"MT": 10
},
{
"EC": 8,
"MT": 11
},
{
"EC": 0,
"MT": 12
},
{
"EC": 0,
"MT": 13
},
{
"EC": 74,
"MT": 1
}
],
"MG": 295524958,
"N": 26258,
"P": 3,
"PN": "3 Set",
"SI": 4,
"SS": 3,
"T": 99,
"R": 99
}
],
"SVoAP": true,
"VA": 1,
"VI": "5305152",
"ZP": 547307
}
}

View File

@@ -0,0 +1,788 @@
{
"Error": "",
"ErrorCode": 0,
"Guid": "",
"Id": 0,
"Success": true,
"Value": {
"CHIMG": "251ff8af4272c6b5988404b5d39bb8de.png",
"CID": 1,
"CN": "World",
"CO": 50,
"COI": 225,
"EC": 18,
"EGC": 4,
"GE": [
{
"E": [
[
{
"C": 4.06,
"G": 1,
"T": 1
}
],
[
{
"C": 1.184,
"G": 1,
"T": 3
}
]
],
"G": 1
},
{
"E": [
[
{
"C": 1.848,
"CE": 1,
"G": 17,
"P": 40.5,
"T": 9
},
{
"C": 1.995,
"G": 17,
"P": 41.5,
"T": 9
}
],
[
{
"C": 1.875,
"CE": 1,
"G": 17,
"P": 40.5,
"T": 10
},
{
"C": 1.76,
"G": 17,
"P": 41.5,
"T": 10
}
]
],
"G": 17
},
{
"E": [
[
{
"C": 2.32,
"G": 2,
"P": 3.5,
"T": 7
},
{
"C": 1.95,
"CE": 1,
"G": 2,
"P": 4.5,
"T": 7
},
{
"C": 1.595,
"G": 2,
"P": 5.5,
"T": 7
}
],
[
{
"C": 1.565,
"G": 2,
"P": -3.5,
"T": 8
},
{
"C": 1.78,
"CE": 1,
"G": 2,
"P": -4.5,
"T": 8
},
{
"C": 2.256,
"G": 2,
"P": -5.5,
"T": 8
}
]
],
"G": 2
},
{
"E": [
[
{
"C": 2.345,
"G": 952,
"P": 0.03,
"T": 1964
},
{
"C": 3.62,
"G": 952,
"P": 100.03,
"T": 1964
},
{
"C": 5.76,
"G": 952,
"P": 200.03,
"T": 1964
},
{
"B": true,
"C": 12,
"G": 952,
"P": 300,
"T": 1964
},
{
"C": 12,
"G": 952,
"P": 300.01,
"T": 1964
},
{
"C": 5.11,
"G": 952,
"P": 300.02,
"T": 1964
}
]
],
"G": 952
}
],
"I": 295524958,
"KI": 1,
"L": "Russia. Masters",
"LE": "Russia. Masters",
"LI": 1877467,
"LR": "Россия. Мастерс",
"MEC": [
{
"EC": 0,
"MT": 2
},
{
"EC": 4,
"MT": 3
},
{
"EC": 0,
"MT": 4
},
{
"EC": 0,
"MT": 5
},
{
"EC": 0,
"MT": 6
},
{
"EC": 0,
"MT": 7
},
{
"EC": 0,
"MT": 8
},
{
"EC": 0,
"MT": 9
},
{
"EC": 0,
"MT": 10
},
{
"EC": 0,
"MT": 11
},
{
"EC": 0,
"MT": 12
},
{
"EC": 0,
"MT": 13
},
{
"EC": 18,
"MT": 1
}
],
"MIO": {
"MaF": "5 sets"
},
"MIS": [
{
"K": 3,
"V": "5 sets"
},
{
"K": 10,
"V": "Hard"
}
],
"MS": [
0
],
"N": 26255,
"O1": "Dmitriy Khalyapin",
"O1C": 1,
"O1E": "Dmitriy Khalyapin",
"O1I": 4808537,
"O1IMG": [
"033b86b04b4e386f59d8afab8b1874de.png"
],
"O1IS": [
4808537
],
"O1R": "Дмитрий Халяпин",
"O2": "Vladimir Gunko",
"O2C": 1,
"O2E": "Vladimir Gunko",
"O2I": 4702045,
"O2IMG": [
"e011b488505d0e9bb1bb7d2b98d3b043.png"
],
"O2IS": [
4702045
],
"O2R": "Владимир Гунько",
"S": 1618616754,
"SE": "Tennis",
"SGI": "607a2235f75a663f69fdaeb5",
"SI": 4,
"SN": "Tennis",
"SR": "Теннис",
"SS": 3,
"STI": "5ff0075bf75a663f69dc1194",
"T": 99,
"TN": "Set",
"AM": true,
"HMH": 1,
"OuR": true,
"R": 99,
"SC": {
"CP": 2,
"CPS": "2 Set",
"FS": {
"S2": 1
},
"P": 2,
"PS": [
{
"Key": 1,
"Value": {
"S1": 5,
"S2": 7
}
},
{
"Key": 2,
"Value": {
"S1": 3,
"S2": 4
}
}
],
"S": [],
"SS": {
"S1": "0",
"S2": "15"
},
"TR": -1
},
"SG": [
{
"EC": 51,
"EGC": 11,
"GE": [
{
"E": [
[
{
"C": 5.07,
"G": 1,
"T": 1
}
],
[
{
"C": 1.125,
"G": 1,
"T": 3
}
]
],
"G": 1
},
{
"E": [
[
{
"C": 1.296,
"G": 17,
"P": 9.5,
"T": 9
},
{
"C": 3.25,
"CE": 1,
"G": 17,
"P": 10.5,
"T": 9
},
{
"C": 5.94,
"G": 17,
"P": 12.5,
"T": 9
}
],
[
{
"C": 3.245,
"G": 17,
"P": 9.5,
"T": 10
},
{
"C": 1.304,
"CE": 1,
"G": 17,
"P": 10.5,
"T": 10
},
{
"C": 1.096,
"G": 17,
"P": 12.5,
"T": 10
}
]
],
"G": 17
},
{
"E": [
[
{
"C": 3.51,
"G": 2,
"P": 1.5,
"T": 7
},
{
"C": 1.3,
"CE": 1,
"G": 2,
"P": 2.5,
"T": 7
}
],
[
{
"C": 1.256,
"G": 2,
"P": -1.5,
"T": 8
},
{
"C": 3.264,
"CE": 1,
"G": 2,
"P": -2.5,
"T": 8
}
]
],
"G": 2
},
{
"E": [
[
{
"C": 1.296,
"G": 15,
"P": 3.5,
"T": 11
},
{
"C": 2.784,
"CE": 1,
"G": 15,
"P": 4.5,
"T": 11
}
],
[
{
"C": 3.245,
"G": 15,
"P": 3.5,
"T": 12
},
{
"C": 1.384,
"CE": 1,
"G": 15,
"P": 4.5,
"T": 12
}
]
],
"G": 15
},
{
"E": [
[
{
"C": 15.8,
"G": 136,
"P": 6.004,
"T": 731
},
{
"C": 12.1,
"G": 136,
"P": 7.005,
"T": 731
},
{
"C": 9.94,
"G": 136,
"P": 7.006,
"T": 731
}
],
[
{
"C": 2.704,
"G": 136,
"P": 3.006,
"T": 731
},
{
"C": 2.04,
"G": 136,
"P": 4.006,
"T": 731
},
{
"C": 10.9,
"G": 136,
"P": 5.007,
"T": 731
},
{
"C": 9.34,
"G": 136,
"P": 6.007,
"T": 731
}
]
],
"G": 136
},
{
"E": [
[
{
"C": 1.656,
"G": 14,
"T": 182
}
],
[
{
"C": 2.1,
"G": 14,
"T": 183
}
]
],
"G": 14
},
{
"E": [
[
{
"C": 4.35,
"G": 22,
"P": 8,
"T": 50
},
{
"C": 1.45,
"G": 22,
"P": 9,
"T": 50
}
],
[
{
"C": 1.175,
"G": 22,
"P": 8,
"T": 51
},
{
"C": 2.555,
"G": 22,
"P": 9,
"T": 51
}
]
],
"G": 22
},
{
"E": [
[
{
"C": 3.435,
"G": 57,
"P": 8,
"T": 538
},
{
"C": 2.99,
"G": 57,
"P": 9,
"T": 538
}
],
[
{
"C": 1.22,
"G": 57,
"P": 8,
"T": 539
},
{
"C": 1.288,
"G": 57,
"P": 9,
"T": 539
}
]
],
"G": 57
},
{
"E": [
[
{
"C": 2.144,
"G": 135,
"P": 3.008,
"T": 1794
},
{
"C": 2.144,
"G": 135,
"P": 4.008,
"T": 1794
},
{
"C": 1.67,
"G": 135,
"P": 1.009,
"T": 1794
},
{
"C": 1.67,
"G": 135,
"P": 2.009,
"T": 1794
},
{
"C": 1.67,
"G": 135,
"P": 3.009,
"T": 1794
},
{
"C": 1.67,
"G": 135,
"P": 4.009,
"T": 1794
}
],
[
{
"C": 1.645,
"G": 135,
"P": 3.008,
"T": 1795
},
{
"C": 1.645,
"G": 135,
"P": 4.008,
"T": 1795
},
{
"C": 2.096,
"G": 135,
"P": 1.009,
"T": 1795
},
{
"C": 2.096,
"G": 135,
"P": 2.009,
"T": 1795
},
{
"C": 2.096,
"G": 135,
"P": 3.009,
"T": 1795
},
{
"C": 2.096,
"G": 135,
"P": 4.009,
"T": 1795
}
]
],
"G": 135
},
{
"E": [
[
{
"C": 1.21,
"G": 3213,
"P": 9,
"T": 4393
},
{
"C": 1.235,
"G": 3213,
"P": 8,
"T": 4393
}
],
[
{
"C": 3.51,
"G": 3213,
"P": 9,
"T": 4394
},
{
"C": 3.312,
"G": 3213,
"P": 8,
"T": 4394
}
]
],
"G": 3213
},
{
"E": [
[
{
"C": 1.296,
"G": 10447,
"P": 4,
"T": 13780
}
],
[
{
"C": 3.245,
"G": 10447,
"P": 4,
"T": 13781
}
]
],
"G": 10447
}
],
"I": 295530826,
"MEC": [
{
"EC": 0,
"MT": 2
},
{
"EC": 12,
"MT": 3
},
{
"EC": 0,
"MT": 4
},
{
"EC": 0,
"MT": 5
},
{
"EC": 0,
"MT": 6
},
{
"EC": 0,
"MT": 7
},
{
"EC": 0,
"MT": 8
},
{
"EC": 0,
"MT": 9
},
{
"EC": 0,
"MT": 10
},
{
"EC": 8,
"MT": 11
},
{
"EC": 0,
"MT": 12
},
{
"EC": 0,
"MT": 13
},
{
"EC": 51,
"MT": 1
}
],
"MG": 295524958,
"N": 26257,
"P": 2,
"PN": "2 Set",
"SI": 4,
"SS": 3,
"T": 99,
"R": 99
}
],
"SVoAP": true,
"VA": 1,
"VI": "5305152",
"ZP": 547307
}
}

108
football.go Executable file
View File

@@ -0,0 +1,108 @@
package onexbet
import (
//"bytes"
"regexp"
"strings"
//"time"
//"github.com/PuerkitoBio/goquery"
)
var (
reCyber = regexp.MustCompile(`(?i)\bCyber\b`)
reEsoccer = regexp.MustCompile(`(?i)\bEsoccer\b`)
bannedFootballChamps = []string{
"Alternative Matches",
"2x2",
"3х3",
"3x3",
"4x4",
"4х4",
"5x5",
"5x5",
"5х5",
"6x6",
"7x7",
"8 х 8",
"8 х 8",
"LFL",
"SRL",
"Club Friendlies",
"Soap Soccer League",
"League short football",
"Dragon League",
"CFL. Championship",
"Table Soccer League",
"Ladies League",
"Dragon League. National",
"Student League",
"ACL Indoor",
"Nacional Night League",
"PRO Soccer League",
"PRO Soccer League 2",
"Short Football",
"Derby League",
"Serie A8",
"Nacional League",
"BumperBall Cup",
"KLASK",
"Copa Diego Armando Maradona",
"Copa Luis Toti Brunengo",
}
bannedFootballPrefixes = []string{
"FIFA 20.",
"FIFA.",
"USSR.",
"PES 2020.",
}
)
func FootballChampFilter(champName string) (_ bool) {
// Мусор (champName)
// FIFA 20. GT League G-26
// Division 4х4
// FIFA. eSports Battle. Night Retro International
// Soccer Box 2x2
// FIFA. 247. Division 1
// USSR. 3x3. Division B
// Turkey. Cyber FIFA20 Super Lig Matches
// "Soccer Box 2x2"
// "BudnesLiga LFL 5x5"
// "RPL 6x6"
// "La Liga Roja 7x7"
// "6x6. Liga Pro"
// "Short Football 4x4 L1"
// "Short Football 4x4 L2"
// "Division 4х4"
// "Dream League 3x3"
// "Short Football 5x5"
// "6х6. Czech Republic. Regional League"
// "Esoccer GT Leagues"
// "Club Friendlies"
// "Club Friendlies. Women"
// Soap Soccer League. Women
for _, bannedChampName := range bannedFootballChamps {
if strings.Contains(champName, bannedChampName) {
return
}
}
for _, bannedPrefix := range bannedFootballPrefixes {
if strings.HasPrefix(champName, bannedPrefix) {
return
}
}
if reCyber.MatchString(champName) {
return
}
if reEsoccer.MatchString(champName) {
return
}
return true
}

24
go.mod Normal file
View File

@@ -0,0 +1,24 @@
module gordenko.dev/dima/onexbet
go 1.25.0
require (
gordenko.dev/dima/flashscore v0.0.0-20260721004947-e8a0b2ba4291
gordenko.dev/dima/httpreq v1.0.1-0.20230801160925-0916b3afaf24
gordenko.dev/dima/spider v0.0.0-20260721003948-776f3225b709
gordenko.dev/dima/web v0.0.0-20260719063304-9b080f685b42
gordenko.dev/dima/ws v0.0.0-20260720220933-ee2de7671d35
)
require (
github.com/PuerkitoBio/goquery v1.12.0 // indirect
github.com/andybalholm/cascadia v1.3.3 // indirect
github.com/gorilla/websocket v1.5.0 // indirect
golang.org/x/crypto v0.54.0 // indirect
golang.org/x/net v0.57.0 // indirect
golang.org/x/text v0.40.0 // indirect
gordenko.dev/dima/fixme v0.0.0-20230801160335-c5c6b3b00ea2 // indirect
gordenko.dev/dima/pretty v0.0.0-20221225212746-0c27d8c0ac69 // indirect
gordenko.dev/dima/qx v0.0.0-20260720012756-9323ec898f91 // indirect
gordenko.dev/dima/textutil v0.0.0-20260718203502-62db7f60f8f7 // indirect
)

99
go.sum Normal file
View File

@@ -0,0 +1,99 @@
filippo.io/edwards25519 v1.2.0 h1:crnVqOiS4jqYleHd9vaKZ+HKtHfllngJIiOpNpoJsjo=
filippo.io/edwards25519 v1.2.0/go.mod h1:xzAOLCNug/yB62zG1bQ8uziwrIqIuxhctzJT18Q77mc=
github.com/PuerkitoBio/goquery v1.12.0 h1:pAcL4g3WRXekcB9AU/y1mbKez2dbY2AajVhtkO8RIBo=
github.com/PuerkitoBio/goquery v1.12.0/go.mod h1:802ej+gV2y7bbIhOIoPY5sT183ZW0YFofScC4q/hIpQ=
github.com/andybalholm/cascadia v1.3.3 h1:AG2YHrzJIm4BZ19iwJ/DAua6Btl3IwJX+VI4kktS1LM=
github.com/andybalholm/cascadia v1.3.3/go.mod h1:xNd9bqTn98Ln4DwST8/nG+H0yuB8Hmgu1YHNnWw0GeA=
github.com/go-sql-driver/mysql v1.10.0 h1:Q+1LV8DkHJvSYAdR83XzuhDaTykuDx0l6fkXxoWCWfw=
github.com/go-sql-driver/mysql v1.10.0/go.mod h1:M+cqaI7+xxXGG9swrdeUIoPG3Y3KCkF0pZej+SK+nWk=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/crypto v0.54.0 h1:YLIA59K4fiNzHzjnZt2tUJQjQtUWfWbeHBqKtk3eScw=
golang.org/x/crypto v0.54.0/go.mod h1:KWL8ny2AZdGR2cWmzeHrp2azQPGogOv+HeQaVEXC2dk=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
golang.org/x/net v0.57.0 h1:K5+3DljvIuDG9/Jv9rvyMywYNFCQ9RSUY6OOTTkT+tE=
golang.org/x/net v0.57.0/go.mod h1:KpXc8iv+r3XplLAG/f7Jsf9RPszJzdR0f58q9vGOuEU=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/text v0.40.0 h1:Ub2Z6/xjgF1WrYQz2nuITOEegKFtiIy+rieRJ5lHZKs=
golang.org/x/text v0.40.0/go.mod h1:hpnzDAfGV753zIKo+wk3u1bVKCGPbrnF7+7LBF/UHVY=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gordenko.dev/dima/fixme v0.0.0-20230801160335-c5c6b3b00ea2 h1:gKOjARkSrD8ARC29Wp0IOjnmCujtL4ju8andsNqH2BQ=
gordenko.dev/dima/fixme v0.0.0-20230801160335-c5c6b3b00ea2/go.mod h1:7tMDvA2ej8e7VwXIlcIZNtUe/Tk5jFAK498WqC+aMYE=
gordenko.dev/dima/flashscore v0.0.0-20260721004947-e8a0b2ba4291 h1:U+ogwnU9eCG8RMwkSaoIwoYeNKO76f2mb7nrEC9GFp4=
gordenko.dev/dima/flashscore v0.0.0-20260721004947-e8a0b2ba4291/go.mod h1:QeZWECcy5wpLsnVBuS9mKeNFHI6ShH5spIE41ojKY9U=
gordenko.dev/dima/httpreq v1.0.1-0.20230801160925-0916b3afaf24 h1:InEx24mURTm8oJstSxUPcBgbPyr/KtxBUaJzSGrOe1I=
gordenko.dev/dima/httpreq v1.0.1-0.20230801160925-0916b3afaf24/go.mod h1:N7UdxORFT8FsC6laerBR3Zf+ur0Zgpv/zuLD2n7SNTQ=
gordenko.dev/dima/pretty v0.0.0-20221225212746-0c27d8c0ac69 h1:nyJ3mzTQ46yUeMZCdLyYcs7B5JCS54c67v84miyhq2E=
gordenko.dev/dima/pretty v0.0.0-20221225212746-0c27d8c0ac69/go.mod h1:AxgKDktpqBVyIOhIcP+nlCpK+EsJyjN5kPdqyd8euVU=
gordenko.dev/dima/qx v0.0.0-20260720012756-9323ec898f91 h1:J+C9mqwH5XR1idAi/PcdTMFPRrySyo3EVpDWd4m0FFs=
gordenko.dev/dima/qx v0.0.0-20260720012756-9323ec898f91/go.mod h1:CR5GISpjHFHZ9HeSIo3fYRyp1+kVx0R9exzBYsIuB3c=
gordenko.dev/dima/spider v0.0.0-20260721003948-776f3225b709 h1:zhooBnyXJUCrViDncwUN7Kh6JMobo6vaiAtobHitxG4=
gordenko.dev/dima/spider v0.0.0-20260721003948-776f3225b709/go.mod h1:ZVNbQYItuH9vADWOc9d1Alam646Y29q6JIA6UNFj9N8=
gordenko.dev/dima/textutil v0.0.0-20260718203502-62db7f60f8f7 h1:KzwamqlIEgAjQdTVNVfjGnS1f3bC+PzkTdZfuQhbVfk=
gordenko.dev/dima/textutil v0.0.0-20260718203502-62db7f60f8f7/go.mod h1:M3e8S3N1USCMYmL6Q5uDQJaxfzBlWHnTX7rxCJQ6iJs=
gordenko.dev/dima/web v0.0.0-20260719063304-9b080f685b42 h1:dSMZyLvRuCg24UgKWr68kmgQ1SNcFHtd0jD0nhvnQLU=
gordenko.dev/dima/web v0.0.0-20260719063304-9b080f685b42/go.mod h1:m6fQP/HjoYlcz9gw2pXZNkvRzb6I/9rsMYESpnFzG8Q=
gordenko.dev/dima/ws v0.0.0-20260720220933-ee2de7671d35 h1:/hZR1r74CR0OJ8kYVU701F2lVmnNtDh+MyuYMBZZDoo=
gordenko.dev/dima/ws v0.0.0-20260720220933-ee2de7671d35/go.mod h1:/51DHPs14MGkShfh7nXNOrzqlD3yMOS7WXWWNttcfCw=

32
handball.go Normal file
View File

@@ -0,0 +1,32 @@
package onexbet
import "strings"
var (
bannedHandballChamps = []string{
"Alternative Matches",
"Element League",
"Niko Cup",
"Avalanche Сup",
}
bannedHandballPrefixes = []string{
"PRO League",
}
)
func HandballChampFilter(champName string) (_ bool) {
for _, bannedChampName := range bannedHandballChamps {
if strings.Contains(champName, bannedChampName) {
return
}
}
for _, bannedPrefix := range bannedHandballPrefixes {
if strings.HasPrefix(champName, bannedPrefix) {
return
}
}
return true
}

11
notes Executable file
View File

@@ -0,0 +1,11 @@
Футбол. Украина. Премьер-лига.
Доп. рынки по отдельным ссылкам из массива BIG. Нет SG, либо SG ?
Футбол. Португалия. Примера.
Доп. рынки по отдельным ссылкам из массива SG
Футбол. Греция. Суперлига.
Доп. рынки по отдельным ссылкам из массива SG
BIG, вместо SG - то до матча

138
onexbet_test.go Normal file
View File

@@ -0,0 +1,138 @@
package onexbet
import (
"encoding/json"
"fmt"
"testing"
)
/*
func TestSearchTeamCandidates(t *testing.T) {
list, err := SearchTeamMatches(tennisLiveSearchURL, "Gunko")
if err != nil {
t.Fatal(err)
}
for _, m := range list {
fmt.Printf("%#v\n\n", m)
}
}
func TestLoadTeamLiveMatchData(t *testing.T) {
data, isMatchFinished, err := LoadTeamLiveMatchData("295524958")
if err != nil {
t.Fatal(err)
}
buf, _ := json.MarshalIndent(data, "", " ")
fmt.Printf("%s\n", buf)
fmt.Printf("isMatchFinished: %t\n", isMatchFinished)
}
*/
/*
func TestSearchMatchCandidates(t *testing.T) {
list, err := SearchMatchCandidates("west ham", "manchester city", nil)
if err != nil {
t.Fatal(err)
}
for _, m := range list {
fmt.Printf("%#v\n\n", m)
}
}
*/
/*
func TestListLiveMatchesBySport(t *testing.T) {
list, err := ListLiveMatchesBySport(Tennis)
if err != nil {
t.Fatal(err)
}
for _, m := range list {
buf, _ := json.MarshalIndent(m, "", " ")
fmt.Printf("%s\n", buf)
}
}
*/
/*
func TestLoadTeamLiveMatchData(t *testing.T) {
list, err := ListLiveMatchesBySport(Handball)
if err != nil {
t.Fatal(err)
}
if len(list) == 0 {
fmt.Printf("No live matches\n")
return
}
data, err := LoadTeamLiveMatchData(list[0].MatchID)
if err != nil {
t.Fatal(err)
}
buf, _ := json.MarshalIndent(data, "", " ")
fmt.Printf("%s\n", buf)
}
*/
/*
*/
func TestGetTennisMatchResult(t *testing.T) {
onexbetStatsMatchID := "610afdf8f75a663f695c36ac"
stat, err := GetTennisMatchResult(onexbetStatsMatchID)
if err != nil {
t.Fatal(err)
}
fmt.Printf("onexbetStatsMatchID: %s\n", onexbetStatsMatchID)
buf, _ := json.MarshalIndent(stat, "", " ")
fmt.Printf("%s\n", buf)
}
/*
func TestLoadTennisLiveMatchDataOfFirstInLive(t *testing.T) {
list, err := ListLiveMatchesBySport(Tennis)
if err != nil {
t.Fatal(err)
}
if len(list) == 0 {
fmt.Printf("No live matches\n")
return
}
data, isMatchFinished, err := LoadTennisLiveMatchData(list[0].MatchID)
if err != nil {
t.Fatal(err)
}
buf, _ := json.MarshalIndent(data, "", " ")
fmt.Printf("%s\nisMatchFinished: %t\n", buf, isMatchFinished)
}
func TestLoadTennisLiveMatchData(t *testing.T) {
data, isMatchFinished, err := LoadTennisLiveMatchData("298023063")
if err != nil {
t.Fatal(err)
}
buf, _ := json.MarshalIndent(data, "", " ")
fmt.Printf("%s\nisMatchFinished: %t\n", buf, isMatchFinished)
}
*/
/*
func TestGetTeamMatchResult(t *testing.T) {
stat, err := GetTeamMatchResult("60257269f75a663f693fd68d")
if err != nil {
t.Fatal(err)
}
buf, _ := json.MarshalIndent(stat, "", " ")
fmt.Printf("%s\n", buf)
}
*/

248
parser.go Executable file
View File

@@ -0,0 +1,248 @@
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"`
}

14051
some js/bet-groups.json Executable file

File diff suppressed because it is too large Load Diff

71288
some js/betsNames_full_en.js Executable file

File diff suppressed because it is too large Load Diff

4411
some js/england-sg-links.json Executable file

File diff suppressed because it is too large Load Diff

7322
some js/football.live.html Executable file

File diff suppressed because one or more lines are too long

3187
some js/italy-sg-links.json Executable file

File diff suppressed because it is too large Load Diff

813
some js/live-matches.json Executable file
View File

@@ -0,0 +1,813 @@
[
{
"@context": "http://schema.org",
"@type": "SportsEvent",
"name": "Alexander Klintcharov — Rob Reynolds",
"startDate": "2020-06-12T02:00:00+03:00",
"url": "https://ua-1x-bet.com/live/Tennis/2101033-New-Zealand-Premier-League/240759633-Alexander-Klintcharov-Rob-Reynolds/",
"organizer": {
"@type": "SportsOrganization",
"name": "New Zealand. Premier League",
"sport": "Tennis"
},
"location": {
"@type": "Place",
"name": "Alexander Klintcharov",
"address": "World"
},
"homeTeam": {
"@type": "SportsTeam",
"name": "Alexander Klintcharov",
"sport": "Tennis",
"memberOf": {
"@type": "SportsOrganization",
"name": "New Zealand. Premier League",
"sport": "Tennis"
}
},
"awayTeam": {
"@type": "SportsTeam",
"name": "Rob Reynolds",
"sport": "Tennis",
"memberOf": {
"@type": "SportsOrganization",
"name": "New Zealand. Premier League",
"sport": "Tennis"
}
},
"offers": [
{
"@type": "Offer",
"availability": "OnlineOnly",
"availabilityEnds": "2020-06-12T02:00:00+03:00",
"url": "https://ua-1x-bet.com/live/Tennis/2101033-New-Zealand-Premier-League/240759633-Alexander-Klintcharov-Rob-Reynolds/",
"name": "W1",
"price": "6.59"
},
{
"@type": "Offer",
"availability": "OnlineOnly",
"availabilityEnds": "2020-06-12T02:00:00+03:00",
"url": "https://ua-1x-bet.com/live/Tennis/2101033-New-Zealand-Premier-League/240759633-Alexander-Klintcharov-Rob-Reynolds/",
"name": "W2"
}
]
},
{
"@context": "http://schema.org",
"@type": "SportsEvent",
"name": "Isaac Becroft — Mikal Statham",
"startDate": "2020-06-12T02:15:00+03:00",
"url": "https://ua-1x-bet.com/live/Tennis/2101033-New-Zealand-Premier-League/240760768-Isaac-Becroft-Mikal-Statham/",
"organizer": {
"@type": "SportsOrganization",
"name": "New Zealand. Premier League",
"sport": "Tennis"
},
"location": {
"@type": "Place",
"name": "Isaac Becroft",
"address": "World"
},
"homeTeam": {
"@type": "SportsTeam",
"name": "Isaac Becroft",
"sport": "Tennis",
"memberOf": {
"@type": "SportsOrganization",
"name": "New Zealand. Premier League",
"sport": "Tennis"
}
},
"awayTeam": {
"@type": "SportsTeam",
"name": "Mikal Statham",
"sport": "Tennis",
"memberOf": {
"@type": "SportsOrganization",
"name": "New Zealand. Premier League",
"sport": "Tennis"
}
},
"offers": [
{
"@type": "Offer",
"availability": "OnlineOnly",
"availabilityEnds": "2020-06-12T02:15:00+03:00",
"url": "https://ua-1x-bet.com/live/Tennis/2101033-New-Zealand-Premier-League/240760768-Isaac-Becroft-Mikal-Statham/",
"name": "W1"
},
{
"@type": "Offer",
"availability": "OnlineOnly",
"availabilityEnds": "2020-06-12T02:15:00+03:00",
"url": "https://ua-1x-bet.com/live/Tennis/2101033-New-Zealand-Premier-League/240760768-Isaac-Becroft-Mikal-Statham/",
"name": "W2"
}
]
},
{
"@context": "http://schema.org",
"@type": "SportsEvent",
"name": "Sunrise+ — Flame+",
"startDate": "2020-06-12T03:56:34+03:00",
"url": "https://ua-1x-bet.com/live/Football/2055922-Short-Football-3x3/240768306-Sunrise-Flame/",
"organizer": {
"@type": "SportsOrganization",
"name": "Short Football 3x3",
"sport": "Football"
},
"location": {
"@type": "Place",
"name": "Sunrise+",
"address": "World"
},
"homeTeam": {
"@type": "SportsTeam",
"name": "Sunrise+",
"sport": "Football",
"memberOf": {
"@type": "SportsOrganization",
"name": "Short Football 3x3",
"sport": "Football"
}
},
"awayTeam": {
"@type": "SportsTeam",
"name": "Flame+",
"sport": "Football",
"memberOf": {
"@type": "SportsOrganization",
"name": "Short Football 3x3",
"sport": "Football"
}
},
"offers": [
{
"@type": "Offer",
"availability": "OnlineOnly",
"availabilityEnds": "2020-06-12T03:56:34+03:00",
"url": "https://ua-1x-bet.com/live/Football/2055922-Short-Football-3x3/240768306-Sunrise-Flame/",
"name": "W1",
"price": "2.375"
},
{
"@type": "Offer",
"availability": "OnlineOnly",
"availabilityEnds": "2020-06-12T03:56:34+03:00",
"url": "https://ua-1x-bet.com/live/Football/2055922-Short-Football-3x3/240768306-Sunrise-Flame/",
"name": "W2",
"price": "2.18"
}
]
},
{
"@context": "http://schema.org",
"@type": "SportsEvent",
"name": "Matvey Panafutin — Ivan Grishunin",
"startDate": "2020-06-12T03:29:01+03:00",
"url": "https://ua-1x-bet.com/live/Table-Tennis/2064427-Masters/240766627-Matvey-Panafutin-Ivan-Grishunin/",
"organizer": {
"@type": "SportsOrganization",
"name": "Masters",
"sport": "Table Tennis"
},
"location": {
"@type": "Place",
"name": "Matvey Panafutin",
"address": "World"
},
"homeTeam": {
"@type": "SportsTeam",
"name": "Matvey Panafutin",
"sport": "Table Tennis",
"memberOf": {
"@type": "SportsOrganization",
"name": "Masters",
"sport": "Table Tennis"
}
},
"awayTeam": {
"@type": "SportsTeam",
"name": "Ivan Grishunin",
"sport": "Table Tennis",
"memberOf": {
"@type": "SportsOrganization",
"name": "Masters",
"sport": "Table Tennis"
}
},
"offers": [
{
"@type": "Offer",
"availability": "OnlineOnly",
"availabilityEnds": "2020-06-12T03:29:01+03:00",
"url": "https://ua-1x-bet.com/live/Table-Tennis/2064427-Masters/240766627-Matvey-Panafutin-Ivan-Grishunin/",
"name": "W1",
"price": "2.225"
},
{
"@type": "Offer",
"availability": "OnlineOnly",
"availabilityEnds": "2020-06-12T03:29:01+03:00",
"url": "https://ua-1x-bet.com/live/Table-Tennis/2064427-Masters/240766627-Matvey-Panafutin-Ivan-Grishunin/",
"name": "W2"
}
]
},
{
"@context": "http://schema.org",
"@type": "SportsEvent",
"name": "Ural Legion — Ural Volley",
"startDate": "2020-06-12T03:30:44+03:00",
"url": "https://ua-1x-bet.com/live/Volleyball/2085771-Ural-League-2/240765965-Ural-Legion-Ural-Volley/",
"organizer": {
"@type": "SportsOrganization",
"name": "Ural League 2",
"sport": "Volleyball"
},
"location": {
"@type": "Place",
"name": "Ural Legion",
"address": "World"
},
"homeTeam": {
"@type": "SportsTeam",
"name": "Ural Legion",
"sport": "Volleyball",
"memberOf": {
"@type": "SportsOrganization",
"name": "Ural League 2",
"sport": "Volleyball"
}
},
"awayTeam": {
"@type": "SportsTeam",
"name": "Ural Volley",
"sport": "Volleyball",
"memberOf": {
"@type": "SportsOrganization",
"name": "Ural League 2",
"sport": "Volleyball"
}
},
"offers": [
{
"@type": "Offer",
"availability": "OnlineOnly",
"availabilityEnds": "2020-06-12T03:30:44+03:00",
"url": "https://ua-1x-bet.com/live/Volleyball/2085771-Ural-League-2/240765965-Ural-Legion-Ural-Volley/",
"name": "W1",
"price": "1.935"
},
{
"@type": "Offer",
"availability": "OnlineOnly",
"availabilityEnds": "2020-06-12T03:30:44+03:00",
"url": "https://ua-1x-bet.com/live/Volleyball/2085771-Ural-League-2/240765965-Ural-Legion-Ural-Volley/",
"name": "W2"
}
]
},
{
"@context": "http://schema.org",
"@type": "SportsEvent",
"name": "Hoffenheim (Amateur) — Koln (Amateur)",
"startDate": "2020-06-12T04:00:00+03:00",
"url": "https://ua-1x-bet.com/live/Football/2055972-BudnesLiga-LFL-5x5/240768282-Hoffenheim-Amateur-Koln-Amateur/",
"organizer": {
"@type": "SportsOrganization",
"name": "BudnesLiga LFL 5x5",
"sport": "Football"
},
"location": {
"@type": "Place",
"name": "Hoffenheim (Amateur)",
"address": "World"
},
"homeTeam": {
"@type": "SportsTeam",
"name": "Hoffenheim (Amateur)",
"sport": "Football",
"memberOf": {
"@type": "SportsOrganization",
"name": "BudnesLiga LFL 5x5",
"sport": "Football"
}
},
"awayTeam": {
"@type": "SportsTeam",
"name": "Koln (Amateur)",
"sport": "Football",
"memberOf": {
"@type": "SportsOrganization",
"name": "BudnesLiga LFL 5x5",
"sport": "Football"
}
},
"offers": [
{
"@type": "Offer",
"availability": "OnlineOnly",
"availabilityEnds": "2020-06-12T04:00:00+03:00",
"url": "https://ua-1x-bet.com/live/Football/2055972-BudnesLiga-LFL-5x5/240768282-Hoffenheim-Amateur-Koln-Amateur/",
"name": "W1",
"price": "2.504"
},
{
"@type": "Offer",
"availability": "OnlineOnly",
"availabilityEnds": "2020-06-12T04:00:00+03:00",
"url": "https://ua-1x-bet.com/live/Football/2055972-BudnesLiga-LFL-5x5/240768282-Hoffenheim-Amateur-Koln-Amateur/",
"name": "W2",
"price": "1.85"
}
]
},
{
"@context": "http://schema.org",
"@type": "SportsEvent",
"name": "Alexander Kulagin A. — Sergey Chernikov",
"startDate": "2020-06-12T03:45:01+03:00",
"url": "https://ua-1x-bet.com/live/Table-Tennis/2078421-BoomCup/240767612-Alexander-Kulagin-A-Sergey-Chernikov/",
"organizer": {
"@type": "SportsOrganization",
"name": "BoomCup",
"sport": "Table Tennis"
},
"location": {
"@type": "Place",
"name": "Alexander Kulagin A.",
"address": "World"
},
"homeTeam": {
"@type": "SportsTeam",
"name": "Alexander Kulagin A.",
"sport": "Table Tennis",
"memberOf": {
"@type": "SportsOrganization",
"name": "BoomCup",
"sport": "Table Tennis"
}
},
"awayTeam": {
"@type": "SportsTeam",
"name": "Sergey Chernikov",
"sport": "Table Tennis",
"memberOf": {
"@type": "SportsOrganization",
"name": "BoomCup",
"sport": "Table Tennis"
}
},
"offers": [
{
"@type": "Offer",
"availability": "OnlineOnly",
"availabilityEnds": "2020-06-12T03:45:01+03:00",
"url": "https://ua-1x-bet.com/live/Table-Tennis/2078421-BoomCup/240767612-Alexander-Kulagin-A-Sergey-Chernikov/",
"name": "W1",
"price": "5.41"
},
{
"@type": "Offer",
"availability": "OnlineOnly",
"availabilityEnds": "2020-06-12T03:45:01+03:00",
"url": "https://ua-1x-bet.com/live/Table-Tennis/2078421-BoomCup/240767612-Alexander-Kulagin-A-Sergey-Chernikov/",
"name": "W2"
}
]
},
{
"@context": "http://schema.org",
"@type": "SportsEvent",
"name": "PSV (Amateur) — Vitesse (Amateur)",
"startDate": "2020-06-12T04:00:01+03:00",
"url": "https://ua-1x-bet.com/live/Football/2055846-Division-4h4/240768130-PSV-Amateur-Vitesse-Amateur/",
"organizer": {
"@type": "SportsOrganization",
"name": "Division 4х4",
"sport": "Football"
},
"location": {
"@type": "Place",
"name": "PSV (Amateur)",
"address": "World"
},
"homeTeam": {
"@type": "SportsTeam",
"name": "PSV (Amateur)",
"sport": "Football",
"memberOf": {
"@type": "SportsOrganization",
"name": "Division 4х4",
"sport": "Football"
}
},
"awayTeam": {
"@type": "SportsTeam",
"name": "Vitesse (Amateur)",
"sport": "Football",
"memberOf": {
"@type": "SportsOrganization",
"name": "Division 4х4",
"sport": "Football"
}
},
"offers": [
{
"@type": "Offer",
"availability": "OnlineOnly",
"availabilityEnds": "2020-06-12T04:00:01+03:00",
"url": "https://ua-1x-bet.com/live/Football/2055846-Division-4h4/240768130-PSV-Amateur-Vitesse-Amateur/",
"name": "W1",
"price": "2.375"
},
{
"@type": "Offer",
"availability": "OnlineOnly",
"availabilityEnds": "2020-06-12T04:00:01+03:00",
"url": "https://ua-1x-bet.com/live/Football/2055846-Division-4h4/240768130-PSV-Amateur-Vitesse-Amateur/",
"name": "W2",
"price": "2.04"
}
]
},
{
"@context": "http://schema.org",
"@type": "SportsEvent",
"name": "Devils — Drozdy",
"startDate": "2020-06-12T03:41:12+03:00",
"url": "https://ua-1x-bet.com/live/Football/2086020-Soccer-Box-2x2/240767331-Devils-Drozdy/",
"organizer": {
"@type": "SportsOrganization",
"name": "Soccer Box 2x2",
"sport": "Football"
},
"location": {
"@type": "Place",
"name": "Devils",
"address": "World"
},
"homeTeam": {
"@type": "SportsTeam",
"name": "Devils",
"sport": "Football",
"memberOf": {
"@type": "SportsOrganization",
"name": "Soccer Box 2x2",
"sport": "Football"
}
},
"awayTeam": {
"@type": "SportsTeam",
"name": "Drozdy",
"sport": "Football",
"memberOf": {
"@type": "SportsOrganization",
"name": "Soccer Box 2x2",
"sport": "Football"
}
},
"offers": [
{
"@type": "Offer",
"availability": "OnlineOnly",
"availabilityEnds": "2020-06-12T03:41:12+03:00",
"url": "https://ua-1x-bet.com/live/Football/2086020-Soccer-Box-2x2/240767331-Devils-Drozdy/",
"name": "W1"
},
{
"@type": "Offer",
"availability": "OnlineOnly",
"availabilityEnds": "2020-06-12T03:41:12+03:00",
"url": "https://ua-1x-bet.com/live/Football/2086020-Soccer-Box-2x2/240767331-Devils-Drozdy/",
"name": "W2"
}
]
},
{
"@context": "http://schema.org",
"@type": "SportsEvent",
"name": "Legion — Scout",
"startDate": "2020-06-12T03:14:49+03:00",
"url": "https://ua-1x-bet.com/live/Volleyball/2097331-District-League/240765870-Legion-Scout/",
"organizer": {
"@type": "SportsOrganization",
"name": "District League",
"sport": "Volleyball"
},
"location": {
"@type": "Place",
"name": "Legion",
"address": "World"
},
"homeTeam": {
"@type": "SportsTeam",
"name": "Legion",
"sport": "Volleyball",
"memberOf": {
"@type": "SportsOrganization",
"name": "District League",
"sport": "Volleyball"
}
},
"awayTeam": {
"@type": "SportsTeam",
"name": "Scout",
"sport": "Volleyball",
"memberOf": {
"@type": "SportsOrganization",
"name": "District League",
"sport": "Volleyball"
}
},
"offers": [
{
"@type": "Offer",
"availability": "OnlineOnly",
"availabilityEnds": "2020-06-12T03:14:49+03:00",
"url": "https://ua-1x-bet.com/live/Volleyball/2097331-District-League/240765870-Legion-Scout/",
"name": "W1",
"price": "5.36"
},
{
"@type": "Offer",
"availability": "OnlineOnly",
"availabilityEnds": "2020-06-12T03:14:49+03:00",
"url": "https://ua-1x-bet.com/live/Volleyball/2097331-District-League/240765870-Legion-Scout/",
"name": "W2"
}
]
},
{
"@context": "http://schema.org",
"@type": "SportsEvent",
"name": "Italy (cyber) — Portugal (cyber)",
"startDate": "2020-06-12T03:48:00+03:00",
"url": "https://ua-1x-bet.com/live/Football/2082488-FIFA-Cyber-PRO-League/240767256-Italy-cyber-Portugal-cyber/",
"organizer": {
"@type": "SportsOrganization",
"name": "FIFA. Cyber PRO League",
"sport": "Football"
},
"location": {
"@type": "Place",
"name": "Italy (cyber)",
"address": "World"
},
"homeTeam": {
"@type": "SportsTeam",
"name": "Italy (cyber)",
"sport": "Football",
"memberOf": {
"@type": "SportsOrganization",
"name": "FIFA. Cyber PRO League",
"sport": "Football"
}
},
"awayTeam": {
"@type": "SportsTeam",
"name": "Portugal (cyber)",
"sport": "Football",
"memberOf": {
"@type": "SportsOrganization",
"name": "FIFA. Cyber PRO League",
"sport": "Football"
}
},
"offers": [
{
"@type": "Offer",
"availability": "OnlineOnly",
"availabilityEnds": "2020-06-12T03:48:00+03:00",
"url": "https://ua-1x-bet.com/live/Football/2082488-FIFA-Cyber-PRO-League/240767256-Italy-cyber-Portugal-cyber/",
"name": "W1",
"price": "3.98"
},
{
"@type": "Offer",
"availability": "OnlineOnly",
"availabilityEnds": "2020-06-12T03:48:00+03:00",
"url": "https://ua-1x-bet.com/live/Football/2082488-FIFA-Cyber-PRO-League/240767256-Italy-cyber-Portugal-cyber/",
"name": "W2",
"price": "1.63"
}
]
},
{
"@context": "http://schema.org",
"@type": "SportsEvent",
"name": "Milwaukee Bucks (Iga) — Brooklyn Nets (Ruza)",
"startDate": "2020-06-12T03:45:01+03:00",
"url": "https://ua-1x-bet.com/live/Basketball/2083327-NBA-Cyber-PRO-League/240766991-Milwaukee-Bucks-Iga-Brooklyn-Nets-Ruza/",
"organizer": {
"@type": "SportsOrganization",
"name": "NBA. Cyber PRO League",
"sport": "Basketball"
},
"location": {
"@type": "Place",
"name": "Milwaukee Bucks (Iga)",
"address": "World"
},
"homeTeam": {
"@type": "SportsTeam",
"name": "Milwaukee Bucks (Iga)",
"sport": "Basketball",
"memberOf": {
"@type": "SportsOrganization",
"name": "NBA. Cyber PRO League",
"sport": "Basketball"
}
},
"awayTeam": {
"@type": "SportsTeam",
"name": "Brooklyn Nets (Ruza)",
"sport": "Basketball",
"memberOf": {
"@type": "SportsOrganization",
"name": "NBA. Cyber PRO League",
"sport": "Basketball"
}
},
"offers": [
{
"@type": "Offer",
"availability": "OnlineOnly",
"availabilityEnds": "2020-06-12T03:45:01+03:00",
"url": "https://ua-1x-bet.com/live/Basketball/2083327-NBA-Cyber-PRO-League/240766991-Milwaukee-Bucks-Iga-Brooklyn-Nets-Ruza/",
"name": "W1"
},
{
"@type": "Offer",
"availability": "OnlineOnly",
"availabilityEnds": "2020-06-12T03:45:01+03:00",
"url": "https://ua-1x-bet.com/live/Basketball/2083327-NBA-Cyber-PRO-League/240766991-Milwaukee-Bucks-Iga-Brooklyn-Nets-Ruza/",
"name": "W2"
}
]
},
{
"@context": "http://schema.org",
"@type": "SportsEvent",
"name": "Winnipeg Jets (cyber) — Toronto Maple Leafs (cyber)",
"startDate": "2020-06-12T02:50:00+03:00",
"url": "https://ua-1x-bet.com/live/Ice-Hockey/2082327-NHL-20-Cyber-League/240763565-Winnipeg-Jets-cyber-Toronto-Maple-Leafs-cyber/",
"organizer": {
"@type": "SportsOrganization",
"name": "NHL 20. Cyber League",
"sport": "Ice Hockey"
},
"location": {
"@type": "Place",
"name": "Winnipeg Jets (cyber)",
"address": "World"
},
"homeTeam": {
"@type": "SportsTeam",
"name": "Winnipeg Jets (cyber)",
"sport": "Ice Hockey",
"memberOf": {
"@type": "SportsOrganization",
"name": "NHL 20. Cyber League",
"sport": "Ice Hockey"
}
},
"awayTeam": {
"@type": "SportsTeam",
"name": "Toronto Maple Leafs (cyber)",
"sport": "Ice Hockey",
"memberOf": {
"@type": "SportsOrganization",
"name": "NHL 20. Cyber League",
"sport": "Ice Hockey"
}
},
"offers": [
{
"@type": "Offer",
"availability": "OnlineOnly",
"availabilityEnds": "2020-06-12T02:50:00+03:00",
"url": "https://ua-1x-bet.com/live/Ice-Hockey/2082327-NHL-20-Cyber-League/240763565-Winnipeg-Jets-cyber-Toronto-Maple-Leafs-cyber/",
"name": "W1",
"price": "1.02"
},
{
"@type": "Offer",
"availability": "OnlineOnly",
"availabilityEnds": "2020-06-12T02:50:00+03:00",
"url": "https://ua-1x-bet.com/live/Ice-Hockey/2082327-NHL-20-Cyber-League/240763565-Winnipeg-Jets-cyber-Toronto-Maple-Leafs-cyber/",
"name": "W2",
"price": "32"
}
]
},
{
"@context": "http://schema.org",
"@type": "SportsEvent",
"name": "Denver Nuggets (cyber) — Boston Celtics (cyber)",
"startDate": "2020-06-12T02:47:01+03:00",
"url": "https://ua-1x-bet.com/live/Basketball/2082107-NBA-2K20-Cyber-League/240763406-Denver-Nuggets-cyber-Boston-Celtics-cyber/",
"organizer": {
"@type": "SportsOrganization",
"name": "NBA 2K20. Cyber League",
"sport": "Basketball"
},
"location": {
"@type": "Place",
"name": "Denver Nuggets (cyber)",
"address": "World"
},
"homeTeam": {
"@type": "SportsTeam",
"name": "Denver Nuggets (cyber)",
"sport": "Basketball",
"memberOf": {
"@type": "SportsOrganization",
"name": "NBA 2K20. Cyber League",
"sport": "Basketball"
}
},
"awayTeam": {
"@type": "SportsTeam",
"name": "Boston Celtics (cyber)",
"sport": "Basketball",
"memberOf": {
"@type": "SportsOrganization",
"name": "NBA 2K20. Cyber League",
"sport": "Basketball"
}
},
"offers": [
{
"@type": "Offer",
"availability": "OnlineOnly",
"availabilityEnds": "2020-06-12T02:47:01+03:00",
"url": "https://ua-1x-bet.com/live/Basketball/2082107-NBA-2K20-Cyber-League/240763406-Denver-Nuggets-cyber-Boston-Celtics-cyber/",
"name": "W1"
},
{
"@type": "Offer",
"availability": "OnlineOnly",
"availabilityEnds": "2020-06-12T02:47:01+03:00",
"url": "https://ua-1x-bet.com/live/Basketball/2082107-NBA-2K20-Cyber-League/240763406-Denver-Nuggets-cyber-Boston-Celtics-cyber/",
"name": "W2"
}
]
},
{
"@context": "http://schema.org",
"@type": "SportsEvent",
"name": "Dmitry Ptitsyn — Evgeny Treshchev",
"startDate": "2020-06-12T03:45:00+03:00",
"url": "https://ua-1x-bet.com/live/Table-Tennis/1792858-Win-Cup/240768237-Dmitry-Ptitsyn-Evgeny-Treshchev/",
"organizer": {
"@type": "SportsOrganization",
"name": "Win Cup",
"sport": "Table Tennis"
},
"location": {
"@type": "Place",
"name": "Dmitry Ptitsyn",
"address": "World"
},
"homeTeam": {
"@type": "SportsTeam",
"name": "Dmitry Ptitsyn",
"sport": "Table Tennis",
"memberOf": {
"@type": "SportsOrganization",
"name": "Win Cup",
"sport": "Table Tennis"
}
},
"awayTeam": {
"@type": "SportsTeam",
"name": "Evgeny Treshchev",
"sport": "Table Tennis",
"memberOf": {
"@type": "SportsOrganization",
"name": "Win Cup",
"sport": "Table Tennis"
}
},
"offers": [
{
"@type": "Offer",
"availability": "OnlineOnly",
"availabilityEnds": "2020-06-12T03:45:00+03:00",
"url": "https://ua-1x-bet.com/live/Table-Tennis/1792858-Win-Cup/240768237-Dmitry-Ptitsyn-Evgeny-Treshchev/",
"name": "W1",
"price": "2.4"
},
{
"@type": "Offer",
"availability": "OnlineOnly",
"availabilityEnds": "2020-06-12T03:45:00+03:00",
"url": "https://ua-1x-bet.com/live/Table-Tennis/1792858-Win-Cup/240768237-Dmitry-Ptitsyn-Evgeny-Treshchev/",
"name": "W2"
}
]
}
]

1753
some js/not_full_1x2.json Normal file

File diff suppressed because it is too large Load Diff

3349
some js/portugal-sg-links.json Executable file

File diff suppressed because it is too large Load Diff

3473
some js/spain-sg-links.json Executable file

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

475
team.go Normal file
View File

@@ -0,0 +1,475 @@
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
}

343
tennis.go Normal file
View File

@@ -0,0 +1,343 @@
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
}

212
types.go Executable file
View File

@@ -0,0 +1,212 @@
package onexbet
//"time"
type OnexbetSport int
const (
Football OnexbetSport = 1
Handball OnexbetSport = 8
Tennis OnexbetSport = 4
defaultPartnerID = "25"
liveSportURLPattern = "https://ua-1x-bet.com/LiveFeed/Get1x2_VZip?sports=%d&count=100&lng=en&mode=4&country=2&partner=25&getEmpty=true"
// Все лайв матчи
liveURL = "https://ua-1x-bet.com/en/live/"
// https://ua-1x-bet.com/LiveFeed/GetGameZip?id=241215601&lng=en&cfview=0&isSubGames=true&GroupEvents=true&allEventsGroupSubGames=true&countevents=250&partner=25&marketType=1
liveMatchURLPattern = "https://ua-1x-bet.com/LiveFeed/GetGameZip?id=%s&lng=en&cfview=0&isSubGames=true&GroupEvents=true&allEventsGroupSubGames=true&countevents=250&partner=25&marketType=1"
// Параметр - это поле SGI из LiveMatchURL
// Старый URL
//matchStatsURLPattern = "https://ua-1x-bet.com/SiteService/StatByStatGameId2?id=%s&ln=en&cfview=0"
// id, тот же что и для liveMatchURLPattern
//liveMatchChronoPattern = "https://ua-1x-bet.com/LiveFeed/GetChronoOfPlay?id=%s&lng=en"
// gameId имеет вид "5d5447addc49007c51bd9359". Узнать можно из поля "SGI" в JSON по ссылке liveMatchURLPattern
//liveMatchLineupsPattern = "https://ua-1x-bet.com/en/SiteService/LineUps?gameId=%s&ln=en"
// https://ua-1x-bet.com/LiveFeed/Web_SearchZip?text=leeds+united&limit=50&lng=en&mode=4&partner=25
stdLiveSearchURL = "https://ua-1x-bet.com/LiveFeed/Web_SearchZip"
// https://ua-1x-bet.com/LineFeed/Web_SearchZip?text=leeds+united&limit=50&lng=en&mode=4&partner=25
stdLineSearchURL = "https://ua-1x-bet.com/LineFeed/Web_SearchZip"
tennisLiveSearchURL = "https://lite.ua-1x-bet.com/service-api/LiveFeed/Web_SearchZip"
tennisLineSearchURL = "https://lite.ua-1x-bet.com/service-api/LineFeed/Web_SearchZip"
//https://ua-1x-bet.com/SiteService/Game?gameId=60f319a7f75a663f692dc28b&ln=en&partner=25&geo=2
matchStatsURLPattern = "https://ua-1x-bet.com/SiteService/Game?gameId=%s&ln=en&partner=25&geo=1"
// Mon Jan 2 15:04:05 -0700 MST 2006
startDateLayout = "2006-01-02T15:04:05-07:00"
tennisMarketWinner = 1
tennisMarketTotal = 17
tennisMarketHandicap = 2
teamMarketWinner3Way = 1
teamMarketTotal = 17
teamMarketIndividualTotal1 = 15
teamMarketIndividualTotal2 = 62
// Websocket message types
WsMsgLiveMatches = "liveMatches"
WsMsgTeamLiveMatchData = "teamLiveMatchData"
WsMsgTennisLiveMatchData = "tennisLiveMatchData"
StatusMatchCompleted = 3
StatusMatchInplay = 2
StatusMatchWaiting = 1
TopicLive = "live"
)
type WatchReq struct {
SportID OnexbetSport
MatchID string
}
type LiveMatch struct {
MatchID string `json:"matchID"`
SportName string `json:"sportName"`
ChampName string `json:"champName"`
URL string `json:"url"`
Home string `json:"home"`
Away string `json:"away"`
StartTime int64 `json:"startTime"`
StatsMatchID string `json:"statsMatchID"`
Score Score `json:"score"`
ScoreByPeriods map[int]Score `json:"scoreByPeriods"`
CurrentPeriod int `json:"currentPeriod"`
}
type Score struct {
Home int `json:"home"`
Away int `json:"away"`
}
type Period struct {
PeriodNumber int `json:"periodNumber"` // 1 - 5
Total Total `json:"total"`
Handicap Handicap `json:"handicap"`
}
/*
type TennisLiveMatchData struct {
MatchID string `json:"matchID"`
Winner *Winner2Way `json:"winner"`
Total Total `json:"total"`
Sets []TennisSet `json:"sets"`
// Текущий сет
CurrentSetNumber int `json:"currentSetNumber"`
// Счет по сетам, например 1-0 или 2-1
SetScore Score `json:"setScore"`
// ключ - номер сета, значение - счет по геймам внутри сета
GameScore map[int]Score `json:"gameScore"`
}
*/
type TeamLiveMatchData struct {
MatchID string `json:"matchID"`
HomeAttacks int `json:"homeAttacks"`
HomeDangerousAttacks int `json:"homeDangerousAttacks"`
HomeShotsOnTarget int `json:"homeShotsOnTarget"`
HomeShotsOffTarget int `json:"homeShotsOffTarget"`
AwayAttacks int `json:"awayAttacks"`
AwayDangerousAttacks int `json:"awayDangerousAttacks"`
AwayShotsOnTarget int `json:"awayShotsOnTarget"`
AwayShotsOffTarget int `json:"awayShotsOffTarget"`
CurrentTime int `json:"currentTime"`
HomeGoals int `json:"homeGoals"`
AwayGoals int `json:"awayGoals"`
Winner *Winner3Way `json:"winner"`
Total Total `json:"total"`
Handicap Handicap `json:"handicap"`
IndividualTotalHome Total `json:"individualTotalHome"`
IndividualTotalAway Total `json:"individualTotalAway"`
H1 Half `json:"h1"` // 1st half
// tennis
Winner2Way *Winner2Way `json:"winner2way"`
// score
Score Score `json:"score"`
ScoreByPeriods map[int]Score `json:"scoreByPeriods"`
CurrentPeriod int `json:"currentPeriod"`
Periods map[int]Period `json:"periods"`
}
// Бывают матчи, где перестают принимать ставки на один из исходов. Поэтому нужен флаг
type Winner2Way struct {
HasHome bool `json:"hasHome"`
Home float64 `json:"home"`
HasAway bool `json:"hasAway"`
Away float64 `json:"away"`
}
type Winner3Way struct {
Home float64 `json:"home"`
Draw float64 `json:"draw"`
Away float64 `json:"away"`
}
type Half struct {
Total Total `json:"total"`
IndividualTotalHome Total `json:"individualTotalHome"`
IndividualTotalAway Total `json:"individualTotalAway"`
}
type Total struct {
Over []ParamOffer `json:"over"`
Under []ParamOffer `json:"under"`
}
type Handicap struct {
Home []ParamOffer `json:"home"`
Away []ParamOffer `json:"away"`
}
type ParamOffer struct {
Price float64 `json:"price"`
Param string `json:"param"`
ParamFloat float64 `json:"paramFloat"`
}
type LiveMatchesMessage struct {
SportID OnexbetSport `json:"sportID"`
Matches []LiveMatch `json:"matches"`
Watching []string `json:"watching"`
}
/*
type LiveMatchesUpdateMessage struct {
RemovedMatchIDs []string `json:"removedMatchIDs"`
AddedMatches []LiveMatch `json:"addedMatches"`
}
*/
type MatchCandidate struct {
Sport string `json:"sport"`
ChampName string `json:"champName"`
Home string `json:"home"`
Away string `json:"away"`
StartTime int64 `json:"startTime"`
MatchID string `json:"matchID"`
}
type PeriodScore struct {
HomePoints int `json:"homePoints"`
AwayPoints int `json:"awayPoints"`
}
type TeamMatchResult struct {
Status int `json:"status"`
HomePoints int `json:"homePoints"`
AwayPoints int `json:"awayPoints"`
ScoreByPeriods map[int]PeriodScore
}