"use strict"; const { decodeHtml, stripTags, cleanText, extractHtmlAttr, extractFirstImgUrl } = require("../utils/html"); const { normalizeCspc } = require("../utils/sku"); const { extractPriceFromTmbBlock } = require("../utils/woocommerce"); function allowMaltsExcludeGinTequilaMezcal(item) { if (item && item.inStock === false) return false; const cats = Array.isArray(item?.cats) ? item.cats : []; const has = (re) => cats.some((c) => re.test(String(c || ""))); if (has(/\bgin\b/i)) return false; if (has(/\btequila\b/i) || has(/\bmezcal\b/i)) return false; return true; } function parseProductsMaltsAndGrains(html, ctx) { const s = String(html || ""); const items = []; const re = /]*class=["'][^"']*\bproduct\b[^"']*["'][^>]*>[\s\S]*?<\/li>/gi; const blocks = [...s.matchAll(re)].map((m) => m[0] || ""); ctx.logger?.dbg?.(`parseProductsMaltsAndGrains: li.product blocks=${blocks.length} bytes=${s.length}`); const base = `https://${(ctx && ctx.store && ctx.store.host) || "maltsandgrains.store"}/`; for (const block of blocks) { const classAttr = extractHtmlAttr(block, "class"); const isOut = /\boutofstock\b/i.test(classAttr) || /ast-shop-product-out-of-stock/i.test(block) || />\s*out of stock\s*]*href=["']([^"']+)["'][^>]*class=["'][^"']*\b(woocommerce-LoopProduct-link|woocommerce-loop-product__link|ast-loop-product__link)\b/i, )?.[1] || block.match( /]*class=["'][^"']*\b(woocommerce-LoopProduct-link|woocommerce-loop-product__link|ast-loop-product__link)\b[^"']*["'][^>]*href=["']([^"']+)["']/i, )?.[2] || block.match(/]*href=["']([^"']*\/product\/[^"']+)["']/i)?.[1]; if (!href) continue; let url = ""; try { url = new URL(decodeHtml(href), base).toString(); } catch { continue; } if (!/^https?:\/\//i.test(url)) continue; const mTitle = block.match( /]*class=["'][^"']*\bwoocommerce-loop-product__title\b[^"']*["'][^>]*>([\s\S]*?)<\/h2>/i, ); const name = mTitle && mTitle[1] ? cleanText(decodeHtml(stripTags(mTitle[1]))) : ""; if (!name) continue; const price = extractPriceFromTmbBlock(block); const sku = normalizeCspc( block.match(/\bdata-product_sku=["']([^"']+)["']/i)?.[1] || block.match(/\bSKU[:\s]*([0-9]{6})\b/i)?.[1] || "", ); const img = extractFirstImgUrl(block, base); items.push({ name, price, url, sku, img, cats, inStock: true }); } const uniq = new Map(); for (const it of items) uniq.set(it.url, it); return [...uniq.values()]; } function createStore(defaultUa) { return { key: "maltsandgrains", name: "Malts & Grains", host: "maltsandgrains.store", ua: defaultUa, parseProducts: parseProductsMaltsAndGrains, categories: [ { key: "all-minus-gin-tequila-mezcal", label: "All Spirits", startUrl: "https://maltsandgrains.store/shop/page/1/", discoveryStartPage: 15, allowUrl: allowMaltsExcludeGinTequilaMezcal, }, ], }; } module.exports = { createStore };