You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
258 lines
6.7 KiB
JavaScript
258 lines
6.7 KiB
JavaScript
"use strict";
|
|
|
|
const Promise = require("bluebird");
|
|
const dotty = require("dotty");
|
|
const cheerio = require("cheerio");
|
|
const url = require("url");
|
|
const chalk = require("chalk");
|
|
|
|
const config = require("./config.json");
|
|
const bhttp = require("bhttp").session({ headers: { "user-agent": `barcodebot (problems: ${config.email})` } });
|
|
const parseFoodbook = require("./parse-foodbook");
|
|
|
|
function log(barcode, message) {
|
|
console.log(`${chalk.cyan(`[${barcode}]`)} ${message}`);
|
|
}
|
|
|
|
function mapCurrency(abbreviation) {
|
|
if (abbreviation === "USD") {
|
|
return "$";
|
|
} else if (abbreviation === "EUR") {
|
|
return "€";
|
|
} else {
|
|
throw new Error(`Unrecognized currency: ${abbreviation}`);
|
|
}
|
|
}
|
|
|
|
function cleanValue(value) {
|
|
if(value != null) {
|
|
return value.replace(/\n/g, " ").replace(/\s+/, " ").trim();
|
|
}
|
|
}
|
|
|
|
let barcodeGetters = [
|
|
getBarcode_coop,
|
|
getBarcode_bolCom,
|
|
getBarcode_buycott,
|
|
getBarcode_foodBook,
|
|
getBarcode_eanData,
|
|
getBarcode_openFoodFacts,
|
|
getBarcode_calorielijst,
|
|
];
|
|
|
|
module.exports = function getBarcode(barcode) {
|
|
return Promise.try(() => {
|
|
return Promise.reduce(barcodeGetters, (lastResult, getter) => {
|
|
if (lastResult == null) {
|
|
return getter(barcode);
|
|
} else {
|
|
return lastResult;
|
|
}
|
|
}, null);
|
|
}).then((result) => {
|
|
if (result != null) {
|
|
return {
|
|
... result,
|
|
image: (result.image !== "") ? result.image : null
|
|
};
|
|
} else {
|
|
return null;
|
|
}
|
|
})
|
|
};
|
|
|
|
function getBarcode_eanData(barcode) {
|
|
return Promise.try(() => {
|
|
log(barcode, "Trying EANData...");
|
|
|
|
return bhttp.get(`https://eandata.com/feed/?v=3&keycode=${config.apiKeys.eanData}&mode=json&find=${barcode}`);
|
|
}).then((response) => {
|
|
if (response.statusCode === 200) {
|
|
let productName = dotty.get(response.body, "product.attributes.product");
|
|
|
|
if (productName != null) {
|
|
|
|
let data = {
|
|
name: cleanValue(productName),
|
|
image: dotty.get(response.body, "product.image")
|
|
};
|
|
|
|
let priceCurrency = dotty.get(response.body, "product.attributes.price_new_extra");
|
|
let priceAmount = dotty.get(response.body, "product.attributes.price_new");
|
|
|
|
if (priceAmount != null) {
|
|
data.price = `${mapCurrency(priceCurrency)} ${priceAmount}`;
|
|
}
|
|
|
|
return data;
|
|
} else {
|
|
return null;
|
|
}
|
|
} else {
|
|
return null;
|
|
}
|
|
});
|
|
}
|
|
|
|
let coop404Regex = /We kunnen de pagina die je zoekt niet vinden/;
|
|
|
|
function getBarcode_coop(barcode) {
|
|
return Promise.try(() => {
|
|
log(barcode, "Trying Coop...");
|
|
|
|
return bhttp.get(`https://www.coop.nl/inventory/product/${barcode}`);
|
|
}).then((response) => {
|
|
if (response.statusCode === 200 && !coop404Regex.test(response.body.toString())) {
|
|
let $ = cheerio.load(response.body);
|
|
let $imageNoscript = cheerio.load($("picture noscript").html());
|
|
|
|
return {
|
|
name: cleanValue($(".detailHeader").text().trim().replace(/\n/g, " - ")),
|
|
// image: $('meta[property="og:image"]').attr("content"),
|
|
image: $imageNoscript('img').attr("src"),
|
|
price: `€ ${$(".mainActions ins.price").text()}`
|
|
};
|
|
} else {
|
|
return null;
|
|
}
|
|
});
|
|
}
|
|
|
|
function getBarcode_foodBook(barcode) {
|
|
return Promise.try(() => {
|
|
log(barcode, "Trying FoodBook...");
|
|
|
|
return bhttp.post(`https://foodbook.psinfoodservice.com/prod/dc`, {
|
|
"SelectedFilterOptions.collapseFilterId": "filterkeywordPanelBody",
|
|
"SelectedFilterOptions.FreeText": "",
|
|
"SelectedFilterOptions.ProductGroupId": "",
|
|
"SelectedFilterOptions.BrandId": "",
|
|
"SelectedFilterOptions.GrossierId": "",
|
|
"SelectedFilterOptions.Ean": barcode,
|
|
"SelectedFilterOptions.ProducerNumber": "",
|
|
"SelectedFilterOptions.GrossierNumber": "",
|
|
"PS_brands_input": "",
|
|
"PS_brands": ""
|
|
});
|
|
}).then((response) => {
|
|
let products = parseFoodbook.parseSearch(response.body);
|
|
|
|
if (products.length > 0) {
|
|
return Promise.try(() => {
|
|
let productUrl = url.resolve("https://foodbook.psinfoodservice.com/prod/dc", products[0]);
|
|
|
|
return bhttp.get(productUrl);
|
|
}).then((response) => {
|
|
if (response.statusCode === 200) {
|
|
return parseFoodbook.parseProduct(response.body);
|
|
} else {
|
|
return null;
|
|
}
|
|
});
|
|
} else {
|
|
return null;
|
|
}
|
|
});
|
|
}
|
|
|
|
function getBarcode_openFoodFacts(barcode) {
|
|
return Promise.try(() => {
|
|
log(barcode, "Trying OpenFoodFacts...");
|
|
|
|
return bhttp.get(`https://world.openfoodfacts.org/api/v0/product/${encodeURIComponent(barcode)}.json`);
|
|
}).then((response) => {
|
|
if (response.statusCode === 200 && response.body.status === 1 && response.body.product.product_name != null) {
|
|
return {
|
|
name: cleanValue(response.body.product.product_name),
|
|
image: response.body.product.image_url
|
|
};
|
|
} else {
|
|
return null;
|
|
}
|
|
});
|
|
}
|
|
|
|
function getBarcode_buycott(barcode) {
|
|
return Promise.try(() => {
|
|
log(barcode, "Trying Buycott...");
|
|
|
|
return bhttp.get(`https://www.buycott.com/upc/${encodeURIComponent(barcode)}`);
|
|
}).then((response) => {
|
|
if (response.statusCode === 200) {
|
|
let $ = cheerio.load(response.body);
|
|
|
|
let product = cleanValue($("#container_header h2").text());
|
|
|
|
let tableData = $("table.product_info_table tr").get().map((row) => {
|
|
let $row = $(row);
|
|
|
|
return [
|
|
cleanValue($row.find("td:nth-child(1)").text()),
|
|
cleanValue($row.find("td:nth-child(2)").text()),
|
|
];
|
|
});
|
|
|
|
let brandRow = tableData.find(([key, value]) => (key === "GS1 Name"));
|
|
|
|
let productName;
|
|
|
|
if (brandRow != null) {
|
|
productName = `${brandRow[1]}, ${product}`;
|
|
} else {
|
|
productName = product;
|
|
}
|
|
|
|
return {
|
|
name: productName,
|
|
image: $(".header_image img").attr("src")
|
|
};
|
|
}
|
|
});
|
|
}
|
|
|
|
function getBarcode_bolCom(barcode) {
|
|
return Promise.try(() => {
|
|
log(barcode, "Trying bol.com...");
|
|
|
|
return bhttp.get(`https://www.bol.com/nl/s/algemeen/zoekresultaten/Ntt/${encodeURIComponent(barcode)}/N/0/Nty/1/search/true/searchType/qck/defaultSearchContext/media_all/sc/media_all/index.html`);
|
|
}).then((response) => {
|
|
if (response.statusCode === 200) {
|
|
let $ = cheerio.load(response.body);
|
|
|
|
let productName = cleanValue($(".pdp-header__title").text());
|
|
|
|
if (productName.length > 0) {
|
|
return {
|
|
name: productName,
|
|
image: $(".js_product_img").attr("src"),
|
|
price: `€ ${cleanValue($(".promo-price").text())}`
|
|
};
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
function getBarcode_calorielijst(barcode) {
|
|
return Promise.try(() => {
|
|
log(barcode, "Trying calorielijst.nl...");
|
|
|
|
return bhttp.get(`http://www.calorielijst.nl/?search=${encodeURIComponent(barcode)}`);
|
|
}).then((response) => {
|
|
if (response.statusCode === 200) {
|
|
let $ = cheerio.load(response.body);
|
|
|
|
let a = $(".calorienamediv a").first();
|
|
let productName = cleanValue(a.text());
|
|
|
|
if (productName.length > 0) {
|
|
let image = a.data().id;
|
|
return {
|
|
name: productName,
|
|
image: (image != "") ? `http://img.calorielijst.nl/product/${image}` : null,
|
|
//~ price: "N/A",
|
|
};
|
|
}
|
|
}
|
|
});
|
|
}
|