diff --git a/cardsinfo/prices.go b/cardsinfo/prices.go new file mode 100644 index 0000000..a03d1ea --- /dev/null +++ b/cardsinfo/prices.go @@ -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 +} diff --git a/cardsinfo/scgprices.go b/cardsinfo/scgprices.go deleted file mode 100644 index cb1b502..0000000 --- a/cardsinfo/scgprices.go +++ /dev/null @@ -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 "" -} diff --git a/cardsinfo/structs.go b/cardsinfo/structs.go index 41b31a1..3fb3653 100644 --- a/cardsinfo/structs.go +++ b/cardsinfo/structs.go @@ -6,7 +6,7 @@ import ( type CardPrice struct { Name string - Price float64 + Price string Link string Edition string } diff --git a/go.mod b/go.mod index e9fc0d1..e31abc9 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/flygrounder/go-mtg-vk go 1.12 require ( + github.com/BlueMonday/go-scryfall v0.1.0 github.com/antchfx/htmlquery v1.0.0 github.com/antchfx/xpath v1.0.0 // 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/onsi/ginkgo v1.10.3 // indirect github.com/onsi/gomega v1.7.1 // indirect + github.com/pkg/errors v0.9.1 github.com/stretchr/testify v1.3.0 golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 golang.org/x/sys v0.0.0-20191029155521-f43be2a4598c // indirect diff --git a/go.sum b/go.sum index 98dac55..93a05fa 100644 --- a/go.sum +++ b/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/go.mod h1:MS9yksVSQXls00iXkiMqXr0J+umL/AmxXKuP28SUJM8= 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.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= 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/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= 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/gomega v1.7.1 h1:K0jcRCwNQM3vFGh1ppMtDh/+7ApJrjldlX8fA0jDTLQ= 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/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= diff --git a/tests/price_format_test.go b/tests/price_format_test.go index 0eadb5e..01097ab 100644 --- a/tests/price_format_test.go +++ b/tests/price_format_test.go @@ -10,13 +10,13 @@ func TestFormat(t *testing.T) { data := []cardsinfo.CardPrice{ { Name: "Green lotus", - Price: 22.8, + Price: "22.8", Link: "scg.com/1", Edition: "alpha", }, { Name: "White lotus", - Price: 3.22, + Price: "3.22", Link: "scg.com/2", Edition: "gamma", }, diff --git a/tests/prices_test.go b/tests/prices_test.go index 601ccf5..67d15b1 100644 --- a/tests/prices_test.go +++ b/tests/prices_test.go @@ -1,13 +1,15 @@ package tests import ( + "fmt" "github.com/flygrounder/go-mtg-vk/cardsinfo" "github.com/stretchr/testify/assert" "testing" ) func TestParser(t *testing.T) { - prices, err := cardsinfo.GetSCGPrices("shock") + prices, err := cardsinfo.GetPrices("shock") assert.Nil(t, err) assert.NotEmpty(t, prices) + fmt.Println(prices) } diff --git a/vk/handlers.go b/vk/handlers.go index 4b4ec74..b30da1d 100644 --- a/vk/handlers.go +++ b/vk/handlers.go @@ -58,7 +58,7 @@ func GetPrices(cardName string) ([]cardsinfo.CardPrice, error) { val, err := client.Get(cardName) var prices []cardsinfo.CardPrice if err != nil { - prices, err = cardsinfo.GetSCGPrices(cardName) + prices, err = cardsinfo.GetPrices(cardName) if err != nil { return nil, err }