From c2071075171e7764f0e6e9a1f717bfe5b621d2c1 Mon Sep 17 00:00:00 2001 From: Ronoaldo JLP Date: Sat, 17 Dec 2016 22:26:38 +0000 Subject: [PATCH] Added new command to load mods. --- cmd/swgoh/main.go | 34 ++++++++--- swgohgg/client.go | 13 +++- swgohgg/mods.go | 153 ++++++++++++++++++++++++++++++++++++++++++++++ swgohgg/roster.go | 29 ++++++++- swgohgg/vars.go | 9 +++ 5 files changed, 223 insertions(+), 15 deletions(-) create mode 100644 swgohgg/mods.go create mode 100644 swgohgg/vars.go diff --git a/cmd/swgoh/main.go b/cmd/swgoh/main.go index 68f5cbe..bc78935 100644 --- a/cmd/swgoh/main.go +++ b/cmd/swgoh/main.go @@ -8,25 +8,41 @@ import ( ) var ( - profile string - starLevel int + profile string + starLevel int + showRoster bool + showMods bool ) func init() { + flag.BoolVar(&showRoster, "roster", false, "Show user character collection") + flag.BoolVar(&showMods, "mods", false, "Show user mods collection") flag.StringVar(&profile, "profile", "", "The user `profile` on https://swgoh.gg/") flag.IntVar(&starLevel, "stars", 0, "The minimal `character stars` to filter") } func main() { flag.Parse() - - roster, err := swgohgg.NewClient().Roster(profile) - if err != nil { - log.Fatal(err) + swgg := swgohgg.NewClient(profile) + if showRoster { + roster, err := swgg.Roster() + if err != nil { + log.Fatal(err) + } + for _, char := range roster { + if char.Stars >= starLevel { + fmt.Println(char) + } + } } - for _, char := range roster { - if char.Stars >= starLevel { - fmt.Println(char) + + if showMods { + mods, err := swgg.Mods() + if err != nil { + log.Fatal(err) + } + for _, mod := range mods { + fmt.Println(mod) } } } diff --git a/swgohgg/client.go b/swgohgg/client.go index 8b9e6e6..a48c87e 100644 --- a/swgohgg/client.go +++ b/swgohgg/client.go @@ -5,12 +5,14 @@ import ( ) type Client struct { - hc *http.Client + hc *http.Client + profile string } -func NewClient() *Client { +func NewClient(profile string) *Client { return &Client{ - hc: http.DefaultClient, + hc: http.DefaultClient, + profile: profile, } } @@ -18,3 +20,8 @@ func (c *Client) UseHTTPClient(hc *http.Client) *Client { c.hc = hc return c } + +func (c *Client) Profile(profile string) *Client { + c.profile = profile + return c +} diff --git a/swgohgg/mods.go b/swgohgg/mods.go new file mode 100644 index 0000000..f07c0ec --- /dev/null +++ b/swgohgg/mods.go @@ -0,0 +1,153 @@ +package swgohgg + +import ( + "fmt" + "github.com/PuerkitoBio/goquery" + "log" + "strconv" + "strings" +) + +type Mod struct { + ID string + Level int + Rarity int + Shape string + BonusSet string + + PrimStat ModStat + SecStat []ModStat + + UsingIn string +} + +func (m *Mod) String() string { + if m == nil { + return "nil mod" + } + str := fmt.Sprintf("%s %-18s L%d %d* %v %v", m.ShapeIcon(), m.BonusSet, m.Level, m.Rarity, m.PrimStat, m.SecStat) + if m.UsingIn != "" { + str += " (" + m.UsingIn + ")" + } + return str +} + +func (m *Mod) ShapeIcon() string { + switch m.Shape { + case "Transmitter": + return "◻" + case "Processor": + return "◇" + case "Holo-Array": + return "△" + case "Data-Bus": + return "○" + case "Receiver": + return "◹" + case "Multiplexer": + return "+" + default: + return m.Shape + } +} + +func (m *Mod) ShapeName() string { + switch m.Shape { + case "Transmitter": + return "Quadrado " + case "Processor": + return "Losango " + case "Holo-Array": + return "Triangulo" + case "Data-Bus": + return "Circulo " + case "Receiver": + return "Seta " + case "Multiplexer": + return "Cruz " + default: + return m.Shape + } +} + +type ModStat struct { + Type string + Value float64 + ValuePercent bool +} + +func (ms ModStat) String() string { + if ms.ValuePercent { + return fmt.Sprintf("%.02f%% %s", ms.Value, ms.Type) + } + return fmt.Sprintf("%.02f %s", ms.Value, ms.Type) +} + +type ModCollection []*Mod + +func (c *Client) Mods() (mods ModCollection, err error) { + url := fmt.Sprintf("https://swgoh.gg/u/%s/mods/", c.profile) + resp, err := c.hc.Get(url) + if err != nil { + return nil, err + } + defer resp.Body.Close() + doc, err := goquery.NewDocumentFromReader(resp.Body) + if err != nil { + return nil, err + } + doc.Find(".collection-mod").Each(func(i int, s *goquery.Selection) { + mod := parseMod(s) + mods = append(mods, mod) + }) + return mods, nil +} + +func parseMod(s *goquery.Selection) *Mod { + var err error + mod := &Mod{} + mod.ID = s.AttrOr("data-id", "") + mod.Level, err = strconv.Atoi(s.Find(".statmod-level").Text()) + if err != nil { + log.Println("Error: %v", err) + } + mod.Rarity = s.Find(".statmod-pip").Length() + shortname := strings.Fields(s.Find(".statmod-img").AttrOr("alt", "!Unkown!")) + switch len(shortname) { + case 4: + mod.BonusSet = shortname[2] + mod.Shape = shortname[3] + case 5: + mod.BonusSet = shortname[2] + " " + shortname[3] + mod.Shape = shortname[4] + default: + mod.BonusSet = "?" + mod.Shape = "?" + } + + // Primary stat + mod.PrimStat = parseStat(s.Find(".statmod-stats-1 .statmod-stat")) + // Secondary stats + s.Find(".statmod-stats-2 .statmod-stat").Each(func(i int, stat *goquery.Selection) { + mod.SecStat = append(mod.SecStat, parseStat(stat)) + }) + + mod.UsingIn = s.Find("img.char-portrait-img").AttrOr("alt", "") + return mod +} + +func parseStat(s *goquery.Selection) (stat ModStat) { + stat.Type = s.Find(".statmod-stat-label").Text() + + strvalue := s.Find(".statmod-stat-value").Text() + strvalue = strings.Replace(strvalue, "%", "", -1) + strvalue = strings.Replace(strvalue, "+", "", -1) + + var err error + stat.Value, err = strconv.ParseFloat(strvalue, 64) + if err != nil { + log.Printf("parsestat: invalid value %s", s.Find(".statmod-stat-value").Text()) + } + stat.ValuePercent = strings.Contains(s.Find(".statmod-stat-value").Text(), "%") + return stat +} diff --git a/swgohgg/roster.go b/swgohgg/roster.go index c0425c2..b22822d 100644 --- a/swgohgg/roster.go +++ b/swgohgg/roster.go @@ -6,8 +6,8 @@ import ( "strconv" ) -func (c *Client) Roster(profile string) (roster []*Char, err error) { - url := fmt.Sprintf("https://swgoh.gg/u/%s/collection/", profile) +func (c *Client) Roster() (roster Roster, err error) { + url := fmt.Sprintf("https://swgoh.gg/u/%s/collection/", c.profile) resp, err := c.hc.Get(url) if err != nil { return nil, err @@ -30,6 +30,26 @@ func (c *Client) Roster(profile string) (roster []*Char, err error) { return roster, nil } +type Roster []*Char + +func (r Roster) Contains(char string) bool { + for i := range r { + if r[i].Name == char { + return true + } + } + return false +} + +func (r Roster) ContainsAll(chars ...string) bool { + for _, char := range chars { + if !r.Contains(char) { + return false + } + } + return true +} + type Char struct { Name string Stars int @@ -38,7 +58,10 @@ type Char struct { } func (c *Char) String() string { - return fmt.Sprintf("%s %d* G%d", c.Name, c.Stars, c.Gear) + if c == nil { + return "nil" + } + return fmt.Sprintf("%s %d* G%d Lvl%d", c.Name, c.Stars, c.Gear, c.Level) } func parseChar(s *goquery.Selection) *Char { diff --git a/swgohgg/vars.go b/swgohgg/vars.go new file mode 100644 index 0000000..d2a8f92 --- /dev/null +++ b/swgohgg/vars.go @@ -0,0 +1,9 @@ +package swgohgg + +import ( + "fmt" +) + +var ( + errNotImplemented = fmt.Errorf("swgohgg: not implemented") +)