'❤️') and decode HTML entities function normalize_emoji($s) { if (!$s) return $s; // decode numeric HTML entities like ❤ etc. $s = html_entity_decode($s, ENT_QUOTES | ENT_HTML5, 'UTF-8'); $orig = $s; $s_trim = trim(mb_strtolower($s, 'UTF-8')); $map = [ 'corazon' => '❤️', 'heart' => '❤️', '<3' => '❤️', 'like' => '👍', 'thumbsup' => '👍', 'thumbs_up' => '👍', '👍' => '👍', 'laugh' => '😂', 'risa' => '😂', 'joy' => '😂', 'wow' => '😮', 'surprised' => '😮', 'sad' => '😢', 'triste' => '😢', 'clap' => '👏', 'applause' => '👏', 'party' => '🎉', 'celebrate' => '🎉', 'fire' => '🔥', 'pray' => '🙏', 'thanks' => '🙏' ]; // If the string looks like an alias (letters, dashes, underscores, or short), map it $key = preg_replace('/[^a-z0-9_\-]/u', '', $s_trim); if (isset($map[$key])) return $map[$key]; // If the string itself contains an emoji (most common case), return the first emoji-like char or the original // A simple heuristic: keep any non-ASCII or known emoji utf ranges if (preg_match('/[\x{1F300}-\x{1F6FF}\x{1F900}-\x{1F9FF}\x{2600}-\x{26FF}\x{2700}-\x{27BF}]/u', $orig, $m)) { return $m[0]; } // fallback to original trimmed string return trim($orig); } if (!$message_id || !$emoji) { http_response_code(400); echo json_encode(['success' => false, 'error' => 'Parámetros faltantes: message_id, emoji']); exit; } // Normalize emoji early $emoji = normalize_emoji($emoji); try { $db = Database::getInstance(); // Buscar mensaje objetivo para obtener user_id $stmt = $db->query("SELECT id, user_id, message_id, direction FROM conversations WHERE message_id = ? OR id = ? LIMIT 1", [$message_id, $message_id]); $target = $stmt->fetch(); if (!$target) { http_response_code(404); echo json_encode(['success' => false, 'error' => 'Mensaje objetivo no encontrado']); exit; } // Obtener teléfono del usuario $user = $db->query("SELECT phone_number FROM users WHERE id = ? LIMIT 1", [$target['user_id']])->fetch(); if (!$user || empty($user['phone_number'])) { http_response_code(500); echo json_encode(['success' => false, 'error' => 'No se pudo determinar el teléfono del usuario']); exit; } // Enviar reacción a través de WhatsAppService $wa = new WhatsAppService(); $to = $user['phone_number']; $resp = null; try { $resp = $wa->sendReaction($to, $message_id, $emoji, false); } catch (Exception $e) { // fallar la operación de envío pero seguir guardando registro con status "failed" error_log('sendReaction failed: ' . $e->getMessage()); } // Registrar la reacción en el mensaje objetivo (no crear un nuevo "reaction" como mensaje saliente) try { $update = ['reaction_emoji' => $emoji, 'reaction_to_message_id' => $message_id, 'updated_at' => date('Y-m-d H:i:s')]; // If send failed, optionally record a status on target (not mandatory) if (!$resp) $update['reaction_status'] = 'failed'; $db->update('conversations', $update, 'id = :id', ['id' => $target['id']]); // Return updated row for convenience $updated = $db->query('SELECT * FROM conversations WHERE id = ? LIMIT 1', [$target['id']])->fetch(); echo json_encode(['success' => true, 'message' => 'Reacción aplicada', 'updated' => $updated]); } catch (Exception $e) { http_response_code(500); echo json_encode(['success' => false, 'error' => 'No se pudo guardar la reacción: ' . $e->getMessage()]); } } catch (Exception $e) { http_response_code(500); echo json_encode(['success' => false, 'error' => $e->getMessage()]); }