From 1b01afb7da73e9609bfd212702552979aa9118f5 Mon Sep 17 00:00:00 2001 From: surskitt Date: Sat, 3 Feb 2024 20:34:58 +0000 Subject: [PATCH] feat: add player last game subcomand --- main.go | 4 ++- xboxlive.go | 66 ++++++++++++++++++++++++++++++++++++++++---- xboxlive_test.go | 72 ++++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 134 insertions(+), 8 deletions(-) diff --git a/main.go b/main.go index 8971bfe..336013a 100644 --- a/main.go +++ b/main.go @@ -132,6 +132,8 @@ func genXblHandler(client *req.Client, kv *bolt.DB) func(m gowon.Message) (strin case "s", "set": return setUserHandler(client, kv, m.Nick, user) case "r", "recent": + return CommandHandler(client, kv, m.Nick, user, xblRecentGames) + case "l", "last": return CommandHandler(client, kv, m.Nick, user, xblLastGame) case "a", "achievement": return CommandHandler(client, kv, m.Nick, user, xblLastAchievement) @@ -139,7 +141,7 @@ func genXblHandler(client *req.Client, kv *bolt.DB) func(m gowon.Message) (strin return CommandHandler(client, kv, m.Nick, user, xblPlayerSummary) } - return "one of [s]et, [r]ecent, [a]chievements or [p]layer must be passed as a command", nil + return "one of [s]et, [r]ecent, [l]ast, [a]chievements or [p]layer must be passed as a command", nil } } diff --git a/xboxlive.go b/xboxlive.go index b398cbf..7559563 100644 --- a/xboxlive.go +++ b/xboxlive.go @@ -15,7 +15,7 @@ const ( var ( userNotFoundErr = errors.New("user not found") - userNoTitlesErr = errors.New("user has no achievements") + userNoTitlesErr = errors.New("user hasn't played any games") titleNoAchievementsErr = errors.New("title has no achievements") ) @@ -78,12 +78,43 @@ func (xblth *XBLTitleHistory) RecentNames() (out []string) { return out } -func (xblpa *XBLTitleHistory) FirstTitleID() (string, error) { - if len(xblpa.Titles) == 0 { +func (xblth *XBLTitleHistory) FirstTitleID() (string, error) { + if len(xblth.Titles) == 0 { return "", userNoTitlesErr } - return xblpa.Titles[0].TitleID, nil + return xblth.Titles[0].TitleID, nil +} + +func (xblth *XBLTitleHistory) FirstTitleSummary() (string, error) { + if len(xblth.Titles) == 0 { + return "", userNoTitlesErr + } + + t := xblth.Titles[0] + + var sb strings.Builder + + w := func(in, colour string) { + s := colourString(in, colour) + sb.WriteString(s) + } + + w(t.Name, "cyan") + + sb.WriteString(" | ") + + w(fmt.Sprintf("Score: %d/%d", t.Achievement.CurrentGamerscore, t.Achievement.TotalGamerscore), "yellow") + + sb.WriteString(" | ") + + w(fmt.Sprintf("Achievements: %d", t.Achievement.CurrentAchievements), "green") + + sb.WriteString(" | ") + + w(fmt.Sprintf("%d%%", t.Achievement.ProgressPercentage), "magenta") + + return sb.String(), nil } type XBLPlayerTitleAchievements struct { @@ -213,7 +244,7 @@ func xblGetXuid(client *req.Client, user string) (string, string, error) { return result.People[0].Xuid, result.People[0].Gamertag, nil } -func xblLastGame(client *req.Client, gamerTag, xuid string) (string, error) { +func xblRecentGames(client *req.Client, gamerTag, xuid string) (string, error) { result := XBLTitleHistory{} _, err := client.R(). @@ -301,3 +332,28 @@ func xblPlayerSummary(client *req.Client, gamerTag, xuid string) (string, error) return fmt.Sprintf("Xbox live player summary: %s", playerSummary.Summary()), nil } + +func xblLastGame(client *req.Client, gamerTag, xuid string) (string, error) { + result := &XBLTitleHistory{} + + _, err := client.R(). + SetPathParam("xuid", xuid). + SetSuccessResult(&result). + Get("https://xbl.io/api/v2/player/titleHistory/{xuid}") + + if err != nil { + return "", err + } + + summary, err := result.FirstTitleSummary() + + if errors.Is(err, userNoTitlesErr) { + return fmt.Sprintf("%s hasn't played any games", gamerTag), nil + } + + if err != nil { + return "", err + } + + return fmt.Sprintf("%s's last played game: %s", gamerTag, summary), nil +} diff --git a/xboxlive_test.go b/xboxlive_test.go index 5520708..da1bd92 100755 --- a/xboxlive_test.go +++ b/xboxlive_test.go @@ -138,6 +138,38 @@ func TestXBLTitleHistoryFirstTitleID(t *testing.T) { } } +func TestXBLTitleHistoryFirstTitleSummary(t *testing.T) { + cases := map[string]struct { + xblthfn string + expected string + err error + }{ + "no titles": { + xblthfn: "no_titles.json", + expected: "", + err: userNoTitlesErr, + }, + "one title": { + xblthfn: "recent_titles.json", + expected: "{cyan}Persona 3 Reload{clear} | {yellow}Score: 275/1000{clear} | {green}Achievements: 20{clear} | {magenta}28%{clear}", + err: nil, + }, + } + + for name, tc := range cases { + t.Run(name, func(t *testing.T) { + xblthjson := openTestFile(t, "XBLTitleHistory", tc.xblthfn) + xblth := XBLTitleHistory{} + err := json.Unmarshal(xblthjson, &xblth) + assert.Nil(t, err) + + out, err := xblth.FirstTitleSummary() + assert.Equal(t, tc.expected, out) + assert.ErrorIs(t, tc.err, err) + }) + } +} + func TestXBLPlayerTitleAchievementsNewestAchievement(t *testing.T) { cases := map[string]struct { xblptafn string @@ -239,7 +271,7 @@ func TestXblGetXuid(t *testing.T) { } } -func TestXblLastGame(t *testing.T) { +func TestXblRecentGames(t *testing.T) { cases := map[string]struct { xblxs string msg string @@ -273,7 +305,7 @@ func TestXblLastGame(t *testing.T) { return resp, nil }) - out, err := xblLastGame(client, "test", "test") + out, err := xblRecentGames(client, "test", "test") assert.Equal(t, tc.msg, out) assert.ErrorIs(t, tc.err, err) }) @@ -369,3 +401,39 @@ func TestXblPlayerSummary(t *testing.T) { }) } } + +func TestXblLastGame(t *testing.T) { + cases := map[string]struct { + xblthfn string + expected string + err error + }{ + "has played": { + xblthfn: "recent_titles.json", + expected: "test's last played game: {cyan}Persona 3 Reload{clear} | {yellow}Score: 275/1000{clear} | {green}Achievements: 20{clear} | {magenta}28%{clear}", + err: nil, + }, + "hasn't played": { + xblthfn: "no_titles.json", + expected: "test hasn't played any games", + err: nil, + }, + } + + for name, tc := range cases { + t.Run(name, func(t *testing.T) { + xblpsjson := openTestFile(t, "XBLTitleHistory", tc.xblthfn) + + client := req.C() + httpmock.ActivateNonDefault(client.GetClient()) + httpmock.RegisterResponder("GET", "https://xbl.io/api/v2/player/titleHistory/test", func(request *http.Request) (*http.Response, error) { + resp := httpmock.NewBytesResponse(http.StatusOK, xblpsjson) + return resp, nil + }) + + out, err := xblLastGame(client, "test", "test") + assert.Equal(t, tc.expected, out) + assert.ErrorIs(t, tc.err, err) + }) + } +}