Object.entries(require('./common.js')).forEach(([name, exported]) => global[name] = exported);
const url = "https://www.bccannabisstores.com/";
const spray = /\bspray\b/i;
const vape = /\b(?:cart(?:ridge)?|disposable)\b/i;
const drink = /\b(tea|powder|shot)\b/i;
function reqq(collection, name, ua) {
return request({url: url + 'collections/' + collection + '.oembed', method: 'GET', headers: {'User-Agent': ua}, gzip: true}).then(obj => {
obj = JSON.parse(obj).products;
for(let i = obj.length - 1; i >= 0; --i) {
obj[i].variants = [];
for(let j = 0; j < obj[i].offers.length; ++j) {
let fuck = obj[i].offers[j].title.match(qty);
if(fuck !== null) {
let [, portions, quantity] = fuck;
if(!portions || portions < 2) {
portions = 1;
}
obj[i].variants.push({portions, quantity, gay: obj[i].offers[j].offer_id, sku: obj[i].offers[j].sku, price: obj[i].offers[j].price});
}
}
if(obj[i].variants.length) {
if(spray.test(obj[i].title) && name === 'oil') {
obj[i].type = 'spray';
} else if(name === 'vape') {
let m = obj[i].title.match(vape);
if(m?.[0]) {
obj[i].type = m[0].toLowerCase();
if(obj[i].type === 'cart') {
obj[i].type = 'cartridge';
}
} else {
console.log(obj[i].title, 'bye');
obj.splice(i, 1);
}
} else if(name === 'drink') {
let m = obj[i].title.match(drink);
if(m?.[0]) {
obj[i].type = m[0].toLowerCase();
} else {
if(obj[i].variants.length > 1) {
console.log(obj[i].title, 'shit', obj[i].variants);
}
for(let j = 0; j < obj[i].variants.length; ++j) {
if(obj[i].variants[j].quantity > 150) {
obj[i].type = 'rtd';
} else {
obj[i].type = 'liquid';
}
}
}
} else if(name === 'c') {
if(obj[i].title.toLowerCase().includes('mint')) {
obj[i].type = 'mint';
} else {
obj[i].type = 'gummy';
}
} else {
obj[i].type = name;
}
} else {
console.log('fuck off', obj[i]);
obj.splice(i, 1);
}
}
return obj;
})
}
(async function() {
const browser = await puppeteer.launch({
//headless: false,
args: ["--no-sandbox", '--proxy-server=socks5://' + process.env.TOR_IP + ':' + process.env.TOR_PORT],
});
let ua = (await ((await browser.pages())[0]).evaluate('navigator.userAgent')).replace('Headless', '');
let e = (await Promise.all([
reqq('flower', 'flower', ua),
reqq('pre-rolls', 'preroll', ua),
reqq('bottles-drops', 'oil', ua),//oils and sprays
reqq('capsules', 'capsule', ua),
reqq('vape-kits-cartridges', 'vape', ua),//cartridge and disposable
reqq('beverages', 'drink', ua),//tea, powder additive, shot, liquid additive, ready to drink
reqq('chocolate', 'chocolate', ua),
reqq('baked-goods-snacks', 'baked', ua),
reqq('chews-candy', 'c', ua)//gummy and mint
])).flat();
await postgres.connect();
await postgres.query('begin');
let storeID = (await postgres.query(`select ios_store_id('bccannabisstores', 'british columbia', 'canada', _url => $1, _delivery => true, _prepayment => true)`, [url])).rows[0].ios_store_id;
let i = 0;
async function deal() {
const page = await (await browser.createIncognitoBrowserContext()).newPage();
await page.setUserAgent((await page.evaluate('navigator.userAgent')).replace('Headless', ''));
await page.setJavaScriptEnabled(false);
await page.setRequestInterception(true);
page.on('request', request => {
let resourceType = request.resourceType();
let URL = request.url();
if(resourceType === 'document' || (resourceType === 'fetch' && (URL === url + "cart/add.js" || URL === url + "cart/change.js"))) {
request.continue();
} else {
request.abort();
}
});
while(i < e.length) {
let idx = i++;
let path = 'products/' + e[idx].product_id;
await goto(page, url, path, 0);
let obj = {
name: e[idx].title,
variants: e[idx].variants,
type: e[idx].type,
blend: false,
strain: await page.evaluate(() => {
let x = document.querySelector('.product-disclaimer');
x.parentNode.removeChild(x);
return document.evaluate("//span[contains(., 'Common name')]", document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue?.nextElementSibling?.textContent;
}),
description: e[idx].description
};
let unit;
for(const match of (await page.$eval('.product-details--thc-cbd', el => el.textContent)).matchAll(concentration)) {
console.log('matching concentrations')
obj['min' + match[1]/*.toUpperCase()*/] = new Big(match[2]);
obj['max' + match[1]] = match[3] ? new Big(match[3]) : obj['min' + match[1]];
if(obj['min' + match[1]].gt(obj['max' + match[1]])) {
let temp = obj['min' + match[1]];
obj['min' + match[1]] = obj['max' + match[1]];
obj['max' + match[1]] = temp;
}
obj['min' + match[1]] = obj['min' + match[1]].toString();
obj['max' + match[1]] = obj['max' + match[1]].toString();
}
console.log(obj);
try {
obj.species = await page.$eval('.product-strain', el => el.textContent.match(/hybrid|indica|sativa|blend/i)?.[0]?.toLowerCase());
if(obj.species === 'blend' && !obj.strain) {
obj.blend = true;
throw 'gay';
} else if(obj.species === undefined) {
throw 'gay';
} else {
throw 'ultra gay'
}
} catch(e) {
console.log(e, path);
let yuck = await page.evaluate(() => {
let shit = document.querySelector('.product--outer').textContent.replace(/\s+/g, ' ');
let x = shit.match(/indica|sativa/i);
if(x !== null) {
return [x[0], shit];
}
});
if(yuck) {
obj.species = yuck[0].toLowerCase();
console.log(yuck);
} else {
obj.species = 'hybrid';
}
}
if(!obj.blend && !obj.strain) {
let yuck = await page.evaluate(() => {
let shit = document.querySelector('.product--outer').textContent.replace(/\s+/g, ' ');
if(/blend/i.test(shit)) {
return shit;
}
});
if(yuck) {
obj.blend = true;
console.log(path, yuck);
}
}
console.log('LOOK AT ME', obj.species);
let ayy = JSON.parse(await page.$eval('[data-section-id="static-product"]', el => el.textContent));
//let [p, b] = (await page.$eval('.product-brand', el => el.textContent.trim())).split(/\s*by\s*/);
let [producer, brand, join_name, name, hack] = normalize(
ayy.product.tags.find(x => x.startsWith('brand::'))?.substring(7),
obj.name,
obj.type,
ayy.product?.vendor
);
let terpenes = ayy.terpene?.names?.map(x => x.match(/[a-z]+/i)?.[0]);
//check for terepenes removed
console.log('fuckoff', [ obj.species,
obj.blend,
strain_clean(obj.strain)])
let productID = (await postgres.query(`select ios_product_id($1, $2, $3, $4, $5, $6, $7, $8, $9, true, $10, $11)`, [
producer,
brand,
name,
join_name,
obj.type,
obj.species,
obj.blend,
strain_clean(obj.strain),
obj.description,
terpenes,
brand === 'dosist' ? '.00225' : null
])).rows[0].ios_product_id;
if(process.env.CLOUDINARY_URL) {
await images(
new Set(
await page.$$eval('.product-gallery--image-background > img', elements => {
for(let i = 0; i < elements.length; ++i) {
elements[i] = elements[i].src.split('?')[0].replace(/_\d+x\d+/, '').replace('.progressive', '').replace('.png.jpg', '.png');
}
return elements;
})
),
productID
);
}//.product.tags itearte starts with gtin:: actually fuck gtin. only one even though there are multiple variants...
for(let j = 0; j < obj.variants.length; ++j) {
let params = [productID, obj.variants[j].portions, obj.variants[j].quantity];
console.log('loop')
let [variantID, g] = (await postgres.query(
`select ios_variant_id($1, $2, $3${obj.type === 'oil' || obj.type === 'spray' || obj.type === 'capsule' ? `, _gram_equivalency => $${params.push(ayy.volumeEquivalencies[obj.variants[j].sku])}` : ''})`,
params
)).rows[0].ios_variant_id.slice(1, -1).split(',');
console.log('var')
if(g.length && ayy.volumeEquivalencies[obj.variants[j].sku] !== null && !(new Big(ayy.volumeEquivalencies[obj.variants[j].sku])).eq(g)) {
console.log('warning: gram_equivalency does not match', ayy.volumeEquivalencies[obj.variants[j].sku], g, productID, variantID, `${url}${path}?variant=${obj.variants[j].gay}`);
}
console.log( [
variantID,
storeID,
`[${obj.minCBD},${obj.maxCBD}]`,
`[${obj.minTHC},${obj.maxTHC}]`,
obj.variants[j].price,
ayy.variantAvailabilities[obj.variants[j].gay].vq,
`${path}?variant=${obj.variants[j].gay}`
])
if(obj.minCBD !== undefined && obj.maxCBD !== undefined && obj.minTHC !== undefined && obj.maxTHC !== undefined)
await postgres.query(
`insert into menu_item (
variant_id,
store_id,
cbd,
thc,
price,
stock,
path
) values (
$1,
$2,
$3,
$4,
$5,
$6,
$7
) on conflict (variant_id, store_id) do update set cbd = excluded.cbd, thc = excluded.thc, price = excluded.price, stock = excluded.stock`,
[
variantID,
storeID,
`[${obj.minCBD},${obj.maxCBD}]`,
`[${obj.minTHC},${obj.maxTHC}]`,
obj.variants[j].price,
ayy.variantAvailabilities[obj.variants[j].gay].vq,
`${path}?variant=${obj.variants[j].gay}`
]
);
}
console.log(idx);
//console.log(idx, obj);//pictures, stock, price, variants, terpenes, gtin, the batch quarter half quarter are actually variants of each other. everie drinks are variants of each other(flavor), twd.28 sativa bud remove bud
}
}
await Promise.all([
deal(),
deal(),
deal(),
deal()
]).then(browser.close.bind(browser));
await postgres.query('commit');
await postgres.end();
})();