Compare commits

..

3 Commits

Author SHA1 Message Date
milhound f773677bcf Merge branch 'main' of github.com:Milhound/battlenetapi 2025-03-20 23:50:50 -05:00
Daniel Milholland 47f4e2f783 Initial commit 2025-03-20 23:43:48 -05:00
milhound ccc8eb0da1 Implemented World of Warcraft - Game Data APIs - PvP Season API 2025-03-20 23:39:47 -05:00
8 changed files with 294 additions and 7 deletions
+2
View File
@@ -0,0 +1,2 @@
.env
*.json
+16 -4
View File
@@ -1,9 +1,21 @@
MIT License MIT License
Copyright (c) 2025 Milhound Copyright (c) 2025 Daniel Milholland
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
-3
View File
@@ -1,3 +0,0 @@
# battlenetapi
A simple Golang library for Battle.net APIs
+91
View File
@@ -0,0 +1,91 @@
package battlenet
import (
"encoding/json"
"fmt"
"io"
"net/http"
"strings"
)
const (
STATIC string = "static"
DYNAMIC string = "dynamic"
PROFILE string = "profile"
)
const (
US string = "us"
EU string = "eu"
KR string = "kr"
TW string = "tw"
)
type clientCredentialsAPI struct {
AccessToken string `json:"access_token"`
TokenType string `json:"token_type"`
ExpiresIn int `json:"expires_in"`
Sub string `json:"sub"`
}
type BattleNetAPIParams struct {
UrlOrEndpoint string
Namespace string
Region string
Token string
}
func GetAccessToken(clientID string, clientSecret string) clientCredentialsAPI {
const authenticationUrl string = "https://oauth.battle.net/token"
resp, err := http.Post(authenticationUrl, "application/x-www-form-urlencoded", strings.NewReader(fmt.Sprintf("grant_type=client_credentials&client_id=%s&client_secret=%s", clientID, clientSecret)))
if err != nil {
fmt.Println(err)
}
defer resp.Body.Close()
respBody, err := io.ReadAll(resp.Body)
if err != nil {
fmt.Println(err)
}
if resp.StatusCode != 200 {
fmt.Println(resp.Status)
panic("Failed to get access token")
}
var credentials clientCredentialsAPI
err = json.Unmarshal(respBody, &credentials)
if err != nil {
fmt.Println(err)
panic("Failed to parse access token response")
}
fmt.Println("Access Token: ", credentials.AccessToken)
return credentials
}
func BattleNetAPI(params BattleNetAPIParams) []byte {
var requestURL string
if strings.HasPrefix(params.UrlOrEndpoint, "http") {
requestURL = params.UrlOrEndpoint
} else {
var baseURL string = fmt.Sprintf("https://%s.api.blizzard.com", params.Region)
requestURL = fmt.Sprintf("%s%s?namespace=%s-%s", baseURL, params.UrlOrEndpoint, params.Namespace, params.Region)
}
fmt.Println(requestURL)
req, err := http.NewRequest("GET", requestURL, nil)
if err != nil {
fmt.Println(err)
}
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", params.Token))
resp, err := http.DefaultClient.Do(req)
if err != nil {
fmt.Println(err)
}
defer resp.Body.Close()
respBody, err := io.ReadAll(resp.Body)
if err != nil {
fmt.Println(err)
}
if resp.StatusCode != 200 {
fmt.Println(resp.Status)
panic("Failed to process the request")
}
return (respBody)
}
+52
View File
@@ -0,0 +1,52 @@
package main
import (
"encoding/json"
"fmt"
"os"
"battlenetapi/battlenet"
"battlenetapi/wow/gamedata"
"github.com/joho/godotenv"
)
func main() {
// Load the .env file
err := godotenv.Load(".env")
if err != nil {
fmt.Println("Error loading .env file")
}
// Authenticate with the BattleNet API
clientID := os.Getenv("CLIENT_ID")
clientSecret := os.Getenv("CLIENT_SECRET")
credentials := battlenet.GetAccessToken(clientID, clientSecret)
// Get the current PVP season
params := battlenet.BattleNetAPIParams{
UrlOrEndpoint: gamedata.PvpSeasonIndexEndpoint,
Namespace: battlenet.DYNAMIC,
Region: battlenet.US,
Token: credentials.AccessToken,
}
response := battlenet.BattleNetAPI(params)
var pvpIndex gamedata.PvpSeasonIndexAPI
json.Unmarshal(response, &pvpIndex)
// Get the leaderboards for the current PVP season and Bracket
shuffleOrBlitz := fmt.Sprintf("blitz-%s", gamedata.ClassDemonHunterHavoc)
params.UrlOrEndpoint = fmt.Sprintf(gamedata.PvpLeaderboardEndpoint, pvpIndex.CurrentSeason.ID, shuffleOrBlitz)
response = battlenet.BattleNetAPI(params)
file, err := os.Create(fmt.Sprintf("pvp_season_%d_leaderboard-bracket_%s.json", pvpIndex.CurrentSeason.ID, shuffleOrBlitz))
if err != nil {
fmt.Println("Error creating file:", err)
return
}
defer file.Close()
_, err = file.Write(response)
if err != nil {
fmt.Println("Error writing data to file:", err)
return
}
}
+5
View File
@@ -0,0 +1,5 @@
module battlenetapi
go 1.24.1
require github.com/joho/godotenv v1.5.1
+2
View File
@@ -0,0 +1,2 @@
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
+126
View File
@@ -0,0 +1,126 @@
package gamedata
const (
PvpSeasonIndexEndpoint string = "/data/wow/pvp-season/index"
PvpSeasonEndpoint string = "/data/wow/pvp-season/%d"
PvpLeaderboardIndexEndpoint string = "/data/wow/pvp-season/%d/pvp-leaderboard/index"
PvpLeaderboardEndpoint string = "/data/wow/pvp-season/%d/pvp-leaderboard/%s"
PvpRewardsIndexEndpoint string = "/data/wow/pvp-season/%d/pvp-reward/index"
)
const (
Bracket2v2 string = "2v2"
Bracket3v3 string = "3v3"
BracketRBG string = "rbg"
ClassDeathKnightBlood string = "deathknight-blood"
ClassDeathKnightFrost string = "deathknight-frost"
ClassDeathKnightUnholy string = "deathknight-unholy"
ClassDemonHunterHavoc string = "demonhunter-havoc"
ClassDemonHunterVengeance string = "demonhunter-vengeance"
ClassDruidBalance string = "druid-balance"
ClassDruidFeral string = "druid-feral"
ClassDruidGuardian string = "druid-guardian"
ClassDruidRestoration string = "druid-restoration"
ClassEvokerAugmentation string = "evoker-augmentation"
ClassEvokerDevasation string = "evoker-devastation"
ClassEvokerPreservation string = "evoker-preservation"
ClassHunterBeastMastery string = "hunter-beastmastery"
ClassHunterMarksmanship string = "hunter-marksmanship"
ClassHunterSurvival string = "hunter-survival"
ClassMageArcane string = "mage-arcane"
ClassMageFire string = "mage-fire"
ClassMageFrost string = "mage-frost"
ClassMonkBrewmaster string = "monk-brewmaster"
ClassMonkMistweaver string = "monk-mistweaver"
ClassMonkWindwalker string = "monk-windwalker"
ClassPaladinHoly string = "paladin-holy"
ClassPaladinProtection string = "paladin-protection"
ClassPaladinRetribution string = "paladin-retribution"
ClassPriestDiscipline string = "priest-discipline"
ClassPriestHoly string = "priest-holy"
ClassPriestShadow string = "priest-shadow"
ClassRogueAssassination string = "rogue-assassination"
ClassRogueOutlaw string = "rogue-outlaw"
ClassRogueSubtlety string = "rogue-subtlety"
ClassShamanElemental string = "shaman-elemental"
ClassShamanEnhancement string = "shaman-enhancement"
ClassShamanRestoration string = "shaman-restoration"
ClassWarlockAffliction string = "warlock-affliction"
ClassWarlockDemonology string = "warlock-demonology"
ClassWarlockDestruction string = "warlock-destruction"
ClassWarriorArms string = "warrior-arms"
ClassWarriorFury string = "warrior-fury"
ClassWarriorProtection string = "warrior-protection"
)
type href struct {
Href string `json:"href"`
}
type idAndKey struct {
ID int `json:"id"`
Key href `json:"key"`
}
type idAndType struct {
ID int `json:"id"`
Type string `json:"type"`
}
type PvpSeasonIndexAPI struct {
Seasons []idAndKey `json:"seasons"`
CurrentSeason idAndKey `json:"current_season"`
}
type PvpSeasonAPI struct {
ID int `json:"id"`
Leaderboards href `json:"leaderboards"`
Rewards href `json:"rewards"`
SeasonStartTimestamp int `json:"season_start_timestamp"`
SeasonEndTimestamp int `json:"season_end_timestamp"`
}
type PvpLeaderboardAPI struct {
Season idAndKey `json:"season"`
Name string `json:"name"`
Bracket idAndType `json:"bracket"`
Entries []struct {
Character struct {
Name string `json:"name"`
ID int `json:"id"`
Realm struct {
Key href `json:"key"`
ID int `json:"id"`
Slug string `json:"slug"`
} `json:"realm"`
} `json:"character"`
Faction struct {
Type string `json:"type"`
} `json:"faction"`
Rank int `json:"rank"`
Rating int `json:"rating"`
SeasonMatchStatistics struct {
Played int `json:"played"`
Won int `json:"won"`
Lost int `json:"lost"`
} `json:"season_match_statistics"`
Tier idAndKey `json:"tier"`
} `json:"entries"`
}
type PvpRewardsIndexAPI struct {
Season idAndKey `json:"season"`
Rewards struct {
Bracket idAndType `json:"bracket"`
Achievement struct {
ID int `json:"id"`
Key href `json:"key"`
Name string `json:"name"`
} `json:"achievement"`
RatingCutOff int `json:"rating_cutoff"`
Faction struct {
Type string `json:"type"`
Name string `json:"name"`
} `json:"faction"`
} `json:"rewards"`
}