/* Copyright © 2024 filifa This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ package statsapi import ( "encoding/json" "io" "net/http" "net/url" "github.com/evanphx/json-patch/v5" ) // DefaultClient is a statsapi.Client instantiated using http.DefaultClient. var DefaultClient = NewClient(http.DefaultClient) // RequestSchedule uses DefaultClient to access the schedule endpoint. func RequestSchedule(sportId, teamId, date string) ([]byte, error) { return DefaultClient.RequestSchedule(sportId, teamId, date) } // RequestFeed uses DefaultClient to access the feed endpoint. func RequestFeed(gamePk string) ([]byte, error) { return DefaultClient.RequestFeed(gamePk) } // RequestDiffPatch uses DefaultClient to access the diffpatch endpoint. func RequestDiffPatch(gamePk, startTimecode, pushUpdateId string) (DiffPatchResponse, error) { return DefaultClient.RequestDiffPatch(gamePk, startTimecode, pushUpdateId) } // RequestContent uses DefaultClient to access the content endpoint. func RequestContent(gamePk string) ([]byte, error) { return DefaultClient.RequestContent(gamePk) } // RequestStandings uses DefaultClient to access the standings endpoint. func RequestStandings(leagueId string) ([]byte, error) { return DefaultClient.RequestStandings(leagueId) } type DiffPatchResponse []byte // Client is a struct used for making Stats API requests. type Client struct { baseURL url.URL httpClient *http.Client } // NewClient returns a statsapi.Client with statsapi.mlb.com as the base URL. func NewClient(c *http.Client) *Client { return &Client{ baseURL: url.URL{ Scheme: "https", Host: "statsapi.mlb.com", }, httpClient: c, } } // ExtractPatches extracts a list of jsonpatch.Patch structs from the response // given by the diffpatch endpoint. These patches can then be applied more // easily than the raw response. func (resp *DiffPatchResponse) ExtractPatches() ([]jsonpatch.Patch, error) { var patches []jsonpatch.Patch var objs []map[string]jsonpatch.Patch err := json.Unmarshal([]byte(*resp), &objs) if err != nil { return patches, err } for _, obj := range objs { patch := obj["diff"] patches = append(patches, patch) } return patches, err } // RequestSchedule accesses the schedule endpoint. func (c *Client) RequestSchedule(sportId, teamId, date string) ([]byte, error) { endpoint := url.URL{Path: "api/v1/schedule"} query := endpoint.Query() query.Add("sportId", sportId) query.Add("teamId", teamId) query.Add("date", date) endpoint.RawQuery = query.Encode() return c.get(&endpoint) } // RequestFeed accesses the feed endpoint. func (c *Client) RequestFeed(gamePk string) ([]byte, error) { endpoint := url.URL{Path: "api/v1.1/game/" + gamePk + "/feed/live"} return c.get(&endpoint) } // RequestDiffPatch accesses the diffpatch endpoint. func (c *Client) RequestDiffPatch(gamePk, startTimecode, pushUpdateId string) (DiffPatchResponse, error) { endpoint := url.URL{Path: "api/v1.1/game/" + gamePk + "/feed/live/diffPatch"} query := endpoint.Query() query.Add("language", "en") query.Add("startTimecode", startTimecode) query.Add("pushUpdateId", pushUpdateId) endpoint.RawQuery = query.Encode() return c.get(&endpoint) } // RequestContent accesses the content endpoint. func (c *Client) RequestContent(gamePk string) ([]byte, error) { endpoint := url.URL{Path: "api/v1/game/" + gamePk + "/content"} return c.get(&endpoint) } // RequestStandings accesses the standings endpoint. func (c *Client) RequestStandings(leagueId string) ([]byte, error) { endpoint := url.URL{Path: "api/v1/standings"} query := endpoint.Query() query.Add("leagueId", leagueId) endpoint.RawQuery = query.Encode() return c.get(&endpoint) } // get makes a GET request to the given endpoint and returns the bytes from the // body of the response. func (c *Client) get(endpoint *url.URL) ([]byte, error) { url := c.baseURL.ResolveReference(endpoint) resp, err := c.httpClient.Get(url.String()) if err != nil { return nil, err } defer resp.Body.Close() body, err := io.ReadAll(resp.Body) return body, err }