1st
This commit is contained in:
76
cleaner/main.go
Executable file
76
cleaner/main.go
Executable file
@@ -0,0 +1,76 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
/*
|
||||||
|
Чистит названия команд в БД.
|
||||||
|
Убирает приписки к названиям команд в виде трехбуквенного кода страны в скобках (Aut).
|
||||||
|
Важно не удалить вместо кода страны приписки типа (Am), (Woman)
|
||||||
|
*/
|
||||||
|
|
||||||
|
import (
|
||||||
|
"gordenko.dev/dima/flashscore/model"
|
||||||
|
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"gordenko.dev/dima/qx"
|
||||||
|
|
||||||
|
_ "github.com/go-sql-driver/mysql"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
reCountryCode = regexp.MustCompile(`\([a-zA-Z]{3}\)`)
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
db, err := qx.Open("mysql", "user:password@/tipper")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
/*
|
||||||
|
m := model.New(db)
|
||||||
|
|
||||||
|
teams, err := m.ListFootballTeams()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, team := range teams {
|
||||||
|
if reCountryCode.MatchString(team.CanonicalName) {
|
||||||
|
//newCanonicalName := reCountryCode.ReplaceAllString(team.CanonicalName, "")
|
||||||
|
|
||||||
|
// Внутри метода будет удален старый alias и добавлен новый
|
||||||
|
err = m.UpdateFootballTeamCanonicalName(model.UpdateFootballTeamCanonicalNameIn{
|
||||||
|
TeamID: team.TeamID,
|
||||||
|
CanonicalName: team.CanonicalName,
|
||||||
|
NewCanonicalName: team.CanonicalName,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
// Capitalize names
|
||||||
|
|
||||||
|
var teams []model.Team
|
||||||
|
|
||||||
|
db.List(&teams)
|
||||||
|
|
||||||
|
tx, err := db.Tx()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, team := range teams {
|
||||||
|
name := strings.Title(team.CanonicalName)
|
||||||
|
|
||||||
|
err = tx.Exec(`UPDATE footballTeams SET canonicalName=? WHERE teamID=?`, name, team.TeamID)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tx.Commit()
|
||||||
|
}
|
||||||
581
cmd/main.go
Normal file
581
cmd/main.go
Normal file
@@ -0,0 +1,581 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
/*
|
||||||
|
Отписка от матча:
|
||||||
|
- метод Unwatch
|
||||||
|
- когда матч исчезнет из лайва
|
||||||
|
*/
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os/signal"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"gordenko.dev/dima/flashscore"
|
||||||
|
"gordenko.dev/dima/qx"
|
||||||
|
|
||||||
|
"gordenko.dev/dima/flashscore/daemon"
|
||||||
|
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"gordenko.dev/dima/web"
|
||||||
|
|
||||||
|
"gordenko.dev/dima/web/api"
|
||||||
|
"gordenko.dev/dima/web/router"
|
||||||
|
|
||||||
|
_ "github.com/go-sql-driver/mysql"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
port = 7767
|
||||||
|
dsn = "user:password@/tipper"
|
||||||
|
baseDir = "/home/ubuntu/hfs"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
logger := log.New(os.Stdout, "", log.LstdFlags)
|
||||||
|
|
||||||
|
db, err := qx.Open("mysql", dsn)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("qx.Open: %s\n", err)
|
||||||
|
}
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
//fs := flashscore.New(db)
|
||||||
|
|
||||||
|
d, err := daemon.New(daemon.Options{
|
||||||
|
BaseDir: baseDir,
|
||||||
|
Logger: logger,
|
||||||
|
CheckInterval: 60 * 60 * 6,
|
||||||
|
Db: db,
|
||||||
|
SportIDs: []int{
|
||||||
|
flashscore.Tennis,
|
||||||
|
flashscore.Handball,
|
||||||
|
flashscore.Football,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("daemon.New: %s\n", err)
|
||||||
|
}
|
||||||
|
defer d.Close()
|
||||||
|
|
||||||
|
publicAPI, err := api.NewAPI(api.Options{
|
||||||
|
Logger: logger,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("api.NewAPI: %s\n", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
publicAPI.PublicFunc("listUpcomingMatchReports", d.ListUpcomingMatchReports)
|
||||||
|
|
||||||
|
r := router.New()
|
||||||
|
r.HandleFunc("/reports", d.ShowReports)
|
||||||
|
r.PrefixHandle("/api", publicAPI)
|
||||||
|
|
||||||
|
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.")
|
||||||
|
|
||||||
|
/*
|
||||||
|
//err = flashscore.ListFootballChampFixtures(fixturesURL)
|
||||||
|
_, err = flashscore.GetUpcomingFootballMatchReport("riQ962dS")
|
||||||
|
//err = testParseTr(trsrc)
|
||||||
|
//err = loadOdds()
|
||||||
|
//_, err = loadFootballMatchSummary("Sv4qwtWD")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
FootballChampIDs: []string{
|
||||||
|
"/england/premier-league",
|
||||||
|
"/england/championship",
|
||||||
|
"/england/league-one",
|
||||||
|
//"/england/league-two",
|
||||||
|
//"/england/national-league",
|
||||||
|
//"/england/national-league-north",
|
||||||
|
//"/england/national-league-south",
|
||||||
|
//"/england/npl-premier-division",
|
||||||
|
//"/england/southern-league-central-division",
|
||||||
|
//"/england/southern-league-south-division",
|
||||||
|
//"/england/isthmian-league-premier-division",
|
||||||
|
//"/england/professional-development-league",
|
||||||
|
//"/england/efl-cup",
|
||||||
|
//"/england/efl-trophy",
|
||||||
|
//"/england/fa-cup",
|
||||||
|
//"/england/fa-trophy",
|
||||||
|
//"/england/premier-league-cup",
|
||||||
|
//"/england/premier-league-international-cup",
|
||||||
|
//"/england/premier-league-2",
|
||||||
|
//"/england/premier-league-u18",
|
||||||
|
//"/england/fa-youth-cup",
|
||||||
|
//"/england/women-s-super-league", 06.09
|
||||||
|
//"/england/women-s-championship",
|
||||||
|
//"/england/women-s-fa-cup", 26.09
|
||||||
|
//"/england/women-s-league-cup",
|
||||||
|
//"/germany/bundesliga",
|
||||||
|
//"/germany/2-bundesliga",
|
||||||
|
"/germany/3-liga",
|
||||||
|
//"/germany/regionalliga-north",
|
||||||
|
"/germany/regionalliga-nordost",
|
||||||
|
//"/germany/regionalliga-west", // 04.09
|
||||||
|
//"/germany/regionalliga-sudwest", // 4.09
|
||||||
|
//"/germany/regionalliga-bayern",
|
||||||
|
//"/germany/dfb-pokal", // 12.09
|
||||||
|
"/germany/oberliga-nofv-nord",
|
||||||
|
"/germany/oberliga-nofv-sud",
|
||||||
|
//"/germany/bundesliga-women", // 4.09
|
||||||
|
//"/germany/2-bundesliga-women",
|
||||||
|
"/austria/tipico-bundesliga",
|
||||||
|
"/austria/2-liga",
|
||||||
|
//"/regionalliga-west-promotion-group", // WTF ?
|
||||||
|
"/austria/regionalliga-east",
|
||||||
|
"/austria/regionalliga-central",
|
||||||
|
"/austria/regionalliga-west-tyrol",
|
||||||
|
"/austria/regionalliga-west-salzburg",
|
||||||
|
"/austria/regionalliga-west-vorarlberg",
|
||||||
|
"/austria/regionalliga-west",
|
||||||
|
"/austria/ofb-cup",
|
||||||
|
"/austria/bundesliga-women",
|
||||||
|
//"/netherlands/eredivisie", // 12.09
|
||||||
|
"/netherlands/eerste-divisie", // 28.08
|
||||||
|
//"/netherlands/tweede-divisie", // 5.09
|
||||||
|
//"/netherlands/derde-divisie", 05.09
|
||||||
|
//"/netherlands/reserve-league",
|
||||||
|
"/netherlands/knvb-beker",
|
||||||
|
//"/netherlands/eredivisie-u19",
|
||||||
|
"/france/ligue-1",
|
||||||
|
"/france/ligue-2",
|
||||||
|
"/france/national",
|
||||||
|
"/france/national-2-group-a",
|
||||||
|
"/france/national-2-group-b",
|
||||||
|
"/france/national-2-group-c",
|
||||||
|
"/france/national-2-group-d",
|
||||||
|
//"/france/coupe-de-la-ligue",
|
||||||
|
//"/france/coupe-de-france",
|
||||||
|
//"/france/division-1-women", 09
|
||||||
|
//"/france/division-2-women", 09
|
||||||
|
//"/france/coupe-de-france-women",
|
||||||
|
//"/netherlands/eredivisie-women", 06.09
|
||||||
|
//"/netherlands/knvb-beker-women",
|
||||||
|
//"/netherlands/eredivisie-cup-women",
|
||||||
|
"/italy/serie-a",
|
||||||
|
"/italy/serie-b",
|
||||||
|
"/italy/serie-a-women",
|
||||||
|
//"/italy/serie-b-women",
|
||||||
|
//"/italy/coppa-italia-women",
|
||||||
|
"/spain/laliga",
|
||||||
|
"/spain/laliga2",
|
||||||
|
//"/spain/primera-division-women",
|
||||||
|
//"/spain/copa-de-la-reina-women",
|
||||||
|
"/portugal/primeira-liga",
|
||||||
|
//"/portugal/ligapro",
|
||||||
|
//"/portugal/campeonato-de-portugal",
|
||||||
|
//"/portugal/taca-de-portugal",
|
||||||
|
//"/portugal/league-cup",
|
||||||
|
//"/portugal/liga-revelacao-u23",
|
||||||
|
//"/portugal/liga-bpi-women",
|
||||||
|
//"/portugal/taca-de-portugal-women",
|
||||||
|
"/belgium/jupiler-league", // 08.08
|
||||||
|
//"/belgium/proximus-league", // 22.08
|
||||||
|
//"/belgium/first-amateur-division",
|
||||||
|
//"/belgium/belgian-cup",
|
||||||
|
//"/belgium/pro-league-u21",
|
||||||
|
//"/belgium/super-league-women",
|
||||||
|
//"/belgium/1st-national-women",
|
||||||
|
//"/belgium/belgian-cup-women",
|
||||||
|
"/russia/premier-league",
|
||||||
|
"/russia/fnl",
|
||||||
|
"/russia/russian-cup",
|
||||||
|
"/russia/fnl-cup",
|
||||||
|
"/russia/pfl-group-1",
|
||||||
|
"/russia/pfl-group-2",
|
||||||
|
"/russia/pfl-group-3",
|
||||||
|
"/russia/pfl-group-4",
|
||||||
|
"/russia/youth-league",
|
||||||
|
"/russia/supreme-division-women",
|
||||||
|
"/switzerland/super-league",
|
||||||
|
"/switzerland/challenge-league",
|
||||||
|
//"/switzerland/promotion-league", 15.08 ??? (FRO after date)
|
||||||
|
//"/switzerland/1-liga-classic-group-1",
|
||||||
|
//"/switzerland/1-liga-classic-group-2",
|
||||||
|
//"/switzerland/1-liga-classic-group-3",
|
||||||
|
//"/switzerland/1-liga-classic-play-offs",
|
||||||
|
//"/switzerland/swiss-cup",
|
||||||
|
"/switzerland/nationalliga-a-women",
|
||||||
|
"/ukraine/premier-league",
|
||||||
|
"/ukraine/persha-liga",
|
||||||
|
"/ukraine/druha-liga",
|
||||||
|
"/belarus/vysshaya-liga",
|
||||||
|
"/belarus/pershaya-liga",
|
||||||
|
"/belarus/vysshaya-liga-women",
|
||||||
|
"/estonia/meistriliiga",
|
||||||
|
"/estonia/esiliiga",
|
||||||
|
"/estonia/estonian-cup",
|
||||||
|
"/latvia/optibet-virsliga",
|
||||||
|
"/latvia/1-liga",
|
||||||
|
"/latvia/latvian-cup",
|
||||||
|
"/lithuania/a-lyga",
|
||||||
|
"/lithuania/i-lyga",
|
||||||
|
"/lithuania/lithuanian-cup",
|
||||||
|
"/moldova/divizia-nationala",
|
||||||
|
"/moldova/moldovan-cup",
|
||||||
|
"/greece/super-league",
|
||||||
|
"/greece/super-league-2",
|
||||||
|
"/greece/football-league",
|
||||||
|
"/north-macedonia/1-mfl",
|
||||||
|
//"/north-macedonia/2-mfl",
|
||||||
|
//"/north-macedonia/macedonian-cup",
|
||||||
|
"/turkey/super-lig",
|
||||||
|
"/turkey/1-lig",
|
||||||
|
//"/turkey/turkish-cup",
|
||||||
|
"/bulgaria/parva-liga",
|
||||||
|
"/bulgaria/vtora-liga",
|
||||||
|
//"/cyprus/first-division", // 22.08
|
||||||
|
//"/cyprus/division-2", // ??
|
||||||
|
"/israel/ligat-ha-al",
|
||||||
|
//"/israel/leumit-league",
|
||||||
|
"/israel/state-cup",
|
||||||
|
"/bahrain/premier-league",
|
||||||
|
//"/bahrain/king-s-cup",
|
||||||
|
//"/bahrain/bahrain-cup",
|
||||||
|
"/faroe-islands/premier-league",
|
||||||
|
"/faroe-islands/faroe-islands-cup",
|
||||||
|
"/hungary/otp-bank-liga",
|
||||||
|
"/hungary/merkantil-bank-liga",
|
||||||
|
"/hungary/nb-iii-east",
|
||||||
|
"/hungary/nb-iii-centre",
|
||||||
|
"/hungary/nb-iii-west",
|
||||||
|
"/hungary/hungarian-cup",
|
||||||
|
//"/hungary/nb-i-women",
|
||||||
|
"/montenegro/prva-crnogorska-liga",
|
||||||
|
"/bosnia-and-herzegovina/premier-league",
|
||||||
|
"/luxembourg/national-division", // 23.08
|
||||||
|
"/luxembourg/luxembourg-cup",
|
||||||
|
"/romania/liga-1",
|
||||||
|
"/romania/liga-2",
|
||||||
|
//"/romania/romanian-cup",
|
||||||
|
//"/romania/superliga-women",
|
||||||
|
//"/romania/romanian-cup-women",
|
||||||
|
"/croatia/1-hnl",
|
||||||
|
"/croatia/2-hnl",
|
||||||
|
//"/croatia/croatian-cup",
|
||||||
|
"/czech-republic/1-liga",
|
||||||
|
"/czech-republic/division-2",
|
||||||
|
"/czech-republic/cfl-group-a",
|
||||||
|
"/czech-republic/cfl-group-b",
|
||||||
|
"/czech-republic/msfl",
|
||||||
|
"/czech-republic/mol-cup",
|
||||||
|
"/czech-republic/u19-league",
|
||||||
|
"/czech-republic/division-a",
|
||||||
|
"/czech-republic/division-b",
|
||||||
|
"/czech-republic/division-c",
|
||||||
|
"/czech-republic/division-d",
|
||||||
|
"/czech-republic/division-e",
|
||||||
|
"/czech-republic/division-f",
|
||||||
|
"/czech-republic/1-liga-women",
|
||||||
|
//"/czech-republic/2-liga-women", // 6.09
|
||||||
|
"/czech-republic/czech-cup-women",
|
||||||
|
"/poland/ekstraklasa",
|
||||||
|
"/poland/division-1",
|
||||||
|
"/poland/division-2",
|
||||||
|
"/poland/iii-liga-group-i",
|
||||||
|
"/poland/iii-liga-group-ii",
|
||||||
|
"/poland/iii-liga-group-iii",
|
||||||
|
"/poland/iii-liga-group-iv",
|
||||||
|
"/poland/polish-cup",
|
||||||
|
//"/poland/central-youth-league",
|
||||||
|
"/poland/ekstraliga-women",
|
||||||
|
"/poland/polish-cup-women",
|
||||||
|
"/georgia/crystalbet-erovnuli-liga",
|
||||||
|
"/georgia/crystalbet-erovnuli-liga-2",
|
||||||
|
//"/georgia/georgian-cup",
|
||||||
|
"/armenia/premier-league",
|
||||||
|
"/armenia/first-league",
|
||||||
|
"/denmark/superliga",
|
||||||
|
"/denmark/1st-division",
|
||||||
|
"/denmark/2nd-division-group-1",
|
||||||
|
"/denmark/2nd-division-group-2",
|
||||||
|
"/denmark/2nd-division-group-3",
|
||||||
|
//"/denmark/2nd-division-promotion-group", // WTF?
|
||||||
|
//"/denmark/2nd-division-relegation-group", // WTF
|
||||||
|
//"/denmark/landspokal-cup",
|
||||||
|
"/denmark/elitedivisionen-women",
|
||||||
|
|
||||||
|
"/finland/veikkausliiga",
|
||||||
|
"/finland/ykkonen", // странное расписание
|
||||||
|
"/finland/kakkonen-group-a",
|
||||||
|
"/finland/kakkonen-group-b",
|
||||||
|
"/finland/kakkonen-group-c",
|
||||||
|
"/norway/eliteserien",
|
||||||
|
"/norway/obos-ligaen",
|
||||||
|
"/norway/division-2-group-1",
|
||||||
|
"/norway/division-2-group-2",
|
||||||
|
//"/norway/division-3-group-1",
|
||||||
|
//"/norway/division-3-group-2",
|
||||||
|
//"/norway/division-3-group-3",
|
||||||
|
//"/norway/division-3-group-4",
|
||||||
|
//"/norway/division-3-group-5",
|
||||||
|
//"/norway/division-3-group-6",
|
||||||
|
//"/norway/junior-u19",
|
||||||
|
//"/norway/nm-cup",
|
||||||
|
"/norway/toppserien-women",
|
||||||
|
"/sweden/allsvenskan",
|
||||||
|
"/sweden/superettan",
|
||||||
|
"/sweden/division-1-norra",
|
||||||
|
"/sweden/division-1-sodra",
|
||||||
|
//"/sweden/division-1-relegation",
|
||||||
|
//"/sweden/division-2-norrland", // FRO
|
||||||
|
//"/sweden/division-2-norra-svealand",
|
||||||
|
//"/sweden/division-2-vastra-gotaland",
|
||||||
|
//"/sweden/division-2-sodra-svealand",
|
||||||
|
//"/sweden/division-2-norra-gotaland",
|
||||||
|
"/sweden/svenska-cupen",
|
||||||
|
"/sweden/allsvenskan-women",
|
||||||
|
"/sweden/elitettan-women",
|
||||||
|
"/sweden/svenska-cupen-women",
|
||||||
|
"/iceland/pepsideild",
|
||||||
|
"/iceland/inkasso-deildin",
|
||||||
|
"/iceland/division-2",
|
||||||
|
//"/iceland/league-cup",
|
||||||
|
"/iceland/icelandic-cup",
|
||||||
|
"/iceland/urvalsdeild-women",
|
||||||
|
"/iceland/icelandic-cup-women",
|
||||||
|
"/ireland/premier-division",
|
||||||
|
"/ireland/division-1",
|
||||||
|
"/ireland/fai-cup",
|
||||||
|
"/ireland/league-cup",
|
||||||
|
//"/northern-ireland/nifl-premiership",
|
||||||
|
//"/northern-ireland/nifl-championship",
|
||||||
|
"/scotland/premiership",
|
||||||
|
//"/scotland/championship", // 17.10
|
||||||
|
//"/scotland/league-one", // 17.10
|
||||||
|
//"/scotland/league-two", // 17.10
|
||||||
|
//"/wales/cymru-premier",
|
||||||
|
"/serbia/super-liga",
|
||||||
|
"/serbia/prva-liga",
|
||||||
|
//"/serbia/serbian-cup",
|
||||||
|
"/kosovo/superliga",
|
||||||
|
"/slovakia/fortuna-liga",
|
||||||
|
"/slovakia/2-liga",
|
||||||
|
"/slovakia/divison-c-west",
|
||||||
|
"/slovakia/divison-c-east",
|
||||||
|
"/slovakia/divison-c-central",
|
||||||
|
"/slovakia/divison-c-bratislava",
|
||||||
|
"/slovakia/slovak-cup",
|
||||||
|
"/slovakia/1-liga-women",
|
||||||
|
"/slovenia/prva-liga",
|
||||||
|
"/slovenia/2-snl",
|
||||||
|
"/malta/premier-league",
|
||||||
|
"/malta/division-1",
|
||||||
|
//"/malta/fa-trophy",
|
||||||
|
"/malta/summer-cup",
|
||||||
|
"/kazakhstan/premier-league",
|
||||||
|
"/kazakhstan/first-division",
|
||||||
|
//"/kyrgyzstan/premier-liga", // 8.08 FRO
|
||||||
|
"/egypt/premier-league",
|
||||||
|
"/tunisia/ligue-professionnelle-1",
|
||||||
|
//"/tunisia/ligue-2",
|
||||||
|
"/qatar/qsl",
|
||||||
|
"/qatar/qsl-cup",
|
||||||
|
"/saudi-arabia/saudi-professional-league",
|
||||||
|
"/saudi-arabia/division-1",
|
||||||
|
//"/saudi-arabia/king-cup",
|
||||||
|
"/jordan/premier-league",
|
||||||
|
//"/jordan/jordan-cup",
|
||||||
|
//"/jordan/shield-cup",
|
||||||
|
//"/oman/professional-league",
|
||||||
|
//"/oman/sultan-cup",
|
||||||
|
//"/iran/persian-gulf-pro-league",
|
||||||
|
"/iran/division-1",
|
||||||
|
"/iran/hazfi-cup",
|
||||||
|
//"/iraq/super-league",
|
||||||
|
"/kuwait/premier-league",
|
||||||
|
//"/kuwait/crown-prince-cup",
|
||||||
|
"/kuwait/emir-cup",
|
||||||
|
//"/pakistan/premier-league",
|
||||||
|
|
||||||
|
// AMERICA
|
||||||
|
|
||||||
|
"/uruguay/primera-division",
|
||||||
|
"/uruguay/segunda-division",
|
||||||
|
"/usa/mls",
|
||||||
|
"/usa/usl-championship",
|
||||||
|
"/usa/usl-league-one",
|
||||||
|
//"/usa/usl-league-two",
|
||||||
|
"/usa/nisa",
|
||||||
|
"/usa/nwsl-women",
|
||||||
|
//"/usa/nwsl-challenge-cup-women",
|
||||||
|
"/canada/canadian-premier-league",
|
||||||
|
"/canada/championship",
|
||||||
|
"/brazil/serie-a",
|
||||||
|
"/brazil/serie-b",
|
||||||
|
"/brazil/serie-c",
|
||||||
|
//"/brazil/serie-d", // 06.09
|
||||||
|
//"/brazil/primeira-liga",
|
||||||
|
|
||||||
|
//"/brazil/campeonato-acreano",
|
||||||
|
"/brazil/campeonato-alagoano",
|
||||||
|
//"/brazil/campeonato-amapaense",
|
||||||
|
//"/brazil/campeonato-amazonense",
|
||||||
|
"/brazil/campeonato-baiano",
|
||||||
|
//"/brazil/campeonato-brasiliense",
|
||||||
|
//"/brazil/campeonato-capixaba",
|
||||||
|
//"/brazil/campeonato-carioca",
|
||||||
|
"/brazil/campeonato-catarinense",
|
||||||
|
//"/brazil/campeonato-cearense",
|
||||||
|
"/brazil/campeonato-gaucho",
|
||||||
|
//"/brazil/campeonato-gaucho-2",
|
||||||
|
//"/brazil/campeonato-goiano",
|
||||||
|
"/brazil/campeonato-maranhense",
|
||||||
|
//"/brazil/campeonato-matogrossense",
|
||||||
|
"/brazil/campeonato-mineiro",
|
||||||
|
//"/brazil/campeonato-mineiro-2",
|
||||||
|
"/brazil/campeonato-paraense",
|
||||||
|
"/brazil/campeonato-paraibano",
|
||||||
|
"/brazil/campeonato-paranaense",
|
||||||
|
"/brazil/campeonato-paulista",
|
||||||
|
//"/brazil/campeonato-paulista-a2",
|
||||||
|
"/brazil/campeonato-pernambucano",
|
||||||
|
//"/brazil/campeonato-piauiense",
|
||||||
|
"/brazil/campeonato-potiguar",
|
||||||
|
//"/brazil/campeonato-rondoniense",
|
||||||
|
"/brazil/campeonato-roraimense",
|
||||||
|
"/brazil/campeonato-sergipano",
|
||||||
|
//"/brazil/campeonato-sul-matogrossense",
|
||||||
|
"/brazil/campeonato-tocantinense",
|
||||||
|
"/brazil/copa-do-brasil",
|
||||||
|
//"/brazil/copa-do-brasil-u20",
|
||||||
|
//"/brazil/copa-verde",
|
||||||
|
"/brazil/copa-do-nordeste",
|
||||||
|
//"/brazil/brasileiro-u20",
|
||||||
|
//"/brazil/brasileiro-women",
|
||||||
|
//"/brazil/brasileiro-a2-women",
|
||||||
|
//"/argentina/superliga",
|
||||||
|
//"/argentina/primera-nacional",
|
||||||
|
//"/argentina/torneo-federal",
|
||||||
|
"/mexico/liga-mx",
|
||||||
|
//"/mexico/ascenso-mx",
|
||||||
|
"/mexico/liga-mx-women",
|
||||||
|
//"/bolivia/division-profesional",
|
||||||
|
//"/bolivia/nacional-b",
|
||||||
|
//"/chile/primera-division",
|
||||||
|
//"/chile/primera-b",
|
||||||
|
//"/chile/chilean-cup",
|
||||||
|
//"/colombia/primera-a", // 06.09
|
||||||
|
//"/colombia/primera-b", // 05.09
|
||||||
|
"/ecuador/liga-pro",
|
||||||
|
"/ecuador/serie-b",
|
||||||
|
//"/ecuador/copa-ecuador",
|
||||||
|
"/costa-rica/primera-division",
|
||||||
|
//"/costa-rica/liga-de-ascenso", end
|
||||||
|
"/paraguay/primera-division",
|
||||||
|
//"/paraguay/division-intermedia",
|
||||||
|
//"/paraguay/copa-paraguay",
|
||||||
|
"/peru/liga-1",
|
||||||
|
//"/peru/liga-2",
|
||||||
|
"/panama/lpf",
|
||||||
|
"/costa-rica/primera-division",
|
||||||
|
//"/costa-rica/liga-de-ascenso",
|
||||||
|
"/nicaragua/liga-primera",
|
||||||
|
//"/jamaica/premier-league",
|
||||||
|
// OCEANIA
|
||||||
|
"/australia/a-league",
|
||||||
|
"/australia/npl-act",
|
||||||
|
"/australia/npl-northern-nsw",
|
||||||
|
"/australia/npl-nsw",
|
||||||
|
"/australia/npl-queensland",
|
||||||
|
"/australia/npl-south-australian",
|
||||||
|
"/australia/npl-tasmania",
|
||||||
|
"/australia/npl-victoria",
|
||||||
|
"/australia/npl-western-australia",
|
||||||
|
"/australia/brisbane-premier-league",
|
||||||
|
"/australia/ffa-cup",
|
||||||
|
//"/australia/y-league",
|
||||||
|
//"/australia/w-league",
|
||||||
|
"/new-zealand/football-championship",
|
||||||
|
//"/new-zealand/national-league-women",
|
||||||
|
//"/philippines/pfl",
|
||||||
|
"/china/super-league",
|
||||||
|
//"/china/jia-league",
|
||||||
|
//"/china/yi-league",
|
||||||
|
//"/china/fa-cup",
|
||||||
|
"/mongolia/premier-league",
|
||||||
|
"/south-korea/k-league-1",
|
||||||
|
"/south-korea/k-league-2",
|
||||||
|
"/south-korea/k3-league",
|
||||||
|
"/south-korea/korean-cup",
|
||||||
|
"/south-korea/league-cup",
|
||||||
|
"/south-korea/wk-league-women",
|
||||||
|
"/japan/j1-league",
|
||||||
|
"/japan/j2-league",
|
||||||
|
"/japan/j3-league",
|
||||||
|
"/japan/ybc-levain-cup",
|
||||||
|
//"/japan/emperors-cup",
|
||||||
|
"/japan/nadeshiko-league-women",
|
||||||
|
"/japan/empress-s-cup-women",
|
||||||
|
"/vietnam/v-league-1",
|
||||||
|
"/vietnam/v-league-2",
|
||||||
|
//"/vietnam/vietnamese-cup",
|
||||||
|
"/vietnam/national-league-women",
|
||||||
|
"/vietnam/vietnamese-cup-women",
|
||||||
|
"/singapore/premier-league",
|
||||||
|
//"/singapore/singapore-cup",
|
||||||
|
"/taiwan/premier-league",
|
||||||
|
"/taiwan/mulan-football-league-women",
|
||||||
|
"/thailand/thai-league-1",
|
||||||
|
"/thailand/thai-league-2",
|
||||||
|
//"/thailand/thai-fa-cup",
|
||||||
|
"/cambodia/c-league",
|
||||||
|
"/laos/lao-league",
|
||||||
|
"/indonesia/liga-1",
|
||||||
|
"/indonesia/liga-2",
|
||||||
|
"/indonesia/president-cup",
|
||||||
|
"/malaysia/super-league",
|
||||||
|
"/malaysia/premier-league",
|
||||||
|
//"/malaysia/malaysia-cup",
|
||||||
|
//"/malaysia/fa-cup",
|
||||||
|
//"/sri-lanka/champions-league", end
|
||||||
|
// EUROPA
|
||||||
|
"/europe/europa-league",
|
||||||
|
"/europe/champions-league",
|
||||||
|
// AFRICA
|
||||||
|
//"/nigeria/npfl",
|
||||||
|
//"/nigeria/nnl",
|
||||||
|
//"/nigeria/aiteo-cup",
|
||||||
|
//"/senegal/ligue-1",
|
||||||
|
"/south-africa/premier-league",
|
||||||
|
"/south-africa/gladafrica-championship",
|
||||||
|
"/south-africa/nedbank-cup",
|
||||||
|
//"/sudan/premier-league",
|
||||||
|
//"/zimbabwe/premier-soccer-league",
|
||||||
|
"/zambia/super-league",
|
||||||
|
//"/cameroon/elite-one",
|
||||||
|
//"/cameroon/elite-two",
|
||||||
|
//"/ivory-coast/ligue-1",
|
||||||
|
//"/kenya/premier-league",
|
||||||
|
//"/lesotho/premier-league",
|
||||||
|
"/morocco/botola-pro",
|
||||||
|
"/morocco/botola-2",
|
||||||
|
//"/morocco/coupe-du-trone",
|
||||||
|
|
||||||
|
},
|
||||||
|
*/
|
||||||
159
common.go
Normal file
159
common.go
Normal file
@@ -0,0 +1,159 @@
|
|||||||
|
package flashscore
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/PuerkitoBio/goquery"
|
||||||
|
"gordenko.dev/dima/flashscore/model"
|
||||||
|
webspider "gordenko.dev/dima/spider"
|
||||||
|
)
|
||||||
|
|
||||||
|
func loadHomeAndAwayTeams(spider *webspider.Spider, matchID string, sportID int) (home, away model.Team, err error) {
|
||||||
|
// Загружаем Landing
|
||||||
|
landingURL := fmt.Sprintf(h2hLandingURLPattern, matchID)
|
||||||
|
|
||||||
|
var (
|
||||||
|
buf []byte
|
||||||
|
doc *goquery.Document
|
||||||
|
)
|
||||||
|
|
||||||
|
buf, err = spider.Get(landingURL, nil)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("load h2hLanding: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//ioutil.WriteFile("h2h.html", buf, 0644)
|
||||||
|
|
||||||
|
doc, err = goquery.NewDocumentFromReader(bytes.NewBuffer(buf))
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("goquery.NewDocumentFromReader: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//var home, away model.Team
|
||||||
|
|
||||||
|
switch sportID {
|
||||||
|
|
||||||
|
// БОЛЬШЕ НЕ АКТУАЛЬНО!
|
||||||
|
// case Football:
|
||||||
|
// // Данные футбольных команд закодированы в HTML на странице
|
||||||
|
// // Home team
|
||||||
|
// home, err = parseTeam(doc.Find(".tname-home"))
|
||||||
|
// if err != nil {
|
||||||
|
// err = fmt.Errorf("Parse home team: %s", err)
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // Away team
|
||||||
|
// away, err = parseTeam(doc.Find(".tname-away"))
|
||||||
|
// if err != nil {
|
||||||
|
// err = fmt.Errorf("Parse away team: %s", err)
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
|
||||||
|
case Football, Handball, Tennis:
|
||||||
|
// Данные гандбольных команд закодированы в JSON переменной 'environment'
|
||||||
|
// в теге <script> на странице
|
||||||
|
home, away, err = parseTeamsOnMatchPageInJsVar(doc)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("parseTeamsOnMatchPageInJsVar: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//case Tennis:
|
||||||
|
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// storedHome, storedAway *model.Team
|
||||||
|
|
||||||
|
func listOfScheduledMatches(sportID int) (result []ScheduledMatch, err error) {
|
||||||
|
spider, err := webspider.NewSpider(nil)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
referer string
|
||||||
|
urls []string
|
||||||
|
)
|
||||||
|
|
||||||
|
switch sportID {
|
||||||
|
case Football:
|
||||||
|
urls = []string{
|
||||||
|
footballAllGamesTodayURL,
|
||||||
|
footballAllGamesTomorrowURL,
|
||||||
|
}
|
||||||
|
referer = "https://www.flashscore.com/football/"
|
||||||
|
|
||||||
|
case Handball:
|
||||||
|
urls = []string{
|
||||||
|
handballAllGamesTodayURL,
|
||||||
|
handballAllGamesTomorrowURL,
|
||||||
|
}
|
||||||
|
referer = "https://www.flashscore.com/handball/"
|
||||||
|
|
||||||
|
case Tennis:
|
||||||
|
urls = []string{
|
||||||
|
tennisAllGamesTodayURL,
|
||||||
|
tennisAllGamesTomorrowURL,
|
||||||
|
}
|
||||||
|
referer = "https://www.flashscore.com/tennis/"
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Загружаем AJAX страницы со списками матчей
|
||||||
|
|
||||||
|
for _, allGamesURL := range urls {
|
||||||
|
var (
|
||||||
|
buf []byte
|
||||||
|
list []ScheduledMatch
|
||||||
|
)
|
||||||
|
|
||||||
|
buf, err = spider.Get(allGamesURL, map[string]string{
|
||||||
|
"referer": "https://d.flashscore.com/x/feed/proxy-local",
|
||||||
|
"x-requested-with": "XMLHttpRequest",
|
||||||
|
// Важное поле! Код можно найти в исходниках либо подсмотреть. Без кода
|
||||||
|
// сервер отдает 401 ошибку
|
||||||
|
"x-fsign": feedSign,
|
||||||
|
"x-referer": referer,
|
||||||
|
"x-geoip": "1",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("Load all Games feed: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//fmt.Printf("%s\n\n", buf)
|
||||||
|
|
||||||
|
list, err = parseFlashscoreEncodedAllGames(string(buf), sportID)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("parseListOfScheduledMatches: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
result = append(result, list...)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadH2H(spider *webspider.Spider, matchID string, sportID int, homeCanonicalName, awayCanonicalName string) (homeTeamMatches, awayTeamMatches, h2hMatches []model.TeamMatch, err error) {
|
||||||
|
switch sportID {
|
||||||
|
case Football:
|
||||||
|
//return loadFootballH2H(spider, matchID)
|
||||||
|
return loadHandballH2H(spider, matchID, homeCanonicalName, awayCanonicalName)
|
||||||
|
|
||||||
|
case Handball:
|
||||||
|
return loadHandballH2H(spider, matchID, homeCanonicalName, awayCanonicalName)
|
||||||
|
|
||||||
|
case Tennis:
|
||||||
|
// pass
|
||||||
|
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("Bug: h2h loader for the sportID %d is undefined", sportID))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
80
common_test.go
Normal file
80
common_test.go
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
package flashscore
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
webspider "gordenko.dev/dima/spider"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestTennisLoadHomeAndAwayTeams(t *testing.T) {
|
||||||
|
spider, _ := webspider.NewSpider(nil)
|
||||||
|
|
||||||
|
home, away, err := loadHomeAndAwayTeams(spider, "dUf6AMy8", Tennis)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("home: %#v\n", home)
|
||||||
|
fmt.Printf("away: %#v\n", away)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFootballLoadHomeAndAwayTeams(t *testing.T) {
|
||||||
|
spider, _ := webspider.NewSpider(nil)
|
||||||
|
|
||||||
|
home, away, err := loadHomeAndAwayTeams(spider, "rLXrnfEM", Football)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("home: %#v\n", home)
|
||||||
|
fmt.Printf("away: %#v\n", away)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFootballLoadH2H(t *testing.T) {
|
||||||
|
spider, _ := webspider.NewSpider(nil)
|
||||||
|
|
||||||
|
homeTeamMatches, awayTeamMatches, h2hMatches, err := loadH2H(spider, "rLXrnfEM", Football, "Gent B", "Saint Eloois")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var buf []byte
|
||||||
|
|
||||||
|
buf, _ = json.MarshalIndent(homeTeamMatches, "", " ")
|
||||||
|
fmt.Printf("home: %s\n", buf)
|
||||||
|
|
||||||
|
buf, _ = json.MarshalIndent(awayTeamMatches, "", " ")
|
||||||
|
fmt.Printf("away: %s\n", buf)
|
||||||
|
|
||||||
|
buf, _ = json.MarshalIndent(h2hMatches, "", " ")
|
||||||
|
fmt.Printf("h2h: %s\n", buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoadOdds(t *testing.T) {
|
||||||
|
spider, _ := webspider.NewSpider(nil)
|
||||||
|
|
||||||
|
matchID := "OtpuFKWi"
|
||||||
|
|
||||||
|
url := fmt.Sprintf(handballOddsURLPattern, matchID)
|
||||||
|
|
||||||
|
buf, err := spider.Get(url, map[string]string{
|
||||||
|
"x-requested-with": "XMLHttpRequest",
|
||||||
|
"x-fsign": feedSign,
|
||||||
|
"x-geoip": "1",
|
||||||
|
"referer": fmt.Sprintf(h2hLandingURLPattern, matchID),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("Load handball odds: %s; url=%s", err, url)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
odds, err := parseFlashscoreEncodedOdds(string(buf))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
buf, _ = json.MarshalIndent(odds, "", " ")
|
||||||
|
fmt.Printf("TestLoadOdds: %s\n", buf)
|
||||||
|
}
|
||||||
6452
daemon/Bundesliga.data
Executable file
6452
daemon/Bundesliga.data
Executable file
File diff suppressed because it is too large
Load Diff
1462
daemon/LaLiga.data
Executable file
1462
daemon/LaLiga.data
Executable file
File diff suppressed because it is too large
Load Diff
1452
daemon/Premier League.data
Executable file
1452
daemon/Premier League.data
Executable file
File diff suppressed because it is too large
Load Diff
1477
daemon/Serie A.data
Executable file
1477
daemon/Serie A.data
Executable file
File diff suppressed because it is too large
Load Diff
BIN
daemon/daemon
Executable file
BIN
daemon/daemon
Executable file
Binary file not shown.
273
daemon/daemon.go
Executable file
273
daemon/daemon.go
Executable file
@@ -0,0 +1,273 @@
|
|||||||
|
package daemon
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"sort"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"gordenko.dev/dima/flashscore"
|
||||||
|
"gordenko.dev/dima/flashscore/model"
|
||||||
|
"gordenko.dev/dima/qx"
|
||||||
|
"gordenko.dev/dima/web"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
defaultCheckInterval = 1 * time.Hour
|
||||||
|
)
|
||||||
|
|
||||||
|
type Options struct {
|
||||||
|
BaseDir string
|
||||||
|
CheckInterval int
|
||||||
|
Logger *log.Logger
|
||||||
|
Db *qx.Db
|
||||||
|
SportIDs []int
|
||||||
|
}
|
||||||
|
|
||||||
|
type Daemon struct {
|
||||||
|
mutex sync.Mutex
|
||||||
|
checkInterval time.Duration
|
||||||
|
fs *flashscore.Flashscore
|
||||||
|
baseDir string
|
||||||
|
dumpFilepath string
|
||||||
|
logger *log.Logger
|
||||||
|
port int
|
||||||
|
model *model.Model
|
||||||
|
// map[sportID]map[matchID]
|
||||||
|
reports map[int]map[string]flashscore.UpcomingMatchReport
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(opt Options) (*Daemon, error) {
|
||||||
|
if opt.Logger == nil {
|
||||||
|
return nil, errors.New("Logger option is required")
|
||||||
|
}
|
||||||
|
if opt.Db == nil {
|
||||||
|
return nil, errors.New("Db option is required")
|
||||||
|
}
|
||||||
|
if opt.BaseDir == "" {
|
||||||
|
return nil, errors.New("BaseDir option is required")
|
||||||
|
}
|
||||||
|
s := &Daemon{
|
||||||
|
logger: opt.Logger,
|
||||||
|
baseDir: opt.BaseDir,
|
||||||
|
dumpFilepath: filepath.Join(opt.BaseDir, "reports.dump"),
|
||||||
|
fs: flashscore.New(opt.Db, opt.Logger),
|
||||||
|
model: model.New(opt.Db),
|
||||||
|
reports: make(map[int]map[string]flashscore.UpcomingMatchReport),
|
||||||
|
}
|
||||||
|
if opt.CheckInterval <= 0 {
|
||||||
|
s.checkInterval = defaultCheckInterval
|
||||||
|
} else {
|
||||||
|
s.checkInterval = time.Duration(opt.CheckInterval) * time.Second
|
||||||
|
}
|
||||||
|
for _, sportID := range opt.SportIDs {
|
||||||
|
s.reports[sportID] = make(map[string]flashscore.UpcomingMatchReport)
|
||||||
|
}
|
||||||
|
err := s.tryRestoreReportsFromDump()
|
||||||
|
if err != nil {
|
||||||
|
// только логгируем, ибо если файла нет или он поврежден - это не проблема
|
||||||
|
s.logger.Printf("tryRestoreReportsFromDump: %s\n", err)
|
||||||
|
}
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Daemon) Close() {
|
||||||
|
s.mutex.Lock()
|
||||||
|
defer s.mutex.Unlock()
|
||||||
|
|
||||||
|
buf, err := json.MarshalIndent(s.reports, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = os.WriteFile(s.dumpFilepath, buf, 0666)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Daemon) tryRestoreReportsFromDump() (err error) {
|
||||||
|
buf, err := os.ReadFile(s.dumpFilepath)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return json.Unmarshal(buf, &s.reports)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Daemon) ShowReports(state *web.State, reply *web.Reply) (err error) {
|
||||||
|
buf, _ := json.MarshalIndent(s.reports, "", " ")
|
||||||
|
str := fmt.Sprintf("%s", buf)
|
||||||
|
|
||||||
|
reply.WriteString(str)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Daemon) Run() {
|
||||||
|
ticker := time.NewTicker(s.checkInterval)
|
||||||
|
|
||||||
|
s.logger.Println("tick")
|
||||||
|
for sportID := range s.reports {
|
||||||
|
s.checkUpcomingMatches(sportID)
|
||||||
|
}
|
||||||
|
s.logger.Println("tick processed")
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ticker.C:
|
||||||
|
s.logger.Println("tick")
|
||||||
|
for sportID := range s.reports {
|
||||||
|
s.checkUpcomingMatches(sportID)
|
||||||
|
}
|
||||||
|
s.logger.Println("tick processed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Daemon) ListUpcomingMatchReports(sportID int) (list []flashscore.UpcomingMatchReport, err error) {
|
||||||
|
s.mutex.Lock()
|
||||||
|
defer s.mutex.Unlock()
|
||||||
|
|
||||||
|
now := time.Now().Unix() - 3600
|
||||||
|
|
||||||
|
reports, ok := s.reports[sportID]
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("sportID: %d, reports size: %d\n", sportID, len(reports))
|
||||||
|
|
||||||
|
var started []string
|
||||||
|
|
||||||
|
for matchID, report := range reports {
|
||||||
|
if report.StartTime < now {
|
||||||
|
// Матч уже начался
|
||||||
|
//fmt.Printf("matchStarted: startTime %d < now %d\n", report.StartTime, now)
|
||||||
|
started = append(started, matchID)
|
||||||
|
} else {
|
||||||
|
if report.IsTeamsLoaded && report.IsHistoryLoaded {
|
||||||
|
list = append(list, report)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Удаляем матчи, которые начались
|
||||||
|
for _, matchID := range started {
|
||||||
|
delete(reports, matchID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Сортируем матчи
|
||||||
|
sort.Slice(list, func(i, j int) bool {
|
||||||
|
a := list[i]
|
||||||
|
b := list[j]
|
||||||
|
|
||||||
|
if a.StartTime == b.StartTime {
|
||||||
|
// Время начала равно - сравниваем MatchID
|
||||||
|
return a.MatchID < b.MatchID
|
||||||
|
}
|
||||||
|
|
||||||
|
return a.StartTime < b.StartTime
|
||||||
|
})
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Daemon) checkUpcomingMatches(sportID int) {
|
||||||
|
scheduledMatches, err := s.fs.ListOfScheduledMatches(sportID)
|
||||||
|
if err != nil {
|
||||||
|
s.logger.Printf("ListOfScheduledMatches: %s\n", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
s.logger.Printf("Sport: %d, scheduled matches found: %d\n", sportID, len(scheduledMatches))
|
||||||
|
|
||||||
|
reports := s.reports[sportID]
|
||||||
|
|
||||||
|
for idx, match := range scheduledMatches {
|
||||||
|
if sportID == flashscore.Tennis {
|
||||||
|
// Пары игнорим
|
||||||
|
if flashscore.IsTennisDoublesChamp(match.FullChampName) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Здесь лок не нужен, ибо нет race conditions, пишем в мапу в этом же потоке
|
||||||
|
report, ok := reports[match.MatchID]
|
||||||
|
if !ok {
|
||||||
|
report = flashscore.UpcomingMatchReport{
|
||||||
|
MatchID: match.MatchID,
|
||||||
|
SportID: match.SportID,
|
||||||
|
StartTime: match.StartTime.Unix(),
|
||||||
|
Zone: model.TeamZone{
|
||||||
|
ZoneID: match.Zone.ZoneID,
|
||||||
|
Name: match.Zone.Name,
|
||||||
|
SportID: match.SportID,
|
||||||
|
},
|
||||||
|
Champ: model.TeamChamp{
|
||||||
|
ChampID: match.ChampID,
|
||||||
|
},
|
||||||
|
FullChampName: match.FullChampName,
|
||||||
|
}
|
||||||
|
// Ниже по тексту report будет перезаписан в коллекции reports
|
||||||
|
}
|
||||||
|
|
||||||
|
// Даже если отчет полный - синкаем, чтобы обновить odds
|
||||||
|
|
||||||
|
err = s.fs.SyncUpcomingMatchReport(&report)
|
||||||
|
if err != nil {
|
||||||
|
s.logger.Printf("SyncUpcomingMatchReport: %s; matchID=%s\n", err, report.MatchID)
|
||||||
|
} else {
|
||||||
|
report.ParseTime = time.Now()
|
||||||
|
//buf, _ := json.MarshalIndent(report, "", " ")
|
||||||
|
//fmt.Printf("REPORT:\n\n%s\n\n", buf)
|
||||||
|
|
||||||
|
s.mutex.Lock()
|
||||||
|
reports[report.MatchID] = report
|
||||||
|
s.mutex.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
//fmt.Printf("REPORT:\n\n%s\n\n", buf)
|
||||||
|
if (idx % 10) == 0 {
|
||||||
|
s.logger.Printf("%d matches scanned\n", idx+1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
s.logger.Printf("Sport: %d, all matches scanned\n", sportID)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
func (s *Flashscore) ListUpcomingMatchReports(sportID int, reportCh chan *UpcomingMatchReport) (err error) {
|
||||||
|
matches, err := s.listOfScheduledMatches(sportID)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("listOfScheduledMatches: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//matches = DiscardScheduledMatchesFurtherThan(matches, 24*time.Hour)
|
||||||
|
reports := s.reports
|
||||||
|
|
||||||
|
for _, match := range matches {
|
||||||
|
s.Li
|
||||||
|
|
||||||
|
report = &UpcomingMatchReport{
|
||||||
|
IsComplete: true,
|
||||||
|
MatchID: match.MatchID,
|
||||||
|
SportID: match.SportID,
|
||||||
|
StartTime: match.StartTime.Unix(),
|
||||||
|
}
|
||||||
|
|
||||||
|
report, reportErr := s.getUpcomingMatchReport(match)
|
||||||
|
if reportErr != nil {
|
||||||
|
s.logger.Printf("getUpcomingMatchReport: %s; matchID=%s", reportErr, match.MatchID)
|
||||||
|
} else {
|
||||||
|
reportCh <- &report
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
close(reportCh)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
*/
|
||||||
36
final-round.data
Executable file
36
final-round.data
Executable file
File diff suppressed because one or more lines are too long
23
flashscore.go
Executable file
23
flashscore.go
Executable file
@@ -0,0 +1,23 @@
|
|||||||
|
package flashscore
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"gordenko.dev/dima/flashscore/model"
|
||||||
|
"gordenko.dev/dima/qx"
|
||||||
|
webspider "gordenko.dev/dima/spider"
|
||||||
|
)
|
||||||
|
|
||||||
|
func New(qxdb *qx.Db, logger *log.Logger) *Flashscore {
|
||||||
|
s := new(Flashscore)
|
||||||
|
s.model = model.New(qxdb)
|
||||||
|
s.logger = logger
|
||||||
|
|
||||||
|
var err error
|
||||||
|
|
||||||
|
s.spider, err = webspider.NewSpider(nil)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
109
football.go
Executable file
109
football.go
Executable file
@@ -0,0 +1,109 @@
|
|||||||
|
package flashscore
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/PuerkitoBio/goquery"
|
||||||
|
)
|
||||||
|
|
||||||
|
func parseOdds1x2(tableSelection *goquery.Selection) (offers []Winner3Way, err error) {
|
||||||
|
tdList := tableSelection.Find("td.bookmaker")
|
||||||
|
|
||||||
|
for idx := 0; idx < tdList.Length(); idx++ {
|
||||||
|
//tdList.Each(func(idx int, tdSelection *goquery.Selection) {
|
||||||
|
var offer Winner3Way
|
||||||
|
//.Get(0).Data //.Html()
|
||||||
|
//fmt.Printf("%s\n", txt)
|
||||||
|
//fmt.Printf("%v\n", tdSelection)
|
||||||
|
//tdSelection.Get(0)
|
||||||
|
//tdSelection.Next()
|
||||||
|
|
||||||
|
tdSelection := tdList.Eq(idx)
|
||||||
|
|
||||||
|
offer.Bookmaker, err = getBookmaker(tdSelection)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
priceTd := tdSelection.Next()
|
||||||
|
|
||||||
|
offer.Home, err = getPrice(priceTd)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("get home price: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
priceTd = priceTd.Next()
|
||||||
|
|
||||||
|
offer.Draw, err = getPrice(priceTd)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("get draw price: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
priceTd = priceTd.Next()
|
||||||
|
|
||||||
|
offer.Away, err = getPrice(priceTd)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("get away price: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
offers = append(offers, offer)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// /bookmaker/17/
|
||||||
|
var reBookmakerID = regexp.MustCompile(`/bookmaker/(\d+)`)
|
||||||
|
|
||||||
|
func getBookmaker(tdSelection *goquery.Selection) (bookmakerID int, err error) {
|
||||||
|
aSelection := tdSelection.Find("a")
|
||||||
|
if aSelection.Length() == 0 {
|
||||||
|
err = fmt.Errorf("<a> tag not found in bookmaker's <td>")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//a := aSelection.Get(0)
|
||||||
|
href, exists := aSelection.Eq(0).Attr("href")
|
||||||
|
if !exists {
|
||||||
|
err = fmt.Errorf("href attr not found in <a> tag")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// /fmt.Printf("href: %s\n", href)
|
||||||
|
|
||||||
|
groups := reBookmakerID.FindStringSubmatch(href)
|
||||||
|
if len(groups) != 2 {
|
||||||
|
err = fmt.Errorf("BookmakerID in href not found. Regexp result: %q", groups)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
bookmakerID, _ = strconv.Atoi(groups[1])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func getPrice(tdSelection *goquery.Selection) (price float64, err error) {
|
||||||
|
if tdSelection == nil {
|
||||||
|
err = fmt.Errorf("<td> tag selection is nil")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
spanSelection := tdSelection.Find("span")
|
||||||
|
if spanSelection.Length() == 0 {
|
||||||
|
err = fmt.Errorf("<span> not found in <td>")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
span := spanSelection.Get(0)
|
||||||
|
priceStr := span.FirstChild.Data // fix - if not text node
|
||||||
|
|
||||||
|
price, err = strconv.ParseFloat(priceStr, 64)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("Parse price: %s; priceStr=%q", err, priceStr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
45
football_test.go
Executable file
45
football_test.go
Executable file
@@ -0,0 +1,45 @@
|
|||||||
|
package flashscore
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
_ "github.com/go-sql-driver/mysql"
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
func TestFixtures(t *testing.T) {
|
||||||
|
var err error
|
||||||
|
//'req, err := http.NewRequest("GET", url, nil)
|
||||||
|
|
||||||
|
logger := log.New(os.Stdout, "", log.LstdFlags)
|
||||||
|
|
||||||
|
db, err := qx.Open("mysql", "root:zkx75fcy@/tipper")
|
||||||
|
//db, err := qx.Open("mysql", "root:lklnmf6@/tipper")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
fs := New(db, logger)
|
||||||
|
round, err := fs.ListFootballChampNextRoundFixtures("/germany/3-liga")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("ListFootballChampFixtures: %s\n", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
buf, _ := json.MarshalIndent(round, "", " ")
|
||||||
|
fmt.Printf("%s\n", buf)
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
func TestFootballListOfScheduledMatches(t *testing.T) {
|
||||||
|
result, err := listOfScheduledMatches(Football)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
buf, _ := json.MarshalIndent(result, "", " ")
|
||||||
|
fmt.Printf("ListOfScheduledMatches: %s\n", buf)
|
||||||
|
}
|
||||||
1
for_parse/allGamesFootball.fsf
Normal file
1
for_parse/allGamesFootball.fsf
Normal file
File diff suppressed because one or more lines are too long
1
for_parse/allGamesHandball.fsf
Normal file
1
for_parse/allGamesHandball.fsf
Normal file
File diff suppressed because one or more lines are too long
1
for_parse/allGamesTennis.fsf
Normal file
1
for_parse/allGamesTennis.fsf
Normal file
File diff suppressed because one or more lines are too long
30
for_parse/environmentJsVarOnMatchPage.html
Normal file
30
for_parse/environmentJsVarOnMatchPage.html
Normal file
File diff suppressed because one or more lines are too long
1
for_parse/h2hHandball.fsf
Normal file
1
for_parse/h2hHandball.fsf
Normal file
File diff suppressed because one or more lines are too long
4532
for_parse/jsVarEnvironment.json
Normal file
4532
for_parse/jsVarEnvironment.json
Normal file
File diff suppressed because it is too large
Load Diff
1
for_parse/oddsHandball.fsf
Normal file
1
for_parse/oddsHandball.fsf
Normal file
File diff suppressed because one or more lines are too long
1
for_parse/tennisMatchStat.fsf
Normal file
1
for_parse/tennisMatchStat.fsf
Normal file
@@ -0,0 +1 @@
|
|||||||
|
SE÷Match¬~SF÷Service¬~SG÷Aces¬SH÷3¬SI÷2¬~SG÷Double Faults¬SH÷0¬SI÷0¬~SG÷1st Serve Percentage¬SH÷71%¬SI÷76%¬~SG÷1st Serve Points Won¬SH÷63% (22/35)¬SI÷79% (23/29)¬~SG÷2nd Serve Points Won¬SH÷29% (4/14)¬SI÷56% (5/9)¬~SG÷Break Points Saved¬SH÷63% (5/8)¬SI÷100% (2/2)¬~SF÷Return¬~SG÷1st Return Points Won¬SH÷21% (6/29)¬SI÷37% (13/35)¬~SG÷2nd Return Points Won¬SH÷44% (4/9)¬SI÷71% (10/14)¬~SG÷Break Points Converted¬SH÷0% (0/2)¬SI÷38% (3/8)¬~SF÷Points¬~SG÷Max Points In Row¬SH÷5¬SI÷7¬~SG÷Service Points Won¬SH÷53% (26/49)¬SI÷74% (28/38)¬~SG÷Return Points Won¬SH÷26% (10/38)¬SI÷47% (23/49)¬~SG÷Total Points Won¬SH÷41% (36/87)¬SI÷59% (51/87)¬~SF÷Games¬~SG÷Max Games In Row¬SH÷1¬SI÷3¬~SG÷Service Games Won¬SH÷57% (4/7)¬SI÷100% (6/6)¬~SG÷Return Games Won¬SH÷0% (0/6)¬SI÷43% (3/7)¬~SG÷Total Games Won¬SH÷31% (4/13)¬SI÷69% (9/13)¬~SE÷Set 1¬~SF÷Service¬~SG÷Aces¬SH÷3¬SI÷2¬~SG÷Double Faults¬SH÷0¬SI÷0¬~SG÷1st Serve Percentage¬SH÷67%¬SI÷81%¬~SG÷1st Serve Points Won¬SH÷75% (12/16)¬SI÷82% (14/17)¬~SG÷2nd Serve Points Won¬SH÷13% (1/8)¬SI÷75% (3/4)¬~SG÷Break Points Saved¬SH÷33% (1/3)¬SI÷100% (2/2)¬~SF÷Return¬~SG÷1st Return Points Won¬SH÷18% (3/17)¬SI÷25% (4/16)¬~SG÷2nd Return Points Won¬SH÷25% (1/4)¬SI÷88% (7/8)¬~SG÷Break Points Converted¬SH÷0% (0/2)¬SI÷67% (2/3)¬~SF÷Points¬~SG÷Service Points Won¬SH÷54% (13/24)¬SI÷81% (17/21)¬~SG÷Return Points Won¬SH÷19% (4/21)¬SI÷46% (11/24)¬~SG÷Total Points Won¬SH÷38% (17/45)¬SI÷62% (28/45)¬~SF÷Games¬~SG÷Service Games Won¬SH÷50% (2/4)¬SI÷100% (4/4)¬~SG÷Return Games Won¬SH÷0% (0/4)¬SI÷50% (2/4)¬~SG÷Total Games Won¬SH÷25% (2/8)¬SI÷75% (6/8)¬~SE÷Set 2¬~SF÷Service¬~SG÷Aces¬SH÷0¬SI÷0¬~SG÷Double Faults¬SH÷0¬SI÷0¬~SG÷1st Serve Percentage¬SH÷76%¬SI÷71%¬~SG÷1st Serve Points Won¬SH÷53% (10/19)¬SI÷75% (9/12)¬~SG÷2nd Serve Points Won¬SH÷50% (3/6)¬SI÷40% (2/5)¬~SG÷Break Points Saved¬SH÷80% (4/5)¬SI÷0% (0/0)¬~SF÷Return¬~SG÷1st Return Points Won¬SH÷25% (3/12)¬SI÷47% (9/19)¬~SG÷2nd Return Points Won¬SH÷60% (3/5)¬SI÷50% (3/6)¬~SG÷Break Points Converted¬SH÷0% (0/0)¬SI÷20% (1/5)¬~SF÷Points¬~SG÷Service Points Won¬SH÷52% (13/25)¬SI÷65% (11/17)¬~SG÷Return Points Won¬SH÷35% (6/17)¬SI÷48% (12/25)¬~SG÷Total Points Won¬SH÷45% (19/42)¬SI÷55% (23/42)¬~SF÷Games¬~SG÷Service Games Won¬SH÷67% (2/3)¬SI÷100% (2/2)¬~SG÷Return Games Won¬SH÷0% (0/2)¬SI÷33% (1/3)¬~SG÷Total Games Won¬SH÷40% (2/5)¬SI÷60% (3/5)¬~A1÷9bec4594788454c378180830ab0eaa63¬~
|
||||||
1
for_parse/tennisOdds.fsf
Normal file
1
for_parse/tennisOdds.fsf
Normal file
File diff suppressed because one or more lines are too long
23
go.mod
Normal file
23
go.mod
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
module gordenko.dev/dima/flashscore
|
||||||
|
|
||||||
|
go 1.25.0
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/PuerkitoBio/goquery v1.12.0
|
||||||
|
github.com/go-sql-driver/mysql v1.10.0
|
||||||
|
golang.org/x/net v0.57.0
|
||||||
|
gordenko.dev/dima/fixme v0.0.0-20230801160335-c5c6b3b00ea2
|
||||||
|
gordenko.dev/dima/pretty v0.0.0-20221225212746-0c27d8c0ac69
|
||||||
|
gordenko.dev/dima/qx v0.0.0-20260720012756-9323ec898f91
|
||||||
|
gordenko.dev/dima/spider v0.0.0-20260721003948-776f3225b709
|
||||||
|
gordenko.dev/dima/textutil v0.0.0-20260718203502-62db7f60f8f7
|
||||||
|
gordenko.dev/dima/web v0.0.0-20260719063304-9b080f685b42
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
filippo.io/edwards25519 v1.2.0 // indirect
|
||||||
|
github.com/andybalholm/cascadia v1.3.3 // indirect
|
||||||
|
golang.org/x/crypto v0.54.0 // indirect
|
||||||
|
golang.org/x/text v0.40.0 // indirect
|
||||||
|
gordenko.dev/dima/httpreq v1.0.1-0.20230801160925-0916b3afaf24 // indirect
|
||||||
|
)
|
||||||
93
go.sum
Normal file
93
go.sum
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
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/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/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=
|
||||||
203
handball.sgo
Normal file
203
handball.sgo
Normal file
@@ -0,0 +1,203 @@
|
|||||||
|
package flashscore
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
webspider "spider"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/PuerkitoBio/goquery"
|
||||||
|
)
|
||||||
|
|
||||||
|
func listScheduledHandballMatches() (result []ScheduledMatch, err error) {
|
||||||
|
|
||||||
|
spider, err := webspider.NewSpider(nil)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Загружаем AJAX страницу с таблицами матчей
|
||||||
|
//url := fmt.Sprintf(h2hTablesURLPattern, matchID)
|
||||||
|
|
||||||
|
buf, err := spider.Get(handballAllGamesFeedURL, map[string]string{
|
||||||
|
"referer": "https://d.flashscore.com/x/feed/proxy-local",
|
||||||
|
"x-requested-with": "XMLHttpRequest",
|
||||||
|
// Важное поле! Код можно найти в исходниках либо подсмотреть. Без кода
|
||||||
|
// сервер отдает 401 ошибку
|
||||||
|
"x-fsign": feedSign,
|
||||||
|
"x-referer": "https://www.flashscore.com/handball/",
|
||||||
|
"x-geoip": "1",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("Load handball All Games feed: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err = parseListOfScheduledMatches(string(buf))
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("parseListOfScheduledMatches: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func Check() (err error) {
|
||||||
|
matches, err := listScheduledHandballMatches()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
matches = DiscardScheduledMatchesFurtherThan(matches, 24*time.Hour)
|
||||||
|
|
||||||
|
for _, m := range matches {
|
||||||
|
report, err := GetUpcomingMatchReport(m.MatchID)
|
||||||
|
if err != nil {
|
||||||
|
// log
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetUpcomingMatchReport(matchID string) (report HandballUpcomingMatchReport, err error) {
|
||||||
|
report.IsComplete = true
|
||||||
|
|
||||||
|
spider, err := webspider.NewSpider(nil)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Загружаем Landing
|
||||||
|
landingURL := fmt.Sprintf(h2hLandingURLPattern, matchID)
|
||||||
|
|
||||||
|
buf, err := spider.Get(landingURL, nil)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("load h2hLanding: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
doc, err := goquery.NewDocumentFromReader(bytes.NewBuffer(buf))
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("goquery.NewDocumentFromReader: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIX - добавить чемпионат и парсинг инфы
|
||||||
|
//_, _, report.Home, report.Away, err = s.parseFootballMatchInfo(doc)
|
||||||
|
//if err != nil {
|
||||||
|
// return
|
||||||
|
//}
|
||||||
|
|
||||||
|
// Загружаем AJAX страницу с таблицами матчей
|
||||||
|
url := fmt.Sprintf(h2hTablesURLPattern, matchID)
|
||||||
|
|
||||||
|
buf, err = spider.Get(url, map[string]string{
|
||||||
|
"referer": "https://d.flashscore.com/x/feed/proxy-local",
|
||||||
|
"x-requested-with": "XMLHttpRequest",
|
||||||
|
// Важное поле! Код можно найти в исходниках либо подсмотреть. Без кода
|
||||||
|
// сервер отдает 401 ошибку
|
||||||
|
"x-fsign": feedSign,
|
||||||
|
"x-referer": fmt.Sprintf(h2hLandingURLPattern, matchID),
|
||||||
|
"x-geoip": "1",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("load h2hTables: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
doc, err = goquery.NewDocumentFromReader(bytes.NewBuffer(buf))
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("goquery.NewDocumentFromReader: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// var (dirtyTeamID string
|
||||||
|
|
||||||
|
// OVERALL - последние матчи команд (хозяев и гостей)
|
||||||
|
// Внутри блока <div id="tab-h2h-overall" будут 2 таблицы:
|
||||||
|
// <table class="head_to_head h2h_home"
|
||||||
|
// <table class="head_to_head h2h_away"
|
||||||
|
//
|
||||||
|
// Строки (tr) внутри таблицы имеют вид:
|
||||||
|
// <tr class="odd highlight"
|
||||||
|
// <tr class="even highlight"
|
||||||
|
|
||||||
|
overall := doc.Find("#tab-h2h-overall")
|
||||||
|
if overall.Length() == 0 {
|
||||||
|
err = fmt.Errorf("Div #tab-h2h-overall not found")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
overall = overall.First()
|
||||||
|
|
||||||
|
// Последние матчи хозяев (во всех турнирах, на выезде и в гостях)
|
||||||
|
home := overall.Find(".h2h_home")
|
||||||
|
if home.Length() == 0 {
|
||||||
|
err = fmt.Errorf("Table .h2h_home not found")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
report.HomeTeamMatches, err = parseMatchesResults(home.First().Find(".highlight"))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Последние матчи AWAY команды (во всех турнирах, на выезде и в гостях)
|
||||||
|
away := overall.Find(".h2h_away")
|
||||||
|
if away.Length() == 0 {
|
||||||
|
err = fmt.Errorf("Table .h2h_away not found")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
report.AwayTeamMatches, err = parseMatchesResults(away.First().Find(".highlight"))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Последние личные встречи (во всех турнирах, на выезде и в гостях)
|
||||||
|
h2h := overall.Find(".h2h_mutual")
|
||||||
|
if home.Length() == 0 {
|
||||||
|
err = fmt.Errorf("Table .h2h_mutual not found")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
report.H2H, err = parseMatchesResults(h2h.First().Find(".highlight"))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
var hasLostMatch bool
|
||||||
|
|
||||||
|
hasLostMatch, report.HomeTeamMatches, err = s.addSummaryToFootballMatches(report.HomeTeamMatches)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if hasLostMatch {
|
||||||
|
report.IsComplete = false
|
||||||
|
}
|
||||||
|
|
||||||
|
hasLostMatch, report.AwayTeamMatches, err = s.addSummaryToFootballMatches(report.AwayTeamMatches)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if hasLostMatch {
|
||||||
|
report.IsComplete = false
|
||||||
|
}
|
||||||
|
|
||||||
|
hasLostMatch, report.H2H, err = s.addSummaryToFootballMatches(report.H2H)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if hasLostMatch {
|
||||||
|
report.IsComplete = false
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
// load odds +
|
||||||
|
report.Odds, err = loadFootballOdds(spider, matchID)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("loadOdds: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
14
helpers.go
Normal file
14
helpers.go
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
package flashscore
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
func DiscardScheduledMatchesFurtherThan(matches []ScheduledMatch, furtherThan time.Duration) (result []ScheduledMatch) {
|
||||||
|
now := time.Now()
|
||||||
|
|
||||||
|
for _, m := range matches {
|
||||||
|
if now.Add(furtherThan).After(m.StartTime) {
|
||||||
|
result = append(result, m)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
1310
html/england.html
Executable file
1310
html/england.html
Executable file
File diff suppressed because one or more lines are too long
1
html/fixtures-data-string
Executable file
1
html/fixtures-data-string
Executable file
File diff suppressed because one or more lines are too long
17
html/fixtures-div.html
Executable file
17
html/fixtures-div.html
Executable file
File diff suppressed because one or more lines are too long
1
html/fixtures-unescaped
Executable file
1
html/fixtures-unescaped
Executable file
File diff suppressed because one or more lines are too long
BIN
html/flashscore
Executable file
BIN
html/flashscore
Executable file
Binary file not shown.
85
html/football.match_sumamry.html
Executable file
85
html/football.match_sumamry.html
Executable file
@@ -0,0 +1,85 @@
|
|||||||
|
|
||||||
|
<div class="detailMS">
|
||||||
|
<div class="detailMS__incidentsHeader stage-12">
|
||||||
|
<div class="detailMS__headerText">1st Half</div>
|
||||||
|
<div class="detailMS__headerScore">
|
||||||
|
<span class="p1_home" title="">1</span> - <span class="p1_away" title="">0</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="detailMS__incidentRow incidentRow--home odd">
|
||||||
|
<div class="time-box">40'</div>
|
||||||
|
<div title="Harvey Barnes (Leicester) runs on to a clever<br />pass from Marc Albrighton and finds himself<br />one-on-one with the keeper. He still has<br />work to do but finishes with aplomb into<br />the back of the net." onmouseover="tt.show(this, event, true)" onmouseout="tt.hide(this)" class="icon-box soccer-ball">
|
||||||
|
<span class="icon soccer-ball"> </span>
|
||||||
|
</div>
|
||||||
|
<span class="participant-name">
|
||||||
|
<a href="#" onclick="window.open('/player/barnes-harvey/Wjj9WTTi/'); return false;">Barnes H.</a>
|
||||||
|
</span>
|
||||||
|
<span class="assist note-name">(<a href="#" onclick="window.open('/player/albrighton-marc/fLz0nwDI/'); return false;">Albrighton M.</a>)</span>
|
||||||
|
</div>
|
||||||
|
<div class="detailMS__incidentRow incidentRow--away even">
|
||||||
|
<div class="time-box">43'</div>
|
||||||
|
<div title="A yellow card for a tackle by Conor Hourihane<br />(Aston Villa). Michael Oliver doesn't hesitate<br />at all to make this decision." onmouseover="tt.show(this, event, false)" onmouseout="tt.hide(this)" class="icon-box y-card">
|
||||||
|
<span class="icon y-card"> </span>
|
||||||
|
</div>
|
||||||
|
<span class="subincident-penalty subincident-name">(Holding)</span>
|
||||||
|
<span class="participant-name">
|
||||||
|
<a href="#" onclick="window.open('/player/hourihane-conor/ruzYi0fn/'); return false;">Hourihane C.</a>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="detailMS__incidentsHeader stage-13">
|
||||||
|
<div class="detailMS__headerText">2nd Half</div>
|
||||||
|
<div class="detailMS__headerScore">
|
||||||
|
<span class="p2_home" title="">3</span> -<span class="p2_away" title="">0</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="detailMS__incidentRow incidentRow--home odd">
|
||||||
|
<div class="time-box">59'</div>
|
||||||
|
<div title="Substitution. Kelechi Iheanacho makes his<br />way off the pitch and Jamie Vardy (Leicester)<br />comes on as his replacement." onmouseover="tt.show(this, event, true)" onmouseout="tt.hide(this)" class="icon-box substitution-in">
|
||||||
|
<span class="icon substitution-in"> </span>
|
||||||
|
</div>
|
||||||
|
<span class="substitution-in-name">
|
||||||
|
<a href="#" onclick="window.open('/player/vardy-jamie/fP3OohgD/'); return false;">Vardy J.</a>
|
||||||
|
</span>
|
||||||
|
<span class="substitution-out-name">
|
||||||
|
<span class="icon substitution-out"> </span>
|
||||||
|
<a href="#" onclick="window.open('/player/iheanacho-kelechi/dIaES012/'); return false;">Iheanacho K.</a>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="detailMS__incidentRow incidentRow--home even">
|
||||||
|
<div class="time-box">63'</div>
|
||||||
|
<div title="Jamie Vardy (Leicester) makes no mistake<br />from the penalty spot after waiting for<br />Pepe Reina to move before slotting the ball<br />low into the middle of the goal!" onmouseover="tt.show(this, event, true)" onmouseout="tt.hide(this)" class="icon-box soccer-ball">
|
||||||
|
<span class="icon soccer-ball"> </span>
|
||||||
|
</div>
|
||||||
|
<span class="participant-name">
|
||||||
|
<a href="#" onclick="window.open('/player/vardy-jamie/fP3OohgD/'); return false;">Vardy J.</a>
|
||||||
|
</span>
|
||||||
|
<span class=" subincident-name">(Penalty)</span>
|
||||||
|
</div>
|
||||||
|
<div class="detailMS__incidentRow incidentRow--away odd">
|
||||||
|
<div class="time-box">63'</div>
|
||||||
|
<div title="Ahmed Elmohamady goes off the pitch and<br />Anwar El-Ghazi (Aston Villa) comes on." onmouseover="tt.show(this, event, false)" onmouseout="tt.hide(this)" class="icon-box substitution-in">
|
||||||
|
<span class="icon substitution-in"> </span>
|
||||||
|
</div>
|
||||||
|
<span class="substitution-in-name">
|
||||||
|
<a href="#" onclick="window.open('/player/el-ghazi-anwar/fsRCsnjP/'); return false;">El-Ghazi A.</a>
|
||||||
|
</span>
|
||||||
|
<span class="substitution-out-name">
|
||||||
|
<a href="#" onclick="window.open('/player/elmohamady-ahmed/CONYFX94/'); return false;">Elmohamady A.</a>
|
||||||
|
<span class="icon substitution-out"> </span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="detailMS__incidentRow incidentRow--away even">
|
||||||
|
<div class="time-box">67'</div>
|
||||||
|
<div title="Dean Smith has decided to make a change.<br />Keinan Davis (Aston Villa) replaces Conor<br />Hourihane." onmouseover="tt.show(this, event, false)" onmouseout="tt.hide(this)" class="icon-box substitution-in"><span class="icon substitution-in"> </span></div><span class="substitution-in-name"><a href="#" onclick="window.open('/player/davis-keinan/SKxz4XFS/'); return false;">Davis K.</a></span><span class="substitution-out-name"><a href="#" onclick="window.open('/player/hourihane-conor/ruzYi0fn/'); return false;">Hourihane C.</a><span class="icon substitution-out"> </span></span></div><div class="detailMS__incidentRow incidentRow--home odd"><div class="time-box">71'</div><div title="Dennis Praet (Leicester) sees a yellow card<br />from Michael Oliver after he sent one of<br />the opposing players to the ground." onmouseover="tt.show(this, event, true)" onmouseout="tt.hide(this)" class="icon-box y-card"><span class="icon y-card"> </span></div><span class="participant-name"><a href="#" onclick="window.open('/player/praet-dennis/dUXdn4vC/'); return false;">Praet D.</a></span><span class="subincident-penalty subincident-name">(Tripping)</span></div><div class="detailMS__incidentRow incidentRow--home even"><div class="time-box">76'</div><div title="Michael Oliver shows the yellow card to<br />Jonny Evans (Leicester) for a heavy challenge." onmouseover="tt.show(this, event, true)" onmouseout="tt.hide(this)" class="icon-box y-card"><span class="icon y-card"> </span></div><span class="participant-name"><a href="#" onclick="window.open('/player/evans-jonny/vo08q1o2/'); return false;">Evans J.</a></span><span class="subincident-penalty subincident-name">(Foul)</span></div><div class="detailMS__incidentRow incidentRow--home odd"><div class="time-box">76'</div><div title="Brendan Rodgers prepares a substitution.<br />Dennis Praet is replaced by Youri Tielemans<br />(Leicester)." onmouseover="tt.show(this, event, true)" onmouseout="tt.hide(this)" class="icon-box substitution-in"><span class="icon substitution-in"> </span></div><span class="substitution-in-name"><a href="#" onclick="window.open('/player/tielemans-youri/GtkTQyib/'); return false;">Tielemans Y.</a></span><span class="substitution-out-name"><span class="icon substitution-out"> </span><a href="#" onclick="window.open('/player/praet-dennis/dUXdn4vC/'); return false;">Praet D.</a></span></div><div class="detailMS__incidentRow incidentRow--home even"><div class="time-box">79'</div><div title="Jamie Vardy (Leicester) takes advantage<br />of a goalmouth scramble by calmly slotting<br />the ball into the bottom left corner of<br />the net!" onmouseover="tt.show(this, event, true)" onmouseout="tt.hide(this)" class="icon-box soccer-ball"><span class="icon soccer-ball"> </span></div><span class="participant-name"><a href="#" onclick="window.open('/player/vardy-jamie/fP3OohgD/'); return false;">Vardy J.</a></span></div><div class="detailMS__incidentRow incidentRow--home odd"><div class="time-box">83'</div><div title="The manager has decided to make a substitution<br />and Wilfred Ndidi walks off the pitch to<br />be replaced by Nampalys Mendy (Leicester)." onmouseover="tt.show(this, event, true)" onmouseout="tt.hide(this)" class="icon-box substitution-in"><span class="icon substitution-in"> </span></div><span class="substitution-in-name"><a href="#" onclick="window.open('/player/mendy-nampalys/SAjbGhS3/'); return false;">Mendy N.</a></span><span class="substitution-out-name"><span class="icon substitution-out"> </span><a href="#" onclick="window.open('/player/ndidi-wilfred/E71XyFGi/'); return false;">Ndidi W.</a></span></div><div class="detailMS__incidentRow incidentRow--home even"><div class="time-box">85'</div><div title="Harvey Barnes (Leicester) finds himself<br />in a very promising position after getting<br />on the end of a brilliant pass and fires<br />the ball inside the right post!" onmouseover="tt.show(this, event, true)" onmouseout="tt.hide(this)" class="icon-box soccer-ball"><span class="icon soccer-ball"> </span></div><span class="participant-name"><a href="#" onclick="window.open('/player/barnes-harvey/Wjj9WTTi/'); return false;">Barnes H.</a></span><span class="assist note-name">(<a href="#" onclick="window.open('/player/albrighton-marc/fLz0nwDI/'); return false;">Albrighton M.</a>)</span></div></div><div class="match-information-wrapper">
|
||||||
|
<div class="parts match-information">
|
||||||
|
<div>
|
||||||
|
<div class="stage-header">
|
||||||
|
<div class="h-part">Match Information</div>
|
||||||
|
</div>
|
||||||
|
<div class="match-information-data">
|
||||||
|
<div class="content">Referee: Oliver M. (Eng), </div>
|
||||||
|
<div class="content">Attendance: 32 125, </div>
|
||||||
|
<div class="content">Venue: King Power Stadium (Leicester)</div>
|
||||||
|
</div></div></div></div><div id="secret_hash"></div>
|
||||||
480
html/football.odds.html
Executable file
480
html/football.odds.html
Executable file
File diff suppressed because one or more lines are too long
20
html/football_h2h_tr.html
Executable file
20
html/football_h2h_tr.html
Executable file
@@ -0,0 +1,20 @@
|
|||||||
|
<tr class="odd highlight" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_z7bKeorA', null, false); });">
|
||||||
|
<td>
|
||||||
|
<span class="date">1583784000</span>
|
||||||
|
</td>
|
||||||
|
<td class="flag_td " title="Premier League (England)">
|
||||||
|
<span class="flag fl_198"></span>PL]
|
||||||
|
</td>
|
||||||
|
<td class="name">
|
||||||
|
<span><strong>Leicester</strong></span>
|
||||||
|
</td>
|
||||||
|
<td class="name highTeam">
|
||||||
|
<span>Aston Villa</span>
|
||||||
|
</td>
|
||||||
|
<td class="">
|
||||||
|
<span class="score"><strong>4 : 0</strong></span>
|
||||||
|
</td>
|
||||||
|
<td class="winLose">
|
||||||
|
<a title="Loss" class="wld wld--l">L</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
204
html/h2h.data.england.html
Executable file
204
html/h2h.data.england.html
Executable file
@@ -0,0 +1,204 @@
|
|||||||
|
<div class="color-px-spacer"> </div><div class="h2h-submenu"><ul class="ifmenu"><li class="divider"><li id="h2h-overall" class="li0"><span><a onclick="detail_tab(['head-2-head', 'all_h2h']);">Overall</a></span></li><li class="divider"><li id="h2h-home" class="li1"><span><a onclick="detail_tab(['head-2-head', '1_h2h']);">Aston Villa - Home</a></span></li><li class="divider"><li id="h2h-away" class="li2"><span><a onclick="detail_tab(['head-2-head', '2_h2h']);">Sheffield Utd - Away</a></span></li><li class="divider"></ul></div><div class="color-spacer lineup-spacer"></div><div id="tab-h2h-overall" style="display: block;"><div class="h2h-wrapper"><table class="head_to_head h2h_home"><colgroup><col width="1%"/><col width="1%"/><col width="48%"/><col width="48%"/><col width="1%"/><col width="1%"/></colgroup><thead><tr><td colspan="6" class="h-part last">Last matches: ASTON VILLA</td></tr></thead><tbody><tr class="odd highlight" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_z7bKeorA', null, false); });"><td><span class="date">1583784000</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name"><span><strong>Leicester</strong></span></td><td class="name highTeam"><span>Aston Villa</span></td><td class=""><span class="score"><strong>4 : 0</strong></span></td><td class="winLose"><a title="Loss" class="wld wld--l">L</a></td></tr><tr class="even highlight" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_OK2NPb5H', null, false); });"><td><span class="date">1583080200</span></td><td class="flag_td "
|
||||||
|
title="EFL Cup (England)"><span class="flag fl_198"></span>EFL</td><td class="name highTeam"><span>Aston Villa</span></td><td class="name"><span><strong>Manchester City</strong></span></td><td class=""><span class="score"><strong>1 : 2</strong></span></td><td class="winLose"><a title="Loss" class="wld wld--l">L</a></td></tr><tr class="odd highlight" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_GtjJSnEN', null, false); });"><td><span class="date">1582383600</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name"><span><strong>Southampton</strong></span></td><td class="name highTeam"><span>Aston Villa</span></td><td class=""><span class="score"><strong>2 : 0</strong></span></td><td class="winLose"><a title="Loss" class="wld wld--l">L</a></td></tr><tr class="even highlight" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_Sv4qwtWD', null, false); });"><td><span class="date">1581861600</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name highTeam"><span>Aston Villa</span></td><td class="name"><span><strong>Tottenham</strong></span></td><td class=""><span class="score"><strong>2 : 3</strong></span></td><td class="winLose"><a title="Loss" class="wld wld--l">L</a></td></tr><tr class="odd highlight" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_fLKR1J97', null, false); });"><td><span class="date">1580569200</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name"><span><strong>Bournemouth</strong></span></td><td class="name highTeam"><span>Aston Villa</span></td><td class=""><span class="score"><strong>2 : 1</strong></span></td><td class="winLose"><a title="Loss" class="wld wld--l">L</a></td></tr><tr class="even highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_EDD0cOI3', null, false); });"><td><span class="date">1580240700</span></td><td class="flag_td "
|
||||||
|
title="EFL Cup (England)"><span class="flag fl_198"></span>EFL</td><td class="name highTeam"><span><strong>Aston Villa</strong></span></td><td class="name"><span>Leicester</span></td><td class=""><span class="score"><strong>2 : 1</strong></span></td><td class="winLose"><a title="Win" class="wld wld--w">W</a></td></tr><tr class="odd highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_fXQp1nJd', null, false); });"><td><span class="date">1579635000</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name highTeam"><span><strong>Aston Villa</strong></span></td><td class="name"><span>Watford</span></td><td class=""><span class="score"><strong>2 : 1</strong></span></td><td class="winLose"><a title="Win" class="wld wld--w">W</a></td></tr><tr class="even highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_dKZ3Hnnt', null, false); });"><td><span class="date">1579359600</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name"><span>Brighton</span></td><td class="name highTeam"><span>Aston Villa</span></td><td class=""><span class="score"><strong>1 : 1</strong></span></td><td class="winLose"><a title="Draw" class="wld wld--d">D</a></td></tr><tr class="odd highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_nVe6899P', null, false); });"><td><span class="date">1578846600</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name highTeam"><span>Aston Villa</span></td><td class="name"><span><strong>Manchester City</strong></span></td><td class=""><span class="score"><strong>1 : 6</strong></span></td><td class="winLose"><a title="Loss" class="wld wld--l">L</a></td></tr><tr class="even highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_WlOeb43c', null, false); });"><td><span class="date">1578513600</span></td><td class="flag_td "
|
||||||
|
title="EFL Cup (England)"><span class="flag fl_198"></span>EFL</td><td class="name"><span>Leicester</span></td><td class="name highTeam"><span>Aston Villa</span></td><td class=""><span class="score"><strong>1 : 1</strong></span></td><td class="winLose"><a title="Draw" class="wld wld--d">D</a></td></tr><tr class="odd highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_lfjtUlPM', null, false); });"><td><span class="date">1578150060</span></td><td class="flag_td "
|
||||||
|
title="FA Cup (England)"><span class="flag fl_198"></span>FAC</td><td class="name"><span><strong>Fulham</strong></span></td><td class="name highTeam"><span>Aston Villa</span></td><td class=""><span class="score"><strong>2 : 1</strong></span></td><td class="winLose"><a title="Loss" class="wld wld--l">L</a></td></tr><tr class="even highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_j3tSfjuQ', null, false); });"><td><span class="date">1577881800</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name"><span>Burnley</span></td><td class="name highTeam"><span><strong>Aston Villa</strong></span></td><td class=""><span class="score"><strong>1 : 2</strong></span></td><td class="winLose"><a title="Win" class="wld wld--w">W</a></td></tr><tr class="odd highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_M7MJ3uue', null, false); });"><td><span class="date">1577545200</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name"><span><strong>Watford</strong></span></td><td class="name highTeam"><span>Aston Villa</span></td><td class=""><span class="score"><strong>3 : 0</strong></span></td><td class="winLose"><a title="Loss" class="wld wld--l">L</a></td></tr><tr class="even highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_bR8sqgtE', null, false); });"><td><span class="date">1577372400</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name highTeam"><span><strong>Aston Villa</strong></span></td><td class="name"><span>Norwich</span></td><td class=""><span class="score"><strong>1 : 0</strong></span></td><td class="winLose"><a title="Win" class="wld wld--w">W</a></td></tr><tr class="odd highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_EuEcq2zc', null, false); });"><td><span class="date">1576940400</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name highTeam"><span>Aston Villa</span></td><td class="name"><span><strong>Southampton</strong></span></td><td class=""><span class="score"><strong>1 : 3</strong></span></td><td class="winLose"><a title="Loss" class="wld wld--l">L</a></td></tr><tr class="even highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_A7aQqcfp', null, false); });"><td><span class="date">1576611900</span></td><td class="flag_td "
|
||||||
|
title="EFL Cup (England)"><span class="flag fl_198"></span>EFL</td><td class="name highTeam"><span><strong>Aston Villa</strong></span></td><td class="name"><span>Liverpool</span></td><td class=""><span class="score"><strong>5 : 0</strong></span></td><td class="winLose"><a title="Win" class="wld wld--w">W</a></td></tr><tr class="odd highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_vRNnRKKS', null, false); });"><td><span class="date">1576335600</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name"><span><strong>Sheffield Utd</strong></span></td><td class="name highTeam"><span>Aston Villa</span></td><td class=""><span class="score"><strong>2 : 0</strong></span></td><td class="winLose"><a title="Loss" class="wld wld--l">L</a></td></tr><tr class="even highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_thatKo7o', null, false); });"><td><span class="date">1575813600</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name highTeam"><span>Aston Villa</span></td><td class="name"><span><strong>Leicester</strong></span></td><td class=""><span class="score"><strong>1 : 4</strong></span></td><td class="winLose"><a title="Loss" class="wld wld--l">L</a></td></tr><tr class="odd highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_MmWJ2pxB', null, false); });"><td><span class="date">1575487800</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name"><span><strong>Chelsea</strong></span></td><td class="name highTeam"><span>Aston Villa</span></td><td class=""><span class="score"><strong>2 : 1</strong></span></td><td class="winLose"><a title="Loss" class="wld wld--l">L</a></td></tr><tr class="even highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_v5ETiTNh', null, false); });"><td><span class="date">1575217800</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name"><span>Manchester Utd</span></td><td class="name highTeam"><span>Aston Villa</span></td><td class=""><span class="score"><strong>2 : 2</strong></span></td><td class="winLose"><a title="Draw" class="wld wld--d">D</a></td></tr><tr class="odd highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_l2dMcbfE', null, false); });"><td><span class="date">1574712000</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name highTeam"><span><strong>Aston Villa</strong></span></td><td class="name"><span>Newcastle</span></td><td class=""><span class="score"><strong>2 : 0</strong></span></td><td class="winLose"><a title="Win" class="wld wld--w">W</a></td></tr><tr class="even highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_hzTXjSL1', null, false); });"><td><span class="date">1573394400</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name"><span><strong>Wolves</strong></span></td><td class="name highTeam"><span>Aston Villa</span></td><td class=""><span class="score"><strong>2 : 1</strong></span></td><td class="winLose"><a title="Loss" class="wld wld--l">L</a></td></tr><tr class="odd highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_OIHalZa3', null, false); });"><td><span class="date">1572706800</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name highTeam"><span>Aston Villa</span></td><td class="name"><span><strong>Liverpool</strong></span></td><td class=""><span class="score"><strong>1 : 2</strong></span></td><td class="winLose"><a title="Loss" class="wld wld--l">L</a></td></tr><tr class="even highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_CAtTaYiG', null, false); });"><td><span class="date">1572464700</span></td><td class="flag_td "
|
||||||
|
title="EFL Cup (England)"><span class="flag fl_198"></span>EFL</td><td class="name highTeam"><span><strong>Aston Villa</strong></span></td><td class="name"><span>Wolves</span></td><td class=""><span class="score"><strong>2 : 1</strong></span></td><td class="winLose"><a title="Win" class="wld wld--w">W</a></td></tr><tr class="odd highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_KOZXZZpA', null, false); });"><td><span class="date">1572089400</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name"><span><strong>Manchester City</strong></span></td><td class="name highTeam"><span>Aston Villa</span></td><td class=""><span class="score"><strong>3 : 0</strong></span></td><td class="winLose"><a title="Loss" class="wld wld--l">L</a></td></tr><tr class="even highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_Q3kEto2P', null, false); });"><td><span class="date">1571493600</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name highTeam"><span><strong>Aston Villa</strong></span></td><td class="name"><span>Brighton</span></td><td class=""><span class="score"><strong>2 : 1</strong></span></td><td class="winLose"><a title="Win" class="wld wld--w">W</a></td></tr><tr class="odd highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_UH9FLqQa', null, false); });"><td><span class="date">1570284000</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name"><span>Norwich</span></td><td class="name highTeam"><span><strong>Aston Villa</strong></span></td><td class=""><span class="score"><strong>1 : 5</strong></span></td><td class="winLose"><a title="Win" class="wld wld--w">W</a></td></tr><tr class="even highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_x02kHUYD', null, false); });"><td><span class="date">1569679200</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name highTeam"><span>Aston Villa</span></td><td class="name"><span>Burnley</span></td><td class=""><span class="score"><strong>2 : 2</strong></span></td><td class="winLose"><a title="Draw" class="wld wld--d">D</a></td></tr><tr class="odd highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_48g7pGOs', null, false); });"><td><span class="date">1569437100</span></td><td class="flag_td "
|
||||||
|
title="EFL Cup (England)"><span class="flag fl_198"></span>EFL</td><td class="name"><span>Brighton</span></td><td class="name highTeam"><span><strong>Aston Villa</strong></span></td><td class=""><span class="score"><strong>1 : 3</strong></span></td><td class="winLose"><a title="Win" class="wld wld--w">W</a></td></tr><tr class="even highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_4tJXu8C7', null, false); });"><td><span class="date">1569166200</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name"><span><strong>Arsenal</strong></span></td><td class="name highTeam"><span>Aston Villa</span></td><td class=""><span class="score"><strong>3 : 2</strong></span></td><td class="winLose"><a title="Loss" class="wld wld--l">L</a></td></tr><tr class="odd highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_GhCW40qi', null, false); });"><td><span class="date">1568660400</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name highTeam"><span>Aston Villa</span></td><td class="name"><span>West Ham</span></td><td class=""><span class="score"><strong>0 : 0</strong></span></td><td class="winLose"><a title="Draw" class="wld wld--d">D</a></td></tr><tr class="even highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_2kncj1Uc', null, false); });"><td><span class="date">1567260000</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name"><span><strong>Crystal Palace</strong></span></td><td class="name highTeam"><span>Aston Villa</span></td><td class=""><span class="score"><strong>1 : 0</strong></span></td><td class="winLose"><a title="Loss" class="wld wld--l">L</a></td></tr><tr class="odd highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_zmbIsbmI', null, false); });"><td><span class="date">1566931500</span></td><td class="flag_td "
|
||||||
|
title="EFL Cup (England)"><span class="flag fl_198"></span>EFL</td><td class="name"><span>Crewe</span></td><td class="name highTeam"><span><strong>Aston Villa</strong></span></td><td class=""><span class="score"><strong>1 : 6</strong></span></td><td class="winLose"><a title="Win" class="wld wld--w">W</a></td></tr><tr class="even highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_8hhFupGu', null, false); });"><td><span class="date">1566586800</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name highTeam"><span><strong>Aston Villa</strong></span></td><td class="name"><span>Everton</span></td><td class=""><span class="score"><strong>2 : 0</strong></span></td><td class="winLose"><a title="Win" class="wld wld--w">W</a></td></tr><tr class="odd highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_EogafSFP', null, false); });"><td><span class="date">1566050400</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name highTeam"><span>Aston Villa</span></td><td class="name"><span><strong>Bournemouth</strong></span></td><td class=""><span class="score"><strong>1 : 2</strong></span></td><td class="winLose"><a title="Loss" class="wld wld--l">L</a></td></tr><tr class="even highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_jXHoudAL', null, false); });"><td><span class="date">1565454600</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name"><span><strong>Tottenham</strong></span></td><td class="name highTeam"><span>Aston Villa</span></td><td class=""><span class="score"><strong>3 : 1</strong></span></td><td class="winLose"><a title="Loss" class="wld wld--l">L</a></td></tr><tr class="odd highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_ABTAIwRe', null, false); });"><td><span class="date">1564839000</span></td><td class="flag_td "
|
||||||
|
title="Club Friendly (World)"><span class="flag fl_8"></span>CF</td><td class="name"><span>RB Leipzig</span></td><td class="name highTeam"><span><strong>Aston Villa</strong></span></td><td class=""><span class="score"><strong>1 : 3</strong></span></td><td class="winLose"><a title="Win" class="wld wld--w">W</a></td></tr><tr class="even highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_MLwhJZlD', null, false); });"><td><span class="date">1564236000</span></td><td class="flag_td "
|
||||||
|
title="Club Friendly (World)"><span class="flag fl_8"></span>CF</td><td class="name"><span>Charlton</span></td><td class="name highTeam"><span><strong>Aston Villa</strong></span></td><td class=""><span class="score"><strong>1 : 4</strong></span></td><td class="winLose"><a title="Win" class="wld wld--w">W</a></td></tr><tr class="odd highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_lAx8pCmC', null, false); });"><td><span class="date">1563993900</span></td><td class="flag_td "
|
||||||
|
title="Club Friendly (World)"><span class="flag fl_8"></span>CF</td><td class="name"><span>Walsall</span></td><td class="name highTeam"><span><strong>Aston Villa</strong></span></td><td class=""><span class="score"><strong>1 : 5</strong></span></td><td class="winLose"><a title="Win" class="wld wld--w">W</a></td></tr><tr class="even highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_tQWmkzZt', null, false); });"><td><span class="date">1563714000</span></td><td class="flag_td "
|
||||||
|
title="Club Friendly (World)"><span class="flag fl_8"></span>CF</td><td class="name"><span>Shrewsbury</span></td><td class="name highTeam"><span><strong>Aston Villa</strong></span></td><td class=""><span class="score"><strong>0 : 1</strong></span></td><td class="winLose"><a title="Win" class="wld wld--w">W</a></td></tr><tr class="odd highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_pQ7hEwo0', null, false); });"><td><span class="date">1563408000</span></td><td class="flag_td "
|
||||||
|
title="Club Friendly (World)"><span class="flag fl_8"></span>CF</td><td class="name"><span>Minnesota United</span></td><td class="name highTeam"><span><strong>Aston Villa</strong></span></td><td class=""><span class="score"><strong>0 : 3</strong></span></td><td class="winLose"><a title="Win" class="wld wld--w">W</a></td></tr><tr class="even highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_KWJnxf6a', null, false); });"><td><span class="date">1558965600</span></td><td class="flag_td "
|
||||||
|
title="Championship (England)"><span class="flag fl_198"></span>CHA</td><td class="name highTeam"><span><strong>Aston Villa</strong></span></td><td class="name"><span>Derby</span></td><td class=""><span class="score"><strong>2 : 1</strong></span></td><td class="winLose"><a title="Win" class="wld wld--w">W</a></td></tr><tr class="odd highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_ARXKdR4H', null, false); });"><td><span class="date">1557860400</span></td><td class="flag_td "
|
||||||
|
title="Championship (England)"><span class="flag fl_198"></span>CHA</td><td class="name"><span><strong>West Brom</strong></span></td><td class="name highTeam"><span>Aston Villa</span></td><td class=""><span class="score"><strong>1 : 1</strong><br/>(1 : 0)</span></td><td class="winLose"><a title="Loss/Draw" class="wld wld--dl">D</a></td></tr><tr class="even highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_EavCbmz5', null, false); });"><td><span class="date">1557574200</span></td><td class="flag_td "
|
||||||
|
title="Championship (England)"><span class="flag fl_198"></span>CHA</td><td class="name highTeam"><span><strong>Aston Villa</strong></span></td><td class="name"><span>West Brom</span></td><td class=""><span class="score"><strong>2 : 1</strong></span></td><td class="winLose"><a title="Win" class="wld wld--w">W</a></td></tr><tr class="odd highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_MospggQp', null, false); });"><td><span class="date">1557055800</span></td><td class="flag_td "
|
||||||
|
title="Championship (England)"><span class="flag fl_198"></span>CHA</td><td class="name highTeam"><span>Aston Villa</span></td><td class="name"><span><strong>Norwich</strong></span></td><td class=""><span class="score"><strong>1 : 2</strong></span></td><td class="winLose"><a title="Loss" class="wld wld--l">L</a></td></tr><tr class="even highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_SO5l4JYo', null, false); });"><td><span class="date">1556449200</span></td><td class="flag_td "
|
||||||
|
title="Championship (England)"><span class="flag fl_198"></span>CHA</td><td class="name"><span>Leeds</span></td><td class="name highTeam"><span>Aston Villa</span></td><td class=""><span class="score"><strong>1 : 1</strong></span></td><td class="winLose"><a title="Draw" class="wld wld--d">D</a></td></tr><tr class="odd highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_lCal2xn1', null, false); });"><td><span class="date">1555934400</span></td><td class="flag_td "
|
||||||
|
title="Championship (England)"><span class="flag fl_198"></span>CHA</td><td class="name highTeam"><span><strong>Aston Villa</strong></span></td><td class="name"><span>Millwall</span></td><td class=""><span class="score"><strong>1 : 0</strong></span></td><td class="winLose"><a title="Win" class="wld wld--w">W</a></td></tr><tr class="even highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_GpM7ka2e', null, false); });"><td><span class="date">1555682400</span></td><td class="flag_td "
|
||||||
|
title="Championship (England)"><span class="flag fl_198"></span>CHA</td><td class="name"><span>Bolton</span></td><td class="name highTeam"><span><strong>Aston Villa</strong></span></td><td class=""><span class="score"><strong>0 : 2</strong></span></td><td class="winLose"><a title="Win" class="wld wld--w">W</a></td></tr><tr class="odd highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_n9f1rreS', null, false); });"><td><span class="date">1555164000</span></td><td class="flag_td "
|
||||||
|
title="Championship (England)"><span class="flag fl_198"></span>CHA</td><td class="name highTeam"><span><strong>Aston Villa</strong></span></td><td class="name"><span>Bristol City</span></td><td class=""><span class="score"><strong>2 : 1</strong></span></td><td class="winLose"><a title="Win" class="wld wld--w">W</a></td></tr><tr class="even highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_8IecqOuM', null, false); });"><td><span class="date">1554921900</span></td><td class="flag_td "
|
||||||
|
title="Championship (England)"><span class="flag fl_198"></span>CHA</td><td class="name"><span>Rotherham</span></td><td class="name highTeam"><span><strong>Aston Villa</strong></span></td><td class=""><span class="score"><strong>1 : 2</strong></span></td><td class="winLose"><a title="Win" class="wld wld--w">W</a></td></tr><tr class="hid"><td colspan="6" class="last lastR"><a class="show_more" onclick="glib_show_hidden('tab-h2h-overall','h2h_home',5); return false" href="#">Show more matches <span class="arrow"></span></a></td></tr></tbody></table></div><div class="h2h-wrapper"><table class="head_to_head h2h_away"><colgroup><col width="1%"/><col width="1%"/><col width="48%"/><col width="48%"/><col width="1%"/><col width="1%"/></colgroup><thead><tr><td colspan="6" class="h-part last">Last matches: SHEFFIELD UTD</td></tr></thead><tbody><tr class="odd highlight" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_OSjXhqSS', null, false); });"><td><span class="date">1583593200</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name highTeam"><span><strong>Sheffield Utd</strong></span></td><td class="name"><span>Norwich</span></td><td class=""><span class="score"><strong>1 : 0</strong></span></td><td class="winLose"><a title="Win" class="wld wld--w">W</a></td></tr><tr class="even highlight" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_vZNmafet', null, false); });"><td><span class="date">1583265600</span></td><td class="flag_td "
|
||||||
|
title="FA Cup (England)"><span class="flag fl_198"></span>FAC</td><td class="name"><span>Reading</span></td><td class="name highTeam"><span>Sheffield Utd</span></td><td class=""><span class="score"><strong>1 : 2</strong><br/>(1 : 1)</span></td><td class="winLose"><a title="Draw/Win" class="wld wld--wo">W</a></td></tr><tr class="odd highlight" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_IeXFTSbH', null, false); });"><td><span class="date">1582383600</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name highTeam"><span>Sheffield Utd</span></td><td class="name"><span>Brighton</span></td><td class=""><span class="score"><strong>1 : 1</strong></span></td><td class="winLose"><a title="Draw" class="wld wld--d">D</a></td></tr><tr class="even highlight" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_G4xXTyg0', null, false); });"><td><span class="date">1581256800</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name highTeam"><span><strong>Sheffield Utd</strong></span></td><td class="name"><span>Bournemouth</span></td><td class=""><span class="score"><strong>2 : 1</strong></span></td><td class="winLose"><a title="Win" class="wld wld--w">W</a></td></tr><tr class="odd highlight" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_KEv6PM1E', null, false); });"><td><span class="date">1580569200</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name"><span>Crystal Palace</span></td><td class="name highTeam"><span><strong>Sheffield Utd</strong></span></td><td class=""><span class="score"><strong>0 : 1</strong></span></td><td class="winLose"><a title="Win" class="wld wld--w">W</a></td></tr><tr class="even highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_SOiNQMcH', null, false); });"><td><span class="date">1579964400</span></td><td class="flag_td "
|
||||||
|
title="FA Cup (England)"><span class="flag fl_198"></span>FAC</td><td class="name"><span>Millwall</span></td><td class="name highTeam"><span><strong>Sheffield Utd</strong></span></td><td class=""><span class="score"><strong>0 : 2</strong></span></td><td class="winLose"><a title="Win" class="wld wld--w">W</a></td></tr><tr class="odd highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_KvOdbp3F', null, false); });"><td><span class="date">1579635000</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name highTeam"><span>Sheffield Utd</span></td><td class="name"><span><strong>Manchester City</strong></span></td><td class=""><span class="score"><strong>0 : 1</strong></span></td><td class="winLose"><a title="Loss" class="wld wld--l">L</a></td></tr><tr class="even highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_pngbd3GU', null, false); });"><td><span class="date">1579359600</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name"><span>Arsenal</span></td><td class="name highTeam"><span>Sheffield Utd</span></td><td class=""><span class="score"><strong>1 : 1</strong></span></td><td class="winLose"><a title="Draw" class="wld wld--d">D</a></td></tr><tr class="odd highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_AT0oa5WB', null, false); });"><td><span class="date">1578686400</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name highTeam"><span><strong>Sheffield Utd</strong></span></td><td class="name"><span>West Ham</span></td><td class=""><span class="score"><strong>1 : 0</strong></span></td><td class="winLose"><a title="Win" class="wld wld--w">W</a></td></tr><tr class="even highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_tQUREIJI', null, false); });"><td><span class="date">1578232860</span></td><td class="flag_td "
|
||||||
|
title="FA Cup (England)"><span class="flag fl_198"></span>FAC</td><td class="name highTeam"><span><strong>Sheffield Utd</strong></span></td><td class="name"><span>AFC Fylde</span></td><td class=""><span class="score"><strong>2 : 1</strong></span></td><td class="winLose"><a title="Win" class="wld wld--w">W</a></td></tr><tr class="odd highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_WdOfjAms', null, false); });"><td><span class="date">1577995200</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name"><span><strong>Liverpool</strong></span></td><td class="name highTeam"><span>Sheffield Utd</span></td><td class=""><span class="score"><strong>2 : 0</strong></span></td><td class="winLose"><a title="Loss" class="wld wld--l">L</a></td></tr><tr class="even highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_fcxuBNnL', null, false); });"><td><span class="date">1577642400</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name"><span><strong>Manchester City</strong></span></td><td class="name highTeam"><span>Sheffield Utd</span></td><td class=""><span class="score"><strong>2 : 0</strong></span></td><td class="winLose"><a title="Loss" class="wld wld--l">L</a></td></tr><tr class="odd highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_Oj1TZl30', null, false); });"><td><span class="date">1577372400</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name highTeam"><span>Sheffield Utd</span></td><td class="name"><span>Watford</span></td><td class=""><span class="score"><strong>1 : 1</strong></span></td><td class="winLose"><a title="Draw" class="wld wld--d">D</a></td></tr><tr class="even highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_SrI1rMk3', null, false); });"><td><span class="date">1576940400</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name"><span>Brighton</span></td><td class="name highTeam"><span><strong>Sheffield Utd</strong></span></td><td class=""><span class="score"><strong>0 : 1</strong></span></td><td class="winLose"><a title="Win" class="wld wld--w">W</a></td></tr><tr class="odd highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_vRNnRKKS', null, false); });"><td><span class="date">1576335600</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name highTeam"><span><strong>Sheffield Utd</strong></span></td><td class="name"><span>Aston Villa</span></td><td class=""><span class="score"><strong>2 : 0</strong></span></td><td class="winLose"><a title="Win" class="wld wld--w">W</a></td></tr><tr class="even highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_lOk1FNLG', null, false); });"><td><span class="date">1575813600</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name"><span>Norwich</span></td><td class="name highTeam"><span><strong>Sheffield Utd</strong></span></td><td class=""><span class="score"><strong>1 : 2</strong></span></td><td class="winLose"><a title="Win" class="wld wld--w">W</a></td></tr><tr class="odd highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_nVw75nhh', null, false); });"><td><span class="date">1575574200</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name highTeam"><span>Sheffield Utd</span></td><td class="name"><span><strong>Newcastle</strong></span></td><td class=""><span class="score"><strong>0 : 2</strong></span></td><td class="winLose"><a title="Loss" class="wld wld--l">L</a></td></tr><tr class="even highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_xKAlm5xO', null, false); });"><td><span class="date">1575208800</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name"><span>Wolves</span></td><td class="name highTeam"><span>Sheffield Utd</span></td><td class=""><span class="score"><strong>1 : 1</strong></span></td><td class="winLose"><a title="Draw" class="wld wld--d">D</a></td></tr><tr class="odd highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_86u5kz2f', null, false); });"><td><span class="date">1574613000</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name highTeam"><span>Sheffield Utd</span></td><td class="name"><span>Manchester Utd</span></td><td class=""><span class="score"><strong>3 : 3</strong></span></td><td class="winLose"><a title="Draw" class="wld wld--d">D</a></td></tr><tr class="even highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_bDTTi86e', null, false); });"><td><span class="date">1573311600</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name"><span>Tottenham</span></td><td class="name highTeam"><span>Sheffield Utd</span></td><td class=""><span class="score"><strong>1 : 1</strong></span></td><td class="winLose"><a title="Draw" class="wld wld--d">D</a></td></tr><tr class="odd highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_UXFstB7q', null, false); });"><td><span class="date">1572706800</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name highTeam"><span><strong>Sheffield Utd</strong></span></td><td class="name"><span>Burnley</span></td><td class=""><span class="score"><strong>3 : 0</strong></span></td><td class="winLose"><a title="Win" class="wld wld--w">W</a></td></tr><tr class="even highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_UJz9RBhj', null, false); });"><td><span class="date">1572098400</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name"><span>West Ham</span></td><td class="name highTeam"><span>Sheffield Utd</span></td><td class=""><span class="score"><strong>1 : 1</strong></span></td><td class="winLose"><a title="Draw" class="wld wld--d">D</a></td></tr><tr class="odd highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_QRX0XtPB', null, false); });"><td><span class="date">1571684400</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name highTeam"><span><strong>Sheffield Utd</strong></span></td><td class="name"><span>Arsenal</span></td><td class=""><span class="score"><strong>1 : 0</strong></span></td><td class="winLose"><a title="Win" class="wld wld--w">W</a></td></tr><tr class="even highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_jXcOJNfC', null, false); });"><td><span class="date">1570284000</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name"><span>Watford</span></td><td class="name highTeam"><span>Sheffield Utd</span></td><td class=""><span class="score"><strong>0 : 0</strong></span></td><td class="winLose"><a title="Draw" class="wld wld--d">D</a></td></tr><tr class="odd highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_xOpx8Qe0', null, false); });"><td><span class="date">1569670200</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name highTeam"><span>Sheffield Utd</span></td><td class="name"><span><strong>Liverpool</strong></span></td><td class=""><span class="score"><strong>0 : 1</strong></span></td><td class="winLose"><a title="Loss" class="wld wld--l">L</a></td></tr><tr class="even highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_jPHlcg8m', null, false); });"><td><span class="date">1569437100</span></td><td class="flag_td "
|
||||||
|
title="EFL Cup (England)"><span class="flag fl_198"></span>EFL</td><td class="name highTeam"><span>Sheffield Utd</span></td><td class="name"><span><strong>Sunderland</strong></span></td><td class=""><span class="score"><strong>0 : 1</strong></span></td><td class="winLose"><a title="Loss" class="wld wld--l">L</a></td></tr><tr class="odd highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_xpER1kZQ', null, false); });"><td><span class="date">1569074400</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name"><span>Everton</span></td><td class="name highTeam"><span><strong>Sheffield Utd</strong></span></td><td class=""><span class="score"><strong>0 : 2</strong></span></td><td class="winLose"><a title="Win" class="wld wld--w">W</a></td></tr><tr class="even highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_z1RGqjDr', null, false); });"><td><span class="date">1568469600</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name highTeam"><span>Sheffield Utd</span></td><td class="name"><span><strong>Southampton</strong></span></td><td class=""><span class="score"><strong>0 : 1</strong></span></td><td class="winLose"><a title="Loss" class="wld wld--l">L</a></td></tr><tr class="odd highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_KQdhisFi', null, false); });"><td><span class="date">1567260000</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name"><span>Chelsea</span></td><td class="name highTeam"><span>Sheffield Utd</span></td><td class=""><span class="score"><strong>2 : 2</strong></span></td><td class="winLose"><a title="Draw" class="wld wld--d">D</a></td></tr><tr class="even highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_nRNW0Lmt', null, false); });"><td><span class="date">1566931500</span></td><td class="flag_td "
|
||||||
|
title="EFL Cup (England)"><span class="flag fl_198"></span>EFL</td><td class="name highTeam"><span><strong>Sheffield Utd</strong></span></td><td class="name"><span>Blackburn</span></td><td class=""><span class="score"><strong>2 : 1</strong></span></td><td class="winLose"><a title="Win" class="wld wld--w">W</a></td></tr><tr class="odd highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_0MPRzMUA', null, false); });"><td><span class="date">1566655200</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name highTeam"><span>Sheffield Utd</span></td><td class="name"><span><strong>Leicester</strong></span></td><td class=""><span class="score"><strong>1 : 2</strong></span></td><td class="winLose"><a title="Loss" class="wld wld--l">L</a></td></tr><tr class="even highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_0rRnoOxC', null, false); });"><td><span class="date">1566133200</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name highTeam"><span><strong>Sheffield Utd</strong></span></td><td class="name"><span>Crystal Palace</span></td><td class=""><span class="score"><strong>1 : 0</strong></span></td><td class="winLose"><a title="Win" class="wld wld--w">W</a></td></tr><tr class="odd highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_OKxmKfYe', null, false); });"><td><span class="date">1565445600</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name"><span>Bournemouth</span></td><td class="name highTeam"><span>Sheffield Utd</span></td><td class=""><span class="score"><strong>1 : 1</strong></span></td><td class="winLose"><a title="Draw" class="wld wld--d">D</a></td></tr><tr class="even highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_6Z6ljPZD', null, false); });"><td><span class="date">1564846200</span></td><td class="flag_td "
|
||||||
|
title="Club Friendly (World)"><span class="flag fl_8"></span>CF</td><td class="name"><span><strong>Reims</strong></span></td><td class="name highTeam"><span>Sheffield Utd</span></td><td class=""><span class="score"><strong>3 : 1</strong></span></td><td class="winLose"><a title="Loss" class="wld wld--l">L</a></td></tr><tr class="odd highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_veRqu6Vd', null, false); });"><td><span class="date">1564225200</span></td><td class="flag_td "
|
||||||
|
title="Club Friendly (World)"><span class="flag fl_8"></span>CF</td><td class="name"><span>Barnsley</span></td><td class="name highTeam"><span><strong>Sheffield Utd</strong></span></td><td class=""><span class="score"><strong>1 : 4</strong></span></td><td class="winLose"><a title="Win" class="wld wld--w">W</a></td></tr><tr class="even highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_Qiw2GMhd', null, false); });"><td><span class="date">1563906600</span></td><td class="flag_td "
|
||||||
|
title="Club Friendly (World)"><span class="flag fl_8"></span>CF</td><td class="name"><span>Chesterfield</span></td><td class="name highTeam"><span><strong>Sheffield Utd</strong></span></td><td class=""><span class="score"><strong>0 : 5</strong></span></td><td class="winLose"><a title="Win" class="wld wld--w">W</a></td></tr><tr class="odd highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_ClZcH2wj', null, false); });"><td><span class="date">1563631200</span></td><td class="flag_td "
|
||||||
|
title="Club Friendly (World)"><span class="flag fl_8"></span>CF</td><td class="name"><span>Northampton</span></td><td class="name highTeam"><span><strong>Sheffield Utd</strong></span></td><td class=""><span class="score"><strong>0 : 2</strong></span></td><td class="winLose"><a title="Win" class="wld wld--w">W</a></td></tr><tr class="even highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_8K2MMVTR', null, false); });"><td><span class="date">1563302700</span></td><td class="flag_td "
|
||||||
|
title="Club Friendly (World)"><span class="flag fl_8"></span>CF</td><td class="name"><span><strong>Burton</strong></span></td><td class="name highTeam"><span>Sheffield Utd</span></td><td class=""><span class="score"><strong>2 : 1</strong></span></td><td class="winLose"><a title="Loss" class="wld wld--l">L</a></td></tr><tr class="odd highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_0zXQarPT', null, false); });"><td><span class="date">1562958000</span></td><td class="flag_td "
|
||||||
|
title="Club Friendly (World)"><span class="flag fl_8"></span>CF</td><td class="name"><span>Betis</span></td><td class="name highTeam"><span><strong>Sheffield Utd</strong></span></td><td class=""><span class="score"><strong>0 : 1</strong></span></td><td class="winLose"><a title="Win" class="wld wld--w">W</a></td></tr><tr class="even highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_tQrF6Cfp', null, false); });"><td><span class="date">1557055800</span></td><td class="flag_td "
|
||||||
|
title="Championship (England)"><span class="flag fl_198"></span>CHA</td><td class="name"><span>Stoke</span></td><td class="name highTeam"><span>Sheffield Utd</span></td><td class=""><span class="score"><strong>2 : 2</strong></span></td><td class="winLose"><a title="Draw" class="wld wld--d">D</a></td></tr><tr class="odd highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_210DbF2N', null, false); });"><td><span class="date">1556381700</span></td><td class="flag_td "
|
||||||
|
title="Championship (England)"><span class="flag fl_198"></span>CHA</td><td class="name highTeam"><span><strong>Sheffield Utd</strong></span></td><td class="name"><span>Ipswich</span></td><td class=""><span class="score"><strong>2 : 0</strong></span></td><td class="winLose"><a title="Win" class="wld wld--w">W</a></td></tr><tr class="even highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_AJ987XNt', null, false); });"><td><span class="date">1555941600</span></td><td class="flag_td "
|
||||||
|
title="Championship (England)"><span class="flag fl_198"></span>CHA</td><td class="name"><span>Hull</span></td><td class="name highTeam"><span><strong>Sheffield Utd</strong></span></td><td class=""><span class="score"><strong>0 : 3</strong></span></td><td class="winLose"><a title="Win" class="wld wld--w">W</a></td></tr><tr class="odd highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_Gb1t4bHl', null, false); });"><td><span class="date">1555673400</span></td><td class="flag_td "
|
||||||
|
title="Championship (England)"><span class="flag fl_198"></span>CHA</td><td class="name highTeam"><span><strong>Sheffield Utd</strong></span></td><td class="name"><span>Nottingham</span></td><td class=""><span class="score"><strong>2 : 0</strong></span></td><td class="winLose"><a title="Win" class="wld wld--w">W</a></td></tr><tr class="even highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_n9GWcNfF', null, false); });"><td><span class="date">1555164000</span></td><td class="flag_td "
|
||||||
|
title="Championship (England)"><span class="flag fl_198"></span>CHA</td><td class="name highTeam"><span>Sheffield Utd</span></td><td class="name"><span>Millwall</span></td><td class=""><span class="score"><strong>1 : 1</strong></span></td><td class="winLose"><a title="Draw" class="wld wld--d">D</a></td></tr><tr class="odd highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_4Qbtm6tc', null, false); });"><td><span class="date">1554921900</span></td><td class="flag_td "
|
||||||
|
title="Championship (England)"><span class="flag fl_198"></span>CHA</td><td class="name"><span>Birmingham</span></td><td class="name highTeam"><span>Sheffield Utd</span></td><td class=""><span class="score"><strong>1 : 1</strong></span></td><td class="winLose"><a title="Draw" class="wld wld--d">D</a></td></tr><tr class="even highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_bXiIRbEJ', null, false); });"><td><span class="date">1554559200</span></td><td class="flag_td "
|
||||||
|
title="Championship (England)"><span class="flag fl_198"></span>CHA</td><td class="name"><span>Preston</span></td><td class="name highTeam"><span><strong>Sheffield Utd</strong></span></td><td class=""><span class="score"><strong>0 : 1</strong></span></td><td class="winLose"><a title="Win" class="wld wld--w">W</a></td></tr><tr class="odd highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_GljliIx7', null, false); });"><td><span class="date">1553958000</span></td><td class="flag_td "
|
||||||
|
title="Championship (England)"><span class="flag fl_198"></span>CHA</td><td class="name highTeam"><span>Sheffield Utd</span></td><td class="name"><span><strong>Bristol City</strong></span></td><td class=""><span class="score"><strong>2 : 3</strong></span></td><td class="winLose"><a title="Loss" class="wld wld--l">L</a></td></tr><tr class="even highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_2JBQb9vG', null, false); });"><td><span class="date">1552739400</span></td><td class="flag_td "
|
||||||
|
title="Championship (England)"><span class="flag fl_198"></span>CHA</td><td class="name"><span>Leeds</span></td><td class="name highTeam"><span><strong>Sheffield Utd</strong></span></td><td class=""><span class="score"><strong>0 : 1</strong></span></td><td class="winLose"><a title="Win" class="wld wld--w">W</a></td></tr><tr class="odd highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_nDUl33E4', null, false); });"><td><span class="date">1552419900</span></td><td class="flag_td "
|
||||||
|
title="Championship (England)"><span class="flag fl_198"></span>CHA</td><td class="name highTeam"><span><strong>Sheffield Utd</strong></span></td><td class="name"><span>Brentford</span></td><td class=""><span class="score"><strong>2 : 0</strong></span></td><td class="winLose"><a title="Win" class="wld wld--w">W</a></td></tr><tr class="even highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_v77NnrUN', null, false); });"><td><span class="date">1552133700</span></td><td class="flag_td "
|
||||||
|
title="Championship (England)"><span class="flag fl_198"></span>CHA</td><td class="name highTeam"><span><strong>Sheffield Utd</strong></span></td><td class="name"><span>Rotherham</span></td><td class=""><span class="score"><strong>2 : 0</strong></span></td><td class="winLose"><a title="Win" class="wld wld--w">W</a></td></tr><tr class="hid"><td colspan="6" class="last lastR"><a class="show_more" onclick="glib_show_hidden('tab-h2h-overall','h2h_away',5); return false" href="#">Show more matches <span class="arrow"></span></a></td></tr></tbody></table></div><div class="h2h-wrapper">
|
||||||
|
|
||||||
|
<table class="head_to_head h2h_mutual"><colgroup><col width="1%"/><col width="1%"/><col width="48%"/><col width="48%"/><col width="2%"/></colgroup><thead>
|
||||||
|
|
||||||
|
<tr><td colspan="6" class="h-part last">Head-to-head matches: ASTON VILLA - SHEFFIELD UTD</td></tr></thead><tbody><tr class="odd highlight" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_vRNnRKKS', null, false); });"><td><span class="date">1576335600</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name"><span><strong>Sheffield Utd</strong></span></td><td class="name"><span>Aston Villa</span></td><td class="last"><span class="score"><strong>2 : 0</strong></span></td></tr><tr class="even highlight" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_G8pqgPk4', null, false); });"><td><span class="date">1549655100</span></td><td class="flag_td "
|
||||||
|
title="Championship (England)"><span class="flag fl_198"></span>CHA</td><td class="name"><span>Aston Villa</span></td><td class="name"><span>Sheffield Utd</span></td><td class="last"><span class="score"><strong>3 : 3</strong></span></td></tr><tr class="odd highlight" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_vqecO3iq', null, false); });"><td><span class="date">1535810400</span></td><td class="flag_td "
|
||||||
|
title="Championship (England)"><span class="flag fl_198"></span>CHA</td><td class="name"><span><strong>Sheffield Utd</strong></span></td><td class="name"><span>Aston Villa</span></td><td class="last"><span class="score"><strong>4 : 1</strong></span></td></tr><tr class="even highlight" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_06POS74c', null, false); });"><td><span class="date">1517341500</span></td><td class="flag_td "
|
||||||
|
title="Championship (England)"><span class="flag fl_198"></span>CHA</td><td class="name"><span>Sheffield Utd</span></td><td class="name"><span><strong>Aston Villa</strong></span></td><td class="last"><span class="score"><strong>0 : 1</strong></span></td></tr><tr class="odd highlight" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_Kvptsq7H', null, false); });"><td><span class="date">1514050200</span></td><td class="flag_td "
|
||||||
|
title="Championship (England)"><span class="flag fl_198"></span>CHA</td><td class="name"><span>Aston Villa</span></td><td class="name"><span>Sheffield Utd</span></td><td class="last"><span class="score"><strong>2 : 2</strong></span></td></tr><tr class="even highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_z7vempF1', null, false); });"><td><span class="date">1388847600</span></td><td class="flag_td "
|
||||||
|
title="FA Cup (England)"><span class="flag fl_198"></span>FAC</td><td class="name"><span>Aston Villa</span></td><td class="name"><span><strong>Sheffield Utd</strong></span></td><td class="last"><span class="score"><strong>1 : 2</strong></span></td></tr><tr class="odd highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_6Tz7GGmR', null, false); });"><td><span class="date">1294498800</span></td><td class="flag_td "
|
||||||
|
title="FA Cup (England)"><span class="flag fl_198"></span>FAC</td><td class="name"><span>Sheffield Utd</span></td><td class="name"><span><strong>Aston Villa</strong></span></td><td class="last"><span class="score"><strong>1 : 3</strong></span></td></tr><tr class="even highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_dCEqGp39', null, false); });"><td><span class="date">1178381700</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name"><span><strong>Aston Villa</strong></span></td><td class="name"><span>Sheffield Utd</span></td><td class="last"><span class="score"><strong>3 : 0</strong></span></td></tr><tr class="odd highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_ngN3tziG', null, false); });"><td><span class="date">1165867200</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name"><span>Sheffield Utd</span></td><td class="name"><span>Aston Villa</span></td><td class="last"><span class="score"><strong>2 : 2</strong></span></td></tr><tr class="even highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_hpfLrUu2', null, false); });"><td><span class="date">1105187400</span></td><td class="flag_td "
|
||||||
|
title="FA Cup (England)"><span class="flag fl_198"></span>FAC</td><td class="name"><span><strong>Sheffield Utd</strong></span></td><td class="name"><span>Aston Villa</span></td><td class="last"><span class="score"><strong>3 : 1</strong></span></td></tr><tr class="odd highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_Ozck43w1', null, false); });"><td><span class="date">766504800</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name"><span>Sheffield Utd</span></td><td class="name"><span><strong>Aston Villa</strong></span></td><td class="last"><span class="score"><strong>1 : 2</strong></span></td></tr><tr class="even highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_lUA7cUpC', null, false); });"><td><span class="date">753807600</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name"><span><strong>Aston Villa</strong></span></td><td class="name"><span>Sheffield Utd</span></td><td class="last"><span class="score"><strong>1 : 0</strong></span></td></tr><tr class="odd highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_tEGW7kYb', null, false); });"><td><span class="date">728146800</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name"><span><strong>Aston Villa</strong></span></td><td class="name"><span>Sheffield Utd</span></td><td class="last"><span class="score"><strong>3 : 1</strong></span></td></tr><tr class="even highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_6m0LQW8L', null, false); });"><td><span class="date">715096800</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name"><span>Sheffield Utd</span></td><td class="name"><span><strong>Aston Villa</strong></span></td><td class="last"><span class="score"><strong>0 : 2</strong></span></td></tr><tr class="odd highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_4n8isfBD', null, false); });"><td><span class="date">702050400</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name"><span>Aston Villa</span></td><td class="name"><span>Sheffield Utd</span></td><td class="last"><span class="score"><strong>1 : 1</strong></span></td></tr><tr class="even highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_4hPJg0u8', null, false); });"><td><span class="date">692722800</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name"><span><strong>Sheffield Utd</strong></span></td><td class="name"><span>Aston Villa</span></td><td class="last"><span class="score"><strong>2 : 0</strong></span></td></tr><tr class="odd highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_08AJAZ3n', null, false); });"><td><span class="date">667926000</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name"><span><strong>Sheffield Utd</strong></span></td><td class="name"><span>Aston Villa</span></td><td class="last"><span class="score"><strong>2 : 1</strong></span></td></tr><tr class="even highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_CEEUjQfN', null, false); });"><td><span class="date">660063600</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name"><span><strong>Aston Villa</strong></span></td><td class="name"><span>Sheffield Utd</span></td><td class="last"><span class="score"><strong>2 : 1</strong></span></td></tr><tr class="hid"><td colspan="6" class="last"><a class="show_more" onclick="glib_show_hidden('tab-h2h-overall','h2h_mutual',0); return false" href="#">Show more matches <span class="arrow"></span></a></td></tr></tbody></table></div></div>
|
||||||
|
|
||||||
|
|
||||||
|
<div id="tab-h2h-home" style="display: none;">
|
||||||
|
<div class="h2h-wrapper">
|
||||||
|
<table class="head_to_head h2h_home"><colgroup><col width="1%"/><col width="1%"/>
|
||||||
|
<col width="48%"/><col width="48%"/><col width="1%"/><col width="1%"/></colgroup>
|
||||||
|
<thead><tr><td colspan="6" class="h-part last">Last matches: ASTON VILLA</td></tr>
|
||||||
|
</thead><tbody>
|
||||||
|
<tr class="odd highlight" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_OK2NPb5H', null, false); });">
|
||||||
|
<td><span class="date">1583080200</span></td>
|
||||||
|
<td class="flag_td " title="EFL Cup (England)"><span class="flag fl_198"></span>EFL</td>
|
||||||
|
<td class="name highTeam"><span>Aston Villa</span></td>
|
||||||
|
<td class="name"><span><strong>Manchester City</strong></span></td>
|
||||||
|
<td class=""><span class="score"><strong>1 : 2</strong></span></td>
|
||||||
|
<td class="winLose"><a title="Loss" class="wld wld--l">L</a></td>
|
||||||
|
</tr><tr class="even highlight" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_Sv4qwtWD', null, false); });"><td><span class="date">1581861600</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name highTeam"><span>Aston Villa</span></td><td class="name"><span><strong>Tottenham</strong></span></td><td class=""><span class="score"><strong>2 : 3</strong></span></td><td class="winLose"><a title="Loss" class="wld wld--l">L</a></td></tr><tr class="odd highlight" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_EDD0cOI3', null, false); });"><td><span class="date">1580240700</span></td><td class="flag_td "
|
||||||
|
title="EFL Cup (England)"><span class="flag fl_198"></span>EFL</td><td class="name highTeam"><span><strong>Aston Villa</strong></span></td><td class="name"><span>Leicester</span></td><td class=""><span class="score"><strong>2 : 1</strong></span></td><td class="winLose"><a title="Win" class="wld wld--w">W</a></td></tr><tr class="even highlight" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_fXQp1nJd', null, false); });"><td><span class="date">1579635000</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name highTeam"><span><strong>Aston Villa</strong></span></td><td class="name"><span>Watford</span></td><td class=""><span class="score"><strong>2 : 1</strong></span></td><td class="winLose"><a title="Win" class="wld wld--w">W</a></td></tr><tr class="odd highlight" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_nVe6899P', null, false); });"><td><span class="date">1578846600</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name highTeam"><span>Aston Villa</span></td><td class="name"><span><strong>Manchester City</strong></span></td><td class=""><span class="score"><strong>1 : 6</strong></span></td><td class="winLose"><a title="Loss" class="wld wld--l">L</a></td></tr><tr class="even highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_bR8sqgtE', null, false); });"><td><span class="date">1577372400</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name highTeam"><span><strong>Aston Villa</strong></span></td><td class="name"><span>Norwich</span></td><td class=""><span class="score"><strong>1 : 0</strong></span></td><td class="winLose"><a title="Win" class="wld wld--w">W</a></td></tr><tr class="odd highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_EuEcq2zc', null, false); });"><td><span class="date">1576940400</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name highTeam"><span>Aston Villa</span></td><td class="name"><span><strong>Southampton</strong></span></td><td class=""><span class="score"><strong>1 : 3</strong></span></td><td class="winLose"><a title="Loss" class="wld wld--l">L</a></td></tr><tr class="even highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_A7aQqcfp', null, false); });"><td><span class="date">1576611900</span></td><td class="flag_td "
|
||||||
|
title="EFL Cup (England)"><span class="flag fl_198"></span>EFL</td><td class="name highTeam"><span><strong>Aston Villa</strong></span></td><td class="name"><span>Liverpool</span></td><td class=""><span class="score"><strong>5 : 0</strong></span></td><td class="winLose"><a title="Win" class="wld wld--w">W</a></td></tr><tr class="odd highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_thatKo7o', null, false); });"><td><span class="date">1575813600</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name highTeam"><span>Aston Villa</span></td><td class="name"><span><strong>Leicester</strong></span></td><td class=""><span class="score"><strong>1 : 4</strong></span></td><td class="winLose"><a title="Loss" class="wld wld--l">L</a></td></tr><tr class="even highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_l2dMcbfE', null, false); });"><td><span class="date">1574712000</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name highTeam"><span><strong>Aston Villa</strong></span></td><td class="name"><span>Newcastle</span></td><td class=""><span class="score"><strong>2 : 0</strong></span></td><td class="winLose"><a title="Win" class="wld wld--w">W</a></td></tr><tr class="odd highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_OIHalZa3', null, false); });"><td><span class="date">1572706800</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name highTeam"><span>Aston Villa</span></td><td class="name"><span><strong>Liverpool</strong></span></td><td class=""><span class="score"><strong>1 : 2</strong></span></td><td class="winLose"><a title="Loss" class="wld wld--l">L</a></td></tr><tr class="even highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_CAtTaYiG', null, false); });"><td><span class="date">1572464700</span></td><td class="flag_td "
|
||||||
|
title="EFL Cup (England)"><span class="flag fl_198"></span>EFL</td><td class="name highTeam"><span><strong>Aston Villa</strong></span></td><td class="name"><span>Wolves</span></td><td class=""><span class="score"><strong>2 : 1</strong></span></td><td class="winLose"><a title="Win" class="wld wld--w">W</a></td></tr><tr class="odd highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_Q3kEto2P', null, false); });"><td><span class="date">1571493600</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name highTeam"><span><strong>Aston Villa</strong></span></td><td class="name"><span>Brighton</span></td><td class=""><span class="score"><strong>2 : 1</strong></span></td><td class="winLose"><a title="Win" class="wld wld--w">W</a></td></tr><tr class="even highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_x02kHUYD', null, false); });"><td><span class="date">1569679200</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name highTeam"><span>Aston Villa</span></td><td class="name"><span>Burnley</span></td><td class=""><span class="score"><strong>2 : 2</strong></span></td><td class="winLose"><a title="Draw" class="wld wld--d">D</a></td></tr><tr class="odd highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_GhCW40qi', null, false); });"><td><span class="date">1568660400</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name highTeam"><span>Aston Villa</span></td><td class="name"><span>West Ham</span></td><td class=""><span class="score"><strong>0 : 0</strong></span></td><td class="winLose"><a title="Draw" class="wld wld--d">D</a></td></tr><tr class="even highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_8hhFupGu', null, false); });"><td><span class="date">1566586800</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name highTeam"><span><strong>Aston Villa</strong></span></td><td class="name"><span>Everton</span></td><td class=""><span class="score"><strong>2 : 0</strong></span></td><td class="winLose"><a title="Win" class="wld wld--w">W</a></td></tr><tr class="odd highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_EogafSFP', null, false); });"><td><span class="date">1566050400</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name highTeam"><span>Aston Villa</span></td><td class="name"><span><strong>Bournemouth</strong></span></td><td class=""><span class="score"><strong>1 : 2</strong></span></td><td class="winLose"><a title="Loss" class="wld wld--l">L</a></td></tr><tr class="even highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_KWJnxf6a', null, false); });"><td><span class="date">1558965600</span></td><td class="flag_td "
|
||||||
|
title="Championship (England)"><span class="flag fl_198"></span>CHA</td><td class="name highTeam"><span><strong>Aston Villa</strong></span></td><td class="name"><span>Derby</span></td><td class=""><span class="score"><strong>2 : 1</strong></span></td><td class="winLose"><a title="Win" class="wld wld--w">W</a></td></tr><tr class="odd highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_EavCbmz5', null, false); });"><td><span class="date">1557574200</span></td><td class="flag_td "
|
||||||
|
title="Championship (England)"><span class="flag fl_198"></span>CHA</td><td class="name highTeam"><span><strong>Aston Villa</strong></span></td><td class="name"><span>West Brom</span></td><td class=""><span class="score"><strong>2 : 1</strong></span></td><td class="winLose"><a title="Win" class="wld wld--w">W</a></td></tr><tr class="even highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_MospggQp', null, false); });"><td><span class="date">1557055800</span></td><td class="flag_td "
|
||||||
|
title="Championship (England)"><span class="flag fl_198"></span>CHA</td><td class="name highTeam"><span>Aston Villa</span></td><td class="name"><span><strong>Norwich</strong></span></td><td class=""><span class="score"><strong>1 : 2</strong></span></td><td class="winLose"><a title="Loss" class="wld wld--l">L</a></td></tr><tr class="odd highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_lCal2xn1', null, false); });"><td><span class="date">1555934400</span></td><td class="flag_td "
|
||||||
|
title="Championship (England)"><span class="flag fl_198"></span>CHA</td><td class="name highTeam"><span><strong>Aston Villa</strong></span></td><td class="name"><span>Millwall</span></td><td class=""><span class="score"><strong>1 : 0</strong></span></td><td class="winLose"><a title="Win" class="wld wld--w">W</a></td></tr><tr class="even highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_n9f1rreS', null, false); });"><td><span class="date">1555164000</span></td><td class="flag_td "
|
||||||
|
title="Championship (England)"><span class="flag fl_198"></span>CHA</td><td class="name highTeam"><span><strong>Aston Villa</strong></span></td><td class="name"><span>Bristol City</span></td><td class=""><span class="score"><strong>2 : 1</strong></span></td><td class="winLose"><a title="Win" class="wld wld--w">W</a></td></tr><tr class="hid"><td colspan="6" class="last lastR"><a class="show_more" onclick="glib_show_hidden('tab-h2h-home','h2h_home',5); return false" href="#">Show more matches <span class="arrow"></span></a></td></tr></tbody></table></div><div class="h2h-wrapper">
|
||||||
|
|
||||||
|
<table class="head_to_head h2h_mutual"><colgroup><col width="1%"/><col width="1%"/><col width="48%"/><col width="48%"/><col width="2%"/></colgroup><thead><tr><td colspan="6" class="h-part last">Head-to-head matches: ASTON VILLA - SHEFFIELD UTD</td></tr></thead><tbody><tr class="odd highlight" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_G8pqgPk4', null, false); });"><td><span class="date">1549655100</span></td><td class="flag_td "
|
||||||
|
title="Championship (England)"><span class="flag fl_198"></span>CHA</td><td class="name"><span>Aston Villa</span></td><td class="name"><span>Sheffield Utd</span></td><td class="last"><span class="score"><strong>3 : 3</strong></span></td></tr><tr class="even highlight" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_Kvptsq7H', null, false); });"><td><span class="date">1514050200</span></td><td class="flag_td "
|
||||||
|
title="Championship (England)"><span class="flag fl_198"></span>CHA</td><td class="name"><span>Aston Villa</span></td><td class="name"><span>Sheffield Utd</span></td><td class="last"><span class="score"><strong>2 : 2</strong></span></td></tr><tr class="odd highlight" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_z7vempF1', null, false); });"><td><span class="date">1388847600</span></td><td class="flag_td "
|
||||||
|
title="FA Cup (England)"><span class="flag fl_198"></span>FAC</td><td class="name"><span>Aston Villa</span></td><td class="name"><span><strong>Sheffield Utd</strong></span></td><td class="last"><span class="score"><strong>1 : 2</strong></span></td></tr><tr class="even highlight" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_dCEqGp39', null, false); });"><td><span class="date">1178381700</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name"><span><strong>Aston Villa</strong></span></td><td class="name"><span>Sheffield Utd</span></td><td class="last"><span class="score"><strong>3 : 0</strong></span></td></tr><tr class="odd highlight" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_lUA7cUpC', null, false); });"><td><span class="date">753807600</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name"><span><strong>Aston Villa</strong></span></td><td class="name"><span>Sheffield Utd</span></td><td class="last"><span class="score"><strong>1 : 0</strong></span></td></tr><tr class="even highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_tEGW7kYb', null, false); });"><td><span class="date">728146800</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name"><span><strong>Aston Villa</strong></span></td><td class="name"><span>Sheffield Utd</span></td><td class="last"><span class="score"><strong>3 : 1</strong></span></td></tr><tr class="odd highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_4n8isfBD', null, false); });"><td><span class="date">702050400</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name"><span>Aston Villa</span></td><td class="name"><span>Sheffield Utd</span></td><td class="last"><span class="score"><strong>1 : 1</strong></span></td></tr><tr class="even highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_CEEUjQfN', null, false); });"><td><span class="date">660063600</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name"><span><strong>Aston Villa</strong></span></td><td class="name"><span>Sheffield Utd</span></td><td class="last"><span class="score"><strong>2 : 1</strong></span></td></tr><tr class="hid"><td colspan="6" class="last"><a class="show_more" onclick="glib_show_hidden('tab-h2h-home','h2h_mutual',0); return false" href="#">Show more matches <span class="arrow"></span></a></td></tr></tbody></table></div></div><div id="tab-h2h-away" style="display: none;"><div class="h2h-wrapper"><table class="head_to_head h2h_away"><colgroup><col width="1%"/><col width="1%"/><col width="48%"/><col width="48%"/><col width="1%"/><col width="1%"/></colgroup><thead><tr><td colspan="6" class="h-part last">Last matches: SHEFFIELD UTD</td></tr></thead><tbody><tr class="odd highlight" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_vZNmafet', null, false); });"><td><span class="date">1583265600</span></td><td class="flag_td "
|
||||||
|
title="FA Cup (England)"><span class="flag fl_198"></span>FAC</td><td class="name"><span>Reading</span></td><td class="name highTeam"><span>Sheffield Utd</span></td><td class=""><span class="score"><strong>1 : 2</strong><br/>(1 : 1)</span></td><td class="winLose"><a title="Draw/Win" class="wld wld--wo">W</a></td></tr><tr class="even highlight" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_KEv6PM1E', null, false); });"><td><span class="date">1580569200</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name"><span>Crystal Palace</span></td><td class="name highTeam"><span><strong>Sheffield Utd</strong></span></td><td class=""><span class="score"><strong>0 : 1</strong></span></td><td class="winLose"><a title="Win" class="wld wld--w">W</a></td></tr><tr class="odd highlight" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_SOiNQMcH', null, false); });"><td><span class="date">1579964400</span></td><td class="flag_td "
|
||||||
|
title="FA Cup (England)"><span class="flag fl_198"></span>FAC</td><td class="name"><span>Millwall</span></td><td class="name highTeam"><span><strong>Sheffield Utd</strong></span></td><td class=""><span class="score"><strong>0 : 2</strong></span></td><td class="winLose"><a title="Win" class="wld wld--w">W</a></td></tr><tr class="even highlight" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_pngbd3GU', null, false); });"><td><span class="date">1579359600</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name"><span>Arsenal</span></td><td class="name highTeam"><span>Sheffield Utd</span></td><td class=""><span class="score"><strong>1 : 1</strong></span></td><td class="winLose"><a title="Draw" class="wld wld--d">D</a></td></tr><tr class="odd highlight" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_WdOfjAms', null, false); });"><td><span class="date">1577995200</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name"><span><strong>Liverpool</strong></span></td><td class="name highTeam"><span>Sheffield Utd</span></td><td class=""><span class="score"><strong>2 : 0</strong></span></td><td class="winLose"><a title="Loss" class="wld wld--l">L</a></td></tr><tr class="even highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_fcxuBNnL', null, false); });"><td><span class="date">1577642400</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name"><span><strong>Manchester City</strong></span></td><td class="name highTeam"><span>Sheffield Utd</span></td><td class=""><span class="score"><strong>2 : 0</strong></span></td><td class="winLose"><a title="Loss" class="wld wld--l">L</a></td></tr><tr class="odd highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_SrI1rMk3', null, false); });"><td><span class="date">1576940400</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name"><span>Brighton</span></td><td class="name highTeam"><span><strong>Sheffield Utd</strong></span></td><td class=""><span class="score"><strong>0 : 1</strong></span></td><td class="winLose"><a title="Win" class="wld wld--w">W</a></td></tr><tr class="even highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_lOk1FNLG', null, false); });"><td><span class="date">1575813600</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name"><span>Norwich</span></td><td class="name highTeam"><span><strong>Sheffield Utd</strong></span></td><td class=""><span class="score"><strong>1 : 2</strong></span></td><td class="winLose"><a title="Win" class="wld wld--w">W</a></td></tr><tr class="odd highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_xKAlm5xO', null, false); });"><td><span class="date">1575208800</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name"><span>Wolves</span></td><td class="name highTeam"><span>Sheffield Utd</span></td><td class=""><span class="score"><strong>1 : 1</strong></span></td><td class="winLose"><a title="Draw" class="wld wld--d">D</a></td></tr><tr class="even highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_bDTTi86e', null, false); });"><td><span class="date">1573311600</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name"><span>Tottenham</span></td><td class="name highTeam"><span>Sheffield Utd</span></td><td class=""><span class="score"><strong>1 : 1</strong></span></td><td class="winLose"><a title="Draw" class="wld wld--d">D</a></td></tr><tr class="odd highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_UJz9RBhj', null, false); });"><td><span class="date">1572098400</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name"><span>West Ham</span></td><td class="name highTeam"><span>Sheffield Utd</span></td><td class=""><span class="score"><strong>1 : 1</strong></span></td><td class="winLose"><a title="Draw" class="wld wld--d">D</a></td></tr><tr class="even highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_jXcOJNfC', null, false); });"><td><span class="date">1570284000</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name"><span>Watford</span></td><td class="name highTeam"><span>Sheffield Utd</span></td><td class=""><span class="score"><strong>0 : 0</strong></span></td><td class="winLose"><a title="Draw" class="wld wld--d">D</a></td></tr><tr class="odd highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_xpER1kZQ', null, false); });"><td><span class="date">1569074400</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name"><span>Everton</span></td><td class="name highTeam"><span><strong>Sheffield Utd</strong></span></td><td class=""><span class="score"><strong>0 : 2</strong></span></td><td class="winLose"><a title="Win" class="wld wld--w">W</a></td></tr><tr class="even highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_KQdhisFi', null, false); });"><td><span class="date">1567260000</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name"><span>Chelsea</span></td><td class="name highTeam"><span>Sheffield Utd</span></td><td class=""><span class="score"><strong>2 : 2</strong></span></td><td class="winLose"><a title="Draw" class="wld wld--d">D</a></td></tr><tr class="odd highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_OKxmKfYe', null, false); });"><td><span class="date">1565445600</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name"><span>Bournemouth</span></td><td class="name highTeam"><span>Sheffield Utd</span></td><td class=""><span class="score"><strong>1 : 1</strong></span></td><td class="winLose"><a title="Draw" class="wld wld--d">D</a></td></tr><tr class="even highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_6Z6ljPZD', null, false); });"><td><span class="date">1564846200</span></td><td class="flag_td "
|
||||||
|
title="Club Friendly (World)"><span class="flag fl_8"></span>CF</td><td class="name"><span><strong>Reims</strong></span></td><td class="name highTeam"><span>Sheffield Utd</span></td><td class=""><span class="score"><strong>3 : 1</strong></span></td><td class="winLose"><a title="Loss" class="wld wld--l">L</a></td></tr><tr class="odd highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_veRqu6Vd', null, false); });"><td><span class="date">1564225200</span></td><td class="flag_td "
|
||||||
|
title="Club Friendly (World)"><span class="flag fl_8"></span>CF</td><td class="name"><span>Barnsley</span></td><td class="name highTeam"><span><strong>Sheffield Utd</strong></span></td><td class=""><span class="score"><strong>1 : 4</strong></span></td><td class="winLose"><a title="Win" class="wld wld--w">W</a></td></tr><tr class="even highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_Qiw2GMhd', null, false); });"><td><span class="date">1563906600</span></td><td class="flag_td "
|
||||||
|
title="Club Friendly (World)"><span class="flag fl_8"></span>CF</td><td class="name"><span>Chesterfield</span></td><td class="name highTeam"><span><strong>Sheffield Utd</strong></span></td><td class=""><span class="score"><strong>0 : 5</strong></span></td><td class="winLose"><a title="Win" class="wld wld--w">W</a></td></tr><tr class="odd highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_ClZcH2wj', null, false); });"><td><span class="date">1563631200</span></td><td class="flag_td "
|
||||||
|
title="Club Friendly (World)"><span class="flag fl_8"></span>CF</td><td class="name"><span>Northampton</span></td><td class="name highTeam"><span><strong>Sheffield Utd</strong></span></td><td class=""><span class="score"><strong>0 : 2</strong></span></td><td class="winLose"><a title="Win" class="wld wld--w">W</a></td></tr><tr class="even highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_8K2MMVTR', null, false); });"><td><span class="date">1563302700</span></td><td class="flag_td "
|
||||||
|
title="Club Friendly (World)"><span class="flag fl_8"></span>CF</td><td class="name"><span><strong>Burton</strong></span></td><td class="name highTeam"><span>Sheffield Utd</span></td><td class=""><span class="score"><strong>2 : 1</strong></span></td><td class="winLose"><a title="Loss" class="wld wld--l">L</a></td></tr><tr class="odd highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_0zXQarPT', null, false); });"><td><span class="date">1562958000</span></td><td class="flag_td "
|
||||||
|
title="Club Friendly (World)"><span class="flag fl_8"></span>CF</td><td class="name"><span>Betis</span></td><td class="name highTeam"><span><strong>Sheffield Utd</strong></span></td><td class=""><span class="score"><strong>0 : 1</strong></span></td><td class="winLose"><a title="Win" class="wld wld--w">W</a></td></tr><tr class="even highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_tQrF6Cfp', null, false); });"><td><span class="date">1557055800</span></td><td class="flag_td "
|
||||||
|
title="Championship (England)"><span class="flag fl_198"></span>CHA</td><td class="name"><span>Stoke</span></td><td class="name highTeam"><span>Sheffield Utd</span></td><td class=""><span class="score"><strong>2 : 2</strong></span></td><td class="winLose"><a title="Draw" class="wld wld--d">D</a></td></tr><tr class="odd highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_AJ987XNt', null, false); });"><td><span class="date">1555941600</span></td><td class="flag_td "
|
||||||
|
title="Championship (England)"><span class="flag fl_198"></span>CHA</td><td class="name"><span>Hull</span></td><td class="name highTeam"><span><strong>Sheffield Utd</strong></span></td><td class=""><span class="score"><strong>0 : 3</strong></span></td><td class="winLose"><a title="Win" class="wld wld--w">W</a></td></tr><tr class="even highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_4Qbtm6tc', null, false); });"><td><span class="date">1554921900</span></td><td class="flag_td "
|
||||||
|
title="Championship (England)"><span class="flag fl_198"></span>CHA</td><td class="name"><span>Birmingham</span></td><td class="name highTeam"><span>Sheffield Utd</span></td><td class=""><span class="score"><strong>1 : 1</strong></span></td><td class="winLose"><a title="Draw" class="wld wld--d">D</a></td></tr><tr class="odd highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_bXiIRbEJ', null, false); });"><td><span class="date">1554559200</span></td><td class="flag_td "
|
||||||
|
title="Championship (England)"><span class="flag fl_198"></span>CHA</td><td class="name"><span>Preston</span></td><td class="name highTeam"><span><strong>Sheffield Utd</strong></span></td><td class=""><span class="score"><strong>0 : 1</strong></span></td><td class="winLose"><a title="Win" class="wld wld--w">W</a></td></tr><tr class="even highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_2JBQb9vG', null, false); });"><td><span class="date">1552739400</span></td><td class="flag_td "
|
||||||
|
title="Championship (England)"><span class="flag fl_198"></span>CHA</td><td class="name"><span>Leeds</span></td><td class="name highTeam"><span><strong>Sheffield Utd</strong></span></td><td class=""><span class="score"><strong>0 : 1</strong></span></td><td class="winLose"><a title="Win" class="wld wld--w">W</a></td></tr><tr class="hid"><td colspan="6" class="last lastR"><a class="show_more" onclick="glib_show_hidden('tab-h2h-away','h2h_away',5); return false" href="#">Show more matches <span class="arrow"></span></a></td></tr></tbody></table></div><div class="h2h-wrapper"><table class="head_to_head h2h_mutual"><colgroup><col width="1%"/><col width="1%"/><col width="48%"/><col width="48%"/><col width="2%"/></colgroup><thead><tr><td colspan="6" class="h-part last">Head-to-head matches: ASTON VILLA - SHEFFIELD UTD</td></tr></thead><tbody><tr class="odd highlight" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_G8pqgPk4', null, false); });"><td><span class="date">1549655100</span></td><td class="flag_td "
|
||||||
|
title="Championship (England)"><span class="flag fl_198"></span>CHA</td><td class="name"><span>Aston Villa</span></td><td class="name"><span>Sheffield Utd</span></td><td class="last"><span class="score"><strong>3 : 3</strong></span></td></tr><tr class="even highlight" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_Kvptsq7H', null, false); });"><td><span class="date">1514050200</span></td><td class="flag_td "
|
||||||
|
title="Championship (England)"><span class="flag fl_198"></span>CHA</td><td class="name"><span>Aston Villa</span></td><td class="name"><span>Sheffield Utd</span></td><td class="last"><span class="score"><strong>2 : 2</strong></span></td></tr><tr class="odd highlight" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_z7vempF1', null, false); });"><td><span class="date">1388847600</span></td><td class="flag_td "
|
||||||
|
title="FA Cup (England)"><span class="flag fl_198"></span>FAC</td><td class="name"><span>Aston Villa</span></td><td class="name"><span><strong>Sheffield Utd</strong></span></td><td class="last"><span class="score"><strong>1 : 2</strong></span></td></tr><tr class="even highlight" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_dCEqGp39', null, false); });"><td><span class="date">1178381700</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name"><span><strong>Aston Villa</strong></span></td><td class="name"><span>Sheffield Utd</span></td><td class="last"><span class="score"><strong>3 : 0</strong></span></td></tr><tr class="odd highlight" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_lUA7cUpC', null, false); });"><td><span class="date">753807600</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name"><span><strong>Aston Villa</strong></span></td><td class="name"><span>Sheffield Utd</span></td><td class="last"><span class="score"><strong>1 : 0</strong></span></td></tr><tr class="even highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_tEGW7kYb', null, false); });"><td><span class="date">728146800</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name"><span><strong>Aston Villa</strong></span></td><td class="name"><span>Sheffield Utd</span></td><td class="last"><span class="score"><strong>3 : 1</strong></span></td></tr><tr class="odd highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_4n8isfBD', null, false); });"><td><span class="date">702050400</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name"><span>Aston Villa</span></td><td class="name"><span>Sheffield Utd</span></td><td class="last"><span class="score"><strong>1 : 1</strong></span></td></tr><tr class="even highlight hidden" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_CEEUjQfN', null, false); });"><td><span class="date">660063600</span></td><td class="flag_td "
|
||||||
|
title="Premier League (England)"><span class="flag fl_198"></span>PL</td><td class="name"><span><strong>Aston Villa</strong></span></td><td class="name"><span>Sheffield Utd</span></td><td class="last"><span class="score"><strong>2 : 1</strong></span></td></tr><tr class="hid"><td colspan="6" class="last"><a class="show_more" onclick="glib_show_hidden('tab-h2h-away','h2h_mutual',0); return false" href="#">Show more matches <span class="arrow"></span></a></td></tr></tbody></table></div></div><div class="spacer-block"></div><div id="secret_hash"></div>
|
||||||
480
html/h2h.england.html
Executable file
480
html/h2h.england.html
Executable file
File diff suppressed because one or more lines are too long
9426
html/odds.html
Executable file
9426
html/odds.html
Executable file
File diff suppressed because it is too large
Load Diff
45
html/odds_full_time.tr.html
Executable file
45
html/odds_full_time.tr.html
Executable file
@@ -0,0 +1,45 @@
|
|||||||
|
<tr class="odd">
|
||||||
|
<td class="bookmaker">
|
||||||
|
<div class="blink" onclick="e_t.track_click('bookmaker-button-click', 'block-1x2_ft_bookmaker');">
|
||||||
|
<a href="/bookmaker/417/?sport=1&from=odds-comparison" onclick="return !window.open(this.href)" title="1xBet" class="elink">
|
||||||
|
<span style="background-image:url(/res/image/data/bookmakers/30-417.png); background-size: 100%; background-repeat: no-repeat;" class="detail-blogos"></span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div title="This match will be available for LIVE betting!" onmouseover="tt.show(this, event)" onmouseout="tt.hide(this)" class="live-icon-bookmaker" onclick="window.open('/bookmaker/417/?sport=1&from=live-detail' + (window.fs_detail && fs_detail.DA ? (fs_detail.DA == cjs.Api.constantsManager.getEventStageTypes().LIVE ? '-live-match' : '') : ''));">
|
||||||
|
<span class="icon live-offer1" onclick="e_t.track_click('bookmaker-button-click', 'block-1x2_ft_live-offer');"> </span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td class="kx" onclick="e_t.track_click('bookmaker-button-click', 'block-1x2_ft_1');">
|
||||||
|
<span class=" odds-wrap up" alt="3.01[u]3.24:">3.24</span>
|
||||||
|
</td>
|
||||||
|
<td class="kx max" onclick="e_t.track_click('bookmaker-button-click', 'block-1x2_ft_0');">
|
||||||
|
<span class=" odds-wrap down" alt="3.56[d]3.50:">3.50</span>
|
||||||
|
</td>
|
||||||
|
<td class="kx max" onclick="e_t.track_click('bookmaker-button-click', 'block-1x2_ft_2');">
|
||||||
|
<span class=" odds-wrap down" alt="2.49[d]2.37:">2.37</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr class="even">
|
||||||
|
<td class="bookmaker">
|
||||||
|
<div class="blink" onclick="e_t.track_click('bookmaker-button-click', 'block-1x2_ft_bookmaker');">
|
||||||
|
<a href="/bookmaker/417/?sport=1&from=odds-comparison" onclick="return !window.open(this.href)" title="1xBet" class="elink">
|
||||||
|
<span style="background-image:url(/res/image/data/bookmakers/30-417.png); background-size: 100%; background-repeat: no-repeat;" class="detail-blogos"></span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div title="This match will be available for LIVE betting!" onmouseover="tt.show(this, event)" onmouseout="tt.hide(this)" class="live-icon-bookmaker" onclick="window.open('/bookmaker/417/?sport=1&from=live-detail' + (window.fs_detail && fs_detail.DA ? (fs_detail.DA == cjs.Api.constantsManager.getEventStageTypes().LIVE ? '-live-match' : '') : ''));">
|
||||||
|
<span class="icon live-offer1" onclick="e_t.track_click('bookmaker-button-click', 'block-1x2_ft_live-offer');"> </span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td class="kx" onclick="e_t.track_click('bookmaker-button-click', 'block-1x2_ft_1');">
|
||||||
|
<span class=" odds-wrap up" alt="3.01[u]3.24:">3.24</span>
|
||||||
|
</td>
|
||||||
|
<td class="kx max" onclick="e_t.track_click('bookmaker-button-click', 'block-1x2_ft_0');">
|
||||||
|
<span class=" odds-wrap down" alt="3.56[d]3.50:">3.50</span>
|
||||||
|
</td>
|
||||||
|
<td class="kx max" onclick="e_t.track_click('bookmaker-button-click', 'block-1x2_ft_2');">
|
||||||
|
<span class=" odds-wrap down" alt="2.49[d]2.37:">2.37</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
1
html/temp.html
Executable file
1
html/temp.html
Executable file
File diff suppressed because one or more lines are too long
7
html/top-block
Executable file
7
html/top-block
Executable file
@@ -0,0 +1,7 @@
|
|||||||
|
AA÷h4n4CUOI¬AD÷1593630900¬ADE÷1593630900¬AB÷1¬CR÷1¬AC÷1¬CX÷West Ham¬ER÷Round 32¬RW÷0¬AX÷0¬BX÷-1¬WQ÷¬WM÷WHU¬AE÷West Ham¬JA÷bs9XOElg¬WU÷west-ham¬WN÷CHE¬AF÷Chelsea¬JB÷t6bxOY3a¬WV÷chelsea¬AW÷1¬AN÷n¬
|
||||||
|
|
||||||
|
AA÷pI7L1DA0¬AD÷1593104400¬ADE÷1593104400¬AB÷1¬CR÷1¬AC÷1¬CX÷Southampton¬ER÷Round 31¬RW÷0¬AX÷0¬BX÷-1¬WQ÷¬WM÷SOU¬AE÷Southampton¬JA÷0CQcsH5D¬WU÷southampton¬WN÷ARS¬AF÷Arsenal¬JB÷2H4t60rE¬WV÷arsenal¬AL÷{"1":[{"TVI":794,"BU":"https:\/\/www.mycanal.fr\/","BN":"CANAL+ Sport (Fra)"},{"TVI":11,"BU":"http:\/\/decouvrir.mycanal.fr\/","BN":"C+ Sport (Fra)"},{"TVI":680,"BU":"http:\/\/www.skysports.com\/","BN":"Sky Sports Premier League (UK\/Irl)"}]}¬BGS÷{"GB":[16],"CZ":[49,45],"SK":[411]}¬AW÷1¬AN÷n¬MW÷417|453|455|33|508|24|27|471|43¬
|
||||||
|
|
||||||
|
|
||||||
|
AA÷2y7t1tKk¬AD÷1592652600¬ADE÷1592652600¬AB÷1¬CR÷1¬AC÷1¬CX÷Watford¬ER÷Round 30¬RW÷0¬AX÷0¬BX÷-1¬WQ÷¬WM÷WAT¬AE÷Watford¬JA÷4bfJhj8c¬WU÷watford¬WN÷LEI¬AF÷Leicester¬JB÷bLgNiAN3¬WV÷leicester¬AL÷{"1":[{"TVI":361,"BU":"http:\/\/sport.bt.com\/","BN":"BT Sport"},{"TVI":73,"BU":"http:\/\/decouvrir.mycanal.fr\/","BN":"C+ France"}]}¬BGS÷{"GB":[16],"CZ":[49,45],"SK":[411]}¬AW÷1¬AN÷n¬MW÷417|453|454|455|33|544|508|24|27|471|43¬
|
||||||
|
|
||||||
124
main.xgo
Executable file
124
main.xgo
Executable file
@@ -0,0 +1,124 @@
|
|||||||
|
package flashscore
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
//"io/ioutil"
|
||||||
|
//webspider "spider"
|
||||||
|
//"strconv"
|
||||||
|
//"strings"
|
||||||
|
//"time"
|
||||||
|
"bytes"
|
||||||
|
|
||||||
|
"github.com/PuerkitoBio/goquery"
|
||||||
|
|
||||||
|
//"github.com/kr/pretty"
|
||||||
|
//"html"
|
||||||
|
"flashscore/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
//var (
|
||||||
|
// spider *webspider.Spider
|
||||||
|
//)
|
||||||
|
|
||||||
|
/*
|
||||||
|
err = ioutil.WriteFile("fixtures-unescaped", []byte(content), 0666)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
var trsrc = `<html>
|
||||||
|
<body>
|
||||||
|
<table>
|
||||||
|
<tr class="odd highlight" onclick="cjs.Api.loader.get('cjs').call(function(_cjs){_cjs.fromGlobalScope.detail_open('g_0_z7bKeorA', null, false); });">
|
||||||
|
<td>
|
||||||
|
<span class="date">1583784000</span>
|
||||||
|
</td>
|
||||||
|
<td class="flag_td " title="Premier League (England)">
|
||||||
|
<span class="flag fl_198"></span>PL]
|
||||||
|
</td>
|
||||||
|
<td class="name">
|
||||||
|
<span><strong>Leicester</strong></span>
|
||||||
|
</td>
|
||||||
|
<td class="name highTeam">
|
||||||
|
<span>Aston Villa</span>
|
||||||
|
</td>
|
||||||
|
<td class="">
|
||||||
|
<span class="score"><strong>4 : 0</strong></span>
|
||||||
|
</td>
|
||||||
|
<td class="winLose">
|
||||||
|
<a title="Loss" class="wld wld--l">L</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</body>
|
||||||
|
</html>`
|
||||||
|
|
||||||
|
func testParseTr(src string) (err error) {
|
||||||
|
doc, err := goquery.NewDocumentFromReader(bytes.NewBuffer([]byte(src)))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
txt, err := doc.Html()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("%s\n", txt)
|
||||||
|
|
||||||
|
|
||||||
|
trs := doc.Find("tr")
|
||||||
|
|
||||||
|
fmt.Printf("trs: %d\n", trs.Length())
|
||||||
|
*/
|
||||||
|
trs := doc.Find(".highlight")
|
||||||
|
for trIdx := range trs.Nodes {
|
||||||
|
var m model.TeamMatch
|
||||||
|
//fmt.Printf("trIdx: %d\n", trIdx)
|
||||||
|
|
||||||
|
m, err = parseMatchResultTr(trs.Eq(trIdx))
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("parseTr: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("%v\n", m)
|
||||||
|
fmt.Println()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
*/
|
||||||
|
// cut
|
||||||
|
/*
|
||||||
|
txt, err := first.Html()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = ioutil.WriteFile("temp", []byte(html.UnescapeString(txt)), 0666)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
// end cut
|
||||||
|
//n := first.Get(0)
|
||||||
|
|
||||||
|
//fmt.Printf("node: %#v\n\n", txt)
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
Всё. Я написал. Парсит следующие страницы:
|
||||||
|
|
||||||
|
https://www.flashscore.com/football/england/premier-league/fixtures/ - расписание матчей
|
||||||
|
|
||||||
|
https://www.flashscore.com/match/lUKXoAEo/#h2h;overall - H2H (все 3 вкладки: overall, home, away)
|
||||||
|
|
||||||
|
https://www.flashscore.com/match/lUKXoAEo/#odds-comparison;1x2-odds;full-time - коэфф. на FullTime 1 x 2 (плюс определяет букмекеров)
|
||||||
|
|
||||||
|
https://www.flashscore.com/match/Sv4qwtWD/#match-summary - из отчета по матчу достает только счет 1го и 2го тайма
|
||||||
|
|
||||||
|
*/
|
||||||
21
model/errors.go
Executable file
21
model/errors.go
Executable file
@@ -0,0 +1,21 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
import "gordenko.dev/dima/fixme"
|
||||||
|
|
||||||
|
const (
|
||||||
|
EmptyValue fixme.Code = "100"
|
||||||
|
UnknownZone fixme.Code = "101"
|
||||||
|
UnknownChamp fixme.Code = "102"
|
||||||
|
UnknownTeam fixme.Code = "103"
|
||||||
|
Duplicate fixme.Code = "104"
|
||||||
|
)
|
||||||
|
|
||||||
|
var errorMessages = map[fixme.Code]string{
|
||||||
|
EmptyValue: "Пустое значение недопустимо",
|
||||||
|
UnknownZone: "Неизвестная страна/регион",
|
||||||
|
UnknownChamp: "Неизвестный чемпионат",
|
||||||
|
UnknownTeam: "Неизвестная команда",
|
||||||
|
Duplicate: "Дубликат!",
|
||||||
|
}
|
||||||
|
|
||||||
|
var fix = fixme.New(errorMessages)
|
||||||
416
model/football.sgo
Normal file
416
model/football.sgo
Normal file
@@ -0,0 +1,416 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"web/api"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (s *Model) GetFootballZone(zoneID string) (_ *FootballZone, err error) {
|
||||||
|
zone := FootballZone{
|
||||||
|
ZoneID: zoneID,
|
||||||
|
}
|
||||||
|
|
||||||
|
found, err := s.qx.One(&zone)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return &zone, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Model) GetFootballChamp(champID string) (_ *FootballChamp, err error) {
|
||||||
|
champ := FootballChamp{
|
||||||
|
ChampID: champID,
|
||||||
|
}
|
||||||
|
|
||||||
|
found, err := s.qx.One(&champ)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return &champ, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Model) GetFootballTeam(teamID string) (_ *FootballTeam, err error) {
|
||||||
|
team := FootballTeam{
|
||||||
|
TeamID: teamID,
|
||||||
|
}
|
||||||
|
|
||||||
|
found, err := s.qx.One(&team)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return &team, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Model) GetFootballMatch(matchID string) (_ *FootballMatch, err error) {
|
||||||
|
m := FootballMatch{
|
||||||
|
MatchID: matchID,
|
||||||
|
}
|
||||||
|
|
||||||
|
found, err := s.qx.One(&m)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return &m, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Model) AddFootballZone(req FootballZone) (err error) {
|
||||||
|
if req.Name == "" {
|
||||||
|
err = api.Err{
|
||||||
|
Code: EmptyValue,
|
||||||
|
Field: "name",
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
tx, err := s.qx.Tx()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer tx.Close(&err)
|
||||||
|
|
||||||
|
_, err = tx.Insert(req)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = tx.Commit()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Model) AddFootballChamp(req FootballChamp) (err error) {
|
||||||
|
if req.Name == "" {
|
||||||
|
err = api.Err{
|
||||||
|
Code: EmptyValue,
|
||||||
|
Field: "name",
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
tx, err := s.qx.Tx()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer tx.Close(&err)
|
||||||
|
|
||||||
|
has, err := tx.Has("footballZones", "WHERE zoneID=?", req.ZoneID)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !has {
|
||||||
|
err = api.Err{
|
||||||
|
Code: UnknownZone,
|
||||||
|
Field: "zoneID",
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = tx.Insert(req)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = tx.Commit()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Model) UpdateFootballChampName(req FootballChamp) (err error) {
|
||||||
|
if req.Name == "" {
|
||||||
|
err = api.Err{
|
||||||
|
Code: EmptyValue,
|
||||||
|
Field: "name",
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = s.qx.Exec("UPDATE footballChamps SET name=? WHERE champID=?", req.Name, req.ChampID)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Model) AddFootballTeam(req FootballTeam) (err error) {
|
||||||
|
req.CanonicalName = NormalizeNameToAlias(req.CanonicalName)
|
||||||
|
|
||||||
|
if req.CanonicalName == "" {
|
||||||
|
err = api.Err{
|
||||||
|
Code: EmptyValue,
|
||||||
|
Field: "canonicalName",
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
tx, err := s.qx.Tx()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer tx.Close(&err)
|
||||||
|
|
||||||
|
_, err = tx.Insert(req)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Добавляем alias
|
||||||
|
_, err = tx.Insert(FootballAlias{
|
||||||
|
Name: req.CanonicalName,
|
||||||
|
TeamID: req.TeamID,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = tx.Commit()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Model) AddFootballTeamAlias(req FootballAlias) (err error) {
|
||||||
|
req.AliasID = 0
|
||||||
|
req.Name = NormalizeNameToAlias(req.Name)
|
||||||
|
|
||||||
|
if req.Name == "" {
|
||||||
|
err = api.Err{
|
||||||
|
Code: EmptyValue,
|
||||||
|
Field: "name",
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
tx, err := s.qx.Tx()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer tx.Close(&err)
|
||||||
|
|
||||||
|
hasTeam, err := tx.Has("footballTeams", "WHERE teamID=?", req.TeamID)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !hasTeam {
|
||||||
|
err = api.Err{
|
||||||
|
Code: UnknownTeam,
|
||||||
|
Field: "teamID",
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
hasAlias, err := tx.Has("footballTeamAliases", "WHERE teamID=? AND name=?",
|
||||||
|
req.TeamID, req.Name)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if hasAlias {
|
||||||
|
err = api.Err{
|
||||||
|
Code: Duplicate,
|
||||||
|
Field: "name",
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Добавляем alias
|
||||||
|
_, err = tx.Insert(req)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = tx.Commit()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Model) SetFootballTeamWasLinked(teamID string) (err error) {
|
||||||
|
err = s.qx.Exec(`UPDATE footballTeams SET wasLinked=true WHERE teamID=?`, teamID)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Model) RemoveFootballTeamAlias(aliasID int64) (err error) {
|
||||||
|
return s.qx.Exec(`DELETE FROM footballTeamAliases WHERE aliasID=?`, aliasID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Model) AddFootballMatch(req FootballMatch) (err error) {
|
||||||
|
tx, err := s.qx.Tx()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer tx.Close(&err)
|
||||||
|
|
||||||
|
req.MatchID = strings.TrimSpace(req.MatchID)
|
||||||
|
|
||||||
|
if req.MatchID == "" {
|
||||||
|
err = api.Err{
|
||||||
|
Code: EmptyValue,
|
||||||
|
Field: "matchID",
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
hasChamp, err := tx.Has("footballChamps", "WHERE champID=?", req.ChampID)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !hasChamp {
|
||||||
|
err = api.Err{
|
||||||
|
Code: UnknownChamp,
|
||||||
|
Field: "champID",
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
hasHomeTeam, err := tx.Has("footballTeams", "WHERE teamID=?", req.HomeTeamID)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !hasHomeTeam {
|
||||||
|
err = api.Err{
|
||||||
|
Code: UnknownTeam,
|
||||||
|
Field: "teamID",
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
hasAwayTeam, err := tx.Has("footballTeams", "WHERE teamID=?", req.AwayTeamID)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !hasAwayTeam {
|
||||||
|
err = api.Err{
|
||||||
|
Code: UnknownTeam,
|
||||||
|
Field: "teamID",
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = tx.Insert(req)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = tx.Commit()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
type UpdateFootballTeamCanonicalNameIn struct {
|
||||||
|
TeamID string
|
||||||
|
CanonicalName string
|
||||||
|
NewCanonicalName string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Model) UpdateFootballTeamCanonicalName(in UpdateFootballTeamCanonicalNameIn) (err error) {
|
||||||
|
tx, err := s.qx.Tx()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer tx.Close(&err)
|
||||||
|
|
||||||
|
// Старый canonicalName не нормализуем, ибо предполагается что имя мы получили из БД
|
||||||
|
in.NewCanonicalName = NormalizeNameToAlias(in.NewCanonicalName)
|
||||||
|
|
||||||
|
if in.NewCanonicalName == "" {
|
||||||
|
err = api.Err{
|
||||||
|
Code: EmptyValue,
|
||||||
|
Field: "newCanonicalName",
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Удаляем старый alias
|
||||||
|
err = tx.Exec("DELETE FROM footballTeamAliases WHERE teamID=? AND name=?",
|
||||||
|
in.TeamID, in.CanonicalName)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Обновляем canonicalName
|
||||||
|
err = tx.Exec("UPDATE footballTeams SET canonicalName=? WHERE teamID=?",
|
||||||
|
in.NewCanonicalName, in.TeamID)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("old: %q, new: %q, teamID: %s\n", in.CanonicalName,
|
||||||
|
in.NewCanonicalName, in.TeamID)
|
||||||
|
|
||||||
|
//return
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
hasAlias, err := tx.Has("footballTeamAliases", "WHERE teamID=? AND name=?",
|
||||||
|
in.TeamID, in.NewCanonicalName)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !hasAlias {
|
||||||
|
// Добавляем новый alias
|
||||||
|
_, err = tx.Insert(FootballAlias{
|
||||||
|
Name: in.NewCanonicalName,
|
||||||
|
TeamID: in.TeamID,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = tx.Commit()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Model) ListFootballZones() (list []FootballZone, err error) {
|
||||||
|
err = s.qx.ListBy(&list, `ORDER BY name ASC`)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Model) ListFootballTeamCandidates(name string) (matchIDs []string, err error) {
|
||||||
|
name = NormalizeNameToAlias(name)
|
||||||
|
|
||||||
|
err = s.qx.ListQuery(&matchIDs,
|
||||||
|
`SELECT teamID FROM footballTeamAliases WHERE name=?`, name)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Model) ListFootballTeamsByZone(zoneID string) (list []FootballTeam, err error) {
|
||||||
|
err = s.qx.ListBy(&list, `WHERE zoneID=? ORDER BY canonicalName ASC`, zoneID)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Model) ListFootballTeamAliases(teamID string) (list []FootballAlias, err error) {
|
||||||
|
err = s.qx.ListBy(&list, `WHERE teamID=? ORDER BY name ASC`, teamID)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Model) ListFootballTeams() (list []FootballTeam, err error) {
|
||||||
|
err = s.qx.List(&list)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
163
model/football.sql
Executable file
163
model/football.sql
Executable file
@@ -0,0 +1,163 @@
|
|||||||
|
CREATE TABLE footballZones (
|
||||||
|
zoneID VARCHAR(255) NOT NULL,
|
||||||
|
name VARCHAR(255) NOT NULL,
|
||||||
|
|
||||||
|
PRIMARY KEY(zoneID),
|
||||||
|
UNIQUE(name)
|
||||||
|
)
|
||||||
|
ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;
|
||||||
|
|
||||||
|
CREATE TABLE footballChamps (
|
||||||
|
champID VARCHAR(255) NOT NULL,
|
||||||
|
zoneID VARCHAR(255) NOT NULL,
|
||||||
|
name VARCHAR(255) NOT NULL,
|
||||||
|
|
||||||
|
PRIMARY KEY(champID),
|
||||||
|
UNIQUE(zoneID, name),
|
||||||
|
|
||||||
|
FOREIGN KEY (zoneID) REFERENCES footballZones(zoneID)
|
||||||
|
)
|
||||||
|
ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;
|
||||||
|
|
||||||
|
|
||||||
|
CREATE TABLE footballTeams (
|
||||||
|
teamID VARCHAR(255) NOT NULL,
|
||||||
|
zoneID VARCHAR(255) NOT NULL,
|
||||||
|
canonicalName VARCHAR(255) NOT NULL,
|
||||||
|
wasLinked BOOL NOT NULL,
|
||||||
|
|
||||||
|
PRIMARY KEY(teamID),
|
||||||
|
--UNIQUE(zoneID, canonicalName),
|
||||||
|
|
||||||
|
FOREIGN KEY (zoneID) REFERENCES footballZones(zoneID)
|
||||||
|
)
|
||||||
|
ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;
|
||||||
|
|
||||||
|
|
||||||
|
CREATE TABLE footballTeamAliases (
|
||||||
|
aliasID INT NOT NULL AUTO_INCREMENT,
|
||||||
|
teamID VARCHAR(255) NOT NULL,
|
||||||
|
name VARCHAR(255) NOT NULL,
|
||||||
|
|
||||||
|
PRIMARY KEY(aliasID),
|
||||||
|
UNIQUE(teamID, name),
|
||||||
|
|
||||||
|
FOREIGN KEY (teamID) REFERENCES footballTeams(teamID)
|
||||||
|
)
|
||||||
|
ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;
|
||||||
|
|
||||||
|
|
||||||
|
CREATE TABLE footballMatches (
|
||||||
|
matchID VARCHAR(255) NOT NULL,
|
||||||
|
startTime INT NOT NULL,
|
||||||
|
champID VARCHAR(255) NOT NULL,
|
||||||
|
homeTeamID VARCHAR(255) NOT NULL,
|
||||||
|
awayTeamID VARCHAR(255) NOT NULL,
|
||||||
|
homeGoals INT NOT NULL,
|
||||||
|
awayGoals INT NOT NULL,
|
||||||
|
hasStats BOOL NOT NULL,
|
||||||
|
p1HomeGoals INT NOT NULL,
|
||||||
|
p1AwayGoals INT NOT NULL,
|
||||||
|
p2HomeGoals INT NOT NULL,
|
||||||
|
p2AwayGoals INT NOT NULL,
|
||||||
|
|
||||||
|
PRIMARY KEY(matchID),
|
||||||
|
|
||||||
|
FOREIGN KEY (champID) REFERENCES footballChamps(champID),
|
||||||
|
FOREIGN KEY (homeTeamID) REFERENCES footballTeams(teamID),
|
||||||
|
FOREIGN KEY (awayTeamID) REFERENCES footballTeams(teamID)
|
||||||
|
)
|
||||||
|
ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;
|
||||||
|
|
||||||
|
--INSERT INTO footballChamps VALUES (NULL, "Seria A (Italy)");
|
||||||
|
--INSERT INTO footballChamps VALUES (NULL, "Bundesliga (Germany)");
|
||||||
|
--INSERT INTO footballChamps VALUES (NULL, "Premier League (England)");
|
||||||
|
|
||||||
|
delete from footballTeamAliases;
|
||||||
|
delete from footballMatches;
|
||||||
|
delete from footballTeams;
|
||||||
|
delete from footballChamps;
|
||||||
|
delete from footballZones;
|
||||||
|
|
||||||
|
|
||||||
|
-- HANDBALL
|
||||||
|
|
||||||
|
CREATE TABLE handballZones (
|
||||||
|
zoneID VARCHAR(255) NOT NULL,
|
||||||
|
name VARCHAR(255) NOT NULL,
|
||||||
|
|
||||||
|
PRIMARY KEY(zoneID),
|
||||||
|
UNIQUE(name)
|
||||||
|
)
|
||||||
|
ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;
|
||||||
|
|
||||||
|
CREATE TABLE handballChamps (
|
||||||
|
champID VARCHAR(255) NOT NULL,
|
||||||
|
zoneID VARCHAR(255) NOT NULL,
|
||||||
|
name VARCHAR(255) NOT NULL,
|
||||||
|
|
||||||
|
PRIMARY KEY(champID),
|
||||||
|
UNIQUE(zoneID, name),
|
||||||
|
|
||||||
|
FOREIGN KEY (zoneID) REFERENCES handballZones(zoneID)
|
||||||
|
)
|
||||||
|
ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;
|
||||||
|
|
||||||
|
|
||||||
|
CREATE TABLE handballTeams (
|
||||||
|
teamID VARCHAR(255) NOT NULL,
|
||||||
|
zoneID VARCHAR(255) NOT NULL,
|
||||||
|
canonicalName VARCHAR(255) NOT NULL,
|
||||||
|
wasLinked BOOL NOT NULL,
|
||||||
|
|
||||||
|
PRIMARY KEY(teamID),
|
||||||
|
|
||||||
|
FOREIGN KEY (zoneID) REFERENCES handballZones(zoneID)
|
||||||
|
)
|
||||||
|
ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;
|
||||||
|
|
||||||
|
|
||||||
|
CREATE TABLE handballTeamAliases (
|
||||||
|
aliasID INT NOT NULL AUTO_INCREMENT,
|
||||||
|
teamID VARCHAR(255) NOT NULL,
|
||||||
|
name VARCHAR(255) NOT NULL,
|
||||||
|
|
||||||
|
PRIMARY KEY(aliasID),
|
||||||
|
UNIQUE(teamID, name),
|
||||||
|
|
||||||
|
FOREIGN KEY (teamID) REFERENCES handballTeams(teamID)
|
||||||
|
)
|
||||||
|
ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;
|
||||||
|
|
||||||
|
|
||||||
|
CREATE TABLE handballMatches (
|
||||||
|
matchID VARCHAR(255) NOT NULL,
|
||||||
|
startTime INT NOT NULL,
|
||||||
|
champID VARCHAR(255) NOT NULL,
|
||||||
|
homeTeamID VARCHAR(255) NOT NULL,
|
||||||
|
awayTeamID VARCHAR(255) NOT NULL,
|
||||||
|
homeGoals INT NOT NULL,
|
||||||
|
awayGoals INT NOT NULL,
|
||||||
|
hasStats BOOL NOT NULL,
|
||||||
|
p1HomeGoals INT NOT NULL,
|
||||||
|
p1AwayGoals INT NOT NULL,
|
||||||
|
p2HomeGoals INT NOT NULL,
|
||||||
|
p2AwayGoals INT NOT NULL,
|
||||||
|
|
||||||
|
PRIMARY KEY(matchID),
|
||||||
|
|
||||||
|
FOREIGN KEY (champID) REFERENCES handballChamps(champID),
|
||||||
|
FOREIGN KEY (homeTeamID) REFERENCES handballTeams(teamID),
|
||||||
|
FOREIGN KEY (awayTeamID) REFERENCES handballTeams(teamID)
|
||||||
|
)
|
||||||
|
ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;
|
||||||
|
|
||||||
|
--INSERT INTO footballChamps VALUES (NULL, "Seria A (Italy)");
|
||||||
|
--INSERT INTO footballChamps VALUES (NULL, "Bundesliga (Germany)");
|
||||||
|
--INSERT INTO footballChamps VALUES (NULL, "Premier League (England)");
|
||||||
|
|
||||||
|
delete from handballTeamAliases;
|
||||||
|
delete from handballMatches;
|
||||||
|
delete from handballTeams;
|
||||||
|
delete from handballChamps;
|
||||||
|
delete from handballZones;
|
||||||
416
model/handball.sgo
Normal file
416
model/handball.sgo
Normal file
@@ -0,0 +1,416 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"web/api"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (s *Model) GetHandballZone(zoneID string) (_ *HandballZone, err error) {
|
||||||
|
zone := HandballZone{
|
||||||
|
ZoneID: zoneID,
|
||||||
|
}
|
||||||
|
|
||||||
|
found, err := s.qx.One(&zone)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return &zone, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Model) GetHandballChamp(champID string) (_ *HandballChamp, err error) {
|
||||||
|
champ := HandballChamp{
|
||||||
|
ChampID: champID,
|
||||||
|
}
|
||||||
|
|
||||||
|
found, err := s.qx.One(&champ)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return &champ, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Model) GetHandballTeam(teamID string) (_ *HandballTeam, err error) {
|
||||||
|
team := HandballTeam{
|
||||||
|
TeamID: teamID,
|
||||||
|
}
|
||||||
|
|
||||||
|
found, err := s.qx.One(&team)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return &team, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Model) GetHandballMatch(matchID string) (_ *HandballMatch, err error) {
|
||||||
|
m := HandballMatch{
|
||||||
|
MatchID: matchID,
|
||||||
|
}
|
||||||
|
|
||||||
|
found, err := s.qx.One(&m)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return &m, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Model) AddHandballZone(req HandballZone) (err error) {
|
||||||
|
if req.Name == "" {
|
||||||
|
err = api.Err{
|
||||||
|
Code: EmptyValue,
|
||||||
|
Field: "name",
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
tx, err := s.qx.Tx()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer tx.Close(&err)
|
||||||
|
|
||||||
|
_, err = tx.Insert(req)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = tx.Commit()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Model) AddHandballChamp(req HandballChamp) (err error) {
|
||||||
|
if req.Name == "" {
|
||||||
|
err = api.Err{
|
||||||
|
Code: EmptyValue,
|
||||||
|
Field: "name",
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
tx, err := s.qx.Tx()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer tx.Close(&err)
|
||||||
|
|
||||||
|
has, err := tx.Has("handballZones", "WHERE zoneID=?", req.ZoneID)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !has {
|
||||||
|
err = api.Err{
|
||||||
|
Code: UnknownZone,
|
||||||
|
Field: "zoneID",
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = tx.Insert(req)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = tx.Commit()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Model) UpdateHandballChampName(req HandballChamp) (err error) {
|
||||||
|
if req.Name == "" {
|
||||||
|
err = api.Err{
|
||||||
|
Code: EmptyValue,
|
||||||
|
Field: "name",
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = s.qx.Exec("UPDATE handballChamps SET name=? WHERE champID=?", req.Name, req.ChampID)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Model) AddHandballTeam(req HandballTeam) (err error) {
|
||||||
|
req.CanonicalName = NormalizeNameToAlias(req.CanonicalName)
|
||||||
|
|
||||||
|
if req.CanonicalName == "" {
|
||||||
|
err = api.Err{
|
||||||
|
Code: EmptyValue,
|
||||||
|
Field: "canonicalName",
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
tx, err := s.qx.Tx()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer tx.Close(&err)
|
||||||
|
|
||||||
|
_, err = tx.Insert(req)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Добавляем alias
|
||||||
|
_, err = tx.Insert(HandballAlias{
|
||||||
|
Name: req.CanonicalName,
|
||||||
|
TeamID: req.TeamID,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = tx.Commit()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Model) AddHandballTeamAlias(req HandballAlias) (err error) {
|
||||||
|
req.AliasID = 0
|
||||||
|
req.Name = NormalizeNameToAlias(req.Name)
|
||||||
|
|
||||||
|
if req.Name == "" {
|
||||||
|
err = api.Err{
|
||||||
|
Code: EmptyValue,
|
||||||
|
Field: "name",
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
tx, err := s.qx.Tx()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer tx.Close(&err)
|
||||||
|
|
||||||
|
hasTeam, err := tx.Has("handballTeams", "WHERE teamID=?", req.TeamID)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !hasTeam {
|
||||||
|
err = api.Err{
|
||||||
|
Code: UnknownTeam,
|
||||||
|
Field: "teamID",
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
hasAlias, err := tx.Has("handballTeamAliases", "WHERE teamID=? AND name=?",
|
||||||
|
req.TeamID, req.Name)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if hasAlias {
|
||||||
|
err = api.Err{
|
||||||
|
Code: Duplicate,
|
||||||
|
Field: "name",
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Добавляем alias
|
||||||
|
_, err = tx.Insert(req)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = tx.Commit()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Model) SetHandballTeamWasLinked(teamID string) (err error) {
|
||||||
|
err = s.qx.Exec(`UPDATE handballTeams SET wasLinked=true WHERE teamID=?`, teamID)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Model) RemoveHandballTeamAlias(aliasID int64) (err error) {
|
||||||
|
return s.qx.Exec(`DELETE FROM handballTeamAliases WHERE aliasID=?`, aliasID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Model) AddHandballMatch(req HandballMatch) (err error) {
|
||||||
|
tx, err := s.qx.Tx()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer tx.Close(&err)
|
||||||
|
|
||||||
|
req.MatchID = strings.TrimSpace(req.MatchID)
|
||||||
|
|
||||||
|
if req.MatchID == "" {
|
||||||
|
err = api.Err{
|
||||||
|
Code: EmptyValue,
|
||||||
|
Field: "matchID",
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
hasChamp, err := tx.Has("handballChamps", "WHERE champID=?", req.ChampID)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !hasChamp {
|
||||||
|
err = api.Err{
|
||||||
|
Code: UnknownChamp,
|
||||||
|
Field: "champID",
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
hasHomeTeam, err := tx.Has("handballTeams", "WHERE teamID=?", req.HomeTeamID)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !hasHomeTeam {
|
||||||
|
err = api.Err{
|
||||||
|
Code: UnknownTeam,
|
||||||
|
Field: "teamID",
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
hasAwayTeam, err := tx.Has("handballTeams", "WHERE teamID=?", req.AwayTeamID)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !hasAwayTeam {
|
||||||
|
err = api.Err{
|
||||||
|
Code: UnknownTeam,
|
||||||
|
Field: "teamID",
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = tx.Insert(req)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = tx.Commit()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
type UpdateHandballTeamCanonicalNameIn struct {
|
||||||
|
TeamID string
|
||||||
|
CanonicalName string
|
||||||
|
NewCanonicalName string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Model) UpdateHandballTeamCanonicalName(in UpdateHandballTeamCanonicalNameIn) (err error) {
|
||||||
|
tx, err := s.qx.Tx()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer tx.Close(&err)
|
||||||
|
|
||||||
|
// Старый canonicalName не нормализуем, ибо предполагается что имя мы получили из БД
|
||||||
|
in.NewCanonicalName = NormalizeNameToAlias(in.NewCanonicalName)
|
||||||
|
|
||||||
|
if in.NewCanonicalName == "" {
|
||||||
|
err = api.Err{
|
||||||
|
Code: EmptyValue,
|
||||||
|
Field: "newCanonicalName",
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Удаляем старый alias
|
||||||
|
err = tx.Exec("DELETE FROM handballTeamAliases WHERE teamID=? AND name=?",
|
||||||
|
in.TeamID, in.CanonicalName)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Обновляем canonicalName
|
||||||
|
err = tx.Exec("UPDATE handballTeams SET canonicalName=? WHERE teamID=?",
|
||||||
|
in.NewCanonicalName, in.TeamID)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("old: %q, new: %q, teamID: %s\n", in.CanonicalName,
|
||||||
|
in.NewCanonicalName, in.TeamID)
|
||||||
|
|
||||||
|
//return
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
hasAlias, err := tx.Has("handballTeamAliases", "WHERE teamID=? AND name=?",
|
||||||
|
in.TeamID, in.NewCanonicalName)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !hasAlias {
|
||||||
|
// Добавляем новый alias
|
||||||
|
_, err = tx.Insert(HandballAlias{
|
||||||
|
Name: in.NewCanonicalName,
|
||||||
|
TeamID: in.TeamID,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = tx.Commit()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Model) ListHandballZones() (list []HandballZone, err error) {
|
||||||
|
err = s.qx.ListBy(&list, `ORDER BY name ASC`)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Model) ListHandballTeamCandidates(name string) (matchIDs []string, err error) {
|
||||||
|
name = NormalizeNameToAlias(name)
|
||||||
|
|
||||||
|
err = s.qx.ListQuery(&matchIDs,
|
||||||
|
`SELECT teamID FROM handballTeamAliases WHERE name=?`, name)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Model) ListHandballTeamsByZone(zoneID string) (list []HandballTeam, err error) {
|
||||||
|
err = s.qx.ListBy(&list, `WHERE zoneID=? ORDER BY canonicalName ASC`, zoneID)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Model) ListHandballTeamAliases(teamID string) (list []HandballAlias, err error) {
|
||||||
|
err = s.qx.ListBy(&list, `WHERE teamID=? ORDER BY name ASC`, teamID)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Model) ListHandballTeams() (list []HandballTeam, err error) {
|
||||||
|
err = s.qx.List(&list)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
82
model/model.go
Executable file
82
model/model.go
Executable file
@@ -0,0 +1,82 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"gordenko.dev/dima/qx"
|
||||||
|
"gordenko.dev/dima/textutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
func New(qxdb *qx.Db) *Model {
|
||||||
|
s := new(Model)
|
||||||
|
s.qx = qxdb
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
var reCountryCode = regexp.MustCompile(`\([a-z]{3}\)`)
|
||||||
|
|
||||||
|
// NormalizeNameToAlias - удаляет лишние пробельные символы. Заменяет whitespace символы на пробел,
|
||||||
|
// приводит к нижнему регистру
|
||||||
|
func NormalizeNameToAlias(name string) string {
|
||||||
|
name = strings.ToLower(name)
|
||||||
|
|
||||||
|
// вырезаем код страны если есть (пример: (Aut))
|
||||||
|
name = reCountryCode.ReplaceAllString(name, "")
|
||||||
|
|
||||||
|
name = strings.TrimSpace(name)
|
||||||
|
|
||||||
|
return textutil.NormalizeWhitespaces(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
func (s *Model) ListFootballChamps() (list []FootballChamp, err error) {
|
||||||
|
err = s.qx.ListBy(&list, "ORDER BY name ASC")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func (s *Model) ListFootballChampTeams(champID int64) (list []Team, err error) {
|
||||||
|
err = s.qx.ListBy(&list, "WHERE champID=? ORDER BY canonicalName ASC", champID)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func (s *Model) ListFootballTeamAliases(teamID int64) (list []Alias, err error) {
|
||||||
|
err = s.qx.ListBy(&list, "WHERE teamID=? ORDER BY name ASC", teamID)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Model) RemoveFootballTeam(teamID int64) (err error) {
|
||||||
|
return s.qx.Exec("DELETE FROM footballTeams WHERE teamID=?", teamID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Model) RemoveFootballAlias(aliasID int64) (err error) {
|
||||||
|
return s.qx.Exec("DELETE FROM footballTeamAliases WHERE aliasID=?", aliasID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Model) AddFootballAlias(req AddFootballAliasReq) (aliasID int64, err error) {
|
||||||
|
if req.Name == "" {
|
||||||
|
err = api.Err{
|
||||||
|
Code: EmptyValue,
|
||||||
|
Field: "name",
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
tx, err := s.qx.Tx()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer tx.Close(&err)
|
||||||
|
|
||||||
|
aliasID, err = tx.Insert(req)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = tx.Commit()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
*/
|
||||||
422
model/team.go
Normal file
422
model/team.go
Normal file
@@ -0,0 +1,422 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (s *Model) GetTeamZone(zoneID string) (_ *TeamZone, err error) {
|
||||||
|
zone := TeamZone{
|
||||||
|
ZoneID: zoneID,
|
||||||
|
}
|
||||||
|
|
||||||
|
found, err := s.qx.One(&zone)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return &zone, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Model) GetTeamChamp(champID string) (_ *TeamChamp, err error) {
|
||||||
|
champ := TeamChamp{
|
||||||
|
ChampID: champID,
|
||||||
|
}
|
||||||
|
|
||||||
|
found, err := s.qx.One(&champ)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return &champ, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Model) GetTeam(teamID string) (_ *Team, err error) {
|
||||||
|
team := Team{
|
||||||
|
TeamID: teamID,
|
||||||
|
}
|
||||||
|
|
||||||
|
found, err := s.qx.One(&team)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return &team, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Model) GetTeamMatch(matchID string) (_ *TeamMatch, err error) {
|
||||||
|
m := TeamMatch{
|
||||||
|
MatchID: matchID,
|
||||||
|
}
|
||||||
|
|
||||||
|
found, err := s.qx.One(&m)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return &m, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Model) AddTeamZone(req TeamZone) (err error) {
|
||||||
|
if req.Name == "" {
|
||||||
|
err = fix.Field(EmptyValue, "name")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Name = strings.Title(strings.ToLower(req.Name))
|
||||||
|
|
||||||
|
tx, err := s.qx.Tx()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer tx.Close(&err)
|
||||||
|
|
||||||
|
_, err = tx.Insert(req)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = tx.Commit()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Model) AddTeamChamp(req TeamChamp) (err error) {
|
||||||
|
if req.Name == "" {
|
||||||
|
err = fix.Field(EmptyValue, "name")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
tx, err := s.qx.Tx()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer tx.Close(&err)
|
||||||
|
|
||||||
|
has, err := tx.Has("teamZones", "WHERE zoneID=?", req.ZoneID)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !has {
|
||||||
|
err = fix.Field(UnknownZone, "zoneID")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = tx.Insert(req)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = tx.Commit()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
func (s *Model) UpdateTeamChampName(req TeamChamp) (err error) {
|
||||||
|
if req.Name == "" {
|
||||||
|
err = fix.Field(EmptyValue, "name")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = s.qx.Exec("UPDATE teamChamps SET name=? WHERE champID=?", req.Name, req.ChampID)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
func (s *Model) AddTeam(req Team) (err error) {
|
||||||
|
aliasName := NormalizeNameToAlias(req.CanonicalName)
|
||||||
|
|
||||||
|
req.CanonicalName = strings.Title(strings.ToLower(aliasName))
|
||||||
|
|
||||||
|
if req.CanonicalName == "" {
|
||||||
|
err = fix.Field(EmptyValue, "canonicalName")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
tx, err := s.qx.Tx()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer tx.Close(&err)
|
||||||
|
|
||||||
|
_, err = tx.Insert(req)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
zone := TeamZone{
|
||||||
|
ZoneID: req.ZoneID,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = tx.One(&zone)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Добавляем alias
|
||||||
|
_, err = tx.Insert(TeamAlias{
|
||||||
|
Name: aliasName,
|
||||||
|
TeamID: req.TeamID,
|
||||||
|
SportID: zone.SportID,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = tx.Commit()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Model) AddTeamAlias(req TeamAlias) (err error) {
|
||||||
|
req.AliasID = 0
|
||||||
|
req.Name = NormalizeNameToAlias(req.Name)
|
||||||
|
|
||||||
|
if req.Name == "" {
|
||||||
|
err = fix.Field(EmptyValue, "name")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
tx, err := s.qx.Tx()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer tx.Close(&err)
|
||||||
|
|
||||||
|
// Чтобы узнать sportID сперва достаем Team
|
||||||
|
team := Team{
|
||||||
|
TeamID: req.TeamID,
|
||||||
|
}
|
||||||
|
|
||||||
|
found, err := tx.One(&team)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
err = fix.Field(UnknownTeam, "teamID")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Чтобы узнать sportID затем достаем teamZone
|
||||||
|
zone := TeamZone{
|
||||||
|
ZoneID: team.ZoneID,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = tx.One(&zone)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
req.SportID = zone.SportID
|
||||||
|
|
||||||
|
/*
|
||||||
|
hasTeam, err := tx.Has("teams", "WHERE teamID=?", req.TeamID)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !hasTeam {
|
||||||
|
err = fix.Field(UnknownTeam, "teamID")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
hasAlias, err := tx.Has("teamAliases", "WHERE teamID=? AND name=?",
|
||||||
|
req.TeamID, req.Name)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if hasAlias {
|
||||||
|
err = fix.Field(Duplicate, "name")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Добавляем alias
|
||||||
|
_, err = tx.Insert(req)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = tx.Commit()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Model) SetTeamWasLinked(teamID string) (err error) {
|
||||||
|
err = s.qx.Exec(`UPDATE teams SET wasLinked=true WHERE teamID=?`, teamID)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Model) RemoveTeamAlias(aliasID int64) (err error) {
|
||||||
|
return s.qx.Exec(`DELETE FROM teamAliases WHERE aliasID=?`, aliasID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Model) AddTeamMatch(req TeamMatch) (err error) {
|
||||||
|
tx, err := s.qx.Tx()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer tx.Close(&err)
|
||||||
|
|
||||||
|
req.MatchID = strings.TrimSpace(req.MatchID)
|
||||||
|
|
||||||
|
if req.MatchID == "" {
|
||||||
|
err = fix.Field(EmptyValue, "matchID")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
hasChamp, err := tx.Has("teamChamps", "WHERE champID=?", req.ChampID)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !hasChamp {
|
||||||
|
err = fix.Field(UnknownChamp, "champID")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
hasHomeTeam, err := tx.Has("teams", "WHERE teamID=?", req.HomeTeamID)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !hasHomeTeam {
|
||||||
|
err = fix.Field(UnknownTeam, "teamID")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
hasAwayTeam, err := tx.Has("teams", "WHERE teamID=?", req.AwayTeamID)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !hasAwayTeam {
|
||||||
|
err = fix.Field(UnknownTeam, "teamID")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
_, err = tx.Insert(req)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = tx.Commit()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
type UpdateTeamCanonicalNameIn struct {
|
||||||
|
TeamID string
|
||||||
|
CanonicalName string
|
||||||
|
NewCanonicalName string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Model) UpdateTeamCanonicalName(in UpdateTeamCanonicalNameIn) (err error) {
|
||||||
|
tx, err := s.qx.Tx()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer tx.Close(&err)
|
||||||
|
|
||||||
|
// Старый canonicalName не нормализуем, ибо предполагается что имя мы получили из БД
|
||||||
|
in.NewCanonicalName = strings.Title(strings.ToLower(NormalizeNameToAlias(in.NewCanonicalName)))
|
||||||
|
|
||||||
|
if in.NewCanonicalName == "" {
|
||||||
|
err = fix.Field(EmptyValue, "newCanonicalName")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Удаляем старый alias
|
||||||
|
err = tx.Exec("DELETE FROM teamAliases WHERE teamID=? AND name=?",
|
||||||
|
in.TeamID, in.CanonicalName)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Обновляем canonicalName
|
||||||
|
err = tx.Exec("UPDATE teams SET canonicalName=? WHERE teamID=?",
|
||||||
|
in.NewCanonicalName, in.TeamID)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("old: %q, new: %q, teamID: %s\n", in.CanonicalName,
|
||||||
|
in.NewCanonicalName, in.TeamID)
|
||||||
|
|
||||||
|
//return
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
hasAlias, err := tx.Has("teamAliases", "WHERE teamID=? AND name=?",
|
||||||
|
in.TeamID, in.NewCanonicalName)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !hasAlias {
|
||||||
|
// Добавляем новый alias
|
||||||
|
_, err = tx.Insert(TeamAlias{
|
||||||
|
Name: in.NewCanonicalName,
|
||||||
|
TeamID: in.TeamID,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = tx.Commit()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Model) ListTeamZones(sportID int) (list []TeamZone, err error) {
|
||||||
|
err = s.qx.ListBy(&list, `WHERE sportID=? ORDER BY name ASC`, sportID)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Model) ListTeamCandidates(sportID int, name string) (matchIDs []string, err error) {
|
||||||
|
name = NormalizeNameToAlias(name)
|
||||||
|
|
||||||
|
err = s.qx.ListQuery(&matchIDs,
|
||||||
|
`SELECT teamID FROM teamAliases WHERE sportID=? AND name=?`, sportID, name)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Model) ListTeamsByZone(zoneID string) (list []Team, err error) {
|
||||||
|
err = s.qx.ListBy(&list, `WHERE zoneID=? ORDER BY canonicalName ASC`, zoneID)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Model) ListTeamAliases(teamID string) (list []TeamAlias, err error) {
|
||||||
|
err = s.qx.ListBy(&list, `WHERE teamID=? ORDER BY name ASC`, teamID)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Model) ListTeams() (list []Team, err error) {
|
||||||
|
err = s.qx.List(&list)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
77
model/team.sql
Normal file
77
model/team.sql
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
CREATE TABLE teamZones (
|
||||||
|
zoneID VARCHAR(255) NOT NULL,
|
||||||
|
name VARCHAR(255) NOT NULL,
|
||||||
|
sportID INT NOT NULL, -- для вывода
|
||||||
|
|
||||||
|
PRIMARY KEY(zoneID),
|
||||||
|
UNIQUE(name)
|
||||||
|
)
|
||||||
|
ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;
|
||||||
|
|
||||||
|
|
||||||
|
CREATE TABLE teamChamps (
|
||||||
|
champID VARCHAR(255) NOT NULL,
|
||||||
|
zoneID VARCHAR(255) NOT NULL,
|
||||||
|
name VARCHAR(255) NOT NULL,
|
||||||
|
|
||||||
|
PRIMARY KEY(champID),
|
||||||
|
UNIQUE(zoneID, name),
|
||||||
|
|
||||||
|
FOREIGN KEY (zoneID) REFERENCES teamZones(zoneID)
|
||||||
|
)
|
||||||
|
ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;
|
||||||
|
|
||||||
|
|
||||||
|
CREATE TABLE teams (
|
||||||
|
teamID VARCHAR(255) NOT NULL,
|
||||||
|
zoneID VARCHAR(255) NOT NULL,
|
||||||
|
canonicalName VARCHAR(255) NOT NULL,
|
||||||
|
wasLinked BOOL NOT NULL,
|
||||||
|
|
||||||
|
PRIMARY KEY(teamID),
|
||||||
|
|
||||||
|
FOREIGN KEY (zoneID) REFERENCES teamZones(zoneID)
|
||||||
|
)
|
||||||
|
ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;
|
||||||
|
|
||||||
|
|
||||||
|
CREATE TABLE teamAliases (
|
||||||
|
aliasID INT NOT NULL AUTO_INCREMENT,
|
||||||
|
teamID VARCHAR(255) NOT NULL,
|
||||||
|
name VARCHAR(255) NOT NULL,
|
||||||
|
sportID INT NOT NULL, -- для поиска названий кандидатов без JOIN-ов
|
||||||
|
|
||||||
|
PRIMARY KEY(aliasID),
|
||||||
|
UNIQUE(teamID, name),
|
||||||
|
|
||||||
|
FOREIGN KEY (teamID) REFERENCES teams(teamID)
|
||||||
|
)
|
||||||
|
ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;
|
||||||
|
|
||||||
|
|
||||||
|
-- это просто cache
|
||||||
|
CREATE TABLE teamMatches (
|
||||||
|
matchID VARCHAR(255) NOT NULL,
|
||||||
|
homeGoals INT NOT NULL,
|
||||||
|
awayGoals INT NOT NULL,
|
||||||
|
hasScoreByPeriods BOOL NOT NULL,
|
||||||
|
p1HomeGoals INT NOT NULL,
|
||||||
|
p1AwayGoals INT NOT NULL,
|
||||||
|
p2HomeGoals INT NOT NULL,
|
||||||
|
p2AwayGoals INT NOT NULL,
|
||||||
|
isMatchScoreLoaded BOOL NOT NULL,
|
||||||
|
isScoreByPeriodsLoaded BOOL NOT NULL,
|
||||||
|
|
||||||
|
PRIMARY KEY(matchID)
|
||||||
|
)
|
||||||
|
ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;
|
||||||
|
|
||||||
|
--INSERT INTO footballChamps VALUES (NULL, "Seria A (Italy)");
|
||||||
|
--INSERT INTO footballChamps VALUES (NULL, "Bundesliga (Germany)");
|
||||||
|
--INSERT INTO footballChamps VALUES (NULL, "Premier League (England)");
|
||||||
|
|
||||||
|
delete from teamAliases;
|
||||||
|
delete from teamMatches;
|
||||||
|
delete from teams;
|
||||||
|
delete from teamChamps;
|
||||||
|
delete from teamZones;
|
||||||
136
model/types.go
Executable file
136
model/types.go
Executable file
@@ -0,0 +1,136 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
import "gordenko.dev/dima/qx"
|
||||||
|
|
||||||
|
type Model struct {
|
||||||
|
qx *qx.Db
|
||||||
|
}
|
||||||
|
|
||||||
|
// TEAM
|
||||||
|
|
||||||
|
type TeamZone struct {
|
||||||
|
ZoneID string `json:"zoneID" qx:"table=teamZones; pk"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
SportID int `json:"sportID"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TeamMatch struct {
|
||||||
|
MatchID string `json:"matchID" qx:"table=teamMatches; pk"`
|
||||||
|
//SportID int `json:"sportID"`
|
||||||
|
//StartTime int64 `json:"startTime"`
|
||||||
|
//ChampID string `json:"champID"`
|
||||||
|
//HomeTeamID string `json:"homeTeamID"`
|
||||||
|
//AwayTeamID string `json:"awayTeamID"`
|
||||||
|
HomeGoals int `json:"homeGoals"`
|
||||||
|
AwayGoals int `json:"awayGoals"`
|
||||||
|
HasScoreByPeriods bool `json:"hasScoreByPeriods"`
|
||||||
|
P1HomeGoals int `json:"p1HomeGoals"`
|
||||||
|
P1AwayGoals int `json:"p1AwayGoals"`
|
||||||
|
P2HomeGoals int `json:"p2HomeGoals"`
|
||||||
|
P2AwayGoals int `json:"p2AwayGoals"`
|
||||||
|
IsMatchScoreLoaded bool `json:"isMatchScoreLoaded"`
|
||||||
|
IsScoreByPeriodsLoaded bool `json:"isScoreByPeriodsLoaded"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Team struct {
|
||||||
|
TeamID string `json:"teamID" qx:"table=teams; pk"`
|
||||||
|
ZoneID string `json:"zoneID"`
|
||||||
|
CanonicalName string `json:"canonicalName"`
|
||||||
|
WasLinked bool `json:"wasLinked"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TeamAlias struct {
|
||||||
|
AliasID int64 `json:"aliasID" qx:"table=teamAliases; pk; seq=auto"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
TeamID string `json:"teamID"`
|
||||||
|
SportID int `json:"sportID"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TeamChamp struct {
|
||||||
|
ChampID string `json:"champID" qx:"table=teamChamps; pk"`
|
||||||
|
ZoneID string `json:"zoneID"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// FOOTBALL
|
||||||
|
/*
|
||||||
|
type FootballZone struct {
|
||||||
|
ZoneID string `json:"zoneID" qx:"table=footballZones; pk"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type FootballMatch struct {
|
||||||
|
MatchID string `json:"matchID" qx:"table=footballMatches; pk"`
|
||||||
|
StartTime int64 `json:"startTime"`
|
||||||
|
ChampID string `json:"champID"`
|
||||||
|
HomeTeamID string `json:"homeTeamID"`
|
||||||
|
AwayTeamID string `json:"awayTeamID"`
|
||||||
|
HomeGoals int `json:"homeGoals"`
|
||||||
|
AwayGoals int `json:"awayGoals"`
|
||||||
|
HasStats bool `json:"hasStats"`
|
||||||
|
P1HomeGoals int `json:"p1HomeGoals"`
|
||||||
|
P1AwayGoals int `json:"p1AwayGoals"`
|
||||||
|
P2HomeGoals int `json:"p2HomeGoals"`
|
||||||
|
P2AwayGoals int `json:"p2AwayGoals"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type FootballTeam struct {
|
||||||
|
TeamID string `json:"teamID" qx:"table=footballTeams; pk"`
|
||||||
|
ZoneID string `json:"zoneID"`
|
||||||
|
CanonicalName string `json:"canonicalName"`
|
||||||
|
WasLinked bool `json:"wasLinked"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type FootballAlias struct {
|
||||||
|
AliasID int64 `json:"aliasID" qx:"table=footballTeamAliases; pk; seq=auto"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
TeamID string `json:"teamID"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type FootballChamp struct {
|
||||||
|
ChampID string `json:"champID" qx:"table=footballChamps; pk"`
|
||||||
|
ZoneID string `json:"zoneID"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// HANDBALL
|
||||||
|
|
||||||
|
type HandballZone struct {
|
||||||
|
ZoneID string `json:"zoneID" qx:"table=handballZones; pk"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type HandballMatch struct {
|
||||||
|
MatchID string `json:"matchID" qx:"table=handballMatches; pk"`
|
||||||
|
StartTime int64 `json:"startTime"`
|
||||||
|
ChampID string `json:"champID"`
|
||||||
|
HomeTeamID string `json:"homeTeamID"`
|
||||||
|
AwayTeamID string `json:"awayTeamID"`
|
||||||
|
HomeGoals int `json:"homeGoals"`
|
||||||
|
AwayGoals int `json:"awayGoals"`
|
||||||
|
HasStats bool `json:"hasStats"`
|
||||||
|
P1HomeGoals int `json:"p1HomeGoals"`
|
||||||
|
P1AwayGoals int `json:"p1AwayGoals"`
|
||||||
|
P2HomeGoals int `json:"p2HomeGoals"`
|
||||||
|
P2AwayGoals int `json:"p2AwayGoals"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type HandballTeam struct {
|
||||||
|
TeamID string `json:"teamID" qx:"table=handballTeams; pk"`
|
||||||
|
ZoneID string `json:"zoneID"`
|
||||||
|
CanonicalName string `json:"canonicalName"`
|
||||||
|
WasLinked bool `json:"wasLinked"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type HandballAlias struct {
|
||||||
|
AliasID int64 `json:"aliasID" qx:"table=handballTeamAliases; pk; seq=auto"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
TeamID string `json:"teamID"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type HandballChamp struct {
|
||||||
|
ChampID string `json:"champID" qx:"table=handballChamps; pk"`
|
||||||
|
ZoneID string `json:"zoneID"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
*/
|
||||||
527
parser_test.go
Executable file
527
parser_test.go
Executable file
@@ -0,0 +1,527 @@
|
|||||||
|
package flashscore
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/PuerkitoBio/goquery"
|
||||||
|
|
||||||
|
// xhtml "golang.org/x/net/html"
|
||||||
|
//"strings"
|
||||||
|
"encoding/json"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
func TestParseFootballZoneAndChamp(t *testing.T) {
|
||||||
|
var src = []byte(`<span class="description__country">ENGLAND - SERIA A - DIVISION 1: <a href="#" onclick="window.open('/football/england/premier-league/'); return false;">Premier League - Round 30</a></span>
|
||||||
|
|
||||||
|
<div class="team-text tname-home">
|
||||||
|
<div class="tname">
|
||||||
|
<div class="tname__text">
|
||||||
|
<span style="display: none" class="dw-icon ico"> </span>
|
||||||
|
<a href="#" class="participant-imglink" onclick="window.open('/team/newcastle-utd/p6ahwuwJ'); return false;">Newcastle & Co</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="team-text tname-away">
|
||||||
|
<div class="tname">
|
||||||
|
<div class="tname__text">
|
||||||
|
<span style="display: none" class="dw-icon ico"> </span>
|
||||||
|
<a href="#" class="participant-imglink" onclick="window.open('/team/sheffield-utd/MeKPSerA'); return false;">Sheffield Utd (Eng)</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>`)
|
||||||
|
|
||||||
|
doc, err := goquery.NewDocumentFromReader(bytes.NewBuffer(src))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
zone, champ, home, away, err := parseTeamMatchInfo(doc, Football)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("zone: %s\n", zone.Name)
|
||||||
|
fmt.Printf("zoneID: %s\n\n", zone.ZoneID)
|
||||||
|
fmt.Printf("champ.ChampID: %s\n", champ.ChampID)
|
||||||
|
fmt.Printf("champ.Name: %v\n", champ.Name)
|
||||||
|
fmt.Printf("champ.ZoneID: %v\n\n", champ.ZoneID)
|
||||||
|
|
||||||
|
fmt.Printf("home.CanonicalName: %s\n", home.CanonicalName)
|
||||||
|
fmt.Printf("home.TeamID: %s\n", home.TeamID)
|
||||||
|
fmt.Printf("home.ZoneID: %s\n\n", home.ZoneID)
|
||||||
|
fmt.Printf("away.CanonicalName: %s\n", away.CanonicalName)
|
||||||
|
fmt.Printf("away.TeamID: %s\n", away.TeamID)
|
||||||
|
fmt.Printf("away.ZoneID: %s\n", away.ZoneID)
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
//var reChampName = regexp.MustCompile(`([^])`)
|
||||||
|
|
||||||
|
func TestTeamZone(t *testing.T) {
|
||||||
|
var teamZone = []byte(`<h2 class="tournament">
|
||||||
|
<div class="sportIcon sportIcon--soccer"></div>
|
||||||
|
<a href="/football/">Football</a>
|
||||||
|
<span class="breadcrumb__arrow"></span>
|
||||||
|
<span class="flag fl_198"></span>
|
||||||
|
<a href="/football/england/">England</a>
|
||||||
|
</h2>`)
|
||||||
|
|
||||||
|
doc, err := goquery.NewDocumentFromReader(bytes.NewBuffer(teamZone))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
zone, err := parseZoneOnTeamPage(doc, Football)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
fmt.Printf("zone.ZoneID: %s\nzone.Name: %s\n", zone.ZoneID, zone.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFullTimeTotalOdds(t *testing.T) {
|
||||||
|
var src = []byte(`<div id="block-under-over-ft" class="odds-table-wrapper" style="display: block;">
|
||||||
|
<table id="odds_ou_0.5" class="odds sortable" cellspacing="0">
|
||||||
|
<colgroup>
|
||||||
|
<col/>
|
||||||
|
<col width="60"/>
|
||||||
|
<col width="60"/>
|
||||||
|
<col width="60"/>
|
||||||
|
</colgroup>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="bookmaker no-padding unsortable">Bookmaker</th>
|
||||||
|
<th class="unsortable">Total</th>
|
||||||
|
<th>Over</th>
|
||||||
|
<th>Under</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr class="odd">
|
||||||
|
<td class="bookmaker">
|
||||||
|
<div class="blink" onclick="e_t.track_click('bookmaker-button-click', 'block-under-over_ft_bookmaker');"><a href="/bookmaker/417/?sport=1&from=odds-comparison" onclick="return !window.open(this.href)" title="1xBet" class="elink"><span style="background-image:url(/res/image/data/bookmakers/30-417.png); background-size: 100%; background-repeat: no-repeat;" class="detail-blogos"></span></a></div>
|
||||||
|
<div title="This match will be available for LIVE betting!" onmouseover="tt.show(this, event)" onmouseout="tt.hide(this)" class="live-icon-bookmaker" onclick="window.open('/bookmaker/417/?sport=1&from=live-detail' + (window.fs_detail && fs_detail.DA ? (fs_detail.DA == cjs.Api.constantsManager.getEventStageTypes().LIVE ? '-live-match' : '') : ''));"><span class="icon live-offer1" onclick="e_t.track_click('bookmaker-button-click', 'block-under-over_ft_live-offer');"> </span></div>
|
||||||
|
</td>
|
||||||
|
<td>0.5</td>
|
||||||
|
<td class="kx" onclick="e_t.track_click('bookmaker-button-click', 'block-under-over_ft_over');"><span class=" odds-wrap up" alt="1.07[u]1.09:">1.09</span></td>
|
||||||
|
<td class="kx" onclick="e_t.track_click('bookmaker-button-click', 'block-under-over_ft_under');"><span class=" odds-wrap down" alt="7.00[d]5.90:">5.90</span></td>
|
||||||
|
</tr>
|
||||||
|
<tr class="even">
|
||||||
|
<td class="bookmaker">
|
||||||
|
<div class="blink" onclick="e_t.track_click('bookmaker-button-click', 'block-under-over_ft_bookmaker');"><a href="/bookmaker/16/?sport=1&from=odds-comparison" onclick="return !window.open(this.href)" title="bet365" class="elink"><span style="background-image:url(/res/image/data/bookmakers/30-16.png); background-size: 100%; background-repeat: no-repeat;" class="detail-blogos"></span></a></div>
|
||||||
|
<div title="This match will be available for LIVE betting!" onmouseover="tt.show(this, event)" onmouseout="tt.hide(this)" class="live-icon-bookmaker" onclick="window.open('/bookmaker/16/?sport=1&from=live-detail' + (window.fs_detail && fs_detail.DA ? (fs_detail.DA == cjs.Api.constantsManager.getEventStageTypes().LIVE ? '-live-match' : '') : ''));"><span class="icon live-offer1" onclick="e_t.track_click('bookmaker-button-click', 'block-under-over_ft_live-offer');"> </span></div>
|
||||||
|
</td>
|
||||||
|
<td>0.5</td>
|
||||||
|
<td class="kx max" onclick="e_t.track_click('bookmaker-button-click', 'block-under-over_ft_over');"><span class=" odds-wrap up" alt="1.10[u]1.11:">1.11</span></td>
|
||||||
|
<td class="kx" onclick="e_t.track_click('bookmaker-button-click', 'block-under-over_ft_under');"><span class=" odds-wrap down" alt="7.00[d]6.50:">6.50</span></td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<table id="odds_ou_2.5" class="odds sortable" cellspacing="0">
|
||||||
|
<colgroup>
|
||||||
|
<col/>
|
||||||
|
<col width="60"/>
|
||||||
|
<col width="60"/>
|
||||||
|
<col width="60"/>
|
||||||
|
</colgroup>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="bookmaker no-padding unsortable">Bookmaker</th>
|
||||||
|
<th class="unsortable">Total</th>
|
||||||
|
<th>Over</th>
|
||||||
|
<th>Under</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr class="odd">
|
||||||
|
<td class="bookmaker">
|
||||||
|
<div class="blink" onclick="e_t.track_click('bookmaker-button-click', 'block-under-over_ft_bookmaker');"><a href="/bookmaker/417/?sport=1&from=odds-comparison" onclick="return !window.open(this.href)" title="1xBet" class="elink"><span style="background-image:url(/res/image/data/bookmakers/30-417.png); background-size: 100%; background-repeat: no-repeat;" class="detail-blogos"></span></a></div>
|
||||||
|
<div title="This match will be available for LIVE betting!" onmouseover="tt.show(this, event)" onmouseout="tt.hide(this)" class="live-icon-bookmaker" onclick="window.open('/bookmaker/417/?sport=1&from=live-detail' + (window.fs_detail && fs_detail.DA ? (fs_detail.DA == cjs.Api.constantsManager.getEventStageTypes().LIVE ? '-live-match' : '') : ''));"><span class="icon live-offer1" onclick="e_t.track_click('bookmaker-button-click', 'block-under-over_ft_live-offer');"> </span></div>
|
||||||
|
</td>
|
||||||
|
<td>2.5</td>
|
||||||
|
<td class="kx max" onclick="e_t.track_click('bookmaker-button-click', 'block-under-over_ft_over');"><span class=" odds-wrap up" alt="2.41[u]2.91:">2.91</span></td>
|
||||||
|
<td class="kx" onclick="e_t.track_click('bookmaker-button-click', 'block-under-over_ft_under');"><span class=" odds-wrap down" alt="1.54[d]1.52:">1.52</span></td>
|
||||||
|
</tr>
|
||||||
|
<tr class="even">
|
||||||
|
<td class="bookmaker">
|
||||||
|
<div class="blink" onclick="e_t.track_click('bookmaker-button-click', 'block-under-over_ft_bookmaker');"><a href="/bookmaker/16/?sport=1&from=odds-comparison" onclick="return !window.open(this.href)" title="bet365" class="elink"><span style="background-image:url(/res/image/data/bookmakers/30-16.png); background-size: 100%; background-repeat: no-repeat;" class="detail-blogos"></span></a></div>
|
||||||
|
<div title="This match will be available for LIVE betting!" onmouseover="tt.show(this, event)" onmouseout="tt.hide(this)" class="live-icon-bookmaker" onclick="window.open('/bookmaker/16/?sport=1&from=live-detail' + (window.fs_detail && fs_detail.DA ? (fs_detail.DA == cjs.Api.constantsManager.getEventStageTypes().LIVE ? '-live-match' : '') : ''));"><span class="icon live-offer1" onclick="e_t.track_click('bookmaker-button-click', 'block-under-over_ft_live-offer');"> </span></div>
|
||||||
|
</td>
|
||||||
|
<td>2.5</td>
|
||||||
|
<td class="kx" onclick="e_t.track_click('bookmaker-button-click', 'block-under-over_ft_over');"><span class=" odds-wrap up" alt="2.50[u]2.75:">2.75</span></td>
|
||||||
|
<td class="kx" onclick="e_t.track_click('bookmaker-button-click', 'block-under-over_ft_under');"><span class=" odds-wrap down" alt="1.53[d]1.44:">1.44</span></td>
|
||||||
|
</tr>
|
||||||
|
<tr class="odd">
|
||||||
|
<td class="bookmaker">
|
||||||
|
<div class="blink" onclick="e_t.track_click('bookmaker-button-click', 'block-under-over_ft_bookmaker');"><a href="/bookmaker/15/?sport=1&from=odds-comparison" onclick="return !window.open(this.href)" title="William Hill" class="elink"><span style="background-image:url(/res/image/data/bookmakers/30-15.png); background-size: 100%; background-repeat: no-repeat;" class="detail-blogos"></span></a></div>
|
||||||
|
</td>
|
||||||
|
<td>2.5</td>
|
||||||
|
<td class="kx" onclick="e_t.track_click('bookmaker-button-click', 'block-under-over_ft_over');"><span class="not-published odds-wrap " alt=":">2.38</span></td>
|
||||||
|
<td class="kx" onclick="e_t.track_click('bookmaker-button-click', 'block-under-over_ft_under');"><span class="not-published odds-wrap " alt=":">1.53</span></td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>`)
|
||||||
|
|
||||||
|
doc, err := goquery.NewDocumentFromReader(bytes.NewBuffer(src))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
odds, err := parseOddsTotal(doc)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
buf, _ := json.MarshalIndent(odds, "", " ")
|
||||||
|
fmt.Printf("Total odds: %s\n", buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
// <table id="odds_1x2"
|
||||||
|
|
||||||
|
func TestParse1x2Odds(t *testing.T) {
|
||||||
|
var oddssrc = []byte(`<table id="odds_1x2"><tr class="odd">
|
||||||
|
<td class="bookmaker">
|
||||||
|
<div class="blink" onclick="e_t.track_click('bookmaker-button-click', 'block-1x2_ft_bookmaker');">
|
||||||
|
<a href="/bookmaker/417/?sport=1&from=odds-comparison" onclick="return !window.open(this.href)" title="1xBet" class="elink">
|
||||||
|
<span style="background-image:url(/res/image/data/bookmakers/30-417.png); background-size: 100%; background-repeat: no-repeat;" class="detail-blogos"></span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div title="This match will be available for LIVE betting!" onmouseover="tt.show(this, event)" onmouseout="tt.hide(this)" class="live-icon-bookmaker" onclick="window.open('/bookmaker/417/?sport=1&from=live-detail' + (window.fs_detail && fs_detail.DA ? (fs_detail.DA == cjs.Api.constantsManager.getEventStageTypes().LIVE ? '-live-match' : '') : ''));">
|
||||||
|
<span class="icon live-offer1" onclick="e_t.track_click('bookmaker-button-click', 'block-1x2_ft_live-offer');"> </span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td class="kx" onclick="e_t.track_click('bookmaker-button-click', 'block-1x2_ft_1');">
|
||||||
|
<span class=" odds-wrap up" alt="3.01[u]3.24:">3.24</span>
|
||||||
|
</td>
|
||||||
|
<td class="kx max" onclick="e_t.track_click('bookmaker-button-click', 'block-1x2_ft_0');">
|
||||||
|
<span class=" odds-wrap down" alt="3.56[d]3.50:">3.50</span>
|
||||||
|
</td>
|
||||||
|
<td class="kx max" onclick="e_t.track_click('bookmaker-button-click', 'block-1x2_ft_2');">
|
||||||
|
<span class=" odds-wrap down" alt="2.49[d]2.37:">2.37</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr class="even">
|
||||||
|
<td class="bookmaker">
|
||||||
|
<div class="blink" onclick="e_t.track_click('bookmaker-button-click', 'block-1x2_ft_bookmaker');">
|
||||||
|
<a href="/bookmaker/17/?sport=1&from=odds-comparison" onclick="return !window.open(this.href)" title="1xBet" class="elink">
|
||||||
|
<span style="background-image:url(/res/image/data/bookmakers/30-417.png); background-size: 100%; background-repeat: no-repeat;" class="detail-blogos"></span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div title="This match will be available for LIVE betting!" onmouseover="tt.show(this, event)" onmouseout="tt.hide(this)" class="live-icon-bookmaker" onclick="window.open('/bookmaker/417/?sport=1&from=live-detail' + (window.fs_detail && fs_detail.DA ? (fs_detail.DA == cjs.Api.constantsManager.getEventStageTypes().LIVE ? '-live-match' : '') : ''));">
|
||||||
|
<span class="icon live-offer1" onclick="e_t.track_click('bookmaker-button-click', 'block-1x2_ft_live-offer');"> </span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td class="kx" onclick="e_t.track_click('bookmaker-button-click', 'block-1x2_ft_1');">
|
||||||
|
<span class=" odds-wrap up" alt="3.01[u]3.24:">2.24</span>
|
||||||
|
</td>
|
||||||
|
<td class="kx max" onclick="e_t.track_click('bookmaker-button-click', 'block-1x2_ft_0');">
|
||||||
|
<span class=" odds-wrap down" alt="3.56[d]3.50:">5.50</span>
|
||||||
|
</td>
|
||||||
|
<td class="kx max" onclick="e_t.track_click('bookmaker-button-click', 'block-1x2_ft_2');">
|
||||||
|
<span class=" odds-wrap down" alt="2.49[d]2.37:">5.37</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>`)
|
||||||
|
|
||||||
|
doc, err := goquery.NewDocumentFromReader(bytes.NewBuffer(oddssrc))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
table1x2Odds := doc.Find("#odds_1x2")
|
||||||
|
|
||||||
|
offers, err := parseOdds1x2(table1x2Odds)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
buf, _ := json.MarshalIndent(offers, "", " ")
|
||||||
|
fmt.Printf("%s\n", buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseNextRoundFixtures(t *testing.T) {
|
||||||
|
/*
|
||||||
|
var handballSchedule = ``
|
||||||
|
|
||||||
|
|
||||||
|
Postponed: (разные чемпионаты)
|
||||||
|
|
||||||
|
~AA÷6R6Qjxol
|
||||||
|
¬AD÷1602514800
|
||||||
|
¬ADE÷1602514800
|
||||||
|
¬AB÷3
|
||||||
|
¬CR÷3
|
||||||
|
¬AC÷4
|
||||||
|
¬CX÷Butel Skopje
|
||||||
|
¬RW÷0
|
||||||
|
¬AX÷0
|
||||||
|
¬BX÷-1
|
||||||
|
¬WQ÷
|
||||||
|
¬WN÷TIK
|
||||||
|
¬AF÷Tikvesh
|
||||||
|
¬JB÷M10TCQre
|
||||||
|
¬WV÷tikvesh
|
||||||
|
¬WM÷BUT
|
||||||
|
¬AE÷Butel Skopje
|
||||||
|
¬JA÷rB1PD6Tl
|
||||||
|
¬WU÷butel-skopje¬
|
||||||
|
|
||||||
|
|
||||||
|
~AA÷CzK7rvtg
|
||||||
|
¬AD÷1602531000
|
||||||
|
¬ADE÷1602531000
|
||||||
|
¬AB÷3
|
||||||
|
¬CR÷3
|
||||||
|
¬AC÷4
|
||||||
|
¬CX÷FH Hafnarfjordur
|
||||||
|
¬RW÷0
|
||||||
|
¬AX÷0
|
||||||
|
¬BX÷-1
|
||||||
|
¬WQ÷
|
||||||
|
¬WN÷GRO
|
||||||
|
¬AF÷Grotta
|
||||||
|
¬JB÷06YZR1Pr
|
||||||
|
¬WV÷grotta
|
||||||
|
¬WM÷FHH
|
||||||
|
¬AE÷FH Hafnarfjordur
|
||||||
|
¬JA÷bcn8WsHR
|
||||||
|
¬WU÷fh-hafnarfjordur
|
||||||
|
¬AM÷Postponed due to safety reasons.¬
|
||||||
|
|
||||||
|
Normal:
|
||||||
|
|
||||||
|
~AA÷MyARroUi
|
||||||
|
¬AD÷1602520200
|
||||||
|
¬ADE÷1602520200
|
||||||
|
¬AB÷1
|
||||||
|
¬CR÷1
|
||||||
|
¬AC÷1
|
||||||
|
¬CX÷Ribe-Esbjerg
|
||||||
|
¬RW÷0
|
||||||
|
¬AX÷0
|
||||||
|
¬BX÷-1
|
||||||
|
¬WQ÷
|
||||||
|
¬WM÷RIB
|
||||||
|
¬AE÷Ribe-Esbjerg
|
||||||
|
¬JA÷xrSUrVlt
|
||||||
|
¬WU÷ribe-esbjerg
|
||||||
|
¬WN÷FRE
|
||||||
|
¬AF÷Fredericia
|
||||||
|
¬JB÷lIVYsk4n
|
||||||
|
¬WV÷fredericia
|
||||||
|
¬AN÷n
|
||||||
|
¬MW÷417|453|455|512|471¬
|
||||||
|
|
||||||
|
|
||||||
|
~AA÷xMZpVA2J
|
||||||
|
¬AD÷1602522000
|
||||||
|
¬ADE÷1602522000
|
||||||
|
¬AB÷1
|
||||||
|
¬CR÷1
|
||||||
|
¬AC÷1
|
||||||
|
¬CX÷Redbergslids
|
||||||
|
¬RW÷0
|
||||||
|
¬AX÷0
|
||||||
|
¬BX÷-1
|
||||||
|
¬WQ÷
|
||||||
|
¬WN÷SKO
|
||||||
|
¬AF÷Skovde
|
||||||
|
¬JB÷AyzHGCHi
|
||||||
|
¬WV÷skovde
|
||||||
|
¬WM÷RED
|
||||||
|
¬AE÷Redbergslids
|
||||||
|
¬JA÷dYVCHh2o
|
||||||
|
¬WU÷redbergslids
|
||||||
|
¬AL÷{"2":[{"BU":"\/bookmaker\/16\/?from=live-streaming&sport=0","IU":"\/res\/image\/data\/bookmakers\/17-16.png","BN":"bet365","BI":16,"BT":"GEO restrictions apply. Funded account required or to have placed a bet in the last 24 hours. GamblingTherapy.org. 18+"},{"BU":"\/bookmaker\/539\/?from=live-streaming&sport=0","IU":"\/res\/image\/data\/bookmakers\/17-539.png","BN":"Betclic.pl","BI":539},{"BU":"\/bookmaker\/45\/?from=live-streaming&sport=0","IU":"\/res\/image\/data\/bookmakers\/17-45.png","BN":"Chance","BI":45},{"BU":"\/bookmaker\/165\/?from=live-streaming&sport=0","IU":"\/res\/image\/data\/bookmakers\/17-165.png","BN":"STS.pl","BI":165},{"BU":"\/bookmaker\/49\/?from=live-streaming&sport=0","IU":"\/res\/image\/data\/bookmakers\/17-49.1.png","BN":"Tipsport","BI":49},{"BU":"\/bookmaker\/411\/?from=live-streaming&sport=0","IU":"\/res\/image\/data\/bookmakers\/17-411.1.png","BN":"Tipsport SK","BI":411},{"BU":"\/bookmaker\/454\/?from=live-streaming&sport=0","IU":"\/res\/image\/data\/bookmakers\/17-454.png","BN":"Winlinebet","BI":454},{"BU":"\/bookmaker\/523\/?from=live-streaming&sport=0","IU":"\/res\/image\/data\/bookmakers\/17-523.png","BN":"Wplay","BI":523}]}
|
||||||
|
¬AN÷y
|
||||||
|
¬MW÷16|417|392|419|453|454|455|49|45|33|165|539|544|38|512|508|24|27|471|411|43|507|549¬
|
||||||
|
|
||||||
|
|
||||||
|
res, err := parseNextRoundFixtures(handballSchedule)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, m := range res.Matches {
|
||||||
|
fmt.Printf("%s: %s\n", m.StartTime, m.MatchID)
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
func TestListScheduledHandballMatches(t *testing.T) {
|
||||||
|
result, err := listScheduledHandballMatches()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, m := range result {
|
||||||
|
fmt.Printf("%s: %s\n", m.StartTime, m.MatchID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetUpcomingHandballMatchReport(t *testing.T) {
|
||||||
|
|
||||||
|
report, err := GetUpcomingMatchReport("MyARroUi")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
buf, _ := json.MarshalIndent(report, "", " ")
|
||||||
|
fmt.Printf("%s\n\n", buf)
|
||||||
|
|
||||||
|
//for _, m := range result {
|
||||||
|
// /fmt.Printf("%s: %s\n", time.Unix(m.StartTime, 0), m.MatchID)
|
||||||
|
//
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
// HANDBALL
|
||||||
|
|
||||||
|
func TestParseHandballOdds(t *testing.T) {
|
||||||
|
// Строку возвращает отдельный запрос на адрес handballOddsURL
|
||||||
|
// flashscore format
|
||||||
|
buf, err := os.ReadFile("for_parse/oddsHandball.fsf")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
str := string(buf)
|
||||||
|
|
||||||
|
odds, err := parseFlashscoreEncodedOdds(str)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
buf, _ = json.MarshalIndent(odds, "", " ")
|
||||||
|
fmt.Printf("%s\n\n", buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseHandballH2H(t *testing.T) {
|
||||||
|
// flashscore format
|
||||||
|
buf, err := os.ReadFile("for_parse/h2hHandball.fsf")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
str := string(buf)
|
||||||
|
|
||||||
|
homeTeamName := "HSG Wetzlar"
|
||||||
|
awayTeamName := "MT Melsungen"
|
||||||
|
|
||||||
|
homeTeamMatches, awayTeamMatches, h2hMatches, err := parseFlashscoreEncodedH2H(str, homeTeamName, awayTeamName)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
buf, _ = json.MarshalIndent(homeTeamMatches, "", " ")
|
||||||
|
fmt.Printf("HomeTeamMatches:\n%s\n\n", buf)
|
||||||
|
|
||||||
|
buf, _ = json.MarshalIndent(awayTeamMatches, "", " ")
|
||||||
|
fmt.Printf("AwayTeamMatches:\n%s\n\n", buf)
|
||||||
|
|
||||||
|
buf, _ = json.MarshalIndent(h2hMatches, "", " ")
|
||||||
|
fmt.Printf("h2hMatches:\n%s\n\n", buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseAllGames(t *testing.T) {
|
||||||
|
// flashscore format
|
||||||
|
buf, err := os.ReadFile("for_parse/allGamesFootball.fsf")
|
||||||
|
//buf, err := os.ReadFile("for_parse/allGamesHandball.fsf")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
str := string(buf)
|
||||||
|
|
||||||
|
/*
|
||||||
|
list, err := ParseFlashscoreEncoding(str)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _,m := range list {
|
||||||
|
for k, v := range m {
|
||||||
|
fmt.Printf("%s: %s\n", k, v)
|
||||||
|
}
|
||||||
|
fmt.Println()
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
matches, err := parseFlashscoreEncodedAllGames(str, Football)
|
||||||
|
//matches, err := parseFlashscoreEncodedAllGames(str, Handball)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
buf, _ = json.MarshalIndent(matches, "", " ")
|
||||||
|
fmt.Printf("%s\n\n", buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseChampNameOnChampPage(t *testing.T) {
|
||||||
|
buf := []byte(`<h2 class="tournament"><div class="sportIcon sportIcon--soccer"></div><a href="/football/">Football</a> <span class="breadcrumb__arrow"></span><span class="flag fl_81"></span><a href="/football/germany/">Germany</a></h2>
|
||||||
|
|
||||||
|
<div class="container__fsbody" id="fsbody"> <div id="fscountry">
|
||||||
|
<div id="fsi"></div>
|
||||||
|
<div id="fscon">
|
||||||
|
<div class="teamHeader">
|
||||||
|
<div class="teamHeader__info">
|
||||||
|
<div class="teamHeader__logo" style="background-image: url('/res/image/data/Q1BvWoB7-KfVhu7bI.png')"></div>
|
||||||
|
<div class="teamHeader__information">
|
||||||
|
<div class="teamHeader__name">Bundesliga
|
||||||
|
<div id="tomyleagues"></div>
|
||||||
|
</div>
|
||||||
|
<div class="teamHeader__name">2020/2021</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>`)
|
||||||
|
|
||||||
|
doc, err := goquery.NewDocumentFromReader(bytes.NewBuffer(buf))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// First -
|
||||||
|
f := doc.Find("div.teamHeader__name").First() //.Children()
|
||||||
|
|
||||||
|
txt := f.Text()
|
||||||
|
txt = strings.TrimSpace(txt)
|
||||||
|
|
||||||
|
champID := "/football/germany/bundesliga"
|
||||||
|
|
||||||
|
parts := strings.Split(champID, "/")
|
||||||
|
|
||||||
|
zoneID := strings.Join(parts[:3], "/")
|
||||||
|
|
||||||
|
fmt.Printf("%q\n\n%q\n\n", txt, zoneID)
|
||||||
|
|
||||||
|
//f := doc.Find("h2.tournament").First()
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseEnvironmentJsVarOnMatchPageInJsVar(t *testing.T) {
|
||||||
|
// flashscore format
|
||||||
|
buf, err := os.ReadFile("for_parse/environmentJsVarOnMatchPage.html")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
doc, err := goquery.NewDocumentFromReader(bytes.NewBuffer(buf))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
home, away, err := parseTeamsOnMatchPageInJsVar(doc)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
buf, _ = json.MarshalIndent(home, "", " ")
|
||||||
|
fmt.Printf("%s\n\n", buf)
|
||||||
|
|
||||||
|
buf, _ = json.MarshalIndent(away, "", " ")
|
||||||
|
fmt.Printf("%s\n\n", buf)
|
||||||
|
}
|
||||||
126
team_test.go
Normal file
126
team_test.go
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
package flashscore
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"gordenko.dev/dima/flashscore/model"
|
||||||
|
"gordenko.dev/dima/qx"
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
func TestGetUpcomingMatchReport(t *testing.T) {
|
||||||
|
var err error
|
||||||
|
//'req, err := http.NewRequest("GET", url, nil)
|
||||||
|
|
||||||
|
logger := log.New(os.Stdout, "", log.LstdFlags)
|
||||||
|
|
||||||
|
db, err := qx.Open("mysql", "root:zkx75fcy@/tipper")
|
||||||
|
//db, err := qx.Open("mysql", "root:lklnmf6@/tipper")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
fs := New(db, logger)
|
||||||
|
|
||||||
|
report, err := fs.GetUpcomingMatchReport(ScheduledMatch{
|
||||||
|
MatchID: "vqgqjCUl",
|
||||||
|
SportID: Handball,
|
||||||
|
StartTime: time.Now(),
|
||||||
|
ChampID: "/handball/denmark/1-division",
|
||||||
|
Zone: model.TeamZone{
|
||||||
|
ZoneID: "/handball/denmark",
|
||||||
|
Name: "Denmark",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
//report, err := fs.GetUpcomingMatchReport("8vF2Fr1o", Football)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("GetUpcomingMatchReport: %s\n", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
buf, _ := json.MarshalIndent(report, "", " ")
|
||||||
|
fmt.Printf("%s\n", buf)
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
func TestListOfScheduledMatches(t *testing.T) {
|
||||||
|
var err error
|
||||||
|
//'req, err := http.NewRequest("GET", url, nil)
|
||||||
|
|
||||||
|
logger := log.New(os.Stdout, "", log.LstdFlags)
|
||||||
|
|
||||||
|
db, err := qx.Open("mysql", "root:zkx75fcy@/tipper")
|
||||||
|
//db, err := qx.Open("mysql", "root:lklnmf6@/tipper")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
fs := New(db, logger)
|
||||||
|
|
||||||
|
reports, err := fs.ListOfScheduledMatches(Handball)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("ListUpcomingMatchReports: %s\n", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = reports
|
||||||
|
//buf, _ := json.MarshalIndent(reports, "", " ")
|
||||||
|
//fmt.Printf("%s\n", buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCheckUpcomingMatchReports(t *testing.T) {
|
||||||
|
var err error
|
||||||
|
//'req, err := http.NewRequest("GET", url, nil)
|
||||||
|
|
||||||
|
logger := log.New(os.Stdout, "", log.LstdFlags)
|
||||||
|
|
||||||
|
db, err := qx.Open("mysql", "root:zkx75fcy@/tipper")
|
||||||
|
//db, err := qx.Open("mysql", "root:lklnmf6@/tipper")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
fs := New(db, logger)
|
||||||
|
|
||||||
|
scheduledMatches, err := fs.ListOfScheduledMatches(Football)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("ListOfScheduledMatches: %s\n", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, match := range scheduledMatches[29:30] {
|
||||||
|
// Здесь лок не нужен, ибо нет race conditions, пишем в мапу в этом же потоке
|
||||||
|
report := UpcomingMatchReport{
|
||||||
|
MatchID: match.MatchID,
|
||||||
|
SportID: match.SportID,
|
||||||
|
StartTime: match.StartTime.Unix(),
|
||||||
|
Zone: model.TeamZone{
|
||||||
|
ZoneID: match.Zone.ZoneID,
|
||||||
|
Name: match.Zone.Name,
|
||||||
|
SportID: match.SportID,
|
||||||
|
},
|
||||||
|
Champ: model.TeamChamp{
|
||||||
|
ChampID: match.ChampID,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Даже если отчет полный - синкаем, чтобы обновить odds
|
||||||
|
|
||||||
|
err = fs.SyncUpcomingMatchReport(&report)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("SyncUpcomingMatchReport: %s; matchID=%s", err, report.MatchID)
|
||||||
|
}
|
||||||
|
|
||||||
|
buf, _ := json.MarshalIndent(report, "", " ")
|
||||||
|
fmt.Printf("REPORT:\n\n%s\n\n", buf)
|
||||||
|
}
|
||||||
|
//buf, _ := json.MarshalIndent(reports, "", " ")
|
||||||
|
//fmt.Printf("%s\n", buf)
|
||||||
|
}
|
||||||
144
tennis.go
Normal file
144
tennis.go
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
package flashscore
|
||||||
|
|
||||||
|
import (
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Surface string
|
||||||
|
type Sex string
|
||||||
|
|
||||||
|
const (
|
||||||
|
SurfaceHard Surface = "hard"
|
||||||
|
SurfaceClay Surface = "clay"
|
||||||
|
SurfaceGrass Surface = "grass"
|
||||||
|
SurfaceCarpet Surface = "carpet"
|
||||||
|
|
||||||
|
SexMen = "men"
|
||||||
|
SexWomen = "women"
|
||||||
|
)
|
||||||
|
|
||||||
|
var reBreakpoints = regexp.MustCompile(`(\d+)%\s*\((\d+)/(\d+)`)
|
||||||
|
|
||||||
|
type Breakpoints struct {
|
||||||
|
Qty int // всего сколько было брейкпоинтов
|
||||||
|
Converted int // реализованных
|
||||||
|
Percent int // процент реализации
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseBreakpoints - извлекает статистику по брейкпоинтам из строки вида "50% (0/4)"
|
||||||
|
func parseBreakpoints(str string) *Breakpoints {
|
||||||
|
groups := reBreakpoints.FindStringSubmatch(str)
|
||||||
|
|
||||||
|
if len(groups) == 4 {
|
||||||
|
qty, _ := strconv.Atoi(groups[3])
|
||||||
|
converted, _ := strconv.Atoi(groups[2])
|
||||||
|
percent, _ := strconv.Atoi(groups[1])
|
||||||
|
|
||||||
|
return &Breakpoints{
|
||||||
|
Qty: qty,
|
||||||
|
Converted: converted,
|
||||||
|
Percent: percent,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// /type TennisSetStat
|
||||||
|
|
||||||
|
type TennisMatchStat struct {
|
||||||
|
// Сколько было брейкпоинтов и сколько реализовал первый теннисист
|
||||||
|
HomeBreakpoints *Breakpoints
|
||||||
|
// Сколько было брейкпоинтов и сколько реализовал второй теннисист
|
||||||
|
AwayBreakpoints *Breakpoints
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseTennisMatchStat(str string) (stat TennisMatchStat, err error) {
|
||||||
|
blocks, err := ParseFlashscoreEncoding(str)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//buf, _ := json.MarshalIndent(blocks, "", " ")
|
||||||
|
//fmt.Printf("%s\n\n", buf)
|
||||||
|
|
||||||
|
var period string
|
||||||
|
|
||||||
|
for _, block := range blocks {
|
||||||
|
if block["SE"] != "" {
|
||||||
|
if block["SE"] == "Match" {
|
||||||
|
period = "match"
|
||||||
|
} else {
|
||||||
|
period = ""
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if period != "match" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
switch block["SG"] {
|
||||||
|
case "Break Points Converted":
|
||||||
|
stat.HomeBreakpoints = parseBreakpoints(block["SH"])
|
||||||
|
stat.AwayBreakpoints = parseBreakpoints(block["SI"])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseTennisSurfaceFromChampName(fullChampName string) Surface {
|
||||||
|
substrings := strings.Split(fullChampName, ",")
|
||||||
|
surface := substrings[len(substrings)-1]
|
||||||
|
|
||||||
|
surface = strings.ToLower(strings.TrimSpace(surface))
|
||||||
|
|
||||||
|
switch surface {
|
||||||
|
case "hard", "indoor hard", "indoor":
|
||||||
|
return SurfaceHard
|
||||||
|
|
||||||
|
case "clay", "indoor clay", "green clay", "red clay":
|
||||||
|
return SurfaceClay
|
||||||
|
|
||||||
|
case "grass":
|
||||||
|
return SurfaceGrass
|
||||||
|
|
||||||
|
case "carpet", "indoor carpet":
|
||||||
|
return SurfaceCarpet
|
||||||
|
|
||||||
|
default:
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ATP - SINGLES
|
||||||
|
// ATP - DOUBLES
|
||||||
|
// CHALLENGER MEN - SINGLES
|
||||||
|
// WTA - DOUBLES
|
||||||
|
// WTA - SINGLES
|
||||||
|
// ITF MEN - DOUBLES
|
||||||
|
// ITF WOMEN
|
||||||
|
// TEAMS - WOMEN
|
||||||
|
|
||||||
|
var reMen = regexp.MustCompile(`\b(atp|men)\b`)
|
||||||
|
var reWomen = regexp.MustCompile(`\b(wta|wom[ea]n)\b`)
|
||||||
|
|
||||||
|
func ParseTennisSexFromChampName(fullChampName string) string {
|
||||||
|
fullChampName = strings.ToLower(fullChampName)
|
||||||
|
|
||||||
|
if reMen.MatchString(fullChampName) {
|
||||||
|
return SexMen
|
||||||
|
}
|
||||||
|
|
||||||
|
if reWomen.MatchString(fullChampName) {
|
||||||
|
return SexWomen
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsTennisDoublesChamp(fullChampName string) bool {
|
||||||
|
fullChampName = strings.ToLower(fullChampName)
|
||||||
|
|
||||||
|
return strings.Contains(fullChampName, "doubles")
|
||||||
|
}
|
||||||
31
tennis_api.go
Normal file
31
tennis_api.go
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
package flashscore
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
webspider "gordenko.dev/dima/spider"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Метод, который вызывается без объекта
|
||||||
|
|
||||||
|
func LoadTennisMatchStat(matchID string) (stat TennisMatchStat, err error) {
|
||||||
|
spider, err := webspider.NewSpider(nil)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
url := fmt.Sprintf(tennisMatchStatURLPattern, matchID)
|
||||||
|
|
||||||
|
buf, err := spider.Get(url, map[string]string{
|
||||||
|
"x-requested-with": "XMLHttpRequest",
|
||||||
|
"x-fsign": feedSign,
|
||||||
|
"x-geoip": "1",
|
||||||
|
"referer": "https://d.flashscore.com/x/feed/proxy-fetch",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("LoadTennisMatchStat: %s; url=%s", err, url)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return parseTennisMatchStat(string(buf))
|
||||||
|
}
|
||||||
15
tennis_api_test.go
Normal file
15
tennis_api_test.go
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
package flashscore
|
||||||
|
|
||||||
|
/*
|
||||||
|
func TestLoadTennisMatchStat(t *testing.T) {
|
||||||
|
stat, err := LoadTennisMatchStat("6V2F8wIB")
|
||||||
|
if err != nil {
|
||||||
|
t.Log(err)
|
||||||
|
t.Failed()
|
||||||
|
} else {
|
||||||
|
buf, _ := json.MarshalIndent(stat, "", " ")
|
||||||
|
fmt.Printf("LoadTennisMatchStat: %s\n", buf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*/
|
||||||
130
tennis_test.go
Normal file
130
tennis_test.go
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
package flashscore
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestTennisParseAllGames(t *testing.T) {
|
||||||
|
// flashscore format
|
||||||
|
buf, err := os.ReadFile("for_parse/allGamesTennis.fsf")
|
||||||
|
//buf, err := ioutil.ReadFile("for_parse/allGamesHandball.fsf")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
str := string(buf)
|
||||||
|
|
||||||
|
/*
|
||||||
|
list, err := ParseFlashscoreEncoding(str)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _,m := range list {
|
||||||
|
for k, v := range m {
|
||||||
|
fmt.Printf("%s: %s\n", k, v)
|
||||||
|
}
|
||||||
|
fmt.Println()
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
matches, err := parseFlashscoreEncodedAllGames(str, Tennis)
|
||||||
|
//matches, err := ParseFlashscoreEncoding(str)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
buf, _ = json.MarshalIndent(matches, "", " ")
|
||||||
|
fmt.Printf("%s\n\n", buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTennisParseMatchScore(t *testing.T) {
|
||||||
|
str := "DA÷3¬DZ÷3¬DB÷3¬DD÷1618078710¬AW÷1¬DC÷1618074000¬DS÷0¬DE÷2¬DF÷0¬DG÷2¬DH÷0¬DI÷-1¬DK÷1618078747¬DL÷3¬DM÷¬DT÷¬DX÷ST,MH,MC,OD,HH,DR¬DEI÷https://www.flashscore.com/res/image/data/p2vtlMHG-xfKxQmxa-MiVx2mEg.png¬DR÷0¬DV÷2¬DJ÷H¬AZ÷1¬SC÷417¬SB÷3¬SD÷1xBet¬A1÷¬~"
|
||||||
|
|
||||||
|
homeScore, awayScore, err := parseHandballMatchScore(str)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("home - away: %d-%d\n\n", homeScore, awayScore)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTennisParseScoreByPeriods(t *testing.T) {
|
||||||
|
// 2 sets
|
||||||
|
// str := "AC÷3¬BA÷6¬BB÷1¬RC÷0:44¬~BC÷6¬BD÷2¬RD÷0:42¬~RB÷1:26¬~A1÷¬~"
|
||||||
|
|
||||||
|
// 3 sets
|
||||||
|
//str := "AC÷3¬BA÷7¬DA÷7¬BB÷6¬DB÷2¬RC÷1:05¬~BC÷4¬BD÷6¬RD÷0:50¬~BE÷6¬BF÷2¬RE÷0:43¬~RB÷2:38¬~PSPH÷WC¬PSPA÷11¬A1÷¬~"
|
||||||
|
|
||||||
|
// 5 sets
|
||||||
|
str := "AC÷3¬BA÷3¬BB÷6¬RC÷0:36¬~BC÷7¬DC÷7¬BD÷6¬DD÷5¬RD÷1:18¬~BE÷7¬DE÷7¬BF÷6¬DF÷4¬RE÷1:08¬~BG÷0¬BH÷6¬RF÷0:29¬~BI÷6¬BJ÷3¬RG÷0:38¬~RB÷4:09¬~PSPH÷20¬PSPA÷12¬A1÷¬~"
|
||||||
|
|
||||||
|
score, hasScoreByPeriods, err := parseTennisTeamScoreByPeriods(str)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
buf, _ := json.MarshalIndent(score, "", " ")
|
||||||
|
fmt.Printf("hasScoreByPeriods: %t, %s\n\n", hasScoreByPeriods, buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTennisParseOdds(t *testing.T) {
|
||||||
|
// Строку возвращает отдельный запрос на адрес handballOddsURL
|
||||||
|
// flashscore format
|
||||||
|
buf, err := os.ReadFile("for_parse/tennisOdds.fsf")
|
||||||
|
//buf, err := os.ReadFile("for_parse/oddsHandball.fsf")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
str := string(buf)
|
||||||
|
|
||||||
|
odds, err := parseFlashscoreEncodedOdds(str)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
//odds, err := ParseFlashscoreEncoding(str)
|
||||||
|
//if err != nil {
|
||||||
|
// return
|
||||||
|
//}
|
||||||
|
|
||||||
|
buf, _ = json.MarshalIndent(odds, "", " ")
|
||||||
|
fmt.Printf("%s\n\n", buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTennisParseMatchStat(t *testing.T) {
|
||||||
|
// Строку возвращает отдельный запрос на адрес handballOddsURL
|
||||||
|
// flashscore format
|
||||||
|
buf, err := os.ReadFile("for_parse/tennisMatchStat.fsf")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
str := string(buf)
|
||||||
|
|
||||||
|
stat, err := parseTennisMatchStat(str)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
buf, _ = json.MarshalIndent(stat, "", " ")
|
||||||
|
fmt.Printf("%s\n\n", buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestParseBreakpointsString - строка из таблицы статистики по матчу.
|
||||||
|
func TestParseBreakpointsString(t *testing.T) {
|
||||||
|
bp := parseBreakpoints("50% (0/4)")
|
||||||
|
|
||||||
|
fmt.Printf("breakpoints: %#v\n", bp)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
func TestParseTennisSurfaceFromChampName(t *testing.T) {
|
||||||
|
surface := ParseTennisSurfaceFromChampName("ITF MEN - SINGLES M25 Reus (Spain), clay")
|
||||||
|
|
||||||
|
fmt.Printf("Surface: %s\n", surface)
|
||||||
|
|
||||||
|
}
|
||||||
220
test/test_html.go
Executable file
220
test/test_html.go
Executable file
@@ -0,0 +1,220 @@
|
|||||||
|
package flashscore
|
||||||
|
|
||||||
|
// Оставил только счет по таймам. Вырезал ленту событий (голы, пенальти, карточки)
|
||||||
|
// Анализ:
|
||||||
|
// - находим .detailMS__headerText с контентом "1st Half" или "2nd Half"
|
||||||
|
// - переходим к след. Div (nextSibling), проверяем что это .detailMS__headerScore
|
||||||
|
// - внутри ищем span.p1_home и span.p1_away
|
||||||
|
var footballMatchSummary = `
|
||||||
|
<div class="detailMS">
|
||||||
|
<div class="detailMS__incidentsHeader stage-12">
|
||||||
|
<div class="detailMS__headerText">1st Half</div>
|
||||||
|
<div class="detailMS__headerScore">
|
||||||
|
<span class="p1_home" title="">1</span> - <span class="p1_away" title="">0</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="detailMS__incidentRow incidentRow--home odd">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="detailMS__incidentsHeader stage-13">
|
||||||
|
<div class="detailMS__headerText">2nd Half</div>
|
||||||
|
<div class="detailMS__headerScore">
|
||||||
|
<span class="p2_home" title="">3</span> -<span class="p2_away" title="">0</span>
|
||||||
|
</div>
|
||||||
|
</div>`
|
||||||
|
|
||||||
|
// Пример пустой статистики
|
||||||
|
var emptyMatchSummary = `
|
||||||
|
<div class="detailMS">
|
||||||
|
<div class="detailMS__incidentsHeader stage-9999">
|
||||||
|
<div class="detailMS__headerText"></div>
|
||||||
|
<div class="detailMS__headerScore">
|
||||||
|
<span class="p1_home" title="">0</span> -<span class="p1_away" title="">2</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="detailMS__incidentRow --empty odd">
|
||||||
|
-
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="match-information-wrapper">
|
||||||
|
<div class="parts match-information">
|
||||||
|
<div>
|
||||||
|
<div class="stage-header">
|
||||||
|
<div class="h-part">Match Information</div>
|
||||||
|
</div>
|
||||||
|
<div class="match-information-data">
|
||||||
|
<div class="content">Venue: Carrow Road (Norwich)</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>`
|
||||||
|
|
||||||
|
// На странице h2h landing
|
||||||
|
var matchZoneChampAndRound = `
|
||||||
|
<span class="description__country">
|
||||||
|
ENGLAND:
|
||||||
|
<a href="#" onclick="window.open('/football/england/premier-league/'); return false;">
|
||||||
|
Premier League - Round 30
|
||||||
|
</a>
|
||||||
|
</span>`
|
||||||
|
|
||||||
|
// На странице h2h landing
|
||||||
|
// Самый простой способ - найти два Div с классом tomyteams. Первый - хозяева, второй - гости. TeamID достать из id атрибута
|
||||||
|
var teamID = `
|
||||||
|
<div class="team-text tname-home">
|
||||||
|
<div class="tname">
|
||||||
|
<div class="tname__text">
|
||||||
|
<span style="display: none" class="dw-icon ico"> </span>
|
||||||
|
<a href="#" class="participant-imglink" onclick="window.open('/team/newcastle-utd/p6ahwuwJ'); return false;">Newcastle</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="team-text tname-away">
|
||||||
|
<div class="tname">
|
||||||
|
<div class="tname__text">
|
||||||
|
<a href="#" class="participant-imglink" onclick="window.open('/team/norwich/Qo6off6p'); return false;">Norwich</a>
|
||||||
|
<span style="display: none" class="dw-icon ico"> </span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
|
||||||
|
var footballMatchScore = `
|
||||||
|
<div class="match-info">
|
||||||
|
<div class="current-result" id="event_detail_current_result">
|
||||||
|
<span class="scoreboard">1</span>
|
||||||
|
<span>
|
||||||
|
<span class="scoreboard-divider">-</span>
|
||||||
|
<span class="scoreboard">0</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="info-status mstat">
|
||||||
|
Finished
|
||||||
|
</div>
|
||||||
|
</div>`
|
||||||
|
|
||||||
|
var footballMatchNotStarted = `
|
||||||
|
<div class="match-info">
|
||||||
|
<div class="current-result" id="event_detail_current_result">
|
||||||
|
<span class="scoreboard-divider">-</span>
|
||||||
|
</div>
|
||||||
|
<div class="info-status mstat">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>`
|
||||||
|
|
||||||
|
// Время матча отдельным запросом на адрес Request URL: https://d.flashscore.com/x/feed/dc_1_riQ962dS
|
||||||
|
// где riQ962dS - matchID
|
||||||
|
// Время матча в поле DD или DC
|
||||||
|
var encodedMatchTime = `DA÷1¬DZ÷1¬DB÷1¬DD÷1592586000¬AW÷1¬DC÷1592586000¬DS÷0¬DI÷-1¬DL÷1¬DM÷No spectators.¬DT÷¬DX÷MC,OD,HH,SCR,LT,TA,TV,NF¬DEI÷https://www.flashscore.com/res/image/data/CY8Xm4wS-Am48qVMh-Ozt88U7U.png¬DV÷1¬SC÷16¬SB÷1¬SD÷bet365¬A1÷¬~`
|
||||||
|
|
||||||
|
// Где счет и надпись Finished
|
||||||
|
var liveFootballMatchInfo = `<div class="match-info">
|
||||||
|
<div class="current-result" id="event_detail_current_result">
|
||||||
|
<span class="scoreboard-divider">-</span>
|
||||||
|
</div>
|
||||||
|
<div class="info-status mstat">
|
||||||
|
2nd Half
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>`
|
||||||
|
|
||||||
|
// На странице команды
|
||||||
|
// Находим ссылки внутри h2.tournament и проверяем href
|
||||||
|
var teamZone = `<h2 class="tournament">
|
||||||
|
<div class="sportIcon sportIcon--soccer"></div>
|
||||||
|
<a href="/football/">Football</a>
|
||||||
|
<span class="breadcrumb__arrow"></span>
|
||||||
|
<span class="flag fl_198"></span>
|
||||||
|
<a href="/football/england/">England</a>
|
||||||
|
</h2>`
|
||||||
|
|
||||||
|
// Full Time OVER/UNDER внутри блока block-under-over-ft
|
||||||
|
// Последние курсы на тотал 2.5 имеют класс .not-published, что означает - удалены букмекером.
|
||||||
|
// На сайте flashscore отображаются перечеркнутыми
|
||||||
|
var overUnderOdds = `<div id="block-under-over-ft" class="odds-table-wrapper" style="display: block;">
|
||||||
|
<table id="odds_ou_0.5" class="odds sortable" cellspacing="0">
|
||||||
|
<colgroup>
|
||||||
|
<col/>
|
||||||
|
<col width="60"/>
|
||||||
|
<col width="60"/>
|
||||||
|
<col width="60"/>
|
||||||
|
</colgroup>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="bookmaker no-padding unsortable">Bookmaker</th>
|
||||||
|
<th class="unsortable">Total</th>
|
||||||
|
<th>Over</th>
|
||||||
|
<th>Under</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr class="odd">
|
||||||
|
<td class="bookmaker">
|
||||||
|
<div class="blink" onclick="e_t.track_click('bookmaker-button-click', 'block-under-over_ft_bookmaker');"><a href="/bookmaker/417/?sport=1&from=odds-comparison" onclick="return !window.open(this.href)" title="1xBet" class="elink"><span style="background-image:url(/res/image/data/bookmakers/30-417.png); background-size: 100%; background-repeat: no-repeat;" class="detail-blogos"></span></a></div>
|
||||||
|
<div title="This match will be available for LIVE betting!" onmouseover="tt.show(this, event)" onmouseout="tt.hide(this)" class="live-icon-bookmaker" onclick="window.open('/bookmaker/417/?sport=1&from=live-detail' + (window.fs_detail && fs_detail.DA ? (fs_detail.DA == cjs.Api.constantsManager.getEventStageTypes().LIVE ? '-live-match' : '') : ''));"><span class="icon live-offer1" onclick="e_t.track_click('bookmaker-button-click', 'block-under-over_ft_live-offer');"> </span></div>
|
||||||
|
</td>
|
||||||
|
<td>0.5</td>
|
||||||
|
<td class="kx" onclick="e_t.track_click('bookmaker-button-click', 'block-under-over_ft_over');"><span class=" odds-wrap up" alt="1.07[u]1.09:">1.09</span></td>
|
||||||
|
<td class="kx" onclick="e_t.track_click('bookmaker-button-click', 'block-under-over_ft_under');"><span class=" odds-wrap down" alt="7.00[d]5.90:">5.90</span></td>
|
||||||
|
</tr>
|
||||||
|
<tr class="even">
|
||||||
|
<td class="bookmaker">
|
||||||
|
<div class="blink" onclick="e_t.track_click('bookmaker-button-click', 'block-under-over_ft_bookmaker');"><a href="/bookmaker/16/?sport=1&from=odds-comparison" onclick="return !window.open(this.href)" title="bet365" class="elink"><span style="background-image:url(/res/image/data/bookmakers/30-16.png); background-size: 100%; background-repeat: no-repeat;" class="detail-blogos"></span></a></div>
|
||||||
|
<div title="This match will be available for LIVE betting!" onmouseover="tt.show(this, event)" onmouseout="tt.hide(this)" class="live-icon-bookmaker" onclick="window.open('/bookmaker/16/?sport=1&from=live-detail' + (window.fs_detail && fs_detail.DA ? (fs_detail.DA == cjs.Api.constantsManager.getEventStageTypes().LIVE ? '-live-match' : '') : ''));"><span class="icon live-offer1" onclick="e_t.track_click('bookmaker-button-click', 'block-under-over_ft_live-offer');"> </span></div>
|
||||||
|
</td>
|
||||||
|
<td>0.5</td>
|
||||||
|
<td class="kx max" onclick="e_t.track_click('bookmaker-button-click', 'block-under-over_ft_over');"><span class=" odds-wrap up" alt="1.10[u]1.11:">1.11</span></td>
|
||||||
|
<td class="kx" onclick="e_t.track_click('bookmaker-button-click', 'block-under-over_ft_under');"><span class=" odds-wrap down" alt="7.00[d]6.50:">6.50</span></td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<table id="odds_ou_2.5" class="odds sortable" cellspacing="0">
|
||||||
|
<colgroup>
|
||||||
|
<col/>
|
||||||
|
<col width="60"/>
|
||||||
|
<col width="60"/>
|
||||||
|
<col width="60"/>
|
||||||
|
</colgroup>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="bookmaker no-padding unsortable">Bookmaker</th>
|
||||||
|
<th class="unsortable">Total</th>
|
||||||
|
<th>Over</th>
|
||||||
|
<th>Under</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr class="odd">
|
||||||
|
<td class="bookmaker">
|
||||||
|
<div class="blink" onclick="e_t.track_click('bookmaker-button-click', 'block-under-over_ft_bookmaker');"><a href="/bookmaker/417/?sport=1&from=odds-comparison" onclick="return !window.open(this.href)" title="1xBet" class="elink"><span style="background-image:url(/res/image/data/bookmakers/30-417.png); background-size: 100%; background-repeat: no-repeat;" class="detail-blogos"></span></a></div>
|
||||||
|
<div title="This match will be available for LIVE betting!" onmouseover="tt.show(this, event)" onmouseout="tt.hide(this)" class="live-icon-bookmaker" onclick="window.open('/bookmaker/417/?sport=1&from=live-detail' + (window.fs_detail && fs_detail.DA ? (fs_detail.DA == cjs.Api.constantsManager.getEventStageTypes().LIVE ? '-live-match' : '') : ''));"><span class="icon live-offer1" onclick="e_t.track_click('bookmaker-button-click', 'block-under-over_ft_live-offer');"> </span></div>
|
||||||
|
</td>
|
||||||
|
<td>2.5</td>
|
||||||
|
<td class="kx max" onclick="e_t.track_click('bookmaker-button-click', 'block-under-over_ft_over');"><span class=" odds-wrap up" alt="2.41[u]2.91:">2.91</span></td>
|
||||||
|
<td class="kx" onclick="e_t.track_click('bookmaker-button-click', 'block-under-over_ft_under');"><span class=" odds-wrap down" alt="1.54[d]1.52:">1.52</span></td>
|
||||||
|
</tr>
|
||||||
|
<tr class="even">
|
||||||
|
<td class="bookmaker">
|
||||||
|
<div class="blink" onclick="e_t.track_click('bookmaker-button-click', 'block-under-over_ft_bookmaker');"><a href="/bookmaker/16/?sport=1&from=odds-comparison" onclick="return !window.open(this.href)" title="bet365" class="elink"><span style="background-image:url(/res/image/data/bookmakers/30-16.png); background-size: 100%; background-repeat: no-repeat;" class="detail-blogos"></span></a></div>
|
||||||
|
<div title="This match will be available for LIVE betting!" onmouseover="tt.show(this, event)" onmouseout="tt.hide(this)" class="live-icon-bookmaker" onclick="window.open('/bookmaker/16/?sport=1&from=live-detail' + (window.fs_detail && fs_detail.DA ? (fs_detail.DA == cjs.Api.constantsManager.getEventStageTypes().LIVE ? '-live-match' : '') : ''));"><span class="icon live-offer1" onclick="e_t.track_click('bookmaker-button-click', 'block-under-over_ft_live-offer');"> </span></div>
|
||||||
|
</td>
|
||||||
|
<td>2.5</td>
|
||||||
|
<td class="kx" onclick="e_t.track_click('bookmaker-button-click', 'block-under-over_ft_over');"><span class=" odds-wrap up" alt="2.50[u]2.75:">2.75</span></td>
|
||||||
|
<td class="kx" onclick="e_t.track_click('bookmaker-button-click', 'block-under-over_ft_under');"><span class=" odds-wrap down" alt="1.53[d]1.44:">1.44</span></td>
|
||||||
|
</tr>
|
||||||
|
<tr class="odd">
|
||||||
|
<td class="bookmaker">
|
||||||
|
<div class="blink" onclick="e_t.track_click('bookmaker-button-click', 'block-under-over_ft_bookmaker');"><a href="/bookmaker/15/?sport=1&from=odds-comparison" onclick="return !window.open(this.href)" title="William Hill" class="elink"><span style="background-image:url(/res/image/data/bookmakers/30-15.png); background-size: 100%; background-repeat: no-repeat;" class="detail-blogos"></span></a></div>
|
||||||
|
</td>
|
||||||
|
<td>2.5</td>
|
||||||
|
<td class="kx" onclick="e_t.track_click('bookmaker-button-click', 'block-under-over_ft_over');"><span class="not-published odds-wrap " alt=":">2.38</span></td>
|
||||||
|
<td class="kx" onclick="e_t.track_click('bookmaker-button-click', 'block-under-over_ft_under');"><span class="not-published odds-wrap " alt=":">1.53</span></td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>`
|
||||||
|
|
||||||
|
// CHECK https://www.flashscore.com/match/vZNmafet/#match-summary EXTRA TIME
|
||||||
47
tmp
Normal file
47
tmp
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
DA÷3¬
|
||||||
|
DZ÷3¬
|
||||||
|
DB÷3¬
|
||||||
|
DD÷1227121200¬
|
||||||
|
AW÷¬
|
||||||
|
DC÷1227121200¬
|
||||||
|
DS÷0¬
|
||||||
|
DE÷24¬
|
||||||
|
DF÷20¬
|
||||||
|
DG÷24¬
|
||||||
|
DH÷20¬
|
||||||
|
DI÷-1¬
|
||||||
|
DL÷1¬
|
||||||
|
DM÷¬
|
||||||
|
DT÷¬
|
||||||
|
DX÷OD,HH,LT,TA¬
|
||||||
|
DEI÷https://www.flashscore.com/res/image/data/SSj7IRf5-WIYXsO4F-OMxrcHJ9.png¬
|
||||||
|
DV÷7¬
|
||||||
|
DJ÷H¬
|
||||||
|
AZ÷1¬
|
||||||
|
SC÷417¬
|
||||||
|
SB÷1¬
|
||||||
|
SD÷1xBet¬A1÷¬~
|
||||||
|
|
||||||
|
|
||||||
|
DA÷3¬DZ÷3¬DB÷3¬DD÷1602340391¬AW÷¬
|
||||||
|
DC÷1602334800¬
|
||||||
|
DS÷0¬
|
||||||
|
DE÷32¬
|
||||||
|
DF÷27¬
|
||||||
|
DG÷32¬
|
||||||
|
DH÷27¬
|
||||||
|
DI÷-1¬
|
||||||
|
DL÷1¬
|
||||||
|
DM÷¬
|
||||||
|
DT÷¬DX÷OD,HH,LT,TA¬DEI÷https://www.flashscore.com/res/image/data/SSj7IRf5-UixbnuN9-0M28Iczn.png¬DV÷7¬DJ÷H¬AZ÷1¬SC÷417¬SB÷1¬SD÷1xBet¬A1÷¬~
|
||||||
|
|
||||||
|
|
||||||
|
DA÷3¬DZ÷3¬DB÷10¬DD÷1552746928¬AW÷¬DC÷1552739400¬DS÷0¬
|
||||||
|
|
||||||
|
DE÷33¬
|
||||||
|
DF÷34¬
|
||||||
|
|
||||||
|
DG÷30¬
|
||||||
|
DH÷30¬
|
||||||
|
DI÷-1¬
|
||||||
|
DL÷1¬DM÷¬DT÷¬BZ÷1¬DX÷LI,OD,HH,DR¬DEI÷https://www.flashscore.com/res/image/data/pIQZwIAr-6m20Ta2F-rNqjPY6e.png¬DV÷7¬DJ÷A¬AZ÷0¬SC÷417¬SB÷1¬SD÷1xBet¬A1÷¬~
|
||||||
183
types.go
Executable file
183
types.go
Executable file
@@ -0,0 +1,183 @@
|
|||||||
|
package flashscore
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
xhtml "golang.org/x/net/html"
|
||||||
|
"gordenko.dev/dima/flashscore/model"
|
||||||
|
webspider "gordenko.dev/dima/spider"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
//WSMessageFootballChamps = "footballChamps"
|
||||||
|
//WSMessageFootballMatchReports = "footballMatchReports"
|
||||||
|
//WSMessageFootballMatchUpdate = "footballMatchUpdate"
|
||||||
|
//WSMTeamMatchReport = "teamMatchReport"
|
||||||
|
|
||||||
|
// Channel = "main"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// В файле https://www.flashscore.com/x/js/core_2_1825000000.js переменной feed_sign жестко задается значение
|
||||||
|
feedSign = "SW9D1eZo"
|
||||||
|
|
||||||
|
//url = "https://www.flashscore.com/football/england/premier-league/"
|
||||||
|
// Слэш в конце, чтобы избежать лишнего редиректа
|
||||||
|
teamURLPattern = "https://www.flashscore.com/team%s/"
|
||||||
|
// champID = /england/premier-league
|
||||||
|
//fixturesURLPattern = "https://www.flashscore.com/football%s/fixtures/"
|
||||||
|
//h2hURL = "https://www.flashscore.com/match/lUKXoAEo/#h2h;overall"
|
||||||
|
//h2hLandingURLPattern = "https://www.flashscore.com/match/%s/#h2h;overall"
|
||||||
|
h2hLandingURLPattern = "https://www.flashscore.com/match/%s/#/h2h/overall"
|
||||||
|
footballH2HTablesURLPattern = "https://d.flashscore.com/x/feed/d_hh_%s_en_1"
|
||||||
|
// Лента событий: голы, карточки, пенальти и т.д.
|
||||||
|
footballMatchSummaryURLPattern = "https://d.flashscore.com/x/feed/d_su_%s_en_1"
|
||||||
|
footballOddsURLPattern = "https://d.flashscore.com/x/feed/d_od_%s_en_1_eu" // en - lang, eu - price format
|
||||||
|
|
||||||
|
handballOddsURLPattern = "https://d.flashscore.com/x/feed/df_od_1_%s"
|
||||||
|
|
||||||
|
//basketballAllGamesURL = "https://d.flashscore.com/x/feed/f_3_0_2_en_1"
|
||||||
|
// basketball next day https://d.flashscore.com/x/feed/f_3_1_2_en_1
|
||||||
|
|
||||||
|
footballAllGamesTodayURL = "https://d.flashscore.com/x/feed/f_1_0_2_en_1"
|
||||||
|
footballAllGamesTomorrowURL = "https://d.flashscore.com/x/feed/f_1_1_2_en_1"
|
||||||
|
|
||||||
|
//footballAllGamesFeedURL = "https://d.flashscore.com/x/feed/f_1_0_3_en_1" ????
|
||||||
|
|
||||||
|
handballAllGamesTodayURL = "https://d.flashscore.com/x/feed/f_7_0_2_en_1"
|
||||||
|
handballAllGamesTomorrowURL = "https://d.flashscore.com/x/feed/f_7_1_2_en_1"
|
||||||
|
|
||||||
|
tennisAllGamesTodayURL = "https://d.flashscore.com/x/feed/f_2_0_3_en_1"
|
||||||
|
tennisAllGamesTomorrowURL = "https://d.flashscore.com/x/feed/f_2_1_3_en_1"
|
||||||
|
|
||||||
|
handballH2HTablesURLPattern = "https://d.flashscore.com/x/feed/df_hh_1_%s"
|
||||||
|
|
||||||
|
tennisH2HTablesURLPattern = "https://d.flashscore.com/x/feed/df_hh_1_%s"
|
||||||
|
|
||||||
|
handballScoreByPeriodsURLPattern = "https://d.flashscore.com/x/feed/df_sur_1_%s"
|
||||||
|
handballMatchScoreURLPattern = "https://d.flashscore.com/x/feed/dc_1_%s"
|
||||||
|
|
||||||
|
tennisMatchStatURLPattern = "https://d.flashscore.com/x/feed/df_st_1_%s"
|
||||||
|
|
||||||
|
teamChampURLPattern = "https://www.flashscore.com%s"
|
||||||
|
|
||||||
|
SymbolNot = "¬"
|
||||||
|
SymbolTilda = "~"
|
||||||
|
SymbolDivide = "÷"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
Football = 1
|
||||||
|
Handball = 2
|
||||||
|
Tennis = 3
|
||||||
|
Basketball = 4
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
BookmakerBwin = 2
|
||||||
|
BookmakerBetAtHome = 3
|
||||||
|
BookmakerUnibet = 5
|
||||||
|
BookmakerWillHill = 15
|
||||||
|
BookmakerBet365 = 16
|
||||||
|
BookmakerBetfair = 429
|
||||||
|
Bookmaker1xBet = 417
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
nodeTypes = map[xhtml.NodeType]string{
|
||||||
|
xhtml.ErrorNode: "ErrorNode",
|
||||||
|
xhtml.TextNode: "TextNode",
|
||||||
|
xhtml.DocumentNode: "DocumentNode",
|
||||||
|
xhtml.ElementNode: "ElementNode",
|
||||||
|
xhtml.CommentNode: "CommentNode",
|
||||||
|
xhtml.DoctypeNode: "DoctypeNode",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
sportPrefixes = map[int]string{
|
||||||
|
Football: "/football",
|
||||||
|
Handball: "/handball",
|
||||||
|
Basketball: "/basketball",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
//type FlashscoreSnapshot struct {
|
||||||
|
// Handball []UpcomingMatchReport
|
||||||
|
//}
|
||||||
|
|
||||||
|
type ScheduledMatch struct {
|
||||||
|
MatchID string
|
||||||
|
StartTime time.Time
|
||||||
|
// Название (правда в верхнем регистре) и ID достаем со страницы AllGames
|
||||||
|
Zone model.TeamZone
|
||||||
|
// Если нет в БД - загружаем страницу с чемпионатом и достаем чистое название
|
||||||
|
// без приписок Play Offs, State 2 и т.д.
|
||||||
|
ChampID string
|
||||||
|
SportID int
|
||||||
|
FullChampName string
|
||||||
|
}
|
||||||
|
|
||||||
|
type UpcomingMatchReport struct {
|
||||||
|
MatchID string `json:"matchID"`
|
||||||
|
SportID int `json:"sportID"`
|
||||||
|
StartTime int64 `json:"startTime"`
|
||||||
|
ParseTime time.Time `json:"parseTime"`
|
||||||
|
Home model.Team `json:"home"`
|
||||||
|
Away model.Team `json:"away"`
|
||||||
|
Champ model.TeamChamp `json:"champ"`
|
||||||
|
Zone model.TeamZone `json:"zone"`
|
||||||
|
FullChampName string `json:"fullChampName"`
|
||||||
|
HomeTeamMatches []model.TeamMatch `json:"homeTeamMatches"`
|
||||||
|
AwayTeamMatches []model.TeamMatch `json:"awayTeamMatches"`
|
||||||
|
H2H []model.TeamMatch `json:"h2h"`
|
||||||
|
Odds Odds `json:"odds"`
|
||||||
|
IsTeamsLoaded bool `json:"isTeamsLoaded"`
|
||||||
|
IsHistoryLoaded bool `json:"isHistoryLoaded"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Flashscore struct {
|
||||||
|
model *model.Model
|
||||||
|
spider *webspider.Spider
|
||||||
|
logger *log.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
type teamScoreByPeriods struct {
|
||||||
|
HasStats bool
|
||||||
|
P1HomeGoals int
|
||||||
|
P1AwayGoals int
|
||||||
|
P2HomeGoals int
|
||||||
|
P2AwayGoals int
|
||||||
|
}
|
||||||
|
|
||||||
|
type Winner3Way struct {
|
||||||
|
Bookmaker int `json:"bookmaker"`
|
||||||
|
Home float64 `json:"home"`
|
||||||
|
Draw float64 `json:"draw"`
|
||||||
|
Away float64 `json:"away"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Winner2Way struct {
|
||||||
|
Bookmaker int `json:"bookmaker"`
|
||||||
|
Home float64 `json:"home"`
|
||||||
|
Away float64 `json:"away"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TotalOffer struct {
|
||||||
|
Bookmaker int `json:"bookmaker"`
|
||||||
|
Over float64 `json:"over"`
|
||||||
|
Under float64 `json:"under"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Total struct {
|
||||||
|
Total string `json:"total"`
|
||||||
|
Offers []TotalOffer `json:"offers"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Odds struct {
|
||||||
|
FullTimeTotal []Total `json:"fullTimeTotal"`
|
||||||
|
H1Total []Total `json:"h1Total"`
|
||||||
|
Winner []Winner3Way `json:"winner3way"` // footabll, handball
|
||||||
|
Winner2Way []Winner2Way `json:"winner2way"` // tennis
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user