#!/usr/bin/env node /** * Coto promotions fetcher - uses ATG internal API (no WAF issues) * Returns bank promos, day-based discounts, community promos * * Usage: node coto_fetch_promos.js * Output: data/coto/promos-latest.json */ 'use strict'; const https = require('https'); const fs = require('fs/promises'); const path = require('path'); const BASE_DIR = path.join('/home/ubuntu/.openclaw/workspace', 'data', 'coto'); const OUT_FILE = path.join(BASE_DIR, 'promos-latest.json'); const ARCHIVE_DIR = path.join(BASE_DIR, 'promos-archive'); const ATG_URL = 'https://www.cotodigital.com.ar/rest/model/atg/actors/cProfileActor/getPromociones?enviroment=ag'; function httpGet(url) { return new Promise((resolve, reject) => { const req = https.request(url, { headers: { 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36', 'Accept': 'application/json', }, }, (res) => { const chunks = []; res.on('data', c => chunks.push(c)); res.on('end', () => resolve({ status: res.statusCode, body: Buffer.concat(chunks).toString('utf8') })); }); req.on('error', reject); req.end(); }); } function parseItems(arrayItem) { if (!Array.isArray(arrayItem)) return []; return arrayItem .filter(i => i && i.titDescuento) .map(i => ({ descuento: i.titDescuento, detalle: i.titDescuentoDet || '', dias: i.titDias || '', footer: i.titFooter || '', imagen: i.imagenPath || '', })); } function groupByDay(groups) { const DAYS = ['Lunes', 'Martes', 'Miercoles', 'Miércoles', 'Jueves', 'Viernes', 'Sabado', 'Sábado', 'Domingo']; const byDay = {}; for (const group of groups) { for (const item of group.items) { const dias = (item.dias || '').toLowerCase(); let dayKey; if (dias.includes('lunes a domingo') || dias.includes('todos')) { dayKey = 'Todos los días'; } else { for (const d of DAYS) { if (dias.includes(d.toLowerCase())) { dayKey = d.replace('Miercoles', 'Miércoles').replace('Sabado', 'Sábado'); break; } } if (!dayKey) dayKey = item.dias || 'Otros'; } if (!byDay[dayKey]) byDay[dayKey] = []; byDay[dayKey].push({ ...item, imagen: group.imagen }); } } return byDay; } async function main() { console.log('Fetching Coto promotions from ATG API...'); const { status, body } = await httpGet(ATG_URL); if (status !== 200) { console.error(`ERROR: HTTP ${status}`); process.exit(1); } let data; try { data = JSON.parse(body); } catch { console.error('ERROR: Invalid JSON:', body.slice(0, 200)); process.exit(1); } const result = data.result || {}; const parseSection = (arr) => (arr || []) .map(g => ({ imagen: g.imageUrl || g.imagenPath || '', items: parseItems(g.arrayItem) })) .filter(g => g.items.length > 0); const porBanco = parseSection(result.porBanco); const porDia = parseSection(result.porDia); const conComunidad = parseSection(result.conComunidad); const allGroups = [...porBanco, ...porDia, ...conComunidad]; const allItems = allGroups.flatMap(g => g.items); const byDay = groupByDay(allGroups); const output = { store: 'coto', generatedAt: new Date().toISOString(), source: ATG_URL, stats: { totalPromos: allItems.length, conDescuento: allItems.filter(i => i.descuento.match(/\d+%/) || i.descuento.toLowerCase().includes('descuento')).length, conCuotas: allItems.filter(i => i.descuento.toLowerCase().includes('cuotas')).length, dias: Object.keys(byDay), }, byDay, porBanco, porDia, conComunidad, }; console.log(`OK coto promos=${allItems.length} dias=[${output.stats.dias.join(', ')}]`); console.log(` descuento%: ${output.stats.conDescuento} cuotas: ${output.stats.conCuotas}`); await fs.mkdir(BASE_DIR, { recursive: true }); await fs.mkdir(ARCHIVE_DIR, { recursive: true }); await fs.writeFile(OUT_FILE, JSON.stringify(output, null, 2), 'utf8'); const ts = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19); await fs.writeFile(path.join(ARCHIVE_DIR, `promos-${ts}.json`), JSON.stringify(output, null, 2), 'utf8'); console.log(`Saved: ${OUT_FILE}`); } main().catch(e => { console.error(e); process.exit(1); });