up
This commit is contained in:
+113
-2
@@ -45,6 +45,55 @@ if ($providedUrl) {
|
|||||||
if (!empty($extraParts)) {
|
if (!empty($extraParts)) {
|
||||||
$mediaUrl .= (strpos($mediaUrl, '?') === false ? '?' : '&') . implode('&', $extraParts);
|
$mediaUrl .= (strpos($mediaUrl, '?') === false ? '?' : '&') . implode('&', $extraParts);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Intentar detectar un media id embebido en la URL (mid=... o URL hacia graph.facebook.com/v.../{id})
|
||||||
|
$resolvedFromGraph = false;
|
||||||
|
$mid = null;
|
||||||
|
|
||||||
|
// Buscar mid en query string (lookaside or other URLs)
|
||||||
|
if (preg_match('/[?&]mid=([^&]+)/i', $mediaUrl, $m)) {
|
||||||
|
$mid = rawurldecode($m[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Buscar patrón directo hacia graph.facebook.com/.../{mediaId}
|
||||||
|
if (!$mid && preg_match('#https?://[^/]*graph\.facebook\.com(?:/v[0-9]+\.[0-9]+)?/([^/?#&]+)#i', $mediaUrl, $m)) {
|
||||||
|
// El primer segmento después de la versión suele ser el media id
|
||||||
|
$candidate = $m[1];
|
||||||
|
// Aceptar si parece un id (numérico o alfanum corto)
|
||||||
|
if (preg_match('/^[a-zA-Z0-9_-]+$/', $candidate)) {
|
||||||
|
$mid = $candidate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Si detectamos un mid, consultamos Graph API desde el servidor para obtener la URL (autenticada)
|
||||||
|
if ($mid) {
|
||||||
|
$token = getConfigFromDB('whatsapp_token', '');
|
||||||
|
$apiUrl = rtrim(getConfigFromDB('whatsapp_api_url', 'https://graph.facebook.com/v22.0/'), '/');
|
||||||
|
$endpoint = "{$apiUrl}/{$mid}";
|
||||||
|
|
||||||
|
$ch = curl_init();
|
||||||
|
curl_setopt_array($ch, [
|
||||||
|
CURLOPT_URL => $endpoint,
|
||||||
|
CURLOPT_RETURNTRANSFER => true,
|
||||||
|
CURLOPT_HTTPHEADER => [ 'Authorization: Bearer ' . $token, 'Accept: application/json' ],
|
||||||
|
CURLOPT_TIMEOUT => 15,
|
||||||
|
]);
|
||||||
|
$response = curl_exec($ch);
|
||||||
|
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||||
|
$err = curl_error($ch);
|
||||||
|
curl_close($ch);
|
||||||
|
|
||||||
|
if (!$err && $httpCode < 400) {
|
||||||
|
$decoded = json_decode($response, true);
|
||||||
|
$resolvedUrl = $decoded['url'] ?? ($decoded['data'][0]['url'] ?? null);
|
||||||
|
if ($resolvedUrl) {
|
||||||
|
$mediaUrl = $resolvedUrl;
|
||||||
|
$resolvedFromGraph = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Si hubo error o no devolvió URL, seguimos usando la URL provista como fallback
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
$token = getConfigFromDB('whatsapp_token', '');
|
$token = getConfigFromDB('whatsapp_token', '');
|
||||||
$apiUrl = rtrim(getConfigFromDB('whatsapp_api_url', 'https://graph.facebook.com/v22.0/'), '/');
|
$apiUrl = rtrim(getConfigFromDB('whatsapp_api_url', 'https://graph.facebook.com/v22.0/'), '/');
|
||||||
@@ -126,9 +175,25 @@ if ($local) {
|
|||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Si se solicitó JSON, devolver la URL
|
// Si se solicitó JSON, devolver la URL y metadata si está disponible
|
||||||
if ($wantsJson) {
|
if ($wantsJson) {
|
||||||
echo json_encode(['success' => true, 'url' => $mediaUrl]);
|
$result = ['success' => true, 'url' => $mediaUrl];
|
||||||
|
|
||||||
|
// Incluir metadata retornada por Graph API si existe en $decoded
|
||||||
|
if (!empty($decoded) && is_array($decoded)) {
|
||||||
|
// Campos esperados directamente
|
||||||
|
foreach (['url','mime_type','sha256','file_size','id','messaging_product'] as $k) {
|
||||||
|
if (isset($decoded[$k])) $result[$k] = $decoded[$k];
|
||||||
|
}
|
||||||
|
// También comprobar 'data'[0] (caso de respuestas anidadas)
|
||||||
|
if (isset($decoded['data']) && is_array($decoded['data']) && isset($decoded['data'][0]) && is_array($decoded['data'][0])) {
|
||||||
|
foreach (['url','mime_type','sha256','file_size','id','messaging_product'] as $k) {
|
||||||
|
if (isset($decoded['data'][0][$k])) $result[$k] = $decoded['data'][0][$k];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
echo json_encode($result);
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -252,6 +317,52 @@ if ($download) {
|
|||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Por defecto: si la URL fue resuelta mediante Graph API, intentar proxificar
|
||||||
|
// (hacer la petición desde el servidor añadiendo Authorization si existe el token)
|
||||||
|
// Esto evita problemas cuando la URL requiere autorización y un 302 directo no funciona.
|
||||||
|
if (!$wantsJson && !empty($resolvedFromGraph) && $resolvedFromGraph) {
|
||||||
|
$ch = curl_init();
|
||||||
|
$token = getConfigFromDB('whatsapp_token', '');
|
||||||
|
$headers = ['User-Agent: WhatsAppMediaProxy/1.0', 'Accept: */*'];
|
||||||
|
if ($token) $headers[] = 'Authorization: Bearer ' . $token;
|
||||||
|
|
||||||
|
curl_setopt_array($ch, [
|
||||||
|
CURLOPT_URL => $mediaUrl,
|
||||||
|
CURLOPT_RETURNTRANSFER => true,
|
||||||
|
CURLOPT_FOLLOWLOCATION => true,
|
||||||
|
CURLOPT_TIMEOUT => 60,
|
||||||
|
CURLOPT_HTTPHEADER => $headers,
|
||||||
|
]);
|
||||||
|
$body = curl_exec($ch);
|
||||||
|
$info = curl_getinfo($ch);
|
||||||
|
$err = curl_error($ch);
|
||||||
|
curl_close($ch);
|
||||||
|
|
||||||
|
if ($err) {
|
||||||
|
http_response_code(502);
|
||||||
|
header('Content-Type: application/json; charset=utf-8');
|
||||||
|
echo json_encode(['success' => false, 'error' => 'Error al descargar el archivo (proxy)', 'detail' => $err]);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$remoteHttp = isset($info['http_code']) ? (int)$info['http_code'] : 0;
|
||||||
|
if ($remoteHttp >= 400) {
|
||||||
|
http_response_code(502);
|
||||||
|
header('Content-Type: application/json; charset=utf-8');
|
||||||
|
echo json_encode(['success' => false, 'error' => 'Remote server returned error status', 'http_code' => $remoteHttp, 'url' => $mediaUrl]);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$contentType = $info['content_type'] ?? null;
|
||||||
|
|
||||||
|
header_remove('Content-Type');
|
||||||
|
header('Cache-Control: public, max-age=3600');
|
||||||
|
if (!empty($contentType)) header('Content-Type: ' . $contentType);
|
||||||
|
header('Content-Length: ' . strlen($body));
|
||||||
|
echo $body;
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
// Por defecto redirigir al URL (navegador hará la descarga o mostrará la imagen)
|
// Por defecto redirigir al URL (navegador hará la descarga o mostrará la imagen)
|
||||||
header('Cache-Control: public, max-age=3600');
|
header('Cache-Control: public, max-age=3600');
|
||||||
header('Location: ' . $mediaUrl, true, 302);
|
header('Location: ' . $mediaUrl, true, 302);
|
||||||
|
|||||||
+22
-9
@@ -619,17 +619,30 @@ if (empty($user_id)) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let fullUrl = '';
|
let fullUrl = '';
|
||||||
if (isValidMediaUrl(msg.media_url_external)) {
|
if (isValidMediaUrl(msg.local_file)) {
|
||||||
fullUrl = `/api/version/media-url.php?local=${encodeURIComponent(msg.media_url_external)}`;
|
fullUrl = `/api/version/media-url.php?local=${encodeURIComponent(msg.local_file)}`;
|
||||||
} else if (isValidMediaUrl(msg.media_url_external)) {
|
} else if (isValidMediaUrl(msg.media_url_external)) {
|
||||||
if (msg.media_url_external.indexOf('api/get_media.php?id=') !== -1) {
|
// Si la URL externa contiene un media id (api/get_media.php?id=... o mid=...), usar id para consultar Graph API
|
||||||
const parts = msg.media_url_external.split('id=');
|
const external = msg.media_url_external;
|
||||||
const mid = parts[1] ? decodeURIComponent(parts[1]) : '';
|
let mid = '';
|
||||||
fullUrl = `/api/version/media-url.php?id=${encodeURIComponent(mid)}`;
|
|
||||||
} else if (/^https?:\/\//i.test(msg.media_url_external)) {
|
if (external.indexOf('api/get_media.php?id=') !== -1) {
|
||||||
fullUrl = `/api/version/media-url.php?url=${encodeURIComponent(msg.media_url_external)}`;
|
const parts = external.split('id=');
|
||||||
|
mid = parts[1] ? decodeURIComponent(parts[1]) : '';
|
||||||
} else {
|
} else {
|
||||||
fullUrl = `/api/version/media-url.php?url=${encodeURIComponent(msg.media_url_external)}`;
|
// Buscar parámetro mid en la query string (ej: ?mid=12345&...)
|
||||||
|
const m = external.match(/[?&]mid=([^&]+)/i);
|
||||||
|
if (m && m[1]) {
|
||||||
|
mid = decodeURIComponent(m[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mid) {
|
||||||
|
fullUrl = `/api/version/media-url.php?id=${encodeURIComponent(mid)}`;
|
||||||
|
} else if (/^https?:\/\//i.test(external)) {
|
||||||
|
fullUrl = `/api/version/media-url.php?url=${encodeURIComponent(external)}`;
|
||||||
|
} else {
|
||||||
|
fullUrl = `/api/version/media-url.php?url=${encodeURIComponent(external)}`;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// No usar msg.media_url como fuente principal para apertura; dejar vacío para forzar 'no disponible'
|
// No usar msg.media_url como fuente principal para apertura; dejar vacío para forzar 'no disponible'
|
||||||
|
|||||||
Reference in New Issue
Block a user