2016-05-12 19:37:17 +02:00
// d3.go
package modules
import (
"dev.ixab.de/WGSWATBP/WGoSlackBot/util"
"encoding/json"
"errors"
"github.com/leekchan/accounting"
"io/ioutil"
"log"
"net/http"
"strconv"
"strings"
"time"
)
var (
d3apikey string
d3botname string
d3boticon string
bnetd3apiurl string = "https://eu.api.battle.net/d3/"
)
func init () {
MsgHandlers [ "d3" ] = d3HandleMessage
log . Println ( "Initializing d3 module" )
go setd3Config ()
}
func setd3Config () {
time . Sleep ( 5 * time . Second )
d3apikey = ModParams [ "d3apikey" ]
d3botname = ModParams [ "d3botname" ]
d3boticon = ModParams [ "d3boticon" ]
}
func d3HandleMessage ( payload * WebhookPayload ) {
if payload . Command == "/d3" {
reply := handleD3Request ( payload , true )
if reply != "" {
2016-05-13 15:17:45 +02:00
SayCh <- GeneratePayload ( payload . ChannelName , d3boticon , reply , d3botname )
2016-05-12 19:37:17 +02:00
}
}
if payload . TriggerWord == "!d3" {
reply := handleD3Request ( payload , false )
if reply != "" {
2016-05-13 15:17:45 +02:00
SayCh <- GeneratePayload ( payload . ChannelName , d3boticon , reply , d3botname )
2016-05-12 19:37:17 +02:00
}
}
}
2016-05-13 15:17:45 +02:00
2016-05-12 19:37:17 +02:00
func handleD3Request ( payload * WebhookPayload , isCommand bool ) string {
var tokMod int
if isCommand {
tokMod = 0
} else {
tokMod = 1
}
tok := strings . Split ( payload . Text , " " )
var result string
if len ( tok ) < 2 + tokMod {
if tok [ 0 + tokMod ] == "" {
SayCh <- GeneratePayload ( "@" + payload . UserName , d3boticon , "Es wurde kein Battletag angegeben." , d3botname )
} else {
battletag , err := checkBattleTag ( tok [ 0 + tokMod ])
if err != nil {
log . Println ( err )
2016-05-13 15:17:45 +02:00
SayCh <- GeneratePayload ( "@" + payload . UserName , d3boticon , "Battletag " + tok [ 0 + tokMod ] + " fehlerhaft oder nicht gefunden." , d3botname )
}
profile , err := getD3Profile ( battletag )
if err != nil {
log . Println ( err )
SayCh <- GeneratePayload ( "@" + payload . UserName , d3boticon , "Battletag " + tok [ 0 + tokMod ] + " fehlerhaft oder nicht gefunden." , d3botname )
result = ""
} else {
readableBattletag := strings . Replace ( battletag , "-" , "#" , 1 )
lastUpdate := time . Unix ( int64 ( profile . LastUpdated ), 0 ). Format ( time . RFC850 )
paragon := util . NumberToString ( profile . ParagonLevel , '.' )
paragonHardcore := util . NumberToString ( profile . ParagonLevelHardcore , '.' )
paragonSeason := util . NumberToString ( profile . ParagonLevelSeason , '.' )
paragonSeasonHardcore := util . NumberToString ( profile . ParagonLevelSeasonHardcore , '.' )
var allHeroes string
for i , hero := range profile . Heroes {
allHeroes += classToIcon ( hero . Class ) + "" + heroTypeToIconString ( hero . Seasonal , hero . Hardcore ) + " " + hero . Name + " " + strconv . Itoa ( hero . Level )
if i + 1 != len ( profile . Heroes ) {
allHeroes += ", "
}
}
result +=
"*" + readableBattletag + "*\n" +
"*Last Update:* " + lastUpdate + "\n" +
"*Paragon:* " + paragon + ", :d3hardcore:" + paragonHardcore + ", :d3season:" + paragonSeason + ", :d3seasonhardcore:" + paragonSeasonHardcore + "\n" +
"*Available Heroes:* " + allHeroes + "\n" +
"https://eu.battle.net/d3/en/profile/" + battletag + "/"
2016-05-12 19:37:17 +02:00
}
}
}
if len ( tok ) >= 2 + tokMod {
battletag , err := checkBattleTag ( tok [ 0 + tokMod ])
if err != nil {
log . Println ( err )
2016-05-13 15:17:45 +02:00
SayCh <- GeneratePayload ( "@" + payload . UserName , d3boticon , "Battletag " + tok [ 0 + tokMod ] + " fehlerhaft oder nicht gefunden." , d3botname )
2016-05-12 19:37:17 +02:00
} else {
2016-05-13 15:17:45 +02:00
profile , err := getD3Profile ( battletag )
if err != nil {
log . Println ( err )
SayCh <- GeneratePayload ( "@" + payload . UserName , d3boticon , "Battletag " + tok [ 0 + tokMod ] + " fehlerhaft oder nicht gefunden." , d3botname )
} else {
hit := false
for _ , currenthero := range profile . Heroes {
if strings . ToLower ( currenthero . Name ) == strings . ToLower ( tok [ 1 + tokMod ]) {
hero := getD3Hero ( battletag , currenthero . ID )
hit = true
// TODO: hero data output
result = "Hero: " + accounting . FormatNumberFloat64 ( hero . Stats . Damage , 0 , "." , "," ) + "\n" +
"https://eu.battle.net/d3/en/profile/" + battletag + "/hero/" + strconv . Itoa ( currenthero . ID )
result = "Hero: " + "https://eu.battle.net/d3/en/profile/" + battletag + "/hero/" + strconv . Itoa ( currenthero . ID )
}
}
if ! hit {
SayCh <- GeneratePayload ( "@" + payload . UserName , d3boticon , "Es konnte kein Charakter mit dem Namen *" + tok [ 1 + tokMod ] + "* unter dem Battletag *" + tok [ 0 + tokMod ] + "* gefunden werden." , d3botname )
2016-05-12 19:37:17 +02:00
}
}
}
}
return result
}
2016-05-13 15:17:45 +02:00
func classToIcon ( class string ) string {
switch class {
case "witch-doctor" :
return ":d3witchdoctor:"
case "barbarian" :
return ":d3barbarian:"
case "crusader" :
return ":d3crusader:"
case "demon-hunter" :
return ":d3demonhunter:"
case "wizard" :
return ":d3wizard:"
case "monk" :
return ":d3monk:"
default :
return ""
}
}
func heroTypeToIconString ( seasonal bool , hardcore bool ) string {
result := ""
if seasonal && hardcore {
result = ":d3seasonhardcore:"
} else {
if seasonal {
result = ":d3season:"
}
if hardcore {
result = ":d3hardcore:"
}
}
return result
}
2016-05-12 19:37:17 +02:00
func checkBattleTag ( battletag string ) ( tag string , e error ) {
if strings . ContainsRune ( battletag , '-' ) {
return battletag , nil
}
if strings . ContainsRune ( battletag , '#' ) {
return strings . Replace ( battletag , "#" , "-" , 1 ), nil
}
return "" , errors . New ( "invalid battletag" )
}
2016-05-13 15:17:45 +02:00
func getD3Profile ( battletag string ) ( profile d3Profile , e error ) {
2016-05-12 19:37:17 +02:00
requesturl := bnetd3apiurl + "profile/" + battletag + "/?locale=en_GB&apikey=" + d3apikey
var result d3Profile
resp , err := http . Get ( requesturl )
if err != nil {
log . Println ( err )
} else {
reader , err := ioutil . ReadAll ( resp . Body )
if err != nil {
log . Println ( err )
} else {
defer resp . Body . Close ()
json . Unmarshal ( reader , & result )
2016-05-13 15:17:45 +02:00
if result . BattleTag == "" {
return result , errors . New ( "Profile not found" )
}
return result , nil
2016-05-12 19:37:17 +02:00
}
}
2016-05-13 15:17:45 +02:00
return result , nil
2016-05-12 19:37:17 +02:00
}
func getD3Hero ( battletag string , heroid int ) d3Hero {
heroidstring := strconv . Itoa ( heroid )
requesturl := bnetd3apiurl + "profile/" + battletag + "/hero/" + heroidstring + "?locale=en_GB&apikey=" + d3apikey
var result d3Hero
resp , err := http . Get ( requesturl )
if err != nil {
log . Println ( err )
} else {
reader , err := ioutil . ReadAll ( resp . Body )
if err != nil {
log . Println ( err )
} else {
defer resp . Body . Close ()
json . Unmarshal ( reader , & result )
return result
}
}
return result
}
type d3Profile struct {
BattleTag string `json:"battleTag"`
Blacksmith struct {
Level int `json:"level"`
Slug string `json:"slug"`
StepCurrent int `json:"stepCurrent"`
StepMax int `json:"stepMax"`
} `json:"blacksmith"`
BlacksmithHardcore struct {
Level int `json:"level"`
Slug string `json:"slug"`
StepCurrent int `json:"stepCurrent"`
StepMax int `json:"stepMax"`
} `json:"blacksmithHardcore"`
BlacksmithSeason struct {
Level int `json:"level"`
Slug string `json:"slug"`
StepCurrent int `json:"stepCurrent"`
StepMax int `json:"stepMax"`
} `json:"blacksmithSeason"`
BlacksmithSeasonHardcore struct {
Level int `json:"level"`
Slug string `json:"slug"`
StepCurrent int `json:"stepCurrent"`
StepMax int `json:"stepMax"`
} `json:"blacksmithSeasonHardcore"`
FallenHeroes [] interface {} `json:"fallenHeroes"`
GuildName string `json:"guildName"`
Heroes [] struct {
Class string `json:"class"`
Dead bool `json:"dead"`
Gender int `json:"gender"`
Hardcore bool `json:"hardcore"`
ID int `json:"id"`
Kills struct {
Elites int `json:"elites"`
} `json:"kills"`
Last_updated int `json:"last-updated"`
Level int `json:"level"`
Name string `json:"name"`
ParagonLevel int `json:"paragonLevel"`
Seasonal bool `json:"seasonal"`
} `json:"heroes"`
HighestHardcoreLevel int `json:"highestHardcoreLevel"`
Jeweler struct {
Level int `json:"level"`
Slug string `json:"slug"`
StepCurrent int `json:"stepCurrent"`
StepMax int `json:"stepMax"`
} `json:"jeweler"`
JewelerHardcore struct {
Level int `json:"level"`
Slug string `json:"slug"`
StepCurrent int `json:"stepCurrent"`
StepMax int `json:"stepMax"`
} `json:"jewelerHardcore"`
JewelerSeason struct {
Level int `json:"level"`
Slug string `json:"slug"`
StepCurrent int `json:"stepCurrent"`
StepMax int `json:"stepMax"`
} `json:"jewelerSeason"`
JewelerSeasonHardcore struct {
Level int `json:"level"`
Slug string `json:"slug"`
StepCurrent int `json:"stepCurrent"`
StepMax int `json:"stepMax"`
} `json:"jewelerSeasonHardcore"`
Kills struct {
Elites int `json:"elites"`
HardcoreMonsters int `json:"hardcoreMonsters"`
Monsters int `json:"monsters"`
} `json:"kills"`
LastHeroPlayed int `json:"lastHeroPlayed"`
LastUpdated int `json:"lastUpdated"`
Mystic struct {
Level int `json:"level"`
Slug string `json:"slug"`
StepCurrent int `json:"stepCurrent"`
StepMax int `json:"stepMax"`
} `json:"mystic"`
MysticHardcore struct {
Level int `json:"level"`
Slug string `json:"slug"`
StepCurrent int `json:"stepCurrent"`
StepMax int `json:"stepMax"`
} `json:"mysticHardcore"`
MysticSeason struct {
Level int `json:"level"`
Slug string `json:"slug"`
StepCurrent int `json:"stepCurrent"`
StepMax int `json:"stepMax"`
} `json:"mysticSeason"`
MysticSeasonHardcore struct {
Level int `json:"level"`
Slug string `json:"slug"`
StepCurrent int `json:"stepCurrent"`
StepMax int `json:"stepMax"`
} `json:"mysticSeasonHardcore"`
ParagonLevel int `json:"paragonLevel"`
ParagonLevelHardcore int `json:"paragonLevelHardcore"`
ParagonLevelSeason int `json:"paragonLevelSeason"`
ParagonLevelSeasonHardcore int `json:"paragonLevelSeasonHardcore"`
Progression struct {
Act1 bool `json:"act1"`
Act2 bool `json:"act2"`
Act3 bool `json:"act3"`
Act4 bool `json:"act4"`
Act5 bool `json:"act5"`
} `json:"progression"`
SeasonalProfiles struct {
Season0 struct {
HighestHardcoreLevel int `json:"highestHardcoreLevel"`
Kills struct {
Elites int `json:"elites"`
HardcoreMonsters int `json:"hardcoreMonsters"`
Monsters int `json:"monsters"`
} `json:"kills"`
ParagonLevel int `json:"paragonLevel"`
ParagonLevelHardcore int `json:"paragonLevelHardcore"`
Progression struct {
Act1 bool `json:"act1"`
Act2 bool `json:"act2"`
Act3 bool `json:"act3"`
Act4 bool `json:"act4"`
Act5 bool `json:"act5"`
} `json:"progression"`
SeasonID int `json:"seasonId"`
TimePlayed struct {
Barbarian float64 `json:"barbarian"`
Crusader float64 `json:"crusader"`
Demon_hunter float64 `json:"demon-hunter"`
Monk float64 `json:"monk"`
Witch_doctor int `json:"witch-doctor"`
Wizard float64 `json:"wizard"`
} `json:"timePlayed"`
} `json:"season0"`
Season1 struct {
HighestHardcoreLevel int `json:"highestHardcoreLevel"`
Kills struct {
Elites int `json:"elites"`
HardcoreMonsters int `json:"hardcoreMonsters"`
Monsters int `json:"monsters"`
} `json:"kills"`
ParagonLevel int `json:"paragonLevel"`
ParagonLevelHardcore int `json:"paragonLevelHardcore"`
Progression struct {
Act1 bool `json:"act1"`
Act2 bool `json:"act2"`
Act3 bool `json:"act3"`
Act4 bool `json:"act4"`
Act5 bool `json:"act5"`
} `json:"progression"`
SeasonID int `json:"seasonId"`
TimePlayed struct {
Barbarian int `json:"barbarian"`
Crusader int `json:"crusader"`
Demon_hunter int `json:"demon-hunter"`
Monk int `json:"monk"`
Witch_doctor int `json:"witch-doctor"`
Wizard int `json:"wizard"`
} `json:"timePlayed"`
} `json:"season1"`
Season2 struct {
HighestHardcoreLevel int `json:"highestHardcoreLevel"`
Kills struct {
Elites int `json:"elites"`
HardcoreMonsters int `json:"hardcoreMonsters"`
Monsters int `json:"monsters"`
} `json:"kills"`
ParagonLevel int `json:"paragonLevel"`
ParagonLevelHardcore int `json:"paragonLevelHardcore"`
Progression struct {
Act1 bool `json:"act1"`
Act2 bool `json:"act2"`
Act3 bool `json:"act3"`
Act4 bool `json:"act4"`
Act5 bool `json:"act5"`
} `json:"progression"`
SeasonID int `json:"seasonId"`
TimePlayed struct {
Barbarian int `json:"barbarian"`
Crusader int `json:"crusader"`
Demon_hunter int `json:"demon-hunter"`
Monk int `json:"monk"`
Witch_doctor int `json:"witch-doctor"`
Wizard int `json:"wizard"`
} `json:"timePlayed"`
} `json:"season2"`
Season3 struct {
HighestHardcoreLevel int `json:"highestHardcoreLevel"`
Kills struct {
Elites int `json:"elites"`
HardcoreMonsters int `json:"hardcoreMonsters"`
Monsters int `json:"monsters"`
} `json:"kills"`
ParagonLevel int `json:"paragonLevel"`
ParagonLevelHardcore int `json:"paragonLevelHardcore"`
Progression struct {
Act1 bool `json:"act1"`
Act2 bool `json:"act2"`
Act3 bool `json:"act3"`
Act4 bool `json:"act4"`
Act5 bool `json:"act5"`
} `json:"progression"`
SeasonID int `json:"seasonId"`
TimePlayed struct {
Barbarian float64 `json:"barbarian"`
Crusader int `json:"crusader"`
Demon_hunter int `json:"demon-hunter"`
Monk int `json:"monk"`
Witch_doctor int `json:"witch-doctor"`
Wizard int `json:"wizard"`
} `json:"timePlayed"`
} `json:"season3"`
Season4 struct {
HighestHardcoreLevel int `json:"highestHardcoreLevel"`
Kills struct {
Elites int `json:"elites"`
HardcoreMonsters int `json:"hardcoreMonsters"`
Monsters int `json:"monsters"`
} `json:"kills"`
ParagonLevel int `json:"paragonLevel"`
ParagonLevelHardcore int `json:"paragonLevelHardcore"`
Progression struct {
Act1 bool `json:"act1"`
Act2 bool `json:"act2"`
Act3 bool `json:"act3"`
Act4 bool `json:"act4"`
Act5 bool `json:"act5"`
} `json:"progression"`
SeasonID int `json:"seasonId"`
TimePlayed struct {
Barbarian float64 `json:"barbarian"`
Crusader int `json:"crusader"`
Demon_hunter int `json:"demon-hunter"`
Monk int `json:"monk"`
Witch_doctor int `json:"witch-doctor"`
Wizard int `json:"wizard"`
} `json:"timePlayed"`
} `json:"season4"`
Season5 struct {
HighestHardcoreLevel int `json:"highestHardcoreLevel"`
Kills struct {
Elites int `json:"elites"`
HardcoreMonsters int `json:"hardcoreMonsters"`
Monsters int `json:"monsters"`
} `json:"kills"`
ParagonLevel int `json:"paragonLevel"`
ParagonLevelHardcore int `json:"paragonLevelHardcore"`
Progression struct {
Act1 bool `json:"act1"`
Act2 bool `json:"act2"`
Act3 bool `json:"act3"`
Act4 bool `json:"act4"`
Act5 bool `json:"act5"`
} `json:"progression"`
SeasonID int `json:"seasonId"`
TimePlayed struct {
Barbarian int `json:"barbarian"`
Crusader float64 `json:"crusader"`
Demon_hunter int `json:"demon-hunter"`
Monk int `json:"monk"`
Witch_doctor int `json:"witch-doctor"`
Wizard int `json:"wizard"`
} `json:"timePlayed"`
} `json:"season5"`
Season6 struct {
HighestHardcoreLevel int `json:"highestHardcoreLevel"`
Kills struct {
Elites int `json:"elites"`
HardcoreMonsters int `json:"hardcoreMonsters"`
Monsters int `json:"monsters"`
} `json:"kills"`
ParagonLevel int `json:"paragonLevel"`
ParagonLevelHardcore int `json:"paragonLevelHardcore"`
Progression struct {
Act1 bool `json:"act1"`
Act2 bool `json:"act2"`
Act3 bool `json:"act3"`
Act4 bool `json:"act4"`
Act5 bool `json:"act5"`
} `json:"progression"`
SeasonID int `json:"seasonId"`
TimePlayed struct {
Barbarian int `json:"barbarian"`
Crusader int `json:"crusader"`
Demon_hunter int `json:"demon-hunter"`
Monk int `json:"monk"`
Witch_doctor int `json:"witch-doctor"`
Wizard int `json:"wizard"`
} `json:"timePlayed"`
} `json:"season6"`
} `json:"seasonalProfiles"`
TimePlayed struct {
Barbarian float64 `json:"barbarian"`
Crusader float64 `json:"crusader"`
Demon_hunter float64 `json:"demon-hunter"`
Monk float64 `json:"monk"`
Witch_doctor int `json:"witch-doctor"`
Wizard float64 `json:"wizard"`
} `json:"timePlayed"`
}
type d3Hero struct {
Class string `json:"class"`
Dead bool `json:"dead"`
Followers struct {
Enchantress struct {
Items struct {} `json:"items"`
Level int `json:"level"`
Skills [] struct {
Skill struct {
Description string `json:"description"`
Icon string `json:"icon"`
Level int `json:"level"`
Name string `json:"name"`
SimpleDescription string `json:"simpleDescription"`
SkillCalcID string `json:"skillCalcId"`
Slug string `json:"slug"`
TooltipURL string `json:"tooltipUrl"`
} `json:"skill"`
} `json:"skills"`
Slug string `json:"slug"`
Stats struct {
ExperienceBonus int `json:"experienceBonus"`
GoldFind int `json:"goldFind"`
MagicFind int `json:"magicFind"`
} `json:"stats"`
} `json:"enchantress"`
Scoundrel struct {
Items struct {
MainHand struct {
DisplayColor string `json:"displayColor"`
Icon string `json:"icon"`
ID string `json:"id"`
Name string `json:"name"`
TooltipParams string `json:"tooltipParams"`
} `json:"mainHand"`
Special struct {
DisplayColor string `json:"displayColor"`
Icon string `json:"icon"`
ID string `json:"id"`
Name string `json:"name"`
TooltipParams string `json:"tooltipParams"`
} `json:"special"`
} `json:"items"`
Level int `json:"level"`
Skills [] struct {
Skill struct {
Description string `json:"description"`
Icon string `json:"icon"`
Level int `json:"level"`
Name string `json:"name"`
SimpleDescription string `json:"simpleDescription"`
SkillCalcID string `json:"skillCalcId"`
Slug string `json:"slug"`
TooltipURL string `json:"tooltipUrl"`
} `json:"skill"`
} `json:"skills"`
Slug string `json:"slug"`
Stats struct {
ExperienceBonus int `json:"experienceBonus"`
GoldFind int `json:"goldFind"`
MagicFind int `json:"magicFind"`
} `json:"stats"`
} `json:"scoundrel"`
Templar struct {
Items struct {} `json:"items"`
Level int `json:"level"`
Skills [] struct {
Skill struct {
Description string `json:"description"`
Icon string `json:"icon"`
Level int `json:"level"`
Name string `json:"name"`
SimpleDescription string `json:"simpleDescription"`
SkillCalcID string `json:"skillCalcId"`
Slug string `json:"slug"`
TooltipURL string `json:"tooltipUrl"`
} `json:"skill"`
} `json:"skills"`
Slug string `json:"slug"`
Stats struct {
ExperienceBonus int `json:"experienceBonus"`
GoldFind int `json:"goldFind"`
MagicFind int `json:"magicFind"`
} `json:"stats"`
} `json:"templar"`
} `json:"followers"`
Gender int `json:"gender"`
Hardcore bool `json:"hardcore"`
ID int `json:"id"`
Items struct {} `json:"items"`
Kills struct {
Elites int `json:"elites"`
} `json:"kills"`
Last_updated int `json:"last-updated"`
LegendaryPowers [] interface {} `json:"legendaryPowers"`
Level int `json:"level"`
Name string `json:"name"`
ParagonLevel int `json:"paragonLevel"`
Progression struct {
Act1 struct {
Completed bool `json:"completed"`
CompletedQuests [] struct {
Name string `json:"name"`
Slug string `json:"slug"`
} `json:"completedQuests"`
} `json:"act1"`
Act2 struct {
Completed bool `json:"completed"`
CompletedQuests [] struct {
Name string `json:"name"`
Slug string `json:"slug"`
} `json:"completedQuests"`
} `json:"act2"`
Act3 struct {
Completed bool `json:"completed"`
CompletedQuests [] struct {
Name string `json:"name"`
Slug string `json:"slug"`
} `json:"completedQuests"`
} `json:"act3"`
Act4 struct {
Completed bool `json:"completed"`
CompletedQuests [] struct {
Name string `json:"name"`
Slug string `json:"slug"`
} `json:"completedQuests"`
} `json:"act4"`
Act5 struct {
Completed bool `json:"completed"`
CompletedQuests [] struct {
Name string `json:"name"`
Slug string `json:"slug"`
} `json:"completedQuests"`
} `json:"act5"`
} `json:"progression"`
SeasonCreated int `json:"seasonCreated"`
Seasonal bool `json:"seasonal"`
Skills struct {
Active [] struct {
Rune struct {
Description string `json:"description"`
Level int `json:"level"`
Name string `json:"name"`
Order int `json:"order"`
SimpleDescription string `json:"simpleDescription"`
SkillCalcID string `json:"skillCalcId"`
Slug string `json:"slug"`
TooltipParams string `json:"tooltipParams"`
Type string `json:"type"`
} `json:"rune"`
Skill struct {
CategorySlug string `json:"categorySlug"`
Description string `json:"description"`
Icon string `json:"icon"`
Level int `json:"level"`
Name string `json:"name"`
SimpleDescription string `json:"simpleDescription"`
SkillCalcID string `json:"skillCalcId"`
Slug string `json:"slug"`
TooltipURL string `json:"tooltipUrl"`
} `json:"skill"`
} `json:"active"`
Passive [] struct {
Skill struct {
Description string `json:"description"`
Flavor string `json:"flavor"`
Icon string `json:"icon"`
Level int `json:"level"`
Name string `json:"name"`
SkillCalcID string `json:"skillCalcId"`
Slug string `json:"slug"`
TooltipURL string `json:"tooltipUrl"`
} `json:"skill"`
} `json:"passive"`
} `json:"skills"`
Stats struct {
ArcaneResist int `json:"arcaneResist"`
Armor int `json:"armor"`
AttackSpeed int `json:"attackSpeed"`
BlockAmountMax int `json:"blockAmountMax"`
BlockAmountMin int `json:"blockAmountMin"`
BlockChance int `json:"blockChance"`
ColdResist int `json:"coldResist"`
CritChance float64 `json:"critChance"`
CritDamage float64 `json:"critDamage"`
Damage float64 `json:"damage"`
DamageIncrease int `json:"damageIncrease"`
DamageReduction int `json:"damageReduction"`
Dexterity int `json:"dexterity"`
FireResist int `json:"fireResist"`
GoldFind int `json:"goldFind"`
Healing float64 `json:"healing"`
Intelligence int `json:"intelligence"`
Life int `json:"life"`
LifeOnHit int `json:"lifeOnHit"`
LifePerKill int `json:"lifePerKill"`
LifeSteal int `json:"lifeSteal"`
LightningResist int `json:"lightningResist"`
MagicFind int `json:"magicFind"`
PhysicalResist int `json:"physicalResist"`
PoisonResist int `json:"poisonResist"`
PrimaryResource int `json:"primaryResource"`
SecondaryResource int `json:"secondaryResource"`
Strength int `json:"strength"`
Thorns int `json:"thorns"`
Toughness float64 `json:"toughness"`
Vitality int `json:"vitality"`
} `json:"stats"`
}