feat: nba segment

This commit is contained in:
joadoumie 2023-10-12 23:34:25 +00:00 committed by Jan De Dobbeleer
parent 008b950b4d
commit cec96d8b94
7 changed files with 1424 additions and 1 deletions

View file

@ -170,6 +170,8 @@ const (
LUA SegmentType = "lua"
// MERCURIAL writes the Mercurial source control information
MERCURIAL SegmentType = "mercurial"
// NBA writes NBA game data
NBA SegmentType = "nba"
// NBGV writes the nbgv version information
NBGV SegmentType = "nbgv"
// NIGHTSCOUT is an open source diabetes system
@ -296,6 +298,7 @@ var Segments = map[SegmentType]func() SegmentWriter{
KUBECTL: func() SegmentWriter { return &segments.Kubectl{} },
LUA: func() SegmentWriter { return &segments.Lua{} },
MERCURIAL: func() SegmentWriter { return &segments.Mercurial{} },
NBA: func() SegmentWriter { return &segments.Nba{} },
NBGV: func() SegmentWriter { return &segments.Nbgv{} },
NIGHTSCOUT: func() SegmentWriter { return &segments.Nightscout{} },
NODE: func() SegmentWriter { return &segments.Node{} },

418
src/segments/nba.go Normal file
View file

@ -0,0 +1,418 @@
package segments
import (
"encoding/json"
"errors"
"fmt"
"time"
"github.com/jandedobbeleer/oh-my-posh/src/platform"
"github.com/jandedobbeleer/oh-my-posh/src/properties"
)
// segment struct, makes templating easier
type Nba struct {
props properties.Properties
env platform.Environment
NBAData
}
// NBA struct contains parsed API data that care about for the segment
type NBAData struct {
HomeTeam string
AwayTeam string
Time string
GameDate string
StartTimeUTC string
GameStatus GameStatus // 1 = scheduled, 2 = in progress, 3 = finished
HomeScore int
AwayScore int
HomeTeamWins int
HomeTeamLosses int
AwayTeamWins int
AwayTeamLosses int
}
func (nba *NBAData) HasStats() bool {
return nba.HomeTeamWins != 0 || nba.HomeTeamLosses != 0 || nba.AwayTeamWins != 0 || nba.AwayTeamLosses != 0
}
func (nba *NBAData) Started() bool {
return nba.GameStatus == InProgress || nba.GameStatus == Finished
}
const (
NBASeason properties.Property = "season"
TeamName properties.Property = "team"
DaysOffset properties.Property = "days_offset"
ScheduledTemplate properties.Property = "scheduled_template"
InProgressTemplate properties.Property = "in_progress_template"
FinishedTemplate properties.Property = "finished_template"
NBAScoreURL string = "https://cdn.nba.com/static/json/liveData/scoreboard/todaysScoreboard_00.json"
NBASchedURLPart1 string = "https://stats.nba.com/stats/internationalbroadcasterschedule?LeagueID=00&Season="
NBASchedURLPart2 string = "&RegionID=1&EST=Y"
Unknown = "Unknown"
)
// Custom type for GameStatus
type GameStatus int
// Constants for GameStatus values
const (
Scheduled GameStatus = 1
InProgress GameStatus = 2
Finished GameStatus = 3
NotFound GameStatus = 4
)
// Int() method for GameStatus to get its integer representation
// This is a helpful method if people want to come up with their own templates
func (gs GameStatus) Int() int {
return int(gs)
}
func (gs GameStatus) Valid() bool {
return gs == Scheduled || gs == InProgress || gs == Finished
}
func (gs GameStatus) String() string {
switch gs {
case Scheduled:
return "Scheduled"
case InProgress:
return "In Progress"
case Finished:
return "Finished"
case NotFound:
return "Not Found"
default:
return Unknown
}
}
// All of the structs needed to retrieve data from the live score endpoint
type ScoreboardResponse struct {
Scoreboard Scoreboard `json:"scoreboard"`
}
type Scoreboard struct {
GameDate string `json:"gameDate"`
Games []Game `json:"games"`
}
type Game struct {
GameStatus int `json:"gameStatus"`
GameStatusText string `json:"gameStatusText"`
GameTimeUTC string `json:"gameTimeUTC"`
HomeTeam Team `json:"homeTeam"`
AwayTeam Team `json:"awayTeam"`
}
type Team struct {
TeamTricode string `json:"teamTricode"`
Wins int `json:"wins"`
Losses int `json:"losses"`
Score int `json:"score"`
}
// All the structs needed to get data from the schedule endpoint
type ScheduleResponse struct {
ResultSets []ResultSet `json:"resultSets"`
}
type ResultSet struct {
CompleteGameList []ScheduledGame `json:"CompleteGameList,omitempty"`
}
type ScheduledGame struct {
VtAbbreviation string `json:"vtAbbreviation"`
HtAbbreviation string `json:"htAbbreviation"`
Date string `json:"date"`
Time string `json:"time"`
}
func (nba *Nba) Template() string {
return " \U000F0806 {{ .HomeTeam}}{{ if .HasStats }} ({{.HomeTeamWins}}-{{.HomeTeamLosses}}){{ end }}{{ if .Started }}:{{.HomeScore}}{{ end }} vs {{ .AwayTeam}}{{ if .HasStats }} ({{.AwayTeamWins}}-{{.AwayTeamLosses}}){{ end }}{{ if .Started }}:{{.AwayScore}}{{ end }} | {{ if not .Started }}{{.GameDate}} | {{ end }}{{.Time}} " //nolint:lll
}
func (nba *Nba) Enabled() bool {
data, err := nba.getResult()
if err != nil || !data.GameStatus.Valid() {
return false
}
nba.NBAData = *data
return true
}
// Returns an empty Game Data struct with the GameStatus set to NotFound
// Helpful for caching the fact that a game was not found for a team
func (nba *Nba) getGameNotFoundData() string {
return `{
"HomeTeam":"",
"AwayTeam":"",
"Time":"",
"GameDate":"",
"StartTimeUTC":"",
"GameStatus":4,
"HomeScore":0,
"AwayScore":0,
"HomeTeamWins":0,
"HomeTeamLosses":0,
"AwayTeamWins":0,
"AwayTeamLosses":0
}`
}
// parses through a set of games from the score endpoint and looks for props.team in away or home team
func (nba *Nba) findGameScoreByTeamTricode(games []Game, teamTricode string) (*Game, error) {
for _, game := range games {
if game.HomeTeam.TeamTricode == teamTricode || game.AwayTeam.TeamTricode == teamTricode {
return &game, nil
}
}
return nil, errors.New("no game score found for team")
}
// parses through a set of games from the schedule endpoint and looks for props.team in away or home team
func (nba *Nba) findGameSchedulebyTeamTricode(games []ScheduledGame, teamTricode string) (*ScheduledGame, error) {
for _, game := range games {
if game.VtAbbreviation == teamTricode || game.HtAbbreviation == teamTricode {
return &game, nil
}
}
return nil, errors.New("no scheduled game found for team")
}
// parses the time and date from the schedule endpoint into a UTC time
func (nba *Nba) parseTimetoUTC(timeEST, date string) string {
combinedTime := date + " " + timeEST
timeUTC, err := time.Parse("01/02/2006 03:04 PM", combinedTime)
if err != nil {
return ""
}
return timeUTC.UTC().Format("2006-01-02T15:04:05Z")
}
// retrieves data from the score endpoint
func (nba *Nba) retrieveScoreData(teamName string, httpTimeout int) (*NBAData, error) {
body, err := nba.env.HTTPRequest(NBAScoreURL, nil, httpTimeout)
if err != nil {
return nil, err
}
var scoreboardResponse *ScoreboardResponse
err = json.Unmarshal(body, &scoreboardResponse)
if err != nil {
return nil, err
}
gameInfo, err := nba.findGameScoreByTeamTricode(scoreboardResponse.Scoreboard.Games, teamName)
if err != nil {
return nil, err
}
return &NBAData{
AwayTeam: gameInfo.AwayTeam.TeamTricode,
HomeTeam: gameInfo.HomeTeam.TeamTricode,
Time: gameInfo.GameStatusText,
GameDate: scoreboardResponse.Scoreboard.GameDate,
StartTimeUTC: gameInfo.GameTimeUTC,
GameStatus: GameStatus(gameInfo.GameStatus),
HomeScore: gameInfo.HomeTeam.Score,
AwayScore: gameInfo.AwayTeam.Score,
HomeTeamWins: gameInfo.HomeTeam.Wins,
HomeTeamLosses: gameInfo.HomeTeam.Losses,
AwayTeamWins: gameInfo.AwayTeam.Wins,
AwayTeamLosses: gameInfo.AwayTeam.Losses,
}, nil
}
// Retrieves the data from the schedule endpoint
func (nba *Nba) retrieveScheduleData(teamName string, httpTimeout int) (*NBAData, error) {
// How many days into the future should we look for a game.
numDaysToSearch := nba.props.GetInt(DaysOffset, 8)
nbaSeason := nba.props.GetString(NBASeason, "2023")
// Get the current date in America/New_York
t := time.Now().In(time.FixedZone("America/New_York", -5*60*60))
// Check to see if a game is scheduled while the numDaysToSearch is greater than 0
for numDaysToSearch > 0 {
// convert t into the format that the API expects "MM/YYYY/DD"
dateStr := fmt.Sprintf("%02d/%02d/%d", t.Month(), t.Day(), t.Year())
urlEndpoint := NBASchedURLPart1 + nbaSeason + "&Date=" + dateStr + NBASchedURLPart2
body, err := nba.env.HTTPRequest(urlEndpoint, nil, httpTimeout)
if err != nil {
return nil, err
}
var scheduleResponse *ScheduleResponse
err = json.Unmarshal(body, &scheduleResponse)
if err != nil {
return nil, err
}
// Check if we can find a game for the team
gameInfo, err := nba.findGameSchedulebyTeamTricode(scheduleResponse.ResultSets[1].CompleteGameList, teamName)
if err != nil {
// We didn't find a game for the team on this day, so we need to check the next day
t = t.AddDate(0, 0, 1)
numDaysToSearch--
continue
}
return &NBAData{
AwayTeam: gameInfo.VtAbbreviation,
HomeTeam: gameInfo.HtAbbreviation,
Time: gameInfo.Time + " ET",
GameDate: gameInfo.Date,
StartTimeUTC: nba.parseTimetoUTC(gameInfo.Time, gameInfo.Date),
GameStatus: Scheduled,
HomeScore: 0,
AwayScore: 0,
HomeTeamWins: 0,
HomeTeamLosses: 0,
AwayTeamWins: 0,
AwayTeamLosses: 0,
}, nil
}
return nil, errors.New("no scheduled game found for team within DaysOffset days")
}
// First try to get the data from the score endpoint, if that fails, try the schedule endpoint
// The score endpoint usually goes live within 12 hours of a game starting
func (nba *Nba) getAvailableGameData(teamName string, httpTimeout int) (*NBAData, error) {
// Get the info from the score endpoint
data, err := nba.retrieveScoreData(teamName, httpTimeout)
if err == nil {
return data, nil
}
// If the score endpoint doesn't have anything get data from the schedule endpoint
data, err = nba.retrieveScheduleData(teamName, httpTimeout)
if err == nil {
return data, nil
}
return nil, err
}
// Gets the data from the cache if it exists
func (nba *Nba) getCacheValue(key string) (*NBAData, error) {
if val, found := nba.env.Cache().Get(key); found {
var nbaData *NBAData
err := json.Unmarshal([]byte(val), &nbaData)
if err != nil {
return nil, err
}
return nbaData, nil
}
return nil, errors.New("no data in cache")
}
// Gets the data from the cache for a scheduled game if it exists
// Checks whether the game should have started and if so, removes the cache entry
func (nba *Nba) getCachedScheduleValue(key string) (*NBAData, error) {
data, err := nba.getCacheValue(key)
if err != nil {
return nil, errors.New("no data in cache")
}
// check if the game was previously not found and we should wait to check again
if data.GameStatus == NotFound {
return data, nil
}
// check if the current time is after the start time of the game
// if so, we need to refresh the data
startTime, err := time.Parse("2006-01-02T15:04:05Z", data.StartTimeUTC)
if err != nil {
return nil, err
}
if time.Now().UTC().After(startTime) {
// remove the cache entry
nba.env.Cache().Delete(key)
return nil, errors.New("game has already started")
}
return data, nil
}
func (nba *Nba) getResult() (*NBAData, error) {
teamName := nba.props.GetString(TeamName, "")
cachedScheduleKey := fmt.Sprintf("%s%s", teamName, "schedule")
cachedScoreKey := fmt.Sprintf("%s%s", teamName, "score")
httpTimeout := nba.props.GetInt(properties.HTTPTimeout, properties.DefaultHTTPTimeout)
// How often you want to query the API to get live score information, defaults to 2 minutes
cacheScoreTimeout := nba.props.GetInt(properties.CacheTimeout, 2)
// Cache the schedule information for a day so we don't call the API too often
cacheScheduleTimeout := nba.props.GetInt(properties.CacheTimeout, 1440)
// Cache the fact a game was not found for 30 minutes so we don't call the API too often
cacheNotFoundTimeout := nba.props.GetInt(properties.CacheTimeout, 30)
nba.env.Debug("Validating cache data for " + teamName)
if cacheScheduleTimeout > 0 {
if data, err := nba.getCachedScheduleValue(cachedScheduleKey); err == nil {
return data, nil
}
}
if cacheScoreTimeout > 0 {
if data, err := nba.getCacheValue(cachedScoreKey); err == nil {
return data, nil
}
}
nba.env.Debug("Fetching available data for " + teamName)
data, err := nba.getAvailableGameData(teamName, httpTimeout)
if err != nil {
// cache the fact that we didn't find a game yet for the day for 30m so we don't continuously ping the endpoints
nba.env.Cache().Set(cachedScheduleKey, nba.getGameNotFoundData(), cacheNotFoundTimeout)
nba.env.Error(errors.Join(err, fmt.Errorf("unable to get data for team %s", teamName)))
return nil, err
}
if !data.GameStatus.Valid() {
err := fmt.Errorf("%d is not a valid game status", data.GameStatus)
nba.env.Error(err)
return nil, err
}
if cacheScheduleTimeout > 0 && data.GameStatus == Scheduled {
// persist data for team in cache
cachedData, _ := json.Marshal(data)
nba.env.Cache().Set(cachedScheduleKey, string(cachedData), cacheScheduleTimeout)
}
// if the game is in progress or finished, we can cache the score
if cacheScoreTimeout > 0 && data.GameStatus == InProgress || data.GameStatus == Finished {
// persist data for team in cache
cachedData, _ := json.Marshal(data)
nba.env.Cache().Set(cachedScoreKey, string(cachedData), cacheScoreTimeout)
}
return data, nil
}
func (nba *Nba) Init(props properties.Properties, env platform.Environment) {
nba.props = props
nba.env = env
}

122
src/segments/nba_test.go Normal file
View file

@ -0,0 +1,122 @@
package segments
import (
"fmt"
"os"
"testing"
"time"
"github.com/jandedobbeleer/oh-my-posh/src/mock"
"github.com/jandedobbeleer/oh-my-posh/src/properties"
"github.com/stretchr/testify/assert"
mock2 "github.com/stretchr/testify/mock"
)
func getTestData(file string) string {
content, _ := os.ReadFile(fmt.Sprintf("../test/%s", file))
return string(content)
}
// create Test segment for NBA segment
func TestNBASegment(t *testing.T) {
jsonScheduleData := getTestData("nba/schedule.json")
jsonScoreData := getTestData("nba/score.json")
cases := []struct {
Case string
JSONResponse string
ExpectedString string
ExpectedEnabled bool
CacheTimeout int
CacheFoundFail bool
TeamName string
DaysOffset int
Error error
}{
{
Case: "Team (Home Team) Scheduled Game",
JSONResponse: jsonScheduleData,
TeamName: "LAL",
ExpectedString: "󰠆 LAL vs PHX | 10/26/2023 | 10:00 PM ET",
ExpectedEnabled: true,
DaysOffset: 8,
},
{
Case: "Team (Away Team) Scheduled Game",
JSONResponse: jsonScheduleData,
TeamName: "PHX",
ExpectedString: "󰠆 LAL vs PHX | 10/26/2023 | 10:00 PM ET",
DaysOffset: 4,
ExpectedEnabled: true,
},
{
Case: "Team (Home Team) Live Game",
JSONResponse: jsonScoreData,
TeamName: "CHA",
ExpectedString: "󰠆 CHA (1-0):13 vs BOS (0-1):8 | Q1 8:23",
ExpectedEnabled: true,
},
{
Case: "Team (Away Team) Live Game",
JSONResponse: jsonScoreData,
TeamName: "BOS",
ExpectedString: "󰠆 CHA (1-0):13 vs BOS (0-1):8 | Q1 8:23",
ExpectedEnabled: true,
},
{
Case: "Team not Found",
JSONResponse: jsonScheduleData,
DaysOffset: 8,
TeamName: "INVALID",
ExpectedEnabled: false,
},
}
for _, tc := range cases {
env := &mock.MockedEnvironment{}
props := properties.Map{
properties.CacheTimeout: tc.CacheTimeout,
TeamName: tc.TeamName,
DaysOffset: tc.DaysOffset,
}
env.On("Error", mock2.Anything)
env.On("Debug", mock2.Anything)
env.On("HTTPRequest", NBAScoreURL).Return([]byte(tc.JSONResponse), tc.Error)
// Add all the daysOffset to the http request responses
for i := 0; i < tc.DaysOffset; i++ {
currTime := time.Now().In(time.FixedZone("America/New_York", -5*60*60))
// add offset days to currTime so we can query for games in the future
currTime = currTime.AddDate(0, 0, i)
dateStr := fmt.Sprintf("%02d/%02d/%d", currTime.Month(), currTime.Day(), currTime.Year())
nbaSeason := fmt.Sprintf("%d", currTime.Year())
scheduleURLEndpoint := NBASchedURLPart1 + nbaSeason + "&Date=" + dateStr + NBASchedURLPart2
env.On("HTTPRequest", scheduleURLEndpoint).Return([]byte(tc.JSONResponse), tc.Error)
}
nba := &Nba{
props: props,
env: env,
}
cachedScheduleKey := fmt.Sprintf("%s%s", tc.TeamName, "schedule")
cachedScoreKey := fmt.Sprintf("%s%s", tc.TeamName, "score")
cache := &mock.MockedCache{}
cache.On("Get", cachedScheduleKey).Return(nba.getGameNotFoundData(), tc.CacheFoundFail)
cache.On("Get", cachedScoreKey).Return(nba.getGameNotFoundData(), tc.CacheFoundFail)
cache.On("Set", cachedScheduleKey, nba.getGameNotFoundData(), tc.CacheTimeout).Return()
cache.On("Set", cachedScoreKey, nba.getGameNotFoundData(), tc.CacheTimeout).Return()
env.On("Cache").Return(cache)
enabled := nba.Enabled()
assert.Equal(t, tc.ExpectedEnabled, enabled, tc.Case)
if !enabled {
continue
}
assert.Equal(t, tc.ExpectedString, renderTemplate(env, nba.Template(), nba), tc.Case)
}
}

View file

@ -158,7 +158,7 @@ func (u *Umbraco) TryFindLegacyUmbraco(configPath string) bool {
u.Modern = false
if len(appSetting.Value) == 0 {
u.Version = "Unknown"
u.Version = Unknown
} else {
u.Version = appSetting.Value
}

View file

@ -0,0 +1,96 @@
{
"resource": "internationalbroadcasterschedule",
"parameters": {
"LeagueID": "00",
"Season": "2023",
"RegionID": 1,
"Date": "10/26/2023",
"EST": "Y"
},
"resultSets": [
{
"NextGameList": [
{
"gameID": "0022300075",
"vtCity": "Philadelphia",
"vtNickName": "76ers",
"vtShortName": "Philadelphia",
"vtAbbreviation": "PHI",
"htCity": "Milwaukee",
"htNickName": "Bucks",
"htShortName": "Milwaukee",
"htAbbreviation": "MIL",
"date": "10/26/2023",
"time": "07:30 PM",
"day": "Thu",
"broadcasters": [
{
"broadcastID": "10",
"broadcasterName": "TNT",
"tapeDelayComments": ""
}
]
},
{
"gameID": "0022300076",
"vtCity": "Phoenix",
"vtNickName": "Suns",
"vtShortName": "Phoenix",
"vtAbbreviation": "PHX",
"htCity": "Los Angeles",
"htNickName": "Lakers",
"htShortName": "L.A. Lakers",
"htAbbreviation": "LAL",
"date": "10/26/2023",
"time": "10:00 PM",
"day": "Thu",
"broadcasters": [
{
"broadcastID": "10",
"broadcasterName": "TNT",
"tapeDelayComments": ""
}
]
}
]
},
{
"CompleteGameList": [
{
"gameID": "0022300075",
"vtCity": "Philadelphia",
"vtNickName": "76ers",
"vtShortName": "Philadelphia",
"vtAbbreviation": "PHI",
"htCity": "Milwaukee",
"htNickName": "Bucks",
"htShortName": "Milwaukee",
"htAbbreviation": "MIL",
"date": "10/26/2023",
"time": "07:30 PM",
"day": "Thu",
"broadcastID": "10",
"broadcasterName": "TNT",
"tapeDelayComments": ""
},
{
"gameID": "0022300076",
"vtCity": "Phoenix",
"vtNickName": "Suns",
"vtShortName": "Phoenix",
"vtAbbreviation": "PHX",
"htCity": "Los Angeles",
"htNickName": "Lakers",
"htShortName": "L.A. Lakers",
"htAbbreviation": "LAL",
"date": "10/26/2023",
"time": "10:00 PM",
"day": "Thu",
"broadcastID": "10",
"broadcasterName": "TNT",
"tapeDelayComments": ""
}
]
}
]
}

699
src/test/nba/score.json Normal file
View file

@ -0,0 +1,699 @@
{
"meta": {
"version": 1,
"request": "https://nba-prod-us-east-1-mediaops-stats.s3.amazonaws.com/NBA/liveData/scoreboard/todaysScoreboard_00.json",
"time": "2023-10-19 01:17:57.1757",
"code": 200
},
"scoreboard": {
"gameDate": "2023-10-19",
"leagueId": "00",
"leagueName": "National Basketball Association",
"games": [
{
"gameId": "0012300060",
"gameCode": "20231019/BOSCHA",
"gameStatus": 2,
"gameStatusText": "Q1 8:23",
"period": 0,
"gameClock": "Q1 8:23",
"gameTimeUTC": "2023-10-19T23:00:00Z",
"gameEt": "2023-10-19T19:00:00Z",
"regulationPeriods": 4,
"ifNecessary": false,
"seriesGameNumber": "",
"seriesText": "Preseason",
"seriesConference": "",
"poRoundDesc": "",
"gameSubtype": "",
"homeTeam": {
"teamId": 1610612766,
"teamName": "Hornets",
"teamCity": "Charlotte",
"teamTricode": "CHA",
"wins": 1,
"losses": 0,
"score": 13,
"seed": null,
"inBonus": null,
"timeoutsRemaining": 0,
"periods": [
{
"period": 1,
"periodType": "REGULAR",
"score": 0
},
{
"period": 2,
"periodType": "REGULAR",
"score": 0
},
{
"period": 3,
"periodType": "REGULAR",
"score": 0
},
{
"period": 4,
"periodType": "REGULAR",
"score": 0
}
]
},
"awayTeam": {
"teamId": 1610612738,
"teamName": "Celtics",
"teamCity": "Boston",
"teamTricode": "BOS",
"wins": 0,
"losses": 1,
"score": 8,
"seed": null,
"inBonus": null,
"timeoutsRemaining": 0,
"periods": [
{
"period": 1,
"periodType": "REGULAR",
"score": 0
},
{
"period": 2,
"periodType": "REGULAR",
"score": 0
},
{
"period": 3,
"periodType": "REGULAR",
"score": 0
},
{
"period": 4,
"periodType": "REGULAR",
"score": 0
}
]
},
"gameLeaders": {
"homeLeaders": {
"personId": 0,
"name": "",
"jerseyNum": "",
"position": "",
"teamTricode": "CHA",
"playerSlug": null,
"points": 0,
"rebounds": 0,
"assists": 0
},
"awayLeaders": {
"personId": 0,
"name": "",
"jerseyNum": "",
"position": "",
"teamTricode": "BOS",
"playerSlug": null,
"points": 0,
"rebounds": 0,
"assists": 0
}
},
"pbOdds": {
"team": null,
"odds": 0.0,
"suspended": 0
}
},
{
"gameId": "0012300061",
"gameCode": "20231019/MINCHI",
"gameStatus": 1,
"gameStatusText": "8:00 pm ET",
"period": 0,
"gameClock": "",
"gameTimeUTC": "2023-10-20T00:00:00Z",
"gameEt": "2023-10-19T20:00:00Z",
"regulationPeriods": 4,
"ifNecessary": false,
"seriesGameNumber": "",
"seriesText": "Preseason",
"seriesConference": "",
"poRoundDesc": "",
"gameSubtype": "",
"homeTeam": {
"teamId": 1610612741,
"teamName": "Bulls",
"teamCity": "Chicago",
"teamTricode": "CHI",
"wins": 0,
"losses": 0,
"score": 0,
"seed": null,
"inBonus": null,
"timeoutsRemaining": 0,
"periods": [
{
"period": 1,
"periodType": "REGULAR",
"score": 0
},
{
"period": 2,
"periodType": "REGULAR",
"score": 0
},
{
"period": 3,
"periodType": "REGULAR",
"score": 0
},
{
"period": 4,
"periodType": "REGULAR",
"score": 0
}
]
},
"awayTeam": {
"teamId": 1610612750,
"teamName": "Timberwolves",
"teamCity": "Minnesota",
"teamTricode": "MIN",
"wins": 0,
"losses": 0,
"score": 0,
"seed": null,
"inBonus": null,
"timeoutsRemaining": 0,
"periods": [
{
"period": 1,
"periodType": "REGULAR",
"score": 0
},
{
"period": 2,
"periodType": "REGULAR",
"score": 0
},
{
"period": 3,
"periodType": "REGULAR",
"score": 0
},
{
"period": 4,
"periodType": "REGULAR",
"score": 0
}
]
},
"gameLeaders": {
"homeLeaders": {
"personId": 0,
"name": "",
"jerseyNum": "",
"position": "",
"teamTricode": "CHI",
"playerSlug": null,
"points": 0,
"rebounds": 0,
"assists": 0
},
"awayLeaders": {
"personId": 0,
"name": "",
"jerseyNum": "",
"position": "",
"teamTricode": "MIN",
"playerSlug": null,
"points": 0,
"rebounds": 0,
"assists": 0
}
},
"pbOdds": {
"team": null,
"odds": 0.0,
"suspended": 0
}
},
{
"gameId": "0012300062",
"gameCode": "20231019/DETOKC",
"gameStatus": 1,
"gameStatusText": "8:00 pm ET",
"period": 0,
"gameClock": "",
"gameTimeUTC": "2023-10-20T00:00:00Z",
"gameEt": "2023-10-19T20:00:00Z",
"regulationPeriods": 4,
"ifNecessary": false,
"seriesGameNumber": "",
"seriesText": "Preseason",
"seriesConference": "",
"poRoundDesc": "",
"gameSubtype": "",
"homeTeam": {
"teamId": 1610612760,
"teamName": "Thunder",
"teamCity": "Oklahoma City",
"teamTricode": "OKC",
"wins": 0,
"losses": 0,
"score": 0,
"seed": null,
"inBonus": null,
"timeoutsRemaining": 0,
"periods": [
{
"period": 1,
"periodType": "REGULAR",
"score": 0
},
{
"period": 2,
"periodType": "REGULAR",
"score": 0
},
{
"period": 3,
"periodType": "REGULAR",
"score": 0
},
{
"period": 4,
"periodType": "REGULAR",
"score": 0
}
]
},
"awayTeam": {
"teamId": 1610612765,
"teamName": "Pistons",
"teamCity": "Detroit",
"teamTricode": "DET",
"wins": 0,
"losses": 0,
"score": 0,
"seed": null,
"inBonus": null,
"timeoutsRemaining": 0,
"periods": [
{
"period": 1,
"periodType": "REGULAR",
"score": 0
},
{
"period": 2,
"periodType": "REGULAR",
"score": 0
},
{
"period": 3,
"periodType": "REGULAR",
"score": 0
},
{
"period": 4,
"periodType": "REGULAR",
"score": 0
}
]
},
"gameLeaders": {
"homeLeaders": {
"personId": 0,
"name": "",
"jerseyNum": "",
"position": "",
"teamTricode": "OKC",
"playerSlug": null,
"points": 0,
"rebounds": 0,
"assists": 0
},
"awayLeaders": {
"personId": 0,
"name": "",
"jerseyNum": "",
"position": "",
"teamTricode": "DET",
"playerSlug": null,
"points": 0,
"rebounds": 0,
"assists": 0
}
},
"pbOdds": {
"team": null,
"odds": 0.0,
"suspended": 0
}
},
{
"gameId": "0012300063",
"gameCode": "20231019/PHXLAL",
"gameStatus": 1,
"gameStatusText": "10:00 pm ET",
"period": 0,
"gameClock": "",
"gameTimeUTC": "2023-10-20T02:00:00Z",
"gameEt": "2023-10-19T22:00:00Z",
"regulationPeriods": 4,
"ifNecessary": false,
"seriesGameNumber": "",
"seriesText": "Preseason",
"seriesConference": "",
"poRoundDesc": "",
"gameSubtype": "",
"homeTeam": {
"teamId": 1610612747,
"teamName": "Lakers",
"teamCity": "Los Angeles",
"teamTricode": "LAL",
"wins": 0,
"losses": 0,
"score": 0,
"seed": null,
"inBonus": null,
"timeoutsRemaining": 0,
"periods": [
{
"period": 1,
"periodType": "REGULAR",
"score": 0
},
{
"period": 2,
"periodType": "REGULAR",
"score": 0
},
{
"period": 3,
"periodType": "REGULAR",
"score": 0
},
{
"period": 4,
"periodType": "REGULAR",
"score": 0
}
]
},
"awayTeam": {
"teamId": 1610612756,
"teamName": "Suns",
"teamCity": "Phoenix",
"teamTricode": "PHX",
"wins": 0,
"losses": 0,
"score": 0,
"seed": null,
"inBonus": null,
"timeoutsRemaining": 0,
"periods": [
{
"period": 1,
"periodType": "REGULAR",
"score": 0
},
{
"period": 2,
"periodType": "REGULAR",
"score": 0
},
{
"period": 3,
"periodType": "REGULAR",
"score": 0
},
{
"period": 4,
"periodType": "REGULAR",
"score": 0
}
]
},
"gameLeaders": {
"homeLeaders": {
"personId": 0,
"name": "",
"jerseyNum": "",
"position": "",
"teamTricode": "LAL",
"playerSlug": null,
"points": 0,
"rebounds": 0,
"assists": 0
},
"awayLeaders": {
"personId": 0,
"name": "",
"jerseyNum": "",
"position": "",
"teamTricode": "PHX",
"playerSlug": null,
"points": 0,
"rebounds": 0,
"assists": 0
}
},
"pbOdds": {
"team": null,
"odds": 0.0,
"suspended": 0
}
},
{
"gameId": "0012300064",
"gameCode": "20231019/DENLAC",
"gameStatus": 1,
"gameStatusText": "10:00 pm ET",
"period": 0,
"gameClock": "",
"gameTimeUTC": "2023-10-20T02:00:00Z",
"gameEt": "2023-10-19T22:00:00Z",
"regulationPeriods": 4,
"ifNecessary": false,
"seriesGameNumber": "",
"seriesText": "Preseason",
"seriesConference": "",
"poRoundDesc": "",
"gameSubtype": "",
"homeTeam": {
"teamId": 1610612746,
"teamName": "Clippers",
"teamCity": "LA",
"teamTricode": "LAC",
"wins": 0,
"losses": 0,
"score": 0,
"seed": null,
"inBonus": null,
"timeoutsRemaining": 0,
"periods": [
{
"period": 1,
"periodType": "REGULAR",
"score": 0
},
{
"period": 2,
"periodType": "REGULAR",
"score": 0
},
{
"period": 3,
"periodType": "REGULAR",
"score": 0
},
{
"period": 4,
"periodType": "REGULAR",
"score": 0
}
]
},
"awayTeam": {
"teamId": 1610612743,
"teamName": "Nuggets",
"teamCity": "Denver",
"teamTricode": "DEN",
"wins": 0,
"losses": 0,
"score": 0,
"seed": null,
"inBonus": null,
"timeoutsRemaining": 0,
"periods": [
{
"period": 1,
"periodType": "REGULAR",
"score": 0
},
{
"period": 2,
"periodType": "REGULAR",
"score": 0
},
{
"period": 3,
"periodType": "REGULAR",
"score": 0
},
{
"period": 4,
"periodType": "REGULAR",
"score": 0
}
]
},
"gameLeaders": {
"homeLeaders": {
"personId": 0,
"name": "",
"jerseyNum": "",
"position": "",
"teamTricode": "LAC",
"playerSlug": null,
"points": 0,
"rebounds": 0,
"assists": 0
},
"awayLeaders": {
"personId": 0,
"name": "",
"jerseyNum": "",
"position": "",
"teamTricode": "DEN",
"playerSlug": null,
"points": 0,
"rebounds": 0,
"assists": 0
}
},
"pbOdds": {
"team": null,
"odds": 0.0,
"suspended": 0
}
},
{
"gameId": "0012300065",
"gameCode": "20231019/UTASAC",
"gameStatus": 1,
"gameStatusText": "10:00 pm ET",
"period": 0,
"gameClock": "",
"gameTimeUTC": "2023-10-20T02:00:00Z",
"gameEt": "2023-10-19T22:00:00Z",
"regulationPeriods": 4,
"ifNecessary": false,
"seriesGameNumber": "",
"seriesText": "Preseason",
"seriesConference": "",
"poRoundDesc": "",
"gameSubtype": "",
"homeTeam": {
"teamId": 1610612758,
"teamName": "Kings",
"teamCity": "Sacramento",
"teamTricode": "SAC",
"wins": 0,
"losses": 0,
"score": 0,
"seed": null,
"inBonus": null,
"timeoutsRemaining": 0,
"periods": [
{
"period": 1,
"periodType": "REGULAR",
"score": 0
},
{
"period": 2,
"periodType": "REGULAR",
"score": 0
},
{
"period": 3,
"periodType": "REGULAR",
"score": 0
},
{
"period": 4,
"periodType": "REGULAR",
"score": 0
}
]
},
"awayTeam": {
"teamId": 1610612762,
"teamName": "Jazz",
"teamCity": "Utah",
"teamTricode": "UTA",
"wins": 0,
"losses": 0,
"score": 0,
"seed": null,
"inBonus": null,
"timeoutsRemaining": 0,
"periods": [
{
"period": 1,
"periodType": "REGULAR",
"score": 0
},
{
"period": 2,
"periodType": "REGULAR",
"score": 0
},
{
"period": 3,
"periodType": "REGULAR",
"score": 0
},
{
"period": 4,
"periodType": "REGULAR",
"score": 0
}
]
},
"gameLeaders": {
"homeLeaders": {
"personId": 0,
"name": "",
"jerseyNum": "",
"position": "",
"teamTricode": "SAC",
"playerSlug": null,
"points": 0,
"rebounds": 0,
"assists": 0
},
"awayLeaders": {
"personId": 0,
"name": "",
"jerseyNum": "",
"position": "",
"teamTricode": "UTA",
"playerSlug": null,
"points": 0,
"rebounds": 0,
"assists": 0
}
},
"pbOdds": {
"team": null,
"odds": 0.0,
"suspended": 0
}
}
]
}
}

View file

@ -0,0 +1,85 @@
---
id: nba
title: NBA
sidebar_label: NBA
---
## What
The NBA segment allows you to display the scheduling and score information for your
favorite NBA team!
## Sample Configuration
In order to use the NBA segment, you need to provide a valid team
[tri-code](https://liaison.reuters.com/tools/sports-team-codes) that you'd
like to get data for inside of the configuration. For example, if you'd like
to get information for the Los Angeles Lakers, you'd need to use the "LAL"
tri-code.
This example uses "LAL" to get information for the Los Angeles Lakers. It also
sets the foreground and background colors to match the theming for the team.
If you are interested in getting information about specific foreground and
background colors you could use for other teams, you can explore some of
the color schemes [here](https://teamcolorcodes.com/nba-team-color-codes/).
It is recommended that you set the HTTP timeout to a higher value than the
normal default in case it takes some time to gather the scoreboard information.
In this case we have the http_timeout set to 1500.
import Config from "@site/src/components/Config.js";
<Config
data={{
background: "#e9ac2f",
foreground: "#8748dc",
leading_diamond: "\ue0b6",
style: "diamond",
trailing_diamond: "\ue0b0",
type: "nba",
properties: {
team: "LAL",
http_timeout: 1500,
},
}}
/>
## Properties
| Name | Type | Description |
| -------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| `team` | `string` | tri-code for the NBA team you want to get data for |
| `days_offset` | `int` | how many days in advance you wish to see that information for, defaults to 8 |
| `http_timeout` | `int` | How long do you want to wait before you want to see your prompt more than your sugar? I figure a half second is a good default - defaults to 500ms |
## Template ([info][templates])
:::note default template
```template
\U000F0806 {{ .HomeTeam}}{{ if .HasStats }} ({{.HomeTeamWins}}-{{.HomeTeamLosses}}){{ end }}{{ if .Started }}:{{.HomeScore}}{{ end }} vs {{ .AwayTeam}}{{ if .HasStats }} ({{.AwayTeamWins}}-{{.AwayTeamLosses}}){{ end }}{{ if .Started }}:{{.AwayScore}}{{ end }} | {{ if not .Started }}{{.GameDate}} | {{ end }}{{.Time}}
```
:::
### Properties
| Name | Type | Description |
| --------------- | --------- | ----------------------------------------------------------- |
| .HomeTeam | `string` | home team for the upcoming game |
| .AwayTeam | `string` | away team for the upcoming game |
| .Time | `string` | time (EST) that the upcoming game will start |
| .GameDate | `string` | date the game will happen |
| .StartTimeUTC | `string` | time (UTC) the game will start |
| .GameStatus | `integer` | integer, 1 = scheduled, 2 = in progress, 3 = finished |
| .HomeScore | `int` | score of the home team |
| .AwayScore | `int` | score of the away team |
| .HomeTeamWins | `int` | number of wins the home team currently has for the season |
| .HomeTeamLosses | `int` | number of losses the home team currently has for the season |
| .AwayTeamWins | `int` | number of wins the away team currently has for the season |
| .AwayTeamLosses | `int` | number of losses the away team currently has for the season |
| .Started | `boolean` | if the game was started or not |
| .HasStats | `boolean` | if the game has game stats or not |
[templates]: /docs/configuration/templates
[nf-search]: https://www.nerdfonts.com/cheat-sheet