├── debug.log ├── screenshots ├── demo.gif ├── logo.png ├── standings.png ├── demo.tape └── nba-cli.svg ├── nba ├── global.go ├── game.go ├── utils.go ├── scoreboard.go ├── standings.go └── teams.json ├── .github └── workflows │ ├── dependabot.yaml │ └── release.yml ├── main.go ├── .gitignore ├── nag ├── params │ ├── params_test.go │ └── params.go ├── boxscoredefensive.go ├── boxscoresummaryv2.go ├── boxscoreplayertrackv2.go ├── gamerotation.go ├── commonplayerinfo.go ├── response.go ├── playbyplayv2.go ├── commonteamroster.go ├── commonplayoffseries.go ├── scoreboardv2.go ├── leaguestandingsv3.go ├── commonallplayers.go ├── teamgamelog.go ├── alltimeleadersgrids.go ├── assistleaders.go ├── boxscoremiscv2.go ├── boxscoreusagev2.go ├── boxscorescoringv2.go ├── boxscoreadvancedv2.go ├── boxscorefourfactorsv2.go ├── boxscoretraditionalv2.go ├── client.go ├── teamgamelogs.go └── interfaces.go ├── ui ├── keymaps.go ├── ui.go ├── scoreboards.go ├── gameboard │ └── scoretext │ │ └── scoretext.go ├── constants │ └── consts.go ├── standings.go └── game.go ├── cmd ├── root.go └── game.go ├── LICENSE ├── go.mod ├── README.md ├── .goreleaser.yml ├── go.sum └── entity └── game.go /debug.log: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /screenshots/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dylantientcheu/nbacli/HEAD/screenshots/demo.gif -------------------------------------------------------------------------------- /screenshots/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dylantientcheu/nbacli/HEAD/screenshots/logo.png -------------------------------------------------------------------------------- /screenshots/standings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dylantientcheu/nbacli/HEAD/screenshots/standings.png -------------------------------------------------------------------------------- /nba/global.go: -------------------------------------------------------------------------------- 1 | package nba 2 | 3 | var ( 4 | Gm *BoxScoreRepository 5 | Sb *ScoreboardRepository 6 | St *StandingsRepository 7 | ) 8 | -------------------------------------------------------------------------------- /.github/workflows/dependabot.yaml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | updates: 4 | - package-ecosystem: gomod 5 | directory: / 6 | schedule: 7 | interval: daily 8 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2022 Dylan Tientcheu hi@dylantientcheu.dev 3 | 4 | */ 5 | package main 6 | 7 | import "github.com/dylantientcheu/nbacli/cmd" 8 | 9 | func main() { 10 | cmd.Execute() 11 | } 12 | -------------------------------------------------------------------------------- /screenshots/demo.tape: -------------------------------------------------------------------------------- 1 | Output demo.gif 2 | 3 | Require echo 4 | 5 | Set FontSize 10 6 | Set Width 1200 7 | Set Height 800 8 | Set LoopOffset 50% 9 | Set TypingSpeed 200ms 10 | 11 | Type "nbacli games" Sleep 5s Enter 12 | 13 | Left Sleep 3s 14 | Left Sleep 3s 15 | Down Sleep 3s 16 | Down Sleep 3s 17 | Down Sleep 5s 18 | 19 | Enter Sleep 5s 20 | Down 21 | Sleep 3s 22 | Down 23 | Sleep 3s 24 | Left 25 | Sleep 3s 26 | Sleep 7s 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # If you prefer the allow list template instead of the deny list, see community template: 2 | # https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore 3 | # 4 | # Binaries for programs and plugins 5 | *.exe 6 | *.exe~ 7 | *.dll 8 | *.so 9 | *.dylib 10 | 11 | # Test binary, built with `go test -c` 12 | *.test 13 | 14 | # Output of the go coverage tool, specifically when used with LiteIDE 15 | *.out 16 | 17 | # Dependency directories (remove the comment below to include it) 18 | vendor/ 19 | 20 | # Go workspace file 21 | go.work 22 | 23 | # Logs 24 | *.log 25 | -------------------------------------------------------------------------------- /nag/params/params_test.go: -------------------------------------------------------------------------------- 1 | package params_test 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | 7 | "github.com/dylantientcheu/nbacli/nag/params" 8 | ) 9 | 10 | // probably PBT here 11 | func TestSeason(t *testing.T) { 12 | tests := []struct { 13 | in string 14 | want string 15 | }{ 16 | { 17 | in: "2021-10-01", 18 | want: "2021-22", 19 | }, 20 | { 21 | in: "2021-09-30", 22 | want: "2020-21", 23 | }, 24 | } 25 | 26 | for _, tt := range tests { 27 | t.Run(tt.in, func(t *testing.T) { 28 | tim, _ := time.Parse("2006-01-02", tt.in) 29 | got := params.Season(tim) 30 | if got != tt.want { 31 | t.Errorf("want %s, got %s", tt.want, got) 32 | } 33 | }) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /ui/keymaps.go: -------------------------------------------------------------------------------- 1 | package ui 2 | 3 | import ( 4 | "github.com/charmbracelet/bubbles/key" 5 | ) 6 | 7 | // game help keymap 8 | type GameKM struct { 9 | Down key.Binding 10 | Up key.Binding 11 | Previous key.Binding 12 | } 13 | 14 | func (k GameKM) FullHelp() [][]key.Binding { 15 | return [][]key.Binding{k.ShortHelp()} 16 | } 17 | 18 | func (k GameKM) ShortHelp() []key.Binding { 19 | return []key.Binding{k.Down, k.Up, k.Previous} 20 | } 21 | 22 | // standing help keymap 23 | type StandingKM struct { 24 | Down key.Binding 25 | Up key.Binding 26 | Previous key.Binding 27 | NextTab key.Binding 28 | } 29 | 30 | func (k StandingKM) FullHelp() [][]key.Binding { 31 | return [][]key.Binding{k.ShortHelp()} 32 | } 33 | 34 | func (k StandingKM) ShortHelp() []key.Binding { 35 | return []key.Binding{k.NextTab, k.Down, k.Up, k.Previous} 36 | } 37 | -------------------------------------------------------------------------------- /ui/ui.go: -------------------------------------------------------------------------------- 1 | package ui 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "os" 7 | "time" 8 | 9 | "github.com/dylantientcheu/nbacli/nba" 10 | "github.com/dylantientcheu/nbacli/ui/constants" 11 | 12 | tea "github.com/charmbracelet/bubbletea" 13 | ) 14 | 15 | // StartTea the entry point for the UI. Initializes the model. 16 | func StartTea(date time.Time) { 17 | scbrd := nba.ScoreboardRepository{} 18 | if f, err := tea.LogToFile("debug.log", "help"); err != nil { 19 | fmt.Println("Couldn't open a file for logging:", err) 20 | os.Exit(1) 21 | } else { 22 | defer func() { 23 | err = f.Close() 24 | if err != nil { 25 | log.Fatal(err) 26 | } 27 | }() 28 | } 29 | nba.Sb = &scbrd 30 | 31 | m := InitScoreboard(date) 32 | UpdateTeaView(m) 33 | } 34 | 35 | func StartStanding() { 36 | m := InitStandingsView() 37 | UpdateTeaView(m) 38 | } 39 | 40 | func UpdateTeaView(m tea.Model) { 41 | constants.P = tea.NewProgram(m, tea.WithAltScreen()) 42 | if _, err := constants.P.Run(); err != nil { 43 | fmt.Println("Error running program:", err) 44 | os.Exit(1) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /cmd/root.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2022 Dylan Tientcheu hi@dylantientcheu.dev 3 | 4 | */ 5 | package cmd 6 | 7 | import ( 8 | "os" 9 | 10 | "github.com/spf13/cobra" 11 | ) 12 | 13 | // rootCmd represents the base command when called without any subcommands 14 | var rootCmd = &cobra.Command{ 15 | Use: "nbacli", 16 | Short: "NBA GoLang CLI", 17 | Long: `GoLang NBA CLI client.`, 18 | } 19 | 20 | // Execute adds all child commands to the root command and sets flags appropriately. 21 | // This is called by main.main(). It only needs to happen once to the rootCmd. 22 | func Execute() { 23 | err := rootCmd.Execute() 24 | if err != nil { 25 | os.Exit(1) 26 | } 27 | } 28 | 29 | func init() { 30 | // Here you will define your flags and configuration settings. 31 | // Cobra supports persistent flags, which, if defined here, 32 | // will be global for your application. 33 | 34 | // rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.go-nba.yaml)") 35 | 36 | // Cobra also supports local flags, which will only run 37 | // when this action is called directly. 38 | } 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Dylan Tientcheu 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /nag/boxscoredefensive.go: -------------------------------------------------------------------------------- 1 | package nag 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "net/http" 7 | ) 8 | 9 | // BoxScoreDefensive wraps request to and response from boxscoredefensive endpoint. 10 | type BoxScoreDefensive struct { 11 | *Client 12 | GameID string 13 | 14 | Response *Response 15 | } 16 | 17 | // NewBoxScoreDefensive creates a default BoxScoreDefensive instance. 18 | func NewBoxScoreDefensive(id string) *BoxScoreDefensive { 19 | return &BoxScoreDefensive{ 20 | Client: NewDefaultClient(), 21 | GameID: id, 22 | } 23 | } 24 | 25 | // Get sends a GET request to boxscoredefensive endpoint. 26 | func (c *BoxScoreDefensive) Get() error { 27 | req, err := http.NewRequest("GET", fmt.Sprintf("%s/boxscoredefensive", c.BaseURL.String()), nil) 28 | if err != nil { 29 | return err 30 | } 31 | 32 | req.Header = DefaultStatsHeader 33 | 34 | q := req.URL.Query() 35 | q.Add("GameID", c.GameID) 36 | req.URL.RawQuery = q.Encode() 37 | 38 | b, err := c.Do(req) 39 | if err != nil { 40 | return err 41 | } 42 | 43 | var res Response 44 | if err := json.Unmarshal(b, &res); err != nil { 45 | return err 46 | } 47 | c.Response = &res 48 | return nil 49 | } 50 | -------------------------------------------------------------------------------- /nag/boxscoresummaryv2.go: -------------------------------------------------------------------------------- 1 | package nag 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "net/http" 7 | ) 8 | 9 | // BoxScoreSummaryV2 wraps request to and response from boxscoresummaryv2 endpoint. 10 | type BoxScoreSummaryV2 struct { 11 | *Client 12 | GameID string 13 | 14 | Response *Response 15 | } 16 | 17 | // NewBoxScoreSummaryV2 creates a default BoxScoreSummaryV2 instance. 18 | func NewBoxScoreSummaryV2(id string) *BoxScoreSummaryV2 { 19 | return &BoxScoreSummaryV2{ 20 | Client: NewDefaultClient(), 21 | GameID: id, 22 | } 23 | } 24 | 25 | // Get sends a GET request to boxscoresummaryv2 endpoint. 26 | func (c *BoxScoreSummaryV2) Get() error { 27 | req, err := http.NewRequest("GET", fmt.Sprintf("%s/boxscoresummaryv2", c.BaseURL.String()), nil) 28 | if err != nil { 29 | return err 30 | } 31 | 32 | req.Header = DefaultStatsHeader 33 | 34 | q := req.URL.Query() 35 | q.Add("GameID", c.GameID) 36 | req.URL.RawQuery = q.Encode() 37 | 38 | b, err := c.Do(req) 39 | if err != nil { 40 | return err 41 | } 42 | 43 | var res Response 44 | if err := json.Unmarshal(b, &res); err != nil { 45 | return err 46 | } 47 | c.Response = &res 48 | return nil 49 | } 50 | -------------------------------------------------------------------------------- /nag/boxscoreplayertrackv2.go: -------------------------------------------------------------------------------- 1 | package nag 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "net/http" 7 | ) 8 | 9 | // BoxScorePlayerTrackV2 wraps request to and response from boxscoreplayertrackv2 endpoint. 10 | type BoxScorePlayerTrackV2 struct { 11 | *Client 12 | GameID string 13 | 14 | Response *Response 15 | } 16 | 17 | // NewBoxScorePlayerTrackV2 creates a default BoxScorePlayerTrackV2 instance. 18 | func NewBoxScorePlayerTrackV2(id string) *BoxScorePlayerTrackV2 { 19 | return &BoxScorePlayerTrackV2{ 20 | Client: NewDefaultClient(), 21 | GameID: id, 22 | } 23 | } 24 | 25 | // Get sends a GET request to boxscoreplayertrackv2 endpoint. 26 | func (c *BoxScorePlayerTrackV2) Get() error { 27 | req, err := http.NewRequest("GET", fmt.Sprintf("%s/boxscoreplayertrackv2", c.BaseURL.String()), nil) 28 | if err != nil { 29 | return err 30 | } 31 | 32 | req.Header = DefaultStatsHeader 33 | 34 | q := req.URL.Query() 35 | q.Add("GameID", c.GameID) 36 | req.URL.RawQuery = q.Encode() 37 | 38 | b, err := c.Do(req) 39 | if err != nil { 40 | return err 41 | } 42 | 43 | var res Response 44 | if err := json.Unmarshal(b, &res); err != nil { 45 | return err 46 | } 47 | c.Response = &res 48 | return nil 49 | } 50 | -------------------------------------------------------------------------------- /nag/gamerotation.go: -------------------------------------------------------------------------------- 1 | package nag 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "net/http" 7 | 8 | "github.com/dylantientcheu/nbacli/nag/params" 9 | ) 10 | 11 | // GameRotation wraps request to and response from gamerotation endpoint. 12 | type GameRotation struct { 13 | *Client 14 | GameID string 15 | LeagueID string 16 | 17 | Response *Response 18 | } 19 | 20 | // NewGameRotation creates a default GameRotation instance. 21 | func NewGameRotation(id string) *GameRotation { 22 | return &GameRotation{ 23 | Client: NewDefaultClient(), 24 | GameID: id, 25 | LeagueID: params.LeagueID.Default(), 26 | } 27 | } 28 | 29 | // Get sends a GET request to gamerotation endpoint. 30 | func (c *GameRotation) Get() error { 31 | req, err := http.NewRequest("GET", fmt.Sprintf("%s/gamerotation", c.BaseURL.String()), nil) 32 | if err != nil { 33 | return err 34 | } 35 | 36 | req.Header = DefaultStatsHeader 37 | 38 | q := req.URL.Query() 39 | q.Add("LeagueID", c.LeagueID) 40 | q.Add("GameID", c.GameID) 41 | req.URL.RawQuery = q.Encode() 42 | 43 | b, err := c.Do(req) 44 | if err != nil { 45 | return err 46 | } 47 | 48 | var res Response 49 | if err := json.Unmarshal(b, &res); err != nil { 50 | return err 51 | } 52 | c.Response = &res 53 | return nil 54 | } 55 | -------------------------------------------------------------------------------- /nag/commonplayerinfo.go: -------------------------------------------------------------------------------- 1 | package nag 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "net/http" 7 | 8 | "github.com/dylantientcheu/nbacli/nag/params" 9 | ) 10 | 11 | // CommonPlayerInfo wraps request to and response from commonplayerinfo endpoint. 12 | type CommonPlayerInfo struct { 13 | *Client 14 | PlayerID string 15 | LeagueID string 16 | 17 | Response *Response 18 | } 19 | 20 | // NewCommonPlayerInfo creates a default CommonPlayerInfo instance. 21 | func NewCommonPlayerInfo(id string) *CommonPlayerInfo { 22 | return &CommonPlayerInfo{ 23 | Client: NewDefaultClient(), 24 | PlayerID: id, 25 | LeagueID: params.LeagueID.Default(), 26 | } 27 | } 28 | 29 | // Get sends a GET request to commonplayerinfo endpoint. 30 | func (c *CommonPlayerInfo) Get() error { 31 | req, err := http.NewRequest("GET", fmt.Sprintf("%s/commonplayerinfo", c.BaseURL.String()), nil) 32 | if err != nil { 33 | return err 34 | } 35 | 36 | req.Header = DefaultStatsHeader 37 | 38 | q := req.URL.Query() 39 | q.Add("PlayerID", c.PlayerID) 40 | q.Add("LeagueID", c.LeagueID) 41 | req.URL.RawQuery = q.Encode() 42 | 43 | b, err := c.Do(req) 44 | if err != nil { 45 | return err 46 | } 47 | 48 | var res Response 49 | if err := json.Unmarshal(b, &res); err != nil { 50 | return err 51 | } 52 | c.Response = &res 53 | return nil 54 | } 55 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: goreleaser 2 | 3 | on: 4 | push: 5 | # run only against tags 6 | tags: 7 | - '*' 8 | 9 | permissions: 10 | contents: write 11 | 12 | jobs: 13 | goreleaser: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v3 17 | with: 18 | fetch-depth: 0 19 | - run: git fetch --force --tags 20 | - uses: actions/setup-go@v3 21 | with: 22 | go-version: '>=1.19.3' 23 | cache: true 24 | 25 | - name: install chocolatey 26 | run: | 27 | mkdir -p /opt/chocolatey 28 | wget -q -O - "https://github.com/chocolatey/choco/releases/download/${CHOCOLATEY_VERSION}/chocolatey.v${CHOCOLATEY_VERSION}.tar.gz" | tar -xz -C "/opt/chocolatey" 29 | echo '#!/bin/bash' >> /usr/local/bin/choco 30 | echo 'mono /opt/chocolatey/choco.exe $@' >> /usr/local/bin/choco 31 | chmod +x /usr/local/bin/choco 32 | env: 33 | CHOCOLATEY_VERSION: 1.2.0 34 | 35 | - uses: goreleaser/goreleaser-action@v2 36 | with: 37 | distribution: goreleaser 38 | version: ${{ env.GITHUB_REF_NAME }} 39 | args: release --rm-dist 40 | env: 41 | GITHUB_TOKEN: ${{ secrets.PUBLISHER_TOKEN }} 42 | CHOCOLATEY_API_KEY: ${{ secrets.CHOCOLATEY_API_KEY }} -------------------------------------------------------------------------------- /nag/response.go: -------------------------------------------------------------------------------- 1 | package nag 2 | 3 | import ( 4 | "encoding/json" 5 | ) 6 | 7 | // Response represents response from NBA endpoint. 8 | type Response struct { 9 | Resource string `json:"resource"` 10 | // Parameters map[string]interface{} `json:"parameters"` 11 | ResultSet ResultSet `json:"resultSet"` 12 | ResultSets []ResultSet `json:"resultSets"` 13 | } 14 | 15 | // ResultSet represents relevant data from Response. 16 | type ResultSet struct { 17 | Name string `json:"name"` 18 | Headers []string `json:"headers"` 19 | RowSet [][]interface{} `json:"rowSet"` 20 | } 21 | 22 | // Map maps ResultSet(s) from Response based on name and headers. 23 | func Map(res Response) map[string]interface{} { 24 | var resultSets []ResultSet 25 | if res.ResultSets != nil { 26 | resultSets = res.ResultSets 27 | } else { 28 | resultSets = []ResultSet{res.ResultSet} 29 | } 30 | 31 | m := make(map[string]interface{}) 32 | for _, rs := range resultSets { 33 | var rows []map[string]interface{} 34 | for _, row := range rs.RowSet { 35 | mm := make(map[string]interface{}) 36 | for i, h := range rs.Headers { 37 | mm[h] = row[i] 38 | } 39 | rows = append(rows, mm) 40 | } 41 | m[rs.Name] = rows 42 | } 43 | return m 44 | } 45 | 46 | // JSON returns a JSON representation of Map 47 | func JSON(res Response) (json.RawMessage, error) { 48 | return json.Marshal(Map(res)) 49 | } 50 | -------------------------------------------------------------------------------- /nag/playbyplayv2.go: -------------------------------------------------------------------------------- 1 | package nag 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "net/http" 7 | 8 | "github.com/dylantientcheu/nbacli/nag/params" 9 | ) 10 | 11 | // PlayByPlayV2 wraps request to and response from playbyplayv2 endpoint. 12 | type PlayByPlayV2 struct { 13 | *Client 14 | GameID string 15 | StartPeriod string 16 | EndPeriod string 17 | 18 | Response *Response 19 | } 20 | 21 | // NewPlayByPlayV2 creates a default PlayByPlayV2 instance. 22 | func NewPlayByPlayV2(id string) *PlayByPlayV2 { 23 | return &PlayByPlayV2{ 24 | Client: NewDefaultClient(), 25 | GameID: id, 26 | StartPeriod: params.Period.Default(), 27 | EndPeriod: params.Period.Default(), 28 | } 29 | } 30 | 31 | // Get sends a GET request to playbyplayv2 endpoint. 32 | func (c *PlayByPlayV2) Get() error { 33 | req, err := http.NewRequest("GET", fmt.Sprintf("%s/playbyplayv2", c.BaseURL.String()), nil) 34 | if err != nil { 35 | return err 36 | } 37 | 38 | req.Header = DefaultStatsHeader 39 | 40 | q := req.URL.Query() 41 | q.Add("GameID", c.GameID) 42 | q.Add("StartPeriod", c.StartPeriod) 43 | q.Add("EndPeriod", c.EndPeriod) 44 | req.URL.RawQuery = q.Encode() 45 | 46 | b, err := c.Do(req) 47 | if err != nil { 48 | return err 49 | } 50 | 51 | var res Response 52 | if err := json.Unmarshal(b, &res); err != nil { 53 | return err 54 | } 55 | c.Response = &res 56 | return nil 57 | } 58 | -------------------------------------------------------------------------------- /nag/commonteamroster.go: -------------------------------------------------------------------------------- 1 | package nag 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "net/http" 7 | 8 | "github.com/dylantientcheu/nbacli/nag/params" 9 | ) 10 | 11 | // CommonTeamRoster wraps request to and response from commonteamroster endpoint. 12 | type CommonTeamRoster struct { 13 | *Client 14 | TeamID string 15 | Season string 16 | LeagueID string 17 | 18 | Response *Response 19 | } 20 | 21 | // NewCommonTeamRoster creates a default CommonTeamRoster instance. 22 | func NewCommonTeamRoster(id string) *CommonTeamRoster { 23 | return &CommonTeamRoster{ 24 | Client: NewDefaultClient(), 25 | TeamID: id, 26 | Season: params.CurrentSeason, 27 | LeagueID: params.LeagueID.Default(), 28 | } 29 | } 30 | 31 | // Get sends a GET request to commonteamroster endpoint. 32 | func (c *CommonTeamRoster) Get() error { 33 | req, err := http.NewRequest("GET", fmt.Sprintf("%s/commonteamroster", c.BaseURL.String()), nil) 34 | if err != nil { 35 | return err 36 | } 37 | 38 | req.Header = DefaultStatsHeader 39 | 40 | q := req.URL.Query() 41 | q.Add("TeamID", c.TeamID) 42 | q.Add("Season", c.Season) 43 | q.Add("LeagueID", c.LeagueID) 44 | req.URL.RawQuery = q.Encode() 45 | 46 | b, err := c.Do(req) 47 | if err != nil { 48 | return err 49 | } 50 | 51 | var res Response 52 | if err := json.Unmarshal(b, &res); err != nil { 53 | return err 54 | } 55 | c.Response = &res 56 | return nil 57 | } 58 | -------------------------------------------------------------------------------- /nag/commonplayoffseries.go: -------------------------------------------------------------------------------- 1 | package nag 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "net/http" 7 | 8 | "github.com/dylantientcheu/nbacli/nag/params" 9 | ) 10 | 11 | // CommonPlayoffSeries wraps request to and response from commonplayoffseries endpoint. 12 | type CommonPlayoffSeries struct { 13 | *Client 14 | LeagueID string 15 | Season string 16 | SeriesID string 17 | 18 | Response *Response 19 | } 20 | 21 | // NewCommonPlayoffSeries creates a default CommonPlayoffSeries instance. 22 | func NewCommonPlayoffSeries() *CommonPlayoffSeries { 23 | return &CommonPlayoffSeries{ 24 | Client: NewDefaultClient(), 25 | LeagueID: params.LeagueID.Default(), 26 | Season: params.CurrentSeason, 27 | } 28 | } 29 | 30 | // Get sends a GET request to commonplayoffseries endpoint. 31 | func (c *CommonPlayoffSeries) Get() error { 32 | req, err := http.NewRequest("GET", fmt.Sprintf("%s/commonplayoffseries", c.BaseURL.String()), nil) 33 | if err != nil { 34 | return err 35 | } 36 | 37 | req.Header = DefaultStatsHeader 38 | 39 | q := req.URL.Query() 40 | q.Add("LeagueID", c.LeagueID) 41 | q.Add("Season", c.Season) 42 | q.Add("SeriesID", c.SeriesID) 43 | req.URL.RawQuery = q.Encode() 44 | 45 | b, err := c.Do(req) 46 | if err != nil { 47 | return err 48 | } 49 | 50 | var res Response 51 | if err := json.Unmarshal(b, &res); err != nil { 52 | return err 53 | } 54 | c.Response = &res 55 | return nil 56 | } 57 | -------------------------------------------------------------------------------- /nag/scoreboardv2.go: -------------------------------------------------------------------------------- 1 | package nag 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "net/http" 7 | "strconv" 8 | "time" 9 | 10 | "github.com/dylantientcheu/nbacli/nag/params" 11 | ) 12 | 13 | // ScoreBoardV2 wraps request to and response from scoreboardv2 endpoint. 14 | type ScoreBoardV2 struct { 15 | *Client 16 | DayOffset int 17 | GameDate string 18 | LeagueID string 19 | 20 | Response *Response 21 | } 22 | 23 | // NewScoreBoardV2 creates a default ScoreBoardV2 instance. 24 | func NewScoreBoardV2(date time.Time) *ScoreBoardV2 { 25 | return &ScoreBoardV2{ 26 | Client: NewDefaultClient(), 27 | DayOffset: 0, 28 | GameDate: date.Format("2006-01-02"), 29 | LeagueID: params.LeagueID.Default(), 30 | } 31 | } 32 | 33 | // Get sends a GET request to scoreboardv2 endpoint. 34 | func (c *ScoreBoardV2) Get() error { 35 | req, err := http.NewRequest("GET", fmt.Sprintf("%s/scoreboardv2", c.BaseURL.String()), nil) 36 | if err != nil { 37 | return err 38 | } 39 | 40 | req.Header = DefaultStatsHeader 41 | 42 | q := req.URL.Query() 43 | q.Add("DayOffset", strconv.Itoa(c.DayOffset)) 44 | q.Add("GameDate", c.GameDate) 45 | q.Add("LeagueID", c.LeagueID) 46 | req.URL.RawQuery = q.Encode() 47 | 48 | b, err := c.Do(req) 49 | if err != nil { 50 | return err 51 | } 52 | 53 | var res Response 54 | if err := json.Unmarshal(b, &res); err != nil { 55 | return err 56 | } 57 | 58 | c.Response = &res 59 | 60 | return nil 61 | } 62 | -------------------------------------------------------------------------------- /nag/leaguestandingsv3.go: -------------------------------------------------------------------------------- 1 | package nag 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "net/http" 7 | 8 | "github.com/dylantientcheu/nbacli/nag/params" 9 | ) 10 | 11 | // LeagueStandingsV3 wraps request to and response from leaguestandingsv3 endpoint. 12 | type LeagueStandingsV3 struct { 13 | *Client 14 | LeagueID string 15 | Season string 16 | SeasonType params.SeasonType 17 | 18 | Response *Response 19 | } 20 | 21 | // NewLeagueStandingsV3 creates a default LeagueStandingsV3 instance. 22 | func NewLeagueStandingsV3() *LeagueStandingsV3 { 23 | return &LeagueStandingsV3{ 24 | Client: NewDefaultClient(), 25 | 26 | LeagueID: params.LeagueID.Default(), 27 | 28 | Season: params.CurrentSeason, 29 | SeasonType: params.DefaultSeasonType, 30 | } 31 | } 32 | 33 | // Get sends a GET request to leaguestandingsv3 endpoint. 34 | func (c *LeagueStandingsV3) Get() error { 35 | req, err := http.NewRequest("GET", fmt.Sprintf("%s/leaguestandingsv3", c.BaseURL.String()), nil) 36 | if err != nil { 37 | return err 38 | } 39 | 40 | req.Header = DefaultStatsHeader 41 | 42 | q := req.URL.Query() 43 | q.Add("LeagueID", c.LeagueID) 44 | q.Add("Season", c.Season) 45 | q.Add("SeasonType", string(c.SeasonType)) 46 | 47 | req.URL.RawQuery = q.Encode() 48 | 49 | b, err := c.Do(req) 50 | if err != nil { 51 | return err 52 | } 53 | 54 | var res Response 55 | if err := json.Unmarshal(b, &res); err != nil { 56 | return err 57 | } 58 | 59 | c.Response = &res 60 | return nil 61 | } 62 | -------------------------------------------------------------------------------- /nag/commonallplayers.go: -------------------------------------------------------------------------------- 1 | package nag 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "net/http" 7 | "strconv" 8 | 9 | "github.com/dylantientcheu/nbacli/nag/params" 10 | ) 11 | 12 | // CommonAllPlayers wraps request to and response from commonallplayers endpoint. 13 | type CommonAllPlayers struct { 14 | *Client 15 | IsOnlyCurrentSeason int 16 | LeagueID string 17 | Season string 18 | 19 | Response *Response 20 | } 21 | 22 | // NewCommonAllPlayers creates a default CommonAllPlayers instance. 23 | func NewCommonAllPlayers() *CommonAllPlayers { 24 | return &CommonAllPlayers{ 25 | Client: NewDefaultClient(), 26 | IsOnlyCurrentSeason: 0, 27 | LeagueID: params.LeagueID.Default(), 28 | Season: params.CurrentSeason, 29 | } 30 | } 31 | 32 | // Get sends a GET request to commonallplayers endpoint. 33 | func (c *CommonAllPlayers) Get() error { 34 | req, err := http.NewRequest("GET", fmt.Sprintf("%s/commonallplayers", c.BaseURL.String()), nil) 35 | if err != nil { 36 | return err 37 | } 38 | 39 | req.Header = DefaultStatsHeader 40 | 41 | q := req.URL.Query() 42 | q.Add("IsOnlyCurrentSeason", strconv.Itoa(c.IsOnlyCurrentSeason)) 43 | q.Add("LeagueID", c.LeagueID) 44 | q.Add("Season", c.Season) 45 | req.URL.RawQuery = q.Encode() 46 | 47 | b, err := c.Do(req) 48 | if err != nil { 49 | return err 50 | } 51 | 52 | var res Response 53 | if err := json.Unmarshal(b, &res); err != nil { 54 | return err 55 | } 56 | c.Response = &res 57 | return nil 58 | } 59 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/dylantientcheu/nbacli 2 | 3 | go 1.18 4 | 5 | require ( 6 | github.com/charmbracelet/bubbletea v0.23.1 7 | github.com/nleeper/goment v1.4.4 8 | ) 9 | 10 | require ( 11 | github.com/atotto/clipboard v0.1.4 // indirect 12 | github.com/aymanbagabas/go-osc52 v1.0.3 // indirect 13 | github.com/inconshreveable/mousetrap v1.1.0 // indirect 14 | github.com/sahilm/fuzzy v0.1.0 // indirect 15 | github.com/samber/lo v1.36.0 // indirect 16 | github.com/spf13/pflag v1.0.5 // indirect 17 | github.com/stretchr/testify v1.8.1 // indirect 18 | github.com/tkuchiki/go-timezone v0.2.2 // indirect 19 | golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 // indirect 20 | ) 21 | 22 | require ( 23 | github.com/charmbracelet/bubbles v0.14.0 24 | github.com/charmbracelet/lipgloss v0.6.0 25 | github.com/containerd/console v1.0.3 // indirect 26 | github.com/evertras/bubble-table v0.14.6 27 | github.com/lucasb-eyer/go-colorful v1.2.0 // indirect 28 | github.com/mattn/go-isatty v0.0.16 // indirect 29 | github.com/mattn/go-localereader v0.0.1 // indirect 30 | github.com/mattn/go-runewidth v0.0.14 // indirect 31 | github.com/mitchellh/mapstructure v1.5.0 32 | github.com/muesli/ansi v0.0.0-20221106050444-61f0cd9a192a // indirect 33 | github.com/muesli/cancelreader v0.2.2 // indirect 34 | github.com/muesli/reflow v0.3.0 // indirect 35 | github.com/muesli/termenv v0.13.0 // indirect 36 | github.com/rivo/uniseg v0.4.3 // indirect 37 | github.com/spf13/cobra v1.6.1 38 | golang.org/x/sys v0.2.0 // indirect 39 | golang.org/x/term v0.2.0 40 | golang.org/x/text v0.4.0 // indirect 41 | ) 42 | -------------------------------------------------------------------------------- /nag/teamgamelog.go: -------------------------------------------------------------------------------- 1 | package nag 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "net/http" 7 | 8 | "github.com/dylantientcheu/nbacli/nag/params" 9 | ) 10 | 11 | // TeamGameLog wraps request to and response from teamgamelog endpoint. 12 | type TeamGameLog struct { 13 | *Client 14 | LeagueID string 15 | TeamID string 16 | Season string 17 | SeasonType params.SeasonType 18 | DateFrom string 19 | DateTo string 20 | 21 | Response *Response 22 | } 23 | 24 | // NewTeamGameLog creates a default TeamGameLog instance. 25 | func NewTeamGameLog(id string) *TeamGameLog { 26 | return &TeamGameLog{ 27 | Client: NewDefaultClient(), 28 | LeagueID: params.LeagueID.Default(), 29 | TeamID: id, 30 | Season: params.CurrentSeason, 31 | SeasonType: params.AllStar, 32 | } 33 | } 34 | 35 | // Get sends a GET request to teamgamelog endpoint. 36 | func (c *TeamGameLog) Get() error { 37 | req, err := http.NewRequest("GET", fmt.Sprintf("%s/teamgamelog", c.BaseURL.String()), nil) 38 | if err != nil { 39 | return err 40 | } 41 | 42 | req.Header = DefaultStatsHeader 43 | 44 | q := req.URL.Query() 45 | q.Add("LeagueID", c.LeagueID) 46 | q.Add("TeamID", c.TeamID) 47 | q.Add("Season", c.Season) 48 | q.Add("SeasonType", string(c.SeasonType)) 49 | q.Add("DateFrom", c.DateFrom) 50 | q.Add("DateTo", c.DateTo) 51 | req.URL.RawQuery = q.Encode() 52 | 53 | b, err := c.Do(req) 54 | if err != nil { 55 | return err 56 | } 57 | 58 | var res Response 59 | if err := json.Unmarshal(b, &res); err != nil { 60 | return err 61 | } 62 | c.Response = &res 63 | return nil 64 | } 65 | -------------------------------------------------------------------------------- /nag/alltimeleadersgrids.go: -------------------------------------------------------------------------------- 1 | package nag 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "net/http" 7 | "strconv" 8 | 9 | "github.com/dylantientcheu/nbacli/nag/params" 10 | ) 11 | 12 | // AllTimeLeadersGrids wraps request to and response from alltimeleadersgrids endpoint. 13 | type AllTimeLeadersGrids struct { 14 | *Client 15 | LeagueID string 16 | PerMode params.PerMode 17 | SeasonType params.SeasonType 18 | TopX int 19 | 20 | Response *Response 21 | } 22 | 23 | // NewAllTimeLeadersGrids creates a default AllTimeLeadersGrids instance. 24 | func NewAllTimeLeadersGrids() *AllTimeLeadersGrids { 25 | return &AllTimeLeadersGrids{ 26 | Client: NewDefaultClient(), 27 | LeagueID: params.LeagueID.Default(), 28 | PerMode: params.DefaultPerMode, 29 | SeasonType: params.DefaultSeasonType, 30 | TopX: 10, 31 | } 32 | } 33 | 34 | // Get sends a GET request to alltimeleadersgrids endpoint. 35 | func (c *AllTimeLeadersGrids) Get() error { 36 | req, err := http.NewRequest("GET", fmt.Sprintf("%s/alltimeleadersgrids", c.BaseURL.String()), nil) 37 | if err != nil { 38 | return err 39 | } 40 | 41 | req.Header = DefaultStatsHeader 42 | 43 | q := req.URL.Query() 44 | q.Add("LeagueID", c.LeagueID) 45 | q.Add("PerMode", string(c.PerMode)) 46 | q.Add("SeasonType", string(c.SeasonType)) 47 | q.Add("TopX", strconv.Itoa(c.TopX)) 48 | req.URL.RawQuery = q.Encode() 49 | 50 | b, err := c.Do(req) 51 | if err != nil { 52 | return err 53 | } 54 | 55 | var res Response 56 | if err := json.Unmarshal(b, &res); err != nil { 57 | return err 58 | } 59 | c.Response = &res 60 | return nil 61 | } 62 | -------------------------------------------------------------------------------- /cmd/game.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2022 NAME HERE 3 | */ 4 | package cmd 5 | 6 | import ( 7 | "time" 8 | 9 | "github.com/dylantientcheu/nbacli/ui" 10 | 11 | "github.com/spf13/cobra" 12 | ) 13 | 14 | // args 15 | var date = "" 16 | 17 | // var gameID = "" 18 | 19 | var hasYesterday = false 20 | var hasTomorrow = false 21 | 22 | // gameCmd represents the game command 23 | var gameCmd = &cobra.Command{ 24 | Use: "games", 25 | Short: "Get the NBA schedule for a specific date", 26 | Run: func(cmd *cobra.Command, args []string) { 27 | 28 | // no date then get today's games 29 | dateArg := time.Now() 30 | 31 | if hasYesterday { 32 | dateArg = time.Now().AddDate(0, 0, -1) 33 | } 34 | if hasTomorrow { 35 | dateArg = time.Now().AddDate(0, 0, 1) 36 | } 37 | if date != "" { 38 | dateArg, _ = time.Parse("20060102", date) 39 | } 40 | 41 | // start the tui 42 | ui.StartTea(dateArg) 43 | 44 | }, 45 | } 46 | 47 | var StandingCmd = &cobra.Command{ 48 | Use: "standings", 49 | Short: "Get the NBA standings for the current season", 50 | Run: func(cmd *cobra.Command, args []string) { 51 | // start the tui 52 | ui.StartStanding() 53 | }, 54 | } 55 | 56 | func init() { 57 | rootCmd.AddCommand(gameCmd) 58 | rootCmd.PersistentFlags().StringVarP(&date, "date", "d", "", "Date to get the schedule for (YYYYMMDD)") 59 | rootCmd.PersistentFlags().BoolVarP(&hasYesterday, "yesterday", "y", false, "Get yesterday's games") 60 | rootCmd.PersistentFlags().BoolVarP(&hasTomorrow, "tomorrow", "t", false, "Get tomorrow's games") 61 | rootCmd.MarkFlagsMutuallyExclusive("yesterday", "tomorrow", "date") 62 | 63 | rootCmd.AddCommand(StandingCmd) 64 | } 65 | -------------------------------------------------------------------------------- /nag/assistleaders.go: -------------------------------------------------------------------------------- 1 | package nag 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "net/http" 7 | 8 | "github.com/dylantientcheu/nbacli/nag/params" 9 | ) 10 | 11 | // AssistLeaders wraps request to and response from assistleaders endpoint. 12 | type AssistLeaders struct { 13 | *Client 14 | LeagueID string 15 | PerMode params.PerMode 16 | PlayerOrTeam params.PlayerOrTeam 17 | Season string 18 | SeasonType params.SeasonType 19 | 20 | Response *Response 21 | } 22 | 23 | // NewAssistLeaders creates a default AssistLeaders instance. 24 | func NewAssistLeaders() *AssistLeaders { 25 | return &AssistLeaders{ 26 | Client: NewDefaultClient(), 27 | LeagueID: params.LeagueID.Default(), 28 | PerMode: params.DefaultPerMode, 29 | PlayerOrTeam: params.DefaultPlayerOrTeam, 30 | Season: params.CurrentSeason, 31 | SeasonType: params.DefaultSeasonType, 32 | } 33 | } 34 | 35 | // Get sends a GET request to assistleaders endpoint. 36 | func (c *AssistLeaders) Get() error { 37 | req, err := http.NewRequest("GET", fmt.Sprintf("%s/assistleaders", c.BaseURL.String()), nil) 38 | if err != nil { 39 | return err 40 | } 41 | 42 | req.Header = DefaultStatsHeader 43 | 44 | q := req.URL.Query() 45 | q.Add("LeagueID", c.LeagueID) 46 | q.Add("PerMode", string(c.PerMode)) 47 | q.Add("PlayerOrTeam", string(c.PlayerOrTeam)) 48 | q.Add("Season", c.Season) 49 | q.Add("SeasonType", string(c.SeasonType)) 50 | req.URL.RawQuery = q.Encode() 51 | 52 | b, err := c.Do(req) 53 | if err != nil { 54 | return err 55 | } 56 | 57 | var res Response 58 | if err := json.Unmarshal(b, &res); err != nil { 59 | return err 60 | } 61 | c.Response = &res 62 | return nil 63 | } 64 | -------------------------------------------------------------------------------- /nag/boxscoremiscv2.go: -------------------------------------------------------------------------------- 1 | package nag 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "net/http" 7 | 8 | "github.com/dylantientcheu/nbacli/nag/params" 9 | ) 10 | 11 | // BoxScoreMiscV2 wraps request to and response from boxscoremiscv2 endpoint. 12 | type BoxScoreMiscV2 struct { 13 | *Client 14 | GameID string 15 | StartRange string 16 | EndRange string 17 | RangeType string 18 | StartPeriod string 19 | EndPeriod string 20 | 21 | Response *Response 22 | } 23 | 24 | // NewBoxScoreMiscV2 creates a default BoxScoreMiscV2 instance. 25 | func NewBoxScoreMiscV2(id string) *BoxScoreMiscV2 { 26 | return &BoxScoreMiscV2{ 27 | Client: NewDefaultClient(), 28 | GameID: id, 29 | StartRange: params.DefaultStartRange, 30 | EndRange: params.DefaultEndRange, 31 | RangeType: params.DefaultRangeType, 32 | StartPeriod: params.Period.Default(), 33 | EndPeriod: params.Period.Default(), 34 | } 35 | } 36 | 37 | // Get sends a GET request to boxscoremiscv2 endpoint. 38 | func (c *BoxScoreMiscV2) Get() error { 39 | req, err := http.NewRequest("GET", fmt.Sprintf("%s/boxscoremiscv2", c.BaseURL.String()), nil) 40 | if err != nil { 41 | return err 42 | } 43 | 44 | req.Header = DefaultStatsHeader 45 | 46 | q := req.URL.Query() 47 | q.Add("GameID", c.GameID) 48 | q.Add("StartRange", c.StartRange) 49 | q.Add("EndRange", c.EndRange) 50 | q.Add("RangeType", c.RangeType) 51 | q.Add("StartPeriod", c.StartPeriod) 52 | q.Add("EndPeriod", c.EndPeriod) 53 | req.URL.RawQuery = q.Encode() 54 | 55 | b, err := c.Do(req) 56 | if err != nil { 57 | return err 58 | } 59 | 60 | var res Response 61 | if err := json.Unmarshal(b, &res); err != nil { 62 | return err 63 | } 64 | c.Response = &res 65 | return nil 66 | } 67 | -------------------------------------------------------------------------------- /nag/boxscoreusagev2.go: -------------------------------------------------------------------------------- 1 | package nag 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "net/http" 7 | 8 | "github.com/dylantientcheu/nbacli/nag/params" 9 | ) 10 | 11 | // BoxScoreUsageV2 wraps request to and response from boxscoreusagev2 endpoint. 12 | type BoxScoreUsageV2 struct { 13 | *Client 14 | GameID string 15 | StartRange string 16 | EndRange string 17 | RangeType string 18 | StartPeriod string 19 | EndPeriod string 20 | 21 | Response *Response 22 | } 23 | 24 | // NewBoxScoreUsageV2 creates a default BoxScoreUsageV2 instance. 25 | func NewBoxScoreUsageV2(id string) *BoxScoreUsageV2 { 26 | return &BoxScoreUsageV2{ 27 | Client: NewDefaultClient(), 28 | GameID: id, 29 | StartRange: params.DefaultStartRange, 30 | EndRange: params.DefaultEndRange, 31 | RangeType: params.DefaultRangeType, 32 | StartPeriod: params.Period.Default(), 33 | EndPeriod: params.Period.Default(), 34 | } 35 | } 36 | 37 | // Get sends a GET request to boxscoreusagev2 endpoint. 38 | func (c *BoxScoreUsageV2) Get() error { 39 | req, err := http.NewRequest("GET", fmt.Sprintf("%s/boxscoreusagev2", c.BaseURL.String()), nil) 40 | if err != nil { 41 | return err 42 | } 43 | 44 | req.Header = DefaultStatsHeader 45 | 46 | q := req.URL.Query() 47 | q.Add("GameID", c.GameID) 48 | q.Add("StartRange", c.StartRange) 49 | q.Add("EndRange", c.EndRange) 50 | q.Add("RangeType", c.RangeType) 51 | q.Add("StartPeriod", c.StartPeriod) 52 | q.Add("EndPeriod", c.EndPeriod) 53 | req.URL.RawQuery = q.Encode() 54 | 55 | b, err := c.Do(req) 56 | if err != nil { 57 | return err 58 | } 59 | 60 | var res Response 61 | if err := json.Unmarshal(b, &res); err != nil { 62 | return err 63 | } 64 | c.Response = &res 65 | return nil 66 | } 67 | -------------------------------------------------------------------------------- /nag/boxscorescoringv2.go: -------------------------------------------------------------------------------- 1 | package nag 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "net/http" 7 | 8 | "github.com/dylantientcheu/nbacli/nag/params" 9 | ) 10 | 11 | // BoxScoreScoringV2 wraps request to and response from boxscorescoringv2 endpoint. 12 | type BoxScoreScoringV2 struct { 13 | *Client 14 | GameID string 15 | StartRange string 16 | EndRange string 17 | RangeType string 18 | StartPeriod string 19 | EndPeriod string 20 | 21 | Response *Response 22 | } 23 | 24 | // NewBoxScoreScoringV2 creates a default BoxScoreScoringV2 instance. 25 | func NewBoxScoreScoringV2(id string) *BoxScoreScoringV2 { 26 | return &BoxScoreScoringV2{ 27 | Client: NewDefaultClient(), 28 | GameID: id, 29 | StartRange: params.DefaultStartRange, 30 | EndRange: params.DefaultEndRange, 31 | RangeType: params.DefaultRangeType, 32 | StartPeriod: params.Period.Default(), 33 | EndPeriod: params.Period.Default(), 34 | } 35 | } 36 | 37 | // Get sends a GET request to boxscorescoringv2 endpoint. 38 | func (c *BoxScoreScoringV2) Get() error { 39 | req, err := http.NewRequest("GET", fmt.Sprintf("%s/boxscorescoringv2", c.BaseURL.String()), nil) 40 | if err != nil { 41 | return err 42 | } 43 | 44 | req.Header = DefaultStatsHeader 45 | 46 | q := req.URL.Query() 47 | q.Add("GameID", c.GameID) 48 | q.Add("StartRange", c.StartRange) 49 | q.Add("EndRange", c.EndRange) 50 | q.Add("RangeType", c.RangeType) 51 | q.Add("StartPeriod", c.StartPeriod) 52 | q.Add("EndPeriod", c.EndPeriod) 53 | req.URL.RawQuery = q.Encode() 54 | 55 | b, err := c.Do(req) 56 | if err != nil { 57 | return err 58 | } 59 | 60 | var res Response 61 | if err := json.Unmarshal(b, &res); err != nil { 62 | return err 63 | } 64 | c.Response = &res 65 | return nil 66 | } 67 | -------------------------------------------------------------------------------- /nag/boxscoreadvancedv2.go: -------------------------------------------------------------------------------- 1 | package nag 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "net/http" 7 | 8 | "github.com/dylantientcheu/nbacli/nag/params" 9 | ) 10 | 11 | // BoxScoreAdvancedV2 wraps request to and response from boxscoreadvancedv2 endpoint. 12 | type BoxScoreAdvancedV2 struct { 13 | *Client 14 | GameID string 15 | StartRange string 16 | EndRange string 17 | RangeType string 18 | StartPeriod string 19 | EndPeriod string 20 | 21 | Response *Response 22 | } 23 | 24 | // NewBoxScoreAdvancedV2 creates a default BoxScoreAdvancedV2 instance. 25 | func NewBoxScoreAdvancedV2(id string) *BoxScoreAdvancedV2 { 26 | return &BoxScoreAdvancedV2{ 27 | Client: NewDefaultClient(), 28 | GameID: id, 29 | StartRange: params.DefaultStartRange, 30 | EndRange: params.DefaultEndRange, 31 | RangeType: params.DefaultRangeType, 32 | StartPeriod: params.Period.Default(), 33 | EndPeriod: params.Period.Default(), 34 | } 35 | } 36 | 37 | // Get sends a GET request to boxscoreadvancedv2 endpoint. 38 | func (c *BoxScoreAdvancedV2) Get() error { 39 | req, err := http.NewRequest("GET", fmt.Sprintf("%s/boxscoreadvancedv2", c.BaseURL.String()), nil) 40 | if err != nil { 41 | return err 42 | } 43 | 44 | req.Header = DefaultStatsHeader 45 | 46 | q := req.URL.Query() 47 | q.Add("GameID", c.GameID) 48 | q.Add("StartRange", c.StartRange) 49 | q.Add("EndRange", c.EndRange) 50 | q.Add("RangeType", c.RangeType) 51 | q.Add("StartPeriod", c.StartPeriod) 52 | q.Add("EndPeriod", c.EndPeriod) 53 | req.URL.RawQuery = q.Encode() 54 | 55 | b, err := c.Do(req) 56 | if err != nil { 57 | return err 58 | } 59 | 60 | var res Response 61 | if err := json.Unmarshal(b, &res); err != nil { 62 | return err 63 | } 64 | c.Response = &res 65 | return nil 66 | } 67 | -------------------------------------------------------------------------------- /nag/boxscorefourfactorsv2.go: -------------------------------------------------------------------------------- 1 | package nag 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "net/http" 7 | 8 | "github.com/dylantientcheu/nbacli/nag/params" 9 | ) 10 | 11 | // BoxScoreFourFactorsV2 wraps request to and response from boxscorefourfactorsv2 endpoint. 12 | type BoxScoreFourFactorsV2 struct { 13 | *Client 14 | GameID string 15 | StartRange string 16 | EndRange string 17 | RangeType string 18 | StartPeriod string 19 | EndPeriod string 20 | 21 | Response *Response 22 | } 23 | 24 | // NewBoxScoreFourFactorsV2 creates a default BoxScoreFourFactorsV2 instance. 25 | func NewBoxScoreFourFactorsV2(id string) *BoxScoreFourFactorsV2 { 26 | return &BoxScoreFourFactorsV2{ 27 | Client: NewDefaultClient(), 28 | GameID: id, 29 | StartRange: params.DefaultStartRange, 30 | EndRange: params.DefaultEndRange, 31 | RangeType: params.DefaultRangeType, 32 | StartPeriod: params.Period.Default(), 33 | EndPeriod: params.Period.Default(), 34 | } 35 | } 36 | 37 | // Get sends a GET request to boxscorefourfactorsv2 endpoint. 38 | func (c *BoxScoreFourFactorsV2) Get() error { 39 | req, err := http.NewRequest("GET", fmt.Sprintf("%s/boxscorefourfactorsv2", c.BaseURL.String()), nil) 40 | if err != nil { 41 | return err 42 | } 43 | 44 | req.Header = DefaultStatsHeader 45 | 46 | q := req.URL.Query() 47 | q.Add("GameID", c.GameID) 48 | q.Add("StartRange", c.StartRange) 49 | q.Add("EndRange", c.EndRange) 50 | q.Add("RangeType", c.RangeType) 51 | q.Add("StartPeriod", c.StartPeriod) 52 | q.Add("EndPeriod", c.EndPeriod) 53 | req.URL.RawQuery = q.Encode() 54 | 55 | b, err := c.Do(req) 56 | if err != nil { 57 | return err 58 | } 59 | 60 | var res Response 61 | if err := json.Unmarshal(b, &res); err != nil { 62 | return err 63 | } 64 | c.Response = &res 65 | return nil 66 | } 67 | -------------------------------------------------------------------------------- /nag/boxscoretraditionalv2.go: -------------------------------------------------------------------------------- 1 | package nag 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "net/http" 7 | 8 | "github.com/dylantientcheu/nbacli/nag/params" 9 | ) 10 | 11 | // BoxScoreTraditionalV2 wraps request to and response from boxscoretraditionalv2 endpoint. 12 | type BoxScoreTraditionalV2 struct { 13 | *Client 14 | GameID string 15 | StartRange string 16 | EndRange string 17 | RangeType string 18 | StartPeriod string 19 | EndPeriod string 20 | 21 | Response *Response 22 | } 23 | 24 | // NewBoxScoreTraditionalV2 creates a default BoxScoreTraditionalV2 instance. 25 | func NewBoxScoreTraditionalV2(id string) *BoxScoreTraditionalV2 { 26 | return &BoxScoreTraditionalV2{ 27 | Client: NewDefaultClient(), 28 | GameID: id, 29 | StartRange: params.DefaultStartRange, 30 | EndRange: params.DefaultEndRange, 31 | RangeType: params.DefaultRangeType, 32 | StartPeriod: params.Period.Default(), 33 | EndPeriod: params.Period.Default(), 34 | } 35 | } 36 | 37 | // Get sends a GET request to boxscoretraditionalv2 endpoint. 38 | func (c *BoxScoreTraditionalV2) Get() error { 39 | req, err := http.NewRequest("GET", fmt.Sprintf("%s/boxscoretraditionalv2", c.BaseURL.String()), nil) 40 | if err != nil { 41 | return err 42 | } 43 | 44 | req.Header = DefaultStatsHeader 45 | 46 | q := req.URL.Query() 47 | q.Add("GameID", c.GameID) 48 | q.Add("StartRange", c.StartRange) 49 | q.Add("EndRange", c.EndRange) 50 | q.Add("RangeType", c.RangeType) 51 | q.Add("StartPeriod", c.StartPeriod) 52 | q.Add("EndPeriod", c.EndPeriod) 53 | req.URL.RawQuery = q.Encode() 54 | 55 | b, err := c.Do(req) 56 | if err != nil { 57 | return err 58 | } 59 | 60 | var res Response 61 | if err := json.Unmarshal(b, &res); err != nil { 62 | return err 63 | } 64 | c.Response = &res 65 | return nil 66 | } 67 | -------------------------------------------------------------------------------- /screenshots/nba-cli.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /nag/client.go: -------------------------------------------------------------------------------- 1 | package nag 2 | 3 | import ( 4 | "bytes" 5 | "compress/gzip" 6 | "fmt" 7 | "io" 8 | "net/http" 9 | "net/url" 10 | ) 11 | 12 | var ( 13 | // DefaultBaseURL sets default base URL for request to NBA stats. 14 | DefaultBaseURL = &url.URL{ 15 | Host: "stats.nba.com", 16 | Scheme: "https", 17 | Path: "/stats", 18 | } 19 | // DefaultStatsHeader sets default headers for request to NBA stats. 20 | // no idea which is necessary and which is not 21 | DefaultStatsHeader = http.Header{ 22 | "Host": []string{"stats.nba.com"}, 23 | "Referer": []string{"https://stats.nba.com"}, 24 | "User-Agent": []string{"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:72.0) Gecko/20100101 Firefox/72.0"}, 25 | "Connection": []string{"keep-alive"}, 26 | "Pragma": []string{"no-cache"}, 27 | "Cache-Control": []string{"no-cache"}, 28 | "Accept": []string{"application/json", "text/plain", "*/*"}, 29 | "Accept-Encoding": []string{"gzip", "deflate", "br"}, 30 | "Accept-Language": []string{"en-US,en;q=0.9"}, 31 | "x-nba-stats-origin": []string{"stats"}, 32 | "x-nba-stats-token": []string{"true"}, 33 | } 34 | ) 35 | 36 | // Client contains the base URL to send request to and the HTTP client being used. 37 | type Client struct { 38 | BaseURL *url.URL 39 | HTTPClient *http.Client 40 | } 41 | 42 | // NewDefaultClient uses stdlib default HTTP client to make request to 43 | // default NBA stats endpoint. 44 | func NewDefaultClient() *Client { 45 | return &Client{ 46 | BaseURL: DefaultBaseURL, 47 | HTTPClient: http.DefaultClient, 48 | } 49 | } 50 | 51 | // Do sends request to NBA stats endpoint and unpacks the received response. 52 | func (c *Client) Do(req *http.Request) ([]byte, error) { 53 | res, err := c.HTTPClient.Do(req) 54 | if err != nil { 55 | return nil, err 56 | } 57 | if res.StatusCode != http.StatusOK { 58 | return nil, fmt.Errorf("%s status code: %d", req.URL.String(), res.StatusCode) 59 | } 60 | defer res.Body.Close() 61 | 62 | b, err := io.ReadAll(res.Body) 63 | if err != nil { 64 | return nil, err 65 | } 66 | 67 | gr, err := gzip.NewReader(bytes.NewReader(b)) 68 | if err != nil { 69 | return nil, nil 70 | } 71 | defer gr.Close() 72 | 73 | b, err = io.ReadAll(gr) 74 | if err != nil { 75 | return nil, err 76 | } 77 | return b, nil 78 | } 79 | -------------------------------------------------------------------------------- /nag/teamgamelogs.go: -------------------------------------------------------------------------------- 1 | package nag 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "net/http" 7 | 8 | "github.com/dylantientcheu/nbacli/nag/params" 9 | ) 10 | 11 | // TeamGameLogs wraps request to and response from teamgamelogs endpoint. 12 | type TeamGameLogs struct { 13 | *Client 14 | LeagueID string 15 | TeamID string 16 | OppTeamID string 17 | Season string 18 | SeasonSegment params.SeasonSegment 19 | SeasonType params.SeasonType 20 | DateFrom string 21 | DateTo string 22 | GameSegment params.GameSegment 23 | LastNGames string 24 | Location string 25 | MeasureType string 26 | Month string 27 | Outcome params.Outcome 28 | PORound string 29 | PerMode params.PerMode 30 | Period string 31 | PlayerID string 32 | ShotClockRange params.ShotClockRange 33 | VsConference params.Conference 34 | VsDivision params.Division 35 | 36 | Response *Response 37 | } 38 | 39 | // NewTeamGameLogs creates a default TeamGameLogs instance. 40 | func NewTeamGameLogs() *TeamGameLogs { 41 | return &TeamGameLogs{ 42 | Client: NewDefaultClient(), 43 | LeagueID: params.LeagueID.Default(), 44 | } 45 | } 46 | 47 | // Get sends a GET request to teamgamelogs endpoint. 48 | func (c *TeamGameLogs) Get() error { 49 | req, err := http.NewRequest("GET", fmt.Sprintf("%s/teamgamelogs", c.BaseURL.String()), nil) 50 | if err != nil { 51 | return err 52 | } 53 | 54 | req.Header = DefaultStatsHeader 55 | 56 | q := req.URL.Query() 57 | q.Add("LeagueID", c.LeagueID) 58 | q.Add("TeamID", c.TeamID) 59 | q.Add("OppTeamID", c.OppTeamID) 60 | q.Add("Season", c.Season) 61 | q.Add("SeasonSegment", string(c.SeasonSegment)) 62 | q.Add("SeasonType", string(c.SeasonType)) 63 | q.Add("DateFrom", c.DateFrom) 64 | q.Add("DateTo", c.DateTo) 65 | q.Add("GameSegment", string(c.GameSegment)) 66 | q.Add("LastNGames", c.LastNGames) 67 | q.Add("Location", c.Location) 68 | q.Add("MeasureType", c.MeasureType) 69 | q.Add("Month", c.Month) 70 | q.Add("Outcome", string(c.Outcome)) 71 | q.Add("PORound", c.PORound) 72 | q.Add("PerMode", string(c.PerMode)) 73 | q.Add("Period", c.Period) 74 | q.Add("PlayerID", c.PlayerID) 75 | q.Add("ShotClockRange", string(c.ShotClockRange)) 76 | q.Add("VsConference", string(c.VsConference)) 77 | q.Add("VsDivision", string(c.VsDivision)) 78 | req.URL.RawQuery = q.Encode() 79 | 80 | b, err := c.Do(req) 81 | if err != nil { 82 | return err 83 | } 84 | 85 | var res Response 86 | if err := json.Unmarshal(b, &res); err != nil { 87 | return err 88 | } 89 | c.Response = &res 90 | return nil 91 | } 92 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | `nbacli` is an UNOFFICIAL command line interface for NBA games. 5 | 6 |

7 | 8 | 9 |

10 | 11 | 12 |
13 | 14 | ## ⚡️ Features 15 | 16 | 🏀 Get the latest scores for NBA games 17 | 18 | 📊 Get the stats for a specific games 19 | 20 | ⛹️ Much more is yet to come - stay tuned! 21 | 22 | ## ⚙️ Installation 23 | 24 | ### Binaries 25 | 26 | Binaries are available for Linux, macOS, and Windows. You can download the latest version from the [releases page](https://github.com/dylantientcheu/nbacli/releases). 27 | 28 | ### From source 29 | 30 | ```bash 31 | git clone https://github.com/dylantientcheu/nbacli.git && cd nbacli 32 | 33 | # insall 34 | go install 35 | 36 | # run 37 | nbacli games 38 | ``` 39 | 40 | > **Note** 41 | > When building from source, make sure that `$GOPATH/bin` is in your `PATH` environment variable 42 | 43 | ### Homebrew 44 | 45 | ```bash 46 | brew tap dylantientcheu/dylantientcheu && brew install nbacli 47 | 48 | # run 49 | nbacli games 50 | ``` 51 | 52 | ## 📖 Usage 53 | 54 | ### Get the latest scores 55 | 56 | ```bash 57 | nbacli games 58 | ``` 59 | 60 | Navigate through the games using the arrow keys and press Enter to get the stats for a specific game. 61 | 62 | ```bash 63 | # Get the stats for the day before 64 | nbacli games --yesterday 65 | # or the day after 66 | nbacli games --tomorrow 67 | # or a specific date 68 | nbacli games --date YYYYMMDD 69 | ``` 70 | 71 | ### Get the conference standings 72 | 73 | ```bash 74 | nbacli standings 75 | ``` 76 | 77 | Highlight rows and press Tab to switch tabs. 78 | 79 | ![Alt text](screenshots/standings.png) 80 | 81 | ## ⌨️ Under the hood 82 | 83 | * Data is fetched from [stats.nba.com](https://stats.nba.com) using a [custom version](./nag/) of [nag](https://github.com/ronaudinho/nag) 84 | * The CLI is built using [Cobra](https://github.com/spf13/cobra) 85 | * TUI is sweetened with [Bubble Tea](https://github.com/charmbracelet/bubbletea) & [Lipgloss](https://github.com/charmbracelet/lipgloss) 86 | * Logo is made from [Gopher Pack](https://www.sketchappsources.com/free-source/4864-gophers-pack-sketch-freebie-resource.html) with Figma. 87 | * Screenshots are beautifully shot with [VHS](https://github.com/charmbracelet/vhs) 📸 88 | * [Warp](https://www.warp.dev/) with dark theme is used on the Terminal 89 | 90 | ## 📝 License 91 | 92 | Refer to [LICENSE.md](./LICENSE). 93 | -------------------------------------------------------------------------------- /.goreleaser.yml: -------------------------------------------------------------------------------- 1 | before: 2 | hooks: 3 | - go mod tidy 4 | 5 | builds: 6 | - <<: &build_defaults 7 | binary: nbacli 8 | id: macos 9 | goos: [darwin] 10 | goarch: [amd64] 11 | 12 | - <<: *build_defaults 13 | id: linux 14 | goos: [linux] 15 | goarch: [386, arm, amd64, arm64] 16 | env: 17 | - CGO_ENABLED=0 18 | 19 | - <<: *build_defaults 20 | id: windows 21 | goos: [windows] 22 | goarch: [386, amd64, arm64] 23 | 24 | archives: 25 | - id: nix 26 | builds: [macos, linux] 27 | <<: &archive_defaults 28 | name_template: "{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}" 29 | wrap_in_directory: true 30 | replacements: 31 | darwin: macOS 32 | format: tar.gz 33 | files: 34 | - LICENSE 35 | 36 | - id: windows 37 | builds: [windows] 38 | <<: *archive_defaults 39 | wrap_in_directory: false 40 | format: zip 41 | files: 42 | - LICENSE 43 | - README.md 44 | 45 | release: 46 | prerelease: auto 47 | 48 | universal_binaries: 49 | - replace: true 50 | 51 | # chocolatey distro 52 | chocolateys: 53 | - 54 | name: nbacli 55 | title: NBA CLI 56 | authors: Dylan TIENTCHEU 57 | ids: 58 | - windows 59 | project_url: https://github.com/dylantientcheu/nbacli 60 | url_template: "https://github.com/dylantientcheu/nbacli/releases/download/{{ .Tag }}/{{ .ArtifactName }}" 61 | icon_url: 'https://raw.githubusercontent.com/dylantientcheu/nbacli/master/screenshots/logo.png' 62 | copyright: 2023 Dylan Tientcheu 63 | license_url: https://github.com/dylantientcheu/nbacli/blob/master/LICENSE 64 | require_license_acceptance: false 65 | project_source_url: https://github.com/dylantientcheu/nbacli 66 | docs_url: https://github.com/dylantientcheu/nbacli/blob/master/README.md 67 | bug_tracker_url: https://github.com/dylantientcheu/nbacli/issues 68 | tags: "nbacli nba terminal standings stats scores" 69 | summary: The NBA in your terminal. 70 | description: | 71 | {{ .ProjectName }} installer package. 72 | The NBA in your terminal 73 | release_notes: "https://github.com/dylantientcheu/nbacli/releases/tag/v{{ .Version }}" 74 | api_key: '{{ .Env.CHOCOLATEY_API_KEY }}' 75 | source_repo: "https://push.chocolatey.org/" 76 | skip_publish: false 77 | goamd64: v1 78 | 79 | 80 | brews: 81 | - 82 | name: nbacli 83 | homepage: https://github.com/dylantientcheu/nbacli 84 | tap: 85 | owner: dylantientcheu 86 | name: homebrew-dylantientcheu 87 | commit_author: 88 | name: Dylan Tientcheu 89 | email: dylantientcheu@gmail.com 90 | 91 | checksum: 92 | name_template: 'checksums.txt' -------------------------------------------------------------------------------- /nba/game.go: -------------------------------------------------------------------------------- 1 | package nba 2 | 3 | import ( 4 | "github.com/mitchellh/mapstructure" 5 | 6 | "github.com/dylantientcheu/nbacli/nag" 7 | ) 8 | 9 | type GameStat struct { 10 | GameID string 11 | TeamID int64 12 | TeamAbbreviation string 13 | TeamCity string 14 | PlayerID int64 15 | PlayerName string 16 | Nickname string 17 | StartPosition string 18 | Comment string 19 | Min string 20 | Fgm int64 21 | Fga int64 22 | FgPct float64 23 | Fg3M int64 24 | Fg3A int64 25 | Fg3Pct float64 26 | Ftm int64 27 | Fta int64 28 | FtPct float64 29 | Oreb int64 30 | Dreb int64 31 | Reb int64 32 | AST int64 33 | Stl int64 34 | Blk int64 35 | To int64 36 | Pf int64 37 | Pts int64 38 | PlusMinus int64 39 | TeamName string 40 | StartersBench string 41 | } 42 | 43 | type BoxScoreRepository struct { 44 | } 45 | 46 | func (g *BoxScoreRepository) GetSingleGameStats(gameID string) []GameStat { 47 | sbv2 := nag.NewBoxScoreTraditionalV2(gameID) 48 | err := sbv2.Get() 49 | if err != nil { 50 | panic(err) 51 | } 52 | if sbv2.Response == nil { 53 | panic("no response") 54 | } 55 | 56 | n := nag.Map(*sbv2.Response) 57 | var result nag.BoxScoreTraditionalResponse 58 | mapstructure.Decode(n, &result) 59 | 60 | stats := make([]GameStat, 0, len(result.PlayerStats)) 61 | 62 | for _, v := range result.PlayerStats { 63 | var gameStat GameStat 64 | gameStat.GameID = v.GameID 65 | gameStat.TeamID = v.TeamID 66 | gameStat.TeamAbbreviation = v.TeamAbbreviation 67 | gameStat.TeamCity = v.TeamCity 68 | gameStat.PlayerID = v.PlayerID 69 | gameStat.PlayerName = v.PlayerName 70 | gameStat.Nickname = v.Nickname 71 | gameStat.StartPosition = v.StartPosition 72 | gameStat.Comment = v.Comment 73 | gameStat.Min = v.Min 74 | gameStat.Fgm = v.Fgm 75 | gameStat.Fga = v.Fga 76 | gameStat.FgPct = v.FgPct 77 | gameStat.Fg3M = v.Fg3M 78 | gameStat.Fg3A = v.Fg3A 79 | gameStat.Fg3Pct = v.Fg3Pct 80 | gameStat.Ftm = v.Ftm 81 | gameStat.Fta = v.Fta 82 | gameStat.FtPct = v.FtPct 83 | gameStat.Oreb = v.Oreb 84 | gameStat.Dreb = v.Dreb 85 | gameStat.Reb = v.Reb 86 | gameStat.AST = v.AST 87 | gameStat.Stl = v.Stl 88 | gameStat.Blk = v.Blk 89 | gameStat.To = v.To 90 | gameStat.Pf = v.Pf 91 | gameStat.Pts = v.Pts 92 | gameStat.PlusMinus = v.PlusMinus 93 | gameStat.TeamName = v.TeamName 94 | gameStat.StartersBench = v.StartersBench 95 | 96 | stats = append(stats, gameStat) 97 | } 98 | 99 | return stats 100 | } 101 | -------------------------------------------------------------------------------- /nba/utils.go: -------------------------------------------------------------------------------- 1 | package nba 2 | 3 | import ( 4 | "embed" 5 | "encoding/json" 6 | "fmt" 7 | "io/ioutil" 8 | "strings" 9 | "time" 10 | ) 11 | 12 | type Team struct { 13 | IsNBAFranchise bool `json:"isNBAFranchise"` 14 | IsAllStar bool `json:"isAllStar"` 15 | City string `json:"city"` 16 | AltCityName string `json:"altCityName"` 17 | FullName string `json:"fullName"` 18 | Tricode string `json:"tricode"` 19 | TeamID int64 `json:"teamId"` 20 | Nickname string `json:"nickname"` 21 | URLName string `json:"urlName"` 22 | TeamShortName string `json:"teamShortName"` 23 | ConfName string `json:"confName"` 24 | DivName string `json:"divName"` 25 | } 26 | 27 | //go:embed teams.json 28 | var f embed.FS 29 | 30 | func GetTeamByIdOrTricode(id int64, tricode string) (Team, error) { 31 | 32 | jsonFile, err := f.Open("teams.json") 33 | 34 | if err != nil { 35 | panic(err) 36 | } 37 | 38 | defer jsonFile.Close() 39 | byteValue, _ := ioutil.ReadAll(jsonFile) 40 | 41 | // we initialize our Teams array 42 | var teams []Team 43 | json.Unmarshal(byteValue, &teams) 44 | 45 | // find the team with the id or tricode 46 | for i := 0; i < len(teams); i++ { 47 | if teams[i].TeamID == id || teams[i].Tricode == tricode { 48 | return teams[i], nil 49 | } 50 | } 51 | 52 | // return an empty team if not found 53 | return Team{}, fmt.Errorf("Team not found") 54 | } 55 | 56 | // format a date passed as DD/MM/YYYY to YYYYMMDD 57 | func FormatDate(date string) string { 58 | return fmt.Sprintf("%s%s%s", date[6:], date[3:5], date[0:2]) 59 | } 60 | 61 | func GetDateTimeFromESTInUTC(estTime string, gameDate string) time.Time { 62 | cleanDate := gameDate[:len(gameDate)-9] 63 | 64 | cleanTime := strings.Replace(estTime, " ", "", -1) 65 | cleanTime = strings.TrimSpace(strings.ToUpper(cleanTime[:len(cleanTime)-2])) 66 | 67 | timeMeridian := cleanTime[len(cleanTime)-2:] 68 | 69 | fullTime := cleanTime[:len(cleanTime)-2] + ":00" 70 | 71 | // prepend a 0 to handle the case where time is 3:04:05 -> 03:04:05 72 | if len(fullTime) < 8 { 73 | fullTime = "0" + fullTime 74 | } 75 | 76 | t, _ := time.Parse("03:04:05PM", fullTime+timeMeridian) 77 | fullTime = t.Format("15:04:05") 78 | 79 | EST, err := time.LoadLocation("America/New_York") 80 | if err != nil { 81 | panic(err) 82 | } 83 | 84 | // parse the date and time in the lyt format 85 | const lyt = "2006-01-02 15:04:05 MST" 86 | 87 | timeAndLoc := fmt.Sprintf("%s %s", cleanDate, fullTime+" EST") 88 | 89 | dt, err := time.ParseInLocation(lyt, timeAndLoc, EST) 90 | if err != nil { 91 | panic(err) 92 | } 93 | 94 | return dt.UTC() 95 | } 96 | 97 | func GetDateFromString(gameDate string) time.Time { 98 | date, err := time.Parse("2006-01-02", gameDate[:len(gameDate)-9]) 99 | if err != nil { 100 | panic(err) 101 | } 102 | return date 103 | } 104 | -------------------------------------------------------------------------------- /nba/scoreboard.go: -------------------------------------------------------------------------------- 1 | package nba 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | "time" 7 | 8 | "github.com/mitchellh/mapstructure" 9 | "github.com/nleeper/goment" 10 | 11 | "github.com/dylantientcheu/nbacli/nag" 12 | "github.com/dylantientcheu/nbacli/ui/constants" 13 | ) 14 | 15 | type BoxScoreSummary struct { 16 | GameId string 17 | GameDate string 18 | GameStatus string 19 | Gamecode string 20 | HomeTeamId int64 21 | HomeTeamName string 22 | VisitorTeamId int64 23 | VisitorTeamName string 24 | HomeTeamScore int 25 | VisitorTeamScore int 26 | ArenaName string 27 | } 28 | 29 | func (g BoxScoreSummary) Title() string { return g.HomeTeamName + " vs " + g.VisitorTeamName } 30 | 31 | // Description the game description to display in a list 32 | func (g BoxScoreSummary) Description() string { 33 | var desc = "" 34 | var status = strings.TrimSpace(g.GameStatus) 35 | if status[len(status)-2:] == "ET" { 36 | // upcoming game 37 | gameTime := GetDateTimeFromESTInUTC(status, g.GameDate) 38 | moment, _ := goment.Unix(gameTime.Unix()) 39 | now, _ := goment.New() 40 | 41 | // show time from now 42 | desc = fmt.Sprintf("Tip-off %s | %s", moment.From(now), g.ArenaName) 43 | desc = constants.DescStyle(desc) 44 | } else if status == "Final" { 45 | // passed game 46 | // gameDate := GetDateFromString(g.GameDate).Format("2006-01-02") 47 | desc = fmt.Sprintf("%s %s", constants.ScoreStyle(g.HomeTeamScore, g.VisitorTeamScore), constants.DescStyle(g.ArenaName)) 48 | } else { 49 | // live game 50 | desc = fmt.Sprintf("%s %s - %s | %s", constants.LiveStyle(), constants.ScoreStyle(g.HomeTeamScore, g.VisitorTeamScore), constants.DescStyle(status), constants.DescStyle(g.ArenaName)) 51 | desc = constants.DescText.Render(desc) 52 | } 53 | 54 | return desc 55 | } 56 | 57 | // FilterValue choose what field to use for filtering in a Bubbletea list component 58 | func (g BoxScoreSummary) FilterValue() string { return g.HomeTeamName + " vs " + g.VisitorTeamName } 59 | 60 | type ScoreboardRepository struct { 61 | } 62 | 63 | func (g *ScoreboardRepository) GetGames(date time.Time) []BoxScoreSummary { 64 | sbv2 := nag.NewScoreBoardV2(date) 65 | err := sbv2.Get() 66 | if err != nil { 67 | panic(err) 68 | } 69 | if sbv2.Response == nil { 70 | panic("no response") 71 | } 72 | 73 | n := nag.Map(*sbv2.Response) 74 | var result nag.ScoreBoardResponse 75 | mapstructure.Decode(n, &result) 76 | 77 | // new games array 78 | games := make([]BoxScoreSummary, 0, len(result.GameHeader)) 79 | 80 | for _, v := range result.GameHeader { 81 | var game BoxScoreSummary 82 | game.GameId = v.GameID 83 | game.GameDate = v.GameDateEst 84 | game.GameStatus = v.GameStatusText 85 | game.HomeTeamId = v.HomeTeamID 86 | game.VisitorTeamId = v.VisitorTeamID 87 | game.Gamecode = v.Gamecode 88 | 89 | // get team name by id 90 | hteam, herr := GetTeamByIdOrTricode(v.HomeTeamID, "") 91 | ateam, aerr := GetTeamByIdOrTricode(v.VisitorTeamID, "") 92 | if herr != nil { 93 | panic(herr) 94 | } 95 | if aerr != nil { 96 | panic(aerr) 97 | } 98 | 99 | game.HomeTeamName = hteam.FullName 100 | game.VisitorTeamName = ateam.FullName 101 | game.ArenaName = v.ArenaName 102 | game.GameStatus = v.GameStatusText 103 | 104 | // get games scores 105 | for _, s := range result.LineScore { 106 | if s.TeamID == v.HomeTeamID { 107 | game.HomeTeamScore = s.Pts 108 | } 109 | if s.TeamID == v.VisitorTeamID { 110 | game.VisitorTeamScore = s.Pts 111 | } 112 | } 113 | 114 | games = append(games, game) 115 | } 116 | return games 117 | } 118 | -------------------------------------------------------------------------------- /ui/scoreboards.go: -------------------------------------------------------------------------------- 1 | package ui 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/dylantientcheu/nbacli/nba" 7 | "github.com/dylantientcheu/nbacli/ui/constants" 8 | 9 | "github.com/charmbracelet/bubbles/key" 10 | "github.com/charmbracelet/bubbles/list" 11 | tea "github.com/charmbracelet/bubbletea" 12 | ) 13 | 14 | type mode int 15 | 16 | type SelectMsg struct { 17 | ActiveScorebardID uint 18 | } 19 | 20 | const ( 21 | nav mode = iota 22 | edit 23 | ) 24 | 25 | type Model struct { 26 | mode mode 27 | list list.Model 28 | currentDate time.Time 29 | quitting bool 30 | gameview bool 31 | } 32 | 33 | func InitScoreboard(date time.Time) tea.Model { 34 | items := newScoreboardList(nba.Sb, date) 35 | m := Model{mode: nav, currentDate: date, list: list.NewModel(items, list.NewDefaultDelegate(), 8, 8)} 36 | if constants.WindowSize.Height != 0 { 37 | top, right, bottom, left := constants.DocStyle.GetMargin() 38 | m.list.SetSize(constants.WindowSize.Width-left-right, constants.WindowSize.Height-top-bottom-1) 39 | } 40 | m.list.Title = "NBA Games - " + m.currentDate.Format("Monday, 2 Jan 06") 41 | m.list.AdditionalShortHelpKeys = func() []key.Binding { 42 | return []key.Binding{ 43 | constants.Keymap.Tomorrow, 44 | constants.Keymap.Yesterday, 45 | constants.Keymap.Back, 46 | } 47 | } 48 | return m 49 | } 50 | 51 | func newScoreboardList(scbrd *nba.ScoreboardRepository, date time.Time) []list.Item { 52 | games := scbrd.GetGames(date) 53 | return gamesToItems(games) 54 | } 55 | 56 | // Init run any intial IO on program start 57 | func (m Model) Init() tea.Cmd { 58 | return nil 59 | } 60 | 61 | // Update handle IO and commands 62 | func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { 63 | var cmd tea.Cmd 64 | var cmds []tea.Cmd 65 | switch msg := msg.(type) { 66 | case tea.WindowSizeMsg: 67 | constants.WindowSize = msg 68 | top, right, bottom, left := constants.DocStyle.GetMargin() 69 | m.list.SetSize(msg.Width-left-right, msg.Height-top-bottom-1) 70 | case tea.KeyMsg: 71 | switch { 72 | case key.Matches(msg, constants.Keymap.Yesterday): 73 | var previousDay nba.ScoreboardRepository 74 | m.currentDate = m.currentDate.AddDate(0, 0, -1) 75 | games := previousDay.GetGames(m.currentDate) 76 | items := gamesToItems(games) 77 | m.list.Title = "NBA Games - " + m.currentDate.Format("Monday, 2 Jan 06") 78 | m.list.SetItems(items) 79 | case key.Matches(msg, constants.Keymap.Tomorrow): 80 | var nextDay nba.ScoreboardRepository 81 | m.currentDate = m.currentDate.AddDate(0, 0, 1) 82 | games := nextDay.GetGames(m.currentDate) 83 | items := gamesToItems(games) 84 | m.list.Title = "NBA Games - " + m.currentDate.Format("Monday, 2 Jan 06") 85 | m.list.SetItems(items) 86 | case key.Matches(msg, constants.Keymap.Quit): 87 | m.quitting = true 88 | return m, tea.Quit 89 | case key.Matches(msg, constants.Keymap.Enter): 90 | m.gameview = true 91 | activeGame := m.list.SelectedItem().(nba.BoxScoreSummary) 92 | gameView := InitGameView(activeGame.GameId, activeGame, m) 93 | return gameView.Update(constants.WindowSize) 94 | default: 95 | m.list, cmd = m.list.Update(msg) 96 | } 97 | cmds = append(cmds, cmd) 98 | } 99 | return m, tea.Batch(cmds...) 100 | } 101 | 102 | // View return the text UI to be output to the terminal 103 | func (m Model) View() string { 104 | if m.quitting { 105 | return "" 106 | } 107 | return constants.DocStyle.Render(m.list.View() + "\n") 108 | } 109 | 110 | func gamesToItems(games []nba.BoxScoreSummary) []list.Item { 111 | items := make([]list.Item, len(games)) 112 | for i, proj := range games { 113 | items[i] = list.Item(proj) 114 | } 115 | return items 116 | } 117 | -------------------------------------------------------------------------------- /ui/gameboard/scoretext/scoretext.go: -------------------------------------------------------------------------------- 1 | package scoretext 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "strings" 7 | 8 | "github.com/charmbracelet/lipgloss" 9 | "github.com/dylantientcheu/nbacli/ui/constants" 10 | "golang.org/x/term" 11 | ) 12 | 13 | var ( 14 | subtle = lipgloss.AdaptiveColor{Light: "#D9DCCF", Dark: "#212121"} 15 | dialogBoxStyle = lipgloss.NewStyle(). 16 | Border(lipgloss.RoundedBorder()). 17 | BorderForeground(constants.Accent). 18 | Padding(1, 1). 19 | BorderTop(true). 20 | BorderLeft(true). 21 | BorderRight(true). 22 | BorderBottom(true) 23 | 24 | scoreTextStyle = lipgloss.NewStyle(). 25 | Padding(0, 1). 26 | MarginTop(1) 27 | 28 | teamNameStyle = lipgloss.NewStyle().Margin(0, 1) 29 | ) 30 | 31 | // gotten from https://fsymbols.com/generators/tarty/ 32 | 33 | var scoreTextFont = map[int]string{ 34 | 420: `       35 |        36 | █████╗ 37 | ╚════╝ 38 |        39 |       `, 40 | 0: ` █████╗  41 | ██╔══██╗ 42 | ██║  ██║ 43 | ██║  ██║ 44 | ╚█████╔╝ 45 |  ╚════╝ `, 46 | 1: `  ███╗   47 |  ████║   48 | ██╔██║   49 | ╚═╝██║   50 | ███████╗ 51 | ╚══════╝`, 52 | 2: `██████╗  53 | ╚════██╗ 54 |   ███╔═╝ 55 | ██╔══╝   56 | ███████╗ 57 | ╚══════╝`, 58 | 3: `██████╗  59 | ╚════██╗ 60 |  █████╔╝ 61 |  ╚═══██╗ 62 | ██████╔╝ 63 | ╚═════╝ `, 64 | 4: `  ██╗██╗ 65 |  ██╔╝██║ 66 | ██╔╝ ██║ 67 | ███████║ 68 | ╚════██║ 69 |      ╚═╝`, 70 | 5: `███████╗ 71 | ██╔════╝ 72 | ██████╗  73 | ╚════██╗ 74 | ██████╔╝ 75 | ╚═════╝ `, 76 | 6: ` █████╗  77 | ██╔═══╝  78 | ██████╗  79 | ██╔══██╗ 80 | ╚█████╔╝ 81 |  ╚════╝ `, 82 | 7: `███████╗ 83 | ╚════██║ 84 |     ██╔╝ 85 |    ██╔╝  86 |   ██╔╝   87 |   ╚═╝   `, 88 | 8: ` █████╗  89 | ██╔══██╗ 90 | ╚█████╔╝ 91 | ██╔══██╗ 92 | ╚█████╔╝ 93 |  ╚════╝ `, 94 | 9: ` █████╗  95 | ██╔══██╗ 96 | ╚██████║ 97 |  ╚═══██║ 98 |  █████╔╝ 99 |  ╚════╝ `, 100 | } 101 | 102 | func RenderScoreText(ArenaName string, gameDate string, scoreHome int, scoreAway int, HomeTeamName string, VisitorTeamName string) string { 103 | width, _, _ := term.GetSize(int(os.Stdout.Fd())) 104 | doc := strings.Builder{} 105 | 106 | { 107 | // game board 108 | scoreTeamHome := lipgloss.JoinHorizontal(lipgloss.Center, teamNameStyle.Render(HomeTeamName), lipgloss.JoinHorizontal(lipgloss.Top, getBigScoreText(scoreHome), getBigScoreText(420))) 109 | scoreAwayTeam := lipgloss.JoinHorizontal(lipgloss.Center, getBigScoreText(scoreAway), teamNameStyle.Render(VisitorTeamName)) 110 | 111 | scoreText := lipgloss.JoinHorizontal(lipgloss.Center, scoreTeamHome, scoreAwayTeam) 112 | 113 | stadium := lipgloss.NewStyle().Width(90).Align(lipgloss.Center).Render(fmt.Sprintf("%s\n%s", ArenaName, gameDate[:len(gameDate)-9])) 114 | 115 | ui := lipgloss.JoinVertical(lipgloss.Center, stadium, scoreText) 116 | 117 | gameBoard := lipgloss.Place(width, 17, 118 | lipgloss.Center, lipgloss.Center, 119 | dialogBoxStyle.Render(ui), 120 | lipgloss.WithWhitespaceChars("░"), 121 | lipgloss.WithWhitespaceForeground(subtle), 122 | ) 123 | 124 | doc.WriteString(gameBoard + "\n\n") 125 | } 126 | 127 | return doc.String() 128 | } 129 | 130 | func getBigScoreText(number int) string { 131 | if number == 420 { 132 | return scoreTextStyle.Render(scoreTextFont[420]) 133 | } 134 | 135 | scoreSlice := splitInt(number) 136 | result := "" 137 | 138 | for _, v := range scoreSlice { 139 | result = lipgloss.JoinHorizontal(lipgloss.Top, result, scoreTextStyle.Render(scoreTextFont[v])) 140 | } 141 | 142 | return result 143 | } 144 | 145 | func splitInt(n int) []int { 146 | slc := []int{} 147 | for n > 0 { 148 | slc = append(slc, n%10) 149 | n /= 10 150 | } 151 | 152 | result := []int{} 153 | for i := range slc { 154 | result = append(result, slc[len(slc)-1-i]) 155 | } 156 | 157 | return result 158 | } 159 | -------------------------------------------------------------------------------- /nba/standings.go: -------------------------------------------------------------------------------- 1 | package nba 2 | 3 | import ( 4 | "github.com/mitchellh/mapstructure" 5 | 6 | "github.com/dylantientcheu/nbacli/nag" 7 | ) 8 | 9 | type Standing struct { 10 | LeagueID string 11 | SeasonID string 12 | TeamID int64 13 | TeamCity string 14 | TeamName string 15 | TeamSlug string 16 | Conference nag.Conference 17 | ConferenceRecord string 18 | PlayoffRank int64 19 | ClinchIndicator string 20 | Division string 21 | DivisionRecord string 22 | DivisionRank int64 23 | WINS int64 24 | Losses int64 25 | WinPCT float64 26 | LeagueRank int64 27 | Record string 28 | Home string 29 | Road string 30 | L10 string 31 | Last10Home string 32 | Last10Road string 33 | Ot string 34 | ThreePTSOrLess string 35 | TenPTSOrMore string 36 | LongHomeStreak int64 37 | StrLongHomeStreak string 38 | LongRoadStreak int64 39 | StrLongRoadStreak string 40 | LongWinStreak int64 41 | LongLossStreak int64 42 | CurrentHomeStreak int64 43 | StrCurrentHomeStreak string 44 | CurrentRoadStreak int64 45 | StrCurrentRoadStreak string 46 | CurrentStreak int64 47 | StrCurrentStreak string 48 | ConferenceGamesBack float64 49 | DivisionGamesBack float64 50 | ClinchedConferenceTitle int64 51 | ClinchedDivisionTitle int64 52 | ClinchedPlayoffBirth int64 53 | ClinchedPlayIn int64 54 | EliminatedConference int64 55 | EliminatedDivision int64 56 | AheadAtHalf string 57 | BehindAtHalf string 58 | TiedAtHalf string 59 | AheadAtThird string 60 | BehindAtThird string 61 | TiedAtThird string 62 | Score100PTS string 63 | OppScore100PTS string 64 | OppOver500 string 65 | LeadInFGPCT string 66 | LeadInReb string 67 | FewerTurnovers string 68 | PointsPG float64 69 | OppPointsPG float64 70 | DiffPointsPG float64 71 | TotalPoints int64 72 | OppTotalPoints int64 73 | DiffTotalPoints int64 74 | } 75 | 76 | type StandingsRepository struct { 77 | } 78 | 79 | func (g *StandingsRepository) GetSeasonStandings() ([]Standing, []Standing) { 80 | sbv2 := nag.NewLeagueStandingsV3() 81 | err := sbv2.Get() 82 | if err != nil { 83 | panic(err) 84 | } 85 | if sbv2.Response == nil { 86 | panic("no response") 87 | } 88 | 89 | n := nag.Map(*sbv2.Response) 90 | var result nag.LeagueStandingsResponse 91 | mapstructure.Decode(n, &result) 92 | 93 | easternConference := make([]Standing, 0, len(result.Standings)/2) 94 | westernConference := make([]Standing, 0, len(result.Standings)/2) 95 | 96 | for _, v := range result.Standings { 97 | var standing Standing 98 | standing.TeamID = v.TeamID 99 | standing.PlayoffRank = v.PlayoffRank 100 | standing.TeamName = v.TeamName 101 | standing.WINS = v.WINS 102 | standing.Losses = v.Losses 103 | standing.WinPCT = v.WinPCT 104 | standing.Home = v.Home 105 | standing.Road = v.Road 106 | standing.Conference = v.Conference 107 | standing.ConferenceRecord = v.ConferenceRecord 108 | standing.PointsPG = v.PointsPG 109 | standing.OppPointsPG = v.OppPointsPG 110 | standing.DiffPointsPG = v.DiffPointsPG 111 | standing.StrCurrentStreak = v.StrCurrentStreak 112 | standing.L10 = v.L10 113 | 114 | if v.Conference == nag.East { 115 | easternConference = append(easternConference, standing) 116 | } else { 117 | westernConference = append(westernConference, standing) 118 | } 119 | } 120 | 121 | return easternConference, westernConference 122 | } 123 | -------------------------------------------------------------------------------- /ui/constants/consts.go: -------------------------------------------------------------------------------- 1 | package constants 2 | 3 | import ( 4 | "strconv" 5 | 6 | "github.com/evertras/bubble-table/table" 7 | 8 | "github.com/charmbracelet/bubbles/key" 9 | tea "github.com/charmbracelet/bubbletea" 10 | "github.com/charmbracelet/lipgloss" 11 | ) 12 | 13 | /* CONSTANTS */ 14 | 15 | var BaseStyle = lipgloss.NewStyle(). 16 | BorderStyle(lipgloss.RoundedBorder()). 17 | BorderForeground(Secondary) 18 | 19 | var ( 20 | // P the current tea program 21 | P *tea.Program 22 | 23 | // WindowSize store the size of the terminal window 24 | WindowSize tea.WindowSizeMsg 25 | 26 | CustomTableBorder = table.Border{ 27 | Top: "─", 28 | Left: "│", 29 | Right: "│", 30 | Bottom: "─", 31 | 32 | TopRight: "╮", 33 | TopLeft: "╭", 34 | BottomRight: "╯", 35 | BottomLeft: "╰", 36 | 37 | TopJunction: "┬", 38 | LeftJunction: "├", 39 | RightJunction: "┤", 40 | BottomJunction: "┴", 41 | InnerJunction: "┼", 42 | 43 | InnerDivider: "│", 44 | } 45 | 46 | LiveText = lipgloss.NewStyle().Background(lipgloss.AdaptiveColor{Light: "#ef2929", Dark: "#ef2929"}).Foreground(lipgloss.AdaptiveColor{Light: "#ffffff", Dark: "#ffffff"}).Bold(true) 47 | 48 | FinalText = lipgloss.NewStyle().Background(lipgloss.Color("#9356DF")).Foreground(lipgloss.Color("#ffffff")).Bold(true) 49 | DescText = lipgloss.NewStyle().Foreground(lipgloss.Color("#818181")) 50 | 51 | ScoreText = lipgloss.NewStyle().Background(lipgloss.AdaptiveColor{Light: "214", Dark: "#181818"}).Foreground(lipgloss.AdaptiveColor{Light: "0", Dark: "214"}) 52 | 53 | Accent = lipgloss.AdaptiveColor{Light: "#5b1b7b", Dark: "#5b1b7b"} 54 | AccentDarker = lipgloss.AdaptiveColor{Light: "#5b1b7b", Dark: "#5b1b7b"} 55 | Secondary = lipgloss.AdaptiveColor{Light: "#ed2265", Dark: "#ed2265"} 56 | Tertiary = lipgloss.AdaptiveColor{Light: "#f69053", Dark: "#f69053"} 57 | 58 | activeTabBorder = lipgloss.Border{ 59 | Bottom: "─", 60 | Top: "─", 61 | Left: "│", 62 | Right: "│", 63 | TopLeft: "╭", 64 | TopRight: "╮", 65 | BottomLeft: "┘", 66 | BottomRight: "└", 67 | } 68 | 69 | tabBorder = lipgloss.Border{ 70 | Bottom: "─", 71 | // Top: "─", 72 | // Left: "│", 73 | // Right: "│", 74 | // TopLeft: "╭", 75 | // TopRight: "╮", 76 | BottomLeft: "─", 77 | BottomRight: "─", 78 | } 79 | 80 | TabStyle = lipgloss.NewStyle(). 81 | Border(tabBorder, true). 82 | BorderForeground(Accent). 83 | Background(Accent). 84 | Foreground(lipgloss.Color("#FFFFFF")). 85 | Padding(0, 1) 86 | 87 | ActiveTabStyle = lipgloss.NewStyle(). 88 | Border(activeTabBorder, true). 89 | BorderForeground(Secondary). 90 | Background(Secondary). 91 | Foreground(lipgloss.Color("#FFFFFF")). 92 | Bold(true). 93 | Padding(0, 1) 94 | 95 | BleedSpaceWidth = 4 96 | ) 97 | 98 | /* STYLING */ 99 | 100 | // DocStyle styling for viewports 101 | var DocStyle = lipgloss.NewStyle().Margin(1, 2) 102 | 103 | // TitleStyle styling for titles 104 | var TitleStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("#FFFFFF")).Background(Accent).Padding(0, 2) 105 | 106 | // HelpStyle styling for help context menu 107 | var HelpStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("241")) 108 | 109 | // ErrStyle provides styling for error messages 110 | var ErrStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("#bd534b")).Render 111 | 112 | // AlertStyle provides styling for alert messages 113 | var AlertStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("62")).Render 114 | 115 | type keymap struct { 116 | Enter key.Binding 117 | Yesterday key.Binding 118 | Tomorrow key.Binding 119 | Back key.Binding 120 | Quit key.Binding 121 | } 122 | 123 | // Keymap reusable key mappings shared across models 124 | var Keymap = keymap{ 125 | Enter: key.NewBinding( 126 | key.WithKeys("enter"), 127 | key.WithHelp("↲/enter", "select"), 128 | ), 129 | Yesterday: key.NewBinding( 130 | key.WithKeys("i", "left"), 131 | key.WithHelp("←/i", "previous day"), 132 | ), 133 | Tomorrow: key.NewBinding( 134 | key.WithKeys("o", "right"), 135 | key.WithHelp("→/o", "next day"), 136 | ), 137 | Back: key.NewBinding( 138 | key.WithKeys("esc"), 139 | key.WithHelp("esc", "back"), 140 | ), 141 | Quit: key.NewBinding( 142 | key.WithKeys("ctrl+c", "q"), 143 | key.WithHelp("ctrl+c/q", "quit"), 144 | ), 145 | } 146 | 147 | func Max(a, b int) int { 148 | if a > b { 149 | return a 150 | } 151 | return b 152 | } 153 | 154 | func LiveStyle() string { 155 | return LiveText.Render(" LIVE ") 156 | } 157 | 158 | func FinalStyle() string { 159 | return FinalText.Render(" FINAL ") 160 | } 161 | 162 | func ScoreStyle(homeScore int, awayScore int) string { 163 | return ScoreText.Render(" " + strconv.Itoa(homeScore) + " - " + strconv.Itoa(awayScore) + " ") 164 | } 165 | 166 | func DescStyle(desc string) string { 167 | return DescText.Render(desc) 168 | } 169 | -------------------------------------------------------------------------------- /ui/standings.go: -------------------------------------------------------------------------------- 1 | package ui 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/dylantientcheu/nbacli/nag/params" 7 | "github.com/dylantientcheu/nbacli/nba" 8 | "github.com/dylantientcheu/nbacli/ui/constants" 9 | 10 | "github.com/evertras/bubble-table/table" 11 | 12 | "github.com/charmbracelet/bubbles/help" 13 | "github.com/charmbracelet/bubbles/key" 14 | tea "github.com/charmbracelet/bubbletea" 15 | "github.com/charmbracelet/lipgloss" 16 | ) 17 | 18 | type StandingsModel struct { 19 | easternConfTable table.Model 20 | westernConfTable table.Model 21 | help help.Model 22 | selectedTab int 23 | width, height, margin int 24 | } 25 | 26 | var standingsKM = StandingKM{ 27 | NextTab: key.NewBinding(key.WithKeys("tab"), key.WithHelp("tab", "toggle conference (change tab)")), 28 | Down: key.NewBinding(key.WithKeys("down"), key.WithHelp("↓", "next row")), 29 | Up: key.NewBinding(key.WithKeys("up"), key.WithHelp("↑", "previous row")), 30 | Previous: key.NewBinding(key.WithKeys("esc", "q"), key.WithHelp("q/esc", "quit")), 31 | } 32 | 33 | func (m *StandingsModel) recalculateTable() { 34 | m.easternConfTable = m.easternConfTable.WithTargetWidth(m.width - constants.BleedSpaceWidth) 35 | m.westernConfTable = m.westernConfTable.WithTargetWidth(m.width - constants.BleedSpaceWidth) 36 | } 37 | 38 | func (m StandingsModel) Init() tea.Cmd { return nil } 39 | 40 | func (m StandingsModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { 41 | var cmd tea.Cmd 42 | switch msg := msg.(type) { 43 | case tea.KeyMsg: 44 | switch msg.String() { 45 | case "tab": 46 | m.selectedTab = (m.selectedTab + 1) % 2 47 | if (m.selectedTab) == 0 { 48 | m.easternConfTable = m.easternConfTable.Focused(true) 49 | m.westernConfTable = m.westernConfTable.Focused(false) 50 | } else { 51 | m.easternConfTable = m.easternConfTable.Focused(false) 52 | m.westernConfTable = m.westernConfTable.Focused(true) 53 | } 54 | case "q", "esc", "ctrl+c": 55 | return m, tea.Quit 56 | case "enter": 57 | // TODO: to team view 58 | // return m, tea.Batch() 59 | } 60 | case tea.WindowSizeMsg: 61 | m.width = msg.Width 62 | m.recalculateTable() 63 | } 64 | 65 | m.easternConfTable, _ = m.easternConfTable.Update(msg) 66 | m.westernConfTable, cmd = m.westernConfTable.Update(msg) 67 | 68 | return m, cmd 69 | } 70 | 71 | func (m StandingsModel) View() string { 72 | easternTable := m.easternConfTable.View() + "\n" 73 | westernTable := m.westernConfTable.View() + "\n" 74 | 75 | helpContainer := lipgloss.NewStyle(). 76 | SetString(m.help.View(standingsKM)). 77 | Width(m.width). 78 | Align(lipgloss.Center). 79 | PaddingTop(1). 80 | String() 81 | 82 | tabGap := constants.TabStyle.Copy(). 83 | BorderTop(false). 84 | BorderLeft(false). 85 | BorderRight(false) 86 | 87 | tabRow := lipgloss.JoinHorizontal( 88 | lipgloss.Top, 89 | constants.ActiveTabStyle.Render("EASTERN CONFERENCE"), 90 | constants.TabStyle.Render("WESTERN CONFERENCE"), 91 | ) 92 | 93 | renderedTable := easternTable 94 | 95 | if m.selectedTab == 1 { 96 | tabRow = lipgloss.JoinHorizontal( 97 | lipgloss.Top, 98 | constants.TabStyle.Render("EASTERN CONFERENCE"), 99 | constants.ActiveTabStyle.Render("WESTERN CONFERENCE"), 100 | ) 101 | renderedTable = westernTable 102 | } 103 | 104 | gap := tabGap.Render(strings.Repeat(" ", constants.Max(0, m.width-lipgloss.Width(tabRow)))) 105 | 106 | tabRow = lipgloss.JoinHorizontal(lipgloss.Bottom, tabRow, gap) 107 | 108 | // title 109 | title := constants.TitleStyle.Render("NBA Standings: " + params.CurrentSeason) 110 | 111 | return constants.DocStyle.Render(title + "\n\n" + tabRow + "\n" + renderedTable + "\n" + helpContainer) 112 | } 113 | 114 | func InitStandingsView() *StandingsModel { 115 | columns := []table.Column{ 116 | table.NewFlexColumn("POS", "POS", 2), 117 | table.NewFlexColumn("TEAM", "TEAM", 10), 118 | table.NewFlexColumn("PCT", "PCT", 5), 119 | table.NewFlexColumn("HOME", "HOME", 5), 120 | table.NewFlexColumn("AWAY", "AWAY", 5), 121 | table.NewFlexColumn("CONF", "CONF", 3), 122 | table.NewFlexColumn("PPG", "PPG", 3), 123 | table.NewFlexColumn("OPPPPG", "OPPPPG", 3), 124 | table.NewFlexColumn("DIFF", "DIFF", 3), 125 | table.NewFlexColumn("STRK", "STRK", 3), 126 | table.NewFlexColumn("L10", "L10", 3), 127 | } 128 | 129 | easternRows, westernRows := newStandingsBoard(nba.St) 130 | 131 | tEast := table.New(columns).WithRows(easternRows).Focused(true).Border(constants.CustomTableBorder).WithBaseStyle(constants.BaseStyle).WithPageSize(10) 132 | tWest := table.New(columns).WithRows(westernRows).Border(constants.CustomTableBorder).WithBaseStyle(constants.BaseStyle).WithPageSize(10) 133 | 134 | m := StandingsModel{tEast, tWest, help.New(), 0, constants.WindowSize.Height, constants.WindowSize.Width, 3} 135 | return &m 136 | } 137 | 138 | func newStandingsBoard(standings *nba.StandingsRepository) ([]table.Row, []table.Row) { 139 | easternConference, westernConference := standings.GetSeasonStandings() 140 | return standingsToRows(easternConference, westernConference) 141 | } 142 | 143 | func standingsToRows(easternConferenceStandings []nba.Standing, westernConferenceStandings []nba.Standing) ([]table.Row, []table.Row) { 144 | var ( 145 | eastRows []table.Row 146 | westRows []table.Row 147 | ) 148 | 149 | for _, stat := range easternConferenceStandings { 150 | eastRows = append(eastRows, table.NewRow( 151 | table.RowData{ 152 | "POS": stat.PlayoffRank, 153 | "TEAM": stat.TeamName, 154 | "PCT": stat.WinPCT, 155 | "HOME": stat.Home, 156 | "AWAY": stat.Road, 157 | "CONF": stat.ConferenceRecord, 158 | "PPG": stat.PointsPG, 159 | "OPPPPG": stat.OppPointsPG, 160 | "DIFF": stat.DiffPointsPG, 161 | "STRK": stat.StrCurrentStreak, 162 | "L10": stat.L10, 163 | }, 164 | )) 165 | } 166 | 167 | for _, stat := range westernConferenceStandings { 168 | westRows = append(westRows, table.NewRow( 169 | table.RowData{ 170 | "POS": stat.PlayoffRank, 171 | "TEAM": stat.TeamName, 172 | "PCT": stat.WinPCT, 173 | "HOME": stat.Home, 174 | "AWAY": stat.Road, 175 | "CONF": stat.ConferenceRecord, 176 | "PPG": stat.PointsPG, 177 | "OPPPPG": stat.OppPointsPG, 178 | "DIFF": stat.DiffPointsPG, 179 | "STRK": stat.StrCurrentStreak, 180 | "L10": stat.L10, 181 | }, 182 | )) 183 | } 184 | return eastRows, westRows 185 | } 186 | -------------------------------------------------------------------------------- /ui/game.go: -------------------------------------------------------------------------------- 1 | package ui 2 | 3 | import ( 4 | "strconv" 5 | 6 | "github.com/dylantientcheu/nbacli/nba" 7 | "github.com/dylantientcheu/nbacli/ui/constants" 8 | "github.com/dylantientcheu/nbacli/ui/gameboard/scoretext" 9 | 10 | "github.com/evertras/bubble-table/table" 11 | 12 | "github.com/charmbracelet/bubbles/help" 13 | "github.com/charmbracelet/bubbles/key" 14 | tea "github.com/charmbracelet/bubbletea" 15 | "github.com/charmbracelet/lipgloss" 16 | ) 17 | 18 | var baseStyle = lipgloss.NewStyle(). 19 | BorderStyle(lipgloss.RoundedBorder()). 20 | BorderForeground(constants.Accent) 21 | 22 | type GameModel struct { 23 | table table.Model 24 | activeGameID string 25 | activeGame nba.BoxScoreSummary 26 | previousModel Model 27 | help help.Model 28 | width, height, margin int 29 | } 30 | 31 | var gameKM = GameKM{ 32 | Down: key.NewBinding(key.WithKeys("down"), key.WithHelp("↓", "highlight next row")), 33 | Up: key.NewBinding(key.WithKeys("up"), key.WithHelp("↑", "highlight previous row")), 34 | Previous: key.NewBinding(key.WithKeys("esc", "q"), key.WithHelp("q/esc", "back to games list")), 35 | } 36 | 37 | func (m *GameModel) recalculateTable() { 38 | m.table = m.table.WithTargetWidth(m.width) 39 | } 40 | 41 | func (m GameModel) Init() tea.Cmd { return nil } 42 | 43 | func (m GameModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { 44 | var cmd tea.Cmd 45 | switch msg := msg.(type) { 46 | case tea.KeyMsg: 47 | switch msg.String() { 48 | case "q", "esc": 49 | // return to previous page 50 | return m.previousModel, tea.Batch() 51 | case "ctrl+c": 52 | return m, tea.Quit 53 | case "enter": 54 | // TODO: to player view 55 | return m, tea.Batch() 56 | } 57 | case tea.WindowSizeMsg: 58 | m.width = msg.Width 59 | m.recalculateTable() 60 | } 61 | 62 | m.table, cmd = m.table.Update(msg) 63 | return m, cmd 64 | } 65 | 66 | func (m GameModel) View() string { 67 | table := m.table.View() + "\n" 68 | 69 | helpContainer := lipgloss.NewStyle(). 70 | SetString(m.help.View(gameKM)). 71 | Width(m.width). 72 | Align(lipgloss.Center). 73 | PaddingTop(1). 74 | String() 75 | 76 | return scoretext.RenderScoreText(m.activeGame.ArenaName, m.activeGame.GameDate, m.activeGame.HomeTeamScore, m.activeGame.VisitorTeamScore, m.activeGame.HomeTeamName, m.activeGame.VisitorTeamName) + table + helpContainer 77 | } 78 | 79 | func InitGameView(activeGameID string, activeGame nba.BoxScoreSummary, previousModel Model) *GameModel { 80 | columns := []table.Column{ 81 | table.NewFlexColumn("POS", "POS", 2), 82 | table.NewFlexColumn("NAME", "NAME", 10), 83 | table.NewFlexColumn("MIN", "MIN", 6), 84 | table.NewFlexColumn("FG", "FG", 6), 85 | table.NewFlexColumn("3PT", "3PT", 3), 86 | table.NewFlexColumn("FT", "FT", 3), 87 | table.NewFlexColumn("REB", "REB", 3), 88 | table.NewFlexColumn("AST", "AST", 3), 89 | table.NewFlexColumn("STL", "STL", 3), 90 | table.NewFlexColumn("BLK", "BLK", 3), 91 | table.NewFlexColumn("TO", "TO", 3), 92 | table.NewFlexColumn("+/-", "+/-", 4), 93 | table.NewFlexColumn("PTS", "PTS", 3), 94 | } 95 | 96 | rows := newStatsBoard(nba.Gm, activeGameID) 97 | 98 | t := table.New(columns).WithRows(rows). 99 | Focused(true). 100 | Border(constants.CustomTableBorder).WithBaseStyle(baseStyle).WithPageSize(constants.WindowSize.Height / 3) 101 | 102 | m := GameModel{t, activeGameID, activeGame, previousModel, help.New(), constants.WindowSize.Height, constants.WindowSize.Width, 3} 103 | return &m 104 | } 105 | 106 | func newStatsBoard(game *nba.BoxScoreRepository, gameID string) []table.Row { 107 | gameStats := game.GetSingleGameStats(gameID) 108 | return statsToRows(gameStats) 109 | } 110 | 111 | func statsToRows(gameStats []nba.GameStat) []table.Row { 112 | var rows []table.Row 113 | areBenchers := false 114 | 115 | rows = append(rows, table.NewRow(renderTeamRow("AWAY TEAM")). 116 | WithStyle(lipgloss.NewStyle().AlignHorizontal(lipgloss.Center). 117 | Background(constants.Secondary))) 118 | 119 | for idx, stat := range gameStats { 120 | // format plus minus 121 | plusMinus := "0" 122 | if stat.PlusMinus > 0 { 123 | plusMinus = "+" + strconv.FormatInt(stat.PlusMinus, 10) 124 | } else { 125 | plusMinus = strconv.FormatInt(stat.PlusMinus, 10) 126 | } 127 | 128 | if (stat.StartPosition == "") && !areBenchers { 129 | rows = append(rows, table.NewRow( 130 | renderBenchRow(), 131 | ).WithStyle(lipgloss.NewStyle().AlignHorizontal(lipgloss.Center).Background(lipgloss.AdaptiveColor{Light: "214", Dark: "#181818"}))) 132 | areBenchers = true 133 | } 134 | 135 | rows = append(rows, table.NewRow( 136 | table.RowData{ 137 | "POS": stat.StartPosition, 138 | "NAME": stat.PlayerName, 139 | "MIN": stat.Min, 140 | "FG": strconv.FormatInt(stat.Fgm, 10) + "-" + strconv.FormatInt(stat.Fga, 10), 141 | "3PT": strconv.FormatInt(stat.Fg3M, 10), 142 | "FT": strconv.FormatInt(stat.Ftm, 10), 143 | "REB": strconv.FormatInt(stat.Reb, 10), 144 | "AST": strconv.FormatInt(stat.AST, 10), 145 | "STL": strconv.FormatInt(stat.Stl, 10), 146 | "BLK": strconv.FormatInt(stat.Blk, 10), 147 | "TO": strconv.FormatInt(stat.To, 10), 148 | "+/-": plusMinus, 149 | "PTS": strconv.FormatInt(stat.Pts, 10), 150 | }, 151 | )) 152 | if stat.StartPosition != "" { 153 | areBenchers = false 154 | } 155 | 156 | if idx < len(gameStats)-1 && gameStats[idx].TeamID != gameStats[idx+1].TeamID { 157 | rows = append(rows, table.NewRow(renderTeamRow("HOME TEAM")).WithStyle(lipgloss.NewStyle(). 158 | AlignHorizontal(lipgloss.Center). 159 | Background(constants.Secondary))) 160 | } 161 | } 162 | return rows 163 | } 164 | 165 | func renderBenchRow() table.RowData { 166 | return table.RowData{ 167 | "POS": "", 168 | "NAME": table.NewStyledCell("B E N C H", lipgloss.NewStyle().Foreground(constants.Tertiary).Padding(0)), 169 | "MIN": "", 170 | "FG": "", 171 | "3PT": "", 172 | "FT": "", 173 | "REB": "", 174 | "AST": "", 175 | "STL": "", 176 | "BLK": "", 177 | "TO": "", 178 | "+/-": "", 179 | "PTS": "", 180 | } 181 | } 182 | 183 | func renderTeamRow(team string) table.RowData { 184 | return table.RowData{ 185 | "POS": "", 186 | "NAME": table.NewStyledCell(team, lipgloss.NewStyle().Foreground(lipgloss.Color("#FFFFFF"))), 187 | "MIN": "", 188 | "FG": "", 189 | "3PT": "", 190 | "FT": "", 191 | "REB": "", 192 | "AST": "", 193 | "STL": "", 194 | "BLK": "", 195 | "TO": "", 196 | "+/-": "", 197 | "PTS": "", 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4= 2 | github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= 3 | github.com/aymanbagabas/go-osc52 v1.0.3 h1:DTwqENW7X9arYimJrPeGZcV0ln14sGMt3pHZspWD+Mg= 4 | github.com/aymanbagabas/go-osc52 v1.0.3/go.mod h1:zT8H+Rk4VSabYN90pWyugflM3ZhpTZNC7cASDfUCdT4= 5 | github.com/charmbracelet/bubbles v0.14.0 h1:DJfCwnARfWjZLvMglhSQzo76UZ2gucuHPy9jLWX45Og= 6 | github.com/charmbracelet/bubbles v0.14.0/go.mod h1:bbeTiXwPww4M031aGi8UK2HT9RDWoiNibae+1yCMtcc= 7 | github.com/charmbracelet/bubbletea v0.21.0/go.mod h1:GgmJMec61d08zXsOhqRC/AiOx4K4pmz+VIcRIm1FKr4= 8 | github.com/charmbracelet/bubbletea v0.23.1 h1:CYdteX1wCiCzKNUlwm25ZHBIc1GXlYFyUIte8WPvhck= 9 | github.com/charmbracelet/bubbletea v0.23.1/go.mod h1:JAfGK/3/pPKHTnAS8JIE2u9f61BjWTQY57RbT25aMXU= 10 | github.com/charmbracelet/harmonica v0.2.0/go.mod h1:KSri/1RMQOZLbw7AHqgcBycp8pgJnQMYYT8QZRqZ1Ao= 11 | github.com/charmbracelet/lipgloss v0.5.0/go.mod h1:EZLha/HbzEt7cYqdFPovlqy5FZPj0xFhg5SaqxScmgs= 12 | github.com/charmbracelet/lipgloss v0.6.0 h1:1StyZB9vBSOyuZxQUcUwGr17JmojPNm87inij9N3wJY= 13 | github.com/charmbracelet/lipgloss v0.6.0/go.mod h1:tHh2wr34xcHjC2HCXIlGSG1jaDF0S0atAUvBMP6Ppuk= 14 | github.com/containerd/console v1.0.3 h1:lIr7SlA5PxZyMV30bDW0MGbiOPXwc63yRuCP0ARubLw= 15 | github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= 16 | github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= 17 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 18 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 19 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 20 | github.com/evertras/bubble-table v0.14.6 h1:JqmJw1/PaWJiaAJBnIgtoft7bLS+FVa23zOzv0uIHyY= 21 | github.com/evertras/bubble-table v0.14.6/go.mod h1:SPOZKbIpyYWPHBNki3fyNpiPBQkvkULAtOT7NTD5fKY= 22 | github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= 23 | github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= 24 | github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= 25 | github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= 26 | github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= 27 | github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= 28 | github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= 29 | github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= 30 | github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= 31 | github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= 32 | github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4= 33 | github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88= 34 | github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= 35 | github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= 36 | github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= 37 | github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= 38 | github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= 39 | github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= 40 | github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= 41 | github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b/go.mod h1:fQuZ0gauxyBcmsdE3ZT4NasjaRdxmbCS0jRHsrWu3Ho= 42 | github.com/muesli/ansi v0.0.0-20221106050444-61f0cd9a192a h1:jlDOeO5TU0pYlbc/y6PFguab5IjANI0Knrpg3u/ton4= 43 | github.com/muesli/ansi v0.0.0-20221106050444-61f0cd9a192a/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo= 44 | github.com/muesli/cancelreader v0.2.0/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= 45 | github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA= 46 | github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= 47 | github.com/muesli/reflow v0.2.1-0.20210115123740-9e1d0d53df68/go.mod h1:Xk+z4oIWdQqJzsxyjgl3P22oYZnHdZ8FFTHAQQt5BMQ= 48 | github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s= 49 | github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8= 50 | github.com/muesli/termenv v0.11.1-0.20220204035834-5ac8409525e0/go.mod h1:Bd5NYQ7pd+SrtBSrSNoBBmXlcY8+Xj4BMJgh8qcZrvs= 51 | github.com/muesli/termenv v0.11.1-0.20220212125758-44cd13922739/go.mod h1:Bd5NYQ7pd+SrtBSrSNoBBmXlcY8+Xj4BMJgh8qcZrvs= 52 | github.com/muesli/termenv v0.13.0 h1:wK20DRpJdDX8b7Ek2QfhvqhRQFZ237RGRO0RQ/Iqdy0= 53 | github.com/muesli/termenv v0.13.0/go.mod h1:sP1+uffeLaEYpyOTb8pLCUctGcGLnoFjSn4YJK5e2bc= 54 | github.com/nleeper/goment v1.4.4 h1:GlMTpxvhueljArSunzYjN9Ri4SOmpn0Vh2hg2z/IIl8= 55 | github.com/nleeper/goment v1.4.4/go.mod h1:zDl5bAyDhqxwQKAvkSXMRLOdCowrdZz53ofRJc4VhTo= 56 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 57 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 58 | github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= 59 | github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= 60 | github.com/rivo/uniseg v0.4.3 h1:utMvzDsuh3suAEnhH0RdHmoPbU648o6CvXxTx4SBMOw= 61 | github.com/rivo/uniseg v0.4.3/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= 62 | github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 63 | github.com/sahilm/fuzzy v0.1.0 h1:FzWGaw2Opqyu+794ZQ9SYifWv2EIXpwP4q8dY1kDAwI= 64 | github.com/sahilm/fuzzy v0.1.0/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y= 65 | github.com/samber/lo v1.36.0 h1:4LaOxH1mHnbDGhTVE0i1z8v/lWaQW8AIfOD3HU4mSaw= 66 | github.com/samber/lo v1.36.0/go.mod h1:HLeWcJRRyLKp3+/XBJvOrerCQn9mhdKMHyd7IRlgeQ8= 67 | github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA= 68 | github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= 69 | github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= 70 | github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 71 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 72 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 73 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 74 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 75 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 76 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 77 | github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= 78 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 79 | github.com/tkuchiki/go-timezone v0.2.0/go.mod h1:b1Ean9v2UXtxSq4TZF0i/TU9NuoWa9hOzOKoGCV2zqY= 80 | github.com/tkuchiki/go-timezone v0.2.2 h1:MdHR65KwgVTwWFQrota4SKzc4L5EfuH5SdZZGtk/P2Q= 81 | github.com/tkuchiki/go-timezone v0.2.2/go.mod h1:oFweWxYl35C/s7HMVZXiA19Jr9Y0qJHMaG/J2TES4LY= 82 | golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 h1:3MTrJm4PyNL9NBqvYDSj3DHl46qQakyfqfWo4jgfaEM= 83 | golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE= 84 | golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 85 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 86 | golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 87 | golang.org/x/sys v0.0.0-20220204135822-1c1b9b1eba6a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 88 | golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 89 | golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 90 | golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A= 91 | golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 92 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 93 | golang.org/x/term v0.2.0 h1:z85xZCsEl7bi/KwbNADeBYoOP0++7W1ipu+aGnpwzRM= 94 | golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= 95 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 96 | golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= 97 | golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 98 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 99 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 100 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 101 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 102 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 103 | -------------------------------------------------------------------------------- /entity/game.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import "time" 4 | 5 | type Today struct { 6 | Internal struct { 7 | PubDateTime string `json:"pubDateTime"` 8 | IgorPath string `json:"igorPath"` 9 | Xslt string `json:"xslt"` 10 | XsltForceRecompile string `json:"xsltForceRecompile"` 11 | XsltInCache string `json:"xsltInCache"` 12 | XsltCompileTimeMillis string `json:"xsltCompileTimeMillis"` 13 | XsltTransformTimeMillis string `json:"xsltTransformTimeMillis"` 14 | ConsolidatedDomKey string `json:"consolidatedDomKey"` 15 | EndToEndTimeMillis string `json:"endToEndTimeMillis"` 16 | } `json:"_internal"` 17 | TeamSitesOnly struct { 18 | SeasonStage int `json:"seasonStage"` 19 | SeasonYear int `json:"seasonYear"` 20 | RosterYear int `json:"rosterYear"` 21 | StatsStage int `json:"statsStage"` 22 | StatsYear int `json:"statsYear"` 23 | DisplayYear string `json:"displayYear"` 24 | LastPlayByPlay string `json:"lastPlayByPlay"` 25 | AllPlayByPlay string `json:"allPlayByPlay"` 26 | PlayerMatchup string `json:"playerMatchup"` 27 | Series string `json:"series"` 28 | } `json:"teamSitesOnly"` 29 | SeasonScheduleYear int `json:"seasonScheduleYear"` 30 | ShowPlayoffsClinch bool `json:"showPlayoffsClinch"` 31 | Links struct { 32 | AnchorDate string `json:"anchorDate"` 33 | CurrentDate string `json:"currentDate"` 34 | Calendar string `json:"calendar"` 35 | TodayScoreboard string `json:"todayScoreboard"` 36 | CurrentScoreboard string `json:"currentScoreboard"` 37 | Teams string `json:"teams"` 38 | Scoreboard string `json:"scoreboard"` 39 | LeagueRosterPlayers string `json:"leagueRosterPlayers"` 40 | AllstarRoster string `json:"allstarRoster"` 41 | LeagueRosterCoaches string `json:"leagueRosterCoaches"` 42 | LeagueSchedule string `json:"leagueSchedule"` 43 | LeagueConfStandings string `json:"leagueConfStandings"` 44 | LeagueDivStandings string `json:"leagueDivStandings"` 45 | LeagueUngroupedStandings string `json:"leagueUngroupedStandings"` 46 | LeagueMiniStandings string `json:"leagueMiniStandings"` 47 | LeagueTeamStatsLeaders string `json:"leagueTeamStatsLeaders"` 48 | LeagueLastFiveGameTeamStats string `json:"leagueLastFiveGameTeamStats"` 49 | PreviewArticle string `json:"previewArticle"` 50 | RecapArticle string `json:"recapArticle"` 51 | GameBookPdf string `json:"gameBookPdf"` 52 | Boxscore string `json:"boxscore"` 53 | MiniBoxscore string `json:"miniBoxscore"` 54 | Pbp string `json:"pbp"` 55 | LeadTracker string `json:"leadTracker"` 56 | PlayerGameLog string `json:"playerGameLog"` 57 | PlayerProfile string `json:"playerProfile"` 58 | PlayerUberStats string `json:"playerUberStats"` 59 | TeamSchedule string `json:"teamSchedule"` 60 | TeamsConfig string `json:"teamsConfig"` 61 | TeamRoster string `json:"teamRoster"` 62 | TeamsConfigYear string `json:"teamsConfigYear"` 63 | TeamScheduleYear string `json:"teamScheduleYear"` 64 | TeamLeaders string `json:"teamLeaders"` 65 | TeamScheduleYear2 string `json:"teamScheduleYear2"` 66 | TeamLeaders2 string `json:"teamLeaders2"` 67 | TeamICS string `json:"teamICS"` 68 | TeamICS2 string `json:"teamICS2"` 69 | PlayoffsBracket string `json:"playoffsBracket"` 70 | PlayoffSeriesLeaders string `json:"playoffSeriesLeaders"` 71 | UniversalLinkMapping string `json:"universalLinkMapping"` 72 | TicketLink string `json:"ticketLink"` 73 | } `json:"links"` 74 | } 75 | 76 | type Game struct { 77 | SeasonStageID int 78 | SeasonYear string 79 | LeagueName string 80 | GameID string 81 | Arena struct { 82 | Name string 83 | IsDomestic bool 84 | City string 85 | StateAbbr string 86 | Country string 87 | } 88 | IsGameActivated bool 89 | StatusNum int 90 | ExtendedStatusNum int 91 | StartTimeEastern string 92 | StartTimeUTC time.Time 93 | EndTimeUTC time.Time 94 | StartDateEastern string 95 | HomeStartDate string 96 | HomeStartTime string 97 | VisitorStartDate string 98 | VisitorStartTime string 99 | GameURLCode string 100 | Clock string 101 | IsBuzzerBeater bool 102 | IsPreviewArticleAvail bool 103 | IsRecapArticleAvail bool 104 | Nugget struct { 105 | Text string 106 | } 107 | Attendance string 108 | Tickets struct { 109 | MobileApp string 110 | DesktopWeb string 111 | MobileWeb string 112 | LeagGameInfo string 113 | LeagTix string 114 | } 115 | HasGameBookPdf bool 116 | IsStartTimeTBD bool 117 | IsNeutralVenue bool 118 | GameDuration struct { 119 | Hours string 120 | Minutes string 121 | } 122 | Period struct { 123 | Current int 124 | Type int 125 | MaxRegular int 126 | IsHalftime bool 127 | IsEndOfPeriod bool 128 | } 129 | VTeam struct { 130 | TeamID string 131 | TriCode string 132 | Win string 133 | Loss string 134 | SeriesWin string 135 | SeriesLoss string 136 | Score string 137 | Linescore []struct { 138 | Score string 139 | } 140 | } 141 | HTeam struct { 142 | TeamID string 143 | TriCode string 144 | Win string 145 | Loss string 146 | SeriesWin string 147 | SeriesLoss string 148 | Score string 149 | Linescore []struct { 150 | Score string 151 | } 152 | } 153 | Watch struct { 154 | Broadcast struct { 155 | Broadcasters struct { 156 | National []interface{} 157 | Canadian []struct { 158 | ShortName string 159 | LongName string 160 | } 161 | VTeam []struct { 162 | ShortName string 163 | LongName string 164 | } 165 | HTeam []struct { 166 | ShortName string 167 | LongName string 168 | } 169 | SpanishHTeam []interface{} 170 | SpanishVTeam []interface{} 171 | SpanishNational []interface{} 172 | } 173 | Video struct { 174 | RegionalBlackoutCodes string 175 | CanPurchase bool 176 | IsLeaguePass bool 177 | IsNationalBlackout bool 178 | IsTNTOT bool 179 | IsVR bool 180 | TntotIsOnAir bool 181 | IsNextVR bool 182 | IsNBAOnTNTVR bool 183 | IsMagicLeap bool 184 | IsOculusVenues bool 185 | Streams []struct { 186 | StreamType string 187 | IsOnAir bool 188 | DoesArchiveExist bool 189 | IsArchiveAvailToWatch bool 190 | StreamID string 191 | Duration int 192 | } 193 | DeepLink []interface{} 194 | } 195 | Audio struct { 196 | National struct { 197 | Streams []struct { 198 | Language string 199 | IsOnAir bool 200 | StreamID string 201 | } 202 | Broadcasters []interface{} 203 | } 204 | VTeam struct { 205 | Streams []struct { 206 | Language string 207 | IsOnAir bool 208 | StreamID string 209 | } 210 | Broadcasters []struct { 211 | ShortName string 212 | LongName string 213 | } 214 | } 215 | HTeam struct { 216 | Streams []struct { 217 | Language string 218 | IsOnAir bool 219 | StreamID string 220 | } 221 | Broadcasters []struct { 222 | ShortName string 223 | LongName string 224 | } 225 | } 226 | } 227 | } 228 | } 229 | } 230 | 231 | type Scoreboard struct { 232 | Internal struct { 233 | PubDateTime string `json:"pubDateTime"` 234 | IgorPath string `json:"igorPath"` 235 | RouteName string `json:"routeName"` 236 | RouteValue string `json:"routeValue"` 237 | Xslt string `json:"xslt"` 238 | XsltForceRecompile string `json:"xsltForceRecompile"` 239 | XsltInCache string `json:"xsltInCache"` 240 | XsltCompileTimeMillis string `json:"xsltCompileTimeMillis"` 241 | XsltTransformTimeMillis string `json:"xsltTransformTimeMillis"` 242 | ConsolidatedDomKey string `json:"consolidatedDomKey"` 243 | EndToEndTimeMillis string `json:"endToEndTimeMillis"` 244 | } `json:"_internal"` 245 | NumGames int `json:"numGames"` 246 | Games []Game 247 | } 248 | 249 | type Teams struct { 250 | Internal struct { 251 | PubDateTime string `json:"pubDateTime"` 252 | IgorPath string `json:"igorPath"` 253 | Xslt string `json:"xslt"` 254 | XsltForceRecompile string `json:"xsltForceRecompile"` 255 | XsltInCache string `json:"xsltInCache"` 256 | XsltCompileTimeMillis string `json:"xsltCompileTimeMillis"` 257 | XsltTransformTimeMillis string `json:"xsltTransformTimeMillis"` 258 | ConsolidatedDomKey string `json:"consolidatedDomKey"` 259 | EndToEndTimeMillis string `json:"endToEndTimeMillis"` 260 | } `json:"_internal"` 261 | League struct { 262 | Standard []struct { 263 | IsNBAFranchise bool `json:"isNBAFranchise"` 264 | IsAllStar bool `json:"isAllStar"` 265 | City string `json:"city"` 266 | AltCityName string `json:"altCityName"` 267 | FullName string `json:"fullName"` 268 | Tricode string `json:"tricode"` 269 | TeamID string `json:"teamId"` 270 | Nickname string `json:"nickname"` 271 | URLName string `json:"urlName"` 272 | TeamShortName string `json:"teamShortName"` 273 | ConfName string `json:"confName"` 274 | DivName string `json:"divName"` 275 | } `json:"standard"` 276 | Africa []interface{} `json:"africa"` 277 | Sacramento []interface{} `json:"sacramento"` 278 | Vegas []interface{} `json:"vegas"` 279 | Utah []interface{} `json:"utah"` 280 | } `json:"league"` 281 | } 282 | -------------------------------------------------------------------------------- /nba/teams.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "isNBAFranchise": true, 4 | "isAllStar": false, 5 | "city": "Atlanta", 6 | "altCityName": "Atlanta", 7 | "fullName": "Atlanta Hawks", 8 | "tricode": "ATL", 9 | "teamId": 1610612737, 10 | "nickname": "Hawks", 11 | "urlName": "hawks", 12 | "teamShortName": "Atlanta", 13 | "confName": "East", 14 | "divName": "Southeast" 15 | }, 16 | { 17 | "isNBAFranchise": true, 18 | "isAllStar": false, 19 | "city": "Boston", 20 | "altCityName": "Boston", 21 | "fullName": "Boston Celtics", 22 | "tricode": "BOS", 23 | "teamId": 1610612738, 24 | "nickname": "Celtics", 25 | "urlName": "celtics", 26 | "teamShortName": "Boston", 27 | "confName": "East", 28 | "divName": "Atlantic" 29 | }, 30 | { 31 | "isNBAFranchise": true, 32 | "isAllStar": false, 33 | "city": "Brooklyn", 34 | "altCityName": "Brooklyn", 35 | "fullName": "Brooklyn Nets", 36 | "tricode": "BKN", 37 | "teamId": 1610612751, 38 | "nickname": "Nets", 39 | "urlName": "nets", 40 | "teamShortName": "Brooklyn", 41 | "confName": "East", 42 | "divName": "Atlantic" 43 | }, 44 | { 45 | "isNBAFranchise": true, 46 | "isAllStar": false, 47 | "city": "Charlotte", 48 | "altCityName": "Charlotte", 49 | "fullName": "Charlotte Hornets", 50 | "tricode": "CHA", 51 | "teamId": 1610612766, 52 | "nickname": "Hornets", 53 | "urlName": "hornets", 54 | "teamShortName": "Charlotte", 55 | "confName": "East", 56 | "divName": "Southeast" 57 | }, 58 | { 59 | "isNBAFranchise": true, 60 | "isAllStar": false, 61 | "city": "Chicago", 62 | "altCityName": "Chicago", 63 | "fullName": "Chicago Bulls", 64 | "tricode": "CHI", 65 | "teamId": 1610612741, 66 | "nickname": "Bulls", 67 | "urlName": "bulls", 68 | "teamShortName": "Chicago", 69 | "confName": "East", 70 | "divName": "Central" 71 | }, 72 | { 73 | "isNBAFranchise": true, 74 | "isAllStar": false, 75 | "city": "Cleveland", 76 | "altCityName": "Cleveland", 77 | "fullName": "Cleveland Cavaliers", 78 | "tricode": "CLE", 79 | "teamId": 1610612739, 80 | "nickname": "Cavaliers", 81 | "urlName": "cavaliers", 82 | "teamShortName": "Cleveland", 83 | "confName": "East", 84 | "divName": "Central" 85 | }, 86 | { 87 | "isNBAFranchise": true, 88 | "isAllStar": false, 89 | "city": "Dallas", 90 | "altCityName": "Dallas", 91 | "fullName": "Dallas Mavericks", 92 | "tricode": "DAL", 93 | "teamId": 1610612742, 94 | "nickname": "Mavericks", 95 | "urlName": "mavericks", 96 | "teamShortName": "Dallas", 97 | "confName": "West", 98 | "divName": "Southwest" 99 | }, 100 | { 101 | "isNBAFranchise": true, 102 | "isAllStar": false, 103 | "city": "Denver", 104 | "altCityName": "Denver", 105 | "fullName": "Denver Nuggets", 106 | "tricode": "DEN", 107 | "teamId": 1610612743, 108 | "nickname": "Nuggets", 109 | "urlName": "nuggets", 110 | "teamShortName": "Denver", 111 | "confName": "West", 112 | "divName": "Northwest" 113 | }, 114 | { 115 | "isNBAFranchise": true, 116 | "isAllStar": false, 117 | "city": "Detroit", 118 | "altCityName": "Detroit", 119 | "fullName": "Detroit Pistons", 120 | "tricode": "DET", 121 | "teamId": 1610612765, 122 | "nickname": "Pistons", 123 | "urlName": "pistons", 124 | "teamShortName": "Detroit", 125 | "confName": "East", 126 | "divName": "Central" 127 | }, 128 | { 129 | "isNBAFranchise": true, 130 | "isAllStar": false, 131 | "city": "Golden State", 132 | "altCityName": "Golden State", 133 | "fullName": "Golden State Warriors", 134 | "tricode": "GSW", 135 | "teamId": 1610612744, 136 | "nickname": "Warriors", 137 | "urlName": "warriors", 138 | "teamShortName": "Golden State", 139 | "confName": "West", 140 | "divName": "Pacific" 141 | }, 142 | { 143 | "isNBAFranchise": true, 144 | "isAllStar": false, 145 | "city": "Houston", 146 | "altCityName": "Houston", 147 | "fullName": "Houston Rockets", 148 | "tricode": "HOU", 149 | "teamId": 1610612745, 150 | "nickname": "Rockets", 151 | "urlName": "rockets", 152 | "teamShortName": "Houston", 153 | "confName": "West", 154 | "divName": "Southwest" 155 | }, 156 | { 157 | "isNBAFranchise": true, 158 | "isAllStar": false, 159 | "city": "Indiana", 160 | "altCityName": "Indiana", 161 | "fullName": "Indiana Pacers", 162 | "tricode": "IND", 163 | "teamId": 1610612754, 164 | "nickname": "Pacers", 165 | "urlName": "pacers", 166 | "teamShortName": "Indiana", 167 | "confName": "East", 168 | "divName": "Central" 169 | }, 170 | { 171 | "isNBAFranchise": true, 172 | "isAllStar": false, 173 | "city": "LA", 174 | "altCityName": "LA Clippers", 175 | "fullName": "LA Clippers", 176 | "tricode": "LAC", 177 | "teamId": 1610612746, 178 | "nickname": "Clippers", 179 | "urlName": "clippers", 180 | "teamShortName": "LA Clippers", 181 | "confName": "West", 182 | "divName": "Pacific" 183 | }, 184 | { 185 | "isNBAFranchise": true, 186 | "isAllStar": false, 187 | "city": "Los Angeles", 188 | "altCityName": "Los Angeles Lakers", 189 | "fullName": "Los Angeles Lakers", 190 | "tricode": "LAL", 191 | "teamId": 1610612747, 192 | "nickname": "Lakers", 193 | "urlName": "lakers", 194 | "teamShortName": "L.A. Lakers", 195 | "confName": "West", 196 | "divName": "Pacific" 197 | }, 198 | { 199 | "isNBAFranchise": true, 200 | "isAllStar": false, 201 | "city": "Memphis", 202 | "altCityName": "Memphis", 203 | "fullName": "Memphis Grizzlies", 204 | "tricode": "MEM", 205 | "teamId": 1610612763, 206 | "nickname": "Grizzlies", 207 | "urlName": "grizzlies", 208 | "teamShortName": "Memphis", 209 | "confName": "West", 210 | "divName": "Southwest" 211 | }, 212 | { 213 | "isNBAFranchise": true, 214 | "isAllStar": false, 215 | "city": "Miami", 216 | "altCityName": "Miami", 217 | "fullName": "Miami Heat", 218 | "tricode": "MIA", 219 | "teamId": 1610612748, 220 | "nickname": "Heat", 221 | "urlName": "heat", 222 | "teamShortName": "Miami", 223 | "confName": "East", 224 | "divName": "Southeast" 225 | }, 226 | { 227 | "isNBAFranchise": true, 228 | "isAllStar": false, 229 | "city": "Milwaukee", 230 | "altCityName": "Milwaukee", 231 | "fullName": "Milwaukee Bucks", 232 | "tricode": "MIL", 233 | "teamId": 1610612749, 234 | "nickname": "Bucks", 235 | "urlName": "bucks", 236 | "teamShortName": "Milwaukee", 237 | "confName": "East", 238 | "divName": "Central" 239 | }, 240 | { 241 | "isNBAFranchise": true, 242 | "isAllStar": false, 243 | "city": "Minnesota", 244 | "altCityName": "Minnesota", 245 | "fullName": "Minnesota Timberwolves", 246 | "tricode": "MIN", 247 | "teamId": 1610612750, 248 | "nickname": "Timberwolves", 249 | "urlName": "timberwolves", 250 | "teamShortName": "Minnesota", 251 | "confName": "West", 252 | "divName": "Northwest" 253 | }, 254 | { 255 | "isNBAFranchise": true, 256 | "isAllStar": false, 257 | "city": "New Orleans", 258 | "altCityName": "New Orleans", 259 | "fullName": "New Orleans Pelicans", 260 | "tricode": "NOP", 261 | "teamId": 1610612740, 262 | "nickname": "Pelicans", 263 | "urlName": "pelicans", 264 | "teamShortName": "New Orleans", 265 | "confName": "West", 266 | "divName": "Southwest" 267 | }, 268 | { 269 | "isNBAFranchise": true, 270 | "isAllStar": false, 271 | "city": "New York", 272 | "altCityName": "New York", 273 | "fullName": "New York Knicks", 274 | "tricode": "NYK", 275 | "teamId": 1610612752, 276 | "nickname": "Knicks", 277 | "urlName": "knicks", 278 | "teamShortName": "New York", 279 | "confName": "East", 280 | "divName": "Atlantic" 281 | }, 282 | { 283 | "isNBAFranchise": true, 284 | "isAllStar": false, 285 | "city": "Oklahoma City", 286 | "altCityName": "Oklahoma City", 287 | "fullName": "Oklahoma City Thunder", 288 | "tricode": "OKC", 289 | "teamId": 1610612760, 290 | "nickname": "Thunder", 291 | "urlName": "thunder", 292 | "teamShortName": "Oklahoma City", 293 | "confName": "West", 294 | "divName": "Northwest" 295 | }, 296 | { 297 | "isNBAFranchise": true, 298 | "isAllStar": false, 299 | "city": "Orlando", 300 | "altCityName": "Orlando", 301 | "fullName": "Orlando Magic", 302 | "tricode": "ORL", 303 | "teamId": 1610612753, 304 | "nickname": "Magic", 305 | "urlName": "magic", 306 | "teamShortName": "Orlando", 307 | "confName": "East", 308 | "divName": "Southeast" 309 | }, 310 | { 311 | "isNBAFranchise": true, 312 | "isAllStar": false, 313 | "city": "Philadelphia", 314 | "altCityName": "Philadelphia", 315 | "fullName": "Philadelphia 76ers", 316 | "tricode": "PHI", 317 | "teamId": 1610612755, 318 | "nickname": "76ers", 319 | "urlName": "sixers", 320 | "teamShortName": "Philadelphia", 321 | "confName": "East", 322 | "divName": "Atlantic" 323 | }, 324 | { 325 | "isNBAFranchise": true, 326 | "isAllStar": false, 327 | "city": "Phoenix", 328 | "altCityName": "Phoenix", 329 | "fullName": "Phoenix Suns", 330 | "tricode": "PHX", 331 | "teamId": 1610612756, 332 | "nickname": "Suns", 333 | "urlName": "suns", 334 | "teamShortName": "Phoenix", 335 | "confName": "West", 336 | "divName": "Pacific" 337 | }, 338 | { 339 | "isNBAFranchise": true, 340 | "isAllStar": false, 341 | "city": "Portland", 342 | "altCityName": "Portland", 343 | "fullName": "Portland Trail Blazers", 344 | "tricode": "POR", 345 | "teamId": 1610612757, 346 | "nickname": "Trail Blazers", 347 | "urlName": "blazers", 348 | "teamShortName": "Portland", 349 | "confName": "West", 350 | "divName": "Northwest" 351 | }, 352 | { 353 | "isNBAFranchise": true, 354 | "isAllStar": false, 355 | "city": "Sacramento", 356 | "altCityName": "Sacramento", 357 | "fullName": "Sacramento Kings", 358 | "tricode": "SAC", 359 | "teamId": 1610612758, 360 | "nickname": "Kings", 361 | "urlName": "kings", 362 | "teamShortName": "Sacramento", 363 | "confName": "West", 364 | "divName": "Pacific" 365 | }, 366 | { 367 | "isNBAFranchise": true, 368 | "isAllStar": false, 369 | "city": "San Antonio", 370 | "altCityName": "San Antonio", 371 | "fullName": "San Antonio Spurs", 372 | "tricode": "SAS", 373 | "teamId": 1610612759, 374 | "nickname": "Spurs", 375 | "urlName": "spurs", 376 | "teamShortName": "San Antonio", 377 | "confName": "West", 378 | "divName": "Southwest" 379 | }, 380 | { 381 | "isNBAFranchise": true, 382 | "isAllStar": false, 383 | "city": "Toronto", 384 | "altCityName": "Toronto", 385 | "fullName": "Toronto Raptors", 386 | "tricode": "TOR", 387 | "teamId": 1610612761, 388 | "nickname": "Raptors", 389 | "urlName": "raptors", 390 | "teamShortName": "Toronto", 391 | "confName": "East", 392 | "divName": "Atlantic" 393 | }, 394 | { 395 | "isNBAFranchise": true, 396 | "isAllStar": false, 397 | "city": "Utah", 398 | "altCityName": "Utah", 399 | "fullName": "Utah Jazz", 400 | "tricode": "UTA", 401 | "teamId": 1610612762, 402 | "nickname": "Jazz", 403 | "urlName": "jazz", 404 | "teamShortName": "Utah", 405 | "confName": "West", 406 | "divName": "Northwest" 407 | }, 408 | { 409 | "isNBAFranchise": true, 410 | "isAllStar": false, 411 | "city": "Washington", 412 | "altCityName": "Washington", 413 | "fullName": "Washington Wizards", 414 | "tricode": "WAS", 415 | "teamId": 1610612764, 416 | "nickname": "Wizards", 417 | "urlName": "wizards", 418 | "teamShortName": "Washington", 419 | "confName": "East", 420 | "divName": "Southeast" 421 | } 422 | ] -------------------------------------------------------------------------------- /nag/params/params.go: -------------------------------------------------------------------------------- 1 | package params 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "time" 7 | ) 8 | 9 | type AheadBehind string 10 | 11 | const ( 12 | AheadOrBehind AheadBehind = "Ahead or Behind" 13 | BehindOrTied = "Behind or Tied" 14 | AheadOrTied = "Ahead or Tied" 15 | DefaultAheadBehind = AheadOrBehind 16 | ) 17 | 18 | type ClutchTime string 19 | 20 | const ( 21 | Last5Minutes ClutchTime = "Last 5 Minutes" 22 | Last4Minutes = "Last 4 Minutes" 23 | Last3Minutes = "Last 3 Minutes" 24 | Last2Minutes = "Last 2 Minutes" 25 | Last1Minute = "Last 1 Minute" 26 | Last30Seconds = "Last 30 Seconds" 27 | Last10Seconds = "Last 10 Seconds" 28 | DefaultClutchTime = Last5Minutes 29 | ) 30 | 31 | type Conference string 32 | 33 | const ( 34 | EastConference Conference = "East" 35 | WestConference = "West" 36 | NoneConference = "" 37 | DefaultConference = NoneConference 38 | ) 39 | 40 | // ContextMeasureSimple(_ContextMeasure): 41 | // ContextMeasureDetailed(_ContextMeasure): 42 | 43 | type DefenseCategory string 44 | 45 | const ( 46 | Overall DefenseCategory = "Overall" 47 | Threes = "3 Pointers" 48 | Twos = "2 Pointers" 49 | LessThan6ft = "Less Than 6Ft" 50 | LessThan10ft = "Less Than 10Ft" 51 | GreaterThan15ft = "Greater Than 15Ft" 52 | DefaultDefenseCategory = Overall 53 | ) 54 | 55 | type Direction string 56 | 57 | const ( 58 | Asc Direction = "ASC" 59 | Desc = "DESC" 60 | 61 | DefaultDirection = Asc 62 | ) 63 | 64 | type DistanceRange string 65 | 66 | const ( 67 | Range5ft DistanceRange = "5ft Range" 68 | Range8ft = "8ft Range" 69 | ByZone = "By Zone" 70 | DefaultDistanceRange = ByZone 71 | ) 72 | 73 | type DivisionSimple string 74 | 75 | const ( 76 | Atlantic DivisionSimple = "Atlantic" 77 | Central = "Central" 78 | Northwest = "Northwest" 79 | Pacific = "Pacific" 80 | Southeast = "Southeast" 81 | Southwest = "Southwest" 82 | DefaultDivisionSimple = Atlantic 83 | ) 84 | 85 | type Division string 86 | 87 | const ( 88 | EastDivision Division = "East" 89 | WestDivision = "West" 90 | DefaultDivision = EastDivision 91 | ) 92 | 93 | type GameScopeSimple string 94 | 95 | const ( 96 | Last10 GameScopeSimple = "Last 10" 97 | Yesterday = "Yesterday" 98 | DefaultGameScopeSimple = Last10 99 | ) 100 | 101 | // GameScopeDetailed(GameScopeSimple): 102 | type GameScopeDetailed string 103 | 104 | const ( 105 | GameScopeDetailedSeason GameScopeDetailed = "Season" 106 | GameScopeDetailedFinals = "Finals" 107 | DefaultGameScopeDetailed = GameScopeDetailedSeason 108 | ) 109 | 110 | type GameSegment string 111 | 112 | const ( 113 | FirstHalf GameSegment = "First Half" 114 | SecondHalf = "Second Half" 115 | Overtime = "Overtime" 116 | DefaultGameSegment = FirstHalf 117 | ) 118 | 119 | var ( 120 | GroupQuantity = func(i int) string { return strconv.Itoa(i) } 121 | DefaultGroupQuantity = "5" 122 | ) 123 | 124 | var ( 125 | LastNGames = func(i int) string { return strconv.Itoa(i) } 126 | DefaultLastNGames = "0" 127 | ) 128 | 129 | type leagueID struct{} 130 | 131 | var LeagueID = leagueID{} 132 | 133 | func (leagueID) NBA() string { return "00" } 134 | func (leagueID) ABA() string { return "01" } 135 | func (leagueID) WNBA() string { return "10" } 136 | func (leagueID) GLeague() string { return "20" } 137 | func (l leagueID) Default() string { return l.NBA() } 138 | 139 | type Location string 140 | 141 | const ( 142 | Home Location = "Home" 143 | Road = "Road" 144 | DefaultLocation = Home 145 | ) 146 | 147 | type MeasureTypeBase string 148 | 149 | const ( 150 | Base MeasureTypeBase = "Base" 151 | DefaultMeasureTypeBase = Base 152 | ) 153 | 154 | // MeasureTypeSimple(MeasureTypeBase): 155 | // MeasureTypePlayerGameLogs(MeasureTypeBase): 156 | 157 | var ( 158 | NumberOfGames = func(i int) string { return strconv.Itoa(i) } 159 | DefaultNumberOfGames = "2147483647" 160 | ) 161 | 162 | type Outcome string 163 | 164 | const ( 165 | Win Outcome = "W" 166 | Loss = "L" 167 | DefaultOutcome = Win 168 | ) 169 | 170 | // PaceAdjust(_YesNo): 171 | // PaceAdjustNo(_No): 172 | // PlusMinus(_YesNo): 173 | // PlusMinusNo(_No): 174 | 175 | type period struct{} 176 | 177 | var Period = period{} 178 | 179 | func (period) All() string { return "0" } 180 | func (period) First() string { return "1" } 181 | func (period) Second() string { return "2" } 182 | func (period) Third() string { return "3" } 183 | func (period) Fourth() string { return "4" } 184 | func (period) Quarter(i int) string { return strconv.Itoa(i) } 185 | func (period) Overtime(i int) string { return strconv.Itoa(4 + i) } 186 | func (p period) Default() string { return p.All() } 187 | 188 | type PerMode string 189 | 190 | const ( 191 | Totals PerMode = "Totals" 192 | PerGame = "PerGame" 193 | DefaultPerMode = Totals 194 | ) 195 | 196 | // PerMode36(PerModeSimple): 197 | // PerMode48(PerModeSimple): 198 | // PerModeTime(PerMode36, PerMode48): 199 | // PerModeDetailed(PerModeTime): 200 | 201 | type PlayerExperience string 202 | 203 | const ( 204 | Rookie PlayerExperience = "Rookie" 205 | Sophomore = "Sophomore" 206 | Veteran = "Veteran" 207 | DefaultPlayerExperience = Rookie 208 | ) 209 | 210 | type PlayerOrTeam string 211 | 212 | const ( 213 | Player PlayerOrTeam = "Player" 214 | Team = "Team" 215 | DefaultPlayerOrTeam = Team 216 | ) 217 | 218 | type PlayerOrTeamAbbreviation string 219 | 220 | const ( 221 | P PlayerOrTeamAbbreviation = "P" 222 | T = "T" 223 | DefaultPlayerOrTeamAbbreviation = Team 224 | ) 225 | 226 | type PlayerPosition string 227 | 228 | const ( 229 | Guard PlayerPosition = "Guard" 230 | Forward = "Forward" 231 | Center = "Center" 232 | DefaultPlayerPosition = Guard 233 | ) 234 | 235 | type PlayerPositionAbbreviation string 236 | 237 | const ( 238 | G PlayerPositionAbbreviation = "G" 239 | F = "F" 240 | C = "C" 241 | GF = "G-F" 242 | FG = "F-G" 243 | FC = "F-C" 244 | CF = "C-F" 245 | DefaultPlayerPositionAbbreviation = G 246 | ) 247 | 248 | type PlayerScope string 249 | 250 | const ( 251 | AllPlayers PlayerScope = "All Players" 252 | Rookies = "Rookies" 253 | DefaultPlayerScope = AllPlayers 254 | ) 255 | 256 | // TodaysPlayers(_YesNo): 257 | // ActivePlayers(_YesNo): 258 | 259 | type PlayType string 260 | 261 | const ( 262 | Transition PlayType = "Transition" 263 | Isolation = "Isolation" 264 | PRBallHandler = "PRBallHandler" 265 | PRRollMan = "PRRollman" 266 | PostUp = "Postup" 267 | SpotUp = "Spotup" 268 | Handoff = "Handoff" 269 | Cut = "Cut" 270 | OffScreen = "OffScreen" 271 | Putbacks = "OffRebound" 272 | Misc = "Misc" 273 | DefaultPlayType = Transition 274 | ) 275 | 276 | var ( 277 | PointDiff = func(i int) string { return strconv.Itoa(i) } 278 | DefaultPointDiff = "5" 279 | ) 280 | 281 | type PtMeasureType string 282 | 283 | const ( 284 | SpeedDistance PtMeasureType = "SpeedDistance" 285 | Rebounding = "Rebounding" 286 | Possessions = "Possessions" 287 | CatchShoot = "CatchShoot" 288 | PullUpShot = "PullUpShot" 289 | Defense = "Defense" 290 | Drives = "Drives" 291 | Passing = "Passing" 292 | ElbowTouch = "ElbowTouch" 293 | PostTouch = "PostTouch" 294 | PaintTouch = "PaintTouch" 295 | Efficiency = "Efficiency" 296 | DefaultPtMeasureType = SpeedDistance 297 | ) 298 | 299 | const ( 300 | DefaultStartRange = "0" 301 | DefaultEndRange = "0" 302 | DefaultRangeType = "0" 303 | ) 304 | 305 | // Rank(_YesNo): 306 | // RankNo(_No): 307 | 308 | type RunType string 309 | 310 | const DefaultRunType RunType = "each second" 311 | 312 | type Scope string 313 | 314 | const ( 315 | RSScope Scope = "RS" 316 | SScope = "S" 317 | RookiesScope = "Rookies" 318 | DefaultScope = SScope 319 | ) 320 | 321 | // SeasonYear: 322 | var ( 323 | Season = func(t time.Time) string { 324 | cur := t.Year() 325 | if t.Month() <= 9 { 326 | cur = cur - 1 327 | } 328 | nxt := strconv.Itoa(cur + 1)[2:] 329 | return fmt.Sprintf("%d-%s", cur, nxt) 330 | } 331 | CurrentSeason = Season(time.Now()) 332 | ) 333 | 334 | // SeasonAll(Season): 335 | // SeasonAll_Time(Season): 336 | // SeasonAllTime(Season): 337 | // SeasonID(SeasonYear): 338 | 339 | type SeasonType string 340 | 341 | const ( 342 | Regular SeasonType = "Regular Season" 343 | PreSeason = "Pre Season" 344 | Playoffs = "Playoff" 345 | AllStar = "All Star" 346 | DefaultSeasonType = Regular 347 | ) 348 | 349 | type SeasonSegment string 350 | 351 | const ( 352 | PostAllStar SeasonSegment = "Post All-Star" 353 | PreAllStar = "Pre All-Star" 354 | DefaultSeasonSegment = PostAllStar 355 | ) 356 | 357 | type ShotClockRange string 358 | 359 | const ( 360 | Range2224 ShotClockRange = "24-22" 361 | Range1822 = "22-18 Very Early" 362 | Range1518 = "18-15 Early" 363 | Range715 = "15-7 Average" 364 | Range47 = "7-4 Late" 365 | Range04 = "4-0 Very Late" 366 | ShotClockOff = "ShotClock Off" 367 | Empty = "" 368 | DefaultShotClockRange = ShotClockOff 369 | ) 370 | 371 | /* 372 | func CalculateRange(i int64) ShotClockRange { 373 | switch { 374 | case i > 24, i <= 0: 375 | return Empty 376 | case 22 < i <= 24: 377 | return Range2224 378 | case 18 < i <= 22: 379 | return Range1822 380 | case 15 < i <= 18: 381 | return Range1518 382 | case 7 < i <= 15: 383 | return Range715 384 | case 4 < i <= 7: 385 | return Range47 386 | case 0 < i <= 4: 387 | return Range04 388 | } 389 | } 390 | */ 391 | 392 | type Sorter string 393 | 394 | const ( 395 | SorterFGM Sorter = "FGM" 396 | SorterFGA = "FGA" 397 | SorterFG_PCT = "FG_PCT" 398 | SorterFG3M = "FG3M" 399 | SorterFG3A = "FG3A" 400 | SorterFG3_PCT = "FG3_PCT" 401 | SorterFTM = "FTM" 402 | SorterFTA = "FTA" 403 | SorterFT_PCT = "FT_PCT" 404 | SorterOREB = "OREB" 405 | SorterDREB = "DREB" 406 | SorterAST = "AST" 407 | SorterSTL = "STL" 408 | SorterBLK = "BLK" 409 | SorterTOV = "TOV" 410 | SorterREB = "REB" 411 | SorterPTS = "PTS" 412 | SorterDate = "DATE" 413 | DefaultSorter = SorterDate 414 | ) 415 | 416 | type StarterBench string 417 | 418 | const ( 419 | Starters StarterBench = "Starters" 420 | Bench = "Bench" 421 | DefaultStarterBench = Starters 422 | ) 423 | 424 | type Stat string 425 | 426 | const ( 427 | Points Stat = "PTS" 428 | Rebounds = "REB" 429 | Assists = "AST" 430 | FieldGoalPercent = "FG_PCT" 431 | FreeThrowPercent = "FT_PCT" 432 | ThreesPercent = "FG3_PCT" 433 | Steals = "STL" 434 | Blocks = "BLK" 435 | DefaultStat = Points 436 | ) 437 | 438 | type StatCategory string 439 | 440 | const ( 441 | StatCategoryPoints StatCategory = "Points" 442 | StatCategoryRebounds = "Rebounds" 443 | StatCategoryAssists = "Assists" 444 | StatCategoryDefense = "Defense" 445 | StatCategoryClutch = "Clutch" 446 | StatCategoryPlaymaking = "Playmaking" 447 | StatCategoryEfficiency = "Efficiency" 448 | StatCategoryFastBreak = "Fast Break" 449 | StatCategoryScoringBreakdown = "Scoring Breakdown" 450 | DefaultStatCategory = StatCategoryPoints 451 | ) 452 | 453 | type StatCategoryAbbreviation string 454 | 455 | const ( 456 | PTS StatCategoryAbbreviation = "PTS" 457 | FGM = "FGM" 458 | FGA = "FGA" 459 | FG_PCT = "FG_PCT" 460 | FG3M = "FG3M" 461 | FG3A = "FG3A" 462 | FG3_PCT = "FG3_PCT" 463 | FTM = "FTM" 464 | FTA = "FTA" 465 | OREB = "OREB" 466 | DREB = "DREB" 467 | AST = "AST" 468 | STL = "STL" 469 | BLK = "BLK" 470 | TOV = "TOV" 471 | REB = "REB" 472 | DefaultStatCategoryAbbreviation = PTS 473 | ) 474 | 475 | type StatType string 476 | 477 | const ( 478 | Traditional StatType = "Traditional" 479 | Advanced = "Advanced" 480 | Tracking = "Tracking" 481 | DefaultStatType = Traditional 482 | ) 483 | 484 | type TypeGrouping string 485 | 486 | const ( 487 | Offensive TypeGrouping = "offensive" 488 | Defensive = "defensive" 489 | DefaultTypeGrouping = Offensive 490 | ) 491 | -------------------------------------------------------------------------------- /nag/interfaces.go: -------------------------------------------------------------------------------- 1 | package nag 2 | 3 | type BoxScoreTraditionalResponse struct { 4 | PlayerStats []Stat `mapstructure:"PlayerStats"` 5 | TeamStats []Stat `mapstructure:"TeamStats"` 6 | TeamStarterBenchStats []Stat `mapstructure:"TeamStarterBenchStats"` 7 | } 8 | 9 | type LeagueStandingsResponse struct { 10 | Standings []Standing `mapstructure:"Standings"` 11 | } 12 | 13 | type Standing struct { 14 | LeagueID string `mapstructure:"LeagueID"` 15 | SeasonID string `mapstructure:"SeasonID"` 16 | TeamID int64 `mapstructure:"TeamID"` 17 | TeamCity string `mapstructure:"TeamCity"` 18 | TeamName string `mapstructure:"TeamName"` 19 | TeamSlug string `mapstructure:"TeamSlug"` 20 | Conference Conference `mapstructure:"Conference"` 21 | ConferenceRecord string `mapstructure:"ConferenceRecord"` 22 | PlayoffRank int64 `mapstructure:"PlayoffRank"` 23 | ClinchIndicator string `mapstructure:"ClinchIndicator"` 24 | Division string `mapstructure:"Division"` 25 | DivisionRecord string `mapstructure:"DivisionRecord"` 26 | DivisionRank int64 `mapstructure:"DivisionRank"` 27 | WINS int64 `mapstructure:"WINS"` 28 | Losses int64 `mapstructure:"LOSSES"` 29 | WinPCT float64 `mapstructure:"WinPCT"` 30 | LeagueRank int64 `mapstructure:"LeagueRank"` 31 | Record string `mapstructure:"Record"` 32 | Home string `mapstructure:"HOME"` 33 | Road string `mapstructure:"ROAD"` 34 | L10 string `mapstructure:"L10"` 35 | Last10Home string `mapstructure:"Last10Home"` 36 | Last10Road string `mapstructure:"Last10Road"` 37 | Ot string `mapstructure:"OT"` 38 | ThreePTSOrLess string `mapstructure:"ThreePTSOrLess"` 39 | TenPTSOrMore string `mapstructure:"TenPTSOrMore"` 40 | LongHomeStreak int64 `mapstructure:"LongHomeStreak"` 41 | StrLongHomeStreak string `mapstructure:"strLongHomeStreak"` 42 | LongRoadStreak int64 `mapstructure:"LongRoadStreak"` 43 | StrLongRoadStreak string `mapstructure:"strLongRoadStreak"` 44 | LongWinStreak int64 `mapstructure:"LongWinStreak"` 45 | LongLossStreak int64 `mapstructure:"LongLossStreak"` 46 | CurrentHomeStreak int64 `mapstructure:"CurrentHomeStreak"` 47 | StrCurrentHomeStreak string `mapstructure:"strCurrentHomeStreak"` 48 | CurrentRoadStreak int64 `mapstructure:"CurrentRoadStreak"` 49 | StrCurrentRoadStreak string `mapstructure:"strCurrentRoadStreak"` 50 | CurrentStreak int64 `mapstructure:"CurrentStreak"` 51 | StrCurrentStreak string `mapstructure:"strCurrentStreak"` 52 | ConferenceGamesBack float64 `mapstructure:"ConferenceGamesBack"` 53 | DivisionGamesBack float64 `mapstructure:"DivisionGamesBack"` 54 | ClinchedConferenceTitle int64 `mapstructure:"ClinchedConferenceTitle"` 55 | ClinchedDivisionTitle int64 `mapstructure:"ClinchedDivisionTitle"` 56 | ClinchedPlayoffBirth int64 `mapstructure:"ClinchedPlayoffBirth"` 57 | ClinchedPlayIn int64 `mapstructure:"ClinchedPlayIn"` 58 | EliminatedConference int64 `mapstructure:"EliminatedConference"` 59 | EliminatedDivision int64 `mapstructure:"EliminatedDivision"` 60 | AheadAtHalf string `mapstructure:"AheadAtHalf"` 61 | BehindAtHalf string `mapstructure:"BehindAtHalf"` 62 | TiedAtHalf string `mapstructure:"TiedAtHalf"` 63 | AheadAtThird string `mapstructure:"AheadAtThird"` 64 | BehindAtThird string `mapstructure:"BehindAtThird"` 65 | TiedAtThird string `mapstructure:"TiedAtThird"` 66 | Score100PTS string `mapstructure:"Score100PTS"` 67 | OppScore100PTS string `mapstructure:"OppScore100PTS"` 68 | OppOver500 string `mapstructure:"OppOver500"` 69 | LeadInFGPCT string `mapstructure:"LeadInFGPCT"` 70 | LeadInReb string `mapstructure:"LeadInReb"` 71 | FewerTurnovers string `mapstructure:"FewerTurnovers"` 72 | PointsPG float64 `mapstructure:"PointsPG"` 73 | OppPointsPG float64 `mapstructure:"OppPointsPG"` 74 | DiffPointsPG float64 `mapstructure:"DiffPointsPG"` 75 | VsEast string `mapstructure:"vsEast"` 76 | VsAtlantic string `mapstructure:"vsAtlantic"` 77 | VsCentral string `mapstructure:"vsCentral"` 78 | VsSoutheast string `mapstructure:"vsSoutheast"` 79 | VsWest string `mapstructure:"vsWest"` 80 | VsNorthwest string `mapstructure:"vsNorthwest"` 81 | VsPacific string `mapstructure:"vsPacific"` 82 | VsSouthwest string `mapstructure:"vsSouthwest"` 83 | Jan interface{} `mapstructure:"Jan"` 84 | Feb interface{} `mapstructure:"Feb"` 85 | Mar interface{} `mapstructure:"Mar"` 86 | APR interface{} `mapstructure:"Apr"` 87 | May interface{} `mapstructure:"May"` 88 | Jun interface{} `mapstructure:"Jun"` 89 | Jul interface{} `mapstructure:"Jul"` 90 | Aug interface{} `mapstructure:"Aug"` 91 | Sep interface{} `mapstructure:"Sep"` 92 | Oct string `mapstructure:"Oct"` 93 | Nov string `mapstructure:"Nov"` 94 | DEC string `mapstructure:"Dec"` 95 | Score80_Plus string `mapstructure:"Score_80_Plus"` 96 | OppScore80_Plus string `mapstructure:"Opp_Score_80_Plus"` 97 | ScoreBelow80 string `mapstructure:"Score_Below_80"` 98 | OppScoreBelow80 string `mapstructure:"Opp_Score_Below_80"` 99 | TotalPoints int64 `mapstructure:"TotalPoints"` 100 | OppTotalPoints int64 `mapstructure:"OppTotalPoints"` 101 | DiffTotalPoints int64 `mapstructure:"DiffTotalPoints"` 102 | } 103 | 104 | type Stat struct { 105 | GameID string `mapstructure:"GAME_ID"` 106 | TeamID int64 `mapstructure:"TEAM_ID"` 107 | TeamAbbreviation string `mapstructure:"TEAM_ABBREVIATION"` 108 | TeamCity string `mapstructure:"TEAM_CITY"` 109 | PlayerID int64 `mapstructure:"PLAYER_ID,omitempty"` 110 | PlayerName string `mapstructure:"PLAYER_NAME,omitempty"` 111 | Nickname string `mapstructure:"NICKNAME,omitempty"` 112 | StartPosition string `mapstructure:"START_POSITION,omitempty"` 113 | Comment string `mapstructure:"COMMENT,omitempty"` 114 | Min string `mapstructure:"MIN"` 115 | Fgm int64 `mapstructure:"FGM"` 116 | Fga int64 `mapstructure:"FGA"` 117 | FgPct float64 `mapstructure:"FG_PCT"` 118 | Fg3M int64 `mapstructure:"FG3M"` 119 | Fg3A int64 `mapstructure:"FG3A"` 120 | Fg3Pct float64 `mapstructure:"FG3_PCT"` 121 | Ftm int64 `mapstructure:"FTM"` 122 | Fta int64 `mapstructure:"FTA"` 123 | FtPct float64 `mapstructure:"FT_PCT"` 124 | Oreb int64 `mapstructure:"OREB"` 125 | Dreb int64 `mapstructure:"DREB"` 126 | Reb int64 `mapstructure:"REB"` 127 | AST int64 `mapstructure:"AST"` 128 | Stl int64 `mapstructure:"STL"` 129 | Blk int64 `mapstructure:"BLK"` 130 | To int64 `mapstructure:"TO"` 131 | Pf int64 `mapstructure:"PF"` 132 | Pts int64 `mapstructure:"PTS"` 133 | PlusMinus int64 `mapstructure:"PLUS_MINUS"` 134 | TeamName string `mapstructure:"TEAM_NAME,omitempty"` 135 | StartersBench string `mapstructure:"STARTERS_BENCH,omitempty"` 136 | } 137 | 138 | type NewBoxScoreAdvancedResponse struct { 139 | PlayerStats []AdvStat `mapstructure:"PlayerStats"` 140 | TeamStats []AdvStat `mapstructure:"TeamStats"` 141 | } 142 | 143 | type AdvStat struct { 144 | GameID string `mapstructure:"GAME_ID"` 145 | TeamID int64 `mapstructure:"TEAM_ID"` 146 | TeamAbbreviation string `mapstructure:"TEAM_ABBREVIATION"` 147 | TeamCity string `mapstructure:"TEAM_CITY"` 148 | PlayerID int64 `mapstructure:"PLAYER_ID,omitempty"` 149 | PlayerName string `mapstructure:"PLAYER_NAME,omitempty"` 150 | Nickname string `mapstructure:"NICKNAME,omitempty"` 151 | StartPosition string `mapstructure:"START_POSITION,omitempty"` 152 | Comment string `mapstructure:"COMMENT,omitempty"` 153 | Min string `mapstructure:"MIN"` 154 | EOffRating float64 `mapstructure:"E_OFF_RATING"` 155 | OffRating float64 `mapstructure:"OFF_RATING"` 156 | EDefRating float64 `mapstructure:"E_DEF_RATING"` 157 | DefRating float64 `mapstructure:"DEF_RATING"` 158 | ENetRating float64 `mapstructure:"E_NET_RATING"` 159 | NetRating float64 `mapstructure:"NET_RATING"` 160 | ASTPct float64 `mapstructure:"AST_PCT"` 161 | ASTTov float64 `mapstructure:"AST_TOV"` 162 | ASTRatio float64 `mapstructure:"AST_RATIO"` 163 | OrebPct float64 `mapstructure:"OREB_PCT"` 164 | DrebPct float64 `mapstructure:"DREB_PCT"` 165 | RebPct float64 `mapstructure:"REB_PCT"` 166 | TmTovPct float64 `mapstructure:"TM_TOV_PCT"` 167 | EfgPct float64 `mapstructure:"EFG_PCT"` 168 | TsPct float64 `mapstructure:"TS_PCT"` 169 | UsgPct float64 `mapstructure:"USG_PCT"` 170 | EUsgPct float64 `mapstructure:"E_USG_PCT"` 171 | EPace float64 `mapstructure:"E_PACE"` 172 | Pace float64 `mapstructure:"PACE"` 173 | PacePer40 float64 `mapstructure:"PACE_PER40"` 174 | Poss int64 `mapstructure:"POSS"` 175 | Pie float64 `mapstructure:"PIE"` 176 | TeamName string `mapstructure:"TEAM_NAME,omitempty"` 177 | ETmTovPct float64 `mapstructure:"E_TM_TOV_PCT,omitempty"` 178 | } 179 | 180 | type BoxScoreSummaryResponse struct { 181 | GameSummary []GameSummary `mapstructure:"GameSummary"` 182 | OtherStats []OtherStat `mapstructure:"OtherStats"` 183 | Officials []Official `mapstructure:"Officials"` 184 | InactivePlayers []InactivePlayer `mapstructure:"InactivePlayers"` 185 | GameInfo []GameInfo `mapstructure:"GameInfo"` 186 | LineScore []LineScore `mapstructure:"LineScore"` 187 | LastMeeting []LastMeeting `mapstructure:"LastMeeting"` 188 | SeasonSeries []SeasonSery `mapstructure:"SeasonSeries"` 189 | AvailableVideo []AvailableVideo `mapstructure:"AvailableVideo"` 190 | } 191 | 192 | type AvailableVideo struct { 193 | GameID string `mapstructure:"GAME_ID"` 194 | VideoAvailableFlag int64 `mapstructure:"VIDEO_AVAILABLE_FLAG"` 195 | PtAvailable int64 `mapstructure:"PT_AVAILABLE"` 196 | PtXyzAvailable int64 `mapstructure:"PT_XYZ_AVAILABLE"` 197 | WhStatus int64 `mapstructure:"WH_STATUS"` 198 | HustleStatus int64 `mapstructure:"HUSTLE_STATUS"` 199 | HistoricalStatus int64 `mapstructure:"HISTORICAL_STATUS"` 200 | } 201 | 202 | type GameInfo struct { 203 | GameDate string `mapstructure:"GAME_DATE"` 204 | Attendance int64 `mapstructure:"ATTENDANCE"` 205 | GameTime string `mapstructure:"GAME_TIME"` 206 | } 207 | 208 | type GameSummary struct { 209 | GameDateEst string `mapstructure:"GAME_DATE_EST"` 210 | GameSequence int64 `mapstructure:"GAME_SEQUENCE"` 211 | GameID string `mapstructure:"GAME_ID"` 212 | GameStatusID int64 `mapstructure:"GAME_STATUS_ID"` 213 | GameStatusText string `mapstructure:"GAME_STATUS_TEXT"` 214 | Gamecode string `mapstructure:"GAMECODE"` 215 | HomeTeamID int64 `mapstructure:"HOME_TEAM_ID"` 216 | VisitorTeamID int64 `mapstructure:"VISITOR_TEAM_ID"` 217 | Season string `mapstructure:"SEASON"` 218 | LivePeriod int64 `mapstructure:"LIVE_PERIOD"` 219 | LivePCTime string `mapstructure:"LIVE_PC_TIME"` 220 | NatlTvBroadcasterAbbreviation interface{} `mapstructure:"NATL_TV_BROADCASTER_ABBREVIATION"` 221 | LivePeriodTimeBcast string `mapstructure:"LIVE_PERIOD_TIME_BCAST"` 222 | WhStatus int64 `mapstructure:"WH_STATUS"` 223 | } 224 | 225 | type InactivePlayer struct { 226 | PlayerID int64 `mapstructure:"PLAYER_ID"` 227 | FirstName string `mapstructure:"FIRST_NAME"` 228 | LastName string `mapstructure:"LAST_NAME"` 229 | JerseyNum string `mapstructure:"JERSEY_NUM"` 230 | TeamID int64 `mapstructure:"TEAM_ID"` 231 | TeamCity string `mapstructure:"TEAM_CITY"` 232 | TeamName string `mapstructure:"TEAM_NAME"` 233 | TeamAbbreviation string `mapstructure:"TEAM_ABBREVIATION"` 234 | } 235 | 236 | type Official struct { 237 | OfficialID int64 `mapstructure:"OFFICIAL_ID"` 238 | FirstName string `mapstructure:"FIRST_NAME"` 239 | LastName string `mapstructure:"LAST_NAME"` 240 | JerseyNum string `mapstructure:"JERSEY_NUM"` 241 | } 242 | 243 | type OtherStat struct { 244 | LeagueID string `mapstructure:"LEAGUE_ID"` 245 | TeamID int64 `mapstructure:"TEAM_ID"` 246 | TeamAbbreviation string `mapstructure:"TEAM_ABBREVIATION"` 247 | TeamCity string `mapstructure:"TEAM_CITY"` 248 | PtsPaint int64 `mapstructure:"PTS_PAINT"` 249 | Pts2NdChance int64 `mapstructure:"PTS_2ND_CHANCE"` 250 | PtsFb int64 `mapstructure:"PTS_FB"` 251 | LargestLead int64 `mapstructure:"LARGEST_LEAD"` 252 | LeadChanges int64 `mapstructure:"LEAD_CHANGES"` 253 | TimesTied int64 `mapstructure:"TIMES_TIED"` 254 | TeamTurnovers int64 `mapstructure:"TEAM_TURNOVERS"` 255 | TotalTurnovers int64 `mapstructure:"TOTAL_TURNOVERS"` 256 | TeamRebounds int64 `mapstructure:"TEAM_REBOUNDS"` 257 | PtsOffTo int64 `mapstructure:"PTS_OFF_TO"` 258 | } 259 | 260 | type SeasonSery struct { 261 | GameID string `mapstructure:"GAME_ID"` 262 | } 263 | 264 | type ScoreBoardResponse struct { 265 | Available []Available `mapstructure:"Available"` 266 | EastConfStandingsByDay []StConfStandingsByDay `mapstructure:"EastConfStandingsByDay"` 267 | GameHeader []GameHeader `mapstructure:"GameHeader"` 268 | LastMeeting []LastMeeting `mapstructure:"LastMeeting"` 269 | LineScore []LineScore `mapstructure:"LineScore"` 270 | SeriesStandings interface{} `mapstructure:"SeriesStandings"` 271 | TeamLeaders interface{} `mapstructure:"TeamLeaders"` 272 | TicketLinks interface{} `mapstructure:"TicketLinks"` 273 | WestConfStandingsByDay []StConfStandingsByDay `mapstructure:"WestConfStandingsByDay"` 274 | WinProbability interface{} `mapstructure:"WinProbability"` 275 | } 276 | 277 | type Available struct { 278 | GameID string `mapstructure:"GAME_ID"` 279 | PtAvailable int64 `mapstructure:"PT_AVAILABLE"` 280 | } 281 | 282 | type StConfStandingsByDay struct { 283 | Conference Conference `mapstructure:"CONFERENCE"` 284 | G int64 `mapstructure:"G"` 285 | HomeRecord string `mapstructure:"HOME_RECORD"` 286 | L int64 `mapstructure:"L"` 287 | LeagueID string `mapstructure:"LEAGUE_ID"` 288 | RoadRecord string `mapstructure:"ROAD_RECORD"` 289 | SeasonID string `mapstructure:"SEASON_ID"` 290 | Standingsdate Standingsdate `mapstructure:"STANDINGSDATE"` 291 | Team string `mapstructure:"TEAM"` 292 | TeamID int64 `mapstructure:"TEAM_ID"` 293 | W int64 `mapstructure:"W"` 294 | WPct float64 `mapstructure:"W_PCT"` 295 | } 296 | 297 | type GameHeader struct { 298 | ArenaName string `mapstructure:"ARENA_NAME"` 299 | AwayTvBroadcasterAbbreviation string `mapstructure:"AWAY_TV_BROADCASTER_ABBREVIATION"` 300 | Gamecode string `mapstructure:"GAMECODE"` 301 | GameDateEst string `mapstructure:"GAME_DATE_EST"` 302 | GameID string `mapstructure:"GAME_ID"` 303 | GameSequence int64 `mapstructure:"GAME_SEQUENCE"` 304 | GameStatusID int64 `mapstructure:"GAME_STATUS_ID"` 305 | GameStatusText string `mapstructure:"GAME_STATUS_TEXT"` 306 | HomeTeamID int64 `mapstructure:"HOME_TEAM_ID"` 307 | HomeTvBroadcasterAbbreviation string `mapstructure:"HOME_TV_BROADCASTER_ABBREVIATION"` 308 | LivePCTime string `mapstructure:"LIVE_PC_TIME"` 309 | LivePeriod int64 `mapstructure:"LIVE_PERIOD"` 310 | LivePeriodTimeBcast string `mapstructure:"LIVE_PERIOD_TIME_BCAST"` 311 | NatlTvBroadcasterAbbreviation interface{} `mapstructure:"NATL_TV_BROADCASTER_ABBREVIATION"` 312 | Season string `mapstructure:"SEASON"` 313 | VisitorTeamID int64 `mapstructure:"VISITOR_TEAM_ID"` 314 | WhStatus int64 `mapstructure:"WH_STATUS"` 315 | WnbaCommissionerFlag int64 `mapstructure:"WNBA_COMMISSIONER_FLAG"` 316 | } 317 | 318 | type LastMeeting struct { 319 | GameID string `mapstructure:"GAME_ID"` 320 | LastGameDateEst string `mapstructure:"LAST_GAME_DATE_EST"` 321 | LastGameHomeTeamAbbreviation string `mapstructure:"LAST_GAME_HOME_TEAM_ABBREVIATION"` 322 | LastGameHomeTeamCity string `mapstructure:"LAST_GAME_HOME_TEAM_CITY"` 323 | LastGameHomeTeamID int64 `mapstructure:"LAST_GAME_HOME_TEAM_ID"` 324 | LastGameHomeTeamName string `mapstructure:"LAST_GAME_HOME_TEAM_NAME"` 325 | LastGameHomeTeamPoints int64 `mapstructure:"LAST_GAME_HOME_TEAM_POINTS"` 326 | LastGameID string `mapstructure:"LAST_GAME_ID"` 327 | LastGameVisitorTeamCity string `mapstructure:"LAST_GAME_VISITOR_TEAM_CITY"` 328 | LastGameVisitorTeamCity1 string `mapstructure:"LAST_GAME_VISITOR_TEAM_CITY1"` 329 | LastGameVisitorTeamID int64 `mapstructure:"LAST_GAME_VISITOR_TEAM_ID"` 330 | LastGameVisitorTeamName string `mapstructure:"LAST_GAME_VISITOR_TEAM_NAME"` 331 | LastGameVisitorTeamPoints int64 `mapstructure:"LAST_GAME_VISITOR_TEAM_POINTS"` 332 | } 333 | 334 | type LineScore struct { 335 | AST interface{} `mapstructure:"AST"` 336 | Fg3Pct interface{} `mapstructure:"FG3_PCT"` 337 | FgPct interface{} `mapstructure:"FG_PCT"` 338 | FtPct interface{} `mapstructure:"FT_PCT"` 339 | GameDateEst string `mapstructure:"GAME_DATE_EST"` 340 | GameID string `mapstructure:"GAME_ID"` 341 | GameSequence int64 `mapstructure:"GAME_SEQUENCE"` 342 | Pts int `mapstructure:"PTS"` 343 | PtsOt1 interface{} `mapstructure:"PTS_OT1"` 344 | PtsOt10 interface{} `mapstructure:"PTS_OT10"` 345 | PtsOt2 interface{} `mapstructure:"PTS_OT2"` 346 | PtsOt3 interface{} `mapstructure:"PTS_OT3"` 347 | PtsOt4 interface{} `mapstructure:"PTS_OT4"` 348 | PtsOt5 interface{} `mapstructure:"PTS_OT5"` 349 | PtsOt6 interface{} `mapstructure:"PTS_OT6"` 350 | PtsOt7 interface{} `mapstructure:"PTS_OT7"` 351 | PtsOt8 interface{} `mapstructure:"PTS_OT8"` 352 | PtsOt9 interface{} `mapstructure:"PTS_OT9"` 353 | PtsQtr1 interface{} `mapstructure:"PTS_QTR1"` 354 | PtsQtr2 interface{} `mapstructure:"PTS_QTR2"` 355 | PtsQtr3 interface{} `mapstructure:"PTS_QTR3"` 356 | PtsQtr4 interface{} `mapstructure:"PTS_QTR4"` 357 | Reb interface{} `mapstructure:"REB"` 358 | TeamAbbreviation string `mapstructure:"TEAM_ABBREVIATION"` 359 | TeamCityName string `mapstructure:"TEAM_CITY_NAME"` 360 | TeamID int64 `mapstructure:"TEAM_ID"` 361 | TeamName string `mapstructure:"TEAM_NAME"` 362 | TeamWINSLosses string `mapstructure:"TEAM_WINS_LOSSES"` 363 | Tov interface{} `mapstructure:"TOV"` 364 | } 365 | 366 | type Conference string 367 | 368 | const ( 369 | East Conference = "East" 370 | West Conference = "West" 371 | ) 372 | 373 | type Standingsdate string 374 | 375 | const ( 376 | The11062022 Standingsdate = "11/06/2022" 377 | ) 378 | --------------------------------------------------------------------------------