diff --git a/dripzone-app/src/app/admin/grupos-whatsapp/page.tsx b/dripzone-app/src/app/admin/grupos-whatsapp/page.tsx index db9b690..c994201 100644 --- a/dripzone-app/src/app/admin/grupos-whatsapp/page.tsx +++ b/dripzone-app/src/app/admin/grupos-whatsapp/page.tsx @@ -82,7 +82,20 @@ export default function AdminWhatsAppGroupsPage() { } }; + const isGroupIgnored = (jid: string, name?: string): boolean => { + if (!jid) return false; + if (jid === '120363400148850031@g.us' || jid === '120363420942364759@g.us') return true; + if (name && /black diamonds|team emporio|emporio/i.test(name)) return true; + return false; + }; + const handleToggleSelectJid = (jid: string) => { + const grp = liveGroups.find((g) => g.jid === jid); + if (grp && isGroupIgnored(grp.jid, grp.name)) { + setToast(`⛔ El grupo "${grp.name}" está excluido permanentemente y no puede seleccionarse.`); + setTimeout(() => setToast(null), 4000); + return; + } setSelectedJids((prev) => { const next = new Set(prev); if (next.has(jid)) next.delete(jid); @@ -94,7 +107,11 @@ export default function AdminWhatsAppGroupsPage() { const handleSelectAllFiltered = (filtered: MonitoredWhatsAppGroup[]) => { setSelectedJids((prev) => { const next = new Set(prev); - filtered.forEach((g) => next.add(g.jid)); + filtered.forEach((g) => { + if (!isGroupIgnored(g.jid, g.name)) { + next.add(g.jid); + } + }); return next; }); }; @@ -108,9 +125,10 @@ export default function AdminWhatsAppGroupsPage() { try { const updatedGroups: MonitoredWhatsAppGroup[] = liveGroups.map((lg) => ({ ...lg, - isMonitored: selectedJids.has(lg.jid), - autoPublish: true, + isMonitored: !isGroupIgnored(lg.jid, lg.name) && selectedJids.has(lg.jid), + autoPublish: !isGroupIgnored(lg.jid, lg.name), profitMarginOverride: 35, + notes: isGroupIgnored(lg.jid, lg.name) ? 'IGNORADO EXPLICITAMENTE' : lg.notes, })); const res = await fetch('/api/whatsapp-groups', { @@ -134,6 +152,12 @@ export default function AdminWhatsAppGroupsPage() { }; const handleToggleGroup = async (group: MonitoredWhatsAppGroup) => { + if (isGroupIgnored(group.jid, group.name)) { + setToast(`⛔ El grupo "${group.name}" está en la lista de exclusión (ignorado permanente) y no puede ser monitoreado.`); + setTimeout(() => setToast(null), 4500); + return; + } + const nextState = !group.isMonitored; const updatedList = groups.map((g) => g.jid === group.jid || g.id === group.id ? { ...g, isMonitored: nextState } : g @@ -329,12 +353,15 @@ export default function AdminWhatsAppGroupsPage() { ) : ( filteredGroups.map((group) => { const isMonitored = group.isMonitored; + const isIgnored = isGroupIgnored(group.jid, group.name); return (
-
+

{group.name}

+ {isIgnored && ( + + ⛔ Ignorado + + )}

{group.jid} @@ -360,13 +392,21 @@ export default function AdminWhatsAppGroupsPage() { {/* Toggle Switch */}

) : ( filteredPickerGroups.map((group) => { - const isSelected = selectedJids.has(group.jid); + const isIgnored = isGroupIgnored(group.jid, group.name); + const isSelected = !isIgnored && selectedJids.has(group.jid); return (
handleToggleSelectJid(group.jid)} - className={`p-3.5 rounded-2xl border transition-all cursor-pointer flex items-center justify-between gap-3 ${ - isSelected - ? 'bg-purple-950/30 border-purple-500/50 shadow-md ring-1 ring-purple-500/20' - : 'bg-neutral-950 border-neutral-800 hover:border-neutral-700' + onClick={() => !isIgnored && handleToggleSelectJid(group.jid)} + className={`p-3.5 rounded-2xl border transition-all flex items-center justify-between gap-3 ${ + isIgnored + ? 'bg-neutral-950/60 border-rose-950/60 opacity-60 cursor-not-allowed' + : isSelected + ? 'bg-purple-950/30 border-purple-500/50 shadow-md ring-1 ring-purple-500/20 cursor-pointer' + : 'bg-neutral-950 border-neutral-800 hover:border-neutral-700 cursor-pointer' }`} >
-

- {group.name} +

+ {group.name} + {isIgnored && ( + + ⛔ Ignorado + + )}

{group.jid} @@ -534,7 +584,11 @@ export default function AdminWhatsAppGroupsPage() {

- {isSelected ? ( + {isIgnored ? ( + + Bloqueado + + ) : isSelected ? ( Monitorear diff --git a/dripzone-app/src/app/api/webhook/whatsapp/route.ts b/dripzone-app/src/app/api/webhook/whatsapp/route.ts index 4a6d8a2..0cdf785 100644 --- a/dripzone-app/src/app/api/webhook/whatsapp/route.ts +++ b/dripzone-app/src/app/api/webhook/whatsapp/route.ts @@ -1,5 +1,5 @@ import { NextResponse } from 'next/server'; -import { dataService } from '@/lib/data-service'; +import { dataService, isGroupIgnoredOrBlacklisted } from '@/lib/data-service'; import { parseSupplierMessage, detectCategory } from '@/lib/gemini-parser'; import { sanitizePublicationTitle } from '@/lib/sanitizer'; import { generateAttractiveProductCopy } from '@/lib/attractive-copy'; @@ -14,6 +14,7 @@ import { } from '@/lib/evolution-api'; import { getMonitoredGroupsFromAppwrite, + saveMonitoredGroupToAppwrite, saveProductToAppwrite, saveSupplierPostToAppwrite, updateSupplierPostStatusInAppwrite, @@ -238,7 +239,7 @@ export async function POST(request: Request) { } // ========================================================================= - // CASO B: GRUPOS MAYORISTAS DE PROVEEDORES (Publicación Automática Incondicional) + // CASO B: GRUPOS MAYORISTAS DE PROVEEDORES (Monitoreo Estricto por Lista Blanca) // ========================================================================= // Resolver el nombre del grupo de forma precisa usando Evolution API si no viene en el payload let groupSubject = data.groupInfo?.subject || data.chatName || ''; @@ -255,6 +256,17 @@ export async function POST(request: Request) { } } + // 0. LISTA NEGRA PERMANENTE DE GRUPOS IGNORADOS (Blacklist incondicional) + if (isGroupIgnoredOrBlacklisted(remoteJid, groupSubject)) { + console.log(`[DripZone Webhook] Grupo IGNORADO por lista negra: "${groupSubject}" (${remoteJid})`); + return NextResponse.json({ + success: true, + ignored: 'Grupo en lista negra comercial permanente (no monitorear ni publicar)', + chat: groupSubject, + remoteJid, + }); + } + // 1. FILTRAR EXCLUSIVAMENTE GRUPOS DE PRENSA Y GOBIERNO (Reenviar a portal de noticias Almeira) const isNonProductNews = /gob cauca|gobernacion|alcaldia|prensa|medios|noticias|comunicaciones|sedes asociadas|notificaciones almeira|inelpac|meliextra|almeira producciones|publicaciones almeira|quilichao en medios/i.test( groupSubject @@ -292,25 +304,68 @@ export async function POST(request: Request) { }); } - // Auto-registrar grupo como monitoreado y activo si no existía previamente - try { - const currentGroups = dataService.getMonitoredGroups(); - const existingGrp = currentGroups.find((g) => g.jid === remoteJid); - if (!existingGrp) { - dataService.saveMonitoredGroups([ - ...currentGroups, - { - id: `grp-${remoteJid.replace(/\D/g, '').slice(-8)}`, - jid: remoteJid, - name: groupSubject, - isMonitored: true, - autoPublish: true, - notes: 'Auto-registrado desde Webhook activo de WhatsApp', - supplierName: groupSubject, - }, - ]); - } - } catch (_) {} + // 1.2 FILTRAR CONTENIDO DE EVENTOS, CONFERENCIAS, REUNIONES VIRTUALES O SPAM + const isSpamOrAnnouncement = /conferencia|webinar|zoom\.us|pantalla de zoom|podcast|mañanas millonarias|liderazgo|liderea|orador|taller virtual|evento online|conversaciones m[aá]s dinero|estrategia que funciona|elevax10n|smoothie|strawberry flavored|smartwatch|reloj inteligente|forex|cripto|trading|hoy 🚨|empezamos en \d+ minutos|llamada 911/i.test( + `${messageContent} ${groupSubject}` + ); + if (isSpamOrAnnouncement) { + console.log(`[DripZone Webhook] Mensaje descartado por ser anuncio/evento/no calzado: "${messageContent.slice(0, 60)}"`); + return NextResponse.json({ + success: true, + ignored: 'Mensaje es un anuncio, evento o contenido no calzado', + chat: groupSubject, + remoteJid, + }); + } + + // 1.3 VERIFICAR QUE EL GRUPO ESTÉ EXPRESAMENTE MONITOREADO Y ACTIVO + let currentGroups = dataService.getMonitoredGroups(); + let existingGrp = currentGroups.find( + (g) => g.jid === remoteJid || g.name.toLowerCase().replace(/\s+/g, ' ').trim() === groupSubject.toLowerCase().replace(/\s+/g, ' ').trim() + ); + + if (!existingGrp) { + try { + const appwriteGroups = await getMonitoredGroupsFromAppwrite(); + existingGrp = appwriteGroups.find((g) => g.jid === remoteJid); + if (existingGrp) { + dataService.saveMonitoredGroups([...currentGroups, existingGrp]); + } + } catch (_) {} + } + + // Si el grupo nunca se ha configurado, registrarlo como INACTIVO (pendiente de aprobación en panel) + if (!existingGrp) { + const newGroupDoc = { + id: `grp-${remoteJid.replace(/\D/g, '').slice(-8)}`, + jid: remoteJid, + name: groupSubject, + isMonitored: false, + autoPublish: false, + notes: 'Detectado en webhook (inactivo por defecto)', + supplierName: groupSubject, + }; + dataService.saveMonitoredGroups([...currentGroups, newGroupDoc]); + saveMonitoredGroupToAppwrite(newGroupDoc).catch(() => {}); + console.log(`[DripZone Webhook] Nuevo grupo detectado pero REGISTRADO COMO INACTIVO: "${groupSubject}" (${remoteJid})`); + return NextResponse.json({ + success: true, + ignored: 'Grupo nuevo no monitoreado (registrado inactivo en panel admin)', + chat: groupSubject, + remoteJid, + }); + } + + // Si el grupo está desactivado o tiene auto-publicación apagada + if (!existingGrp.isMonitored || existingGrp.autoPublish === false) { + console.log(`[DripZone Webhook] Mensaje descartado: grupo no monitoreado o autoPublish apagado: "${groupSubject}" (${remoteJid})`); + return NextResponse.json({ + success: true, + ignored: 'Grupo no monitoreado o auto-publicación apagada', + chat: groupSubject, + remoteJid, + }); + } // 2. VERIFICACIÓN DE DUPLICADOS if (isDuplicateMessage(whatsappMessageId, messageContent)) { @@ -397,6 +452,17 @@ export async function POST(request: Request) { }; } + // Validación de coherencia y descarte de spam + const invalidTitleCheck = /hoy 🚨|conferencia|evento|zoom|podcast|mañanas millonarias|liderea|sin producto|elevax10n|smoothie|no especificado/i; + if (invalidTitleCheck.test(extracted.name || '') || invalidTitleCheck.test(messageContent)) { + console.log(`[DripZone Webhook] Publicación cancelada por contenido no comercial/calzado: "${extracted.name}"`); + return NextResponse.json({ + success: true, + ignored: 'Contenido descartado por no ser calzado o producto legítimo', + name: extracted.name, + }); + } + // Categorización segura if (!extracted.category || extracted.category === 'Calzado / Sneakers' || extracted.category === 'Sneakers / Colección') { extracted.category = detectCategory(`${extracted.name || ''} ${messageContent}`).category; diff --git a/dripzone-app/src/app/api/whatsapp-groups/route.ts b/dripzone-app/src/app/api/whatsapp-groups/route.ts index ea8d76a..29a6559 100644 --- a/dripzone-app/src/app/api/whatsapp-groups/route.ts +++ b/dripzone-app/src/app/api/whatsapp-groups/route.ts @@ -1,5 +1,5 @@ import { NextResponse } from 'next/server'; -import { dataService } from '@/lib/data-service'; +import { dataService, isGroupIgnoredOrBlacklisted } from '@/lib/data-service'; import { MonitoredWhatsAppGroup } from '@/types'; import { getMonitoredGroupsFromAppwrite, @@ -22,7 +22,7 @@ export async function GET(request: Request) { const appwriteGroups = await getMonitoredGroupsFromAppwrite(); if (appwriteGroups.length > 0) { dataService.saveMonitoredGroups(appwriteGroups); - localGroups = appwriteGroups; + localGroups = dataService.getMonitoredGroups(); } } catch (err) { console.warn('Error leyendo grupos desde Appwrite:', err); @@ -36,24 +36,46 @@ export async function GET(request: Request) { }); } - // Consultar grupos en vivo desde Evolution API usando findChats (base de datos local rápida sin rate-limits) + // Consultar grupos en vivo desde Evolution API usando findChats y fetchAllGroups try { - const response = await fetch( - `${EVOLUTION_API_URL}/chat/findChats/${EVOLUTION_INSTANCE_NAME}`, - { - method: 'POST', - headers: { - apikey: EVOLUTION_API_KEY, - 'Content-Type': 'application/json', - }, - body: JSON.stringify({}), + let liveGroups: any[] = []; + try { + const response = await fetch( + `${EVOLUTION_API_URL}/group/fetchAllGroups/${EVOLUTION_INSTANCE_NAME}?getParticipants=false`, + { + headers: { apikey: EVOLUTION_API_KEY }, + signal: AbortSignal.timeout(8000), + } + ); + if (response.ok) { + const all = await response.json(); + if (Array.isArray(all) && all.length > 0) { + liveGroups = all.map((g: any) => ({ + remoteJid: g.id || g.jid, + name: g.subject || g.name || 'Grupo WhatsApp', + size: g.size, + })); + } } - ); + } catch (_) {} - const allChats = await response.json(); - const liveGroups = (Array.isArray(allChats) ? allChats : []).filter( - (c: any) => (c.remoteJid && c.remoteJid.includes('@g.us')) || (c.id && c.id.includes('@g.us')) - ); + if (liveGroups.length === 0) { + const response = await fetch( + `${EVOLUTION_API_URL}/chat/findChats/${EVOLUTION_INSTANCE_NAME}`, + { + method: 'POST', + headers: { + apikey: EVOLUTION_API_KEY, + 'Content-Type': 'application/json', + }, + body: JSON.stringify({}), + } + ); + const allChats = await response.json(); + liveGroups = (Array.isArray(allChats) ? allChats : []).filter( + (c: any) => (c.remoteJid && c.remoteJid.includes('@g.us')) || (c.id && c.id.includes('@g.us')) + ); + } if (liveGroups.length > 0) { const merged: MonitoredWhatsAppGroup[] = await Promise.all( @@ -61,49 +83,34 @@ export async function GET(request: Request) { const jid = lg.remoteJid || lg.id || lg.jid; const name = lg.name || lg.pushName || lg.subject || 'Grupo WhatsApp'; let isStillMember = true; - let participants = undefined; + let participants = lg.size || undefined; - try { - const infoRes = await fetch( - `${EVOLUTION_API_URL}/group/findGroupInfos/${EVOLUTION_INSTANCE_NAME}?groupJid=${jid}`, - { - headers: { apikey: EVOLUTION_API_KEY }, - signal: AbortSignal.timeout(3500), - } - ); - if (infoRes.status === 404 || infoRes.status === 401 || infoRes.status === 403) { - isStillMember = false; - } else if (infoRes.ok) { - const infoData = await infoRes.json(); - if (Array.isArray(infoData.participants)) { - participants = infoData.participants.length; - } - } - } catch { - // Si falla por timeout, mantener estado previo - } + const isBlacklisted = isGroupIgnoredOrBlacklisted(jid, name); + const existing = localGroups.find( + (g) => g.jid === jid || g.name.toLowerCase().replace(/\s+/g, ' ').trim() === name.toLowerCase().replace(/\s+/g, ' ').trim() + ); - const existing = localGroups.find((g) => g.jid === jid || g.name.toLowerCase() === name.toLowerCase()); - - // Pre-marcar como monitoreados grupos reconocidos de proveedores si no había configuración previa - const isKnownSupplierGroup = /exclusive|emporio|jovic|diamonds|mayorista|calzado|sneakers|zapatillas/i.test(name); - const isMonitored = existing + // Pre-marcar SOLO proveedores exclusivos verificados de calzado si no había configuración previa + const isKnownSupplierGroup = !isBlacklisted && /exclusive cc|jovic store/i.test(name); + const isMonitored = isBlacklisted ? false : (existing ? (existing.isMonitored && isStillMember) - : (isKnownSupplierGroup && isStillMember); + : (isKnownSupplierGroup && isStillMember)); return { - id: existing?.id || `grp-${jid.replace(/\D/g, '').slice(-6)}`, + id: existing?.id || `grp-${jid.replace(/\D/g, '').slice(-8)}`, jid, name, - isMonitored: isStillMember ? isMonitored : false, + isMonitored: isBlacklisted ? false : (isStillMember ? isMonitored : false), isStillMember, hasLeft: !isStillMember, supplierId: existing?.supplierId, supplierName: existing?.supplierName || name, profitMarginOverride: existing?.profitMarginOverride || 35, - autoPublish: existing?.autoPublish ?? true, + autoPublish: isBlacklisted ? false : (existing?.autoPublish ?? true), participantsCount: participants ?? lg.unreadCount, - notes: isStillMember ? (existing?.notes || '') : 'Saliste de este grupo en WhatsApp', + notes: isBlacklisted + ? 'IGNORADO EXPLICITAMENTE' + : isStillMember ? (existing?.notes || '') : 'Saliste de este grupo en WhatsApp', }; }) ); @@ -145,7 +152,8 @@ export async function POST(request: Request) { if (body.action === 'toggle' || body.action === 'deactivate-group') { const { idOrJid, isMonitored } = body; - const targetState = isMonitored !== undefined ? isMonitored : false; + const isBlacklisted = isGroupIgnoredOrBlacklisted(idOrJid); + const targetState = isBlacklisted ? false : (isMonitored !== undefined ? isMonitored : false); const updated = dataService.toggleGroupMonitoring(idOrJid, targetState); toggleMonitoredGroupInAppwrite(idOrJid, targetState).catch((e) => console.warn('Error sincronizando toggle con Appwrite:', e) @@ -154,8 +162,14 @@ export async function POST(request: Request) { } if (Array.isArray(body.groups)) { - const saved = dataService.saveMonitoredGroups(body.groups); - Promise.all(body.groups.map((g: any) => saveMonitoredGroupToAppwrite(g))).catch((e) => + const sanitized = body.groups.map((g: any) => { + if (isGroupIgnoredOrBlacklisted(g.jid, g.name)) { + return { ...g, isMonitored: false, autoPublish: false, notes: 'IGNORADO EXPLICITAMENTE' }; + } + return g; + }); + const saved = dataService.saveMonitoredGroups(sanitized); + Promise.all(sanitized.map((g: any) => saveMonitoredGroupToAppwrite(g))).catch((e) => console.warn('Error sincronizando grupos con Appwrite:', e) ); return NextResponse.json({ success: true, groups: saved }); diff --git a/dripzone-app/src/lib/data-service.ts b/dripzone-app/src/lib/data-service.ts index f675195..afe5269 100644 --- a/dripzone-app/src/lib/data-service.ts +++ b/dripzone-app/src/lib/data-service.ts @@ -227,6 +227,19 @@ export const INITIAL_USERS: User[] = [ }, ]; +export const BLACKLISTED_WHATSAPP_GROUPS: string[] = [ + '120363400148850031@g.us', // SOMOS BLACK DIAMONDS 💎💎💎 + '120363420942364759@g.us', // 💎TEAM EMPORIO💎🔥👑❤️‍🔥🤯 +]; + +export function isGroupIgnoredOrBlacklisted(jid: string, name?: string): boolean { + if (!jid) return false; + const cleanJid = jid.trim(); + if (BLACKLISTED_WHATSAPP_GROUPS.includes(cleanJid)) return true; + if (name && /black diamonds|team emporio|emporio/i.test(name)) return true; + return false; +} + export const INITIAL_MONITORED_GROUPS: MonitoredWhatsAppGroup[] = []; function getFsModule() { @@ -1092,21 +1105,48 @@ class DataService { return user || null; } - private monitoredGroups: MonitoredWhatsAppGroup[] = [...INITIAL_MONITORED_GROUPS]; + private monitoredGroups: MonitoredWhatsAppGroup[] = loadPersistedData( + 'persisted_monitored_groups.json', + [...INITIAL_MONITORED_GROUPS] + ); getMonitoredGroups(): MonitoredWhatsAppGroup[] { - return this.monitoredGroups; + if (!this.monitoredGroups || this.monitoredGroups.length === 0) { + this.monitoredGroups = loadPersistedData( + 'persisted_monitored_groups.json', + [...INITIAL_MONITORED_GROUPS] + ); + } + return this.monitoredGroups.map((g) => { + if (isGroupIgnoredOrBlacklisted(g.jid, g.name)) { + return { ...g, isMonitored: false, autoPublish: false, notes: 'IGNORADO EXPLICITAMENTE' }; + } + return g; + }); } saveMonitoredGroups(groups: MonitoredWhatsAppGroup[]): MonitoredWhatsAppGroup[] { - this.monitoredGroups = groups; + this.monitoredGroups = groups.map((g) => { + if (isGroupIgnoredOrBlacklisted(g.jid, g.name)) { + return { ...g, isMonitored: false, autoPublish: false, notes: 'IGNORADO EXPLICITAMENTE' }; + } + return g; + }); + savePersistedData('persisted_monitored_groups.json', this.monitoredGroups); return this.monitoredGroups; } toggleGroupMonitoring(idOrJid: string, isMonitored: boolean): MonitoredWhatsAppGroup | null { const grp = this.monitoredGroups.find((g) => g.id === idOrJid || g.jid === idOrJid); if (grp) { - grp.isMonitored = isMonitored; + if (isGroupIgnoredOrBlacklisted(grp.jid, grp.name)) { + grp.isMonitored = false; + grp.autoPublish = false; + grp.notes = 'IGNORADO EXPLICITAMENTE'; + } else { + grp.isMonitored = isMonitored; + } + savePersistedData('persisted_monitored_groups.json', this.monitoredGroups); return grp; } return null; diff --git a/dripzone-app/src/lib/server-appwrite.ts b/dripzone-app/src/lib/server-appwrite.ts index bdcbedc..0e95010 100644 --- a/dripzone-app/src/lib/server-appwrite.ts +++ b/dripzone-app/src/lib/server-appwrite.ts @@ -11,7 +11,7 @@ import { MessageTemplate, CustomProductRequest, } from '@/types'; -import { INITIAL_CITIES, INITIAL_PAYMENT_METHODS, INITIAL_SUPPLIERS } from './data-service'; +import { INITIAL_CITIES, INITIAL_PAYMENT_METHODS, INITIAL_SUPPLIERS, isGroupIgnoredOrBlacklisted } from './data-service'; import { DEFAULT_MESSAGE_TEMPLATES } from './templates-service'; import { sanitizePublicationTitle, sanitizePublicationText } from './sanitizer'; @@ -717,15 +717,18 @@ export async function getMonitoredGroupsFromAppwrite(): Promise 0) { - return res.documents.map((d: any) => ({ - id: d.$id, - jid: d.jid || '', - name: d.name || '', - isMonitored: Boolean(d.isMonitored), - autoPublish: d.autoPublish !== false, - notes: d.notes || '', - supplierName: d.name || '', - })); + return res.documents.map((d: any) => { + const isBlacklisted = isGroupIgnoredOrBlacklisted(d.jid, d.name); + return { + id: d.$id, + jid: d.jid || '', + name: d.name || '', + isMonitored: isBlacklisted ? false : Boolean(d.isMonitored), + autoPublish: isBlacklisted ? false : d.autoPublish !== false, + notes: isBlacklisted ? 'IGNORADO EXPLICITAMENTE' : d.notes || '', + supplierName: d.name || '', + }; + }); } return []; } catch (e: any) { @@ -737,13 +740,14 @@ export async function getMonitoredGroupsFromAppwrite(): Promise ): Promise { + const isBlacklisted = isGroupIgnoredOrBlacklisted(group.jid || '', group.name); const docId = group.id || `grp-${(group.jid || '').replace(/\D/g, '').slice(-8)}` || `grp-${Date.now()}`; const payload = { jid: group.jid || '', name: group.name || '', - isMonitored: Boolean(group.isMonitored), - autoPublish: group.autoPublish !== false, - notes: group.notes || '', + isMonitored: isBlacklisted ? false : Boolean(group.isMonitored), + autoPublish: isBlacklisted ? false : group.autoPublish !== false, + notes: isBlacklisted ? 'IGNORADO EXPLICITAMENTE' : group.notes || '', }; try { @@ -780,12 +784,24 @@ export async function toggleMonitoredGroupInAppwrite( isMonitored: boolean ): Promise { try { - const groups = await getMonitoredGroupsFromAppwrite(); - const found = groups.find((g) => g.id === idOrJid || g.jid === idOrJid); - if (found) { - await serverDatabases.updateDocument(DB_ID, 'monitored_groups', found.id, { - isMonitored, - }); + const isBlacklisted = isGroupIgnoredOrBlacklisted(idOrJid); + const finalState = isBlacklisted ? false : isMonitored; + + const res = await serverDatabases.listDocuments(DB_ID, 'monitored_groups', [Query.limit(100)]); + const matchingDocs = res.documents.filter( + (d: any) => d.$id === idOrJid || d.jid === idOrJid || d.name === idOrJid + ); + + if (matchingDocs.length > 0) { + await Promise.all( + matchingDocs.map((doc: any) => + serverDatabases.updateDocument(DB_ID, 'monitored_groups', doc.$id, { + isMonitored: finalState, + autoPublish: finalState, + notes: isBlacklisted ? 'IGNORADO EXPLICITAMENTE' : doc.notes, + }) + ) + ); return true; } return false; diff --git a/scripts/clean_unwanted_supplier_products.js b/scripts/clean_unwanted_supplier_products.js new file mode 100644 index 0000000..cc7d8f4 --- /dev/null +++ b/scripts/clean_unwanted_supplier_products.js @@ -0,0 +1,114 @@ +const sdk = require('node-appwrite'); +const client = new sdk.Client() + .setEndpoint('https://appwrite.almeira.com.co/v1') + .setProject('69b84df5003bd8957c5c') + .setKey('standard_cce3cdb866ea6433888d40cedd24dc45987372308fdc3dd0697de08648c0e5c7e04b02c3bcee271403fd656b00a5820f04c08b51631284a6e8a3582d8aed2aa33234f3c29cc777e9b162eb54810b789251b0d006a9a3b081ebc2da53b9baa94614b124b3508ad849fd9a4187cc6094f506d4b57f409b69d9562a74df594f3ec3'); +const dbs = new sdk.Databases(client); + +const DB_ID = 'sneakers_multistore_db'; + +async function main() { + console.log('=== [1] ELIMINANDO PRODUCTOS BASURA DE LOS GRUPOS IGNORADOS ==='); + let offset = 0; + const limit = 100; + const toDelete = []; + + while (true) { + const res = await dbs.listDocuments(DB_ID, 'products', [ + sdk.Query.limit(limit), + sdk.Query.offset(offset) + ]); + if (!res.documents || res.documents.length === 0) break; + + for (const doc of res.documents) { + const sup = (doc.supplierName || '').toLowerCase(); + const name = (doc.name || '').toLowerCase(); + const raw = JSON.stringify(doc).toLowerCase(); + + if ( + sup.includes('emporio') || + sup.includes('black diamonds') || + raw.includes('120363400148850031') || + raw.includes('120363420942364759') || + name.includes('hoy 🚨') || + name.includes('liderea con') + ) { + // Asegurar que no borramos productos legítimos de Jovic o CC Mío + if (sup.includes('jovic') || sup.includes('cc mío') || sup.includes('exclusive')) { + continue; + } + toDelete.push({ id: doc['$id'], name: doc.name, supplier: doc.supplierName }); + } + } + offset += limit; + if (offset >= res.total) break; + } + + console.log(`Encontrados ${toDelete.length} productos para eliminar:`); + for (const p of toDelete) { + try { + await dbs.deleteDocument(DB_ID, 'products', p.id); + console.log(` ✓ Eliminado producto: ${p.id} - ${p.name} (${p.supplier})`); + } catch (e) { + console.error(` ✗ Error eliminando ${p.id}:`, e.message); + } + } + + console.log('\n=== [2] SANITIZANDO COLECCIÓN MONITORED_GROUPS EN APPWRITE ==='); + const groupsRes = await dbs.listDocuments(DB_ID, 'monitored_groups', [sdk.Query.limit(100)]); + console.log(`Total grupos en Appwrite: ${groupsRes.total}`); + + const blacklistedJids = [ + '120363400148850031@g.us', // SOMOS BLACK DIAMONDS + '120363420942364759@g.us', // TEAM EMPORIO + ]; + + // Agrupar por JID para eliminar duplicados + const groupsByJid = new Map(); + for (const doc of groupsRes.documents) { + const jid = doc.jid; + if (!groupsByJid.has(jid)) { + groupsByJid.set(jid, []); + } + groupsByJid.get(jid).push(doc); + } + + for (const [jid, docs] of groupsByJid.entries()) { + const isBlacklisted = blacklistedJids.includes(jid) || + docs.some(d => /black diamonds|team emporio|emporio/i.test(d.name || '')); + + // Mantener 1 documento canónico y eliminar duplicados + const canonicalDoc = docs[0]; + const duplicates = docs.slice(1); + + for (const dup of duplicates) { + try { + await dbs.deleteDocument(DB_ID, 'monitored_groups', dup['$id']); + console.log(` ✓ Eliminado documento duplicado: ${dup['$id']} (${jid})`); + } catch (e) { + console.error(` ✗ Error eliminando duplicado ${dup['$id']}:`, e.message); + } + } + + if (isBlacklisted) { + try { + await dbs.updateDocument(DB_ID, 'monitored_groups', canonicalDoc['$id'], { + isMonitored: 0, + autoPublish: 0, + notes: 'IGNORADO EXPLICITAMENTE - NO PUBLICAR NI MONITOREAR', + }); + console.log(` 🛑 Grupo marcado como IGNORADO permanentemente: ${canonicalDoc.name} (${jid})`); + } catch (e) { + console.error(` ✗ Error actualizando grupo ${canonicalDoc['$id']}:`, e.message); + } + } + } + + console.log('\n=== [3] VERIFICACIÓN FINAL DE GRUPOS EN APPWRITE ==='); + const finalRes = await dbs.listDocuments(DB_ID, 'monitored_groups', [sdk.Query.limit(100)]); + finalRes.documents.forEach((d) => { + console.log(`ID: ${d['$id']} | JID: ${d.jid} | isMonitored: ${d.isMonitored} | Name: ${d.name}`); + }); +} + +main().catch(console.error); diff --git a/scripts/find_all_unwanted_products.js b/scripts/find_all_unwanted_products.js new file mode 100644 index 0000000..4aa5dc8 --- /dev/null +++ b/scripts/find_all_unwanted_products.js @@ -0,0 +1,45 @@ +const sdk = require('node-appwrite'); +const client = new sdk.Client() + .setEndpoint('https://appwrite.almeira.com.co/v1') + .setProject('69b84df5003bd8957c5c') + .setKey('standard_cce3cdb866ea6433888d40cedd24dc45987372308fdc3dd0697de08648c0e5c7e04b02c3bcee271403fd656b00a5820f04c08b51631284a6e8a3582d8aed2aa33234f3c29cc777e9b162eb54810b789251b0d006a9a3b081ebc2da53b9baa94614b124b3508ad849fd9a4187cc6094f506d4b57f409b69d9562a74df594f3ec3'); +const dbs = new sdk.Databases(client); + +async function inspectAll() { + let offset = 0; + const limit = 100; + const toDelete = []; + + while (true) { + const res = await dbs.listDocuments('sneakers_multistore_db', 'products', [ + sdk.Query.limit(limit), + sdk.Query.offset(offset) + ]); + if (!res.documents || res.documents.length === 0) break; + + for (const doc of res.documents) { + const sup = (doc.supplierName || '').toLowerCase(); + const name = (doc.name || '').toLowerCase(); + const raw = JSON.stringify(doc).toLowerCase(); + + if ( + sup.includes('emporio') || + sup.includes('black diamonds') || + raw.includes('120363400148850031') || + raw.includes('120363420942364759') || + name.includes('hoy 🚨') || + name.includes('liderea con') + ) { + toDelete.push({ id: doc.$id, name: doc.name, supplier: doc.supplierName }); + } + } + + offset += limit; + if (offset >= res.total) break; + } + + console.log(`Encontrados ${toDelete.length} productos publicados de estos grupos:`); + toDelete.forEach(p => console.log(` - ID: ${p.id} | Nombre: ${p.name} | Proveedor: ${p.supplier}`)); +} + +inspectAll().catch(console.error); diff --git a/scripts/inspect_target_products.js b/scripts/inspect_target_products.js new file mode 100644 index 0000000..0e015b0 --- /dev/null +++ b/scripts/inspect_target_products.js @@ -0,0 +1,24 @@ +const sdk = require('node-appwrite'); +const client = new sdk.Client() + .setEndpoint('https://appwrite.almeira.com.co/v1') + .setProject('69b84df5003bd8957c5c') + .setKey('standard_cce3cdb866ea6433888d40cedd24dc45987372308fdc3dd0697de08648c0e5c7e04b02c3bcee271403fd656b00a5820f04c08b51631284a6e8a3582d8aed2aa33234f3c29cc777e9b162eb54810b789251b0d006a9a3b081ebc2da53b9baa94614b124b3508ad849fd9a4187cc6094f506d4b57f409b69d9562a74df594f3ec3'); +const dbs = new sdk.Databases(client); + +async function inspect() { + const ids = ['snk-1789056531032-5ut8', 'snk-1789057093753-lz2a', 'snk-1789058687729-e6zk']; + for (const id of ids) { + try { + const doc = await dbs.getDocument('sneakers_multistore_db', 'products', id); + console.log('--- PRODUCT:', doc['$id']); + console.log('Name:', doc.name); + console.log('Description:', (doc.description || '').slice(0, 150)); + console.log('SupplierPostId:', doc.supplierPostId); + console.log('SupplierName:', doc.supplierName); + console.log('IsActive:', doc.isActive); + } catch(e) { + console.log('Error', id, e.message); + } + } +} +inspect().catch(console.error); diff --git a/scripts/seed_monitored_groups.js b/scripts/seed_monitored_groups.js index d412cf0..865aa41 100644 --- a/scripts/seed_monitored_groups.js +++ b/scripts/seed_monitored_groups.js @@ -20,9 +20,9 @@ const GROUPS_TO_SEED = [ { jid: '120363420942364759@g.us', name: '💎TEAM EMPORIO💎🔥👑❤️‍🔥🤯', - isMonitored: true, - autoPublish: true, - notes: 'Proveedor Mayorista Team Emporio Sneakers', + isMonitored: false, + autoPublish: false, + notes: 'IGNORADO EXPLICITAMENTE - NO PUBLICAR NI MONITOREAR', }, { jid: '120363027254133375@g.us', @@ -34,9 +34,9 @@ const GROUPS_TO_SEED = [ { jid: '120363400148850031@g.us', name: 'SOMOS BLACK DIAMONDS 💎💎💎', - isMonitored: true, - autoPublish: true, - notes: 'Proveedor Mayorista Black Diamonds Sneakers', + isMonitored: false, + autoPublish: false, + notes: 'IGNORADO EXPLICITAMENTE - NO PUBLICAR NI MONITOREAR', }, { jid: '120363317242539420@g.us',