up
This commit is contained in:
@@ -31,11 +31,15 @@ try {
|
|||||||
id,
|
id,
|
||||||
user_id,
|
user_id,
|
||||||
COALESCE(content, message_text) as content,
|
COALESCE(content, message_text) as content,
|
||||||
|
message_id,
|
||||||
|
c.media_url,
|
||||||
|
u.phone_number as user_phone,
|
||||||
direction,
|
direction,
|
||||||
message_type,
|
message_type,
|
||||||
status,
|
status,
|
||||||
created_at
|
created_at
|
||||||
FROM conversations
|
FROM conversations c
|
||||||
|
LEFT JOIN users u ON c.user_id = u.id
|
||||||
WHERE user_id = :user_id
|
WHERE user_id = :user_id
|
||||||
UNION ALL
|
UNION ALL
|
||||||
SELECT
|
SELECT
|
||||||
@@ -76,6 +80,9 @@ try {
|
|||||||
'id' => intval($msg['id']),
|
'id' => intval($msg['id']),
|
||||||
'user_id' => intval($msg['user_id']),
|
'user_id' => intval($msg['user_id']),
|
||||||
'content' => $msg['content'] ?? '',
|
'content' => $msg['content'] ?? '',
|
||||||
|
'message_id' => $msg['message_id'] ?? null,
|
||||||
|
'media_url' => $msg['media_url'] ?? null,
|
||||||
|
'user_phone' => $msg['user_phone'] ?? null,
|
||||||
'direction' => $msg['direction'] ?? 'incoming',
|
'direction' => $msg['direction'] ?? 'incoming',
|
||||||
'message_type' => $msg['message_type'] ?? 'text',
|
'message_type' => $msg['message_type'] ?? 'text',
|
||||||
'status' => $msg['status'] ?? 'sent',
|
'status' => $msg['status'] ?? 'sent',
|
||||||
|
|||||||
@@ -155,10 +155,16 @@ try {
|
|||||||
error_log("=== DEBUG WHATSAPP TEXT MESSAGE ===");
|
error_log("=== DEBUG WHATSAPP TEXT MESSAGE ===");
|
||||||
error_log("Recipient: " . $recipient);
|
error_log("Recipient: " . $recipient);
|
||||||
error_log("Message: " . $input['message']);
|
error_log("Message: " . $input['message']);
|
||||||
error_log("Method: sendTextMessage()");
|
error_log("Method: sendTextMessage()/sendTextReply()");
|
||||||
error_log("====================================");
|
error_log("====================================");
|
||||||
|
|
||||||
$response = $whatsappService->sendTextMessage($recipient, $input['message']);
|
// Si viene reply_to, usar reply con contexto
|
||||||
|
if (!empty($input['reply_to'])) {
|
||||||
|
$replyTo = $input['reply_to'];
|
||||||
|
$response = $whatsappService->sendTextReply($recipient, $replyTo, $input['message']);
|
||||||
|
} else {
|
||||||
|
$response = $whatsappService->sendTextMessage($recipient, $input['message']);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'template':
|
case 'template':
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
require_once __DIR__ . '/../config/config_enhanced.php';
|
||||||
|
require_once __DIR__ . '/../config/config.php';
|
||||||
|
|
||||||
|
header('Content-Type: application/json; charset=utf-8');
|
||||||
|
|
||||||
|
// Autenticación para APIs (debug skip)
|
||||||
|
$debugMode = isset($_GET['debug']) && $_GET['debug'] === 'true';
|
||||||
|
if (!$debugMode && function_exists('requireAuthentication')) {
|
||||||
|
requireAuthentication();
|
||||||
|
}
|
||||||
|
|
||||||
|
$input = json_decode(file_get_contents('php://input'), true) ?: $_POST;
|
||||||
|
$to = $input['to'] ?? null;
|
||||||
|
$message_id = $input['message_id'] ?? null;
|
||||||
|
$emoji = $input['emoji'] ?? null;
|
||||||
|
$dry = isset($input['dry_run']) ? (bool)$input['dry_run'] : false;
|
||||||
|
|
||||||
|
if (!$to || !$message_id || !$emoji) {
|
||||||
|
http_response_code(400);
|
||||||
|
echo json_encode(['success' => false, 'error' => 'Parámetros faltantes: to, message_id, emoji']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$wa = new WhatsAppService();
|
||||||
|
$result = $wa->sendReaction($to, $message_id, $emoji, $dry);
|
||||||
|
echo json_encode(['success' => true, 'dry_run' => $dry, 'result' => $result]);
|
||||||
|
} catch (Throwable $t) {
|
||||||
|
http_response_code(500);
|
||||||
|
echo json_encode(['success' => false, 'error' => $t->getMessage()]);
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
require_once __DIR__ . '/../config/config_enhanced.php';
|
||||||
|
require_once __DIR__ . '/../config/config.php';
|
||||||
|
|
||||||
|
header('Content-Type: application/json; charset=utf-8');
|
||||||
|
|
||||||
|
// Autenticación para APIs (debug skip)
|
||||||
|
$debugMode = isset($_GET['debug']) && $_GET['debug'] === 'true';
|
||||||
|
if (!$debugMode && function_exists('requireAuthentication')) {
|
||||||
|
requireAuthentication();
|
||||||
|
}
|
||||||
|
|
||||||
|
$input = json_decode(file_get_contents('php://input'), true) ?: $_POST;
|
||||||
|
$to = $input['to'] ?? null;
|
||||||
|
$message_id = $input['message_id'] ?? null;
|
||||||
|
$text = $input['text'] ?? null;
|
||||||
|
$preview = isset($input['preview_url']) ? (bool)$input['preview_url'] : false;
|
||||||
|
$dry = isset($input['dry_run']) ? (bool)$input['dry_run'] : false;
|
||||||
|
|
||||||
|
if (!$to || !$message_id || !$text) {
|
||||||
|
http_response_code(400);
|
||||||
|
echo json_encode(['success' => false, 'error' => 'Parámetros faltantes: to, message_id, text']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$wa = new WhatsAppService();
|
||||||
|
$result = $wa->sendTextReply($to, $message_id, $text, $preview, $dry);
|
||||||
|
echo json_encode(['success' => true, 'dry_run' => $dry, 'result' => $result]);
|
||||||
|
} catch (Throwable $t) {
|
||||||
|
http_response_code(500);
|
||||||
|
echo json_encode(['success' => false, 'error' => $t->getMessage()]);
|
||||||
|
}
|
||||||
+7
-1
@@ -128,7 +128,13 @@ class WhatsAppWebhook {
|
|||||||
$messageType = 'text';
|
$messageType = 'text';
|
||||||
$mediaUrl = null;
|
$mediaUrl = null;
|
||||||
|
|
||||||
if (isset($message['text'])) {
|
if (isset($message['reaction'])) {
|
||||||
|
// Reacción (emoji) a un mensaje anterior
|
||||||
|
$messageType = 'reaction';
|
||||||
|
$emoji = $message['reaction']['emoji'] ?? '';
|
||||||
|
$reactionTo = $message['reaction']['message_id'] ?? '';
|
||||||
|
$messageText = json_encode(['emoji' => $emoji, 'message_id' => $reactionTo]);
|
||||||
|
} elseif (isset($message['text'])) {
|
||||||
$messageText = $message['text']['body'];
|
$messageText = $message['text']['body'];
|
||||||
$messageType = 'text';
|
$messageType = 'text';
|
||||||
} elseif (isset($message['image'])) {
|
} elseif (isset($message['image'])) {
|
||||||
|
|||||||
@@ -1028,6 +1028,38 @@ class WhatsAppBotManager {
|
|||||||
this.showTab('messages');
|
this.showTab('messages');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
replyToMessage(messageId, userPhone) {
|
||||||
|
// Poner en el input del chat y almacenar reply_to en data attribute
|
||||||
|
document.getElementById('message-input').focus();
|
||||||
|
document.getElementById('message-input').dataset.replyTo = messageId;
|
||||||
|
this.showSuccess('Preparado para responder al mensaje ' + messageId);
|
||||||
|
}
|
||||||
|
|
||||||
|
async reactToMessage(messageId, userPhone) {
|
||||||
|
const emoji = prompt('Escribe el emoji de reacción (ej: ❤️, 👍):');
|
||||||
|
if (!emoji) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const resp = await fetch('api/send_reaction.php', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ to: userPhone, message_id: messageId, emoji: emoji })
|
||||||
|
});
|
||||||
|
const data = await resp.json();
|
||||||
|
if (data.success) {
|
||||||
|
this.showSuccess('Reacción enviada');
|
||||||
|
// Refrescar mensajes
|
||||||
|
await this.loadMessages(this.currentUserId, false);
|
||||||
|
} else {
|
||||||
|
this.showError('Error enviando reacción: ' + (data.error || 'desconocido'));
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error('reactToMessage error', err);
|
||||||
|
this.showError('Error comunicándose con el servidor');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
editUser(userId) {
|
editUser(userId) {
|
||||||
// Implementar modal de edición de usuario
|
// Implementar modal de edición de usuario
|
||||||
console.log('Editar usuario:', userId);
|
console.log('Editar usuario:', userId);
|
||||||
|
|||||||
+30
-30
@@ -44,50 +44,50 @@ function env($key, $default = null) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Configuración de la base de datos
|
// Configuración de la base de datos
|
||||||
define('DB_HOST', env('DB_HOST', 'localhost'));
|
if (!defined('DB_HOST')) define('DB_HOST', env('DB_HOST', 'localhost'));
|
||||||
define('DB_PORT', env('DB_PORT', '3306'));
|
if (!defined('DB_PORT')) define('DB_PORT', env('DB_PORT', '3306'));
|
||||||
define('DB_NAME', env('DB_NAME', 'whatsapp_bot'));
|
if (!defined('DB_NAME')) define('DB_NAME', env('DB_NAME', 'whatsapp_bot'));
|
||||||
define('DB_USER', env('DB_USER', 'whatsapp_user'));
|
if (!defined('DB_USER')) define('DB_USER', env('DB_USER', 'whatsapp_user'));
|
||||||
define('DB_PASS', env('DB_PASS', '')); // Se generará automáticamente en la instalación
|
if (!defined('DB_PASS')) define('DB_PASS', env('DB_PASS', '')); // Se generará automáticamente en la instalación
|
||||||
define('DB_CHARSET', env('DB_CHARSET', 'utf8mb4'));
|
if (!defined('DB_CHARSET')) define('DB_CHARSET', env('DB_CHARSET', 'utf8mb4'));
|
||||||
|
|
||||||
// WhatsApp Business API
|
// WhatsApp Business API
|
||||||
define('WHATSAPP_TOKEN', env('WHATSAPP_TOKEN', 'TU_TOKEN_DE_WHATSAPP_AQUI'));
|
if (!defined('WHATSAPP_TOKEN')) define('WHATSAPP_TOKEN', env('WHATSAPP_TOKEN', 'TU_TOKEN_DE_WHATSAPP_AQUI'));
|
||||||
define('WHATSAPP_PHONE_NUMBER_ID', env('WHATSAPP_PHONE_NUMBER_ID', 'TU_PHONE_ID_AQUI'));
|
if (!defined('WHATSAPP_PHONE_NUMBER_ID')) define('WHATSAPP_PHONE_NUMBER_ID', env('WHATSAPP_PHONE_NUMBER_ID', 'TU_PHONE_ID_AQUI'));
|
||||||
define('WHATSAPP_API_URL', env('WHATSAPP_API_URL', 'https://graph.facebook.com/v22.0/'));
|
if (!defined('WHATSAPP_API_URL')) define('WHATSAPP_API_URL', env('WHATSAPP_API_URL', 'https://graph.facebook.com/v22.0/'));
|
||||||
define('WEBHOOK_VERIFY_TOKEN', env('WEBHOOK_VERIFY_TOKEN', 'mi_token_secreto_123'));
|
if (!defined('WEBHOOK_VERIFY_TOKEN')) define('WEBHOOK_VERIFY_TOKEN', env('WEBHOOK_VERIFY_TOKEN', 'mi_token_secreto_123'));
|
||||||
|
|
||||||
// Auto-approve templates: Si está activado, las plantillas creadas localmente se marcarán como 'approved' automáticamente
|
// Auto-approve templates: Si está activado, las plantillas creadas localmente se marcarán como 'approved' automáticamente
|
||||||
define('AUTO_APPROVE_TEMPLATES', filter_var(env('AUTO_APPROVE_TEMPLATES', 'false'), FILTER_VALIDATE_BOOLEAN));
|
if (!defined('AUTO_APPROVE_TEMPLATES')) define('AUTO_APPROVE_TEMPLATES', filter_var(env('AUTO_APPROVE_TEMPLATES', 'false'), FILTER_VALIDATE_BOOLEAN));
|
||||||
|
|
||||||
// Configuración general
|
// Configuración general
|
||||||
define('APP_NAME', env('APP_NAME', 'WhatsApp Bot System'));
|
if (!defined('APP_NAME')) define('APP_NAME', env('APP_NAME', 'WhatsApp Bot System'));
|
||||||
define('APP_VERSION', env('APP_VERSION', '1.0.0'));
|
if (!defined('APP_VERSION')) define('APP_VERSION', env('APP_VERSION', '1.0.0'));
|
||||||
define('APP_URL', env('APP_URL', 'https://tudominio.com')); // Se auto-detectará
|
if (!defined('APP_URL')) define('APP_URL', env('APP_URL', 'https://tudominio.com'));
|
||||||
define('TIMEZONE', env('TIMEZONE', 'America/Bogota'));
|
if (!defined('TIMEZONE')) define('TIMEZONE', env('TIMEZONE', 'America/Bogota'));
|
||||||
|
|
||||||
// Información del desarrollador
|
// Información del desarrollador
|
||||||
define('DEVELOPER_NAME', 'U-Site.app');
|
if (!defined('DEVELOPER_NAME')) define('DEVELOPER_NAME', 'U-Site.app');
|
||||||
define('DEVELOPER_URL', 'https://u-site.app');
|
if (!defined('DEVELOPER_URL')) define('DEVELOPER_URL', 'https://u-site.app');
|
||||||
define('DEVELOPER_EMAIL', 'support@u-site.app');
|
if (!defined('DEVELOPER_EMAIL')) define('DEVELOPER_EMAIL', 'support@u-site.app');
|
||||||
define('DEVELOPER_SUPPORT', 'https://u-site.app/support');
|
if (!defined('DEVELOPER_SUPPORT')) define('DEVELOPER_SUPPORT', 'https://u-site.app/support');
|
||||||
|
|
||||||
// Configuración de seguridad
|
// Configuración de seguridad
|
||||||
define('INSTALLATION_LOCK', '.installation_completed'); // Archivo de bloqueo
|
if (!defined('INSTALLATION_LOCK')) define('INSTALLATION_LOCK', '.installation_completed');
|
||||||
define('ADMIN_USERNAME', env('ADMIN_USERNAME', 'admin')); // Usuario administrador por defecto
|
if (!defined('ADMIN_USERNAME')) define('ADMIN_USERNAME', env('ADMIN_USERNAME', 'admin'));
|
||||||
define('ADMIN_PASSWORD', env('ADMIN_PASSWORD', '')); // Se configurará en instalación
|
if (!defined('ADMIN_PASSWORD')) define('ADMIN_PASSWORD', env('ADMIN_PASSWORD', ''));
|
||||||
define('SESSION_TIMEOUT', (int)env('SESSION_TIMEOUT', 1800)); // 30 minutos
|
if (!defined('SESSION_TIMEOUT')) define('SESSION_TIMEOUT', (int)env('SESSION_TIMEOUT', 1800));
|
||||||
define('MAX_LOGIN_ATTEMPTS', (int)env('MAX_LOGIN_ATTEMPTS', 3)); // Intentos máximos de login
|
if (!defined('MAX_LOGIN_ATTEMPTS')) define('MAX_LOGIN_ATTEMPTS', (int)env('MAX_LOGIN_ATTEMPTS', 3));
|
||||||
define('LOGIN_LOCKOUT_TIME', (int)env('LOGIN_LOCKOUT_TIME', 900)); // 15 minutos de bloqueo tras fallos
|
if (!defined('LOGIN_LOCKOUT_TIME')) define('LOGIN_LOCKOUT_TIME', (int)env('LOGIN_LOCKOUT_TIME', 900));
|
||||||
|
|
||||||
// Configuración de logs
|
// Configuración de logs
|
||||||
define('ENABLE_LOGGING', filter_var(env('ENABLE_LOGGING', 'true'), FILTER_VALIDATE_BOOLEAN));
|
if (!defined('ENABLE_LOGGING')) define('ENABLE_LOGGING', filter_var(env('ENABLE_LOGGING', 'true'), FILTER_VALIDATE_BOOLEAN));
|
||||||
define('LOG_LEVEL', env('LOG_LEVEL', 'INFO')); // DEBUG, INFO, WARNING, ERROR
|
if (!defined('LOG_LEVEL')) define('LOG_LEVEL', env('LOG_LEVEL', 'INFO')); // DEBUG, INFO, WARNING, ERROR
|
||||||
define('LOG_FILE', env('LOG_FILE', 'logs/system.log'));
|
if (!defined('LOG_FILE')) define('LOG_FILE', env('LOG_FILE', 'logs/system.log'));
|
||||||
|
|
||||||
// Configuración de archivos
|
// Configuración de archivos
|
||||||
define('UPLOAD_MAX_SIZE', (int)env('UPLOAD_MAX_SIZE', 10485760)); // 10MB
|
if (!defined('UPLOAD_MAX_SIZE')) define('UPLOAD_MAX_SIZE', (int)env('UPLOAD_MAX_SIZE', 10485760)); // 10MB
|
||||||
define('ALLOWED_FILE_TYPES', ['jpg', 'jpeg', 'png', 'gif', 'pdf', 'doc', 'docx', 'mp4', 'mp3', 'wav']);
|
if (!defined('ALLOWED_FILE_TYPES')) define('ALLOWED_FILE_TYPES', ['jpg', 'jpeg', 'png', 'gif', 'pdf', 'doc', 'docx', 'mp4', 'mp3', 'wav']);
|
||||||
|
|
||||||
// Configurar zona horaria
|
// Configurar zona horaria
|
||||||
date_default_timezone_set(TIMEZONE);
|
date_default_timezone_set(TIMEZONE);
|
||||||
|
|||||||
+9
-1
@@ -748,6 +748,10 @@
|
|||||||
<div class="message ${msg.direction}">
|
<div class="message ${msg.direction}">
|
||||||
<div class="message-bubble">
|
<div class="message-bubble">
|
||||||
${content}
|
${content}
|
||||||
|
<div class="message-actions mt-1">
|
||||||
|
<button class="btn btn-sm btn-link" onclick="app.replyToMessage('${msg.message_id}','${msg.user_phone}')" title="Responder"><i class="fas fa-reply"></i></button>
|
||||||
|
<button class="btn btn-sm btn-link" onclick="app.reactToMessage('${msg.message_id}','${msg.user_phone}')" title="Reaccionar"><i class="far fa-grin"></i></button>
|
||||||
|
</div>
|
||||||
<div class="message-time">
|
<div class="message-time">
|
||||||
${time}
|
${time}
|
||||||
${msg.direction === 'outgoing' ? `<span class="message-status">${statusIcon}</span>` : ''}
|
${msg.direction === 'outgoing' ? `<span class="message-status">${statusIcon}</span>` : ''}
|
||||||
@@ -785,6 +789,7 @@
|
|||||||
document.getElementById('send-btn').disabled = true;
|
document.getElementById('send-btn').disabled = true;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
const replyTo = input.dataset.replyTo || null;
|
||||||
const response = await fetch('api/send_message.php', {
|
const response = await fetch('api/send_message.php', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
@@ -792,9 +797,12 @@
|
|||||||
},
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
user_id: this.currentUserId,
|
user_id: this.currentUserId,
|
||||||
message: message
|
message: message,
|
||||||
|
reply_to: replyTo
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
// Clear reply data attribute
|
||||||
|
input.dataset.replyTo = null;
|
||||||
|
|
||||||
const result = await response.json();
|
const result = await response.json();
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Safe migration: show current column type, backup distinct statuses, then alter.
|
||||||
|
*/
|
||||||
|
require_once __DIR__ . '/../config/config_enhanced.php';
|
||||||
|
require_once __DIR__ . '/../classes/Database.php';
|
||||||
|
|
||||||
|
try {
|
||||||
|
$db = Database::getInstance();
|
||||||
|
echo "Current structure for conversations table:\n";
|
||||||
|
$rows = $db->fetchAll("SHOW COLUMNS FROM conversations LIKE 'status'");
|
||||||
|
print_r($rows);
|
||||||
|
|
||||||
|
$distinct = $db->fetchAll("SELECT DISTINCT status FROM conversations");
|
||||||
|
echo "Distinct status values in table:\n";
|
||||||
|
print_r(array_column($distinct, 'status'));
|
||||||
|
|
||||||
|
// Ask user confirmation (CLI only)
|
||||||
|
if (php_sapi_name() === 'cli') {
|
||||||
|
echo "About to alter column 'status' to ENUM('sent','delivered','read','failed','received') DEFAULT 'sent'. Continue? (y/N): ";
|
||||||
|
$resp = trim(fgets(STDIN));
|
||||||
|
if (strtolower($resp) !== 'y') {
|
||||||
|
echo "Aborted by user.\n";
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$db->query("ALTER TABLE conversations MODIFY COLUMN status ENUM('sent','delivered','read','failed','received') DEFAULT 'sent'");
|
||||||
|
echo "ALTER OK\n";
|
||||||
|
} catch (Exception $e) {
|
||||||
|
echo "ERROR: " . $e->getMessage() . "\n";
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
<?php
|
||||||
|
ini_set('display_errors',1);
|
||||||
|
error_reporting(E_ALL);
|
||||||
|
|
||||||
|
// Test calling API endpoint internally by setting input stream
|
||||||
|
$payload = json_encode([
|
||||||
|
'to' => '573001234567',
|
||||||
|
'message_id' => '<TEST_MSGID>',
|
||||||
|
'emoji' => '👍',
|
||||||
|
'dry_run' => true
|
||||||
|
]);
|
||||||
|
|
||||||
|
file_put_contents('php://memory', $payload); // no effect; instead call service directly
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../api/send_reaction.php';
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
<?php
|
||||||
|
ini_set('display_errors',1);
|
||||||
|
error_reporting(E_ALL);
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../api/send_reply.php';
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
<?php
|
||||||
|
ini_set('display_errors',1);
|
||||||
|
error_reporting(E_ALL);
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../services/WhatsAppService.php';
|
||||||
|
|
||||||
|
try {
|
||||||
|
$s = new WhatsAppService();
|
||||||
|
echo "WhatsAppService loaded\n";
|
||||||
|
|
||||||
|
echo "Dry-run reaction:\n";
|
||||||
|
$r = $s->sendReaction('573001234567', '<MSGID123>', '❤️', true);
|
||||||
|
print_r($r);
|
||||||
|
|
||||||
|
echo "Dry-run text reply:\n";
|
||||||
|
$rr = $s->sendTextReply('573001234567', '<MSGID123>', 'Gracias por tu mensaje', false, true);
|
||||||
|
print_r($rr);
|
||||||
|
|
||||||
|
} catch (Throwable $t) {
|
||||||
|
echo "ERROR: " . $t->getMessage() . "\n";
|
||||||
|
}
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
<?php
|
||||||
|
ini_set('display_errors',1);
|
||||||
|
error_reporting(E_ALL);
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../api/webhook.php';
|
||||||
|
|
||||||
|
$payload = [
|
||||||
|
"object" => "whatsapp_business_account",
|
||||||
|
"entry" => [
|
||||||
|
[
|
||||||
|
"id" => "0",
|
||||||
|
"changes" => [
|
||||||
|
[
|
||||||
|
"field" => "messages",
|
||||||
|
"value" => [
|
||||||
|
"messaging_product" => "whatsapp",
|
||||||
|
"metadata" => [
|
||||||
|
"display_phone_number" => "16505551111",
|
||||||
|
"phone_number_id" => "123456123"
|
||||||
|
],
|
||||||
|
"contacts" => [
|
||||||
|
[
|
||||||
|
"profile" => ["name" => "test user name"],
|
||||||
|
"wa_id" => "16315551181"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"messages" => [
|
||||||
|
[
|
||||||
|
"from" => "16315551181",
|
||||||
|
"id" => "ABGGFlA5Fpa",
|
||||||
|
"timestamp" => "1504902988",
|
||||||
|
"type" => "text",
|
||||||
|
"text" => ["body" => "this is a text message"]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
try {
|
||||||
|
$w = new WhatsAppWebhook();
|
||||||
|
echo "Webhook instance created\n";
|
||||||
|
$result = $w->processPayload($payload);
|
||||||
|
echo "processPayload result: " . ($result ? 'true' : 'false') . "\n";
|
||||||
|
|
||||||
|
// Simular reacción entrante
|
||||||
|
$reactionPayload = [
|
||||||
|
"object" => "whatsapp_business_account",
|
||||||
|
"entry" => [
|
||||||
|
[
|
||||||
|
"id" => "0",
|
||||||
|
"changes" => [
|
||||||
|
[
|
||||||
|
"field" => "messages",
|
||||||
|
"value" => [
|
||||||
|
"messaging_product" => "whatsapp",
|
||||||
|
"metadata" => [
|
||||||
|
"display_phone_number" => "16505551111",
|
||||||
|
"phone_number_id" => "123456123"
|
||||||
|
],
|
||||||
|
"contacts" => [
|
||||||
|
[
|
||||||
|
"profile" => ["name" => "reactor"],
|
||||||
|
"wa_id" => "16315551181"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"messages" => [
|
||||||
|
[
|
||||||
|
"from" => "16315551181",
|
||||||
|
"id" => "REACTION1",
|
||||||
|
"timestamp" => "1504902990",
|
||||||
|
"type" => "reaction",
|
||||||
|
"reaction" => ["message_id" => "ABGGFlA5Fpa", "emoji" => "❤️"]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
$r2 = $w->processPayload($reactionPayload);
|
||||||
|
echo "processReaction result: " . ($r2 ? 'true' : 'false') . "\n";
|
||||||
|
|
||||||
|
} catch (Throwable $t) {
|
||||||
|
echo "Fatal: " . $t->getMessage() . "\n";
|
||||||
|
echo $t->getTraceAsString() . "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show last lines of system log
|
||||||
|
$log = __DIR__ . '/../logs/system.log';
|
||||||
|
if (file_exists($log)) {
|
||||||
|
echo "-- Last log lines --\n";
|
||||||
|
$lines = array_slice(file($log), -40);
|
||||||
|
foreach ($lines as $line) echo $line;
|
||||||
|
} else {
|
||||||
|
echo "No log file found\n";
|
||||||
|
}
|
||||||
@@ -536,10 +536,62 @@ class WhatsAppService
|
|||||||
return $data['interactive']['body']['text'];
|
return $data['interactive']['body']['text'];
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case 'reaction':
|
||||||
|
return isset($data['reaction']['emoji']) ? $data['reaction']['emoji'] : json_encode($data['reaction']);
|
||||||
}
|
}
|
||||||
return json_encode($data);
|
return json_encode($data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enviar reacción (emoji) a un mensaje previo
|
||||||
|
* @param string $to Teléfono del destinatario
|
||||||
|
* @param string $messageId ID del mensaje al que se reacciona
|
||||||
|
* @param string $emoji Emoji de reacción
|
||||||
|
* @param bool $dryRun Si true, no hace la petición y devuelve el payload
|
||||||
|
*/
|
||||||
|
public function sendReaction($to, $messageId, $emoji, $dryRun = false)
|
||||||
|
{
|
||||||
|
$data = [
|
||||||
|
'messaging_product' => 'whatsapp',
|
||||||
|
'to' => $this->formatPhoneNumber($to),
|
||||||
|
'type' => 'reaction',
|
||||||
|
'reaction' => [
|
||||||
|
'message_id' => $messageId,
|
||||||
|
'emoji' => $emoji
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
if ($dryRun) return $data;
|
||||||
|
return $this->sendMessage($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enviar texto como respuesta (con contexto) a un mensaje existente
|
||||||
|
* @param string $to Teléfono del destinatario
|
||||||
|
* @param string $messageId ID del mensaje al que se responde
|
||||||
|
* @param string $text Contenido del mensaje de respuesta
|
||||||
|
* @param bool $preview_url Incluir preview_url en body.text
|
||||||
|
* @param bool $dryRun Si true, no hace la petición y devuelve el payload
|
||||||
|
*/
|
||||||
|
public function sendTextReply($to, $messageId, $text, $preview_url = false, $dryRun = false)
|
||||||
|
{
|
||||||
|
$data = [
|
||||||
|
'messaging_product' => 'whatsapp',
|
||||||
|
'to' => $this->formatPhoneNumber($to),
|
||||||
|
'type' => 'text',
|
||||||
|
'context' => [
|
||||||
|
'message_id' => $messageId
|
||||||
|
],
|
||||||
|
'text' => [
|
||||||
|
'preview_url' => (bool)$preview_url,
|
||||||
|
'body' => $text
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
if ($dryRun) return $data;
|
||||||
|
return $this->sendMessage($data);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Obtener usuario por teléfono
|
* Obtener usuario por teléfono
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user