596 lines
23 KiB
TypeScript
596 lines
23 KiB
TypeScript
import { NextResponse } from 'next/server';
|
|
import { dataService } from '@/lib/data-service';
|
|
import { parseSupplierMessage, detectCategory } from '@/lib/gemini-parser';
|
|
import { performMarketIntelligenceSearch } from '@/lib/market-intelligence';
|
|
import { getProductSocialProof } from '@/lib/social-proof';
|
|
import { generateAttractiveProductCopy } from '@/lib/attractive-copy';
|
|
import { sanitizePublicationTitle, sanitizePublicationText } from '@/lib/sanitizer';
|
|
import {
|
|
getSupplierPostsFromAppwrite,
|
|
saveSupplierPostToAppwrite,
|
|
updateSupplierPostStatusInAppwrite,
|
|
saveProductToAppwrite,
|
|
getProductsFromAppwrite,
|
|
} from '@/lib/server-appwrite';
|
|
import { SupplierPost, MonitoredWhatsAppGroup } from '@/types';
|
|
|
|
const EVOLUTION_API_URL = process.env.EVOLUTION_API_URL || 'https://evolution.almeira.com.co';
|
|
const EVOLUTION_API_KEY = process.env.EVOLUTION_FEED_API_KEY || 'EE49029EEB85-4568-8F81-C9FDFC6E2943';
|
|
const EVOLUTION_INSTANCE_NAME = process.env.EVOLUTION_FEED_INSTANCE_NAME || 'AutomataAlmeira';
|
|
|
|
export async function GET(request: Request) {
|
|
try {
|
|
const { searchParams } = new URL(request.url);
|
|
const fetchLive = searchParams.get('fetchLive') !== 'false';
|
|
const limit = parseInt(searchParams.get('limit') || '3000', 10);
|
|
const maxPages = parseInt(searchParams.get('pages') || '30', 10);
|
|
const filterJid = searchParams.get('groupJid');
|
|
const statusFilter = (searchParams.get('status') || 'pending').toLowerCase();
|
|
|
|
// 1. Obtener posts y productos existentes en Appwrite para control estricto de IDs de mensajes de WhatsApp
|
|
const [savedPosts, existingProducts] = await Promise.all([
|
|
getSupplierPostsFromAppwrite(),
|
|
getProductsFromAppwrite(),
|
|
]);
|
|
|
|
// Índices de WhatsApp message IDs ya procesados
|
|
const publishedMsgIds = new Set<string>();
|
|
const discardedMsgIds = new Set<string>();
|
|
const appwritePostMap = new Map<string, SupplierPost>();
|
|
|
|
savedPosts.forEach((p) => {
|
|
appwritePostMap.set(p.id, p);
|
|
const msgId = p.whatsappMessageId || p.id.replace('post-', '');
|
|
if (msgId) {
|
|
if (p.status === 'published' || p.status === 'published_store' || p.status === 'published_facebook') {
|
|
publishedMsgIds.add(msgId);
|
|
publishedMsgIds.add(`post-${msgId}`);
|
|
publishedMsgIds.add(p.id);
|
|
} else if (p.status === 'discarded' || p.status === 'ignored') {
|
|
discardedMsgIds.add(msgId);
|
|
discardedMsgIds.add(`post-${msgId}`);
|
|
discardedMsgIds.add(p.id);
|
|
}
|
|
}
|
|
});
|
|
|
|
existingProducts.forEach((prod) => {
|
|
if (prod.whatsappMessageId) {
|
|
publishedMsgIds.add(prod.whatsappMessageId);
|
|
publishedMsgIds.add(`post-${prod.whatsappMessageId}`);
|
|
}
|
|
});
|
|
|
|
// 2. Obtener grupos locales y descubrir grupos en vivo desde Evolution API
|
|
const localGroups: MonitoredWhatsAppGroup[] = dataService.getMonitoredGroups();
|
|
const groupsMap = new Map<string, string>();
|
|
localGroups.forEach((g) => groupsMap.set(g.jid, g.name));
|
|
|
|
let liveGroups: any[] = [];
|
|
try {
|
|
const chatsRes = 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 chatsRes.json();
|
|
if (Array.isArray(allChats)) {
|
|
liveGroups = allChats.filter(
|
|
(c: any) => (c.remoteJid && c.remoteJid.includes('@g.us')) || (c.id && c.id.includes('@g.us'))
|
|
);
|
|
liveGroups.forEach((g: any) => {
|
|
const jid = g.remoteJid || g.id || g.jid;
|
|
const name = g.name || g.pushName || g.subject || 'Grupo WhatsApp';
|
|
if (jid) groupsMap.set(jid, name);
|
|
});
|
|
}
|
|
} catch (e) {
|
|
console.warn('Error resolviendo chats de WhatsApp:', e);
|
|
}
|
|
|
|
// 3. Determinar JIDs objetivo a consultar (excluyendo grupos de los que salimos o inactivos)
|
|
const leftJids = new Set(
|
|
localGroups.filter((g) => g.hasLeft || g.isStillMember === false || !g.isMonitored).map((g) => g.jid)
|
|
);
|
|
|
|
let targetJids: string[] = [];
|
|
if (filterJid && filterJid !== 'all') {
|
|
targetJids = [filterJid];
|
|
} else {
|
|
const monitored = localGroups.filter((g) => g.isMonitored && !g.hasLeft && g.isStillMember !== false).map((g) => g.jid);
|
|
if (monitored.length > 0) {
|
|
targetJids = monitored;
|
|
} else if (liveGroups.length > 0) {
|
|
targetJids = liveGroups
|
|
.map((g: any) => g.remoteJid || g.id)
|
|
.filter((jid: string) => !leftJids.has(jid));
|
|
} else {
|
|
targetJids = Array.from(groupsMap.keys()).filter((jid: string) => !leftJids.has(jid));
|
|
}
|
|
}
|
|
|
|
if (!fetchLive || targetJids.length === 0) {
|
|
const filteredSaved = savedPosts
|
|
.filter((p) => {
|
|
// Si el mensaje es de un grupo del que salimos y estamos en pendientes, no mostrarlo
|
|
if (p.channelJid && leftJids.has(p.channelJid) && statusFilter === 'pending') return false;
|
|
|
|
if (statusFilter === 'pending') return p.status === 'pending_review';
|
|
if (statusFilter === 'published') return p.status === 'published' || p.status === 'published_store';
|
|
if (statusFilter === 'discarded') return p.status === 'discarded';
|
|
return true;
|
|
})
|
|
.map((p) => {
|
|
// Si tiene thumbnail de baja resolución pero cuenta con ID de WhatsApp, suministrar la URL en Alta Resolución Full HD
|
|
if (p.whatsappMessageId && Array.isArray(p.rawImages) && (p.rawImages.length === 0 || p.rawImages[0].startsWith('data:image'))) {
|
|
return {
|
|
...p,
|
|
rawImages: [`/api/whatsapp-media?id=${encodeURIComponent(p.whatsappMessageId)}&remoteJid=${encodeURIComponent(p.channelJid || '')}`],
|
|
};
|
|
}
|
|
return p;
|
|
});
|
|
|
|
return NextResponse.json({
|
|
success: true,
|
|
isLive: false,
|
|
totalFound: filteredSaved.length,
|
|
counts: {
|
|
pending: savedPosts.filter((p) => p.status === 'pending_review').length,
|
|
published: publishedMsgIds.size,
|
|
discarded: discardedMsgIds.size,
|
|
all: savedPosts.length,
|
|
},
|
|
posts: filteredSaved,
|
|
groups: Array.from(groupsMap.entries()).map(([jid, name]) => ({ jid, name })),
|
|
});
|
|
}
|
|
|
|
// 4. Consultar mensajes de los grupos objetivo recorriendo todas las páginas en paralelo
|
|
const groupFetchPromises = targetJids.map(async (jid) => {
|
|
const allGroupRecords: any[] = [];
|
|
const pagesToFetch = Array.from({ length: maxPages }, (_, i) => i + 1);
|
|
|
|
// Lotes concurrentes de 10 páginas para máxima velocidad y profundidad
|
|
const BATCH_SIZE = 10;
|
|
for (let i = 0; i < pagesToFetch.length; i += BATCH_SIZE) {
|
|
const batch = pagesToFetch.slice(i, i + BATCH_SIZE);
|
|
const batchResults = await Promise.allSettled(
|
|
batch.map(async (page) => {
|
|
const msgRes = await fetch(
|
|
`${EVOLUTION_API_URL}/chat/findMessages/${EVOLUTION_INSTANCE_NAME}?page=${page}&limit=50`,
|
|
{
|
|
method: 'POST',
|
|
headers: {
|
|
apikey: EVOLUTION_API_KEY,
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify({ where: { key: { remoteJid: jid } } }),
|
|
}
|
|
);
|
|
const msgData = await msgRes.json();
|
|
return msgData.messages?.records || msgData.records || (Array.isArray(msgData) ? msgData : []);
|
|
})
|
|
);
|
|
|
|
let hitEnd = false;
|
|
for (const res of batchResults) {
|
|
if (res.status === 'fulfilled' && Array.isArray(res.value)) {
|
|
allGroupRecords.push(...res.value);
|
|
if (res.value.length === 0) {
|
|
hitEnd = true;
|
|
}
|
|
}
|
|
}
|
|
if (hitEnd) break;
|
|
}
|
|
return allGroupRecords;
|
|
});
|
|
|
|
const results = await Promise.allSettled(groupFetchPromises);
|
|
const allRecords: any[] = [];
|
|
for (const res of results) {
|
|
if (res.status === 'fulfilled' && Array.isArray(res.value)) {
|
|
allRecords.push(...res.value);
|
|
}
|
|
}
|
|
|
|
// Ordenar cronológicamente (más recientes primero)
|
|
allRecords.sort((a, b) => (b.messageTimestamp || 0) - (a.messageTimestamp || 0));
|
|
|
|
// 5. Procesar mensajes con control estricto de identificador de WhatsApp
|
|
const processedPosts: SupplierPost[] = [];
|
|
const seenMessageIds = new Set<string>();
|
|
|
|
for (const record of allRecords) {
|
|
const remoteJid = record.key?.remoteJid || '';
|
|
const rawMsgId = (record.key?.id || '').trim();
|
|
const cleanMsgId = rawMsgId.replace(/[^a-zA-Z0-9_-]/g, '').slice(0, 36);
|
|
const postId = `post-${cleanMsgId || Date.now()}`;
|
|
|
|
if (seenMessageIds.has(postId) || (rawMsgId && seenMessageIds.has(rawMsgId))) {
|
|
continue;
|
|
}
|
|
seenMessageIds.add(postId);
|
|
if (rawMsgId) seenMessageIds.add(rawMsgId);
|
|
|
|
const text =
|
|
record.message?.conversation ||
|
|
record.message?.extendedTextMessage?.text ||
|
|
record.message?.imageMessage?.caption ||
|
|
record.message?.videoMessage?.caption ||
|
|
'';
|
|
|
|
const groupName = groupsMap.get(remoteJid) || record.pushName || 'Grupo Mayorista';
|
|
const hasImage = Boolean(record.message?.imageMessage);
|
|
|
|
if (!text && !hasImage) continue;
|
|
|
|
const existingInAppwrite =
|
|
appwritePostMap.get(postId) ||
|
|
savedPosts.find(
|
|
(p) =>
|
|
p.id === postId ||
|
|
p.whatsappMessageId === rawMsgId ||
|
|
p.whatsappMessageId === cleanMsgId ||
|
|
(p.rawMessage === text && (p as any).channelJid === remoteJid)
|
|
);
|
|
|
|
const isPublished =
|
|
existingInAppwrite?.status === 'published' ||
|
|
existingInAppwrite?.status === 'published_store' ||
|
|
existingInAppwrite?.status === 'published_facebook' ||
|
|
publishedMsgIds.has(rawMsgId) ||
|
|
publishedMsgIds.has(cleanMsgId) ||
|
|
publishedMsgIds.has(postId);
|
|
|
|
const isDiscarded =
|
|
existingInAppwrite?.status === 'discarded' ||
|
|
existingInAppwrite?.status === 'ignored' ||
|
|
discardedMsgIds.has(rawMsgId) ||
|
|
discardedMsgIds.has(cleanMsgId) ||
|
|
discardedMsgIds.has(postId);
|
|
|
|
const effectiveStatus: SupplierPost['status'] = isPublished
|
|
? 'published'
|
|
: isDiscarded
|
|
? 'discarded'
|
|
: 'pending_review';
|
|
|
|
if (statusFilter === 'pending') {
|
|
if (isPublished || isDiscarded) continue;
|
|
} else if (statusFilter === 'published') {
|
|
if (!isPublished) continue;
|
|
} else if (statusFilter === 'discarded') {
|
|
if (!isDiscarded) continue;
|
|
}
|
|
|
|
let imageUrls: string[] = [];
|
|
const imgMsg = record.message?.imageMessage;
|
|
|
|
if (imgMsg) {
|
|
if (rawMsgId) {
|
|
// URL de streaming en Alta Resolución (Full HD) desde Evolution API
|
|
imageUrls.push(
|
|
`/api/whatsapp-media?id=${encodeURIComponent(rawMsgId)}&remoteJid=${encodeURIComponent(remoteJid)}`
|
|
);
|
|
} else if (imgMsg.jpegThumbnail) {
|
|
imageUrls.push(`data:image/jpeg;base64,${imgMsg.jpegThumbnail}`);
|
|
} else if (imgMsg.url) {
|
|
imageUrls.push(imgMsg.url);
|
|
}
|
|
}
|
|
|
|
if (imageUrls.length === 0) {
|
|
imageUrls.push(
|
|
'https://images.unsplash.com/photo-1595950653106-6c9ebd614d3a?auto=format&fit=crop&w=800&q=80'
|
|
);
|
|
}
|
|
|
|
let extracted = existingInAppwrite?.extractedProduct;
|
|
if (!extracted) {
|
|
const cleanInputText = text || '';
|
|
extracted = await parseSupplierMessage(cleanInputText, undefined, [groupName]);
|
|
} else if (extracted) {
|
|
if (extracted.name) {
|
|
extracted.name = sanitizePublicationTitle(extracted.name, [groupName], extracted.brand);
|
|
}
|
|
if (!extracted.category || extracted.category === 'Calzado / Sneakers' || extracted.category === 'Sneakers / Colección') {
|
|
extracted.category = detectCategory(`${extracted.name || ''} ${text}`).category;
|
|
}
|
|
}
|
|
|
|
const timestamp = record.messageTimestamp
|
|
? new Date(record.messageTimestamp * 1000).toLocaleString('es-CO', {
|
|
hour: '2-digit',
|
|
minute: '2-digit',
|
|
day: '2-digit',
|
|
month: 'short',
|
|
})
|
|
: 'Reciente';
|
|
|
|
const post: SupplierPost = {
|
|
id: postId,
|
|
whatsappMessageId: rawMsgId || cleanMsgId,
|
|
channelName: groupName,
|
|
channelJid: remoteJid,
|
|
rawMessage: text || `[Foto de Calzado enviada en ${groupName}]`,
|
|
rawImages: imageUrls,
|
|
extractedProduct: extracted,
|
|
status: effectiveStatus,
|
|
receivedAt: timestamp,
|
|
supplierName: groupName,
|
|
supplierPhone: record.key?.participant?.replace(/\D/g, '') || '573167455877',
|
|
isRead: Boolean(record.status === 'READ' || record.key?.fromMe),
|
|
};
|
|
|
|
processedPosts.push(post);
|
|
saveSupplierPostToAppwrite(post).catch(() => {});
|
|
|
|
if (processedPosts.length >= limit) break;
|
|
}
|
|
|
|
return NextResponse.json({
|
|
success: true,
|
|
isLive: true,
|
|
statusFilter,
|
|
totalFound: processedPosts.length,
|
|
counts: {
|
|
pending: processedPosts.filter((p) => p.status === 'pending_review').length,
|
|
published: publishedMsgIds.size,
|
|
discarded: discardedMsgIds.size,
|
|
all: processedPosts.length + publishedMsgIds.size + discardedMsgIds.size,
|
|
},
|
|
posts: processedPosts,
|
|
groups: Array.from(groupsMap.entries()).map(([jid, name]) => ({ jid, name })),
|
|
});
|
|
} catch (error: any) {
|
|
console.error('Error en /api/whatsapp-feed:', error);
|
|
return NextResponse.json({ success: false, error: error.message }, { status: 500 });
|
|
}
|
|
}
|
|
|
|
export async function POST(request: Request) {
|
|
try {
|
|
const body = await request.json();
|
|
const {
|
|
action,
|
|
post,
|
|
posts,
|
|
customRetailPrice,
|
|
customName,
|
|
customBrand,
|
|
customColorName,
|
|
customSizes,
|
|
customCategory,
|
|
marketIntelligence,
|
|
} = body;
|
|
|
|
if (action === 'publish' && post) {
|
|
const retailPrice =
|
|
customRetailPrice ||
|
|
post.extractedProduct?.suggestedRetailPrice ||
|
|
(post.extractedProduct?.supplierPrice ? post.extractedProduct.supplierPrice + 35000 : 125000);
|
|
|
|
const rawProdName = customName || post.extractedProduct?.name || 'Artículo Urbano';
|
|
const prodBrand = customBrand || post.extractedProduct?.brand || 'Urbano / Variado';
|
|
const forbiddenContext = [post.channelName, post.supplierName];
|
|
const prodName = sanitizePublicationTitle(rawProdName, forbiddenContext, prodBrand);
|
|
const prodCategory = customCategory || post.extractedProduct?.category || 'Zapatillas';
|
|
const proof = getProductSocialProof({ name: prodName, brand: prodBrand });
|
|
|
|
const prodDescription = generateAttractiveProductCopy({
|
|
name: prodName,
|
|
brand: prodBrand,
|
|
category: prodCategory,
|
|
retailPrice,
|
|
supplierPrice: post.extractedProduct?.supplierPrice || 75000,
|
|
forbiddenTerms: forbiddenContext,
|
|
});
|
|
|
|
const whatsappMsgId = post.whatsappMessageId || post.id.replace('post-', '');
|
|
|
|
const newProduct = {
|
|
id: `snk-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`,
|
|
name: prodName,
|
|
modelName: prodName,
|
|
colorName: customColorName || post.extractedProduct?.colorName,
|
|
brand: prodBrand,
|
|
supplierPrice: post.extractedProduct?.supplierPrice || 75000,
|
|
retailPrice: retailPrice,
|
|
originalPrice: Math.round(retailPrice * 1.35),
|
|
marketPriceCOP: post.extractedProduct?.marketPriceCOP || marketIntelligence?.marketPriceCOP,
|
|
marketPriceSource: post.extractedProduct?.marketPriceSource || marketIntelligence?.marketPriceSource,
|
|
marketPriceSourceUrl: post.extractedProduct?.marketPriceSourceUrl || marketIntelligence?.marketPriceSourceUrl,
|
|
marketComparisonSummary: post.extractedProduct?.marketComparisonSummary || marketIntelligence?.marketComparisonSummary,
|
|
sizes: customSizes || post.extractedProduct?.sizes || ['37', '38', '39', '40', '41', '42', '43'],
|
|
images: Array.isArray(post.rawImages) && post.rawImages.length > 0
|
|
? post.rawImages.map((img: string) => {
|
|
if (img.startsWith('data:image') && whatsappMsgId) {
|
|
return `/api/whatsapp-media?id=${encodeURIComponent(whatsappMsgId)}&remoteJid=${encodeURIComponent(post.channelJid || '')}`;
|
|
}
|
|
return img;
|
|
})
|
|
: ['https://images.unsplash.com/photo-1595950653106-6c9ebd614d3a?auto=format&fit=crop&w=800&q=80'],
|
|
description: prodDescription,
|
|
category: prodCategory,
|
|
inStock: true,
|
|
isActive: true,
|
|
rating: proof.rating,
|
|
reviewsCount: proof.reviewsCount,
|
|
salesCount: proof.salesCount,
|
|
originBadge: proof.badge,
|
|
supplierName: post.supplierName || post.channelName || 'Proveedor Mayorista',
|
|
supplierPhone: post.supplierPhone || '573167455877',
|
|
whatsappMessageId: whatsappMsgId,
|
|
};
|
|
|
|
const added = dataService.addProduct(newProduct as any);
|
|
await saveProductToAppwrite(added);
|
|
await updateSupplierPostStatusInAppwrite(post.id, 'published');
|
|
dataService.updateSupplierPostStatus(post.id, 'published');
|
|
|
|
return NextResponse.json({
|
|
success: true,
|
|
message: `¡${prodName} publicado!`,
|
|
product: added,
|
|
});
|
|
}
|
|
|
|
if (action === 'bulk-publish' && Array.isArray(posts)) {
|
|
const createdProducts = [];
|
|
for (const p of posts) {
|
|
const retailPrice =
|
|
p.extractedProduct?.suggestedRetailPrice ||
|
|
(p.extractedProduct?.supplierPrice ? p.extractedProduct.supplierPrice + 35000 : 125000);
|
|
|
|
const rawProdName = p.extractedProduct?.name || 'Artículo Urbano';
|
|
const prodBrand = p.extractedProduct?.brand || 'Urbano / Variado';
|
|
const forbiddenContext = [p.channelName, p.supplierName];
|
|
const prodName = sanitizePublicationTitle(rawProdName, forbiddenContext, prodBrand);
|
|
const prodCategory = p.extractedProduct?.category || 'Zapatillas';
|
|
const proof = getProductSocialProof({ name: prodName, brand: prodBrand });
|
|
|
|
const prodDescription = generateAttractiveProductCopy({
|
|
name: prodName,
|
|
brand: prodBrand,
|
|
category: prodCategory,
|
|
retailPrice,
|
|
supplierPrice: p.extractedProduct?.supplierPrice || 75000,
|
|
forbiddenTerms: forbiddenContext,
|
|
});
|
|
|
|
const whatsappMsgId = p.whatsappMessageId || p.id.replace('post-', '');
|
|
|
|
const newProd = {
|
|
id: `snk-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`,
|
|
name: prodName,
|
|
modelName: prodName,
|
|
brand: prodBrand,
|
|
supplierPrice: p.extractedProduct?.supplierPrice || 75000,
|
|
retailPrice,
|
|
originalPrice: Math.round(retailPrice * 1.35),
|
|
marketPriceCOP: p.extractedProduct?.marketPriceCOP,
|
|
marketPriceSource: p.extractedProduct?.marketPriceSource,
|
|
marketPriceSourceUrl: p.extractedProduct?.marketPriceSourceUrl,
|
|
marketComparisonSummary: p.extractedProduct?.marketComparisonSummary,
|
|
sizes: p.extractedProduct?.sizes || ['37', '38', '39', '40', '41', '42', '43'],
|
|
images: Array.isArray(p.rawImages) && p.rawImages.length > 0
|
|
? p.rawImages.map((img: string) => {
|
|
if (img.startsWith('data:image') && whatsappMsgId) {
|
|
return `/api/whatsapp-media?id=${encodeURIComponent(whatsappMsgId)}&remoteJid=${encodeURIComponent(p.channelJid || '')}`;
|
|
}
|
|
return img;
|
|
})
|
|
: ['https://images.unsplash.com/photo-1595950653106-6c9ebd614d3a?auto=format&fit=crop&w=800&q=80'],
|
|
description: prodDescription,
|
|
category: prodCategory,
|
|
inStock: true,
|
|
isActive: true,
|
|
rating: proof.rating,
|
|
reviewsCount: proof.reviewsCount,
|
|
salesCount: proof.salesCount,
|
|
originBadge: proof.badge,
|
|
supplierName: p.supplierName || p.channelName || 'Proveedor Mayorista',
|
|
supplierPhone: p.supplierPhone || '573167455877',
|
|
whatsappMessageId: whatsappMsgId,
|
|
};
|
|
|
|
const added = dataService.addProduct(newProd as any);
|
|
await saveProductToAppwrite(added);
|
|
await updateSupplierPostStatusInAppwrite(p.id, 'published');
|
|
dataService.updateSupplierPostStatus(p.id, 'published');
|
|
createdProducts.push(added);
|
|
}
|
|
|
|
return NextResponse.json({
|
|
success: true,
|
|
message: `¡Se publicaron ${createdProducts.length} productos!`,
|
|
products: createdProducts,
|
|
});
|
|
}
|
|
|
|
if (action === 'discard' && post?.id) {
|
|
await updateSupplierPostStatusInAppwrite(post.id, 'discarded');
|
|
dataService.updateSupplierPostStatus(post.id, 'discarded');
|
|
return NextResponse.json({
|
|
success: true,
|
|
message: `Mensaje [${post.whatsappMessageId || post.id}] descartado y archivado.`,
|
|
});
|
|
}
|
|
|
|
if (action === 'bulk-discard' && Array.isArray(posts)) {
|
|
const BATCH_SIZE = 10;
|
|
for (let i = 0; i < posts.length; i += BATCH_SIZE) {
|
|
const batch = posts.slice(i, i + BATCH_SIZE);
|
|
await Promise.all(
|
|
batch.map(async (p) => {
|
|
await updateSupplierPostStatusInAppwrite(p.id, 'discarded');
|
|
dataService.updateSupplierPostStatus(p.id, 'discarded');
|
|
})
|
|
);
|
|
}
|
|
return NextResponse.json({
|
|
success: true,
|
|
message: `¡${posts.length} mensajes descartados y archivados con éxito!`,
|
|
});
|
|
}
|
|
|
|
if (action === 'discard-group' && (body.groupJid || body.groupName)) {
|
|
const { groupJid, groupName } = body;
|
|
const allSaved = await getSupplierPostsFromAppwrite();
|
|
const matchingPosts = allSaved.filter(
|
|
(p: SupplierPost) =>
|
|
(groupJid && p.channelJid === groupJid) ||
|
|
(groupName && (p.channelName === groupName || p.supplierName === groupName))
|
|
);
|
|
|
|
const BATCH_SIZE = 10;
|
|
for (let i = 0; i < matchingPosts.length; i += BATCH_SIZE) {
|
|
const batch = matchingPosts.slice(i, i + BATCH_SIZE);
|
|
await Promise.all(
|
|
batch.map(async (p: SupplierPost) => {
|
|
await updateSupplierPostStatusInAppwrite(p.id, 'discarded');
|
|
dataService.updateSupplierPostStatus(p.id, 'discarded');
|
|
})
|
|
);
|
|
}
|
|
|
|
if (groupJid) {
|
|
dataService.toggleGroupMonitoring(groupJid, false);
|
|
}
|
|
|
|
return NextResponse.json({
|
|
success: true,
|
|
message: `¡Se han descartado y archivado ${matchingPosts.length} mensajes del grupo!`,
|
|
discardedCount: matchingPosts.length,
|
|
});
|
|
}
|
|
|
|
if (action === 'market-research' && post) {
|
|
const img = post.rawImages && post.rawImages[0];
|
|
const res = await performMarketIntelligenceSearch(img, post.rawMessage, post.extractedProduct?.supplierPrice);
|
|
post.extractedProduct = {
|
|
...post.extractedProduct,
|
|
...res,
|
|
};
|
|
post.marketResearchDone = true;
|
|
await saveSupplierPostToAppwrite(post);
|
|
|
|
return NextResponse.json({
|
|
success: true,
|
|
message: '¡Búsqueda visual e inteligencia de precios completada con éxito!',
|
|
extractedProduct: post.extractedProduct,
|
|
});
|
|
}
|
|
|
|
return NextResponse.json({ success: false, error: 'Acción no válida' }, { status: 400 });
|
|
} catch (err: any) {
|
|
console.error('Error en POST /api/whatsapp-feed:', err);
|
|
return NextResponse.json({ success: false, error: err.message }, { status: 500 });
|
|
}
|
|
}
|