Julien Riou
244c9f68e7
Filters are now separate structures to include a product or not based on their own set of properties. For now, include and exclude filters are supported. They take a regex as an argument and include a product if the regex matches (or doesn't match) the product name. This commit will allow us to create new filters on product like on a price range. Signed-off-by: Julien Riou <julien@riou.xyz>
133 lines
3.9 KiB
Go
133 lines
3.9 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"regexp"
|
|
"strings"
|
|
|
|
log "github.com/sirupsen/logrus"
|
|
paapi5 "github.com/spiegel-im-spiegel/pa-api"
|
|
"github.com/spiegel-im-spiegel/pa-api/entity"
|
|
"github.com/spiegel-im-spiegel/pa-api/query"
|
|
)
|
|
|
|
// NewAmazonServer creates an Amazon Server function based on the Marketplace.
|
|
// The paapi5 marketplaceEnum is not exported, so this type cannot be used in simple map.
|
|
func NewAmazonServer(marketplace string) *paapi5.Server {
|
|
switch marketplace {
|
|
case "www.amazon.fr":
|
|
return paapi5.New(paapi5.WithMarketplace(paapi5.LocaleFrance))
|
|
case "www.amazon.com":
|
|
return paapi5.New(paapi5.WithMarketplace(paapi5.LocaleUnitedStates))
|
|
default:
|
|
return paapi5.New() // default Marketplace
|
|
}
|
|
}
|
|
|
|
// Map of messages to detect availability
|
|
var availabilityMessages = []string{"En stock."}
|
|
|
|
// AmazonParser structure to handle Amazon parsing logic
|
|
type AmazonParser struct {
|
|
client paapi5.Client
|
|
searches []string
|
|
includeRegex *regexp.Regexp
|
|
excludeRegex *regexp.Regexp
|
|
amazonFulfilled bool
|
|
amazonMerchant bool
|
|
affiliateLinks bool
|
|
}
|
|
|
|
// NewAmazonParser to create a new AmazonParser instance
|
|
func NewAmazonParser(marketplace string, partnerTag string, accessKey string, secretKey string, searches []string, amazonFulfilled bool, amazonMerchant bool, affiliateLinks bool) *AmazonParser {
|
|
return &AmazonParser{
|
|
client: NewAmazonServer(marketplace).CreateClient(partnerTag, accessKey, secretKey),
|
|
searches: searches,
|
|
amazonFulfilled: amazonFulfilled,
|
|
amazonMerchant: amazonMerchant,
|
|
affiliateLinks: affiliateLinks,
|
|
}
|
|
}
|
|
|
|
// Parse Amazon API to return list of products
|
|
// Implements Parser interface
|
|
func (p *AmazonParser) Parse() ([]*Product, error) {
|
|
|
|
var products []*Product
|
|
|
|
for _, search := range p.searches {
|
|
|
|
log.Debugf("searching for '%s' on %s", search, p.client.Marketplace())
|
|
|
|
// create search request on API
|
|
q := query.NewSearchItems(
|
|
p.client.Marketplace(),
|
|
p.client.PartnerTag(),
|
|
p.client.PartnerType(),
|
|
).Search(query.Keywords, search).EnableItemInfo().EnableOffers()
|
|
body, err := p.client.Request(q)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// decode response
|
|
res, err := entity.DecodeResponse(body)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// decode products
|
|
for _, item := range res.SearchResult.Items {
|
|
|
|
product := &Product{}
|
|
if !p.affiliateLinks {
|
|
product.URL = fmt.Sprintf("https://%s/dp/%s", p.client.Marketplace(), item.ASIN)
|
|
|
|
} else {
|
|
product.URL = item.DetailPageURL // includes partner tag
|
|
}
|
|
product.Name = item.ItemInfo.Title.DisplayValue
|
|
|
|
if item.Offers != nil && *item.Offers.Listings != nil {
|
|
for _, offer := range *item.Offers.Listings {
|
|
// detect if product is packaged by Amazon
|
|
if p.amazonFulfilled && !offer.DeliveryInfo.IsAmazonFulfilled {
|
|
log.Debugf("excluding offer by '%s' for product '%s' because not fulfilled by Amazon", offer.MerchantInfo.Name, product.Name)
|
|
continue
|
|
}
|
|
|
|
// detect if product is sold by Amazon
|
|
if p.amazonMerchant && !strings.HasPrefix(offer.MerchantInfo.Name, "Amazon") {
|
|
log.Debugf("excluding offer by '%s' for product '%s' because not sold by Amazon", offer.MerchantInfo.Name, product.Name)
|
|
continue
|
|
}
|
|
|
|
// detect price
|
|
product.Price = offer.Price.Amount
|
|
product.PriceCurrency = offer.Price.Currency
|
|
|
|
// detect availability
|
|
if ContainsString(availabilityMessages, offer.Availability.Message) {
|
|
product.Available = true
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
products = append(products, product)
|
|
}
|
|
}
|
|
|
|
return products, nil
|
|
}
|
|
|
|
// String to print AmazonParser
|
|
// Implements the Parser interface
|
|
func (p *AmazonParser) String() string {
|
|
return fmt.Sprintf("AmazonParser<%s@%s>", p.client.PartnerTag(), p.client.Marketplace())
|
|
}
|
|
|
|
// ShopName returns shop name from Amazon Marketplace
|
|
func (p *AmazonParser) ShopName() (string, error) {
|
|
return strings.ReplaceAll(p.client.Marketplace(), "www.", ""), nil
|
|
}
|