From 75feab0ab1c5e96ab0f77533479f419870c0e6cb Mon Sep 17 00:00:00 2001 From: "Carlos (Almeira)" Date: Sun, 21 Jun 2026 12:13:55 -0500 Subject: [PATCH] fix: Rewrite script to intelligently match WhatsApp image texts --- publish-cauca.js | 236 +++++++++++++++++++++-------------------------- 1 file changed, 104 insertions(+), 132 deletions(-) diff --git a/publish-cauca.js b/publish-cauca.js index 8423335..e9e914c 100644 --- a/publish-cauca.js +++ b/publish-cauca.js @@ -1,15 +1,10 @@ const fs = require('fs'); const axios = require('axios'); -const { Client, Databases, Storage, ID } = require('node-appwrite'); +const { Client, Databases, Storage, ID, Query } = require('node-appwrite'); require('dotenv').config({ path: '.env.local' }); -// Let's try to get InputFile if it exists in node-appwrite/file (for older SDKs) let InputFile; -try { - InputFile = require('node-appwrite/file').InputFile; -} catch (e) { - InputFile = null; -} +try { InputFile = require('node-appwrite/file').InputFile; } catch (e) { InputFile = null; } const EVOLUTION_API_URL = process.env.EVOLUTION_API_URL; const EVOLUTION_API_KEY = process.env.EVOLUTION_API_KEY; @@ -23,7 +18,6 @@ const client = new Client() const databases = new Databases(client); const storage = new Storage(client); - const DB_ID = process.env.APPWRITE_DATABASE_ID; const BUCKET_ID = process.env.NEXT_PUBLIC_APPWRITE_MEDIA_BUCKET_ID; @@ -34,6 +28,12 @@ async function getCollectionIds() { return map; } +function extractText(msg) { + if (msg.messageType === 'conversation') return msg.message.conversation; + if (msg.messageType === 'extendedTextMessage') return msg.message.extendedTextMessage?.text; + return null; +} + async function run() { console.log("Starting publishing process..."); try { @@ -44,12 +44,17 @@ async function run() { const pubTypes = await databases.listDocuments(DB_ID, pubTypesCollectionId); const defaultPubTypeId = pubTypes.documents.length > 0 ? pubTypes.documents[0].$id : null; - if (!defaultPubTypeId) { - console.error("No publication types found. Can't create posts."); - return; + + // 1. Delete old posts + console.log("Deleting old script posts..."); + const resOld = await databases.listDocuments(DB_ID, postsCollectionId, [Query.limit(500)]); + const scriptPosts = resOld.documents.filter(p => p.slug && p.slug.startsWith('gobernacion-cauca-mes-')); + for (const p of scriptPosts) { + await databases.deleteDocument(DB_ID, postsCollectionId, p.$id); } - const allImages = []; + // 2. Fetch all messages + const allRecords = []; let currentPage = 1; let totalPages = 1; @@ -63,142 +68,109 @@ async function run() { const messagesObj = response.data.messages || {}; totalPages = messagesObj.pages || 1; - const records = messagesObj.records || []; - - for (const msg of records) { - if (msg.messageType === 'imageMessage') { - allImages.push(msg); - } - } + allRecords.push(...(messagesObj.records || [])); if (currentPage > 40) break; currentPage++; } while (currentPage <= totalPages); - console.log(`Total images found in chat history: ${allImages.length}`); + console.log(`Total messages fetched: ${allRecords.length}`); - // We need 32 images (8 for March, 8 for April, 8 for May, 8 for June) - // If we don't have enough images, we'll just use as many as we have. - // We'll distribute them artificially over the months if the actual timestamps are all from June. - - let targetMonths = [3, 4, 5, 6]; - let imageIndex = 0; + // 3. Process images and find associated text + const targetMonths = [3, 4, 5, 6]; + const monthCounts = { 3:0, 4:0, 5:0, 6:0 }; - for (const month of targetMonths) { - console.log(`\n--- Publishing for Month ${month} ---`); - let publishedCount = 0; + for (let i = 0; i < allRecords.length; i++) { + const msg = allRecords[i]; + if (msg.messageType !== 'imageMessage') continue; + + const ts = msg.messageTimestamp; + const date = new Date(ts * 1000); + const month = date.getMonth() + 1; + + if (!targetMonths.includes(month)) continue; + if (monthCounts[month] >= 8) continue; // max 8 per month + + // Find associated text + let caption = msg.message?.imageMessage?.caption; - while (publishedCount < 8 && imageIndex < allImages.length) { - const msg = allImages[imageIndex]; - imageIndex++; - - try { - // Get base64 - const base64Res = await axios.post( - `${EVOLUTION_API_URL}/chat/getBase64FromMediaMessage/${EVOLUTION_INSTANCE_NAME}`, - { message: msg }, - { headers: { 'apikey': EVOLUTION_API_KEY, 'Content-Type': 'application/json' } } - ); - - const base64Data = base64Res.data.base64; - if (!base64Data) continue; - - const buffer = Buffer.from(base64Data, 'base64'); - const filePath = `/tmp/image-${month}-${publishedCount}.jpeg`; - const filename = `image-${month}-${publishedCount}.jpeg`; - fs.writeFileSync(filePath, buffer); - - // Appwrite File Upload Compatibility - let fileParam; - if (InputFile && InputFile.fromPath) { - fileParam = InputFile.fromPath(filePath, filename); - } else if (InputFile && InputFile.fromBuffer) { - fileParam = InputFile.fromBuffer(buffer, filename); - } else { - // Fallback to node fs read stream (works in some SDKs) or directly passing string path if supported - // Appwrite 1.x Node SDK usually accepts a ReadStream - fileParam = fs.createReadStream(filePath); - } - - const file = await storage.createFile( - BUCKET_ID, - ID.unique(), - fileParam - ); - - let mediaDoc; - try { - mediaDoc = await databases.createDocument( - DB_ID, - mediaCollectionId, - ID.unique(), - { - file_id: file.$id, - bucket_id: BUCKET_ID, - titulo: `Pieza Gráfica Gobernación del Cauca - Mes ${month}`, - tipo: 'imagen' - } - ); - } catch (e) { - console.error("Media error:", e.message); - throw e; - } - - let caption = msg.message?.imageMessage?.caption || `Pieza gráfica de la Gobernación del Cauca correspondiente al mes ${month}.`; + if (!caption) { + // Look backwards and forwards within 10 minutes (600 seconds) + let bestText = null; + for (let j = Math.max(0, i - 10); j < Math.min(allRecords.length, i + 10); j++) { + const nearbyMsg = allRecords[j]; + if (Math.abs(nearbyMsg.messageTimestamp - ts) > 600) continue; - let title = `Boletín Gobernación - Mes ${month} #${publishedCount + 1}`; - if (caption && caption.length >= 5) { - const lines = caption.split('\n').map(l => l.trim()).filter(l => l.length > 5); - if (lines.length > 0) { - title = lines[0]; - if ((title.toUpperCase() === title || title.includes("GOBERNACIÓN")) && lines.length > 1) { - title = lines[1]; - } - title = title.replace(/^[\p{Emoji}\s]+/gu, ''); - if (title.length > 100) title = title.substring(0, 100) + '...'; - } + const text = extractText(nearbyMsg); + if (text && text.length > 20 && !text.toLowerCase().includes("comparte en tus historias")) { + bestText = text; + // if this text is very close (older), break + if (j > i) break; } + } + if (bestText) caption = bestText; + } - const slug = `gobernacion-cauca-mes-${month}-pub-${publishedCount}-${Date.now()}`; - - const realDate = new Date(msg.messageTimestamp * 1000).toISOString(); - const cleanCaption = caption || `Publicación gráfica de la Gobernación del Cauca para el mes de ${month}.`; - - try { - await databases.createDocument( - DB_ID, - postsCollectionId, - ID.unique(), - { - titulo: title, - slug: slug, - tipo_publicacion_id: defaultPubTypeId, - estado: 'borrador', // Cambiado a borrador para revisión - fecha_publicacion: realDate, // Fecha real del mensaje - contenido: `

${cleanCaption.replace(/\n/g, '
')}

`, // Formato HTML básico - imagen_destacada_id: mediaDoc.$id - } - ); - } catch(e) { - console.error("Post error:", e.message); - throw e; + let cleanCaption = caption || `Publicación gráfica de la Gobernación del Cauca para el mes de ${month}.`; + let title = `Boletín Gobernación - Mes ${month} #${monthCounts[month] + 1}`; + + if (cleanCaption.length > 20) { + const lines = cleanCaption.split('\n').map(l => l.trim()).filter(l => l.length > 5); + if (lines.length > 0) { + title = lines[0]; + if ((title.toUpperCase() === title || title.includes("GOBERNACIÓN") || title.includes("Boletín")) && lines.length > 1) { + title = lines[1]; } - - console.log(`[Success] Published post ${publishedCount + 1}/8 for Month ${month}`); - publishedCount++; - } catch (err) { - console.error(`Failed to publish image for month ${month}:`, err.message); + title = title.replace(/^[\p{Emoji}\s*_*]+/gu, ''); // Remove emojis, stars, underscores + if (title.length > 100) title = title.substring(0, 100) + '...'; } } - if (publishedCount < 8) { - console.log(`Could only publish ${publishedCount} images for Month ${month} (ran out of images).`); + + try { + // Get base64 + const base64Res = await axios.post( + `${EVOLUTION_API_URL}/chat/getBase64FromMediaMessage/${EVOLUTION_INSTANCE_NAME}`, + { message: msg }, + { headers: { 'apikey': EVOLUTION_API_KEY, 'Content-Type': 'application/json' } } + ); + + const base64Data = base64Res.data.base64; + if (!base64Data) continue; + + const buffer = Buffer.from(base64Data, 'base64'); + const filePath = `/tmp/image-${month}-${monthCounts[month]}.jpeg`; + fs.writeFileSync(filePath, buffer); + + let fileParam; + if (InputFile && InputFile.fromPath) fileParam = InputFile.fromPath(filePath, `image.jpeg`); + else if (InputFile && InputFile.fromBuffer) fileParam = InputFile.fromBuffer(buffer, `image.jpeg`); + else fileParam = fs.createReadStream(filePath); + + const file = await storage.createFile(BUCKET_ID, ID.unique(), fileParam); + + const mediaDoc = await databases.createDocument(DB_ID, mediaCollectionId, ID.unique(), { + file_id: file.$id, bucket_id: BUCKET_ID, + titulo: title, tipo: 'imagen' + }); + + const slug = `gobernacion-cauca-mes-${month}-pub-${monthCounts[month]}-${Date.now()}`; + await databases.createDocument(DB_ID, postsCollectionId, ID.unique(), { + titulo: title, + slug: slug, + tipo_publicacion_id: defaultPubTypeId, + estado: 'borrador', + fecha_publicacion: date.toISOString(), + contenido: `

${cleanCaption.replace(/\n/g, '
')}

`, + imagen_destacada_id: mediaDoc.$id + }); + + console.log(`[Success] Published post ${monthCounts[month] + 1}/8 for Month ${month}: ${title}`); + monthCounts[month]++; + } catch (err) { + console.error(`Failed to publish image for month ${month}:`, err.message); } } - console.log("All done!"); - } catch (e) { - console.error("Error:", e.message); - } + } catch (e) { console.error("Error:", e.message); } } - run();