Skip to content

Commit 15045eb

Browse files
committed
Allow exporting the fixtures.
1 parent d547577 commit 15045eb

2 files changed

Lines changed: 161 additions & 3 deletions

File tree

fbd/fixtures.go

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
package fbd
2+
3+
import (
4+
"fmt"
5+
"io"
6+
"log/slog"
7+
"net/url"
8+
"strconv"
9+
"strings"
10+
)
11+
12+
type fixturesConfig struct {
13+
url string
14+
client *Client
15+
fields url.Values
16+
}
17+
18+
type fixturesConfigBuilder struct {
19+
config fixturesConfig
20+
}
21+
22+
func (c *Client) NewFixturesConfigBuilder() *fixturesConfigBuilder {
23+
return &fixturesConfigBuilder{
24+
config: fixturesConfig{
25+
url: "https://www.football-bet-data.com/history/",
26+
client: c,
27+
fields: url.Values{},
28+
},
29+
}
30+
}
31+
32+
func (b *fixturesConfigBuilder) WithLeagues(leagues map[Country][]string) *fixturesConfigBuilder {
33+
for country, leagueCodes := range ValidLeagues {
34+
for _, leagueCode := range leagueCodes {
35+
key := string(country) + leagueCode
36+
b.config.fields.Del("ctl00$ContentPlaceHolder2$" + key)
37+
}
38+
}
39+
40+
if len(leagues) == 0 {
41+
leagues = ValidLeagues
42+
}
43+
44+
for country, leagueCodes := range leagues {
45+
for _, leagueCode := range leagueCodes {
46+
key := string(country) + leagueCode
47+
b.config.fields.Set(fmt.Sprintf("ctl00$ContentPlaceHolder2$%s", key), key)
48+
}
49+
}
50+
51+
return b
52+
}
53+
54+
func (b *fixturesConfigBuilder) WithSummerSeasons(seasons []int) *fixturesConfigBuilder {
55+
const prefix = "ctl00$ContentPlaceHolder2$"
56+
for key := range b.config.fields {
57+
if !strings.HasPrefix(key, prefix) {
58+
continue
59+
}
60+
name := strings.TrimPrefix(key, prefix)
61+
if len(name) == 4 && isDigits(name) {
62+
b.config.fields.Del(key)
63+
}
64+
}
65+
66+
for _, year := range seasons {
67+
if year <= 0 {
68+
continue
69+
}
70+
key := strconv.Itoa(year)
71+
b.config.fields.Set(fmt.Sprintf("ctl00$ContentPlaceHolder2$%s", key), key)
72+
}
73+
return b
74+
}
75+
76+
// WithWinterSeasons expects season start years, e.g. 2024 for 2024-2025 season
77+
func (b *fixturesConfigBuilder) WithWinterSeasons(seasons []int) *fixturesConfigBuilder {
78+
const prefix = "ctl00$ContentPlaceHolder2$"
79+
for key := range b.config.fields {
80+
if !strings.HasPrefix(key, prefix) {
81+
continue
82+
}
83+
name := strings.TrimPrefix(key, prefix)
84+
if len(name) == 5 && name[2] == '-' && isDigits(name[:2]) && isDigits(name[3:]) {
85+
b.config.fields.Del(key)
86+
}
87+
}
88+
89+
for _, startYear := range seasons {
90+
if startYear <= 0 {
91+
continue
92+
}
93+
short := startYear % 100
94+
next := (startYear + 1) % 100
95+
key := fmt.Sprintf("%02d-%02d", short, next)
96+
b.config.fields.Set(fmt.Sprintf("ctl00$ContentPlaceHolder2$%s", key), key)
97+
}
98+
return b
99+
}
100+
101+
func (b *fixturesConfigBuilder) Build() *fixturesConfig {
102+
return &b.config
103+
}
104+
105+
func (c *fixturesConfig) ExportToExcel() ([]byte, error) {
106+
if err := c.client.ensureReady(); err != nil {
107+
return nil, err
108+
}
109+
110+
slog.Info("Exporting fixtures")
111+
112+
fields, err := c.client.getWebFormsFields(c.url)
113+
if err != nil {
114+
return nil, err
115+
}
116+
117+
fields.Set("ctl00$ContentPlaceHolder2$ButtonEX2", "Export to Excel")
118+
119+
for key, values := range c.fields {
120+
fields.Del(key)
121+
for _, value := range values {
122+
fields.Add(key, value)
123+
}
124+
}
125+
126+
resp, err := c.client.httpClient.PostForm(c.url, fields)
127+
if err != nil {
128+
return nil, fmt.Errorf("post fixtures form: %w", err)
129+
}
130+
defer resp.Body.Close()
131+
132+
cookies := resp.Cookies()
133+
downloadSucceeded := false
134+
for _, cookie := range cookies {
135+
if cookie.Name == "downloadStarted" && cookie.Value == "1" {
136+
downloadSucceeded = true
137+
break
138+
}
139+
}
140+
141+
if !downloadSucceeded {
142+
return nil, fmt.Errorf("download did not succeed: missing or invalid downloadStarted cookie. Cookies: %v. Response body length: %d", cookies, resp.ContentLength)
143+
}
144+
145+
return io.ReadAll(resp.Body)
146+
}

main.go

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import (
99
func main() {
1010
email := ""
1111
password := ""
12-
outputPath := "E1_2025.xlsx"
1312

1413
client, err := fbd.NewClient()
1514
if err != nil {
@@ -26,6 +25,7 @@ func main() {
2625
}
2726
}()
2827

28+
dashboardPath := "dashboard_E_2025.xlsx"
2929
if err := client.NewDashboardConfigBuilder().
3030
WithMatchesNoPrediction().
3131
WithLeagues(map[fbd.Country][]string{fbd.ENGLAND: {"1", "2"}}).
@@ -37,9 +37,21 @@ func main() {
3737
fbd.MonthSep, fbd.MonthOct, fbd.MonthNov, fbd.MonthDec,
3838
}).
3939
Build().
40-
ExportToExcel(outputPath); err != nil {
40+
ExportToExcel(dashboardPath); err != nil {
4141
log.Fatalf("dashboard export failed: %v", err)
4242
}
4343

44-
log.Printf("Dashboard Excel exported to %s", outputPath)
44+
log.Printf("Dashboard Excel exported to %s", dashboardPath)
45+
46+
fixturesOutputPath := "fixtures_E_2025.xlsx"
47+
if err := client.NewFixturesConfigBuilder().
48+
WithLeagues(map[fbd.Country][]string{fbd.ENGLAND: {"1", "2"}}).
49+
WithSummerSeasons([]int{2025}).
50+
WithWinterSeasons([]int{2025}).
51+
Build().
52+
ExportToExcel(fixturesOutputPath); err != nil {
53+
log.Fatalf("fixtures export failed: %v", err)
54+
}
55+
56+
log.Printf("Fixtures Excel exported to %s", fixturesOutputPath)
4557
}

0 commit comments

Comments
 (0)