From 276e0122f3911282e4afd3e7c4478833a59e8612 Mon Sep 17 00:00:00 2001 From: Ryan Yeske Date: Sat, 9 Sep 2023 18:02:27 -0700 Subject: [PATCH] Replace mlb odds with a single !odds which reports on all teams --- fetch/fetch.go | 39 ++++++++++++++++++ handlers/mlb/mlb.go | 87 ++++++++++++++++++++++++++++------------ handlers/mlb/mlb_test.go | 27 +++++++++++++ main.go | 3 +- 4 files changed, 129 insertions(+), 27 deletions(-) create mode 100644 fetch/fetch.go create mode 100644 handlers/mlb/mlb_test.go diff --git a/fetch/fetch.go b/fetch/fetch.go new file mode 100644 index 0000000..752577b --- /dev/null +++ b/fetch/fetch.go @@ -0,0 +1,39 @@ +package fetch + +import ( + "io" + "log" + "net/http" + "time" +) + +type record struct { + StatusCode int + Body []byte + Time time.Time +} + +var cache = make(map[string]record) + +// Fetch url returning cached data if it exists and is not older than maxAge +func Get(url string, maxAge time.Duration) (int, []byte, error) { + rec, ok := cache[url] + if ok && time.Since(rec.Time) < maxAge { + log.Printf("cache hit") + return rec.StatusCode, rec.Body, nil + } + + resp, err := http.Get(url) + if err != nil { + return 0, nil, err + } + + body, err := io.ReadAll(resp.Body) + if err != nil { + return 0, nil, err + } + + cache[url] = record{resp.StatusCode, body, time.Now()} + + return resp.StatusCode, body, nil +} diff --git a/handlers/mlb/mlb.go b/handlers/mlb/mlb.go index 8729d67..8429e68 100644 --- a/handlers/mlb/mlb.go +++ b/handlers/mlb/mlb.go @@ -1,55 +1,92 @@ package mlb import ( + "encoding/json" "fmt" "goirc/bot" - "goirc/shell" + "goirc/fetch" "sort" - "strconv" "strings" "time" ) -func SingleTeamOdds(params bot.HandlerParams) error { - team := strings.ToUpper(params.Matches[1]) +type TeamEndData struct { + PoffTitle float64 +} + +type Team struct { + TeamID int `json:"teamId"` + AbbName string + League string + Division string + EndData TeamEndData +} + +type TeamList []Team + +func (tl TeamList) String() string { + arr := []string{} + for i, team := range tl { + arr = append(arr, fmt.Sprintf("%s:%.0f%%", team.AbbName, 100*team.EndData.PoffTitle)) + if i == 5 { // top 6 teams make the playoffs in each league + arr = append(arr, "|") + } + } + return strings.Join(arr, " ") +} +func fetchTeams() (TeamList, error) { date := time.Now().Format(time.DateOnly) - r, err := shell.Command(fmt.Sprintf(` -curl -s 'https://www.fangraphs.com/api/playoff-odds/odds?dateEnd=%s&dateDelta=&projectionMode=2&standingsType=div' |\ -jq .[] |\ -jq 'select(.abbName == "%s")'.endData.poffTitle -`, date, team)) + url := fmt.Sprintf("https://www.fangraphs.com/api/playoff-odds/odds?dateEnd=%s&dateDelta=&projectionMode=2&standingsType=lg", date) + + _, bytes, err := fetch.Get(url, time.Minute) if err != nil { - return err + return nil, err } - f, err := strconv.ParseFloat(strings.TrimSpace(r), 32) + var teams TeamList + err = json.Unmarshal(bytes, &teams) if err != nil { - return err + return nil, err } - params.Privmsgf(params.Target, "FanGraphs projects %s to have a %.3f%% chance of making the playoffs", team, f*100) + sort.Slice(teams, func(i, j int) bool { + return teams[i].EndData.PoffTitle > teams[j].EndData.PoffTitle + }) - return nil + return teams, nil } -func Teams(params bot.HandlerParams) error { - date := time.Now().Format(time.DateOnly) +func fetchLeagueTeams(league string) (TeamList, error) { + teams, err := fetchTeams() + if err != nil { + return nil, err + } + + var lt TeamList + + for _, t := range teams { + if t.League == league { + lt = append(lt, t) + } + } + + return lt, nil +} - r, err := shell.Command(fmt.Sprintf(` -curl -s 'https://www.fangraphs.com/api/playoff-odds/odds?dateEnd=%s&dateDelta=&projectionMode=2&standingsType=div' | jq -r .[].abbName -`, date)) +func PlayoffOdds(params bot.HandlerParams) error { + teams, err := fetchLeagueTeams("AL") if err != nil { return err } + params.Privmsgf(params.Target, "AL %s", teams.String()) - teams := strings.Split(r, "\n") - sort.Slice(teams, func(i, j int) bool { - return teams[i] < teams[j] - }) - - params.Privmsgf(params.Target, "Teams: %s", strings.Join(teams, " ")) + teams, err = fetchLeagueTeams("NL") + if err != nil { + return err + } + params.Privmsgf(params.Target, "NL %s", teams.String()) return nil } diff --git a/handlers/mlb/mlb_test.go b/handlers/mlb/mlb_test.go new file mode 100644 index 0000000..12b8f22 --- /dev/null +++ b/handlers/mlb/mlb_test.go @@ -0,0 +1,27 @@ +package mlb + +import ( + "testing" +) + +func TestFetchTeams(t *testing.T) { + teams, err := fetchTeams() + if err != nil { + t.Error(err) + } + t.Log(teams) +} + +func TestFetchLeagueTeams(t *testing.T) { + for _, league := range []string{"AL", "NL"} { + t.Run(league, func(t *testing.T) { + teams, err := fetchLeagueTeams(league) + if err != nil { + t.Error(err) + } + if len(teams) != 15 { + t.Errorf("expected 15 teams, got %d", len(teams)) + } + }) + } +} diff --git a/main.go b/main.go index 1bf567f..c7ada03 100644 --- a/main.go +++ b/main.go @@ -48,8 +48,7 @@ func main() { b.Handle(`(https?://\S+)`, handlers.Link) b.Handle(`^!day`, handlers.NationalDay) b.Handle(`\b69\b`, handlers.Nice) - b.Handle(`^mlb odds (...)?$`, mlb.SingleTeamOdds) - b.Handle(`^mlb teams$`, mlb.Teams) + b.Handle(`^!odds`, mlb.PlayoffOdds) b.Handle(`^!pom`, handlers.POM) b.Handle(`^("[^"]+)$`, handlers.Quote) b.Handle(`^!remindme ([^\s]+) (.+)$`, handlers.RemindMe)