const { createPage } = require("./browser.js")
const validShops = [
{
function: "searchAmazon",
id: "amazon-com",
name: "Amazon.com",
url: new URL("https://www.amazon.com"),
deliverTo: ["United States", "France"],
whenToUse: [
"For buying anything"
],
},
{
function: "searchAmazon",
id: "amazon-fr",
name: "Amazon.fr",
url: new URL("https://www.amazon.fr"),
deliverTo: ["France", "Belgium", "Luxembourg", "Monaco", "Germany", "Switzerland"],
whenToUse: [
"For buying anything"
],
},
{
function: "searchAmazon",
id: "amazon-de",
name: "Amazon.de",
url: new URL("https://www.amazon.de"),
deliverTo: ["Germany", "France", "Austria", "Belgium", "Netherlands", "Switzerland", "Italy", "Spain", "Poland"],
whenToUse: [
"For buying anything"
],
},
{
function: "searchAmazon",
id: "amazon-com-be",
name: "Amazon.com-be",
url: new URL("https://www.amazon.com.be"),
deliverTo: ["Belgium", "France"],
whenToUse: [
"For buying anything"
],
},
{
function: "searchAmazon",
id: "amazon-co-uk",
name: "Amazon.co-uk",
url: new URL("https://www.amazon.co.uk"),
deliverTo: ["United Kingdom", "France", "Belgium", "Ireland", "Germany", "Spain", "Italy"],
whenToUse: [
"For buying anything"
],
},
{
function: "searchAmazon",
id: "amazon-it",
name: "Amazon.it",
url: new URL("https://www.amazon.it"),
deliverTo: ["Italy", "France", "Germany", "Belgium", "Spain", "Austria", "Portugal"],
whenToUse: [
"For buying anything"
],
},
{
function: "searchAmazon",
id: "amazon-es",
name: "Amazon.es",
url: new URL("https://www.amazon.es"),
deliverTo: ["Spain", "France", "Belgium", "Germany", "Italy", "Portugal"],
whenToUse: [
"For buying anything"
],
},
{
function: "searchEbay",
id: "ebay-com",
name: "eBay.com",
url: new URL("https://ebay.com"),
deliverTo: ["United States", "International"],
whenToUse: [
"Second-hand items, collectibles",
"Auctions and fixed-price listings",
"Tech, home goods"
],
},
{
function: "searchEbay",
id: "ebay-fr",
name: "eBay.fr",
url: new URL("https://ebay.fr"),
deliverTo: ["France"],
whenToUse: [
"Second-hand items, collectibles",
"Auctions and fixed-price listings",
"Tech, home goods"
],
},
{
function: "searchPepper",
id: "dealabs",
name: "Dealabs",
url: new URL("https://dealabs.com"),
deliverTo: ["France"],
whenToUse: [
"Find the best deals and discounts",
"Community-driven deals",
"Can be used to find offers on various platforms - don't sell products on its own"
],
},
{
function: "searchPepper",
id: "hotukdeals",
name: "hotukdeals",
url: new URL("https://hotukdeals.com"),
deliverTo: ["United Kingdom"],
whenToUse: [
"Find the best deals and discounts",
"Community-driven deals",
"Can be used to find offers on various platforms - don't sell products on its own",
],
},
{
function: "searchPepper",
id: "mydealz",
name: "mydealz",
url: new URL("https://mydealz.de"),
deliverTo: ["Germany"],
whenToUse: [
"Find the best deals and discounts",
"Community-driven deals",
"Can be used to find offers on various platforms - don't sell products on its own",
],
},
{
function: "searchPepper",
id: "pepper-pl",
name: "Pepper.pl",
url: new URL("https://pepper.pl"),
deliverTo: ["Poland"],
whenToUse: [
"Find the best deals and discounts",
"Community-driven deals",
"Can be used to find offers on various platforms - don't sell products on its own",
],
},
{
function: "searchPepper",
id: "chollometro",
name: "Chollometro",
url: new URL("https://chollometro.com"),
deliverTo: ["Spain"],
whenToUse: [
"Find the best deals and discounts",
"Community-driven deals",
"Can be used to find offers on various platforms - don't sell products on its own",
],
},
{
function: "searchPepper",
id: "pepper-it",
name: "Pepper.it",
url: new URL("https://pepper.it"),
deliverTo: ["Italy"],
whenToUse: [
"Find the best deals and discounts",
"Community-driven deals",
"Can be used to find offers on various platforms - don't sell products on its own",
],
},
{
function: "searchPepper",
id: "pepperdeals",
name: "Pepperdeals (US)",
url: new URL("https://www.pepperdeals.com/"),
deliverTo: ["United States"],
whenToUse: [
"Find the best deals and discounts",
"Community-driven deals",
"Can be used to find offers on various platforms - don't sell products on its own",
],
},
]
async function searchAmazon(query, baseUrl){
var page = await createPage({ isMobile: true })
try {
await page.goto(`${baseUrl}/s?k=${encodeURIComponent(query)}`)
await page.waitForSelector("[role=\"listitem\"]", { timeout: 10000 })
} catch (e){}
process.stderr.write("\n\nPage loaded, now searching for products...\n")
var products = await page.$$eval("[role=\"listitem\"]", items => {
return items.map(item => {
var name = item?.querySelector("[data-cy=\"title-recipe\"]")?.innerText?.replace(/\n/g, " - ") || item?.querySelector("h2")?.innerText || "No name"
var price = item?.querySelector(".a-price .a-offscreen")?.innerText || "Could not find price, this product may not be able to be delivered in the user area"
var url = item?.querySelector("a.a-link-normal")?.href || ""
var rating = item?.querySelector("[data-cy=\"reviews-block\"]")?.querySelector("span.a-size-mini.a-color-base")?.innerText || item?.querySelector(".a-icon-alt")?.innerText || "Not rated yet"
var reviewsCount = item?.querySelector("[data-cy=\"reviews-block\"]")?.querySelector("span.a-size-mini.a-color-secondary.puis-normal-weight-text")?.innerText?.replace(/[^0-9]/g, "") || item?.querySelector(".a-size-base")?.innerText || "0"
return {
name,
price,
url,
rating,
reviewsCount
}
})
})
products = products.filter(product => product.name && product.price && product.url && product.name != "No name" && product.url != "")
const seenIds = new Set()
products = products.filter(product => {
var id = product.url.split("/dp/")?.[1]?.split("/")?.[0] || product.url.split("/dp/")?.[1]
if (!id || seenIds.has(id)) return false
seenIds.add(id)
return true
})
products = products.slice(0, 10)
process.stderr.write(`\n\nFound ${products.length} products on Amazon.\n`)
await page.close()
return products
}
async function searchEbay(query, baseUrl){
var page = await createPage({ isMobile: true })
try {
await page.goto(`${baseUrl}/sch/i.html?_nkw=${encodeURIComponent(query)}`)
await page.waitForSelector("ul.srp-results.srp-list.clearfix > li.s-item", { timeout: 10000 })
} catch (e){}
process.stderr.write("\n\nPage loaded, now searching for products...\n")
var products = await page.$$eval("ul.srp-results.srp-list.clearfix > li.s-item", items => {
return items.map(item => {
var name = `${item?.querySelector(".s-item__title")?.innerText || "No name"}${item?.querySelector(".s-item__subtitle")?.innerText}`
var price = `${item?.querySelector(".s-item__price")?.innerText || "Could not find price, this product may not be able to be delivered in the user area"} (${item?.querySelector(".s-item__location.s-item__itemLocation")?.innerText})`
var url = item?.querySelector(".s-item__link")?.href || ""
var rating = item?.querySelector(".x-star-rating > span.clipped")?.innerText || "Not rated yet"
var reviewsCount = item?.querySelector(".s-item__reviews-count")?.innerText?.split("\n")?.[0] || item?.querySelector(".s-item__reviews-count")?.innerText || "0"
return {
name,
price,
url,
rating,
reviewsCount
}
})
})
products = products.filter(product => product.name && product.price && product.url && !product.name.startsWith("No name") && product.url != "")
const seenIds = new Set()
products = products.filter(product => {
var id = product.url.split("/itm/")?.[1]?.split("?")?.[0] || product.url.split("/itm/")?.[1]
if (!id || seenIds.has(id)) return false
seenIds.add(id)
return true
})
products = products.slice(0, 10)
process.stderr.write(`\n\nFound ${products.length} products on eBay.\n`)
await page.close()
return products
}
async function searchPepper(query, baseUrl){
var page = await createPage({ isMobile: false })
try {
await page.goto(`${baseUrl}/search?q=${encodeURIComponent(query)}`)
await page.waitForSelector("div.js-threadList > article.thread", { timeout: 10000 })
} catch (e){}
process.stderr.write("\n\nPage loaded, now searching for products...\n")
var products = await page.$$eval("div.js-threadList > article.thread", items => {
return items.map(item => {
var name = item?.querySelector(".threadListCard-body > .thread-title > a")?.innerText || item?.querySelector(".threadListCard-body > .thread-title > a")?.getAttribute("title") || "No name"
var price = item?.querySelector(".text--b.size--all-xl.size--fromW3-xxl.thread-price")?.innerText || item?.querySelector(".thread-price")?.innerText || item?.querySelector("div.threadListCard-body > div.box--contents > div > div > span > span > span.color--text-NeutralSecondary")?.innerText || "Could not find price, this product may be out of stock or expired"
var url = item?.querySelector(".threadListCard-body > .thread-title > a")?.href || item?.querySelector("a")?.href || ""
var votesCount = item?.querySelector("button.cept-vote-temp.vote-temp")?.innerText || item?.querySelector(".threadListCard-header")?.innerText || "Unknown"
var seller = item?.querySelector("[data-t=\"merchantLink\"]")?.innerText || "Unknown"
var description = item?.querySelector("div.userHtml.userHtml-content")?.innerText || ""
var expiredStatus = item?.querySelector(".chip.chip--type-expired")?.innerText || "N/A"
return {
name,
price,
url,
votesCount,
seller,
description,
expiredStatus
}
})
})
products = products.filter(product => product.name && product.price && product.url && !product.name.startsWith("No name") && product.url != "")
products = products.filter(product => product.expiredStatus == "N/A")
const seenUrls = new Set()
products = products.filter(product => {
if (!product.url || seenUrls.has(product.url)) return false
seenUrls.add(product.url)
return true
})
products = products.slice(0, 10)
process.stderr.write(`\n\nFound ${products.length} products on Pepper.\n`)
await page.close()
return products
}
module.exports = {
validShops,
searchAmazon,
searchEbay,
searchPepper
}