Skip to content

Commit e17b91a

Browse files
committed
Implemented a naive (but working) way to retrieve players from the api.
1 parent ac612a2 commit e17b91a

File tree

8 files changed

+124
-86
lines changed

8 files changed

+124
-86
lines changed

links/link.go

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package links
2+
3+
// Link describes the properties of a link object
4+
type Link struct {
5+
Self string `json:"self"`
6+
Schema string `json:"schema,omitempty"`
7+
}

listing/service.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import "github.com/homeblest/pubg_stat_tracker/players"
44

55
// Service provides player listing operations
66
type Service interface {
7-
GetPlayer(string) (players.Player, error)
7+
GetPlayer(string) (*players.Player, error)
88
}
99

1010
type service struct {
@@ -17,6 +17,6 @@ func NewService(playerRepo players.Repository) Service {
1717
}
1818

1919
// GetPlayer returns a player in the playerRepository
20-
func (s *service) GetPlayer(name string) (players.Player, error) {
20+
func (s *service) GetPlayer(name string) (*players.Player, error) {
2121
return s.playerRepo.Get(name)
2222
}

main.go

+12-23
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package main
22

33
import (
4-
"flag"
54
"fmt"
65
"os"
76
"os/signal"
@@ -28,9 +27,8 @@ var requestSvc requesting.Service
2827
var listingSvc listing.Service
2928

3029
func main() {
31-
flag.StringVar(&Token, "t", "", "Bot Token")
32-
flag.StringVar(&APIKey, "k", "", "PUBG API Key")
33-
flag.Parse()
30+
APIKey = os.Getenv("PUBG_API_KEY")
31+
Token = os.Getenv("DISCORD_BOT_TOKEN")
3432

3533
var playerStorage players.Repository
3634
var matchStorage matches.Repository
@@ -71,30 +69,21 @@ func messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) {
7169
if m.Author.ID == s.State.User.ID {
7270
return
7371
}
72+
inputString := strings.Split(m.Content, " ")
73+
cmd := inputString[0]
7474

75-
switch {
76-
case strings.HasPrefix(m.Content, "!hello"):
75+
switch cmd {
76+
case "!hello":
7777
s.ChannelMessageSend(m.ChannelID, "Hello friend!")
78-
case strings.HasPrefix(m.Content, "!stats"):
79-
player, err := listingSvc.GetPlayer("Homeblest")
78+
case "!stats":
79+
name := inputString[1]
8080

81+
playerPointer, err := requestSvc.RequestPlayer(name, "pc-eu")
8182
if err != nil {
82-
if err == players.ErrorPlayerNotFound {
83-
// Player wasn't found in storage, get him from the API and store him
84-
player, err := requestSvc.RequestPlayer("pc-eu", "Homeblest")
85-
if err != nil {
86-
fmt.Println(err)
87-
return
88-
}
89-
addingSvc.AddPlayer(*player)
90-
} else {
91-
fmt.Println(err)
92-
return
93-
}
94-
83+
fmt.Println(err)
84+
return
9585
}
96-
97-
playerString := fmt.Sprintf("I tried contacting the PUBG API, did it work? playerName: %s", player.Name)
86+
playerString := fmt.Sprintf("I tried contacting the PUBG API, did it work? playerName: %s", playerPointer.Attributes.Name)
9887
s.ChannelMessageSend(m.ChannelID, playerString)
9988
}
10089

matches/match.go

+11-8
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,21 @@ package matches
22

33
import (
44
"errors"
5-
"time"
65
)
76

7+
// Lite describes a list of LiteData match objects
8+
type Lite struct {
9+
Data []LiteData `json:"data"`
10+
}
11+
12+
// LiteData is a lite version of a match Data object, only containing a type and a match ID each
13+
type LiteData struct {
14+
Type string `json:"type"`
15+
ID string `json:"id"`
16+
}
17+
818
// Match defines the properties of a PUBG match
919
type Match struct {
10-
ID string `jsonapi:"primary,match"`
11-
CreatedAt time.Time `jsonapi:"attr,createdAt,iso8601"`
12-
Duration int `jsonapi:"attr,duration"`
13-
GameMode string `jsonapi:"attr,gameMode"`
14-
PatchVersion string `jsonapi:"attr,patchVersion"`
15-
ShardID string `jsonapi:"attr,shardId"`
16-
TitleID string `jsonapi:"attr,titleId"`
1720
}
1821

1922
// ErrorMatchNotFound is used when trying to access a match that doesn't exist in the matches.Repository

players/player.go

+34-9
Original file line numberDiff line numberDiff line change
@@ -5,27 +5,52 @@ import (
55
"errors"
66
"time"
77

8+
"github.com/homeblest/pubg_stat_tracker/links"
89
"github.com/homeblest/pubg_stat_tracker/matches"
910
)
1011

12+
// Data defines the data properties of a player request from the PUBG API
13+
type Data struct {
14+
Players []Player `json:"data"`
15+
Links links.Link `json:"links"`
16+
Meta interface{} `json:"meta"` // Not used by the PUBG API right now, but we must declare it to unmarshal the JSON
17+
}
18+
19+
// attributes defines the attributes of a PUBG player.
20+
type attributes struct {
21+
CreatedAt time.Time `json:"createdAt"`
22+
Name string `json:"name"`
23+
PatchVersion string `json:"patchVersion"`
24+
ShardID string `json:"shardId"`
25+
Stats interface{} `json:"stats"` // Not used by the PUBG API right now, stats are actually in the seasons.
26+
TitleID string `json:"titleId"`
27+
UpdatedAt time.Time `json:"updatedAt"`
28+
}
29+
30+
type assets struct {
31+
Data []interface{} `json:"data"`
32+
}
33+
34+
type relationships struct {
35+
Assets assets `json:"assets"`
36+
Matches matches.Lite `json:"matches"`
37+
}
38+
1139
// Player defines the properties of a PUBG character
1240
type Player struct {
13-
ID string `jsonapi:"primary,player"`
14-
Name string `jsonapi:"attr,name"`
15-
ShardID string `jsonapi:"attr,shardId"`
16-
CreatedAt time.Time `jsonapi:"attr,createdAt,iso8601"`
17-
UpdatedAt time.Time `jsonapi:"attr,updatedAt,iso8601"`
18-
PatchVersion string `jsonapi:"attr,patchVersion"`
19-
TitleID string `jsonapi:"attr,titleId"`
20-
Matches []*matches.Match `jsonapi:"relation,matches"`
41+
Type string `json:"type"`
42+
ID string `json:"id"`
43+
Attributes attributes `json:"attributes"`
44+
Relationships relationships `json:"relationships"`
45+
Links links.Link `json:"links"`
2146
}
2247

2348
// ErrorPlayerNotFound is used when trying to access a player that doesn't exist in the players.Repository
2449
var ErrorPlayerNotFound = errors.New("Player not found")
2550

2651
// Repository provides access to the list of players
2752
type Repository interface {
28-
Get(id string) (Player, error)
53+
Get(id string) (*Player, error)
2954
Add(Player) error
3055
}
3156

requesting/const.go

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package requesting
2+
3+
const (
4+
pubgAPIBaseURL string = "https://api.playbattlegrounds.com"
5+
playersEndpoint string = "/players"
6+
pubgAPIBaseShardURL string = pubgAPIBaseURL + "/shards/%s%s"
7+
)

requesting/service.go

+47-40
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,15 @@
11
package requesting
22

33
import (
4-
"compress/gzip"
4+
"bytes"
5+
"encoding/json"
56
"errors"
67
"fmt"
7-
"io"
8+
"io/ioutil"
89
"net/http"
910
"net/url"
10-
"reflect"
1111

1212
"github.com/homeblest/pubg_stat_tracker/players"
13-
"github.com/slemgrim/jsonapi"
1413
)
1514

1615
// Service takes care of requesting data from the PUBG API
@@ -29,41 +28,43 @@ func NewService(APIKey string) Service {
2928
}
3029
}
3130

32-
func (s *service) RequestPlayer(shard, name string) (*players.Player, error) {
33-
parameters := url.Values{
34-
"filter[playerNames]": {name},
31+
func (s *service) RequestPlayer(name, shard string) (*players.Player, error) {
32+
players, err := s.RequestPlayers(name, shard)
33+
if err != nil {
34+
return nil, err
3535
}
36+
player := players[0]
37+
38+
fmt.Println("RequestPlayer:")
39+
fmt.Println(player.Attributes.Name)
3640

37-
endpointURL := fmt.Sprintf("https://api.playbattlegrounds.com/shards/%s/players?%s", shard, parameters.Encode())
41+
return &player, nil
42+
}
3843

39-
reader, err := httpRequest(endpointURL, s.APIKey)
44+
func (s *service) RequestPlayers(name, shard string) ([]players.Player, error) {
45+
apiURL := fmt.Sprintf(pubgAPIBaseShardURL, string(shard), playersEndpoint)
4046

47+
query := url.Values{"filter[playerNames]": {name}}
48+
49+
body, err := createRequest(apiURL, s.APIKey, query)
4150
if err != nil {
4251
return nil, err
4352
}
44-
result, err := jsonapi.UnmarshalManyPayload(*reader, reflect.TypeOf(new(players.Player)))
4553

54+
playersData := &players.Data{}
55+
56+
err = json.NewDecoder(body).Decode(playersData)
4657
if err != nil {
4758
return nil, err
4859
}
4960

50-
thePlayers := make([]*players.Player, len(result))
61+
players := *playersData
5162

52-
for idx, elt := range result {
53-
player, ok := elt.(*players.Player)
54-
if !ok {
55-
return nil, errors.New("Failed to convert players")
56-
}
57-
thePlayers[idx] = player
58-
}
59-
player := *thePlayers[0]
60-
fmt.Println(player.Name)
61-
62-
return &player, nil
63+
return players.Players, nil
6364
}
6465

65-
// Request makes a request to the PUBG API
66-
func httpRequest(url, key string) (*io.Reader, error) {
66+
// createRequest makes a http GET request to the PUBG API
67+
func createRequest(url, key string, query url.Values) (*bytes.Buffer, error) {
6768
// Create the request
6869
req, err := http.NewRequest("GET", url, nil)
6970

@@ -75,30 +76,36 @@ func httpRequest(url, key string) (*io.Reader, error) {
7576
req.Header.Set("Authorization", key)
7677
req.Header.Set("Accept", "application/vnd.api+json")
7778

79+
if query != nil {
80+
req.URL.RawQuery = query.Encode()
81+
}
82+
7883
// Send the request
79-
client := &http.Client{}
80-
response, err := client.Do(req)
84+
res, err := http.DefaultClient.Do(req)
8185

8286
if err != nil {
8387
return nil, err
8488
}
8589

86-
if response.StatusCode != 200 {
87-
response.Body.Close()
88-
return nil, fmt.Errorf("HTTP request failed: %s", response.Status)
90+
if res.StatusCode != http.StatusOK {
91+
switch res.StatusCode {
92+
case http.StatusUnauthorized:
93+
return nil, errors.New("API key invalid or missing")
94+
case http.StatusNotFound:
95+
return nil, errors.New("The specified resource was not found")
96+
case http.StatusUnsupportedMediaType:
97+
return nil, errors.New("Content type incorrect or not specified")
98+
case http.StatusTooManyRequests:
99+
return nil, errors.New("Too many requests")
100+
default:
101+
return nil, errors.New(res.Status)
102+
}
89103
}
90104

91-
// Retrieve response body
92-
var reader io.Reader
93-
switch response.Header.Get("Content-Encoding") {
94-
case "gzip":
95-
reader, err = gzip.NewReader(response.Body)
96-
if err != nil {
97-
return nil, err
98-
}
99-
default:
100-
reader = response.Body
105+
body, err := ioutil.ReadAll(res.Body)
106+
if err != nil {
107+
return nil, err
101108
}
102109

103-
return &reader, nil
110+
return bytes.NewBuffer(body), nil
104111
}

storage/memory.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,13 @@ func (m *MemoryPlayerStorage) Add(player players.Player) error {
1717
}
1818

1919
// Get retrieves the player from memory, if it exists
20-
func (m *MemoryPlayerStorage) Get(name string) (players.Player, error) {
20+
func (m *MemoryPlayerStorage) Get(name string) (*players.Player, error) {
2121
var emptyPlayer players.Player
2222

2323
for i := range m.players {
24-
if m.players[i].Name == name {
25-
return m.players[i], nil
24+
if m.players[i].Attributes.Name == name {
25+
return &m.players[i], nil
2626
}
2727
}
28-
return emptyPlayer, players.ErrorPlayerNotFound
28+
return &emptyPlayer, players.ErrorPlayerNotFound
2929
}

0 commit comments

Comments
 (0)