import { GoogleGenAI, Type } from '@google/genai'; const ai = new GoogleGenAI({ apiKey: process.env.GEMINI_API_KEY || '' }); export interface MediaInput { base64: string; mimeType: string; id?: string; } export interface ProcessedPublication { titulo: string; tipo: 'noticia' | 'evento' | 'aviso'; seo_descripcion: string; contenido: string; fecha_vencimiento: string | null; selected_image_index?: number; image_selection_reason?: string; } /** * Fallback parser in case Gemini API is rate-limited (429) or unavailable */ function fallbackRuleBasedParser(text: string, mediaCount: number): ProcessedPublication { const clean = (text || '').trim(); const lines = clean.split('\n').map(l => l.trim()).filter(Boolean); let rawTitle = lines[0] || 'Nueva Publicación'; rawTitle = rawTitle.replace(/^[#*_\-•]+\s*/, '').replace(/[*_#]/g, '').trim(); if (rawTitle.length > 70) { rawTitle = rawTitle.substring(0, 67) + '...'; } const lower = clean.toLowerCase(); let tipo: 'noticia' | 'evento' | 'aviso' = 'noticia'; if (lower.includes('evento') || lower.includes('invitación') || lower.includes('invitacion') || lower.includes('jornada') || lower.includes('taller') || lower.includes('festival') || lower.includes('feria') || lower.includes('convocatoria')) { tipo = 'evento'; } else if (lower.includes('se vende') || lower.includes('se arrienda') || lower.includes('clasificado') || lower.includes('empleo') || lower.includes('vacante') || lower.includes('servicio')) { tipo = 'aviso'; } const seo = clean.replace(/\n+/g, ' ').substring(0, 150) || rawTitle; // Format content as clean HTML paragraphs const paragraphs = lines.map(l => `

${l}

`).join(''); const contenido = paragraphs || `

${clean || rawTitle}

`; return { titulo: rawTitle, tipo: tipo, seo_descripcion: seo, contenido: contenido, fecha_vencimiento: null, selected_image_index: mediaCount > 0 ? 0 : undefined, image_selection_reason: mediaCount > 0 ? 'Foto destacada principal' : undefined }; } export async function processWhatsAppMessage( text: string, mediaParam?: string | MediaInput[] | MediaInput, mimeTypeParam?: string ): Promise { // Normalize mediaParam into an array of MediaInput let mediaList: MediaInput[] = []; if (Array.isArray(mediaParam)) { mediaList = mediaParam; } else if (typeof mediaParam === 'string' && mimeTypeParam) { mediaList = [{ base64: mediaParam, mimeType: mimeTypeParam }]; } else if (mediaParam && typeof mediaParam === 'object' && 'base64' in mediaParam) { mediaList = [mediaParam as MediaInput]; } if (!process.env.GEMINI_API_KEY) { console.warn('[Gemini] Sin GEMINI_API_KEY configurada, usando parser de respaldo'); return fallbackRuleBasedParser(text, mediaList.length); } const systemInstruction = ` Eres un asistente editorial experto y editor gráfico de Almeira Web. Analiza los mensajes y recursos multimedia recibidos desde WhatsApp para estructurarlos como publicaciones del portal. Debes estructurar el mensaje en base a lo siguiente: 1. titulo: Asigna un título claro, profesional y conciso (máx. 70 caracteres). 2. tipo: Determina la categoría exacta: - "noticia": Noticias generales, política, actualidad, comunicados y reportajes (dimensiones recomendadas de imagen: 640x360 px, relación 16:9). - "evento": Invitaciones a actividades, jornadas, ferias, foros, congresos, festivales o convocatorias con fecha (dimensiones de imagen: 640x640 px, relación 1:1). - "aviso": Clasificados, ofertas de empleo, compra/venta, servicios profesionales, comunicados comunitarios (dimensiones de imagen: 640x640 px, relación 1:1). 3. seo_descripcion: Un resumen conciso de máximo 150 caracteres optimizado para redes y motores de búsqueda. 4. contenido: El texto detallado de la publicación en HTML válido y limpio (usa

, ,