up
This commit is contained in:
@@ -62,6 +62,21 @@ class BotService {
|
||||
error_log('[BotService] maybeResetConversationAfterIdle threw: ' . $e->getMessage());
|
||||
}
|
||||
|
||||
// ── Flujo de Términos y Condiciones ──────────────────────────────────
|
||||
// 1. Si el usuario está esperando respuesta de términos, procesarla primero.
|
||||
if (!empty($user['terms_pending'])) {
|
||||
if ($this->processTermsResponse($user, $phoneNumber, $messageText)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Si el usuario necesita aceptar (nuevo, expirado, o versión nueva con forzar_reenvio).
|
||||
if ($this->needsTermsAcceptance($user)) {
|
||||
$this->sendTermsMessage($user, $phoneNumber);
|
||||
return;
|
||||
}
|
||||
// ─────────────────────────────────────────────────────────────────────
|
||||
|
||||
// Verificar si es un nuevo usuario (enviar mensaje de bienvenida)
|
||||
if ($this->isNewUser($user['id'])) {
|
||||
$this->sendWelcomeMessage($phoneNumber);
|
||||
@@ -427,6 +442,196 @@ class BotService {
|
||||
return false;
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// TÉRMINOS Y CONDICIONES
|
||||
// =========================================================================
|
||||
|
||||
/**
|
||||
* Obtiene la versión activa de términos o null si no existe ninguna.
|
||||
*/
|
||||
private function getActiveTermsVersion(): ?array {
|
||||
try {
|
||||
return $this->db->fetch(
|
||||
"SELECT * FROM terms_versions WHERE activa = 1 ORDER BY id DESC LIMIT 1"
|
||||
) ?: null;
|
||||
} catch (Exception $e) {
|
||||
error_log('[BotService] getActiveTermsVersion error: ' . $e->getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decide si el usuario debe aceptar (o re-aceptar) los términos.
|
||||
* - No hay aceptación previa (terms_accepted_at IS NULL)
|
||||
* - Han pasado más de 6 meses desde la última aceptación
|
||||
* - La versión activa tiene forzar_reenvio=1 y el usuario aceptó una versión anterior
|
||||
*/
|
||||
private function needsTermsAcceptance(array $user): bool {
|
||||
$terms = $this->getActiveTermsVersion();
|
||||
if (!$terms) return false; // Sin versión activa → no hay términos que aceptar
|
||||
|
||||
// ¿El usuario ya aceptó esta versión exacta?
|
||||
$acceptedVersionId = $user['terms_version_id'] ?? null;
|
||||
$acceptedAt = $user['terms_accepted_at'] ?? null;
|
||||
|
||||
// Nunca aceptó
|
||||
if (!$acceptedAt) return true;
|
||||
|
||||
// Forzar re-aceptación si la versión activa lo requiere y el usuario tiene una versión diferente
|
||||
if (!empty($terms['forzar_reenvio']) && (int)$acceptedVersionId !== (int)$terms['id']) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Han pasado más de 6 meses (26 semanas) desde la última aceptación
|
||||
$sixMonthsAgo = strtotime('-6 months');
|
||||
if (strtotime($acceptedAt) < $sixMonthsAgo) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Envía el mensaje de términos al usuario y registra el envío en BD.
|
||||
*/
|
||||
private function sendTermsMessage(array $user, string $phoneNumber): void {
|
||||
$terms = $this->getActiveTermsVersion();
|
||||
if (!$terms) return;
|
||||
|
||||
// Usar el mensaje configurado en terms_versions o el de config como fallback
|
||||
$msg = !empty($terms['mensaje_aceptacion'])
|
||||
? $terms['mensaje_aceptacion']
|
||||
: getConfigFromDB('terms_message', '');
|
||||
|
||||
if (!$msg) return;
|
||||
|
||||
// Adjuntar URL del documento solo si es una URL pública (no localhost)
|
||||
if (!empty($terms['documento_url'])) {
|
||||
$docUrl = $terms['documento_url'];
|
||||
$isLocal = (
|
||||
strpos($docUrl, 'localhost') !== false ||
|
||||
strpos($docUrl, '127.0.0.1') !== false ||
|
||||
preg_match('/https?:\/\/\d+\.\d+\.\d+\.\d+:\d+/', $docUrl)
|
||||
);
|
||||
if (!$isLocal) {
|
||||
$msg .= "\n\n📄 Documento: " . $docUrl;
|
||||
}
|
||||
}
|
||||
|
||||
$this->whatsappService->sendTextMessage($phoneNumber, $msg);
|
||||
|
||||
// Marcar usuario como "esperando respuesta de términos"
|
||||
try {
|
||||
$this->db->update(
|
||||
'users',
|
||||
['terms_pending' => 1],
|
||||
'id = :id',
|
||||
['id' => $user['id']]
|
||||
);
|
||||
} catch (Exception $e) {
|
||||
error_log('[BotService] sendTermsMessage – update terms_pending: ' . $e->getMessage());
|
||||
}
|
||||
|
||||
// Registrar envío en terms_acceptance con estado pendiente
|
||||
try {
|
||||
$this->db->insert('terms_acceptance', [
|
||||
'user_id' => $user['id'],
|
||||
'terms_version_id' => $terms['id'],
|
||||
'phone_number' => $phoneNumber,
|
||||
'estado' => 'pendiente',
|
||||
'fecha_envio' => date('Y-m-d H:i:s'),
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
error_log('[BotService] sendTermsMessage – insert terms_acceptance: ' . $e->getMessage());
|
||||
}
|
||||
|
||||
error_log("[BotService] sendTermsMessage – sent terms v{$terms['version']} to {$phoneNumber}");
|
||||
}
|
||||
|
||||
/**
|
||||
* Procesa la respuesta del usuario cuando está en estado terms_pending.
|
||||
* Detecta "acepto" / "no acepto" / "rechazo".
|
||||
* Devuelve true si el mensaje fue manejado (para detener el procesamiento normal).
|
||||
*/
|
||||
private function processTermsResponse(array $user, string $phoneNumber, string $messageText): bool {
|
||||
$terms = $this->getActiveTermsVersion();
|
||||
if (!$terms) {
|
||||
// Sin versión activa: limpiar el flag y continuar
|
||||
try {
|
||||
$this->db->update('users', ['terms_pending' => 0], 'id = :id', ['id' => $user['id']]);
|
||||
} catch (Exception $e) {}
|
||||
return false;
|
||||
}
|
||||
|
||||
$normalized = strtolower(trim(
|
||||
iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $messageText) ?: $messageText
|
||||
));
|
||||
|
||||
// Detectar aceptación: "acepto", "si acepto", "sí, acepto", "1", etc.
|
||||
$accepted = (bool) preg_match('/\b(acepto|si acepto|si|sí|1|ok|aceptar|de acuerdo|confirmo)\b/', $normalized);
|
||||
|
||||
// Detectar rechazo explícito: "no acepto", "rechazo", "no", "0", etc.
|
||||
$rejected = !$accepted && (bool) preg_match('/\b(no acepto|no|rechazo|rechazar|negar|0|rechaz[oa])\b/', $normalized);
|
||||
|
||||
if (!$accepted && !$rejected) {
|
||||
// Respuesta no reconocida → volver a preguntar
|
||||
$retry = "No entendí tu respuesta. Por favor responde *Acepto* o *No acepto*.";
|
||||
$this->whatsappService->sendTextMessage($phoneNumber, $retry);
|
||||
return true;
|
||||
}
|
||||
|
||||
$estado = $accepted ? 'aceptado' : 'rechazado';
|
||||
$now = date('Y-m-d H:i:s');
|
||||
|
||||
// Actualizar el último registro pendiente de esta versión
|
||||
try {
|
||||
$this->db->query(
|
||||
"UPDATE terms_acceptance SET estado = :estado, fecha_respuesta = :now
|
||||
WHERE user_id = :uid AND terms_version_id = :vid AND estado = 'pendiente'
|
||||
ORDER BY id DESC LIMIT 1",
|
||||
['estado' => $estado, 'now' => $now, 'uid' => $user['id'], 'vid' => $terms['id']]
|
||||
);
|
||||
} catch (Exception $e) {
|
||||
error_log('[BotService] processTermsResponse – update acceptance: ' . $e->getMessage());
|
||||
}
|
||||
|
||||
if ($accepted) {
|
||||
// Actualizar usuario: aceptó términos
|
||||
try {
|
||||
$this->db->update('users', [
|
||||
'terms_pending' => 0,
|
||||
'terms_accepted_at' => $now,
|
||||
'terms_version_id' => $terms['id'],
|
||||
// Limpiar forzar_reenvio para este usuario (lo manejamos por versión)
|
||||
], 'id = :id', ['id' => $user['id']]);
|
||||
} catch (Exception $e) {
|
||||
error_log('[BotService] processTermsResponse – update user accepted: ' . $e->getMessage());
|
||||
}
|
||||
|
||||
// Continuar con el flujo normal (welcome si es nuevo)
|
||||
if ($this->isNewUser($user['id'])) {
|
||||
$this->sendWelcomeMessage($phoneNumber);
|
||||
} else {
|
||||
// Ya había interactuado antes → solo confirmar y mostrar menú
|
||||
$this->showMainMenu($phoneNumber);
|
||||
}
|
||||
} else {
|
||||
// Rechazó — enviar mensaje de rechazo y dejar terms_pending=1 para que pueda aceptar después
|
||||
$rejMsg = !empty($terms['mensaje_rechazo'])
|
||||
? $terms['mensaje_rechazo']
|
||||
: getConfigFromDB('terms_rejected_message', 'Lamentablemente no podemos proceder sin tu aceptación. Cuando estés listo, responde *Acepto*.');
|
||||
|
||||
$this->whatsappService->sendTextMessage($phoneNumber, $rejMsg);
|
||||
|
||||
// Mantener terms_pending = 1 para que la próxima respuesta sea evaluada
|
||||
}
|
||||
|
||||
error_log("[BotService] processTermsResponse – user {$user['id']} {$estado} terms v{$terms['version']}");
|
||||
return true;
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// FIN TÉRMINOS Y CONDICIONES
|
||||
// =========================================================================
|
||||
|
||||
/**
|
||||
* Verificar si es un nuevo usuario
|
||||
* Ahora: reenvía el welcome si no se envió o si el último welcome fue hace >= 6 horas
|
||||
|
||||
Reference in New Issue
Block a user