Fixed SCG prices
This commit is contained in:
parent
631213a412
commit
3919b21318
8 changed files with 51 additions and 199 deletions
36
cardsinfo/prices.go
Normal file
36
cardsinfo/prices.go
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
package cardsinfo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
scryfall "github.com/BlueMonday/go-scryfall"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetPrices(name string) ([]CardPrice, error) {
|
||||||
|
client, err := scryfall.NewClient()
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "Cannot fetch prices")
|
||||||
|
}
|
||||||
|
ctx := context.Background()
|
||||||
|
opts := scryfall.SearchCardsOptions{
|
||||||
|
|
||||||
|
}
|
||||||
|
resp, err := client.SearchCards(ctx, name, opts)
|
||||||
|
var prices []CardPrice
|
||||||
|
for _, card := range resp.Cards {
|
||||||
|
foilString := ""
|
||||||
|
if card.Foil {
|
||||||
|
foilString = "(Foil)"
|
||||||
|
}
|
||||||
|
edition := card.Set + foilString
|
||||||
|
cardPrice := CardPrice {
|
||||||
|
Edition: edition,
|
||||||
|
Price: card.Prices.USD,
|
||||||
|
Name: card.Name,
|
||||||
|
Link: card.PurchaseURIs.TCGPlayer,
|
||||||
|
}
|
||||||
|
prices = append(prices, cardPrice)
|
||||||
|
}
|
||||||
|
return prices, nil
|
||||||
|
}
|
||||||
|
|
@ -1,194 +0,0 @@
|
||||||
package cardsinfo
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
|
||||||
"github.com/antchfx/htmlquery"
|
|
||||||
"golang.org/x/net/html"
|
|
||||||
"golang.org/x/net/html/charset"
|
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
const Scgurl = "https://www.starcitygames.com/search.php?search_query="
|
|
||||||
const Scgapi = "https://newstarcityconnector.herokuapp.com/eyApi/products/"
|
|
||||||
const MaxCards = 4
|
|
||||||
|
|
||||||
func GetSCGPrices(name string) ([]CardPrice, error) {
|
|
||||||
preprocessedName := preprocessNameForSearch(name)
|
|
||||||
url := getSCGUrl(preprocessedName)
|
|
||||||
doc, err := getScgHTML(url)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return fetchPrices(doc)
|
|
||||||
}
|
|
||||||
|
|
||||||
func getScgHTML(url string) (*html.Node, error) {
|
|
||||||
response, err := http.Get(url)
|
|
||||||
defer func() {
|
|
||||||
_ = response.Body.Close()
|
|
||||||
}()
|
|
||||||
|
|
||||||
if response.StatusCode != http.StatusOK {
|
|
||||||
return nil, errors.New("not ok status")
|
|
||||||
}
|
|
||||||
|
|
||||||
r, err := charset.NewReader(response.Body, response.Header.Get("Content-Type"))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return html.Parse(r)
|
|
||||||
}
|
|
||||||
|
|
||||||
func preprocessNameForSearch(name string) string {
|
|
||||||
return strings.Split(name, "|")[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
func fetchPrices(doc *html.Node) ([]CardPrice, error) {
|
|
||||||
priceContainers := getPriceContainers(doc)
|
|
||||||
if MaxCards < len(priceContainers) {
|
|
||||||
priceContainers = priceContainers[:MaxCards]
|
|
||||||
}
|
|
||||||
length := len(priceContainers)
|
|
||||||
prices := make(chan CardPrice, length)
|
|
||||||
finished := make(chan bool, length)
|
|
||||||
cardPrices := make([]CardPrice, 0)
|
|
||||||
for _, container := range priceContainers {
|
|
||||||
go processCard(container, prices, finished)
|
|
||||||
}
|
|
||||||
processed := 0
|
|
||||||
for {
|
|
||||||
c := <-finished
|
|
||||||
processed++
|
|
||||||
if c {
|
|
||||||
cardPrices = append(cardPrices, <-prices)
|
|
||||||
}
|
|
||||||
if processed == length {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return cardPrices, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func processCard(container *html.Node, prices chan CardPrice, finished chan bool) {
|
|
||||||
name := parseName(container)
|
|
||||||
edition := parseEdition(container)
|
|
||||||
price := parsePrice(container)
|
|
||||||
link := parseLink(container)
|
|
||||||
cardPrice := buildCardPrice(name, edition, price, link)
|
|
||||||
if isValidPrice(&cardPrice) {
|
|
||||||
prices <- cardPrice
|
|
||||||
finished <- true
|
|
||||||
} else {
|
|
||||||
finished <- false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func isValidPrice(price *CardPrice) bool {
|
|
||||||
isValid := true
|
|
||||||
isValid = isValid && (price.Name != "")
|
|
||||||
isValid = isValid && (price.Edition != "")
|
|
||||||
isValid = isValid && (price.Price != 0.0)
|
|
||||||
isValid = isValid && (price.Link != "")
|
|
||||||
return isValid
|
|
||||||
}
|
|
||||||
|
|
||||||
func buildCardPrice(name, edition string, price float64, link string) CardPrice {
|
|
||||||
cardPrice := CardPrice{
|
|
||||||
Name: name,
|
|
||||||
Edition: edition,
|
|
||||||
Price: price,
|
|
||||||
Link: link,
|
|
||||||
}
|
|
||||||
return cardPrice
|
|
||||||
}
|
|
||||||
|
|
||||||
func getPriceContainers(doc *html.Node) []*html.Node {
|
|
||||||
nodes := htmlquery.Find(doc, "//tr[@class='product']")
|
|
||||||
return nodes
|
|
||||||
}
|
|
||||||
|
|
||||||
func getItemId(item *html.Node) string {
|
|
||||||
return htmlquery.SelectAttr(item, "data-id")
|
|
||||||
}
|
|
||||||
|
|
||||||
func getPriceById(id string) float64 {
|
|
||||||
path := Scgapi + id + "/variants"
|
|
||||||
resp, err := http.Get(path)
|
|
||||||
if err != nil || resp.StatusCode != http.StatusOK {
|
|
||||||
return 0.0
|
|
||||||
}
|
|
||||||
respString, err := ioutil.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
return 0.0
|
|
||||||
}
|
|
||||||
var scgResponse ScgResponse
|
|
||||||
err = json.Unmarshal(respString, &scgResponse)
|
|
||||||
if err != nil {
|
|
||||||
return 0.0
|
|
||||||
}
|
|
||||||
for _, v := range scgResponse.Response.Data {
|
|
||||||
if len(v.OptionValues) > 0 && v.OptionValues[0].Label == "Near Mint" {
|
|
||||||
return v.Price
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0.0
|
|
||||||
}
|
|
||||||
|
|
||||||
func getSCGUrl(name string) string {
|
|
||||||
scgName := strings.Replace(name, " ", "+", -1)
|
|
||||||
url := Scgurl + scgName
|
|
||||||
return url
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseName(container *html.Node) string {
|
|
||||||
nameNode := htmlquery.FindOne(container, "//h4[@class='listItem-title']")
|
|
||||||
if nameNode == nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
name := htmlquery.InnerText(nameNode)
|
|
||||||
name = strings.Trim(name, "\n ")
|
|
||||||
return name
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseEdition(container *html.Node) string {
|
|
||||||
editionNode := htmlquery.FindOne(container, "//span[@class='category-row-name-search']")
|
|
||||||
if editionNode == nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
edition := strings.Trim(htmlquery.InnerText(editionNode), "\n ")
|
|
||||||
parts := strings.Split(edition, "/")
|
|
||||||
if len(parts) == 0 {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
last := len(parts) - 1
|
|
||||||
return parts[last]
|
|
||||||
}
|
|
||||||
|
|
||||||
func parsePrice(container *html.Node) float64 {
|
|
||||||
id := getItemId(container)
|
|
||||||
price := getPriceById(id)
|
|
||||||
return price
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseLink(container *html.Node) string {
|
|
||||||
linkNodes := htmlquery.Find(container, "//h4[@class='listItem-title']/a")
|
|
||||||
if len(linkNodes) == 0 {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
linkNode := linkNodes[0]
|
|
||||||
link := fetchLink(linkNode)
|
|
||||||
return link
|
|
||||||
}
|
|
||||||
|
|
||||||
func fetchLink(linkContainer *html.Node) string {
|
|
||||||
for _, v := range linkContainer.Attr {
|
|
||||||
if v.Key == "href" {
|
|
||||||
return v.Val
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
@ -6,7 +6,7 @@ import (
|
||||||
|
|
||||||
type CardPrice struct {
|
type CardPrice struct {
|
||||||
Name string
|
Name string
|
||||||
Price float64
|
Price string
|
||||||
Link string
|
Link string
|
||||||
Edition string
|
Edition string
|
||||||
}
|
}
|
||||||
|
|
|
||||||
2
go.mod
2
go.mod
|
|
@ -3,6 +3,7 @@ module github.com/flygrounder/go-mtg-vk
|
||||||
go 1.12
|
go 1.12
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/BlueMonday/go-scryfall v0.1.0
|
||||||
github.com/antchfx/htmlquery v1.0.0
|
github.com/antchfx/htmlquery v1.0.0
|
||||||
github.com/antchfx/xpath v1.0.0 // indirect
|
github.com/antchfx/xpath v1.0.0 // indirect
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
|
|
@ -10,6 +11,7 @@ require (
|
||||||
github.com/go-redis/redis v6.15.2+incompatible
|
github.com/go-redis/redis v6.15.2+incompatible
|
||||||
github.com/onsi/ginkgo v1.10.3 // indirect
|
github.com/onsi/ginkgo v1.10.3 // indirect
|
||||||
github.com/onsi/gomega v1.7.1 // indirect
|
github.com/onsi/gomega v1.7.1 // indirect
|
||||||
|
github.com/pkg/errors v0.9.1
|
||||||
github.com/stretchr/testify v1.3.0
|
github.com/stretchr/testify v1.3.0
|
||||||
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297
|
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297
|
||||||
golang.org/x/sys v0.0.0-20191029155521-f43be2a4598c // indirect
|
golang.org/x/sys v0.0.0-20191029155521-f43be2a4598c // indirect
|
||||||
|
|
|
||||||
6
go.sum
6
go.sum
|
|
@ -1,3 +1,5 @@
|
||||||
|
github.com/BlueMonday/go-scryfall v0.1.0 h1:AWbPrz3f1Ky6sDT5OAEiIFJjSsSTdPQYv2SqUyPbYjg=
|
||||||
|
github.com/BlueMonday/go-scryfall v0.1.0/go.mod h1:8lDLilohikGawCpspnqY6pknEr+nccwHdwvLYZAzrEc=
|
||||||
github.com/antchfx/htmlquery v1.0.0 h1:O5IXz8fZF3B3MW+B33MZWbTHBlYmcfw0BAxgErHuaMA=
|
github.com/antchfx/htmlquery v1.0.0 h1:O5IXz8fZF3B3MW+B33MZWbTHBlYmcfw0BAxgErHuaMA=
|
||||||
github.com/antchfx/htmlquery v1.0.0/go.mod h1:MS9yksVSQXls00iXkiMqXr0J+umL/AmxXKuP28SUJM8=
|
github.com/antchfx/htmlquery v1.0.0/go.mod h1:MS9yksVSQXls00iXkiMqXr0J+umL/AmxXKuP28SUJM8=
|
||||||
github.com/antchfx/xpath v1.0.0 h1:Q5gFgh2O40VTSwMOVbFE7nFNRBu3tS21Tn0KAWeEjtk=
|
github.com/antchfx/xpath v1.0.0 h1:Q5gFgh2O40VTSwMOVbFE7nFNRBu3tS21Tn0KAWeEjtk=
|
||||||
|
|
@ -17,6 +19,8 @@ github.com/go-redis/redis v6.15.2+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8w
|
||||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
|
github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
|
||||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
|
||||||
|
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||||
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
||||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||||
github.com/json-iterator/go v1.1.6 h1:MrUvLMLTMxbqFJ9kzlvat/rYZqZnW3u4wkLzWTaFwKs=
|
github.com/json-iterator/go v1.1.6 h1:MrUvLMLTMxbqFJ9kzlvat/rYZqZnW3u4wkLzWTaFwKs=
|
||||||
|
|
@ -32,6 +36,8 @@ github.com/onsi/ginkgo v1.10.3 h1:OoxbjfXVZyod1fmWYhI7SEyaD8B00ynP3T+D5GiyHOY=
|
||||||
github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
github.com/onsi/gomega v1.7.1 h1:K0jcRCwNQM3vFGh1ppMtDh/+7ApJrjldlX8fA0jDTLQ=
|
github.com/onsi/gomega v1.7.1 h1:K0jcRCwNQM3vFGh1ppMtDh/+7ApJrjldlX8fA0jDTLQ=
|
||||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||||
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
|
|
||||||
|
|
@ -10,13 +10,13 @@ func TestFormat(t *testing.T) {
|
||||||
data := []cardsinfo.CardPrice{
|
data := []cardsinfo.CardPrice{
|
||||||
{
|
{
|
||||||
Name: "Green lotus",
|
Name: "Green lotus",
|
||||||
Price: 22.8,
|
Price: "22.8",
|
||||||
Link: "scg.com/1",
|
Link: "scg.com/1",
|
||||||
Edition: "alpha",
|
Edition: "alpha",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "White lotus",
|
Name: "White lotus",
|
||||||
Price: 3.22,
|
Price: "3.22",
|
||||||
Link: "scg.com/2",
|
Link: "scg.com/2",
|
||||||
Edition: "gamma",
|
Edition: "gamma",
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,15 @@
|
||||||
package tests
|
package tests
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"github.com/flygrounder/go-mtg-vk/cardsinfo"
|
"github.com/flygrounder/go-mtg-vk/cardsinfo"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestParser(t *testing.T) {
|
func TestParser(t *testing.T) {
|
||||||
prices, err := cardsinfo.GetSCGPrices("shock")
|
prices, err := cardsinfo.GetPrices("shock")
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.NotEmpty(t, prices)
|
assert.NotEmpty(t, prices)
|
||||||
|
fmt.Println(prices)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,7 @@ func GetPrices(cardName string) ([]cardsinfo.CardPrice, error) {
|
||||||
val, err := client.Get(cardName)
|
val, err := client.Get(cardName)
|
||||||
var prices []cardsinfo.CardPrice
|
var prices []cardsinfo.CardPrice
|
||||||
if err != nil {
|
if err != nil {
|
||||||
prices, err = cardsinfo.GetSCGPrices(cardName)
|
prices, err = cardsinfo.GetPrices(cardName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue