Fix: Corregir UModal en MessageContact usando v-model:open
All checks were successful
build-and-deploy / build-and-deploy (push) Successful in 1m7s

El modal de contacto usaba v-model en lugar de v-model:open según
la documentación de Nuxt UI 4. También se actualizó la estructura
para usar los slots correctos del UModal (#header, #body, #footer).
This commit is contained in:
2025-12-04 09:45:25 -06:00
parent ec40cd6826
commit 9cf4faedec
3 changed files with 1539 additions and 24 deletions

File diff suppressed because it is too large Load Diff

400
docs/scrape-media-docs.mjs Normal file
View File

@@ -0,0 +1,400 @@
#!/usr/bin/env node
/**
* Script para scrapear documentación de Baileys relacionada con Media Messaging
* Genera: baileys-media-messaging.md
*/
const BASE_URL = 'https://baileys.wiki/docs/api';
// Elementos a scrapear para media messaging
const ELEMENTS = {
typeAliases: [
'AnyMediaMessageContent',
'AnyMessageContent',
'AnyRegularMessageContent',
'WAContactMessage',
'WAContactsArrayMessage',
'WALocationMessage',
'WAGenericMediaMessage',
'PollMessageOptions',
'EventMessageOptions',
'MediaType',
'MediaGenerationOptions',
'MiscMessageGenerationOptions',
'MessageGenerationOptions',
'MessageContentGenerationOptions',
'WAMediaUpload',
'DownloadableMessage',
'WAContextInfo',
'MessageWithContextInfo',
],
functions: [
'prepareWAMessageMedia',
'generateWAMessage',
'generateWAMessageContent',
'generateWAMessageFromContent',
'getContentType',
'extensionForMediaMessage',
'downloadMediaMessage',
'downloadContentFromMessage',
'encryptedStream',
'getMediaKeys',
],
variables: [
'MEDIA_KEYS',
'MEDIA_PATH_MAP',
'MEDIA_HKDF_KEY_MAPPING',
],
interfaces: [
'WAUrlInfo',
]
};
// Función para hacer fetch con retry
async function fetchWithRetry(url, retries = 3) {
for (let i = 0; i < retries; i++) {
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
return await response.text();
} catch (error) {
console.error(` Intento ${i + 1}/${retries} fallido: ${error.message}`);
if (i === retries - 1) throw error;
await new Promise(r => setTimeout(r, 1000 * (i + 1)));
}
}
}
// Función para extraer contenido principal de una página
function extractContent(html, elementName) {
// Remover scripts, styles, nav, footer
let content = html
.replace(/<script[^>]*>[\s\S]*?<\/script>/gi, '')
.replace(/<style[^>]*>[\s\S]*?<\/style>/gi, '')
.replace(/<nav[^>]*>[\s\S]*?<\/nav>/gi, '')
.replace(/<footer[^>]*>[\s\S]*?<\/footer>/gi, '')
.replace(/<header[^>]*>[\s\S]*?<\/header>/gi, '');
// Buscar el contenido principal (article o main)
const articleMatch = content.match(/<article[^>]*>([\s\S]*?)<\/article>/i);
const mainMatch = content.match(/<main[^>]*>([\s\S]*?)<\/main>/i);
content = articleMatch ? articleMatch[1] : (mainMatch ? mainMatch[1] : content);
// Convertir HTML a texto/markdown básico
content = content
// Headers
.replace(/<h1[^>]*>([\s\S]*?)<\/h1>/gi, '\n# $1\n')
.replace(/<h2[^>]*>([\s\S]*?)<\/h2>/gi, '\n## $1\n')
.replace(/<h3[^>]*>([\s\S]*?)<\/h3>/gi, '\n### $1\n')
.replace(/<h4[^>]*>([\s\S]*?)<\/h4>/gi, '\n#### $1\n')
// Code blocks
.replace(/<pre[^>]*><code[^>]*>([\s\S]*?)<\/code><\/pre>/gi, '\n```typescript\n$1\n```\n')
.replace(/<code[^>]*>([\s\S]*?)<\/code>/gi, '`$1`')
// Lists
.replace(/<li[^>]*>([\s\S]*?)<\/li>/gi, '- $1\n')
.replace(/<ul[^>]*>/gi, '\n')
.replace(/<\/ul>/gi, '\n')
.replace(/<ol[^>]*>/gi, '\n')
.replace(/<\/ol>/gi, '\n')
// Paragraphs
.replace(/<p[^>]*>([\s\S]*?)<\/p>/gi, '\n$1\n')
// Bold/Italic
.replace(/<strong[^>]*>([\s\S]*?)<\/strong>/gi, '**$1**')
.replace(/<b[^>]*>([\s\S]*?)<\/b>/gi, '**$1**')
.replace(/<em[^>]*>([\s\S]*?)<\/em>/gi, '*$1*')
.replace(/<i[^>]*>([\s\S]*?)<\/i>/gi, '*$1*')
// Links
.replace(/<a[^>]*href="([^"]*)"[^>]*>([\s\S]*?)<\/a>/gi, '[$2]($1)')
// Divs y spans
.replace(/<div[^>]*>/gi, '\n')
.replace(/<\/div>/gi, '\n')
.replace(/<span[^>]*>/gi, '')
.replace(/<\/span>/gi, '')
// Breaks
.replace(/<br\s*\/?>/gi, '\n')
// Remover otros tags HTML
.replace(/<[^>]+>/g, '')
// Decodificar entidades HTML
.replace(/&lt;/g, '<')
.replace(/&gt;/g, '>')
.replace(/&amp;/g, '&')
.replace(/&quot;/g, '"')
.replace(/&#x27;/g, "'")
.replace(/&#39;/g, "'")
.replace(/&nbsp;/g, ' ')
// Limpiar espacios múltiples
.replace(/\n{3,}/g, '\n\n')
.replace(/[ \t]+/g, ' ')
.trim();
return content;
}
// Función para construir URL según el tipo
function buildUrl(type, name) {
const urlMap = {
typeAliases: `${BASE_URL}/type-aliases/${name}`,
functions: `${BASE_URL}/functions/${name}`,
variables: `${BASE_URL}/variables/${name}`,
interfaces: `${BASE_URL}/interfaces/${name}`,
};
return urlMap[type];
}
// Función principal
async function main() {
console.log('🚀 Iniciando scraping de documentación de Baileys para Media Messaging...\n');
const results = {
typeAliases: [],
functions: [],
variables: [],
interfaces: [],
};
// Scrapear cada categoría
for (const [category, elements] of Object.entries(ELEMENTS)) {
console.log(`\n📁 Scrapeando ${category}...`);
for (const element of elements) {
const url = buildUrl(category, element);
console.log(` 📄 ${element}...`);
try {
const html = await fetchWithRetry(url);
const content = extractContent(html, element);
results[category].push({
name: element,
url: url,
content: content,
});
console.log(`${element} - OK`);
// Pequeña pausa para no saturar el servidor
await new Promise(r => setTimeout(r, 500));
} catch (error) {
console.log(`${element} - Error: ${error.message}`);
results[category].push({
name: element,
url: url,
content: `Error al obtener documentación: ${error.message}`,
error: true,
});
}
}
}
// Generar markdown
console.log('\n📝 Generando markdown...');
let markdown = `# Baileys Media Messaging API Documentation
> Documentación auto-generada para el desarrollo de mensajes multimedia en WhatsApp Nucleo
> Fuente: https://baileys.wiki
> Generado: ${new Date().toISOString()}
Esta documentación contiene la API de Baileys relevante para enviar diferentes tipos de mensajes multimedia:
- Imágenes y Videos
- Documentos
- Audio y Notas de Voz
- Contactos
- Ubicación
- Encuestas (Polls)
- Eventos
## Tabla de Contenidos
`;
// Generar tabla de contenidos
const categoryNames = {
interfaces: 'Interfaces',
typeAliases: 'Type Aliases',
functions: 'Funciones',
variables: 'Variables',
};
for (const [category, items] of Object.entries(results)) {
if (items.length > 0) {
markdown += `### ${categoryNames[category]}\n`;
for (const item of items) {
const anchor = `${category.slice(0, -1)}-${item.name.toLowerCase()}`;
markdown += `- [${item.name}](#${anchor})\n`;
}
markdown += '\n';
}
}
// Agregar contenido de cada categoría
for (const [category, items] of Object.entries(results)) {
if (items.length > 0) {
markdown += `\n---\n\n# ${categoryNames[category]}\n\n`;
for (const item of items) {
const typeLabel = category.slice(0, -1).replace(/([A-Z])/g, ' $1').trim();
markdown += `---\n\n## ${typeLabel}: ${item.name}\n\n`;
markdown += `**Fuente:** ${item.url}\n\n`;
if (item.error) {
markdown += `> ⚠️ ${item.content}\n\n`;
} else {
markdown += `${item.content}\n\n`;
}
}
}
}
// Agregar sección de ejemplos de uso
markdown += `
---
# Ejemplos de Uso Comunes
## Enviar Imagen
\`\`\`typescript
import { prepareWAMessageMedia, generateWAMessageFromContent } from '@whiskeysockets/baileys'
// Opción 1: Usando sendMessage directamente
await sock.sendMessage(jid, {
image: { url: './image.jpg' }, // o Buffer
caption: 'Descripción de la imagen',
mimetype: 'image/jpeg'
})
// Opción 2: Con prepareWAMessageMedia
const media = await prepareWAMessageMedia(
{ image: { url: './image.jpg' } },
{ upload: sock.waUploadToServer }
)
\`\`\`
## Enviar Video
\`\`\`typescript
await sock.sendMessage(jid, {
video: { url: './video.mp4' },
caption: 'Mi video',
mimetype: 'video/mp4',
gifPlayback: false // true para GIFs
})
\`\`\`
## Enviar Documento
\`\`\`typescript
await sock.sendMessage(jid, {
document: { url: './document.pdf' },
mimetype: 'application/pdf',
fileName: 'documento.pdf',
caption: 'Documento importante'
})
\`\`\`
## Enviar Audio
\`\`\`typescript
// Audio normal
await sock.sendMessage(jid, {
audio: { url: './audio.mp3' },
mimetype: 'audio/mp4'
})
// Nota de voz (PTT)
await sock.sendMessage(jid, {
audio: { url: './voice.ogg' },
mimetype: 'audio/ogg; codecs=opus',
ptt: true // Push To Talk = nota de voz
})
\`\`\`
## Enviar Contacto
\`\`\`typescript
// Un contacto
await sock.sendMessage(jid, {
contacts: {
displayName: 'Juan Pérez',
contacts: [{
vcard: \`BEGIN:VCARD
VERSION:3.0
FN:Juan Pérez
TEL;type=CELL;waid=1234567890:+1234567890
END:VCARD\`
}]
}
})
// Múltiples contactos
await sock.sendMessage(jid, {
contacts: {
displayName: 'Mis Contactos',
contacts: [
{ vcard: '...' },
{ vcard: '...' }
]
}
})
\`\`\`
## Enviar Ubicación
\`\`\`typescript
await sock.sendMessage(jid, {
location: {
degreesLatitude: 24.121231,
degreesLongitude: 55.1121221
}
})
\`\`\`
## Enviar Encuesta (Poll)
\`\`\`typescript
await sock.sendMessage(jid, {
poll: {
name: '¿Cuál es tu color favorito?',
values: ['Rojo', 'Azul', 'Verde', 'Amarillo'],
selectableCount: 1 // Cantidad de opciones seleccionables
}
})
\`\`\`
## Enviar con Quoted (Respuesta)
\`\`\`typescript
// El 'quoted' debe ser el mensaje original completo
await sock.sendMessage(jid,
{ text: 'Esta es una respuesta' },
{ quoted: originalMessage }
)
// También funciona con media
await sock.sendMessage(jid,
{ image: { url: './image.jpg' }, caption: 'Respuesta con imagen' },
{ quoted: originalMessage }
)
\`\`\`
`;
// Guardar archivo
const fs = await import('fs');
const outputPath = './docs/baileys-media-messaging.md';
fs.writeFileSync(outputPath, markdown);
console.log(`\n✅ Documentación generada exitosamente en: ${outputPath}`);
console.log(` Total elementos: ${Object.values(results).flat().length}`);
console.log(` - Type Aliases: ${results.typeAliases.length}`);
console.log(` - Functions: ${results.functions.length}`);
console.log(` - Variables: ${results.variables.length}`);
console.log(` - Interfaces: ${results.interfaces.length}`);
}
main().catch(console.error);