// OBI-Adapter — holt Preis + Marktverfügbarkeit direkt von obi.de (öffentlich). // Verifiziert 27.06.2026: Produktseite liefert serverseitig Preis (799,99 €) und // Markt-Status ("Im OBI Markt … Derzeit nicht vorrätig"). Per-Markt-Abfrage über // OBI Markt-ID; Endpoint via Netzwerk-Capture bestätigen (TODO: marketId-Param). const UA = 'KuehlfinderBot/0.1 (+https://kuehlfinder.de/bot; kontakt@kuehlfinder.de)'; export const meta = { id: 'obi', name: 'OBI', kind: 'baumarkt' }; // Produkt-Map: unsere productId -> OBI Artikelnummer / URL export const products = { 'portasplit': 'https://www.obi.de/p/8620890/midea-mobile-split-klimaanlage-portasplit', }; function parseJsonLd(html) { const out = []; const re = /]+type=["']application\/ld\+json["'][^>]*>([\s\S]*?)<\/script>/gi; let m; while ((m = re.exec(html))) { try { out.push(JSON.parse(m[1])); } catch {} } return out; } function findOffer(ld) { const arr = Array.isArray(ld) ? ld : [ld]; for (const node of arr) { const graph = node['@graph'] || [node]; for (const g of graph) { if (g && (g['@type'] === 'Product' || g.offers)) { const o = Array.isArray(g.offers) ? g.offers[0] : g.offers; if (o) return { price: Number(o.price ?? o.lowPrice), availability: String(o.availability || '') }; } } } return null; } export async function fetchProduct(productId, { marketId } = {}) { const url = products[productId]; if (!url) return null; const res = await fetch(url, { headers: { 'User-Agent': UA, 'Accept-Language': 'de-DE' } }); if (!res.ok) throw new Error('OBI ' + res.status); const html = await res.text(); const ld = parseJsonLd(html).map(findOffer).find(Boolean); // Fallback: HTML-Heuristik für Markt-Status const inStore = /vorrätig/i.test(html) && !/Derzeit nicht vorrätig/i.test(html); const price = ld?.price ?? (html.match(/(\d{2,4},\d{2})\s*€/)?.[1]?.replace(',', '.')); const online = ld ? /InStock/i.test(ld.availability) : /Lieferung[^<]*möglich/i.test(html); return { productId, source: 'obi', marketId: marketId ?? null, price: price ? Number(price) : null, availability: inStore ? 'in_stock' : (online ? 'online_available' : 'out_of_stock'), checkedAt: new Date().toISOString(), url, }; } // Markt-Verzeichnis von OBI selbst (eigene Quelle, nicht braucheklima). // TODO: obi.de/markt liefert Marktliste; hier Regionseiten/Sitemap parsen. export async function fetchStores() { return []; }