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.

233 lines
6.1 KiB
JavaScript

"use strict";
const Promise = require("bluebird");
const bhttp = require("bhttp").session({ headers: { "user-agent": "barcodebot (problems: admin@cryto.net)" } });
const dotty = require("dotty");
const cheerio = require("cheerio");
const url = require("url");
const chalk = require("chalk");
const config = require("./config.json");
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,
];
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())}`
};
}
}
});
}