Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
f773677bcf
|
|||
| 47f4e2f783 | |||
|
ccc8eb0da1
|
@@ -0,0 +1,2 @@
|
||||
.env
|
||||
*.json
|
||||
@@ -1,9 +1,21 @@
|
||||
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.
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
module battlenetapi
|
||||
|
||||
go 1.24.1
|
||||
|
||||
require github.com/joho/godotenv v1.5.1
|
||||
@@ -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=
|
||||
@@ -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"`
|
||||
}
|
||||
Reference in New Issue
Block a user