All checks were successful
build-and-deploy / build-and-deploy (push) Successful in 1m8s
El campo quoted debe ir en el tercer parámetro (opciones) de sendMessage, no dentro del objeto content. Esto corrige el envío de respuestas.
236 lines
6.3 KiB
TypeScript
236 lines
6.3 KiB
TypeScript
/**
|
|
* POST /api/messages/:instanceId/:chatId/send-media
|
|
* Send media messages (images, videos, audio, documents)
|
|
*/
|
|
import { prepareWAMessageMedia, type AnyMediaMessageContent } from '@whiskeysockets/baileys'
|
|
import { baileysManager } from '../../../../services/baileys/manager'
|
|
import { query } from '../../../../utils/database'
|
|
|
|
// Max file sizes (in bytes)
|
|
const MAX_SIZES = {
|
|
image: 16 * 1024 * 1024, // 16 MB
|
|
video: 64 * 1024 * 1024, // 64 MB
|
|
audio: 16 * 1024 * 1024, // 16 MB
|
|
document: 100 * 1024 * 1024, // 100 MB
|
|
}
|
|
|
|
// MIME type to media type mapping
|
|
function getMediaType(mimetype: string): 'image' | 'video' | 'audio' | 'document' | null {
|
|
if (mimetype.startsWith('image/')) return 'image'
|
|
if (mimetype.startsWith('video/')) return 'video'
|
|
if (mimetype.startsWith('audio/')) return 'audio'
|
|
// Everything else is a document
|
|
return 'document'
|
|
}
|
|
|
|
export default defineEventHandler(async (event) => {
|
|
const username = getHeader(event, 'x-authentik-username')
|
|
if (!username) {
|
|
throw createError({ statusCode: 401, message: 'Unauthorized' })
|
|
}
|
|
|
|
const instanceId = getRouterParam(event, 'instanceId')
|
|
const chatId = getRouterParam(event, 'chatId')
|
|
|
|
if (!instanceId || !chatId) {
|
|
throw createError({ statusCode: 400, message: 'Missing instanceId or chatId' })
|
|
}
|
|
|
|
// Get chat JID
|
|
const chatResult = await query(
|
|
'SELECT jid FROM chats WHERE id = $1 AND instance_id = $2',
|
|
[chatId, instanceId]
|
|
)
|
|
|
|
if (chatResult.rows.length === 0) {
|
|
throw createError({ statusCode: 404, message: 'Chat not found' })
|
|
}
|
|
|
|
const jid = chatResult.rows[0].jid
|
|
|
|
// Get socket
|
|
const socket = baileysManager.getSocket(instanceId)
|
|
if (!socket) {
|
|
throw createError({ statusCode: 400, message: 'Instance not connected' })
|
|
}
|
|
|
|
// Parse multipart form data
|
|
const formData = await readMultipartFormData(event)
|
|
if (!formData) {
|
|
throw createError({ statusCode: 400, message: 'No form data received' })
|
|
}
|
|
|
|
// Extract fields
|
|
let caption = ''
|
|
let quotedMessageId = ''
|
|
let isPtt = false
|
|
const files: { name: string; data: Buffer; type: string }[] = []
|
|
|
|
for (const item of formData) {
|
|
if (item.name === 'caption' && item.data) {
|
|
caption = item.data.toString()
|
|
} else if (item.name === 'quotedMessageId' && item.data) {
|
|
quotedMessageId = item.data.toString()
|
|
} else if (item.name === 'isPtt' && item.data) {
|
|
isPtt = item.data.toString() === 'true'
|
|
} else if (item.name === 'files' || item.name === 'file') {
|
|
if (item.data && item.type) {
|
|
files.push({
|
|
name: item.filename || 'file',
|
|
data: item.data,
|
|
type: item.type
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
if (files.length === 0) {
|
|
throw createError({ statusCode: 400, message: 'No files provided' })
|
|
}
|
|
|
|
// Get quoted message if provided
|
|
let quotedMessage = null
|
|
if (quotedMessageId) {
|
|
const quotedResult = await query(
|
|
'SELECT raw_message FROM messages WHERE message_id = $1 AND instance_id = $2',
|
|
[quotedMessageId, instanceId]
|
|
)
|
|
if (quotedResult.rows.length > 0) {
|
|
quotedMessage = quotedResult.rows[0].raw_message
|
|
}
|
|
}
|
|
|
|
const sentMessages = []
|
|
|
|
// Send each file
|
|
for (const file of files) {
|
|
const mediaType = getMediaType(file.type)
|
|
|
|
if (!mediaType) {
|
|
console.warn(`[SendMedia] Unknown media type: ${file.type}`)
|
|
continue
|
|
}
|
|
|
|
// Check file size
|
|
const maxSize = MAX_SIZES[mediaType]
|
|
if (file.data.length > maxSize) {
|
|
throw createError({
|
|
statusCode: 400,
|
|
message: `File ${file.name} exceeds maximum size for ${mediaType}`
|
|
})
|
|
}
|
|
|
|
try {
|
|
// Prepare media content
|
|
let content: AnyMediaMessageContent
|
|
|
|
if (mediaType === 'image') {
|
|
content = {
|
|
image: file.data,
|
|
caption: caption || undefined,
|
|
mimetype: file.type as any
|
|
}
|
|
} else if (mediaType === 'video') {
|
|
content = {
|
|
video: file.data,
|
|
caption: caption || undefined,
|
|
mimetype: file.type as any
|
|
}
|
|
} else if (mediaType === 'audio') {
|
|
content = {
|
|
audio: file.data,
|
|
ptt: isPtt,
|
|
mimetype: file.type as any
|
|
}
|
|
} else {
|
|
// Document
|
|
content = {
|
|
document: file.data,
|
|
fileName: file.name,
|
|
caption: caption || undefined,
|
|
mimetype: file.type as any
|
|
}
|
|
}
|
|
|
|
// Build options with quoted message if exists
|
|
const options: any = {}
|
|
if (quotedMessage) {
|
|
options.quoted = quotedMessage
|
|
}
|
|
|
|
// Send message
|
|
console.log(`[SendMedia] Sending ${mediaType} to ${jid}`)
|
|
const result = await socket.sendMessage(jid, content, options)
|
|
|
|
if (result) {
|
|
sentMessages.push({
|
|
messageId: result.key.id,
|
|
type: mediaType,
|
|
filename: file.name
|
|
})
|
|
|
|
// Save to database
|
|
await saveMediaMessage(instanceId, chatId, jid, result, mediaType, caption, file.name)
|
|
}
|
|
|
|
// Only use caption for first file
|
|
caption = ''
|
|
} catch (error) {
|
|
console.error(`[SendMedia] Error sending ${file.name}:`, error)
|
|
throw createError({
|
|
statusCode: 500,
|
|
message: `Error sending ${file.name}`
|
|
})
|
|
}
|
|
}
|
|
|
|
return {
|
|
success: true,
|
|
messages: sentMessages
|
|
}
|
|
})
|
|
|
|
// Helper to save media message to database
|
|
async function saveMediaMessage(
|
|
instanceId: string,
|
|
chatId: string,
|
|
jid: string,
|
|
result: any,
|
|
messageType: string,
|
|
caption: string,
|
|
filename: string
|
|
) {
|
|
const messageId = result.key.id
|
|
const timestamp = new Date()
|
|
|
|
await query(
|
|
`INSERT INTO messages (
|
|
instance_id, chat_id, message_id, from_jid, to_jid,
|
|
from_me, message_type, content, caption, media_filename,
|
|
timestamp, status, raw_message
|
|
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)
|
|
ON CONFLICT (instance_id, message_id) DO NOTHING`,
|
|
[
|
|
instanceId,
|
|
chatId,
|
|
messageId,
|
|
jid, // from_jid will be our JID
|
|
jid, // to_jid
|
|
true, // from_me
|
|
messageType,
|
|
caption || null,
|
|
caption || null,
|
|
filename,
|
|
timestamp,
|
|
'sent',
|
|
JSON.stringify(result)
|
|
]
|
|
)
|
|
|
|
// Update chat last message
|
|
await query(
|
|
`UPDATE chats SET last_message_at = $1, last_message_type = $2 WHERE id = $3`,
|
|
[timestamp, messageType, chatId]
|
|
)
|
|
}
|