532 lines
25 KiB
PHP
532 lines
25 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace SmsNotifier\Facade;
|
|
|
|
use GuzzleHttp\Client;
|
|
use SmsNotifier\Data\NotificationData;
|
|
use SmsNotifier\Facade\ClientCallBellAPI;
|
|
use SmsNotifier\Factory\MessageTextFactory;
|
|
use SmsNotifier\Service\Logger;
|
|
use SmsNotifier\Service\SmsNumberProvider;
|
|
use Ubnt\UcrmPluginSdk\Service\UcrmApi;
|
|
use Ubnt\UcrmPluginSdk\Service\PluginConfigManager;
|
|
use \DateTime;
|
|
|
|
abstract class AbstractMessageNotifierFacade
|
|
{
|
|
protected $logger;
|
|
protected $messageTextFactory;
|
|
protected $clientPhoneNumber;
|
|
protected $ucrmApi;
|
|
|
|
const SUBJECT_OF_INSTALLER_CHANGE = ["se ha cancelado una tarea que tenías asignada con el folio ", "se te ha desasignado❌ la tarea con el folio "];
|
|
const ADDITIONAL_CHANGE_DATA = ["Ya no es necesario realizar la visita técnica.", "En tu lugar asistirá el técnico 👷🏻♂️➡️ "];
|
|
|
|
public function __construct(Logger $logger, MessageTextFactory $messageTextFactory, SmsNumberProvider $clientPhoneNumber)
|
|
{
|
|
$this->logger = $logger;
|
|
$this->messageTextFactory = $messageTextFactory;
|
|
$this->clientPhoneNumber = $clientPhoneNumber;
|
|
|
|
$config = PluginConfigManager::create()->loadConfig();
|
|
$ipServer = $config['ipserver'] ?? 'localhost';
|
|
$apiUrl = "https://$ipServer/crm/api/v1.0/";
|
|
|
|
$client = new Client([
|
|
'base_uri' => $apiUrl,
|
|
'verify' => false,
|
|
]);
|
|
|
|
$this->ucrmApi = new UcrmApi($client, $config['apitoken'] ?? '');
|
|
}
|
|
|
|
public function verifyPaymentActionToDo(NotificationData $notificationData): void
|
|
{
|
|
$arrayPhones = $this->clientPhoneNumber->getUcrmClientNumbers($notificationData, null);
|
|
foreach ($arrayPhones as $type => $phones) {
|
|
$type = trim(strtolower($type));
|
|
if (!is_array($phones)) continue;
|
|
foreach ($phones as $phone) {
|
|
switch ($type) {
|
|
case 'whatsapp':
|
|
$this->notifyAndUpdate($notificationData, $phone);
|
|
break;
|
|
case 'whatsnotifica':
|
|
$this->notify($notificationData, $phone);
|
|
break;
|
|
case 'whatsactualiza':
|
|
$this->onlyUpdate($notificationData, $phone);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public function verifyClientActionToDo(NotificationData $notificationData): void
|
|
{
|
|
$arrayPhones = $this->clientPhoneNumber->getUcrmClientNumbers($notificationData, null);
|
|
foreach ($arrayPhones as $type => $phones) {
|
|
$type = trim(strtolower($type));
|
|
if (!is_array($phones)) continue;
|
|
foreach ($phones as $phone) {
|
|
if ($type === 'whatsapp' || $type === 'whatsactualiza') $this->onlyUpdate($notificationData, $phone);
|
|
}
|
|
}
|
|
}
|
|
|
|
public function verifyServiceActionToDo(NotificationData $notificationData): void
|
|
{
|
|
$arrayPhones = $this->clientPhoneNumber->getUcrmClientNumbers($notificationData, null);
|
|
foreach ($arrayPhones as $type => $phones) {
|
|
$type = trim(strtolower($type));
|
|
if (!is_array($phones)) continue;
|
|
foreach ($phones as $phone) {
|
|
if ($type === 'whatsapp' || $type === 'whatsactualiza') $this->onlyUpdateService($notificationData, $phone);
|
|
}
|
|
}
|
|
}
|
|
|
|
public function verifyJobActionToDo($jsonNotificationData, $reprogramming = null, $changeInstaller = null): void
|
|
{
|
|
$this->logger->info('Iniciando verifyJobActionToDo');
|
|
$clientId = $jsonNotificationData['extraData']['entity']['clientId'];
|
|
$installerId = $jsonNotificationData['extraData']['entity']['assignedUserId'];
|
|
$jobId = $jsonNotificationData['entityId'];
|
|
|
|
$dateString = $jsonNotificationData['extraData']['entity']['date'] ?? null;
|
|
$formattedDate = $dateString ? sprintf("*%s*", (new DateTime($dateString))->format('d/m/Y')) : '';
|
|
|
|
$config = PluginConfigManager::create()->loadConfig();
|
|
$admin = $this->ucrmApi->get("users/admins/$installerId", []);
|
|
$installerName = trim(($admin['firstName'] ?? '') . ' ' . ($admin['lastName'] ?? ''));
|
|
|
|
$installerWhatsApp = '';
|
|
$installers = json_decode($config['installersDataWhatsApp'] ?? '{"instaladores":[]}', true);
|
|
foreach ($installers['instaladores'] as $inst) {
|
|
if ($inst['id'] == $installerId) {
|
|
$installerWhatsApp = $inst['whatsapp'];
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (empty($installerWhatsApp)) $this->logger->warning("No se encontró número de WhatsApp para el instalador ID: $installerId");
|
|
|
|
$clientCRM = $this->ucrmApi->get("clients/$clientId", []);
|
|
$clientName = trim(($clientCRM['firstName'] ?? '') . ' ' . ($clientCRM['lastName'] ?? ''));
|
|
$passCRM = '';
|
|
foreach ($clientCRM['attributes'] as $attr) {
|
|
if ($attr['key'] === 'passwordAntenaCliente') {
|
|
$passCRM = $attr['value'];
|
|
break;
|
|
}
|
|
}
|
|
|
|
$allPhones = $this->clientPhoneNumber->getAllUcrmClientNumbers($clientCRM);
|
|
$phonesStr = implode(', ', array_map(fn($n) => $this->validarNumeroTelefono($n), $allPhones));
|
|
|
|
$api = new ClientCallBellAPI($config['apitoken'], $config['ipserver'], $config['tokencallbell']);
|
|
|
|
$title = $jsonNotificationData['extraData']['entity']['title'] ?? '';
|
|
$isPending = (stripos($title, '[NOTIFICACION-PENDIENTE]') !== false);
|
|
$isNoWhatsApp = (stripos($title, '[CLIENTE-SIN-WHATSAPP]') !== false);
|
|
|
|
$reprogramming = filter_var($reprogramming, FILTER_VALIDATE_BOOLEAN);
|
|
$changeInstaller = filter_var($changeInstaller, FILTER_VALIDATE_BOOLEAN);
|
|
|
|
$clientPhones = $this->clientPhoneNumber->getUcrmClientNumbers(null, $clientCRM);
|
|
$hasClientWhatsApp = false;
|
|
foreach ($clientPhones as $type => $phones) {
|
|
$type = trim(strtolower($type));
|
|
if (($type === 'whatsapp' || $type === 'whatsnotifica') && !empty($phones)) {
|
|
$hasClientWhatsApp = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
$shouldNotifyTech = ($isPending || $reprogramming || $changeInstaller);
|
|
$shouldNotifyClient = ($isPending || $isNoWhatsApp || $reprogramming || $changeInstaller);
|
|
|
|
// 1. Notificar al Instalador Anterior (Desasignación)
|
|
if ($changeInstaller) {
|
|
$prevId = $jsonNotificationData['extraData']['entityBeforeEdit']['assignedUserId'];
|
|
$prevAdmin = $this->ucrmApi->get("users/admins/$prevId", []);
|
|
$prevWhatsApp = '';
|
|
foreach ($installers['instaladores'] as $inst) {
|
|
if ($inst['id'] == $prevId) {
|
|
$prevWhatsApp = $inst['whatsapp'];
|
|
break;
|
|
}
|
|
}
|
|
if ($prevWhatsApp) {
|
|
$api->sendJobNotificationWhatsAppToInstaller($this->validarNumeroTelefono($prevWhatsApp), [
|
|
"installerName" => "👷🏻♂️" . trim(($prevAdmin['firstName'] ?? '') . ' ' . ($prevAdmin['lastName'] ?? '')),
|
|
"subjectOfChange" => self::SUBJECT_OF_INSTALLER_CHANGE[1],
|
|
"jobId" => $jobId,
|
|
"clientFullName" => "[$clientId] $clientName",
|
|
"additionalChangeData" => self::ADDITIONAL_CHANGE_DATA[1] . ' *' . $installerName . '*',
|
|
], $reprogramming, $changeInstaller);
|
|
sleep(1);
|
|
}
|
|
}
|
|
|
|
// 2. Notificar al Cliente (si aplica)
|
|
$clientNotified = false;
|
|
if ($shouldNotifyClient && $hasClientWhatsApp) {
|
|
foreach ($clientPhones as $type => $phones) {
|
|
$type = trim(strtolower($type));
|
|
if (!is_array($phones) || ($type !== 'whatsapp' && $type !== 'whatsnotifica')) continue;
|
|
foreach ($phones as $phone) {
|
|
if ($api->sendJobNotificationWhatsAppToClient($this->validarNumeroTelefono($phone), ["clientFullName" => $clientName, "jobId" => $jobId, "date" => $formattedDate, "installerName" => $installerName], $reprogramming, $changeInstaller)) {
|
|
$clientNotified = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// 2. Notificar al Técnico (si aplica)
|
|
if ($shouldNotifyTech) {
|
|
$passVault = $this->getVaultCredentialsByClientId($clientId);
|
|
$api->sendJobNotificationWhatsAppToInstaller($this->validarNumeroTelefono($installerWhatsApp), [
|
|
"installerName" => $installerName,
|
|
"clientFullName" => "$clientName [ID:$clientId]",
|
|
"jobId" => $jobId,
|
|
"clientAddress" => $clientCRM['fullAddress'] ?? 'N/A',
|
|
"clientWhatsApp" => !empty($phonesStr) ? $phonesStr : 'Sin WhatsApp',
|
|
"date" => $formattedDate,
|
|
"jobDescription" => $jsonNotificationData['extraData']['entity']['description'] ?? 'S/D',
|
|
"gmapsLocation" => ($clientCRM['addressGpsLat'] && $clientCRM['addressGpsLon']) ? "https://www.google.com/maps?q={$clientCRM['addressGpsLat']},{$clientCRM['addressGpsLon']}" : 'N/A',
|
|
"passwordAntenaCliente" => $this->comparePasswords($passCRM, $passVault)
|
|
], $reprogramming, false);
|
|
}
|
|
|
|
// 3. Gestión del Título / Prefijos
|
|
if ($isPending) {
|
|
if ($clientNotified || $reprogramming || $changeInstaller) {
|
|
$newTitle = str_ireplace('[NOTIFICACION-PENDIENTE]', '', $title);
|
|
$this->ucrmApi->patch("scheduling/jobs/$jobId", ['title' => trim($newTitle)]);
|
|
} else if (!$hasClientWhatsApp) {
|
|
$newTitle = str_ireplace('[NOTIFICACION-PENDIENTE]', '[CLIENTE-SIN-WHATSAPP]', $title);
|
|
$this->ucrmApi->patch("scheduling/jobs/$jobId", ['title' => trim($newTitle)]);
|
|
}
|
|
} else if ($isNoWhatsApp && ($clientNotified || $reprogramming || $changeInstaller)) {
|
|
$newTitle = str_ireplace('[CLIENTE-SIN-WHATSAPP]', '', $title);
|
|
$this->ucrmApi->patch("scheduling/jobs/$jobId", ['title' => trim($newTitle)]);
|
|
}
|
|
}
|
|
|
|
public function verifyInvoiceActionToDo(NotificationData $notificationData): void
|
|
{
|
|
$arrayPhones = $this->clientPhoneNumber->getUcrmClientNumbers($notificationData, null);
|
|
foreach ($arrayPhones as $type => $phones) {
|
|
$type = trim(strtolower($type));
|
|
if (!is_array($phones)) continue;
|
|
foreach ($phones as $phone) {
|
|
if ($type === 'whatsapp' || $type === 'whatsactualiza') $this->onlyUpdate($notificationData, $phone, false);
|
|
}
|
|
}
|
|
}
|
|
|
|
public function notify(NotificationData $notificationData, $phoneToNotify = null): void
|
|
{
|
|
$config = PluginConfigManager::create()->loadConfig();
|
|
$api = new ClientCallBellAPI($config['apitoken'], $config['ipserver'], $config['tokencallbell']);
|
|
$phone = $this->validarNumeroTelefono($phoneToNotify);
|
|
if (!$phone) return;
|
|
if ($config['notificationTypeText'] ?? false) $api->sendTextPaymentNotificationWhatsApp($phone, $notificationData);
|
|
else $api->sendPaymentNotificationWhatsApp($phone, $notificationData);
|
|
}
|
|
|
|
public function notifyAndUpdate(NotificationData $notificationData, $phoneToNotifyAndUpdate = null): void
|
|
{
|
|
$config = PluginConfigManager::create()->loadConfig();
|
|
$api = new ClientCallBellAPI($config['apitoken'], $config['ipserver'], $config['tokencallbell']);
|
|
$phone = $this->validarNumeroTelefono($phoneToNotifyAndUpdate);
|
|
if (!$phone) return;
|
|
if ($config['notificationTypeText'] ?? false) {
|
|
if ($api->sendTextPaymentNotificationWhatsApp($phone, $notificationData)) {
|
|
$contact = json_decode($api->getContactWhatsapp($phone), true);
|
|
if ($contact) $api->patchWhatsapp($contact, $notificationData);
|
|
}
|
|
} else {
|
|
if ($api->sendPaymentNotificationWhatsApp($phone, $notificationData)) {
|
|
$contact = json_decode($api->getContactWhatsapp($phone), true);
|
|
if ($contact) $api->patchWhatsapp($contact, $notificationData);
|
|
}
|
|
}
|
|
}
|
|
|
|
public function notifyOverDue(NotificationData $notificationData): void
|
|
{
|
|
$config = PluginConfigManager::create()->loadConfig();
|
|
$api = new ClientCallBellAPI($config['apitoken'], $config['ipserver'], $config['tokencallbell']);
|
|
$phone = $this->clientPhoneNumber->getUcrmClientNumber($notificationData);
|
|
if ($phone) $api->sendOverdueNotificationWhatsApp($phone, $notificationData);
|
|
}
|
|
|
|
public function onlyUpdate(NotificationData $notificationData, $phoneToUpdate): void
|
|
{
|
|
$this->logger->debug("onlyUpdate: Iniciando actualización para teléfono: $phoneToUpdate");
|
|
$config = PluginConfigManager::create()->loadConfig();
|
|
$api = new ClientCallBellAPI($config['apitoken'], $config['ipserver'], $config['tokencallbell']);
|
|
$phone = $this->validarNumeroTelefono($phoneToUpdate);
|
|
$this->logger->debug("onlyUpdate: Teléfono validado: $phone");
|
|
$contact = json_decode($api->getContactWhatsapp($phone), true);
|
|
$this->logger->debug("onlyUpdate: Contacto obtenido de CallBell: " . json_encode($contact));
|
|
if ($contact) {
|
|
$this->logger->info("onlyUpdate: Ejecutando patchWhatsapp para teléfono: $phone");
|
|
$api->patchWhatsapp($contact, $notificationData);
|
|
} else {
|
|
$this->logger->warning("onlyUpdate: No se encontró contacto en CallBell para teléfono: $phone");
|
|
}
|
|
}
|
|
|
|
public function onlyUpdateService(NotificationData $notificationData, $phoneToUpdate): void
|
|
{
|
|
$config = PluginConfigManager::create()->loadConfig();
|
|
$api = new ClientCallBellAPI($config['apitoken'], $config['ipserver'], $config['tokencallbell']);
|
|
$phone = $this->validarNumeroTelefono($phoneToUpdate);
|
|
$contact = json_decode($api->getContactWhatsapp($phone), true);
|
|
if ($contact) $api->patchServiceStatusWhatsApp($contact, $notificationData);
|
|
}
|
|
|
|
protected function getVaultCredentialsByClientId($clientId): string
|
|
{
|
|
$config = PluginConfigManager::create()->loadConfig();
|
|
$ipServer = $config['ipserver'] ?? '';
|
|
$crm = new Client(['base_uri' => "https://{$ipServer}/crm/api/v1.0/", 'verify' => false]);
|
|
|
|
try {
|
|
// OPT: Lazy Check - Si ya tiene pass válido en CRM, no hace falta procesar nada
|
|
$respClient = $crm->get("clients/$clientId", ['headers' => ['X-Auth-Token' => $config['apitoken']]]);
|
|
$clientData = json_decode($respClient->getBody()->getContents(), true);
|
|
$passCRM = '';
|
|
if (isset($clientData['attributes'])) {
|
|
foreach ($clientData['attributes'] as $attr) {
|
|
if ($attr['key'] === 'passwordAntenaCliente') {
|
|
$passCRM = $attr['value'] ?? '';
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Si el campo no está vacío y no tiene advertencias, usamos el actual para ahorrar recursos
|
|
if (!empty($passCRM) && strpos($passCRM, '⚠️') === false) {
|
|
return $passCRM;
|
|
}
|
|
|
|
// 1. Obtener los servicios del cliente
|
|
$respSvc = $crm->get('clients/services?clientId=' . $clientId, [
|
|
'headers' => ['X-Auth-Token' => $config['apitoken']]
|
|
]);
|
|
$svcs = json_decode($respSvc->getBody()->getContents(), true);
|
|
|
|
if (empty($svcs)) {
|
|
$msg = '⚠️ Cliente sin servicios/antenas';
|
|
$this->syncPasswordWithCrm((int)$clientId, $msg);
|
|
return $msg;
|
|
}
|
|
|
|
$unms = new Client(['base_uri' => "https://{$ipServer}/nms/api/v2.1/", 'verify' => false]);
|
|
$allServicePasswords = [];
|
|
$isTestEnv = ($ipServer === '172.16.5.134' || $ipServer === 'pruebas.internet.mx' || $ipServer === 'venus.siip.mx');
|
|
|
|
$numServices = count($svcs);
|
|
foreach ($svcs as $index => $svc) {
|
|
$label = ($numServices > 1) ? "Servicio " . ($index + 1) . ":" : "";
|
|
$siteId = $svc['unmsClientSiteId'] ?? null;
|
|
$passwordValue = "";
|
|
|
|
if (!$siteId) {
|
|
$passwordValue = "⚠️ Sin sitio";
|
|
} else {
|
|
if ($isTestEnv) {
|
|
// Lógica de bypass: intentar recuperar de la cadena existente
|
|
$foundInCRM = false;
|
|
if (!empty($passCRM)) {
|
|
if ($numServices > 1) {
|
|
if (preg_match('/Servicio ' . ($index + 1) . ':\s*([^⚠️\s]+)/', $passCRM, $matches)) {
|
|
$passwordValue = $matches[1];
|
|
$foundInCRM = true;
|
|
}
|
|
} else {
|
|
// Caso de un solo servicio: si no tiene advertencias ni etiquetas, la tomamos a secas
|
|
if (strpos($passCRM, '⚠️') === false && strpos($passCRM, 'Servicio') === false) {
|
|
$passwordValue = trim($passCRM);
|
|
$foundInCRM = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!$foundInCRM) {
|
|
$passwordValue = $this->generateStrongPassword(16);
|
|
}
|
|
} else {
|
|
// Lógica de producción
|
|
try {
|
|
$respDev = $unms->get("devices?siteId=$siteId", [
|
|
'headers' => ['X-Auth-Token' => $config['unmsApiToken']]
|
|
]);
|
|
$devs = json_decode($respDev->getBody()->getContents(), true);
|
|
|
|
if (empty($devs)) {
|
|
$passwordValue = "⚠️ Sin antena";
|
|
} else {
|
|
$passVault = null;
|
|
$firstDeviceId = null;
|
|
foreach ($devs as $dev) {
|
|
$deviceId = $dev['identification']['id'] ?? null;
|
|
if (!$deviceId) continue;
|
|
if (!$firstDeviceId) $firstDeviceId = $deviceId;
|
|
try {
|
|
$respVault = $unms->get("vault/$deviceId/credentials", [
|
|
'headers' => ['X-Auth-Token' => $config['unmsApiToken']]
|
|
]);
|
|
$vault = json_decode($respVault->getBody()->getContents(), true);
|
|
if (isset($vault['credentials'][0]['password'])) {
|
|
$passVault = $vault['credentials'][0]['password'];
|
|
break;
|
|
}
|
|
} catch (\Exception $e) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if ($passVault) {
|
|
$passwordValue = $passVault;
|
|
} else if ($firstDeviceId) {
|
|
// Regenerar
|
|
$newPass = $this->generateStrongPassword(16);
|
|
try {
|
|
$unms->post("vault/$firstDeviceId/credentials/regenerate", [
|
|
'headers' => ['X-Auth-Token' => $config['unmsApiToken']],
|
|
'json' => [['username' => 'ubnt', 'password' => $newPass, 'readOnly' => true]]
|
|
]);
|
|
$passwordValue = $newPass;
|
|
} catch (\Exception $e) {
|
|
$passwordValue = $newPass;
|
|
}
|
|
} else {
|
|
$passwordValue = "⚠️ Sin antena";
|
|
}
|
|
}
|
|
} catch (\Exception $e) {
|
|
$passwordValue = "⚠️ Error API";
|
|
}
|
|
}
|
|
}
|
|
$allServicePasswords[] = trim("$label $passwordValue");
|
|
}
|
|
|
|
$finalValue = implode(' ', $allServicePasswords);
|
|
|
|
// Evitar sincronización redundante
|
|
if ($finalValue === $passCRM) {
|
|
return $finalValue;
|
|
}
|
|
|
|
$this->syncPasswordWithCrm((int)$clientId, $finalValue);
|
|
return $finalValue;
|
|
} catch (\Exception $e) {
|
|
$this->logger->error("Error en getVaultCredentialsByClientId: " . $e->getMessage());
|
|
return 'Error: ' . $e->getMessage();
|
|
}
|
|
}
|
|
|
|
private function syncPasswordWithCrm(int $clientId, string $passVault): void
|
|
{
|
|
$config = PluginConfigManager::create()->loadConfig();
|
|
$crm = new Client(['base_uri' => "https://{$config['ipserver']}/crm/api/v1.0/", 'verify' => false]);
|
|
try {
|
|
$respClient = $crm->get("clients/$clientId", [
|
|
'headers' => ['X-Auth-Token' => $config['apitoken']]
|
|
]);
|
|
$clientData = json_decode($respClient->getBody()->getContents(), true);
|
|
$passCRM = '';
|
|
$attributeId = 17;
|
|
if (isset($clientData['attributes'])) {
|
|
foreach ($clientData['attributes'] as $attr) {
|
|
if ($attr['key'] === 'passwordAntenaCliente') {
|
|
$passCRM = $attr['value'] ?? '';
|
|
$attributeId = $attr['customAttributeId'];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (empty($passCRM) || $passCRM !== $passVault) {
|
|
$this->logger->info("Sincronizando pass CRM cliente $clientId.");
|
|
$this->patchClientCustomAttribute($clientId, (int)$attributeId, $passVault);
|
|
}
|
|
} catch (\Exception $e) {
|
|
$this->logger->warning("Fallo sincronización pass CRM: " . $e->getMessage());
|
|
}
|
|
}
|
|
|
|
protected function generateStrongPassword(int $length = 16): string
|
|
{
|
|
$lower = 'abcdefghijkmnopqrstuvwxyz'; // Eliminamos 'l'
|
|
$upper = 'ABCDEFGHJKLMNPQRSTUVWXYZ'; // Eliminamos 'I', 'O'
|
|
$digits = '23456789'; // Eliminamos '1', '0'
|
|
$symbols = '@#'; // Solo símbolos amigables para impresoras térmicas
|
|
|
|
$all = $lower . $upper . $digits . $symbols;
|
|
$pwChars = [];
|
|
|
|
// Asegurar que tenga al menos uno de cada tipo si es posible
|
|
$pwChars[] = $lower[random_int(0, strlen($lower) - 1)];
|
|
$pwChars[] = $upper[random_int(0, strlen($upper) - 1)];
|
|
$pwChars[] = $digits[random_int(0, strlen($digits) - 1)];
|
|
$pwChars[] = $symbols[random_int(0, strlen($symbols) - 1)];
|
|
|
|
for ($i = count($pwChars); $i < $length; $i++) {
|
|
$pwChars[] = $all[random_int(0, strlen($all) - 1)];
|
|
}
|
|
|
|
// Mezclar Fisher-Yates
|
|
$n = count($pwChars);
|
|
for ($i = $n - 1; $i > 0; $i--) {
|
|
$j = random_int(0, $i);
|
|
$tmp = $pwChars[$i];
|
|
$pwChars[$i] = $pwChars[$j];
|
|
$pwChars[$j] = $tmp;
|
|
}
|
|
|
|
return implode('', $pwChars);
|
|
}
|
|
|
|
protected function patchClientCustomAttribute(int $clientId, int $attributeId, string $value): bool
|
|
{
|
|
$config = PluginConfigManager::create()->loadConfig();
|
|
$crm = new Client(['base_uri' => "https://{$config['ipserver']}/crm/api/v1.0/", 'verify' => false]);
|
|
try {
|
|
$crm->patch("clients/$clientId", [
|
|
'headers' => ['X-Auth-Token' => $config['apitoken']],
|
|
'json' => [
|
|
"attributes" => [['value' => $value, 'customAttributeId' => $attributeId]]
|
|
]
|
|
]);
|
|
return true;
|
|
} catch (\Exception $e) {
|
|
$this->logger->error("Error patching custom attribute for client $clientId: " . $e->getMessage());
|
|
return false;
|
|
}
|
|
}
|
|
|
|
protected function comparePasswords(?string $crm, ?string $vault): string
|
|
{
|
|
if ($vault && strpos($vault, 'Error') !== 0) return $vault;
|
|
if ($crm && strpos($crm, 'Error') !== 0) return $crm;
|
|
return '⚠️ Probar pass conocida.';
|
|
}
|
|
|
|
protected function validarNumeroTelefono($n): string
|
|
{
|
|
if (!$n) return '';
|
|
$n = preg_replace('/\D/', '', (string)$n);
|
|
return (strlen($n) === 10) ? '52' . $n : $n;
|
|
}
|
|
|
|
abstract protected function sendWhatsApp(NotificationData $notificationData, string $clientSmsNumber): void;
|
|
}
|