From f77523519edf18b29f1873efcf728bd38f05a5b8 Mon Sep 17 00:00:00 2001 From: "Carlos (Almeira)" Date: Sun, 21 Jun 2026 12:00:49 -0500 Subject: [PATCH] feat: Add admin month filter and fix cauca post generation --- publish-cauca.js | 204 +++++++++++++++++++++++++++++++++++++++++++++++ src/app/page.tsx | 65 ++++++++++++--- update-dates.js | 29 +++++++ 3 files changed, 289 insertions(+), 9 deletions(-) create mode 100644 publish-cauca.js create mode 100644 update-dates.js diff --git a/publish-cauca.js b/publish-cauca.js new file mode 100644 index 0000000..8423335 --- /dev/null +++ b/publish-cauca.js @@ -0,0 +1,204 @@ +const fs = require('fs'); +const axios = require('axios'); +const { Client, Databases, Storage, ID } = 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; +} + +const EVOLUTION_API_URL = process.env.EVOLUTION_API_URL; +const EVOLUTION_API_KEY = process.env.EVOLUTION_API_KEY; +const EVOLUTION_INSTANCE_NAME = process.env.EVOLUTION_INSTANCE_NAME; +const CHAT_ID = '573134848203-1408546044@g.us'; + +const client = new Client() + .setEndpoint(process.env.NEXT_PUBLIC_APPWRITE_ENDPOINT) + .setProject(process.env.NEXT_PUBLIC_APPWRITE_PROJECT_ID) + .setKey(process.env.APPWRITE_API_KEY); + +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; + +async function getCollectionIds() { + const list = await databases.listCollections(DB_ID); + const map = {}; + list.collections.forEach(c => { map[c.name.toLowerCase()] = c.$id; }); + return map; +} + +async function run() { + console.log("Starting publishing process..."); + try { + const collections = await getCollectionIds(); + const postsCollectionId = collections['posts'] || 'posts'; + const mediaCollectionId = collections['media'] || 'media'; + const pubTypesCollectionId = collections['publication types'] || collections['publication_types'] || 'publication_types'; + + 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; + } + + const allImages = []; + let currentPage = 1; + let totalPages = 1; + + console.log("Fetching messages from Evolution API..."); + do { + const response = await axios.post( + `${EVOLUTION_API_URL}/chat/findMessages/${EVOLUTION_INSTANCE_NAME}?page=${currentPage}&limit=100`, + { where: { "key": { "remoteJid": CHAT_ID } } }, + { headers: { 'apikey': EVOLUTION_API_KEY, 'Content-Type': 'application/json' } } + ); + + 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); + } + } + + if (currentPage > 40) break; + currentPage++; + } while (currentPage <= totalPages); + + console.log(`Total images found in chat history: ${allImages.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; + + for (const month of targetMonths) { + console.log(`\n--- Publishing for Month ${month} ---`); + let publishedCount = 0; + + 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}.`; + + 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 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; + } + + 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); + } + } + if (publishedCount < 8) { + console.log(`Could only publish ${publishedCount} images for Month ${month} (ran out of images).`); + } + } + + console.log("All done!"); + } catch (e) { + console.error("Error:", e.message); + } +} + +run(); diff --git a/src/app/page.tsx b/src/app/page.tsx index b736cbc..42f5423 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -9,7 +9,7 @@ import { decodeHtmlEntities } from '@/lib/utils'; import { Settings } from '@/types/settings'; import { Post, MediaItem } from '@/types/publications'; -async function fetchHomeData(page: number = 1) { +async function fetchHomeData(page: number = 1, isAdmin: boolean = false, adminMonth: number | null = null) { try { const client = await createAdminClient(); @@ -43,13 +43,30 @@ async function fetchHomeData(page: number = 1) { if (!typeId) return { posts: [], cardSettings, totalPages: 1 }; const offset = (page - 1) * postsCount; - const postsRes = await client.databases.listDocuments(APPWRITE_ALMEIRA_CONFIG.databaseId, 'posts', [ + + const queries = [ Query.equal('tipo_publicacion_id', typeId), - Query.equal('estado', 'Publicado'), Query.orderDesc('fecha_publicacion'), Query.limit(postsCount), Query.offset(offset) - ]); + ]; + + if (isAdmin) { + queries.push(Query.equal('estado', ['Publicado', 'borrador'])); + if (adminMonth) { + const currentYear = new Date().getFullYear(); + // First day of the selected month + const startDate = new Date(currentYear, adminMonth - 1, 1).toISOString(); + // First day of the next month + const endDate = new Date(currentYear, adminMonth, 1).toISOString(); + queries.push(Query.greaterThanEqual('fecha_publicacion', startDate)); + queries.push(Query.lessThan('fecha_publicacion', endDate)); + } + } else { + queries.push(Query.equal('estado', 'Publicado')); + } + + const postsRes = await client.databases.listDocuments(APPWRITE_ALMEIRA_CONFIG.databaseId, 'posts', queries); posts = postsRes.documents as unknown as Post[]; totalPages = Math.ceil(postsRes.total / postsCount); @@ -79,12 +96,14 @@ export const dynamic = "force-dynamic"; export default async function Home(props: { searchParams: Promise<{ [key: string]: string | string[] | undefined }> }) { const searchParams = await props.searchParams; const page = Number(searchParams?.page) || 1; - const { posts, cardSettings, totalPages } = await fetchHomeData(page); - const hasSidebar = await hasSidebarContent(); + const adminMonth = searchParams?.month ? Number(searchParams.month) : null; const user = await getLoggedInUser(); const isAdmin = (user as any)?.labels?.includes('admin') || (user as any)?.labels?.includes('editor') || user?.$id === 'admin-emergency'; + const { posts, cardSettings, totalPages } = await fetchHomeData(page, isAdmin, adminMonth); + const hasSidebar = await hasSidebarContent(); + return (
{/* Sci-Fi Hero Section */} @@ -131,13 +150,41 @@ export default async function Home(props: { searchParams: Promise<{ [key: string
-
+
Último Minuto

Feed de Noticias

+ {isAdmin && ( +
+ + Filtro Admin: + + +
+ )}
{posts.length === 0 ? ( @@ -185,7 +232,7 @@ export default async function Home(props: { searchParams: Promise<{ [key: string {totalPages > 1 && (
{page > 1 ? ( - + Anterior ) : ( @@ -195,7 +242,7 @@ export default async function Home(props: { searchParams: Promise<{ [key: string )} Página {page} de {totalPages} {page < totalPages ? ( - + Siguiente ) : ( diff --git a/update-dates.js b/update-dates.js new file mode 100644 index 0000000..b09bd56 --- /dev/null +++ b/update-dates.js @@ -0,0 +1,29 @@ +const { Client, Databases, Query } = require('node-appwrite'); +require('dotenv').config({ path: '.env.local' }); + +const client = new Client() + .setEndpoint(process.env.NEXT_PUBLIC_APPWRITE_ENDPOINT) + .setProject(process.env.NEXT_PUBLIC_APPWRITE_PROJECT_ID) + .setKey(process.env.APPWRITE_API_KEY); + +const databases = new Databases(client); +const DB_ID = process.env.APPWRITE_DATABASE_ID; +const POSTS_COLLECTION = '66624ef80036ca6dce06'; // We should probably fetch it, but I'll dynamically fetch + +async function run() { + console.log("Fetching all posts to delete old script posts..."); + try { + const res = await databases.listDocuments(DB_ID, 'posts', [Query.limit(500)]); + const scriptPosts = res.documents.filter(p => p.slug && p.slug.startsWith('gobernacion-cauca-mes-')); + console.log(`Found ${scriptPosts.length} posts to delete.`); + + for (const post of scriptPosts) { + await databases.deleteDocument(DB_ID, 'posts', post.$id); + console.log(`Deleted ${post.slug}`); + } + console.log("Deletion complete."); + } catch (e) { + console.error("Error during deletion:", e); + } +} +run();