From 94b9c9f60ecd8139f0cdfa034a567fc713831a3d Mon Sep 17 00:00:00 2001 From: Alexey Portnov Date: Tue, 3 Feb 2026 22:19:41 +0800 Subject: [PATCH 01/19] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=B8=D0=BB=20=D0=BE=D1=82=D0=BE=D0=B1=D1=80=D0=B0=D0=B6=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D0=B5=20=D0=BD=D0=BE=D0=BC=D0=B5=D1=80=D0=B0=20?= =?UTF-8?q?=D1=81=D0=BE=D0=B1=D0=B5=D1=81=D0=B5=D0=B4=D0=BD=D0=B8=D0=BA?= =?UTF-8?q?=D0=B0=20=D0=BF=D1=80=D0=B8=20=D0=B7=D0=B2=D0=BE=D0=BD=D0=BA?= =?UTF-8?q?=D0=B0=D1=85=20=D1=87=D0=B5=D1=80=D0=B5=D0=B7=20=D1=86=D0=B5?= =?UTF-8?q?=D0=BF=D0=BE=D1=87=D0=BA=D1=83=20Local-=D0=BA=D0=B0=D0=BD=D0=B0?= =?UTF-8?q?=D0=BB=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit enrichStatesWithConnections теперь проходит цепочку бриджей через findBridgeChannel вместо прямого lookup партнёра, что корректно разрешает номер при переводах через очереди. --- bin/WorkerActiveCalls.php | 56 +++++++++++++++++---------------------- 1 file changed, 25 insertions(+), 31 deletions(-) diff --git a/bin/WorkerActiveCalls.php b/bin/WorkerActiveCalls.php index 0fe2a42..dbf1f0a 100644 --- a/bin/WorkerActiveCalls.php +++ b/bin/WorkerActiveCalls.php @@ -457,33 +457,11 @@ private function enrichStatesWithConnections(): array { $states = $this->states; - // Строим карту channel -> [linkedId, connectedChannel, connectedNumber] - $channelConnections = []; - foreach ($this->activeBridges as $linkedId => $bridges) { - foreach ($bridges as $bridgeChannels) { - $channelList = array_keys($bridgeChannels); - if (count($channelList) !== 2) { - continue; - } - $ch1 = $channelList[0]; - $ch2 = $channelList[1]; - $num1 = $this->activeChannels[$linkedId][$ch1]['CallerIDNum'] ?? ''; - $num2 = $this->activeChannels[$linkedId][$ch2]['CallerIDNum'] ?? ''; - - $channelConnections[$ch1] = ['channel' => $ch2, 'number' => $num2]; - $channelConnections[$ch2] = ['channel' => $ch1, 'number' => $num1]; - } - } - - // Строим карту channel -> ConnectedLineNum из activeChannels - $channelToConnectedLine = []; + // Строим карту channel -> linkedId + $channelToLinkedId = []; foreach ($this->activeChannels as $linkedId => $channels) { - foreach ($channels as $channel => $data) { - $connectedNum = $data['ConnectedLineNum'] ?? ''; - // Фильтруем и подобные значения - if (!empty($connectedNum) && strpos($connectedNum, '<') === false) { - $channelToConnectedLine[$channel] = $connectedNum; - } + foreach (array_keys($channels) as $channel) { + $channelToLinkedId[$channel] = $linkedId; } } @@ -494,12 +472,28 @@ private function enrichStatesWithConnections(): array } $enrichedChannels = []; foreach (array_keys($stateData['channels']) as $channel) { - if (isset($channelConnections[$channel])) { - $enrichedChannels[$channel] = $channelConnections[$channel]; + $linkedId = $channelToLinkedId[$channel] ?? ''; + if (empty($linkedId)) { + $enrichedChannels[$channel] = ['channel' => '', 'number' => '']; + continue; + } + + // Проходим цепочку бриджей через Local-каналы до реального PJSIP-канала + $resolvedChannel = $channel; + $bridgeStart = time(); + $found = $this->findBridgeChannel($linkedId, $resolvedChannel, $bridgeStart); + + if ($found && $resolvedChannel !== $channel) { + $number = $this->activeChannels[$linkedId][$resolvedChannel]['CallerIDNum'] ?? ''; + $enrichedChannels[$channel] = ['channel' => $resolvedChannel, 'number' => $number]; } else { - // Канал не в бридже (звонит/ожидает) - используем ConnectedLineNum - $connectedNum = $channelToConnectedLine[$channel] ?? ''; - $enrichedChannels[$channel] = ['channel' => '', 'number' => $connectedNum]; + // Канал не в бридже (звонит/ожидает) — используем ConnectedLineNum + $connectedNum = $this->activeChannels[$linkedId][$channel]['ConnectedLineNum'] ?? ''; + if (!empty($connectedNum) && strpos($connectedNum, '<') === false) { + $enrichedChannels[$channel] = ['channel' => '', 'number' => $connectedNum]; + } else { + $enrichedChannels[$channel] = ['channel' => '', 'number' => '']; + } } } $stateData['channels'] = $enrichedChannels; From 1de336a7d9fafdbbac57407c1a737c8563668b77 Mon Sep 17 00:00:00 2001 From: Alexey Portnov Date: Wed, 4 Feb 2026 00:30:32 +0800 Subject: [PATCH 02/19] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=B8=D0=BB=20=D0=BE=D0=BF=D1=80=D0=B5=D0=B4=D0=B5=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D0=B5=20src=5Fchan=20=D0=BF=D1=80=D0=B8=20collectA?= =?UTF-8?q?ctiveChannels=20=D0=B8=20=D0=B1=D0=B5=D1=81=D0=BA=D0=BE=D0=BD?= =?UTF-8?q?=D0=B5=D1=87=D0=BD=D1=8B=D0=B9=20=D1=86=D0=B8=D0=BA=D0=BB=20?= =?UTF-8?q?=D0=BF=D0=B5=D1=80=D0=B5=D0=B7=D0=B0=D0=BF=D1=83=D1=81=D0=BA?= =?UTF-8?q?=D0=BE=D0=B2=20=D0=B2=D0=BE=D1=80=D0=BA=D0=B5=D1=80=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - collectActiveChannels: добавлена проверка Uniqueid === Linkedid для корректного определения инициатора звонка при follow-me на мобильный (порядок обхода каналов не гарантирован, исходящий транк мог ошибочно стать src_chan) - Главный цикл: while(true) заменён на while($this->needRestart === false), чтобы воркер корректно завершался по SIGUSR1 без 3-минутного ожидания SIGTERM --- bin/WorkerActiveCalls.php | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/bin/WorkerActiveCalls.php b/bin/WorkerActiveCalls.php index dbf1f0a..70b03f8 100644 --- a/bin/WorkerActiveCalls.php +++ b/bin/WorkerActiveCalls.php @@ -234,7 +234,7 @@ public function start($argv):void $this->init = false; $this->printActiveCalls(); $this->logger->writeInfo('Wait events...'); - while (true) { + while ($this->needRestart === false) { try { $this->amCustom->waitUserEvent(true); if (!$this->amCustom->loggedIn()) { @@ -705,6 +705,27 @@ private function collectActiveChannels():void 'answer' => strtotime($this->amCustom->GetVar($channel, 'CDR(answer)','', false)) ]; } + } elseif ($chanData['Uniqueid'] === $linkedId) { + // Этот канал — инициатор звонка (Uniqueid == Linkedid). + // Переопределяем src_chan, т.к. порядок обхода каналов не гарантирован. + if($chanData['Type'] === self::ENDPOINT_TYPE_PROVIDER){ + $callType = self::CALL_TYPE_IN; + }elseif ($chanData['Type'] === self::ENDPOINT_TYPE_PEER && !empty($did)){ + $callType = ''; + }elseif ($chanData['Type'] === self::ENDPOINT_TYPE_PEER && empty($did)){ + $callType = self::CALL_TYPE_OUT; + }else{ + $callType = self::CALL_TYPE_INNER; + } + if(!empty($callType)){ + $this->callType[$linkedId] = [ + 'type' => $callType, + 'src_chan' => $channel, + 'did' => $did, + 'time' => str_replace('mikopbx-','',$chanData['Uniqueid']), + 'answer' => strtotime($this->amCustom->GetVar($channel, 'CDR(answer)','', false)) + ]; + } } if($chanData['Type'] === self::ENDPOINT_TYPE_PEER){ $this->states[$endpoint]['channels'][$channel] = true; From 17f85d6dcaf0fdfda7bc85a0353010ac32fd3f64 Mon Sep 17 00:00:00 2001 From: Alexey Portnov Date: Wed, 4 Feb 2026 04:32:18 +0800 Subject: [PATCH 03/19] =?UTF-8?q?=D0=97=D0=B0=D0=BC=D0=B5=D0=BD=D0=B8?= =?UTF-8?q?=D0=BB=20CHECK=5FBY=5FAMI=20=D0=BD=D0=B0=20state-file=20=D0=BA?= =?UTF-8?q?=D0=BE=D0=BD=D1=82=D1=80=D0=BE=D0=BB=D1=8C=20=D0=B7=D0=B4=D0=BE?= =?UTF-8?q?=D1=80=D0=BE=D0=B2=D1=8C=D1=8F=20WorkerActiveCalls?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Проблема: WorkerSafeScriptsCore пинговал воркер через AMI во время тяжёлой инициализации (collectActiveChannels — 7-8 GetVar на канал), воркер не успевал ответить на ping, получал SIGUSR1 и уходил в бесконечный цикл рестартов каждые ~3 минуты. Решение: - CHECK_BY_AMI → CHECK_BY_PID_NOT_ALERT (core не пингует через AMI) - Воркер пишет state-файл (/tmp/MonitorActiveCalls_worker.state) с pid, timestamp и status (starting/running) - AsteriskManager: idle callback в waitUserEvent — вызывается каждые 30 сек когда socket timeout + AMI ping OK (доказывает что процесс жив и AMI-соединение работает) - safe.php: проверяет state-файл — status=starting допускает 120 сек, status=running допускает 90 сек без обновления, иначе kill + restart --- CLAUDE.md | 233 +++++++++++++++++++++++++++++++++ Lib/AsteriskManager.php | 27 ++++ Lib/MonitorActiveCallsConf.php | 2 +- bin/WorkerActiveCalls.php | 25 ++++ bin/safe.php | 92 +++++++++++-- 5 files changed, 368 insertions(+), 11 deletions(-) create mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..063675c --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,233 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## КРИТИЧЕСКИ ВАЖНО: Запреты при разработке + +**КАТЕГОРИЧЕСКИ ЗАПРЕЩЕНО:** +1. **Изменять файлы вне директории модуля** - никогда не модифицировать файлы в `/offload/`, `/usr/www/src/`, или других системных директориях MikoPBX +2. **Использовать rsync, cp -r или tar для установки модуля** - это может перезаписать системные файлы MikoPBX +3. **Удалять директорию модуля целиком** (`rm -rf /storage/.../ModuleMonitorActiveCalls`) - это удалит базу данных модуля + +## Требования к коду + +**Совместимость:** +- Код должен быть совместим с **PHP 7.4** и **PHP 8.x** +- Код должен работать с **Phalcon 4.x** и **Phalcon 5.x** (используйте `MikoPBXVersion`) + +**Избегайте (только PHP 8+):** +- `match` выражения → используйте `switch` +- Union types `function foo(): int|string` → используйте PHPDoc +- Named arguments `foo(name: $value)` +- Constructor property promotion +- Nullsafe operator `?->` +- `str_contains()`, `str_starts_with()`, `str_ends_with()` → используйте `strpos() !== false` + +## Project Overview + +ModuleMonitorActiveCalls is a PHP module for MikoPBX (Asterisk-based VoIP PBX) that monitors active calls and enables supervisors to perform real-time call actions (listen, whisper, barge-in, hangup). + +**Stack:** PHP 7.4.6+, Phalcon MVC framework, Redis cache, Asterisk AMI, Vue.js frontend + +## Сборка и установка модуля + +### Сборка архива (локально) + +```bash +cd /Volumes/DevDisk/apor/Developement/MikoPBX/Extensions/ModuleMonitorActiveCalls +zip -r ../ModuleMonitorActiveCalls.zip . -x "*.git*" -x "*tasks.md*" -x "*.DS_Store*" -x "*CLAUDE.md*" +``` + +### Загрузка на сервер + +```bash +scp ../ModuleMonitorActiveCalls.zip user@server:/home/user/ +``` + +### Установка через WorkerModuleInstaller (ЕДИНСТВЕННЫЙ РАЗРЕШЁННЫЙ СПОСОБ) + +```bash +# На сервере: создать settings.json +cat > /tmp/settings.json << 'EOF' +{ + "currentModuleDir": "/storage/usbdisk1/mikopbx/custom_modules/ModuleMonitorActiveCalls", + "filePath": "/home/user/ModuleMonitorActiveCalls.zip", + "uniqid": "ModuleMonitorActiveCalls" +} +EOF + +# Установить модуль (сохраняет БД!) +php -f /usr/www/src/PBXCoreREST/Workers/WorkerModuleInstaller.php start /tmp/settings.json +``` + +### Быстрая команда (всё в одном) + +```bash +cd /Volumes/DevDisk/apor/Developement/MikoPBX/Extensions/ModuleMonitorActiveCalls && \ +zip -r ../ModuleMonitorActiveCalls.zip . -x "*.git*" -x "*tasks.md*" -x "*.DS_Store*" -x "*CLAUDE.md*" && \ +scp ../ModuleMonitorActiveCalls.zip user@server:/home/user/ && \ +ssh user@server 'php -f /usr/www/src/PBXCoreREST/Workers/WorkerModuleInstaller.php start /tmp/settings.json' +``` + +## Инициализация в скриптах + +Все PHP скрипты (bin/, tests/) должны начинаться с: + +```php +#!/usr/bin/php + 120 сек → зависла инициализация, kill + restart + - `status=running`, age > 90 сек → воркер завис, kill + restart + - Иначе → всё ок + +### Data Flow + +``` +Asterisk AMI Events → WorkerActiveCalls → Redis Cache → Web UI (reads cache) +User Action → Controller → Beanstalk Queue → WorkerAmiActions → Asterisk AMI +``` + +### Key Files + +| File | Purpose | +|------|---------| +| `Lib/MonitorActiveCallsConf.php` | Module configuration, worker definitions, AMI config | +| `Lib/MonitorActiveCallsMain.php` | Module lifecycle (start/stop workers) | +| `Lib/AsteriskManager.php` | Custom AMI socket client | +| `Lib/CacheManager.php` | Redis adapter (DB index 3, prefix `ModuleMonitorActiveCalls_`) | +| `App/Controllers/ModuleMonitorActiveCallsController.php` | Web API endpoints | +| `public/assets/js/module-monitor-active-calls-index.js` | Vue.js frontend | + +### Database Models + +- **ModuleMonitorActiveCalls** - Module settings (admin user ID) +- **UsersSettings** - Per-user preferences (queue filters, min wait visible) + +Tables are created via Phalcon model annotations in `Setup/PbxExtensionSetup.php`. + +### API Endpoints + +- `getActiveChannelsAction()` - Fetch active calls (v1) +- `getActiveChannelsV2Action()` - Fetch active calls with queues (v2) +- `executeCallAction()` - Execute call action (hangup/listen/whisper/join) +- `saveUserAction()` - Save user settings + +### Access Control + +Integrates with optional `ModuleUsersUI` module for role-based filtering. Supervisors see all calls; agents see only their extensions. + +## Channel States + +Constants in `WorkerActiveCalls`: +- `STATE_IDLE`, `STATE_RINGING`, `STATE_ONHOLD`, `STATE_UP`, `STATE_BUSY`, `STATE_UNAVAILABLE` + +## Configuration + +AMI user `monitor-active-calls` is auto-generated with limited permissions (read: system,agent,call,cdr,user; write: system,agent,call,originate). + +## File Paths (на сервере) + +| Path | Description | +|------|-------------| +| `/storage/usbdisk1/mikopbx/custom_modules/ModuleMonitorActiveCalls/` | Module directory | +| `/storage/usbdisk1/mikopbx/custom_modules/ModuleMonitorActiveCalls/db/module.db` | SQLite database | +| `/storage/usbdisk1/mikopbx/logs/ModuleMonitorActiveCalls/` | Log files | +| `/usr/www/src/Core/Config/Globals.php` | MikoPBX bootstrap | + +## Совместимость с Phalcon + +Используйте `MikoPBXVersion` для кросс-версионной совместимости: + +| Метод | Phalcon 4 | Phalcon 5 | +|-------|-----------|-----------| +| `isPhalcon5Version()` | `false` | `true` | +| `getDefaultDi()` | `\Phalcon\Di::getDefault()` | `\Phalcon\Di\Di::getDefault()` | +| `getLoggerClass()` | `\Phalcon\Logger` | `\Phalcon\Logger\Logger` | + +## Translations + +28 languages supported in `/Messages/` directory (PHP arrays). + +## Dependencies + +- **Required:** MikoPBX >= 2024.1.114 +- **Optional:** ModuleUsersUI (access control), ModuleSoftphoneBackend (contact names) +- **PHP:** cesargb/php-log-rotation 2.6.0 + +## JavaScript Build + +Source files are in `public/assets/js/src/`. After modifying them, compiled files must be generated in `public/assets/js/`. + +**IMPORTANT:** Only edit files in `src/` directory. Files in `public/assets/js/*.js` are auto-generated. + +Build process uses Babel via PHPStorm File Watcher: +- See setup: https://docs.mikopbx.com/mikopbx-development/prepare-ide-tools/mac#phpstorm-setup-babel +- Babel path: `/Users/apor/Developement/MikoPBX/MikoPBXUtils/node_modules/.bin/babel` +- Presets: `airbnb` +- Source maps: enabled + +To rebuild manually: +```bash +cd /Users/apor/Developement/MikoPBX/MikoPBXUtils && \ +cp babel.config.json babel.config.json.bak && \ +echo '{"presets":[["@babel/preset-env",{"targets":{"chrome":50,"ie":11,"firefox":45}}]]}' > babel.config.json && \ +./node_modules/.bin/babel \ + /Volumes/DevDisk/apor/Developement/MikoPBX/Extensions/ModuleMonitorActiveCalls/public/assets/js/src/module-monitor-active-calls-index.js \ + --out-dir /Volumes/DevDisk/apor/Developement/MikoPBX/Extensions/ModuleMonitorActiveCalls/public/assets/js/ \ + --source-maps && \ +mv babel.config.json.bak babel.config.json +``` diff --git a/Lib/AsteriskManager.php b/Lib/AsteriskManager.php index 917eb95..c5e46aa 100644 --- a/Lib/AsteriskManager.php +++ b/Lib/AsteriskManager.php @@ -93,6 +93,15 @@ class AsteriskManager */ private bool $_loggedIn = false; + /** @var callable|null Callback invoked periodically when AMI connection is alive and idle */ + private $onIdleCallback = null; + + /** @var int Timestamp of last idle callback invocation */ + private int $lastIdleCall = 0; + + /** @var int Minimum interval between idle callback calls (seconds) */ + private int $idleInterval = 30; + /** * Constructor * @@ -552,6 +561,19 @@ public function ping(): array return $this->sendRequestTimeout('Ping'); } + /** + * Wait for a user events. + * + * @param $allow_timeout bool + * + * @return array of parameters, empty on timeout + */ + public function setOnIdleCallback(callable $callback, int $interval = 30): void + { + $this->onIdleCallback = $callback; + $this->idleInterval = $interval; + } + /** * Wait for a user events. * @@ -578,6 +600,11 @@ public function waitUserEvent(bool $allow_timeout = false): array } if ($type === '' && count($this->ping()) === 0) { $timeout = $allow_timeout; + } elseif ($type === '' && $this->onIdleCallback !== null + && time() - $this->lastIdleCall >= $this->idleInterval + ) { + $this->lastIdleCall = time(); + call_user_func($this->onIdleCallback); } elseif (stripos($type, 'event') !== false) { $this->processEvent($parameters); } diff --git a/Lib/MonitorActiveCallsConf.php b/Lib/MonitorActiveCallsConf.php index 9bcb841..ec6cdf3 100644 --- a/Lib/MonitorActiveCallsConf.php +++ b/Lib/MonitorActiveCallsConf.php @@ -41,7 +41,7 @@ public function getModuleWorkers(): array 'worker' => WorkerAmiActions::class, ], [ - 'type' => WorkerSafeScriptsCore::CHECK_BY_AMI, + 'type' => WorkerSafeScriptsCore::CHECK_BY_PID_NOT_ALERT, 'worker' => WorkerActiveCalls::class, ], ]; diff --git a/bin/WorkerActiveCalls.php b/bin/WorkerActiveCalls.php index 70b03f8..4d2bfe9 100644 --- a/bin/WorkerActiveCalls.php +++ b/bin/WorkerActiveCalls.php @@ -102,6 +102,8 @@ class WorkerActiveCalls extends WorkerBase private const MAX_BRIDGE_ITERATIONS = 200; private const AMI_REQUEST_TIMEOUT = 200000; + public const STATE_FILE = '/tmp/MonitorActiveCalls_worker.state'; + private array $queueEntryes = []; /** @@ -130,6 +132,21 @@ public function replyOnPingRequest(array $parameters): bool return false; } + /** + * Обновляет state-файл с текущим статусом воркера. + * Используется safe.php для контроля здоровья процесса. + * + * @param string $status 'starting' или 'running' + */ + public static function updateStateFile(string $status = 'running'): void + { + file_put_contents(self::STATE_FILE, json_encode([ + 'pid' => getmypid(), + 'ts' => time(), + 'status' => $status, + ])); + } + /** * Дополнительный контроль активных вызовов. * @return void @@ -221,6 +238,7 @@ public function start($argv):void { $this->logger = new Logger('ActiveCalls', 'ModuleMonitorActiveCalls'); $this->logger->writeInfo('Starting...'); + self::updateStateFile('starting'); $this->backendExists = MonitorActiveCallsMain::backendExists(); $this->initManagerAsterisk(); @@ -233,7 +251,11 @@ public function start($argv):void $this->init = false; $this->printActiveCalls(); + self::updateStateFile('running'); $this->logger->writeInfo('Wait events...'); + $this->amCustom->setOnIdleCallback(function () { + self::updateStateFile('running'); + }, 30); while ($this->needRestart === false) { try { $this->amCustom->waitUserEvent(true); @@ -241,6 +263,9 @@ public function start($argv):void sleep(1); $this->logger->writeInfo('initManagerAsterisk...'); $this->initManagerAsterisk(); + $this->amCustom->setOnIdleCallback(function () { + self::updateStateFile('running'); + }, 30); } } catch (\Throwable $e) { $this->logger->writeError("Error in main loop: " . $e->getMessage()); diff --git a/bin/safe.php b/bin/safe.php index 6f3b53e..c2eda53 100644 --- a/bin/safe.php +++ b/bin/safe.php @@ -21,6 +21,7 @@ use MikoPBX\Core\System\Processes; use MikoPBX\Core\System\SystemMessages; use MikoPBX\Modules\PbxExtensionUtils; +use Modules\ModuleMonitorActiveCalls\bin\WorkerActiveCalls; use Modules\ModuleMonitorActiveCalls\Lib\MonitorActiveCallsConf; require_once 'Globals.php'; @@ -28,21 +29,92 @@ if(!$moduleEnable){ exit(1); } + +// Максимальное время на инициализацию (status=starting) +$maxStartingAge = 120; +// Максимальное время без обновления state-файла (status=running) +$maxRunningAge = 90; + $conf = new MonitorActiveCallsConf(); $workers = $conf->getModuleWorkers(); foreach ($workers as $workerData) { - $WorkerPID = Processes::getPidOfProcess($workerData['worker']); - print_r($WorkerPID.PHP_EOL); - if (empty($WorkerPID)) { - Processes::processPHPWorker($workerData['worker']); - SystemMessages::sysLogMsg('MonitorActiveCalls_SAFE', "Service {$workerData['worker']} started.", LOG_NOTICE); - }else{ - // Проверка дубликата процесса. + $workerClass = $workerData['worker']; + $WorkerPID = Processes::getPidOfProcess($workerClass); + + // Проверка дубликатов процесса + if (!empty($WorkerPID)) { $allButLast = array_slice(explode(' ', $WorkerPID), 0, -1); - if(!empty($allButLast)){ - // Завершаем дубликаты процессов. + if (!empty($allButLast)) { $bbPath = Util::which('busybox'); - shell_exec("$bbPath kill -SIGUSR2 ". implode(" ", $allButLast)); + shell_exec("$bbPath kill -SIGUSR2 " . implode(" ", $allButLast)); + } + } + + // Для WorkerActiveCalls — проверяем state-файл + if ($workerClass === WorkerActiveCalls::class) { + $needRestart = false; + $reason = ''; + + if (empty($WorkerPID)) { + $needRestart = true; + $reason = 'no PID found'; + } else { + $stateFile = WorkerActiveCalls::STATE_FILE; + if (!file_exists($stateFile)) { + $needRestart = true; + $reason = 'state file missing'; + } else { + $stateRaw = file_get_contents($stateFile); + $state = json_decode($stateRaw, true); + if (!is_array($state) || empty($state['ts']) || empty($state['status'])) { + $needRestart = true; + $reason = 'state file corrupted'; + } else { + $age = time() - $state['ts']; + $status = $state['status']; + $statePid = $state['pid'] ?? 0; + + // PID в state-файле не совпадает с реальным процессом + $pidParts = explode(' ', $WorkerPID); + $lastPid = (int)trim(end($pidParts)); + if ($statePid > 0 && $statePid !== $lastPid) { + $needRestart = true; + $reason = "state PID ($statePid) != process PID ($lastPid)"; + } elseif ($status === 'starting' && $age > $maxStartingAge) { + $needRestart = true; + $reason = "stuck in starting for {$age}s (max {$maxStartingAge}s)"; + } elseif ($status === 'running' && $age > $maxRunningAge) { + $needRestart = true; + $reason = "state file stale for {$age}s (max {$maxRunningAge}s)"; + } + } + } + } + + if ($needRestart) { + // Убиваем старый процесс если есть + if (!empty($WorkerPID)) { + $bbPath = Util::which('busybox'); + shell_exec("$bbPath kill -9 " . str_replace(' ', ' -9 ', $WorkerPID)); + usleep(500000); + } + // Удаляем старый state-файл + if (file_exists(WorkerActiveCalls::STATE_FILE)) { + unlink(WorkerActiveCalls::STATE_FILE); + } + Processes::processPHPWorker($workerClass); + SystemMessages::sysLogMsg( + 'MonitorActiveCalls_SAFE', + "WorkerActiveCalls restarted: $reason", + LOG_NOTICE + ); } + continue; + } + + // Для остальных воркеров — стандартная проверка по PID + if (empty($WorkerPID)) { + Processes::processPHPWorker($workerClass); + SystemMessages::sysLogMsg('MonitorActiveCalls_SAFE', "Service $workerClass started.", LOG_NOTICE); } } From f70049028074fa1e321d766944e9212ebdfd8a0f Mon Sep 17 00:00:00 2001 From: Alexey Portnov Date: Thu, 5 Feb 2026 03:00:00 +0800 Subject: [PATCH 04/19] =?UTF-8?q?=D0=A0=D0=B0=D1=81=D1=88=D0=B8=D1=80?= =?UTF-8?q?=D0=B5=D0=BD=20getUsersStates:=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=BE=20=D0=BD=D0=B0=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=B7=D0=B2=D0=BE=D0=BD=D0=BA?= =?UTF-8?q?=D0=B0=20=D0=B8=20=D1=81=D0=BE=D1=81=D1=82=D0=BE=D1=8F=D0=BD?= =?UTF-8?q?=D0=B8=D0=B5=20=D0=BE=D1=87=D0=B5=D1=80=D0=B5=D0=B4=D0=B5=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Добавлено поле direction (incoming/outgoing) в структуру channels - Очереди теперь включены в общий список states с автоматическим расчётом состояния на основе доступности агентов - Защита состояния очередей от перезаписи событием ExtensionStatus --- bin/WorkerActiveCalls.php | 58 +++++++++++++++++++++++++++++++++++---- 1 file changed, 53 insertions(+), 5 deletions(-) diff --git a/bin/WorkerActiveCalls.php b/bin/WorkerActiveCalls.php index 4d2bfe9..b686063 100644 --- a/bin/WorkerActiveCalls.php +++ b/bin/WorkerActiveCalls.php @@ -482,6 +482,42 @@ private function enrichStatesWithConnections(): array { $states = $this->states; + // Обновляем состояние очередей на основе состояния агентов + foreach ($this->queuesData as $queueId => $queueData) { + $queueNumber = $queueData['number'] ?? ''; + if (empty($queueNumber) || !isset($states[$queueNumber])) { + continue; + } + + $hasIdleAgent = false; + $hasAvailableAgent = false; + + foreach ($queueData['agents'] as $agentNumber) { + $agentState = ''; + if (isset($this->states[$agentNumber])) { + $agentState = $this->states[$agentNumber]['state']; + } elseif (isset($this->mobileStates[$agentNumber])) { + $agentState = $this->mobileStates[$agentNumber]['state']; + } + + if ($agentState === self::STATE_IDLE) { + $hasIdleAgent = true; + $hasAvailableAgent = true; + break; + } elseif ($agentState !== self::STATE_UNAVAILABLE) { + $hasAvailableAgent = true; + } + } + + if ($hasIdleAgent) { + $states[$queueNumber]['state'] = self::STATE_IDLE; + } elseif ($hasAvailableAgent) { + $states[$queueNumber]['state'] = self::STATE_BUSY; + } else { + $states[$queueNumber]['state'] = self::STATE_UNAVAILABLE; + } + } + // Строим карту channel -> linkedId $channelToLinkedId = []; foreach ($this->activeChannels as $linkedId => $channels) { @@ -499,10 +535,14 @@ private function enrichStatesWithConnections(): array foreach (array_keys($stateData['channels']) as $channel) { $linkedId = $channelToLinkedId[$channel] ?? ''; if (empty($linkedId)) { - $enrichedChannels[$channel] = ['channel' => '', 'number' => '']; + $enrichedChannels[$channel] = ['channel' => '', 'number' => '', 'direction' => '']; continue; } + // Определяем направление звонка относительно владельца канала + $srcChan = $this->callType[$linkedId]['src_chan'] ?? ''; + $direction = ($channel === $srcChan) ? 'outgoing' : 'incoming'; + // Проходим цепочку бриджей через Local-каналы до реального PJSIP-канала $resolvedChannel = $channel; $bridgeStart = time(); @@ -510,14 +550,14 @@ private function enrichStatesWithConnections(): array if ($found && $resolvedChannel !== $channel) { $number = $this->activeChannels[$linkedId][$resolvedChannel]['CallerIDNum'] ?? ''; - $enrichedChannels[$channel] = ['channel' => $resolvedChannel, 'number' => $number]; + $enrichedChannels[$channel] = ['channel' => $resolvedChannel, 'number' => $number, 'direction' => $direction]; } else { // Канал не в бридже (звонит/ожидает) — используем ConnectedLineNum $connectedNum = $this->activeChannels[$linkedId][$channel]['ConnectedLineNum'] ?? ''; if (!empty($connectedNum) && strpos($connectedNum, '<') === false) { - $enrichedChannels[$channel] = ['channel' => '', 'number' => $connectedNum]; + $enrichedChannels[$channel] = ['channel' => '', 'number' => $connectedNum, 'direction' => $direction]; } else { - $enrichedChannels[$channel] = ['channel' => '', 'number' => '']; + $enrichedChannels[$channel] = ['channel' => '', 'number' => '', 'direction' => $direction]; } } } @@ -643,6 +683,14 @@ private function collectQueuesInfo():void foreach ($queues as $queue){ $this->queuesData[$queue->id] = $queue->toArray(); $this->queuesData[$queue->id]['agents'] = []; + + // Добавляем очередь в states + $this->states[$queue->number] = [ + 'state' => self::STATE_UNAVAILABLE, + 'name' => $queue->name, + 'channels' => [], + 'isQueue' => true + ]; } $queuesAgents = CallQueueMembers::find(['columns' => 'queue,extension']); foreach ($queuesAgents as $queuesAgent) { @@ -1166,7 +1214,7 @@ public function stateEvents($parameters):void } if($event === 'ExtensionStatus'){ $exten = $parameters['Exten'] ?? ''; - if(!empty($exten) && isset($this->states[$exten])){ + if(!empty($exten) && isset($this->states[$exten]) && empty($this->states[$exten]['isQueue'])){ $this->states[$exten]['state'] = $parameters['StatusText'] ?? ''; $this->logger->writeInfo($parameters,'stateEvents...'); $this->printActiveCalls(); From 33e6d23b3cc8283945f194644ddeb51e47c0052f Mon Sep 17 00:00:00 2001 From: Alexey Portnov Date: Thu, 5 Feb 2026 04:52:33 +0800 Subject: [PATCH 05/19] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=BE=20=D1=83=D0=B4=D0=B0=D0=BB=D0=B5=D0=BD?= =?UTF-8?q?=D0=B8=D0=B5=20=D0=B7=D0=B0=D0=B2=D0=B5=D1=80=D1=88=D1=91=D0=BD?= =?UTF-8?q?=D0=BD=D1=8B=D1=85=20=D0=B7=D0=B2=D0=BE=D0=BD=D0=BA=D0=BE=D0=B2?= =?UTF-8?q?=20=D0=BF=D1=80=D0=B8=20=D0=B8=D0=B7=D0=BC=D0=B5=D0=BD=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D0=B8=20Linkedid?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Устранена проблема "зависших звонков" в веб-интерфейсе. Проблема: При attended transfer, bridge masquerade или call pickup Asterisk может изменить Linkedid канала. Событие Hangup приходит с новым Linkedid, но канал хранится под старым — удаление не происходило. Решение: В обработке Hangup добавлен fallback-поиск канала во всех активных linkedId, если канал не найден в указанном linkedId из события. --- bin/WorkerActiveCalls.php | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/bin/WorkerActiveCalls.php b/bin/WorkerActiveCalls.php index b686063..bde77b5 100644 --- a/bin/WorkerActiveCalls.php +++ b/bin/WorkerActiveCalls.php @@ -981,11 +981,24 @@ public function callEvents($parameters):void return; } $endpoint = self::getEndpointName($channel); - unset($this->activeChannels[$linkedId][$channel]); + + // Попытка удалить канал из указанного linkedId + $foundLinkedId = $linkedId; + if (!isset($this->activeChannels[$linkedId][$channel])) { + // Linkedid мог измениться (attended transfer, masquerade) — ищем канал во всех linkedId + foreach (array_keys($this->activeChannels) as $altLinkedId) { + if (isset($this->activeChannels[$altLinkedId][$channel])) { + $foundLinkedId = $altLinkedId; + break; + } + } + } + + unset($this->activeChannels[$foundLinkedId][$channel]); unset($this->states[$endpoint]['channels'][$channel]); - if(empty($this->activeChannels[$linkedId])){ - unset($this->activeChannels[$linkedId]); - unset($this->callType[$linkedId]); + if(empty($this->activeChannels[$foundLinkedId])){ + unset($this->activeChannels[$foundLinkedId]); + unset($this->callType[$foundLinkedId]); } }elseif(in_array($event, ['Newchannel','Newstate']) && stripos($parameters['Channel'] ?? '', 'local') === false){ $linkedId = $parameters['Linkedid'] ?? ''; From b3f347b8bb6e6e3753e565f8ed2dad5cdb01cae8 Mon Sep 17 00:00:00 2001 From: Alexey Portnov Date: Fri, 6 Feb 2026 02:10:40 +0800 Subject: [PATCH 06/19] =?UTF-8?q?=D0=A0=D0=B5=D0=B0=D0=BB=D0=B8=D0=B7?= =?UTF-8?q?=D0=BE=D0=B2=D0=B0=D0=BD=D0=B0=20=D0=A4=D0=90=D0=97=D0=90=201?= =?UTF-8?q?=20=D0=BE=D0=BF=D1=82=D0=B8=D0=BC=D0=B8=D0=B7=D0=B0=D1=86=D0=B8?= =?UTF-8?q?=D0=B8=20WebSocket=20=D0=BE=D0=B1=D0=BD=D0=BE=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Задача #11: Throttling/Debouncing обновлений (критично) - Добавлена задержка 200ms перед отправкой WebSocket обновлений - Накопление изменений: если за 200ms приходят новые события, отправляется только последнее состояние - Burst из 32+ событий → 1 отправка вместо 32 (реальный тест на сервере) - Ожидаемая экономия трафика: 83-95% - Добавлено debug-логирование throttling событий (scheduled, pending, sent с метриками) Задача #12: Убрать пустые channels (средний приоритет) - Удаление ключа 'channels' если массив пустой (Idle/Unavailable статусы) - Экономия ~200 байт на сообщение Изменения в bin/WorkerActiveCalls.php: - Добавлены свойства: $stateUpdateScheduled, $lastStateUpdateSent, $pendingUserStatesData - Добавлена константа: STATE_UPDATE_DELAY (200ms) - Изменена логика printActiveCalls(): * Накопление изменений в $pendingUserStatesData * Проверка таймера при каждом вызове * Отправка через 200ms+ после первого изменения * Детальное логирование (delay, payload size, time since last) - Изменена enrichStatesWithConnections(): * Удаление пустых channels после continue * Удаление channels если обогащение не дало результата Реальный результат (лог с сервера boffart.miko.ru): - Входящий звонок с очередью: 32+ AMI событий за 20ms - Отправлено: 1 WebSocket сообщение (вместо 32) - Размер payload: 2633 байта (без пустых channels) - Throttling работает корректно во время burst'а событий --- bin/WorkerActiveCalls.php | 53 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 49 insertions(+), 4 deletions(-) diff --git a/bin/WorkerActiveCalls.php b/bin/WorkerActiveCalls.php index bde77b5..f4bd694 100644 --- a/bin/WorkerActiveCalls.php +++ b/bin/WorkerActiveCalls.php @@ -53,6 +53,11 @@ class WorkerActiveCalls extends WorkerBase private array $spyerChannels = []; private array $agentToQueues = []; // agent number => [queueIds] + // Throttling WebSocket updates + private int $stateUpdateScheduled = 0; // timestamp первого изменения (ms) + private float $lastStateUpdateSent = 0.0; // timestamp последней отправки (ms) + private $pendingUserStatesData = null; // накопленные изменения + public const ENDPOINT_TYPE_PEER = '1'; public const ENDPOINT_TYPE_PROVIDER = '2'; public const STATE_IDLE = 'Idle'; @@ -101,6 +106,7 @@ class WorkerActiveCalls extends WorkerBase private const CONTROL_INTERVAL = 60; private const MAX_BRIDGE_ITERATIONS = 200; private const AMI_REQUEST_TIMEOUT = 200000; + private const STATE_UPDATE_DELAY = 200; // ms - задержка debounce для WebSocket обновлений public const STATE_FILE = '/tmp/MonitorActiveCalls_worker.state'; @@ -462,12 +468,45 @@ private function printActiveCalls():void $data = ['states' => $enrichedStates]; $dataPrint = json_encode($data, JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE); $newPrintHash = md5($dataPrint); + + $now = (int)(microtime(true) * 1000); // текущее время в миллисекундах + + // Если состояние изменилось if($newPrintHash <> $this->lastPrintUserHash){ $this->lastPrintUserHash = $newPrintHash; - CacheManager::setCacheData('getUsersStates', $data, self::CACHE_TTL); - if($this->backendExists){ - BackendApiController::publishUserStates($data); + $this->pendingUserStatesData = $data; + + // Планируем отправку, если ещё не запланирована + if($this->stateUpdateScheduled === 0) { + $this->stateUpdateScheduled = $now; + $this->logger->writeInfo("WS throttle: State change detected, scheduled update"); + } else { + $timeSinceScheduled = $now - $this->stateUpdateScheduled; + $this->logger->writeInfo("WS throttle: State changed again, pending send in {$timeSinceScheduled}ms"); + } + } + + // Проверяем, пора ли отправлять накопленные изменения + if($this->stateUpdateScheduled > 0 && + ($now - $this->stateUpdateScheduled) >= self::STATE_UPDATE_DELAY) { + + if($this->pendingUserStatesData !== null) { + $delayMs = $now - $this->stateUpdateScheduled; + $timeSinceLastSent = $this->lastStateUpdateSent > 0 ? $now - $this->lastStateUpdateSent : 0; + $payloadSize = strlen($dataPrint); + + $this->logger->writeInfo("WS throttle: Sending update after {$delayMs}ms delay, " . + "payload: {$payloadSize} bytes, " . + "time since last: {$timeSinceLastSent}ms"); + + CacheManager::setCacheData('getUsersStates', $this->pendingUserStatesData, self::CACHE_TTL); + if($this->backendExists){ + BackendApiController::publishUserStates($this->pendingUserStatesData); + } + $this->lastStateUpdateSent = $now; + $this->pendingUserStatesData = null; } + $this->stateUpdateScheduled = 0; } } @@ -529,6 +568,7 @@ private function enrichStatesWithConnections(): array // Обновляем channels в копии states foreach ($states as $endpoint => &$stateData) { if (empty($stateData['channels'])) { + unset($stateData['channels']); // Удаляем пустой ключ для экономии трафика continue; } $enrichedChannels = []; @@ -561,7 +601,12 @@ private function enrichStatesWithConnections(): array } } } - $stateData['channels'] = $enrichedChannels; + // Добавляем channels только если есть данные + if (!empty($enrichedChannels)) { + $stateData['channels'] = $enrichedChannels; + } else { + unset($stateData['channels']); // Удаляем если обогащение не дало результата + } } unset($stateData); From 91126c9356bf4f70d4164e4cd4cae04ad58f813e Mon Sep 17 00:00:00 2001 From: Alexey Portnov Date: Fri, 6 Feb 2026 02:24:32 +0800 Subject: [PATCH 07/19] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=20throttle:=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B0=20=D0=BF=D1=80=D0=B8=D0=BD=D1=83=D0=B4?= =?UTF-8?q?=D0=B8=D1=82=D0=B5=D0=BB=D1=8C=D0=BD=D0=B0=D1=8F=20=D0=BE=D1=82?= =?UTF-8?q?=D0=BF=D1=80=D0=B0=D0=B2=D0=BA=D0=B0=20=D0=BE=D0=B1=D0=BD=D0=BE?= =?UTF-8?q?=D0=B2=D0=BB=D0=B5=D0=BD=D0=B8=D0=B9=20=D1=87=D0=B5=D1=80=D0=B5?= =?UTF-8?q?=D0=B7=20idle=20callback?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Проблема: - Throttle механизм задерживает WebSocket обновления на 200ms - Проверка "пора ли отправлять" выполнялась только при новых AMI событиях - Если после Hangup событий больше не приходило, обновление зависало - В UI завершённые звонки продолжали отображаться как активные Решение: - Выделена логика отправки в метод flushPendingStateUpdate() - Добавлен вызов этого метода в idle callback (каждую секунду) - Теперь даже без новых событий обновления отправляются через макс. 1 сек Изменения: - bin/WorkerActiveCalls.php: * Новый метод flushPendingStateUpdate() для отправки pending updates * Idle callback интервал уменьшен с 30 до 1 сек для быстрой отправки * Вызов flushPendingStateUpdate() добавлен в idle callback --- bin/WorkerActiveCalls.php | 56 ++++++++++++++++++++++++++------------- 1 file changed, 37 insertions(+), 19 deletions(-) diff --git a/bin/WorkerActiveCalls.php b/bin/WorkerActiveCalls.php index f4bd694..9879c82 100644 --- a/bin/WorkerActiveCalls.php +++ b/bin/WorkerActiveCalls.php @@ -261,7 +261,8 @@ public function start($argv):void $this->logger->writeInfo('Wait events...'); $this->amCustom->setOnIdleCallback(function () { self::updateStateFile('running'); - }, 30); + $this->flushPendingStateUpdate(); // Отправляем накопленные WS обновления, если есть + }, 1); // Проверка каждую секунду для быстрой отправки WS updates while ($this->needRestart === false) { try { $this->amCustom->waitUserEvent(true); @@ -271,7 +272,8 @@ public function start($argv):void $this->initManagerAsterisk(); $this->amCustom->setOnIdleCallback(function () { self::updateStateFile('running'); - }, 30); + $this->flushPendingStateUpdate(); // Отправляем накопленные WS обновления, если есть + }, 1); // Проверка каждую секунду для быстрой отправки WS updates } } catch (\Throwable $e) { $this->logger->writeError("Error in main loop: " . $e->getMessage()); @@ -487,28 +489,44 @@ private function printActiveCalls():void } // Проверяем, пора ли отправлять накопленные изменения - if($this->stateUpdateScheduled > 0 && - ($now - $this->stateUpdateScheduled) >= self::STATE_UPDATE_DELAY) { + $this->flushPendingStateUpdate(); - if($this->pendingUserStatesData !== null) { - $delayMs = $now - $this->stateUpdateScheduled; - $timeSinceLastSent = $this->lastStateUpdateSent > 0 ? $now - $this->lastStateUpdateSent : 0; - $payloadSize = strlen($dataPrint); + } - $this->logger->writeInfo("WS throttle: Sending update after {$delayMs}ms delay, " . - "payload: {$payloadSize} bytes, " . - "time since last: {$timeSinceLastSent}ms"); + /** + * Отправляет накопленные обновления getUsersStates, если прошло достаточно времени. + * Может вызываться из scheduleUserStatesUpdate() и из idle callback. + */ + private function flushPendingStateUpdate(): void + { + if ($this->stateUpdateScheduled === 0) { + return; // Нет запланированных обновлений + } - CacheManager::setCacheData('getUsersStates', $this->pendingUserStatesData, self::CACHE_TTL); - if($this->backendExists){ - BackendApiController::publishUserStates($this->pendingUserStatesData); - } - $this->lastStateUpdateSent = $now; - $this->pendingUserStatesData = null; - } - $this->stateUpdateScheduled = 0; + $now = (int)(microtime(true) * 1000); + $elapsed = $now - $this->stateUpdateScheduled; + + if ($elapsed < self::STATE_UPDATE_DELAY) { + return; // Ещё рано отправлять } + // Пора отправлять + if ($this->pendingUserStatesData !== null) { + $timeSinceLastSent = $this->lastStateUpdateSent > 0 ? $now - $this->lastStateUpdateSent : 0; + $payloadSize = strlen(json_encode($this->pendingUserStatesData, JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE)); + + $this->logger->writeInfo("WS throttle: Sending update after {$elapsed}ms delay, " . + "payload: {$payloadSize} bytes, " . + "time since last: {$timeSinceLastSent}ms"); + + CacheManager::setCacheData('getUsersStates', $this->pendingUserStatesData, self::CACHE_TTL); + if ($this->backendExists) { + BackendApiController::publishUserStates($this->pendingUserStatesData); + } + $this->lastStateUpdateSent = $now; + $this->pendingUserStatesData = null; + } + $this->stateUpdateScheduled = 0; } /** From 12a80a09e85c7865a19c9911920e87a05af61cf0 Mon Sep 17 00:00:00 2001 From: Alexey Portnov Date: Fri, 6 Feb 2026 02:33:46 +0800 Subject: [PATCH 08/19] =?UTF-8?q?=D0=A3=D0=BC=D0=B5=D0=BD=D1=8C=D1=88?= =?UTF-8?q?=D0=B5=D0=BD=20socket=20timeout=20=D0=B4=D0=BB=D1=8F=20=D0=B1?= =?UTF-8?q?=D1=8B=D1=81=D1=82=D1=80=D0=BE=D0=B9=20=D0=BE=D1=82=D0=BF=D1=80?= =?UTF-8?q?=D0=B0=D0=B2=D0=BA=D0=B8=20WebSocket=20=D0=BE=D0=B1=D0=BD=D0=BE?= =?UTF-8?q?=D0=B2=D0=BB=D0=B5=D0=BD=D0=B8=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Проблема: - Socket timeout был 5 секунд (stream_set_timeout в AsteriskManager) - Idle callback мог срабатывать максимум раз в 5 секунд - WebSocket обновления после Hangup задерживались на ~5 секунд Решение: - Добавлен метод setSocketTimeout() в AsteriskManager - После connect() устанавливается timeout 1 секунда - Теперь idle callback срабатывает каждую секунду - WebSocket updates отправляются максимум через 1 сек после события Изменения: - Lib/AsteriskManager.php: новый метод setSocketTimeout() - bin/WorkerActiveCalls.php: вызов setSocketTimeout(1) после connect() --- Lib/AsteriskManager.php | 15 +++++++++++++++ bin/WorkerActiveCalls.php | 2 ++ 2 files changed, 17 insertions(+) diff --git a/Lib/AsteriskManager.php b/Lib/AsteriskManager.php index c5e46aa..e12897e 100644 --- a/Lib/AsteriskManager.php +++ b/Lib/AsteriskManager.php @@ -561,6 +561,21 @@ public function ping(): array return $this->sendRequestTimeout('Ping'); } + /** + * Sets socket read timeout. + * + * @param int $seconds Timeout in seconds + * @param int $microseconds Timeout in microseconds (optional) + * @return bool True if socket is valid and timeout was set + */ + public function setSocketTimeout(int $seconds, int $microseconds = 0): bool + { + if ($this->socket === false) { + return false; + } + return stream_set_timeout($this->socket, $seconds, $microseconds); + } + /** * Wait for a user events. * diff --git a/bin/WorkerActiveCalls.php b/bin/WorkerActiveCalls.php index 9879c82..b00e999 100644 --- a/bin/WorkerActiveCalls.php +++ b/bin/WorkerActiveCalls.php @@ -1002,6 +1002,8 @@ private function initManagerAsterisk():void $this->amCustom = new CustomAsteriskManager(); // Оригинальный AsteriskManager работает плохо с BridgeList и BridgeInfo $this->amCustom->connect("127.0.0.1:$amiPort", MonitorActiveCallsConf::AMI_USER, MonitorActiveCallsConf::AMI_USER); + // Устанавливаем короткий timeout для быстрой отработки idle callback и отправки WebSocket updates + $this->amCustom->setSocketTimeout(1); $pingTube = $this->makePingTubeName(self::class); $params = ['Operation' => 'Add', 'Filter' => 'UserEvent: '.$pingTube]; From aee33d37654aa6606363f5b298bfc06bd9c45fb6 Mon Sep 17 00:00:00 2001 From: Alexey Portnov Date: Fri, 6 Feb 2026 02:40:31 +0800 Subject: [PATCH 09/19] =?UTF-8?q?=D0=A3=D0=BC=D0=B5=D0=BD=D1=8C=D1=88?= =?UTF-8?q?=D0=B5=D0=BD=20socket=20timeout=20=D1=81=201=20=D1=81=D0=B5?= =?UTF-8?q?=D0=BA=20=D0=B4=D0=BE=20300ms=20=D0=B4=D0=BB=D1=8F=20=D0=BC?= =?UTF-8?q?=D0=B8=D0=BD=D0=B8=D0=BC=D0=B0=D0=BB=D1=8C=D0=BD=D0=BE=D0=B9=20?= =?UTF-8?q?=D0=BB=D0=B0=D1=82=D0=B5=D0=BD=D1=82=D0=BD=D0=BE=D1=81=D1=82?= =?UTF-8?q?=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Оптимизация: - Socket timeout 300ms согласуется с STATE_UPDATE_DELAY (200ms) - Максимальная задержка WebSocket обновлений: 200-500ms (было 200-1200ms) - Среднее время отклика: ~350ms (было ~600ms) - Idle callback срабатывает ~3-4 раза в секунду (было ~1 раз в секунду) Результат: - UI обновляется в 2 раза быстрее после завершения звонков - Минимальная дополнительная нагрузка на CPU --- bin/WorkerActiveCalls.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bin/WorkerActiveCalls.php b/bin/WorkerActiveCalls.php index b00e999..b8c2c75 100644 --- a/bin/WorkerActiveCalls.php +++ b/bin/WorkerActiveCalls.php @@ -1003,7 +1003,8 @@ private function initManagerAsterisk():void // Оригинальный AsteriskManager работает плохо с BridgeList и BridgeInfo $this->amCustom->connect("127.0.0.1:$amiPort", MonitorActiveCallsConf::AMI_USER, MonitorActiveCallsConf::AMI_USER); // Устанавливаем короткий timeout для быстрой отработки idle callback и отправки WebSocket updates - $this->amCustom->setSocketTimeout(1); + // 300ms оптимально согласуется с STATE_UPDATE_DELAY (200ms) + $this->amCustom->setSocketTimeout(0, 300000); $pingTube = $this->makePingTubeName(self::class); $params = ['Operation' => 'Add', 'Filter' => 'UserEvent: '.$pingTube]; From dca0a879df8d5a15f20d02f5e6b8202f506d8b40 Mon Sep 17 00:00:00 2001 From: Alexey Portnov Date: Fri, 6 Feb 2026 04:11:32 +0800 Subject: [PATCH 10/19] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B0=20=D0=BF=D1=80=D0=BE=D0=B1=D0=BB=D0=B5?= =?UTF-8?q?=D0=BC=D0=B0=20=D1=81=20src=5Fchan=20=D0=BF=D1=80=D0=B8=20?= =?UTF-8?q?=D0=BF=D0=B0=D1=80=D0=B0=D0=BB=D0=BB=D0=B5=D0=BB=D1=8C=D0=BD?= =?UTF-8?q?=D0=BE=D0=BC=20=D0=B2=D1=8B=D0=B7=D0=BE=D0=B2=D0=B5=20=D0=BD?= =?UTF-8?q?=D0=B5=D1=81=D0=BA=D0=BE=D0=BB=D1=8C=D0=BA=D0=B8=D1=85=20=D1=83?= =?UTF-8?q?=D1=81=D1=82=D1=80=D0=BE=D0=B9=D1=81=D1=82=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Когда на одном номере зарегистрировано несколько устройств (например, PJSIP/233-00001603&PJSIP/233-00001604), и отвечает не первое устройство, первый канал уничтожается Hangup'ом. Теперь модуль автоматически обновляет src_chan на активный канал с тем же endpoint, предотвращая ссылки на несуществующие каналы. --- bin/WorkerActiveCalls.php | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/bin/WorkerActiveCalls.php b/bin/WorkerActiveCalls.php index b8c2c75..209395e 100644 --- a/bin/WorkerActiveCalls.php +++ b/bin/WorkerActiveCalls.php @@ -1060,8 +1060,25 @@ public function callEvents($parameters):void } } + // Проверяем, был ли удалённый канал src_chan (параллельный вызов на несколько устройств) + $wasSrcChan = isset($this->callType[$foundLinkedId]) && + ($this->callType[$foundLinkedId]['src_chan'] ?? '') === $channel; + unset($this->activeChannels[$foundLinkedId][$channel]); unset($this->states[$endpoint]['channels'][$channel]); + + // Если удалённый канал был src_chan, ищем альтернативный канал с тем же endpoint + if ($wasSrcChan && !empty($this->activeChannels[$foundLinkedId])) { + foreach ($this->activeChannels[$foundLinkedId] as $altChannel => $altChanData) { + if (self::getEndpointName($altChannel) === $endpoint) { + // Нашли активный канал с тем же endpoint — обновляем src_chan + $this->callType[$foundLinkedId]['src_chan'] = $altChannel; + $this->logger->writeInfo("Updated src_chan from $channel to $altChannel for linkedId $foundLinkedId (parallel dial)"); + break; + } + } + } + if(empty($this->activeChannels[$foundLinkedId])){ unset($this->activeChannels[$foundLinkedId]); unset($this->callType[$foundLinkedId]); From 7a5c0a0f3fc3d0a6dd9e413318c15a2623b53508 Mon Sep 17 00:00:00 2001 From: Alexey Portnov Date: Mon, 9 Feb 2026 19:34:41 +0800 Subject: [PATCH 11/19] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B0=20=D0=BF=D1=80=D0=BE=D0=B1=D0=BB=D0=B5?= =?UTF-8?q?=D0=BC=D0=B0=20=D1=81=20=D0=BE=D1=82=D1=81=D0=BB=D0=B5=D0=B6?= =?UTF-8?q?=D0=B8=D0=B2=D0=B0=D0=BD=D0=B8=D0=B5=D0=BC=20=D0=BF=D0=B5=D1=80?= =?UTF-8?q?=D0=B5=D1=85=D0=B2=D0=B0=D1=87=D0=B5=D0=BD=D0=BD=D1=8B=D1=85=20?= =?UTF-8?q?=D0=B7=D0=B2=D0=BE=D0=BD=D0=BA=D0=BE=D0=B2=20(Call=20Pickup=20*?= =?UTF-8?q?8XXX)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Asterisk автоматически меняет linkedid канала при выполнении PickupChan. Канал перехватчика создаётся с новым linkedid, но при входе в bridge получает linkedid исходного звонка. Это приводило к тому, что модуль не мог найти канал и не отправлял информацию о собеседнике в WebSocket (/pbxcore/api/module-softphone-backend/v1/sub/users-state). Решение: - Добавлено отслеживание pickup-каналов по extension *8XXX при Newchannel/Newstate - При BridgeEnter для pickup-канала проверяется изменение linkedid - Если linkedid изменился, канал автоматически мигрируется в новый linkedid - Добавлен метод migrateChannel() для переноса данных канала между linkedId - Добавлен cleanup старых маппингов в channelAdditionalControl() После миграции метод enrichStatesWithConnections() корректно определяет собеседника и направление звонка через findBridgeChannel(). --- bin/WorkerActiveCalls.php | 87 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) diff --git a/bin/WorkerActiveCalls.php b/bin/WorkerActiveCalls.php index 209395e..9c3b77f 100644 --- a/bin/WorkerActiveCalls.php +++ b/bin/WorkerActiveCalls.php @@ -53,6 +53,10 @@ class WorkerActiveCalls extends WorkerBase private array $spyerChannels = []; private array $agentToQueues = []; // agent number => [queueIds] + // Call pickup tracking + private array $pickupChannels = []; // channel => ['target_extension' => ..., 'initial_linkedid' => ..., 'pickup_time' => ...] + private array $channelLinkedIds = []; // channel => current_linkedid (для отслеживания изменений) + // Throttling WebSocket updates private int $stateUpdateScheduled = 0; // timestamp первого изменения (ms) private float $lastStateUpdateSent = 0.0; // timestamp последней отправки (ms) @@ -230,6 +234,28 @@ private function channelAdditionalControl() unset($this->activeBridges[$bridgeLinkedId]); } } + + // Cleanup old pickup mappings (старше 2 минут) + foreach ($this->pickupChannels as $channel => $data) { + if (time() - ($data['pickup_time'] ?? 0) > 120) { + unset($this->pickupChannels[$channel]); + unset($this->channelLinkedIds[$channel]); + } + } + + // Cleanup orphaned channelLinkedIds + foreach (array_keys($this->channelLinkedIds) as $channel) { + $found = false; + foreach ($this->activeChannels as $channels) { + if (isset($channels[$channel])) { + $found = true; + break; + } + } + if (!$found) { + unset($this->channelLinkedIds[$channel]); + } + } }catch (\Throwable $e){ SystemMessages::sysLogMsg( static::class, "Channel control: " . $e->getMessage(), LOG_WARNING); } @@ -655,6 +681,40 @@ private function getQueueData(array $queueCalls, string $linkedid): array return []; } + /** + * Миграция канала из одного linkedId в другой при изменении linkedid (например, при call pickup). + * + * @param string $channel Имя канала + * @param string $oldLinkedId Старый linkedid + * @param string $newLinkedId Новый linkedid + * @return void + */ + private function migrateChannel(string $channel, string $oldLinkedId, string $newLinkedId): void + { + // 1. Переместить данные канала из activeChannels + if (isset($this->activeChannels[$oldLinkedId][$channel])) { + $channelData = $this->activeChannels[$oldLinkedId][$channel]; + $this->activeChannels[$newLinkedId][$channel] = $channelData; + unset($this->activeChannels[$oldLinkedId][$channel]); + + $this->logger->writeInfo("Migrated channel $channel: $oldLinkedId -> $newLinkedId"); + } + + // 2. Очистить старый linkedId если он опустел + if (isset($this->activeChannels[$oldLinkedId]) && empty($this->activeChannels[$oldLinkedId])) { + unset($this->activeChannels[$oldLinkedId]); + $this->logger->writeInfo("Removed empty linkedId $oldLinkedId after migration"); + } + + // 3. Обновить activeBridges если канал был в bridge + foreach ($this->activeBridges[$oldLinkedId] ?? [] as $bridgeId => $channels) { + if (isset($channels[$channel])) { + $this->activeBridges[$newLinkedId][$bridgeId][$channel] = $channels[$channel]; + unset($this->activeBridges[$oldLinkedId][$bridgeId][$channel]); + } + } + } + /** * Поиск связанного канала. * @param $linkedId @@ -1048,6 +1108,10 @@ public function callEvents($parameters):void } $endpoint = self::getEndpointName($channel); + // Очистить маппинги для канала + unset($this->channelLinkedIds[$channel]); + unset($this->pickupChannels[$channel]); + // Попытка удалить канал из указанного linkedId $foundLinkedId = $linkedId; if (!isset($this->activeChannels[$linkedId][$channel])) { @@ -1104,6 +1168,17 @@ public function callEvents($parameters):void $inApp = $context === 'applications'; } + // Определение pickup-канала по extension *8XXX + if (preg_match('/^\*8(\d+)$/', $extension, $matches)) { + $this->channelLinkedIds[$channel] = $linkedId; + $this->pickupChannels[$channel] = [ + 'target_extension' => $matches[1], + 'initial_linkedid' => $linkedId, + 'pickup_time' => time() + ]; + $this->logger->writeInfo("Pickup channel detected: $channel (*8{$matches[1]}), linkedid=$linkedId"); + } + $chanData = [ 'ChannelStateDesc' => $parameters['ChannelStateDesc'] ?? '', 'CallerIDNum' => $parameters['CallerIDNum'] ?? '', @@ -1165,6 +1240,18 @@ public function callEvents($parameters):void $bridgeUniqueid = $parameters['BridgeUniqueid'] ?? ''; $beChannel = $parameters['Channel'] ?? ''; if (!empty($linkedId) && !empty($bridgeUniqueid) && !empty($beChannel)) { + // Проверить изменение linkedid для pickup-канала + if (isset($this->pickupChannels[$beChannel])) { + $initialLinkedId = $this->pickupChannels[$beChannel]['initial_linkedid']; + + if ($initialLinkedId !== $linkedId) { + // LinkedId изменился при bridge! Мигрировать канал + $this->logger->writeInfo("LinkedId changed for pickup channel $beChannel: $initialLinkedId -> $linkedId (BridgeEnter)"); + $this->migrateChannel($beChannel, $initialLinkedId, $linkedId); + $this->channelLinkedIds[$beChannel] = $linkedId; + } + } + $this->activeBridges[$linkedId][$bridgeUniqueid][$beChannel] = $parameters['Timestamp'] ?? time(); } }elseif ('ChanSpyStart' === $event){ From c8873c49fd9e8d6cb0e694ed8d4e0157cb3cddef Mon Sep 17 00:00:00 2001 From: Alexey Portnov Date: Mon, 9 Feb 2026 21:43:33 +0800 Subject: [PATCH 12/19] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B0=20=D0=BF=D1=80=D0=BE=D0=B1=D0=BB=D0=B5?= =?UTF-8?q?=D0=BC=D0=B0=20=D1=81=20=D0=BF=D1=80=D0=BE=D0=BF=D0=B0=D0=B4?= =?UTF-8?q?=D0=B0=D0=BD=D0=B8=D0=B5=D0=BC=20=D1=81=D0=BE=D0=B1=D0=B5=D1=81?= =?UTF-8?q?=D0=B5=D0=B4=D0=BD=D0=B8=D0=BA=D0=B0=20=D0=BF=D0=BE=D1=81=D0=BB?= =?UTF-8?q?=D0=B5=20Call=20Pickup=20(*8XXX)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Проблема: При перехвате вызова (Call Pickup *8233) каналы клиента и сотрудника, перехватившего звонок, оказывались с разными linkedid в одном bridge. Метод findBridgeChannel() искал связанные каналы только внутри одного linkedid, поэтому не находил канал перехватившего сотрудника. Результат: поле "Кому" в таблице становилось пустым. Решение: 1. Добавлен механизм связывания linkedId через массив linkedIdAliases 2. Метод findBridgeChannel() теперь ищет каналы во всех связанных linkedId 3. При BridgeEnter с SwapUniqueid автоматически создается алиас между linkedId исходного звонка и linkedId pickup-канала 4. Добавлена очистка устаревших алиасов при Hangup и в channelAdditionalControl() Изменения в WorkerActiveCalls.php: - Добавлен массив $linkedIdAliases для отслеживания связей - Добавлен метод resolveLinkedId() для разрешения алиасов - Обновлен findBridgeChannel() для поиска в связанных linkedId - Обновлена обработка BridgeEnter для создания алиасов - Обновлена обработка Hangup для очистки алиасов - Обновлен enrichStatesWithConnections() для использования канонических linkedId --- bin/WorkerActiveCalls.php | 117 ++++++++++++++++++++++++++++++++------ 1 file changed, 101 insertions(+), 16 deletions(-) diff --git a/bin/WorkerActiveCalls.php b/bin/WorkerActiveCalls.php index 9c3b77f..f9a918a 100644 --- a/bin/WorkerActiveCalls.php +++ b/bin/WorkerActiveCalls.php @@ -56,6 +56,7 @@ class WorkerActiveCalls extends WorkerBase // Call pickup tracking private array $pickupChannels = []; // channel => ['target_extension' => ..., 'initial_linkedid' => ..., 'pickup_time' => ...] private array $channelLinkedIds = []; // channel => current_linkedid (для отслеживания изменений) + private array $linkedIdAliases = []; // linkedId => canonical_linkedId (для объединения linkedId при pickup) // Throttling WebSocket updates private int $stateUpdateScheduled = 0; // timestamp первого изменения (ms) @@ -243,6 +244,13 @@ private function channelAdditionalControl() } } + // Cleanup orphaned linkedIdAliases + foreach (array_keys($this->linkedIdAliases) as $aliasLinkedId) { + if (!isset($channelsData[$aliasLinkedId])) { + unset($this->linkedIdAliases[$aliasLinkedId]); + } + } + // Cleanup orphaned channelLinkedIds foreach (array_keys($this->channelLinkedIds) as $channel) { $found = false; @@ -601,11 +609,12 @@ private function enrichStatesWithConnections(): array } } - // Строим карту channel -> linkedId + // Строим карту channel -> linkedId (с учетом алиасов) $channelToLinkedId = []; foreach ($this->activeChannels as $linkedId => $channels) { + $canonicalLinkedId = $this->resolveLinkedId($linkedId); foreach (array_keys($channels) as $channel) { - $channelToLinkedId[$channel] = $linkedId; + $channelToLinkedId[$channel] = $canonicalLinkedId; } } @@ -681,6 +690,25 @@ private function getQueueData(array $queueCalls, string $linkedid): array return []; } + /** + * Разрешает linkedId в канонический через цепочку алиасов. + * + * @param string $linkedId LinkedId для разрешения + * @return string Канонический linkedId + */ + private function resolveLinkedId(string $linkedId): string + { + $visited = []; + $current = $linkedId; + + while (isset($this->linkedIdAliases[$current]) && !isset($visited[$current])) { + $visited[$current] = true; + $current = $this->linkedIdAliases[$current]; + } + + return $current; + } + /** * Миграция канала из одного linkedId в другой при изменении linkedid (например, при call pickup). * @@ -735,23 +763,41 @@ private function findBridgeChannel($linkedId, &$dstChannel, &$tmpBridgeStart):bo break; } $chFound = false; - if(!isset($this->activeBridges[$linkedId])){ - break; + + // Собираем список всех linkedId для поиска (текущий + все связанные через алиасы) + $linkedIdsToSearch = [$linkedId]; + // Добавляем все linkedId, которые ссылаются на наш канонический + $canonicalLinkedId = $this->resolveLinkedId($linkedId); + if ($canonicalLinkedId !== $linkedId) { + $linkedIdsToSearch[] = $canonicalLinkedId; + } + // Добавляем все linkedId, которые имеют алиас на наш текущий или канонический + foreach ($this->linkedIdAliases as $alias => $target) { + if ($target === $linkedId || $target === $canonicalLinkedId) { + $linkedIdsToSearch[] = $alias; + } } - foreach ($this->activeBridges[$linkedId] as $bridge) { - if(count($bridge) === 1){ + + // Ищем bridge в любом из связанных linkedId + foreach ($linkedIdsToSearch as $searchLinkedId) { + if(!isset($this->activeBridges[$searchLinkedId])){ continue; } - if($dstChannel === array_key_first($bridge)){ - $dstChannel = $this->swapLocalSuffix(array_key_last($bridge)); - $tmpBridgeStart = $bridge[$dstChannel]??$tmpBridgeStart; - $chFound = true; - break; - }elseif ($dstChannel === array_key_last($bridge)){ - $dstChannel = $this->swapLocalSuffix(array_key_first($bridge)); - $tmpBridgeStart = $bridge[$dstChannel]??$tmpBridgeStart; - $chFound = true; - break; + foreach ($this->activeBridges[$searchLinkedId] as $bridge) { + if(count($bridge) === 1){ + continue; + } + if($dstChannel === array_key_first($bridge)){ + $dstChannel = $this->swapLocalSuffix(array_key_last($bridge)); + $tmpBridgeStart = $bridge[$dstChannel]??$tmpBridgeStart; + $chFound = true; + break 2; // Выход из обоих foreach + }elseif ($dstChannel === array_key_last($bridge)){ + $dstChannel = $this->swapLocalSuffix(array_key_first($bridge)); + $tmpBridgeStart = $bridge[$dstChannel]??$tmpBridgeStart; + $chFound = true; + break 2; // Выход из обоих foreach + } } } } @@ -1146,6 +1192,7 @@ public function callEvents($parameters):void if(empty($this->activeChannels[$foundLinkedId])){ unset($this->activeChannels[$foundLinkedId]); unset($this->callType[$foundLinkedId]); + unset($this->linkedIdAliases[$foundLinkedId]); // Очистить алиас если linkedId опустел } }elseif(in_array($event, ['Newchannel','Newstate']) && stripos($parameters['Channel'] ?? '', 'local') === false){ $linkedId = $parameters['Linkedid'] ?? ''; @@ -1239,10 +1286,34 @@ public function callEvents($parameters):void $linkedId = $parameters['Linkedid'] ?? ''; $bridgeUniqueid = $parameters['BridgeUniqueid'] ?? ''; $beChannel = $parameters['Channel'] ?? ''; + $swapUniqueid = $parameters['SwapUniqueid'] ?? ''; + if (!empty($linkedId) && !empty($bridgeUniqueid) && !empty($beChannel)) { + // Обработка SwapUniqueid - связывание двух linkedId при call pickup + if (!empty($swapUniqueid)) { + // SwapUniqueid - это uniqueid канала, который был в bridge до swap + // Найдем его linkedId + $swapLinkedId = ''; + foreach ($this->activeChannels as $lid => $channels) { + foreach ($channels as $ch => $chData) { + if (($chData['Uniqueid'] ?? '') === $swapUniqueid) { + $swapLinkedId = $lid; + break 2; + } + } + } + + if (!empty($swapLinkedId) && $swapLinkedId !== $linkedId) { + // Создаем связь между linkedId (swap linkedId -> текущий linkedId) + $this->linkedIdAliases[$swapLinkedId] = $linkedId; + $this->logger->writeInfo("LinkedId alias created: $swapLinkedId -> $linkedId (SwapUniqueid=$swapUniqueid, pickup bridge)"); + } + } + // Проверить изменение linkedid для pickup-канала if (isset($this->pickupChannels[$beChannel])) { $initialLinkedId = $this->pickupChannels[$beChannel]['initial_linkedid']; + $targetExtension = $this->pickupChannels[$beChannel]['target_extension']; if ($initialLinkedId !== $linkedId) { // LinkedId изменился при bridge! Мигрировать канал @@ -1250,6 +1321,20 @@ public function callEvents($parameters):void $this->migrateChannel($beChannel, $initialLinkedId, $linkedId); $this->channelLinkedIds[$beChannel] = $linkedId; } + + // Найти linkedId исходного вызова (куда звонили target_extension) + // и связать его с linkedId pickup-канала + foreach ($this->activeChannels as $lid => $channels) { + foreach ($channels as $ch => $chData) { + $endpoint = self::getEndpointName($ch); + if ($endpoint === $targetExtension && $lid !== $linkedId) { + // Нашли исходный вызов - создаем алиас + $this->linkedIdAliases[$lid] = $linkedId; + $this->logger->writeInfo("LinkedId alias created for pickup: $lid -> $linkedId (target=$targetExtension)"); + break 2; + } + } + } } $this->activeBridges[$linkedId][$bridgeUniqueid][$beChannel] = $parameters['Timestamp'] ?? time(); From 9e6548ab55c0f0f45e3e4126c40abe1888d87770 Mon Sep 17 00:00:00 2001 From: Alexey Portnov Date: Mon, 9 Feb 2026 23:05:17 +0800 Subject: [PATCH 13/19] =?UTF-8?q?=D0=9F=D0=BE=D0=BB=D0=BD=D0=BE=D0=B5=20?= =?UTF-8?q?=D0=B8=D1=81=D0=BF=D1=80=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=D0=B8?= =?UTF-8?q?=D0=B5=20Call=20Pickup:=20=D0=BC=D0=B8=D0=B3=D1=80=D0=B0=D1=86?= =?UTF-8?q?=D0=B8=D1=8F=20=D0=BC=D0=B5=D1=82=D0=B0=D0=B4=D0=B0=D0=BD=D0=BD?= =?UTF-8?q?=D1=8B=D1=85=20=D0=B8=20code=20cleanup?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Критические изменения: - Метод migrateChannel теперь переносит callType, spyerChannels, activeBridges - Добавлена очистка пустых linkedId для предотвращения дублирования звонков - Добавлена проверка $oldLinkedId === $newLinkedId для оптимизации Code cleanup: - Все комментарии переписаны на английский - Убрано избыточное debug логирование в BridgeEnter - Улучшена читаемость логики обработки pickup Результат: перехваченные звонки (*8XXX) теперь корректно отображаются как один звонок с полными данными о pickup-канале. --- bin/WorkerActiveCalls.php | 141 ++++++++++++++++++++++++++++---------- 1 file changed, 103 insertions(+), 38 deletions(-) diff --git a/bin/WorkerActiveCalls.php b/bin/WorkerActiveCalls.php index f9a918a..39a35a2 100644 --- a/bin/WorkerActiveCalls.php +++ b/bin/WorkerActiveCalls.php @@ -710,36 +710,64 @@ private function resolveLinkedId(string $linkedId): string } /** - * Миграция канала из одного linkedId в другой при изменении linkedid (например, при call pickup). + * Migrates a channel from one linkedId to another when linkedid changes (e.g., during call pickup). + * Also migrates all associated metadata (callType, bridges, spy channels). * - * @param string $channel Имя канала - * @param string $oldLinkedId Старый linkedid - * @param string $newLinkedId Новый linkedid + * @param string $channel Channel name to migrate + * @param string $oldLinkedId Source linkedid + * @param string $newLinkedId Target linkedid * @return void */ private function migrateChannel(string $channel, string $oldLinkedId, string $newLinkedId): void { - // 1. Переместить данные канала из activeChannels + if ($oldLinkedId === $newLinkedId) { + return; // Nothing to migrate + } + + // 1. Migrate channel data from activeChannels if (isset($this->activeChannels[$oldLinkedId][$channel])) { - $channelData = $this->activeChannels[$oldLinkedId][$channel]; - $this->activeChannels[$newLinkedId][$channel] = $channelData; + $this->activeChannels[$newLinkedId][$channel] = $this->activeChannels[$oldLinkedId][$channel]; unset($this->activeChannels[$oldLinkedId][$channel]); + } - $this->logger->writeInfo("Migrated channel $channel: $oldLinkedId -> $newLinkedId"); + // 2. Migrate call metadata (callType) if old linkedId has no more channels + if (isset($this->callType[$oldLinkedId]) && empty($this->activeChannels[$oldLinkedId])) { + if (!isset($this->callType[$newLinkedId])) { + $this->callType[$newLinkedId] = $this->callType[$oldLinkedId]; + } + unset($this->callType[$oldLinkedId]); } - // 2. Очистить старый linkedId если он опустел - if (isset($this->activeChannels[$oldLinkedId]) && empty($this->activeChannels[$oldLinkedId])) { - unset($this->activeChannels[$oldLinkedId]); - $this->logger->writeInfo("Removed empty linkedId $oldLinkedId after migration"); + // 3. Migrate bridge data + if (isset($this->activeBridges[$oldLinkedId])) { + foreach ($this->activeBridges[$oldLinkedId] as $bridgeId => $channels) { + if (isset($channels[$channel])) { + $this->activeBridges[$newLinkedId][$bridgeId][$channel] = $channels[$channel]; + unset($this->activeBridges[$oldLinkedId][$bridgeId][$channel]); + } + } + // Clean up empty bridges + foreach ($this->activeBridges[$oldLinkedId] as $bridgeId => $channels) { + if (empty($channels)) { + unset($this->activeBridges[$oldLinkedId][$bridgeId]); + } + } + if (empty($this->activeBridges[$oldLinkedId])) { + unset($this->activeBridges[$oldLinkedId]); + } } - // 3. Обновить activeBridges если канал был в bridge - foreach ($this->activeBridges[$oldLinkedId] ?? [] as $bridgeId => $channels) { - if (isset($channels[$channel])) { - $this->activeBridges[$newLinkedId][$bridgeId][$channel] = $channels[$channel]; - unset($this->activeBridges[$oldLinkedId][$bridgeId][$channel]); + // 4. Migrate spy channel data + if (isset($this->spyerChannels[$oldLinkedId]) && empty($this->activeChannels[$oldLinkedId])) { + if (!isset($this->spyerChannels[$newLinkedId])) { + $this->spyerChannels[$newLinkedId] = $this->spyerChannels[$oldLinkedId]; } + unset($this->spyerChannels[$oldLinkedId]); + } + + // 5. Clean up old linkedId if completely empty + if (isset($this->activeChannels[$oldLinkedId]) && empty($this->activeChannels[$oldLinkedId])) { + unset($this->activeChannels[$oldLinkedId]); } } @@ -1289,10 +1317,8 @@ public function callEvents($parameters):void $swapUniqueid = $parameters['SwapUniqueid'] ?? ''; if (!empty($linkedId) && !empty($bridgeUniqueid) && !empty($beChannel)) { - // Обработка SwapUniqueid - связывание двух linkedId при call pickup + // Handle SwapUniqueid - links two linkedIds during call pickup if (!empty($swapUniqueid)) { - // SwapUniqueid - это uniqueid канала, который был в bridge до swap - // Найдем его linkedId $swapLinkedId = ''; foreach ($this->activeChannels as $lid => $channels) { foreach ($channels as $ch => $chData) { @@ -1304,40 +1330,79 @@ public function callEvents($parameters):void } if (!empty($swapLinkedId) && $swapLinkedId !== $linkedId) { - // Создаем связь между linkedId (swap linkedId -> текущий linkedId) $this->linkedIdAliases[$swapLinkedId] = $linkedId; - $this->logger->writeInfo("LinkedId alias created: $swapLinkedId -> $linkedId (SwapUniqueid=$swapUniqueid, pickup bridge)"); } } - // Проверить изменение linkedid для pickup-канала + // Add channel to bridge first (required for subsequent lookups) + $this->activeBridges[$linkedId][$bridgeUniqueid][$beChannel] = $parameters['Timestamp'] ?? time(); + + // Handle pickup channel entering bridge if (isset($this->pickupChannels[$beChannel])) { $initialLinkedId = $this->pickupChannels[$beChannel]['initial_linkedid']; - $targetExtension = $this->pickupChannels[$beChannel]['target_extension']; + // Check if linkedId changed during bridge entry if ($initialLinkedId !== $linkedId) { - // LinkedId изменился при bridge! Мигрировать канал - $this->logger->writeInfo("LinkedId changed for pickup channel $beChannel: $initialLinkedId -> $linkedId (BridgeEnter)"); $this->migrateChannel($beChannel, $initialLinkedId, $linkedId); $this->channelLinkedIds[$beChannel] = $linkedId; } - // Найти linkedId исходного вызова (куда звонили target_extension) - // и связать его с linkedId pickup-канала - foreach ($this->activeChannels as $lid => $channels) { - foreach ($channels as $ch => $chData) { - $endpoint = self::getEndpointName($ch); - if ($endpoint === $targetExtension && $lid !== $linkedId) { - // Нашли исходный вызов - создаем алиас - $this->linkedIdAliases[$lid] = $linkedId; - $this->logger->writeInfo("LinkedId alias created for pickup: $lid -> $linkedId (target=$targetExtension)"); - break 2; + // Find other linkedIds in the same bridge and create alias + // (Asterisk changes linkedId asynchronously AFTER BridgeEnter, + // so we create the alias proactively) + $aliasCreated = false; + foreach ($this->activeBridges as $lid => $bridges) { + if ($lid === $linkedId || $lid === $initialLinkedId) { + continue; + } + if (isset($bridges[$bridgeUniqueid]) && !empty($bridges[$bridgeUniqueid])) { + $this->linkedIdAliases[$initialLinkedId] = $lid; + if ($initialLinkedId !== $linkedId) { + $this->migrateChannel($beChannel, $linkedId, $lid); } + $aliasCreated = true; + break; } } - } - $this->activeBridges[$linkedId][$bridgeUniqueid][$beChannel] = $parameters['Timestamp'] ?? time(); + // Fallback: search by target extension if alias not created + if (!$aliasCreated) { + $targetExtension = $this->pickupChannels[$beChannel]['target_extension']; + foreach ($this->activeChannels as $lid => $channels) { + foreach ($channels as $ch => $chData) { + $endpoint = self::getEndpointName($ch); + if ($endpoint === $targetExtension && $lid !== $linkedId && $lid !== $initialLinkedId) { + $this->linkedIdAliases[$initialLinkedId] = $lid; + if ($initialLinkedId !== $linkedId) { + $this->migrateChannel($beChannel, $linkedId, $lid); + } + break 2; + } + } + } + } + } else { + // Not a pickup channel: check if bridge contains a pickup channel + // (reverse scenario: pickup channel entered first, normal channel entering now) + foreach ($this->activeBridges as $lid => $bridges) { + if ($lid === $linkedId) { + continue; + } + if (isset($bridges[$bridgeUniqueid])) { + foreach ($bridges[$bridgeUniqueid] as $bridgeChannel => $_) { + if (isset($this->pickupChannels[$bridgeChannel])) { + $pickupInitialLinkedId = $this->pickupChannels[$bridgeChannel]['initial_linkedid']; + if ($pickupInitialLinkedId !== $linkedId) { + $this->linkedIdAliases[$pickupInitialLinkedId] = $linkedId; + $this->migrateChannel($bridgeChannel, $pickupInitialLinkedId, $linkedId); + $this->channelLinkedIds[$bridgeChannel] = $linkedId; + break 2; + } + } + } + } + } + } } }elseif ('ChanSpyStart' === $event){ $spyerChannel = $parameters['SpyerChannel'] ?? ''; From bf17fc75f651d3daeee70540fb4a38d6fa1c3fbf Mon Sep 17 00:00:00 2001 From: Alexey Portnov Date: Sat, 14 Feb 2026 23:12:59 +0800 Subject: [PATCH 14/19] =?UTF-8?q?=D0=91=D0=B5=D0=B7=D0=BE=D0=BF=D0=B0?= =?UTF-8?q?=D1=81=D0=BD=D0=BE=D1=81=D1=82=D1=8C=20=D1=82=D0=BE=D0=BA=D0=B5?= =?UTF-8?q?=D0=BD=D0=BE=D0=B2:=20=D1=81=D0=B5=D1=80=D0=B2=D0=B8=D1=81?= =?UTF-8?q?=D0=BD=D1=8B=D0=B9=20=D1=82=D0=BE=D0=BA=D0=B5=D0=BD=20+=20refre?= =?UTF-8?q?sh=20endpoint?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Заменён createLoginResponse('1','admin') на createServiceToken() (требует добавления метода в ModuleSoftphoneBackend) - JS: добавлен refreshAuthToken() через /auth/refresh вместо повторной генерации токенов - Старые refresh-токены теперь попадают в blacklist на сервере --- .../ModuleMonitorActiveCallsController.php | 8 +- .../js/module-monitor-active-calls-index.js | 93 ++++++++++++------- .../module-monitor-active-calls-index.js.map | 2 +- .../src/module-monitor-active-calls-index.js | 31 ++++++- 4 files changed, 92 insertions(+), 42 deletions(-) diff --git a/App/Controllers/ModuleMonitorActiveCallsController.php b/App/Controllers/ModuleMonitorActiveCallsController.php index b8a9ff7..3ba39b8 100644 --- a/App/Controllers/ModuleMonitorActiveCallsController.php +++ b/App/Controllers/ModuleMonitorActiveCallsController.php @@ -152,11 +152,9 @@ public function saveAction() :void public function backandEnableAction() :void { if (MonitorActiveCallsMain::backendExists()) { - // Модуль включен. - $api = new ModuleSoftphoneBackendApi(); - $api->initialize(); - $this->view->data = $api->createLoginResponse('1', 'admin'); - $this->view->success = true; + $result = ModuleSoftphoneBackendApi::createServiceToken('ModuleMonitorActiveCalls'); + $this->view->data = $result; + $this->view->success = !empty($result['success']); }else{ $this->view->success = false; $this->view->data = []; diff --git a/public/assets/js/module-monitor-active-calls-index.js b/public/assets/js/module-monitor-active-calls-index.js index 4cd8e45..83f6720 100644 --- a/public/assets/js/module-monitor-active-calls-index.js +++ b/public/assets/js/module-monitor-active-calls-index.js @@ -1021,6 +1021,35 @@ var ModuleMonitorActiveCalls = { this._authTokens.refresh_token = refreshToken; this._authTokens.exp = this.getJwtExp(accessToken); }, + refreshAuthToken: function refreshAuthToken() { + var _this$_authTokens, + _this4 = this; + var refreshToken = (_this$_authTokens = this._authTokens) === null || _this$_authTokens === void 0 ? void 0 : _this$_authTokens.refresh_token; + if (!refreshToken) { + this.requestBackendEnable(); + return; + } + $.ajax({ + url: '/pbxcore/api/module-softphone-backend/v1/auth/refresh', + method: 'POST', + headers: { + 'Authorization': 'Bearer ' + refreshToken + }, + success: function success(response) { + var accessToken = response === null || response === void 0 ? void 0 : response.access_token; + var newRefreshToken = response === null || response === void 0 ? void 0 : response.refresh_token; + if (accessToken && newRefreshToken) { + _this4.setAuthTokens(accessToken, newRefreshToken); + _this4.scheduleContactsWsTokenRefresh(); + } else { + _this4.requestBackendEnable(); + } + }, + error: function error() { + _this4.requestBackendEnable(); + } + }); + }, getJwtExp: function getJwtExp(token) { try { if (!token || typeof token !== 'string') return 0; @@ -1036,32 +1065,30 @@ var ModuleMonitorActiveCalls = { } }, isAccessTokenExpired: function isAccessTokenExpired() { - var _this$_authTokens; + var _this$_authTokens2; var skewSeconds = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0; - var exp = Number((_this$_authTokens = this._authTokens) === null || _this$_authTokens === void 0 ? void 0 : _this$_authTokens.exp) || 0; + var exp = Number((_this$_authTokens2 = this._authTokens) === null || _this$_authTokens2 === void 0 ? void 0 : _this$_authTokens2.exp) || 0; if (!exp) return false; // unknown exp -> don't force refresh var now = Math.floor(Date.now() / 1000); return now + skewSeconds >= exp; }, scheduleContactsWsTokenRefresh: function scheduleContactsWsTokenRefresh() { - var _this$_authTokens2, - _this4 = this; - // Proactively refresh token shortly before expiry by re-requesting backendEnable. + var _this$_authTokens3, + _this5 = this; if (this._contactsWsTokenTimer) { clearTimeout(this._contactsWsTokenTimer); this._contactsWsTokenTimer = null; } - var exp = Number((_this$_authTokens2 = this._authTokens) === null || _this$_authTokens2 === void 0 ? void 0 : _this$_authTokens2.exp) || 0; + var exp = Number((_this$_authTokens3 = this._authTokens) === null || _this$_authTokens3 === void 0 ? void 0 : _this$_authTokens3.exp) || 0; if (!exp) return; var now = Math.floor(Date.now() / 1000); var refreshInSec = Math.max(1, exp - now - 15); // 15s before exp this._contactsWsTokenTimer = setTimeout(function () { - // Re-get tokens and reconnect WS - _this4.requestBackendEnable(); + _this5.refreshAuthToken(); }, refreshInSec * 1000); }, scheduleContactsWsReconnect: function scheduleContactsWsReconnect(reason) { - var _this5 = this; + var _this6 = this; var forceReAuth = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; if (this._contactsWsReconnectTimer) { clearTimeout(this._contactsWsReconnectTimer); @@ -1070,10 +1097,10 @@ var ModuleMonitorActiveCalls = { this._contactsWsReconnectAttempt = (this._contactsWsReconnectAttempt || 0) + 1; var delay = Math.min(30000, 1000 * Math.pow(2, Math.min(5, this._contactsWsReconnectAttempt - 1))); this._contactsWsReconnectTimer = setTimeout(function () { - if (forceReAuth || _this5.isAccessTokenExpired(5)) { - _this5.requestBackendEnable(); + if (forceReAuth || _this6.isAccessTokenExpired(5)) { + _this6.requestBackendEnable(); } else { - _this5.connectContactsWs(); + _this6.connectContactsWs(); } }, delay); console.log('contacts ws reconnect scheduled', { @@ -1082,10 +1109,10 @@ var ModuleMonitorActiveCalls = { }); }, connectContactsWs: function connectContactsWs() { - var _this6 = this; + var _this7 = this; try { - var _this$_authTokens3; - var accessToken = (_this$_authTokens3 = this._authTokens) === null || _this$_authTokens3 === void 0 ? void 0 : _this$_authTokens3.access_token; + var _this$_authTokens4; + var accessToken = (_this$_authTokens4 = this._authTokens) === null || _this$_authTokens4 === void 0 ? void 0 : _this$_authTokens4.access_token; if (!accessToken) return; // Avoid reconnecting if already connected/connecting @@ -1101,10 +1128,10 @@ var ModuleMonitorActiveCalls = { this._contactsWs = new WebSocket(wsUrl); this._contactsWs.onopen = function () { console.log('contacts ws connected'); - _this6.scheduleContactsWsTokenRefresh(); + _this7.scheduleContactsWsTokenRefresh(); }; this._contactsWs.onmessage = function (event) { - _this6.handleContactsWsMessage(event === null || event === void 0 ? void 0 : event.data); + _this7.handleContactsWsMessage(event === null || event === void 0 ? void 0 : event.data); }; this._contactsWs.onerror = function (event) { console.log('contacts ws error', event); @@ -1116,15 +1143,15 @@ var ModuleMonitorActiveCalls = { code: code, reason: reason }); - if (_this6._contactsWsTokenTimer) { - clearTimeout(_this6._contactsWsTokenTimer); - _this6._contactsWsTokenTimer = null; + if (_this7._contactsWsTokenTimer) { + clearTimeout(_this7._contactsWsTokenTimer); + _this7._contactsWsTokenTimer = null; } // 1000 = normal close -> reconnect; auth closes vary by server implementation. var authCloseCodes = new Set([1008, 4001, 4401, 4403]); - var forceReAuth = authCloseCodes.has(code) || _this6.isAccessTokenExpired(0); - _this6.scheduleContactsWsReconnect('close', forceReAuth); + var forceReAuth = authCloseCodes.has(code) || _this7.isAccessTokenExpired(0); + _this7.scheduleContactsWsReconnect('close', forceReAuth); }; } catch (e) { console.log('contacts ws init error', e); @@ -1132,7 +1159,7 @@ var ModuleMonitorActiveCalls = { } }, scheduleActiveCallsWsReconnect: function scheduleActiveCallsWsReconnect(reason) { - var _this7 = this; + var _this8 = this; var forceReAuth = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; if (this._activeCallsWsReconnectTimer) { clearTimeout(this._activeCallsWsReconnectTimer); @@ -1141,10 +1168,10 @@ var ModuleMonitorActiveCalls = { this._activeCallsWsReconnectAttempt = (this._activeCallsWsReconnectAttempt || 0) + 1; var delay = Math.min(30000, 1000 * Math.pow(2, Math.min(5, this._activeCallsWsReconnectAttempt - 1))); this._activeCallsWsReconnectTimer = setTimeout(function () { - if (forceReAuth || _this7.isAccessTokenExpired(5)) { - _this7.requestBackendEnable(); + if (forceReAuth || _this8.isAccessTokenExpired(5)) { + _this8.requestBackendEnable(); } else { - _this7.connectActiveCallsWs(); + _this8.connectActiveCallsWs(); } }, delay); console.log('active-calls ws reconnect scheduled', { @@ -1153,10 +1180,10 @@ var ModuleMonitorActiveCalls = { }); }, connectActiveCallsWs: function connectActiveCallsWs() { - var _this8 = this; + var _this9 = this; try { - var _this$_authTokens4; - var accessToken = (_this$_authTokens4 = this._authTokens) === null || _this$_authTokens4 === void 0 ? void 0 : _this$_authTokens4.access_token; + var _this$_authTokens5; + var accessToken = (_this$_authTokens5 = this._authTokens) === null || _this$_authTokens5 === void 0 ? void 0 : _this$_authTokens5.access_token; if (!accessToken) return; // Avoid reconnecting if already connected/connecting @@ -1176,10 +1203,10 @@ var ModuleMonitorActiveCalls = { this._activeCallsWs.onopen = function () { console.log('active-calls ws connected'); // Reuse the same token refresh timer (it triggers requestBackendEnable) - _this8.scheduleContactsWsTokenRefresh(); + _this9.scheduleContactsWsTokenRefresh(); }; this._activeCallsWs.onmessage = function (event) { - _this8.handleActiveCallsWsMessage(event === null || event === void 0 ? void 0 : event.data); + _this9.handleActiveCallsWsMessage(event === null || event === void 0 ? void 0 : event.data); }; this._activeCallsWs.onerror = function (event) { console.log('active-calls ws error', event); @@ -1194,8 +1221,8 @@ var ModuleMonitorActiveCalls = { // Auth closes vary by server implementation. var authCloseCodes = new Set([1008, 4001, 4401, 4403]); - var forceReAuth = authCloseCodes.has(code) || _this8.isAccessTokenExpired(0); - _this8.scheduleActiveCallsWsReconnect('close', forceReAuth); + var forceReAuth = authCloseCodes.has(code) || _this9.isAccessTokenExpired(0); + _this9.scheduleActiveCallsWsReconnect('close', forceReAuth); }; } catch (e) { console.log('active-calls ws init error', e); diff --git a/public/assets/js/module-monitor-active-calls-index.js.map b/public/assets/js/module-monitor-active-calls-index.js.map index 1058f22..6a6a1e0 100644 --- a/public/assets/js/module-monitor-active-calls-index.js.map +++ b/public/assets/js/module-monitor-active-calls-index.js.map @@ -1 +1 @@ -{"version":3,"file":"module-monitor-active-calls-index.js","names":["e","t","r","Symbol","n","iterator","o","toStringTag","i","c","prototype","Generator","u","Object","create","_regeneratorDefine2","f","p","y","G","v","a","d","bind","length","l","TypeError","call","done","value","return","GeneratorFunction","GeneratorFunctionPrototype","getPrototypeOf","setPrototypeOf","__proto__","displayName","_regenerator","w","m","defineProperty","_regeneratorDefine","_invoke","enumerable","configurable","writable","asyncGeneratorStep","Promise","resolve","then","_asyncToGenerator","arguments","apply","_next","_throw","_toConsumableArray","_arrayWithoutHoles","_iterableToArray","_unsupportedIterableToArray","_nonIterableSpread","Array","from","isArray","_arrayLikeToArray","_createForOfIteratorHelper","_n","F","s","next","ownKeys","keys","getOwnPropertySymbols","filter","getOwnPropertyDescriptor","push","_objectSpread","forEach","_defineProperty","getOwnPropertyDescriptors","defineProperties","_toPropertyKey","_toPrimitive","_typeof","toPrimitive","String","Number","_slicedToArray","_arrayWithHoles","_iterableToArrayLimit","_nonIterableRest","toString","slice","constructor","name","test","idUrl","idForm","className","inputClassName","ModuleMonitorActiveCalls","isInit","contactsCacheTtlMs","queuesFilterSelector","$formObj","$","$checkBoxes","$dropDowns","activeChannelsUrl","globalRootUrl","activeChannelsUrlV2","backendEnableUrl","executeCallUrl","saveUserActionUrl","$widget","undefined","validateRules","initialize","initContactsCache","requestBackendEnable","dropdown","onChange","text","$choice","window","onChangeSetting","val","userNumber","$widgetQueues","Vue","el","delimiters","methods","updatedCallsFromResponse","data","lastActiveCallsPayload","minWaitVisible","queues","allCalls","calls","initQueuesFilter","$nextTick","normalizeAgentCards","self","$filter","currentSelection","selectedQueueIds","find","hide","show","fullTextSearch","selectedIds","split","JSON","stringify","savedQueueIds","raw","parse","refreshFromLastPayload","getQueueCalls","queueId","queue","getQueueAgentsList","agents","buildAgentsList","hasWaitingCalls","dst_chan","queueData","EnterTime","elapsed","formatElapsedTime","agentsObj","entries","available","unavailable","_i","_entries","_entries$_i","number","agent","state","item","concat","normalizePhone10","phone","digits","replace","updateContactFromWs","contact","phone10","client","trim","$set","contactsByPhone10","getClientNameByPhone","getClientHeader","hasClientByPhone","enterTime","nowTick","$el","artifacts","querySelectorAll","remove","ensureAgentCardsGridMasonry","cardsContainers","cardsContainer","style","alignItems","alignContent","cards","card","alignSelf","headers","fontSize","lineHeight","display","gap","whiteSpace","metas","numLabels","paddingTop","paddingBottom","flex","minWidth","maxWidth","overflow","textOverflow","names","requestAnimationFrame","layoutAgentCardsGridMasonry","adjustAgentCardsGap","container","querySelector","tallCard","shortCard","ht","getBoundingClientRect","height","hs","isFinite","Math","max","min","round","setProperty","adjustAgentCardsColumnCount","clientWidth","minCardWidth","cs","getComputedStyle","gapRaw","columnGap","getPropertyValue","gapPx","parseFloat","count","floor","styleId","styleEl","document","getElementById","createElement","id","head","appendChild","textContent","classList","add","_agentCardsResizeBound","addEventListener","grids","grid","layoutSingleGridMasonry","rowHeight","rowGap","items","gridRowEnd","minHeight","tall","short","h","span","ceil","shortHeights","map","tallHeights","g","shortH","tallH","effectiveGap","getSrcNumForAgent","agentNumber","result","answeredFound","_iterator","_step","dst_num","src_num","calledChannels","match","ch","bridgeChannels","err","tmpCall","spyer","spy_num","exten","hasPeerPhone","getPeerPhoneLabel","getPeerNameLabel","applyContactsCacheToQueueWidget","$callsWidget","fullAccess","callIsVisible","getWaitTime","formatTimestampToTime","timestamp","ts","ms","date","Date","hours","getHours","padStart","minutes","getMinutes","seconds","getSeconds","answer","now","secondToTime","start","getCallTime","_data$calls","Extensions","updatePhonesRepresent","q","hangupAction","event","target","attr","parent","executeCallAction","action","ch1","ch2","joinAction","whisperAction","spChannel","listenAction","lines","checkbox","initializeForm","tab","startUiTicker","removeClass","startPollingActiveCalls","setTimeout","_uiTicker","setInterval","_activeCallsPollTimer","updateLines","stopPollingActiveCalls","clearInterval","_this","_callee","_t","_context","idbLoadAllContacts","_contactsCacheByPhone10","console","log","_i2","_Object$entries","_Object$entries$_i","idbOpenContactsDb","reject","req","indexedDB","open","onupgradeneeded","db","objectStoreNames","contains","createObjectStore","keyPath","onsuccess","onerror","error","idbPutContact","_this2","_callee2","_context2","tx","transaction","store","objectStore","put","updatedAt","oncomplete","close","_this3","_callee3","_context3","getAll","ttlMs","_iterator2","_step2","row","isFresh","delete","api","url","on","method","onSuccess","response","_response$data","_response$data2","accessToken","access_token","refreshToken","refresh_token","setAuthTokens","connectContactsWs","connectActiveCallsWs","onFailure","onError","errorMessage","element","xhr","_authTokens","exp","getJwtExp","token","parts","payloadB64","padded","repeat","json","atob","payload","isAccessTokenExpired","_this$_authTokens","skewSeconds","scheduleContactsWsTokenRefresh","_this$_authTokens2","_this4","_contactsWsTokenTimer","clearTimeout","refreshInSec","scheduleContactsWsReconnect","reason","_this5","forceReAuth","_contactsWsReconnectTimer","_contactsWsReconnectAttempt","delay","pow","delayMs","_this6","_this$_authTokens3","_contactsWs","readyState","WebSocket","OPEN","CONNECTING","wsProto","location","protocol","wsHost","host","tokenParam","encodeURIComponent","wsUrl","onopen","onmessage","handleContactsWsMessage","onclose","code","authCloseCodes","Set","has","scheduleActiveCallsWsReconnect","_this7","_activeCallsWsReconnectTimer","_activeCallsWsReconnectAttempt","_this8","_this$_authTokens4","_activeCallsWs","handleActiveCallsWsMessage","parsed","_iterator3","_step3","catch","$forceUpdate","_parsed$data","diffSeconds","settingName","successTest","success","href","cbBeforeSendForm","settings","form","cbAfterSendForm","Form","ready"],"sources":["src/module-monitor-active-calls-index.js"],"sourcesContent":["/*\n * Copyright (C) MIKO LLC - All Rights Reserved\n * Unauthorized copying of this file, via any medium is strictly prohibited\n * Proprietary and confidential\n * Written by Nikolay Beketov, 11 2018\n *\n */\nconst idUrl = 'module-monitor-active-calls';\nconst idForm = 'module-monitor-active-calls-form';\nconst className = 'ModuleMonitorActiveCalls';\nconst inputClassName = 'mikopbx-module-input';\n\n/* global $, globalRootUrl, globalTranslate, Form, Config, Vue, Extensions */\nconst ModuleMonitorActiveCalls = {\n\tisInit: true,\n\tcontactsCacheTtlMs: 120 * 60 * 1000,\n\tqueuesFilterSelector: '#queuesFilter',\n\t$formObj: $('#'+idForm),\n\t$checkBoxes: $('#'+idForm+' .ui.checkbox'),\n\t$dropDowns: $('#'+idForm+' .ui.dropdown'),\n\tactiveChannelsUrl: globalRootUrl + idUrl + \"/getActiveChannels\",\n\tactiveChannelsUrlV2: globalRootUrl + idUrl + \"/getActiveChannelsV2\",\n\tbackendEnableUrl: globalRootUrl + idUrl + \"/backandEnable\",\n\texecuteCallUrl: globalRootUrl + idUrl + \"/executeCall\",\n\tsaveUserActionUrl: globalRootUrl + idUrl + \"/saveUser\",\n\t$widget: undefined,\n\n\t/**\n\t * Field validation rules\n\t * https://semantic-ui.com/behaviors/form.html\n\t */\n\tvalidateRules: {},\n\t/**\n\t * On page load we init some Semantic UI library\n\t */\n\tinitialize() {\n\t\tthis.initContactsCache();\n\t\tthis.requestBackendEnable();\n\n\t\t$(\"#nowUser.dropdown.enable\").dropdown({\n\t\t\tonChange: function onChange(value, text, $choice) {\n\t\t\t\twindow[className].onChangeSetting('adminUserId', value);\n\t\t\t}\n\t\t});\n\t\t$(\"#minWaitVisible.dropdown.enable\").dropdown({\n\t\t\tonChange: function onChange(value, text, $choice) {\n\t\t\t\t$('#minWaitVisibleValue').val(value);\n\t\t\t\twindow[className].onChangeSetting('minWaitVisible', value);\n\t\t\t}\n\t\t});\n\t\tlet userNumber = $('#userNumber').val();\n\n\t\twindow[className].$widgetQueues = new Vue({\n\t\t\tel: '#app-queue',\n\t\t\tdelimiters: [\"<%\",\"%>\"],\n\t\t\tmethods: {\n\t\t\t\tupdatedCallsFromResponse(data) {\n\t\t\t\t\t// Keep last payload to allow re-render on queue switch (WS mode).\n\t\t\t\t\tthis.lastActiveCallsPayload = data;\n\n\t\t\t\t\tthis.minWaitVisible = 1*$('#minWaitVisibleValue').val();\n\t\t\t\t\tthis.queues = data.queues || {};\n\t\t\t\t\tthis.allCalls = data.calls || [];\n\n\t\t\t\t\t// Initialize multi-select dropdown if not yet done\n\t\t\t\t\tthis.initQueuesFilter();\n\n\t\t\t\t\t// Normalize Semantic UI Card typography after render\n\t\t\t\t\tthis.$nextTick(function() {\n\t\t\t\t\t\tthis.normalizeAgentCards();\n\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t\tinitQueuesFilter() {\n\t\t\t\t\tvar self = this;\n\t\t\t\t\tvar $filter = $(window[className].queuesFilterSelector);\n\t\t\t\t\tif ($filter.length === 0) return;\n\n\t\t\t\t\t// Wait for Vue to render menu items\n\t\t\t\t\tthis.$nextTick(function() {\n\t\t\t\t\t\t// Reinitialize dropdown to pick up new menu items\n\t\t\t\t\t\tif ($filter.data('initialized')) {\n\t\t\t\t\t\t\t// Dropdown already exists, just refresh menu\n\t\t\t\t\t\t\t// Save current selection before refresh to prevent reset\n\t\t\t\t\t\t\tvar currentSelection = self.selectedQueueIds ? self.selectedQueueIds.slice() : [];\n\t\t\t\t\t\t\t$filter.data('refreshing', true);\n\t\t\t\t\t\t\t$filter.dropdown('refresh');\n\t\t\t\t\t\t\t$filter.data('refreshing', false);\n\n\t\t\t\t\t\t\t// Restore selection after refresh if it was cleared\n\t\t\t\t\t\t\tif (currentSelection.length > 0 && (!self.selectedQueueIds || self.selectedQueueIds.length === 0)) {\n\t\t\t\t\t\t\t\tself.selectedQueueIds = currentSelection;\n\t\t\t\t\t\t\t\t$filter.dropdown('set exactly', currentSelection);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// After refresh, ensure default text is hidden if we have selections\n\t\t\t\t\t\t\tif (self.selectedQueueIds && self.selectedQueueIds.length > 0) {\n\t\t\t\t\t\t\t\t$filter.find('.default.text').hide();\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t$filter.find('.default.text').show();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t// First time initialization\n\t\t\t\t\t\t\t$filter.data('initialized', true);\n\t\t\t\t\t\t\t$filter.dropdown({\n\t\t\t\t\t\t\t\tfullTextSearch: true,\n\t\t\t\t\t\t\t\tonChange: function(value) {\n\t\t\t\t\t\t\t\t\t// Skip onChange during programmatic refresh\n\t\t\t\t\t\t\t\t\tif ($filter.data('refreshing')) {\n\t\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t// value is comma-separated string of selected queue IDs\n\t\t\t\t\t\t\t\t\tvar selectedIds = value ? value.split(',').filter(function(v) { return v !== ''; }) : [];\n\t\t\t\t\t\t\t\t\tself.selectedQueueIds = selectedIds;\n\t\t\t\t\t\t\t\t\t// Auto-save on change\n\t\t\t\t\t\t\t\t\twindow[className].onChangeSetting('queueIds', JSON.stringify(selectedIds));\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t});\n\n\t\t\t\t\t\t\t// Set initial values from hidden input\n\t\t\t\t\t\t\tvar savedQueueIds = [];\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tvar raw = $('#queueIds').val();\n\t\t\t\t\t\t\t\tsavedQueueIds = JSON.parse(raw || '[]');\n\t\t\t\t\t\t\t} catch (e) {\n\t\t\t\t\t\t\t\tsavedQueueIds = [];\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (Array.isArray(savedQueueIds) && savedQueueIds.length > 0) {\n\t\t\t\t\t\t\t\twindow[className].isInit = true;\n\t\t\t\t\t\t\t\t$filter.dropdown('set exactly', savedQueueIds);\n\t\t\t\t\t\t\t\tself.selectedQueueIds = savedQueueIds;\n\t\t\t\t\t\t\t\twindow[className].isInit = false;\n\t\t\t\t\t\t\t\t// Hide default text when values are selected\n\t\t\t\t\t\t\t\t$filter.find('.default.text').hide();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t\trefreshFromLastPayload() {\n\t\t\t\t\tif (this.lastActiveCallsPayload) {\n\t\t\t\t\t\tthis.updatedCallsFromResponse(this.lastActiveCallsPayload);\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\tgetQueueCalls(queueId) {\n\t\t\t\t\tvar queue = this.queues[queueId];\n\t\t\t\t\tif (!queue) return [];\n\t\t\t\t\treturn Array.isArray(queue.calls) ? queue.calls : [];\n\t\t\t\t},\n\t\t\t\tgetQueueAgentsList(queueId) {\n\t\t\t\t\tvar queue = this.queues[queueId];\n\t\t\t\t\tif (!queue || !queue.agents) return [];\n\t\t\t\t\treturn this.buildAgentsList(queue.agents);\n\t\t\t\t},\n\t\t\t\thasWaitingCalls(queueId) {\n\t\t\t\t\tvar calls = this.getQueueCalls(queueId);\n\t\t\t\t\tvar self = this;\n\t\t\t\t\tfor (var i = 0; i < calls.length; i++) {\n\t\t\t\t\t\tvar call = calls[i];\n\t\t\t\t\t\tif (call.dst_chan === '' && call.queueData && call.queueData.EnterTime !== undefined) {\n\t\t\t\t\t\t\tvar elapsed = self.formatElapsedTime(call.queueData.EnterTime);\n\t\t\t\t\t\t\tif (self.minWaitVisible <= elapsed) {\n\t\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\treturn false;\n\t\t\t\t},\n\t\t\t\tbuildAgentsList(agentsObj) {\n\t\t\t\t\tconst entries = Object.entries(agentsObj || {});\n\t\t\t\t\tconst available = [];\n\t\t\t\t\tconst unavailable = [];\n\t\t\t\t\tfor (const [number, agent] of entries) {\n\t\t\t\t\t\tconst state = agent?.state || '';\n\t\t\t\t\t\tconst item = { number, ...agent };\n\t\t\t\t\t\tif (state === 'Unavailable') {\n\t\t\t\t\t\t\tunavailable.push(item);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tavailable.push(item);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\treturn available.concat(unavailable);\n\t\t\t\t},\n\t\t\t\tnormalizePhone10(phone) {\n\t\t\t\t\tconst digits = String(phone || '').replace(/\\D+/g, '');\n\t\t\t\t\tif (digits.length <= 10) return digits;\n\t\t\t\t\treturn digits.slice(-10);\n\t\t\t\t},\n\t\t\t\tupdateContactFromWs(contact) {\n\t\t\t\t\tconst phone10 = this.normalizePhone10(contact?.number);\n\t\t\t\t\tif (!phone10) return;\n\t\t\t\t\tconst displayName = String(contact?.client || contact?.contact || '').trim();\n\t\t\t\t\tif (!displayName) return;\n\t\t\t\t\t// Vue2: ensure reactivity for new keys\n\t\t\t\t\tif (this.$set) {\n\t\t\t\t\t\tthis.$set(this.contactsByPhone10, phone10, displayName);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthis.contactsByPhone10[phone10] = displayName;\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\tgetClientNameByPhone(phone) {\n\t\t\t\t\tconst phone10 = this.normalizePhone10(phone);\n\t\t\t\t\treturn this.contactsByPhone10[phone10] || '';\n\t\t\t\t},\n\t\t\t\tgetClientHeader(phone) {\n\t\t\t\t\tconst client = this.getClientNameByPhone(phone);\n\t\t\t\t\tif (!client) return phone;\n\t\t\t\t\treturn `${client} <${phone}>`;\n\t\t\t\t},\n\t\t\t\thasClientByPhone(phone) {\n\t\t\t\t\treturn !!this.getClientNameByPhone(phone);\n\t\t\t\t},\n\t\t\t\tformatElapsedTime(enterTime) {\n\t\t\t\t\t// Make this method reactive to the UI ticker.\n\t\t\t\t\tvoid this.nowTick;\n\t\t\t\t\treturn window[className].formatElapsedTime(enterTime);\n\t\t\t\t},\n\t\t\t\tnormalizeAgentCards() {\n\t\t\t\t\tif (!this.$el) return;\n\t\t\t\t\tvar self = this;\n\n\t\t\t\t\t// Cleanup artifacts from previous experiments (placeholders/spacers).\n\t\t\t\t\tvar artifacts = this.$el.querySelectorAll('.agent-peer-placeholder, .agent-peer-spacer');\n\t\t\t\t\tartifacts.forEach(function(el) { el.remove(); });\n\n\t\t\t\t\t// Dense layout (masonry) that still fills left-to-right:\n\t\t\t\t\t// flex-wrap can't place items into vertical gaps under tall cards.\n\t\t\t\t\tthis.ensureAgentCardsGridMasonry();\n\n\t\t\t\t\t// Process all agent card containers (one per queue block)\n\t\t\t\t\tvar cardsContainers = this.$el.querySelectorAll('.ui.cards.agent-cards');\n\t\t\t\t\tcardsContainers.forEach(function(cardsContainer) {\n\t\t\t\t\t\tcardsContainer.style.alignItems = 'flex-start';\n\t\t\t\t\t\tcardsContainer.style.alignContent = 'flex-start';\n\t\t\t\t\t});\n\n\t\t\t\t\tvar cards = this.$el.querySelectorAll('.ui.cards.agent-cards > .ui.card.agent-card');\n\t\t\t\t\tcards.forEach(function(card) {\n\t\t\t\t\t\tcard.style.alignSelf = 'flex-start';\n\t\t\t\t\t});\n\n\t\t\t\t\t// Semantic UI makes .header bigger than normal text; we need same font size.\n\t\t\t\t\tvar headers = this.$el.querySelectorAll('.ui.card.agent-card .header.agent-card-header');\n\t\t\t\t\theaders.forEach(function(el) {\n\t\t\t\t\t\tel.style.fontSize = '1em';\n\t\t\t\t\t\tel.style.lineHeight = '1.2';\n\t\t\t\t\t\tel.style.display = 'flex';\n\t\t\t\t\t\tel.style.alignItems = 'center';\n\t\t\t\t\t\tel.style.gap = '0.5em';\n\t\t\t\t\t\tel.style.whiteSpace = 'nowrap';\n\t\t\t\t\t});\n\n\t\t\t\t\tvar metas = this.$el.querySelectorAll('.ui.card.agent-card .meta.agent-peer');\n\t\t\t\t\tmetas.forEach(function(el) {\n\t\t\t\t\t\tel.style.fontSize = '1em';\n\t\t\t\t\t\tel.style.lineHeight = '1.2';\n\t\t\t\t\t});\n\n\t\t\t\t\t// Normalize label/name typography so they have same text height.\n\t\t\t\t\tvar numLabels = this.$el.querySelectorAll('.ui.card.agent-card .agent-num-label');\n\t\t\t\t\tnumLabels.forEach(function(el) {\n\t\t\t\t\t\tel.style.fontSize = '1em';\n\t\t\t\t\t\tel.style.lineHeight = '1.2';\n\t\t\t\t\t\tel.style.display = 'inline-flex';\n\t\t\t\t\t\tel.style.alignItems = 'center';\n\t\t\t\t\t\tel.style.paddingTop = '0';\n\t\t\t\t\t\tel.style.paddingBottom = '0';\n\t\t\t\t\t\t// Allow label to shrink (otherwise long numbers force card wider than 180px)\n\t\t\t\t\t\tel.style.flex = '0 1 auto';\n\t\t\t\t\t\tel.style.minWidth = '0';\n\t\t\t\t\t\tel.style.maxWidth = '14ch';\n\t\t\t\t\t\tel.style.overflow = 'hidden';\n\t\t\t\t\t\tel.style.textOverflow = 'ellipsis';\n\t\t\t\t\t\tel.style.whiteSpace = 'nowrap';\n\t\t\t\t\t});\n\t\t\t\t\tvar names = this.$el.querySelectorAll('.ui.card.agent-card .agent-name');\n\t\t\t\t\tnames.forEach(function(el) {\n\t\t\t\t\t\tel.style.lineHeight = '1.2';\n\t\t\t\t\t\t// Ellipsis for long names (e.g. \"Салтыков-Щедрин\")\n\t\t\t\t\t\tel.style.minWidth = '0';\n\t\t\t\t\t\tel.style.flex = '1 1 auto';\n\t\t\t\t\t\tel.style.overflow = 'hidden';\n\t\t\t\t\t\tel.style.textOverflow = 'ellipsis';\n\t\t\t\t\t\tel.style.whiteSpace = 'nowrap';\n\t\t\t\t\t});\n\n\t\t\t\t\t// Grid masonry needs row-span calculation after layout.\n\t\t\t\t\trequestAnimationFrame(function() {\n\t\t\t\t\t\trequestAnimationFrame(function() {\n\t\t\t\t\t\t\tself.layoutAgentCardsGridMasonry();\n\t\t\t\t\t\t});\n\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t\tadjustAgentCardsGap() {\n\t\t\t\t\tif (!this.$el) return;\n\t\t\t\t\tconst container = this.$el.querySelector('.ui.cards.agent-cards');\n\t\t\t\t\tif (!container) return;\n\n\t\t\t\t\tconst cards = Array.from(container.querySelectorAll('.ui.card.agent-card'));\n\t\t\t\t\tif (!cards.length) return;\n\n\t\t\t\t\tconst tallCard = cards.find((c) => c.querySelector('.meta.agent-peer'));\n\t\t\t\t\tconst shortCard = cards.find((c) => !c.querySelector('.meta.agent-peer'));\n\t\t\t\t\tif (!tallCard || !shortCard) return;\n\n\t\t\t\t\tconst ht = tallCard.getBoundingClientRect().height;\n\t\t\t\t\tconst hs = shortCard.getBoundingClientRect().height;\n\t\t\t\t\tif (!ht || !hs) return;\n\n\t\t\t\t\t// From 2*(hs+g) = ht+g => g = ht - 2*hs\n\t\t\t\t\tlet gap = ht - 2 * hs;\n\t\t\t\t\tif (!Number.isFinite(gap)) return;\n\n\t\t\t\t\t// Clamp to sane range; negative means \"no extra gap needed\".\n\t\t\t\t\tgap = Math.max(0, Math.min(20, Math.round(gap)));\n\n\t\t\t\t\tcontainer.style.setProperty('--agent-card-gap', `${gap}px`);\n\t\t\t\t},\n\t\t\t\tadjustAgentCardsColumnCount() {\n\t\t\t\t\tif (!this.$el) return;\n\t\t\t\t\tconst container = this.$el.querySelector('.ui.cards.agent-cards.agent-cards-masonry');\n\t\t\t\t\tif (!container) return;\n\n\t\t\t\t\tconst w = container.clientWidth;\n\t\t\t\t\tif (!w) return;\n\n\t\t\t\t\t// Minimum acceptable card width in px (tune if needed)\n\t\t\t\t\tconst minCardWidth = 150;\n\n\t\t\t\t\tconst cs = window.getComputedStyle(container);\n\t\t\t\t\tconst gapRaw = cs.columnGap || cs.getPropertyValue('column-gap') || '16px';\n\t\t\t\t\tconst gapPx = parseFloat(gapRaw) || 16;\n\n\t\t\t\t\tconst count = Math.max(1, Math.min(12, Math.floor((w + gapPx) / (minCardWidth + gapPx))));\n\t\t\t\t\tcontainer.style.setProperty('--agent-card-col-count', String(count));\n\t\t\t\t},\n\t\t\t\tensureAgentCardsGridMasonry() {\n\t\t\t\t\tvar self = this;\n\t\t\t\t\tvar styleId = 'agent-cards-layout-style';\n\t\t\t\t\tvar styleEl = document.getElementById(styleId);\n\t\t\t\t\tif (!styleEl) {\n\t\t\t\t\t\tstyleEl = document.createElement('style');\n\t\t\t\t\t\tstyleEl.id = styleId;\n\t\t\t\t\t\tdocument.head.appendChild(styleEl);\n\t\t\t\t\t}\n\n\t\t\t\t\t// Grid masonry: fills left-to-right and can pack items into gaps.\n\t\t\t\t\t// minmax(240px, 1fr) - карточки минимум 240px, растягиваются равномерно\n\t\t\t\t\tstyleEl.textContent = '\\\n.ui.cards.agent-cards.agent-cards-grid {\\\n display: grid !important;\\\n grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));\\\n justify-content: start;\\\n gap: var(--agent-card-gap, 8px);\\\n grid-auto-rows: 1px;\\\n margin-bottom: 1em !important;\\\n}\\\n.ui.cards.agent-cards.agent-cards-grid > .ui.card.agent-card {\\\n width: 100% !important;\\\n min-width: 0;\\\n margin: 0 !important;\\\n overflow: hidden;\\\n align-self: start;\\\n}';\n\n\t\t\t\t\t// Process all agent card containers (one per queue block)\n\t\t\t\t\tvar cardsContainers = this.$el ? this.$el.querySelectorAll('.ui.cards.agent-cards') : [];\n\t\t\t\t\tcardsContainers.forEach(function(cardsContainer) {\n\t\t\t\t\t\tcardsContainer.classList.remove('agent-cards-masonry');\n\t\t\t\t\t\tcardsContainer.classList.remove('agent-cards-flex');\n\t\t\t\t\t\tcardsContainer.classList.add('agent-cards-grid');\n\t\t\t\t\t});\n\n\t\t\t\t\t// Bind once: relayout on resize.\n\t\t\t\t\tif (!this._agentCardsResizeBound) {\n\t\t\t\t\t\tthis._agentCardsResizeBound = true;\n\t\t\t\t\t\twindow.addEventListener('resize', function() {\n\t\t\t\t\t\t\tself.layoutAgentCardsGridMasonry();\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\tlayoutAgentCardsGridMasonry() {\n\t\t\t\t\tif (!this.$el) return;\n\t\t\t\t\tvar self = this;\n\n\t\t\t\t\t// Process all grid containers (one per queue block)\n\t\t\t\t\tvar grids = this.$el.querySelectorAll('.ui.cards.agent-cards.agent-cards-grid');\n\t\t\t\t\tgrids.forEach(function(grid) {\n\t\t\t\t\t\tself.layoutSingleGridMasonry(grid);\n\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t\tlayoutSingleGridMasonry(grid) {\n\t\t\t\t\tif (!grid) return;\n\n\t\t\t\t\tvar cs = window.getComputedStyle(grid);\n\t\t\t\t\tvar rowHeight = parseFloat(cs.getPropertyValue('grid-auto-rows')) || 1;\n\t\t\t\t\tvar rowGap = parseFloat(cs.getPropertyValue('row-gap')) || parseFloat(cs.getPropertyValue('gap')) || 8;\n\n\t\t\t\t\tvar items = Array.from(grid.querySelectorAll('.ui.card.agent-card'));\n\t\t\t\t\tif (!items.length) return;\n\n\t\t\t\t\t// Reset row spans and min-heights to measure natural heights.\n\t\t\t\t\titems.forEach(function(item) {\n\t\t\t\t\t\titem.style.gridRowEnd = '';\n\t\t\t\t\t\titem.style.minHeight = '';\n\t\t\t\t\t});\n\n\t\t\t\t\tvar tall = items.filter(function(c) { return c.querySelector('.meta.agent-peer'); });\n\t\t\t\t\tvar short = items.filter(function(c) { return !c.querySelector('.meta.agent-peer'); });\n\n\t\t\t\t\t// If we don't have both types, just do normal masonry spans.\n\t\t\t\t\tif (!tall.length || !short.length) {\n\t\t\t\t\t\titems.forEach(function(item) {\n\t\t\t\t\t\t\tvar h = item.getBoundingClientRect().height;\n\t\t\t\t\t\t\tvar span = Math.max(1, Math.ceil((h + rowGap) / (rowHeight + rowGap)));\n\t\t\t\t\t\t\titem.style.gridRowEnd = 'span ' + span;\n\t\t\t\t\t\t});\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tvar shortHeights = short.map(function(c) { return c.getBoundingClientRect().height; });\n\t\t\t\t\tvar tallHeights = tall.map(function(c) { return c.getBoundingClientRect().height; });\n\t\t\t\t\tvar hs = Math.max.apply(Math, shortHeights);\n\t\t\t\t\tvar ht = Math.max.apply(Math, tallHeights);\n\n\t\t\t\t\t// Want: 2*(hs + g) = (ht + g) => g = ht - 2*hs\n\t\t\t\t\tvar g = ht - 2 * hs;\n\t\t\t\t\tif (!Number.isFinite(g)) g = rowGap;\n\t\t\t\t\tg = Math.max(0, Math.min(24, Math.round(g)));\n\n\t\t\t\t\t// Apply gap and enforce min-heights so the relation holds visually.\n\t\t\t\t\tgrid.style.setProperty('--agent-card-gap', g + 'px');\n\n\t\t\t\t\tvar shortH = Math.round(hs);\n\t\t\t\t\tvar tallH = Math.round(Math.max(ht, 2 * hs + g));\n\t\t\t\t\tshort.forEach(function(c) { c.style.minHeight = shortH + 'px'; });\n\t\t\t\t\ttall.forEach(function(c) { c.style.minHeight = tallH + 'px'; });\n\n\t\t\t\t\t// Now compute row spans from final rendered heights.\n\t\t\t\t\tvar effectiveGap = g;\n\t\t\t\t\titems.forEach(function(item) {\n\t\t\t\t\t\tvar h = item.getBoundingClientRect().height;\n\t\t\t\t\t\tvar span = Math.max(1, Math.ceil((h + effectiveGap) / (rowHeight + effectiveGap)));\n\t\t\t\t\t\titem.style.gridRowEnd = 'span ' + span;\n\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t\tgetSrcNumForAgent(agentNumber) {\n\t\t\t\t\tlet result = '-';\n\t\t\t\t\tlet answeredFound = false;\n\t\t\t\t\tfor (const call of this.allCalls) {\n\t\t\t\t\t\tif(call.dst_num === agentNumber){\n\t\t\t\t\t\t\tansweredFound = true;\n\t\t\t\t\t\t\tresult = call.src_num;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (call.calledChannels && Array.isArray(call.calledChannels)) {\n\t\t\t\t\t\t\tconst match = call.calledChannels.find(ch => ch.number === agentNumber);\n\t\t\t\t\t\t\tif (match) {\n\t\t\t\t\t\t\t\tresult = call.src_num;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (call.bridgeChannels && Array.isArray(call.bridgeChannels)) {\n\t\t\t\t\t\t\tconst match = call.bridgeChannels.find(ch => (ch.src_num === agentNumber || ch.dst_num === agentNumber));\n\t\t\t\t\t\t\tif (match) {\n\t\t\t\t\t\t\t\tif(match.src_num === agentNumber){\n\t\t\t\t\t\t\t\t\tresult = match.dst_num;\n\t\t\t\t\t\t\t\t}else{\n\t\t\t\t\t\t\t\t\tresult = match.src_num;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tansweredFound = true;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif(answeredFound === false){\n\t\t\t\t\t\tfor (let i = 0; i < this.allCalls.length; i++) {\n\t\t\t\t\t\t\tconst tmpCall = this.allCalls[i];\n\t\t\t\t\t\t\tif(tmpCall.src_num === agentNumber){\n\t\t\t\t\t\t\t\t// Исходящий\n\t\t\t\t\t\t\t\tif(tmpCall.dst_num === ''){\n\t\t\t\t\t\t\t\t\t// не ответа, дозвон.\n\t\t\t\t\t\t\t\t\tif (tmpCall.calledChannels && Array.isArray(tmpCall.calledChannels) && tmpCall.calledChannels.length) {\n\t\t\t\t\t\t\t\t\t\tconst match = tmpCall.calledChannels.find(ch => ch.number !== agentNumber);\n\t\t\t\t\t\t\t\t\t\tif (match) {\n\t\t\t\t\t\t\t\t\t\t\tresult = match.number;\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}else if(tmpCall.spyer){\n\t\t\t\t\t\t\t\t\t\t// шпионит за номером.\n\t\t\t\t\t\t\t\t\t\tresult = tmpCall.spy_num;\n\t\t\t\t\t\t\t\t\t}else{\n\t\t\t\t\t\t\t\t\t\t// нет вызываемых каналов, возможно это вызов на приложение / ivr.\n\t\t\t\t\t\t\t\t\t\tresult = tmpCall.exten;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}else{\n\t\t\t\t\t\t\t\t\tresult = tmpCall.dst_num;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}else if(tmpCall.dst_num === agentNumber){\n\t\t\t\t\t\t\t\t// Входящий на агента, отвечен.\n\t\t\t\t\t\t\t\tresult = tmpCall.src_num;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}else{\n\t\t\t\t\t\t\t\tif (tmpCall.calledChannels && Array.isArray(tmpCall.calledChannels)) {\n\t\t\t\t\t\t\t\t\tconst match = tmpCall.calledChannels.find(ch => ch.number === agentNumber);\n\t\t\t\t\t\t\t\t\tif (match) {\n\t\t\t\t\t\t\t\t\t\tresult = tmpCall.src_num;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\treturn result;\n\t\t\t\t},\n\t\t\t\thasPeerPhone(agentNumber) {\n\t\t\t\t\tconst phone = String(this.getSrcNumForAgent(agentNumber) || '').trim();\n\t\t\t\t\treturn phone !== '' && phone !== '-' && phone !== '—';\n\t\t\t\t},\n\t\t\t\tgetPeerPhoneLabel(agentNumber) {\n\t\t\t\t\tconst phone = String(this.getSrcNumForAgent(agentNumber) || '').trim();\n\t\t\t\t\treturn this.hasPeerPhone(agentNumber) ? phone : '—';\n\t\t\t\t},\n\t\t\t\tgetPeerNameLabel(agentNumber) {\n\t\t\t\t\t// Use cached contacts (WS + IndexedDB) to show client name for peer phone.\n\t\t\t\t\tconst phone = this.getPeerPhoneLabel(agentNumber);\n\t\t\t\t\tconst client = this.getClientNameByPhone(phone);\n\t\t\t\t\treturn client || '—';\n\t\t\t\t}\n\t\t\t},\n\t\t\tdata: {\n\t\t\t\t\"minWaitVisible\": 30,\n\t\t\t\t\"nowTick\": 0,\n\t\t\t\t\"queues\": {},\n\t\t\t\t\"allCalls\": [],\n\t\t\t\t\"selectedQueueIds\": [],\n\t\t\t\t\"lastActiveCallsPayload\": null,\n\t\t\t\t\"contactsByPhone10\": {}\n\t\t\t},\n\t\t});\n\t\twindow[className].applyContactsCacheToQueueWidget();\n\n\t\twindow[className].$callsWidget = new Vue({\n\t\t\tel: '#calls',\n\t\t\tdelimiters: [\"<%\",\"%>\"],\n\t\t\tdata: {\n\t\t\t\t\"minWaitVisible\": 30,\n\t\t\t\t\"nowTick\": 0,\n\t\t\t\tuserNumber: userNumber,\n\t\t\t\tfullAccess: ($('#fullAccess').val() === \"1\" || userNumber === ''),\n\t\t\t\tcalls: [\n\t\t\t\t]\n\t\t\t},\n\t\t\tmethods: {\n\t\t\t\tcallIsVisible(call){\n\t\t\t\t\tvoid this.nowTick;\n\t\t\t\t\tif(call.dst_chan==='' && call.queueData.EnterTime !== undefined ){\n\t\t\t\t\t\treturn this.minWaitVisible <= this.getWaitTime(call);\n\t\t\t\t\t}\n\t\t\t\t\treturn true;\n\t\t\t\t},\n\t\t\t\tformatTimestampToTime(timestamp) {\n\t\t\t\t\t// Если timestamp строка — приводим к числу\n\t\t\t\t\tconst ts = typeof timestamp === 'string' ? parseFloat(timestamp) : timestamp;\n\n\t\t\t\t\t// Если timestamp в секундах (меньше 1e10), умножаем на 1000\n\t\t\t\t\tconst ms = ts < 1e10 ? ts * 1000 : ts;\n\n\t\t\t\t\tconst date = new Date(ms);\n\n\t\t\t\t\tconst hours = String(date.getHours()).padStart(2, '0');\n\t\t\t\t\tconst minutes = String(date.getMinutes()).padStart(2, '0');\n\t\t\t\t\tconst seconds = String(date.getSeconds()).padStart(2, '0');\n\n\t\t\t\t\treturn `${hours}:${minutes}:${seconds}`;\n\t\t\t\t},\n\t\t\t\tgetWaitTime(call){\n\t\t\t\t\tvoid this.nowTick;\n\t\t\t\t\tlet answer = Math.floor(Date.now() / 1000);\n\t\t\t\t\tif(call.answer !== ''){\n\t\t\t\t\t\tanswer = call.answer\n\t\t\t\t\t}\n\t\t\t\t\treturn window[className].secondToTime(answer - call.start);\n\t\t\t\t},\n\t\t\t\tgetCallTime(call){\n\t\t\t\t\tvoid this.nowTick;\n\t\t\t\t\tif(call.answer === ''){\n\t\t\t\t\t\treturn '-';\n\t\t\t\t\t}\n\t\t\t\t\treturn window[className].formatElapsedTime(call.answer);\n\t\t\t\t},\n\t\t\t\tupdatedCallsFromResponse(data) {\n\t\t\t\t\tthis.minWaitVisible = 1*$('#minWaitVisibleValue').val();\n\t\t\t\t\t// Проходим по всем очередям\n\t\t\t\t\tfor (const queueId in data.queues) {\n\t\t\t\t\t\tconst queue = data.queues[queueId];\n\t\t\t\t\t\t// Проверяем, есть ли у очереди поле calls и является ли оно массивом\n\t\t\t\t\t\tif (Array.isArray(queue.calls)) {\n\t\t\t\t\t\t\t// Добавляем все вызовы из этой очереди в общий массив\n\t\t\t\t\t\t\tdata.calls.push(...queue.calls);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tthis.calls = data.calls;\n\t\t\t\t\tthis.$nextTick(() => {\n\t\t\t\t\t\tExtensions.updatePhonesRepresent('need-update');\n\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t\tformatElapsedTime(enterTime) {\n\t\t\t\t\treturn window[className].formatElapsedTime(enterTime);\n\t\t\t\t},\n\t\t\t\tgetClientHeader(phone) {\n\t\t\t\t\tconst q = window[className].$widgetQueues;\n\t\t\t\t\tif (q && typeof q.getClientHeader === 'function') {\n\t\t\t\t\t\treturn q.getClientHeader(phone);\n\t\t\t\t\t}\n\t\t\t\t\treturn phone;\n\t\t\t\t},\n\t\t\t\thangupAction(event) {\n\t\t\t\t\tlet target = $(event.target);\n\t\t\t\t\tif(target.attr('data-ch1') === undefined){\n\t\t\t\t\t\ttarget = $(event.target).parent();\n\t\t\t\t\t}\n\t\t\t\t\twindow[className].executeCallAction({action: 'hangup', ch1: target.attr('data-ch1'), ch2: target.attr('data-ch2')});\n\t\t\t\t},\n\t\t\t\tjoinAction(event) {\n\t\t\t\t\tlet target = $(event.target);\n\t\t\t\t\tif(target.attr('data-ch1') === undefined){\n\t\t\t\t\t\ttarget = $(event.target).parent();\n\t\t\t\t\t}\n\t\t\t\t\tif(this.userNumber === ''){\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\twindow[className].executeCallAction({action: 'join', ch1: target.attr('data-ch1'), ch2: target.attr('data-ch2'), number: this.userNumber});\n\t\t\t\t},\n\t\t\t\twhisperAction(event){\n\t\t\t\t\tlet target = $(event.target);\n\t\t\t\t\tif(target.attr('data-ch1') === undefined){\n\t\t\t\t\t\ttarget = $(event.target).parent();\n\t\t\t\t\t}\n\t\t\t\t\tif(this.userNumber === ''){\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tlet spChannel = target.attr('data-ch1');\n\t\t\t\t\tif('incoming' === target.attr('data-call-type')){\n\t\t\t\t\t\tspChannel = target.attr('data-ch2');\n\t\t\t\t\t}\n\t\t\t\t\twindow[className].executeCallAction({action: 'whisper', ch1: spChannel, ch2: '', number: this.userNumber});\n\t\t\t\t},\n\t\t\t\tlistenAction(event){\n\t\t\t\t\tlet target = $(event.target);\n\t\t\t\t\tif(target.attr('data-ch1') === undefined){\n\t\t\t\t\t\ttarget = $(event.target).parent();\n\t\t\t\t\t}\n\t\t\t\t\tif(this.userNumber === ''){\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\twindow[className].executeCallAction({action: 'listen', ch1: target.attr('data-ch1'), ch2: target.attr('data-ch2'), number: this.userNumber});\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\twindow[className].$widget = new Vue({\n\t\t\tel: '#app',\n\t\t\tdelimiters: [\"<%\",\"%>\"],\n\t\t\tdata: {\n\t\t\t\tuserNumber: userNumber,\n\t\t\t\tfullAccess: ($('#fullAccess').val() === \"1\" || userNumber === ''),\n\t\t\t\tcalls: [\n\t\t\t\t]\n\t\t\t},\n\t\t\tmethods: {\n\t\t\t\tupdatedCallsFromResponse(lines) {\n\t\t\t\t\tthis.calls = lines;\n\t\t\t\t\tthis.$nextTick(() => {\n\t\t\t\t\t\tExtensions.updatePhonesRepresent('need-update');\n\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t\thangupAction(event) {\n\t\t\t\t\tlet target = $(event.target);\n\t\t\t\t\tif(target.attr('data-ch1') === undefined){\n\t\t\t\t\t\ttarget = $(event.target).parent();\n\t\t\t\t\t}\n\t\t\t\t\twindow[className].executeCallAction({action: 'hangup', ch1: target.attr('data-ch1'), ch2: target.attr('data-ch2')});\n\t\t\t\t},\n\t\t\t\tjoinAction(event) {\n\t\t\t\t\tlet target = $(event.target);\n\t\t\t\t\tif(target.attr('data-ch1') === undefined){\n\t\t\t\t\t\ttarget = $(event.target).parent();\n\t\t\t\t\t}\n\t\t\t\t\tif(this.userNumber === ''){\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\twindow[className].executeCallAction({action: 'join', ch1: target.attr('data-ch1'), ch2: target.attr('data-ch2'), number: this.userNumber});\n\t\t\t\t},\n\t\t\t\twhisperAction(event){\n\t\t\t\t\tlet target = $(event.target);\n\t\t\t\t\tif(target.attr('data-ch1') === undefined){\n\t\t\t\t\t\ttarget = $(event.target).parent();\n\t\t\t\t\t}\n\t\t\t\t\tif(this.userNumber === ''){\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tlet spChannel = target.attr('data-ch1');\n\t\t\t\t\tif('incoming' === target.attr('data-call-type')){\n\t\t\t\t\t\tspChannel = target.attr('data-ch2');\n\t\t\t\t\t}\n\t\t\t\t\twindow[className].executeCallAction({action: 'whisper', ch1: spChannel, ch2: '', number: this.userNumber});\n\t\t\t\t},\n\t\t\t\tlistenAction(event){\n\t\t\t\t\tlet target = $(event.target);\n\t\t\t\t\tif(target.attr('data-ch1') === undefined){\n\t\t\t\t\t\ttarget = $(event.target).parent();\n\t\t\t\t\t}\n\t\t\t\t\tif(this.userNumber === ''){\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\twindow[className].executeCallAction({action: 'listen', ch1: target.attr('data-ch1'), ch2: target.attr('data-ch2'), number: this.userNumber});\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\twindow[className].$checkBoxes.checkbox();\n\t\twindow[className].$dropDowns.dropdown();\n\t\twindow[className].initializeForm();\n\t\t$('.menu .item').tab();\n\t\twindow[className].startUiTicker();\n\t\t//////\n\t\t// Удаляем отступы контейнера.\n\t\t$('#main-content-container').removeClass('container');\n\t\t$('#module-status-toggle-segment').hide();\n\t\t$('.ui.clearing.hidden.divider').remove();\n\t\t// Окончание форматирования базовой страницы\n\t\t//////\n\t\tthis.startPollingActiveCalls();\n\n\t\t// Allow settings to be saved after initialization\n\t\tsetTimeout(function() {\n\t\t\twindow[className].isInit = false;\n\t\t}, 1000);\n\t},\n\tstartUiTicker() {\n\t\tif (this._uiTicker) return;\n\t\tthis._uiTicker = setInterval(() => {\n\t\t\tconst now = Date.now();\n\t\t\tif (window[className].$widgetQueues) {\n\t\t\t\twindow[className].$widgetQueues.nowTick = now;\n\t\t\t}\n\t\t\tif (window[className].$callsWidget) {\n\t\t\t\twindow[className].$callsWidget.nowTick = now;\n\t\t\t}\n\t\t}, 1000);\n\t},\n\tstartPollingActiveCalls() {\n\t\tif (this._activeCallsPollTimer) return;\n\t\twindow[className].updateLines();\n\t\tthis._activeCallsPollTimer = setInterval(window[className].updateLines, 2000);\n\t},\n\tstopPollingActiveCalls() {\n\t\tif (!this._activeCallsPollTimer) return;\n\t\tclearInterval(this._activeCallsPollTimer);\n\t\tthis._activeCallsPollTimer = null;\n\t},\n\tasync initContactsCache() {\n\t\ttry {\n\t\t\tthis._contactsCacheByPhone10 = await this.idbLoadAllContacts();\n\t\t\tthis.applyContactsCacheToQueueWidget();\n\t\t} catch (e) {\n\t\t\tconsole.log('contacts cache init error', e);\n\t\t\tthis._contactsCacheByPhone10 = {};\n\t\t}\n\t},\n\tapplyContactsCacheToQueueWidget() {\n\t\tif (!this._contactsCacheByPhone10) return;\n\t\tif (!window[className].$widgetQueues) return;\n\t\tfor (const [phone10, client] of Object.entries(this._contactsCacheByPhone10)) {\n\t\t\tif (window[className].$widgetQueues.$set) {\n\t\t\t\twindow[className].$widgetQueues.$set(window[className].$widgetQueues.contactsByPhone10, phone10, client);\n\t\t\t} else {\n\t\t\t\twindow[className].$widgetQueues.contactsByPhone10[phone10] = client;\n\t\t\t}\n\t\t}\n\t},\n\tidbOpenContactsDb() {\n\t\treturn new Promise((resolve, reject) => {\n\t\t\ttry {\n\t\t\t\tconst req = indexedDB.open('ModuleMonitorActiveCalls', 1);\n\t\t\t\treq.onupgradeneeded = () => {\n\t\t\t\t\tconst db = req.result;\n\t\t\t\t\tif (!db.objectStoreNames.contains('contactsByPhone10')) {\n\t\t\t\t\t\tdb.createObjectStore('contactsByPhone10', { keyPath: 'phone10' });\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t\treq.onsuccess = () => resolve(req.result);\n\t\t\t\treq.onerror = () => reject(req.error);\n\t\t\t} catch (e) {\n\t\t\t\treject(e);\n\t\t\t}\n\t\t});\n\t},\n\tasync idbPutContact(phone10, client) {\n\t\tconst db = await this.idbOpenContactsDb();\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tconst tx = db.transaction('contactsByPhone10', 'readwrite');\n\t\t\tconst store = tx.objectStore('contactsByPhone10');\n\t\t\tstore.put({ phone10, client, updatedAt: Date.now() });\n\t\t\ttx.oncomplete = () => { db.close(); resolve(); };\n\t\t\ttx.onerror = () => { const err = tx.error; db.close(); reject(err); };\n\t\t});\n\t},\n\tasync idbLoadAllContacts() {\n\t\tconst db = await this.idbOpenContactsDb();\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tconst tx = db.transaction('contactsByPhone10', 'readwrite');\n\t\t\tconst store = tx.objectStore('contactsByPhone10');\n\t\t\tconst req = store.getAll();\n\t\t\treq.onsuccess = () => {\n\t\t\t\tconst map = {};\n\t\t\t\tconst now = Date.now();\n\t\t\t\tconst ttlMs = Number(this.contactsCacheTtlMs) || (120 * 60 * 1000);\n\t\t\t\tfor (const row of req.result || []) {\n\t\t\t\t\tconst phone10 = row?.phone10;\n\t\t\t\t\tconst client = row?.client;\n\t\t\t\t\tconst updatedAt = Number(row?.updatedAt) || 0;\n\t\t\t\t\tconst isFresh = phone10 && client && updatedAt > 0 && (now - updatedAt) <= ttlMs;\n\t\t\t\t\tif (isFresh) {\n\t\t\t\t\t\tmap[phone10] = client;\n\t\t\t\t\t} else if (phone10) {\n\t\t\t\t\t\t// Cleanup expired/broken records\n\t\t\t\t\t\ttry { store.delete(phone10); } catch (e) { /* ignore */ }\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\ttx.oncomplete = () => { db.close(); resolve(map); };\n\t\t\t\ttx.onerror = () => { const err = tx.error; db.close(); reject(err); };\n\t\t\t};\n\t\t\treq.onerror = () => { const err = req.error; db.close(); reject(err); };\n\t\t});\n\t},\n\trequestBackendEnable() {\n\t\t$.api({\n\t\t\turl: window[className].backendEnableUrl,\n\t\t\ton: 'now',\n\t\t\tmethod: 'POST',\n\t\t\tonSuccess(response) {\n\t\t\t\tconsole.log('backandEnable response', response);\n\t\t\t\tconst accessToken = response?.data?.access_token;\n\t\t\t\tconst refreshToken = response?.data?.refresh_token;\n\t\t\t\tif (accessToken && refreshToken) {\n\t\t\t\t\twindow[className].setAuthTokens(accessToken, refreshToken);\n\t\t\t\t\twindow[className].connectContactsWs();\n\t\t\t\t\twindow[className].connectActiveCallsWs();\n\t\t\t\t}\n\t\t\t},\n\t\t\tonFailure(response) {\n\t\t\t\tconsole.log('backandEnable failure', response);\n\t\t\t},\n\t\t\tonError(errorMessage, element, xhr) {\n\t\t\t\tconsole.log('backandEnable error', errorMessage, xhr);\n\t\t\t}\n\t\t});\n\t},\n\tsetAuthTokens(accessToken, refreshToken) {\n\t\tthis._authTokens = this._authTokens || {};\n\t\tthis._authTokens.access_token = accessToken;\n\t\tthis._authTokens.refresh_token = refreshToken;\n\t\tthis._authTokens.exp = this.getJwtExp(accessToken);\n\t},\n\tgetJwtExp(token) {\n\t\ttry {\n\t\t\tif (!token || typeof token !== 'string') return 0;\n\t\t\tconst parts = token.split('.');\n\t\t\tif (parts.length < 2) return 0;\n\t\t\tconst payloadB64 = parts[1].replace(/-/g, '+').replace(/_/g, '/');\n\t\t\tconst padded = payloadB64 + '='.repeat((4 - (payloadB64.length % 4)) % 4);\n\t\t\tconst json = atob(padded);\n\t\t\tconst payload = JSON.parse(json);\n\t\t\treturn Number(payload?.exp) || 0;\n\t\t} catch (e) {\n\t\t\treturn 0;\n\t\t}\n\t},\n\tisAccessTokenExpired(skewSeconds = 0) {\n\t\tconst exp = Number(this._authTokens?.exp) || 0;\n\t\tif (!exp) return false; // unknown exp -> don't force refresh\n\t\tconst now = Math.floor(Date.now() / 1000);\n\t\treturn now + skewSeconds >= exp;\n\t},\n\tscheduleContactsWsTokenRefresh() {\n\t\t// Proactively refresh token shortly before expiry by re-requesting backendEnable.\n\t\tif (this._contactsWsTokenTimer) {\n\t\t\tclearTimeout(this._contactsWsTokenTimer);\n\t\t\tthis._contactsWsTokenTimer = null;\n\t\t}\n\t\tconst exp = Number(this._authTokens?.exp) || 0;\n\t\tif (!exp) return;\n\t\tconst now = Math.floor(Date.now() / 1000);\n\t\tconst refreshInSec = Math.max(1, exp - now - 15); // 15s before exp\n\t\tthis._contactsWsTokenTimer = setTimeout(() => {\n\t\t\t// Re-get tokens and reconnect WS\n\t\t\tthis.requestBackendEnable();\n\t\t}, refreshInSec * 1000);\n\t},\n\tscheduleContactsWsReconnect(reason, forceReAuth = false) {\n\t\tif (this._contactsWsReconnectTimer) {\n\t\t\tclearTimeout(this._contactsWsReconnectTimer);\n\t\t\tthis._contactsWsReconnectTimer = null;\n\t\t}\n\t\tthis._contactsWsReconnectAttempt = (this._contactsWsReconnectAttempt || 0) + 1;\n\t\tconst delay = Math.min(30000, 1000 * Math.pow(2, Math.min(5, this._contactsWsReconnectAttempt - 1)));\n\t\tthis._contactsWsReconnectTimer = setTimeout(() => {\n\t\t\tif (forceReAuth || this.isAccessTokenExpired(5)) {\n\t\t\t\tthis.requestBackendEnable();\n\t\t\t} else {\n\t\t\t\tthis.connectContactsWs();\n\t\t\t}\n\t\t}, delay);\n\t\tconsole.log('contacts ws reconnect scheduled', { reason, delayMs: delay });\n\t},\n\tconnectContactsWs() {\n\t\ttry {\n\t\t\tconst accessToken = this._authTokens?.access_token;\n\t\t\tif (!accessToken) return;\n\n\t\t\t// Avoid reconnecting if already connected/connecting\n\t\t\tif (this._contactsWs && (this._contactsWs.readyState === WebSocket.OPEN || this._contactsWs.readyState === WebSocket.CONNECTING)) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t// Reset backoff on explicit connect attempt\n\t\t\tthis._contactsWsReconnectAttempt = 0;\n\n\t\t\tconst wsProto = window.location.protocol === 'https:' ? 'wss' : 'ws';\n\t\t\tconst wsHost = window.location.host; // host:port of current page\n\t\t\tconst tokenParam = encodeURIComponent(accessToken);\n\t\t\tconst wsUrl = `${wsProto}://${wsHost}/pbxcore/api/module-softphone-backend/v1/sub/contacts?authorization=${tokenParam}`;\n\n\t\t\tthis._contactsWs = new WebSocket(wsUrl);\n\t\t\tthis._contactsWs.onopen = () => {\n\t\t\t\tconsole.log('contacts ws connected');\n\t\t\t\tthis.scheduleContactsWsTokenRefresh();\n\t\t\t};\n\t\t\tthis._contactsWs.onmessage = (event) => {\n\t\t\t\tthis.handleContactsWsMessage(event?.data);\n\t\t\t};\n\t\t\tthis._contactsWs.onerror = (event) => {\n\t\t\t\tconsole.log('contacts ws error', event);\n\t\t\t};\n\t\t\tthis._contactsWs.onclose = (event) => {\n\t\t\t\tconst code = event?.code;\n\t\t\t\tconst reason = event?.reason;\n\t\t\t\tconsole.log('contacts ws closed', { code, reason });\n\n\t\t\t\tif (this._contactsWsTokenTimer) {\n\t\t\t\t\tclearTimeout(this._contactsWsTokenTimer);\n\t\t\t\t\tthis._contactsWsTokenTimer = null;\n\t\t\t\t}\n\n\t\t\t\t// 1000 = normal close -> reconnect; auth closes vary by server implementation.\n\t\t\t\tconst authCloseCodes = new Set([1008, 4001, 4401, 4403]);\n\t\t\t\tconst forceReAuth = authCloseCodes.has(code) || this.isAccessTokenExpired(0);\n\t\t\t\tthis.scheduleContactsWsReconnect('close', forceReAuth);\n\t\t\t};\n\t\t} catch (e) {\n\t\t\tconsole.log('contacts ws init error', e);\n\t\t\tthis.scheduleContactsWsReconnect('init_error', this.isAccessTokenExpired(0));\n\t\t}\n\t},\n\tscheduleActiveCallsWsReconnect(reason, forceReAuth = false) {\n\t\tif (this._activeCallsWsReconnectTimer) {\n\t\t\tclearTimeout(this._activeCallsWsReconnectTimer);\n\t\t\tthis._activeCallsWsReconnectTimer = null;\n\t\t}\n\t\tthis._activeCallsWsReconnectAttempt = (this._activeCallsWsReconnectAttempt || 0) + 1;\n\t\tconst delay = Math.min(30000, 1000 * Math.pow(2, Math.min(5, this._activeCallsWsReconnectAttempt - 1)));\n\t\tthis._activeCallsWsReconnectTimer = setTimeout(() => {\n\t\t\tif (forceReAuth || this.isAccessTokenExpired(5)) {\n\t\t\t\tthis.requestBackendEnable();\n\t\t\t} else {\n\t\t\t\tthis.connectActiveCallsWs();\n\t\t\t}\n\t\t}, delay);\n\t\tconsole.log('active-calls ws reconnect scheduled', { reason, delayMs: delay });\n\t},\n\tconnectActiveCallsWs() {\n\t\ttry {\n\t\t\tconst accessToken = this._authTokens?.access_token;\n\t\t\tif (!accessToken) return;\n\n\t\t\t// Avoid reconnecting if already connected/connecting\n\t\t\tif (this._activeCallsWs && (this._activeCallsWs.readyState === WebSocket.OPEN || this._activeCallsWs.readyState === WebSocket.CONNECTING)) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t// Reset backoff on explicit connect attempt\n\t\t\tthis._activeCallsWsReconnectAttempt = 0;\n\n\t\t\t// Token exists -> use WS, disable polling fallback\n\t\t\tthis.stopPollingActiveCalls();\n\n\t\t\tconst wsProto = window.location.protocol === 'https:' ? 'wss' : 'ws';\n\t\t\tconst wsHost = window.location.host; // host:port of current page\n\t\t\tconst tokenParam = encodeURIComponent(accessToken);\n\t\t\tconst wsUrl = `${wsProto}://${wsHost}/pbxcore/api/module-softphone-backend/v1/sub/active-calls?authorization=${tokenParam}`;\n\n\t\t\tthis._activeCallsWs = new WebSocket(wsUrl);\n\t\t\tthis._activeCallsWs.onopen = () => {\n\t\t\t\tconsole.log('active-calls ws connected');\n\t\t\t\t// Reuse the same token refresh timer (it triggers requestBackendEnable)\n\t\t\t\tthis.scheduleContactsWsTokenRefresh();\n\t\t\t};\n\t\t\tthis._activeCallsWs.onmessage = (event) => {\n\t\t\t\tthis.handleActiveCallsWsMessage(event?.data);\n\t\t\t};\n\t\t\tthis._activeCallsWs.onerror = (event) => {\n\t\t\t\tconsole.log('active-calls ws error', event);\n\t\t\t};\n\t\t\tthis._activeCallsWs.onclose = (event) => {\n\t\t\t\tconst code = event?.code;\n\t\t\t\tconst reason = event?.reason;\n\t\t\t\tconsole.log('active-calls ws closed', { code, reason });\n\n\t\t\t\t// Auth closes vary by server implementation.\n\t\t\t\tconst authCloseCodes = new Set([1008, 4001, 4401, 4403]);\n\t\t\t\tconst forceReAuth = authCloseCodes.has(code) || this.isAccessTokenExpired(0);\n\t\t\t\tthis.scheduleActiveCallsWsReconnect('close', forceReAuth);\n\t\t\t};\n\t\t} catch (e) {\n\t\t\tconsole.log('active-calls ws init error', e);\n\t\t\tthis.scheduleActiveCallsWsReconnect('init_error', this.isAccessTokenExpired(0));\n\t\t}\n\t},\n\thandleContactsWsMessage(data) {\n\t\ttry {\n\t\t\tif (!data) return;\n\t\t\tconst parsed = typeof data === 'string' ? JSON.parse(data) : data;\n\t\t\tconst items = Array.isArray(parsed) ? parsed : [parsed];\n\t\t\tfor (const item of items) {\n\t\t\t\tconst digits = String(item?.number || '').replace(/\\D+/g, '');\n\t\t\t\tconst phone10 = digits.length <= 10 ? digits : digits.slice(-10);\n\t\t\t\tconst displayName = String(item?.client || item?.contact || '').trim();\n\t\t\t\tif (phone10 && displayName) {\n\t\t\t\t\tthis._contactsCacheByPhone10 = this._contactsCacheByPhone10 || {};\n\t\t\t\t\tthis._contactsCacheByPhone10[phone10] = displayName;\n\t\t\t\t\tthis.idbPutContact(phone10, displayName).catch((e) => console.log('contacts cache save error', e));\n\t\t\t\t}\n\t\t\t\tif (window[className].$widgetQueues) {\n\t\t\t\t\twindow[className].$widgetQueues.updateContactFromWs(item);\n\t\t\t\t}\n\t\t\t\t// Calls table is a separate Vue instance and reads client name via $widgetQueues.\n\t\t\t\t// Vue can't track cross-instance dependency, so force re-render on contact update.\n\t\t\t\tif (window[className].$callsWidget && typeof window[className].$callsWidget.$forceUpdate === 'function') {\n\t\t\t\t\twindow[className].$callsWidget.$forceUpdate();\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (e) {\n\t\t\tconsole.log('contacts ws parse error', e);\n\t\t}\n\t},\n\thandleActiveCallsWsMessage(data) {\n\t\ttry {\n\t\t\tif (!data) return;\n\t\t\tconst parsed = typeof data === 'string' ? JSON.parse(data) : data;\n\t\t\tconst payload = parsed?.queues ? parsed : (parsed?.data?.queues ? parsed.data : null);\n\t\t\tif (!payload) return;\n\t\t\tif (!window[className].$widgetQueues || !window[className].$callsWidget) return;\n\n\t\t\twindow[className].$widgetQueues.updatedCallsFromResponse(payload);\n\t\t\twindow[className].$callsWidget.updatedCallsFromResponse(payload);\n\t\t} catch (e) {\n\t\t\tconsole.log('active-calls ws parse error', e);\n\t\t}\n\t},\n\tformatElapsedTime(enterTime) {\n\t\tif (!enterTime) return '—';\n\n\t\tconst now = Math.floor(Date.now() / 1000);\n\t\tconst diffSeconds = now - enterTime;\n\n\t\treturn window[className].secondToTime(diffSeconds);\n\t},\n\tsecondToTime(diffSeconds){\n\t\tif (diffSeconds < 0) return '0';\n\t\t// Форматируем: чч:мм:сс или мм:сс, или просто секунды\n\t\tconst hours = Math.floor(diffSeconds / 3600);\n\t\tconst minutes = Math.floor((diffSeconds % 3600) / 60);\n\t\tconst seconds = Math.round(diffSeconds % 60);\n\t\tif (hours > 0) {\n\t\t\treturn `${hours}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;\n\t\t} else if (minutes > 0) {\n\t\t\treturn `${minutes}:${seconds.toString().padStart(2, '0')}`;\n\t\t} else {\n\t\t\treturn `${seconds}`;\n\t\t}\n\t},\n\tonChangeSetting(settingName, value) {\n\t\tif(window[className].isInit){\n\t\t\treturn;\n\t\t}\n\t\tvar data = {};\n\t\tdata[settingName] = value;\n\t\t$.api({\n\t\t\turl: window[className].saveUserActionUrl,\n\t\t\ton: 'now',\n\t\t\tmethod: 'POST',\n\t\t\tdata: data,\n\t\t\tsuccessTest: function(response) {\n\t\t\t\treturn response !== undefined && Object.keys(response).length > 0 && response.success === true;\n\t\t\t},\n\t\t\tonSuccess: function(response) {\n\t\t\t\tif(settingName === 'queueIds'){\n\t\t\t\t\t// Update hidden input and Vue data\n\t\t\t\t\t$('#queueIds').val(value);\n\t\t\t\t\t// Re-render queue widget from last received payload (WS mode)\n\t\t\t\t\tif (window[className].$widgetQueues && typeof window[className].$widgetQueues.refreshFromLastPayload === 'function') {\n\t\t\t\t\t\twindow[className].$widgetQueues.refreshFromLastPayload();\n\t\t\t\t\t}\n\t\t\t\t}else if( settingName === 'adminUserId'){\n\t\t\t\t\twindow.location.href = window.location.href;\n\t\t\t\t}\n\t\t\t},\n\t\t\tonFailure: function(response) {\n\t\t\t\tconsole.log(response);\n\t\t\t},\n\t\t\tonError: function(errorMessage, element, xhr) {\n\t\t\t\tconsole.log(errorMessage,xhr);\n\t\t\t}\n\t\t});\n\t},\n\texecuteCallAction(data) {\n\t\t$.api({\n\t\t\turl: window[className].executeCallUrl,\n\t\t\ton: 'now',\n\t\t\tmethod: 'POST',\n\t\t\tdata: data,\n\t\t\tsuccessTest(response) {\n\t\t\t\treturn response !== undefined && Object.keys(response).length > 0 && response.success === true;\n\t\t\t},\n\t\t\tonSuccess(response) {\n\t\t\t\tconsole.log(response);\n\t\t\t},\n\t\t\tonFailure(response) {\n\t\t\t\tconsole.log(response);\n\t\t\t},\n\t\t\tonError(errorMessage, element, xhr) {\n\t\t\t\tconsole.log(errorMessage,xhr);\n\t\t\t}\n\t\t});\n\t},\n\tupdateLines() {\n\t\t$.api({\n\t\t\turl: window[className].activeChannelsUrlV2,\n\t\t\ton: 'now',\n\t\t\tmethod: 'POST',\n\t\t\tsuccessTest(response) {\n\t\t\t\treturn response !== undefined && Object.keys(response).length > 0 && response.success === true;\n\t\t\t},\n\t\t\tonSuccess(response) {\n\t\t\t\twindow[className].$widgetQueues.updatedCallsFromResponse(response);\n\t\t\t\twindow[className].$callsWidget.updatedCallsFromResponse(response);\n\t\t\t},\n\t\t\tonFailure(response) {\n\t\t\t\tconsole.log(response);\n\t\t\t},\n\t\t\tonError(errorMessage, element, xhr) {\n\t\t\t\tconsole.log(errorMessage,xhr);\n\t\t\t}\n\t\t});\n\t},\n\n\t/**\n\t * We can modify some data before form send\n\t * @param settings\n\t * @returns {*}\n\t */\n\tcbBeforeSendForm(settings) {\n\t\tconst result = settings;\n\t\tresult.data = window[className].$formObj.form('get values');\n\t\treturn result;\n\t},\n\t/**\n\t * Some actions after forms send\n\t */\n\tcbAfterSendForm() {\n\n\t},\n\t/**\n\t * Initialize form parameters\n\t */\n\tinitializeForm() {\n\t\tForm.$formObj = window[className].$formObj;\n\t\tForm.url = `${globalRootUrl}${idUrl}/save`;\n\t\tForm.validateRules = window[className].validateRules;\n\t\tForm.cbBeforeSendForm = window[className].cbBeforeSendForm;\n\t\tForm.cbAfterSendForm = window[className].cbAfterSendForm;\n\t\tForm.initialize();\n\t},\n};\n\n$(document).ready(() => {\n\twindow[className].initialize();\n});\n\n"],"mappings":"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","ignoreList":[]} \ No newline at end of file +{"version":3,"file":"module-monitor-active-calls-index.js","names":["e","t","r","Symbol","n","iterator","o","toStringTag","i","c","prototype","Generator","u","Object","create","_regeneratorDefine2","f","p","y","G","v","a","d","bind","length","l","TypeError","call","done","value","return","GeneratorFunction","GeneratorFunctionPrototype","getPrototypeOf","setPrototypeOf","__proto__","displayName","_regenerator","w","m","defineProperty","_regeneratorDefine","_invoke","enumerable","configurable","writable","asyncGeneratorStep","Promise","resolve","then","_asyncToGenerator","arguments","apply","_next","_throw","_toConsumableArray","_arrayWithoutHoles","_iterableToArray","_unsupportedIterableToArray","_nonIterableSpread","Array","from","isArray","_arrayLikeToArray","_createForOfIteratorHelper","_n","F","s","next","ownKeys","keys","getOwnPropertySymbols","filter","getOwnPropertyDescriptor","push","_objectSpread","forEach","_defineProperty","getOwnPropertyDescriptors","defineProperties","_toPropertyKey","_toPrimitive","_typeof","toPrimitive","String","Number","_slicedToArray","_arrayWithHoles","_iterableToArrayLimit","_nonIterableRest","toString","slice","constructor","name","test","idUrl","idForm","className","inputClassName","ModuleMonitorActiveCalls","isInit","contactsCacheTtlMs","queuesFilterSelector","$formObj","$","$checkBoxes","$dropDowns","activeChannelsUrl","globalRootUrl","activeChannelsUrlV2","backendEnableUrl","executeCallUrl","saveUserActionUrl","$widget","undefined","validateRules","initialize","initContactsCache","requestBackendEnable","dropdown","onChange","text","$choice","window","onChangeSetting","val","userNumber","$widgetQueues","Vue","el","delimiters","methods","updatedCallsFromResponse","data","lastActiveCallsPayload","minWaitVisible","queues","allCalls","calls","initQueuesFilter","$nextTick","normalizeAgentCards","self","$filter","currentSelection","selectedQueueIds","find","hide","show","fullTextSearch","selectedIds","split","JSON","stringify","savedQueueIds","raw","parse","refreshFromLastPayload","getQueueCalls","queueId","queue","getQueueAgentsList","agents","buildAgentsList","hasWaitingCalls","dst_chan","queueData","EnterTime","elapsed","formatElapsedTime","agentsObj","entries","available","unavailable","_i","_entries","_entries$_i","number","agent","state","item","concat","normalizePhone10","phone","digits","replace","updateContactFromWs","contact","phone10","client","trim","$set","contactsByPhone10","getClientNameByPhone","getClientHeader","hasClientByPhone","enterTime","nowTick","$el","artifacts","querySelectorAll","remove","ensureAgentCardsGridMasonry","cardsContainers","cardsContainer","style","alignItems","alignContent","cards","card","alignSelf","headers","fontSize","lineHeight","display","gap","whiteSpace","metas","numLabels","paddingTop","paddingBottom","flex","minWidth","maxWidth","overflow","textOverflow","names","requestAnimationFrame","layoutAgentCardsGridMasonry","adjustAgentCardsGap","container","querySelector","tallCard","shortCard","ht","getBoundingClientRect","height","hs","isFinite","Math","max","min","round","setProperty","adjustAgentCardsColumnCount","clientWidth","minCardWidth","cs","getComputedStyle","gapRaw","columnGap","getPropertyValue","gapPx","parseFloat","count","floor","styleId","styleEl","document","getElementById","createElement","id","head","appendChild","textContent","classList","add","_agentCardsResizeBound","addEventListener","grids","grid","layoutSingleGridMasonry","rowHeight","rowGap","items","gridRowEnd","minHeight","tall","short","h","span","ceil","shortHeights","map","tallHeights","g","shortH","tallH","effectiveGap","getSrcNumForAgent","agentNumber","result","answeredFound","_iterator","_step","dst_num","src_num","calledChannels","match","ch","bridgeChannels","err","tmpCall","spyer","spy_num","exten","hasPeerPhone","getPeerPhoneLabel","getPeerNameLabel","applyContactsCacheToQueueWidget","$callsWidget","fullAccess","callIsVisible","getWaitTime","formatTimestampToTime","timestamp","ts","ms","date","Date","hours","getHours","padStart","minutes","getMinutes","seconds","getSeconds","answer","now","secondToTime","start","getCallTime","_data$calls","Extensions","updatePhonesRepresent","q","hangupAction","event","target","attr","parent","executeCallAction","action","ch1","ch2","joinAction","whisperAction","spChannel","listenAction","lines","checkbox","initializeForm","tab","startUiTicker","removeClass","startPollingActiveCalls","setTimeout","_uiTicker","setInterval","_activeCallsPollTimer","updateLines","stopPollingActiveCalls","clearInterval","_this","_callee","_t","_context","idbLoadAllContacts","_contactsCacheByPhone10","console","log","_i2","_Object$entries","_Object$entries$_i","idbOpenContactsDb","reject","req","indexedDB","open","onupgradeneeded","db","objectStoreNames","contains","createObjectStore","keyPath","onsuccess","onerror","error","idbPutContact","_this2","_callee2","_context2","tx","transaction","store","objectStore","put","updatedAt","oncomplete","close","_this3","_callee3","_context3","getAll","ttlMs","_iterator2","_step2","row","isFresh","delete","api","url","on","method","onSuccess","response","_response$data","_response$data2","accessToken","access_token","refreshToken","refresh_token","setAuthTokens","connectContactsWs","connectActiveCallsWs","onFailure","onError","errorMessage","element","xhr","_authTokens","exp","getJwtExp","refreshAuthToken","_this$_authTokens","_this4","ajax","success","newRefreshToken","scheduleContactsWsTokenRefresh","token","parts","payloadB64","padded","repeat","json","atob","payload","isAccessTokenExpired","_this$_authTokens2","skewSeconds","_this$_authTokens3","_this5","_contactsWsTokenTimer","clearTimeout","refreshInSec","scheduleContactsWsReconnect","reason","_this6","forceReAuth","_contactsWsReconnectTimer","_contactsWsReconnectAttempt","delay","pow","delayMs","_this7","_this$_authTokens4","_contactsWs","readyState","WebSocket","OPEN","CONNECTING","wsProto","location","protocol","wsHost","host","tokenParam","encodeURIComponent","wsUrl","onopen","onmessage","handleContactsWsMessage","onclose","code","authCloseCodes","Set","has","scheduleActiveCallsWsReconnect","_this8","_activeCallsWsReconnectTimer","_activeCallsWsReconnectAttempt","_this9","_this$_authTokens5","_activeCallsWs","handleActiveCallsWsMessage","parsed","_iterator3","_step3","catch","$forceUpdate","_parsed$data","diffSeconds","settingName","successTest","href","cbBeforeSendForm","settings","form","cbAfterSendForm","Form","ready"],"sources":["src/module-monitor-active-calls-index.js"],"sourcesContent":["/*\n * Copyright (C) MIKO LLC - All Rights Reserved\n * Unauthorized copying of this file, via any medium is strictly prohibited\n * Proprietary and confidential\n * Written by Nikolay Beketov, 11 2018\n *\n */\nconst idUrl = 'module-monitor-active-calls';\nconst idForm = 'module-monitor-active-calls-form';\nconst className = 'ModuleMonitorActiveCalls';\nconst inputClassName = 'mikopbx-module-input';\n\n/* global $, globalRootUrl, globalTranslate, Form, Config, Vue, Extensions */\nconst ModuleMonitorActiveCalls = {\n\tisInit: true,\n\tcontactsCacheTtlMs: 120 * 60 * 1000,\n\tqueuesFilterSelector: '#queuesFilter',\n\t$formObj: $('#'+idForm),\n\t$checkBoxes: $('#'+idForm+' .ui.checkbox'),\n\t$dropDowns: $('#'+idForm+' .ui.dropdown'),\n\tactiveChannelsUrl: globalRootUrl + idUrl + \"/getActiveChannels\",\n\tactiveChannelsUrlV2: globalRootUrl + idUrl + \"/getActiveChannelsV2\",\n\tbackendEnableUrl: globalRootUrl + idUrl + \"/backandEnable\",\n\texecuteCallUrl: globalRootUrl + idUrl + \"/executeCall\",\n\tsaveUserActionUrl: globalRootUrl + idUrl + \"/saveUser\",\n\t$widget: undefined,\n\n\t/**\n\t * Field validation rules\n\t * https://semantic-ui.com/behaviors/form.html\n\t */\n\tvalidateRules: {},\n\t/**\n\t * On page load we init some Semantic UI library\n\t */\n\tinitialize() {\n\t\tthis.initContactsCache();\n\t\tthis.requestBackendEnable();\n\n\t\t$(\"#nowUser.dropdown.enable\").dropdown({\n\t\t\tonChange: function onChange(value, text, $choice) {\n\t\t\t\twindow[className].onChangeSetting('adminUserId', value);\n\t\t\t}\n\t\t});\n\t\t$(\"#minWaitVisible.dropdown.enable\").dropdown({\n\t\t\tonChange: function onChange(value, text, $choice) {\n\t\t\t\t$('#minWaitVisibleValue').val(value);\n\t\t\t\twindow[className].onChangeSetting('minWaitVisible', value);\n\t\t\t}\n\t\t});\n\t\tlet userNumber = $('#userNumber').val();\n\n\t\twindow[className].$widgetQueues = new Vue({\n\t\t\tel: '#app-queue',\n\t\t\tdelimiters: [\"<%\",\"%>\"],\n\t\t\tmethods: {\n\t\t\t\tupdatedCallsFromResponse(data) {\n\t\t\t\t\t// Keep last payload to allow re-render on queue switch (WS mode).\n\t\t\t\t\tthis.lastActiveCallsPayload = data;\n\n\t\t\t\t\tthis.minWaitVisible = 1*$('#minWaitVisibleValue').val();\n\t\t\t\t\tthis.queues = data.queues || {};\n\t\t\t\t\tthis.allCalls = data.calls || [];\n\n\t\t\t\t\t// Initialize multi-select dropdown if not yet done\n\t\t\t\t\tthis.initQueuesFilter();\n\n\t\t\t\t\t// Normalize Semantic UI Card typography after render\n\t\t\t\t\tthis.$nextTick(function() {\n\t\t\t\t\t\tthis.normalizeAgentCards();\n\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t\tinitQueuesFilter() {\n\t\t\t\t\tvar self = this;\n\t\t\t\t\tvar $filter = $(window[className].queuesFilterSelector);\n\t\t\t\t\tif ($filter.length === 0) return;\n\n\t\t\t\t\t// Wait for Vue to render menu items\n\t\t\t\t\tthis.$nextTick(function() {\n\t\t\t\t\t\t// Reinitialize dropdown to pick up new menu items\n\t\t\t\t\t\tif ($filter.data('initialized')) {\n\t\t\t\t\t\t\t// Dropdown already exists, just refresh menu\n\t\t\t\t\t\t\t// Save current selection before refresh to prevent reset\n\t\t\t\t\t\t\tvar currentSelection = self.selectedQueueIds ? self.selectedQueueIds.slice() : [];\n\t\t\t\t\t\t\t$filter.data('refreshing', true);\n\t\t\t\t\t\t\t$filter.dropdown('refresh');\n\t\t\t\t\t\t\t$filter.data('refreshing', false);\n\n\t\t\t\t\t\t\t// Restore selection after refresh if it was cleared\n\t\t\t\t\t\t\tif (currentSelection.length > 0 && (!self.selectedQueueIds || self.selectedQueueIds.length === 0)) {\n\t\t\t\t\t\t\t\tself.selectedQueueIds = currentSelection;\n\t\t\t\t\t\t\t\t$filter.dropdown('set exactly', currentSelection);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// After refresh, ensure default text is hidden if we have selections\n\t\t\t\t\t\t\tif (self.selectedQueueIds && self.selectedQueueIds.length > 0) {\n\t\t\t\t\t\t\t\t$filter.find('.default.text').hide();\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t$filter.find('.default.text').show();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t// First time initialization\n\t\t\t\t\t\t\t$filter.data('initialized', true);\n\t\t\t\t\t\t\t$filter.dropdown({\n\t\t\t\t\t\t\t\tfullTextSearch: true,\n\t\t\t\t\t\t\t\tonChange: function(value) {\n\t\t\t\t\t\t\t\t\t// Skip onChange during programmatic refresh\n\t\t\t\t\t\t\t\t\tif ($filter.data('refreshing')) {\n\t\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t// value is comma-separated string of selected queue IDs\n\t\t\t\t\t\t\t\t\tvar selectedIds = value ? value.split(',').filter(function(v) { return v !== ''; }) : [];\n\t\t\t\t\t\t\t\t\tself.selectedQueueIds = selectedIds;\n\t\t\t\t\t\t\t\t\t// Auto-save on change\n\t\t\t\t\t\t\t\t\twindow[className].onChangeSetting('queueIds', JSON.stringify(selectedIds));\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t});\n\n\t\t\t\t\t\t\t// Set initial values from hidden input\n\t\t\t\t\t\t\tvar savedQueueIds = [];\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tvar raw = $('#queueIds').val();\n\t\t\t\t\t\t\t\tsavedQueueIds = JSON.parse(raw || '[]');\n\t\t\t\t\t\t\t} catch (e) {\n\t\t\t\t\t\t\t\tsavedQueueIds = [];\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (Array.isArray(savedQueueIds) && savedQueueIds.length > 0) {\n\t\t\t\t\t\t\t\twindow[className].isInit = true;\n\t\t\t\t\t\t\t\t$filter.dropdown('set exactly', savedQueueIds);\n\t\t\t\t\t\t\t\tself.selectedQueueIds = savedQueueIds;\n\t\t\t\t\t\t\t\twindow[className].isInit = false;\n\t\t\t\t\t\t\t\t// Hide default text when values are selected\n\t\t\t\t\t\t\t\t$filter.find('.default.text').hide();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t\trefreshFromLastPayload() {\n\t\t\t\t\tif (this.lastActiveCallsPayload) {\n\t\t\t\t\t\tthis.updatedCallsFromResponse(this.lastActiveCallsPayload);\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\tgetQueueCalls(queueId) {\n\t\t\t\t\tvar queue = this.queues[queueId];\n\t\t\t\t\tif (!queue) return [];\n\t\t\t\t\treturn Array.isArray(queue.calls) ? queue.calls : [];\n\t\t\t\t},\n\t\t\t\tgetQueueAgentsList(queueId) {\n\t\t\t\t\tvar queue = this.queues[queueId];\n\t\t\t\t\tif (!queue || !queue.agents) return [];\n\t\t\t\t\treturn this.buildAgentsList(queue.agents);\n\t\t\t\t},\n\t\t\t\thasWaitingCalls(queueId) {\n\t\t\t\t\tvar calls = this.getQueueCalls(queueId);\n\t\t\t\t\tvar self = this;\n\t\t\t\t\tfor (var i = 0; i < calls.length; i++) {\n\t\t\t\t\t\tvar call = calls[i];\n\t\t\t\t\t\tif (call.dst_chan === '' && call.queueData && call.queueData.EnterTime !== undefined) {\n\t\t\t\t\t\t\tvar elapsed = self.formatElapsedTime(call.queueData.EnterTime);\n\t\t\t\t\t\t\tif (self.minWaitVisible <= elapsed) {\n\t\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\treturn false;\n\t\t\t\t},\n\t\t\t\tbuildAgentsList(agentsObj) {\n\t\t\t\t\tconst entries = Object.entries(agentsObj || {});\n\t\t\t\t\tconst available = [];\n\t\t\t\t\tconst unavailable = [];\n\t\t\t\t\tfor (const [number, agent] of entries) {\n\t\t\t\t\t\tconst state = agent?.state || '';\n\t\t\t\t\t\tconst item = { number, ...agent };\n\t\t\t\t\t\tif (state === 'Unavailable') {\n\t\t\t\t\t\t\tunavailable.push(item);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tavailable.push(item);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\treturn available.concat(unavailable);\n\t\t\t\t},\n\t\t\t\tnormalizePhone10(phone) {\n\t\t\t\t\tconst digits = String(phone || '').replace(/\\D+/g, '');\n\t\t\t\t\tif (digits.length <= 10) return digits;\n\t\t\t\t\treturn digits.slice(-10);\n\t\t\t\t},\n\t\t\t\tupdateContactFromWs(contact) {\n\t\t\t\t\tconst phone10 = this.normalizePhone10(contact?.number);\n\t\t\t\t\tif (!phone10) return;\n\t\t\t\t\tconst displayName = String(contact?.client || contact?.contact || '').trim();\n\t\t\t\t\tif (!displayName) return;\n\t\t\t\t\t// Vue2: ensure reactivity for new keys\n\t\t\t\t\tif (this.$set) {\n\t\t\t\t\t\tthis.$set(this.contactsByPhone10, phone10, displayName);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthis.contactsByPhone10[phone10] = displayName;\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\tgetClientNameByPhone(phone) {\n\t\t\t\t\tconst phone10 = this.normalizePhone10(phone);\n\t\t\t\t\treturn this.contactsByPhone10[phone10] || '';\n\t\t\t\t},\n\t\t\t\tgetClientHeader(phone) {\n\t\t\t\t\tconst client = this.getClientNameByPhone(phone);\n\t\t\t\t\tif (!client) return phone;\n\t\t\t\t\treturn `${client} <${phone}>`;\n\t\t\t\t},\n\t\t\t\thasClientByPhone(phone) {\n\t\t\t\t\treturn !!this.getClientNameByPhone(phone);\n\t\t\t\t},\n\t\t\t\tformatElapsedTime(enterTime) {\n\t\t\t\t\t// Make this method reactive to the UI ticker.\n\t\t\t\t\tvoid this.nowTick;\n\t\t\t\t\treturn window[className].formatElapsedTime(enterTime);\n\t\t\t\t},\n\t\t\t\tnormalizeAgentCards() {\n\t\t\t\t\tif (!this.$el) return;\n\t\t\t\t\tvar self = this;\n\n\t\t\t\t\t// Cleanup artifacts from previous experiments (placeholders/spacers).\n\t\t\t\t\tvar artifacts = this.$el.querySelectorAll('.agent-peer-placeholder, .agent-peer-spacer');\n\t\t\t\t\tartifacts.forEach(function(el) { el.remove(); });\n\n\t\t\t\t\t// Dense layout (masonry) that still fills left-to-right:\n\t\t\t\t\t// flex-wrap can't place items into vertical gaps under tall cards.\n\t\t\t\t\tthis.ensureAgentCardsGridMasonry();\n\n\t\t\t\t\t// Process all agent card containers (one per queue block)\n\t\t\t\t\tvar cardsContainers = this.$el.querySelectorAll('.ui.cards.agent-cards');\n\t\t\t\t\tcardsContainers.forEach(function(cardsContainer) {\n\t\t\t\t\t\tcardsContainer.style.alignItems = 'flex-start';\n\t\t\t\t\t\tcardsContainer.style.alignContent = 'flex-start';\n\t\t\t\t\t});\n\n\t\t\t\t\tvar cards = this.$el.querySelectorAll('.ui.cards.agent-cards > .ui.card.agent-card');\n\t\t\t\t\tcards.forEach(function(card) {\n\t\t\t\t\t\tcard.style.alignSelf = 'flex-start';\n\t\t\t\t\t});\n\n\t\t\t\t\t// Semantic UI makes .header bigger than normal text; we need same font size.\n\t\t\t\t\tvar headers = this.$el.querySelectorAll('.ui.card.agent-card .header.agent-card-header');\n\t\t\t\t\theaders.forEach(function(el) {\n\t\t\t\t\t\tel.style.fontSize = '1em';\n\t\t\t\t\t\tel.style.lineHeight = '1.2';\n\t\t\t\t\t\tel.style.display = 'flex';\n\t\t\t\t\t\tel.style.alignItems = 'center';\n\t\t\t\t\t\tel.style.gap = '0.5em';\n\t\t\t\t\t\tel.style.whiteSpace = 'nowrap';\n\t\t\t\t\t});\n\n\t\t\t\t\tvar metas = this.$el.querySelectorAll('.ui.card.agent-card .meta.agent-peer');\n\t\t\t\t\tmetas.forEach(function(el) {\n\t\t\t\t\t\tel.style.fontSize = '1em';\n\t\t\t\t\t\tel.style.lineHeight = '1.2';\n\t\t\t\t\t});\n\n\t\t\t\t\t// Normalize label/name typography so they have same text height.\n\t\t\t\t\tvar numLabels = this.$el.querySelectorAll('.ui.card.agent-card .agent-num-label');\n\t\t\t\t\tnumLabels.forEach(function(el) {\n\t\t\t\t\t\tel.style.fontSize = '1em';\n\t\t\t\t\t\tel.style.lineHeight = '1.2';\n\t\t\t\t\t\tel.style.display = 'inline-flex';\n\t\t\t\t\t\tel.style.alignItems = 'center';\n\t\t\t\t\t\tel.style.paddingTop = '0';\n\t\t\t\t\t\tel.style.paddingBottom = '0';\n\t\t\t\t\t\t// Allow label to shrink (otherwise long numbers force card wider than 180px)\n\t\t\t\t\t\tel.style.flex = '0 1 auto';\n\t\t\t\t\t\tel.style.minWidth = '0';\n\t\t\t\t\t\tel.style.maxWidth = '14ch';\n\t\t\t\t\t\tel.style.overflow = 'hidden';\n\t\t\t\t\t\tel.style.textOverflow = 'ellipsis';\n\t\t\t\t\t\tel.style.whiteSpace = 'nowrap';\n\t\t\t\t\t});\n\t\t\t\t\tvar names = this.$el.querySelectorAll('.ui.card.agent-card .agent-name');\n\t\t\t\t\tnames.forEach(function(el) {\n\t\t\t\t\t\tel.style.lineHeight = '1.2';\n\t\t\t\t\t\t// Ellipsis for long names (e.g. \"Салтыков-Щедрин\")\n\t\t\t\t\t\tel.style.minWidth = '0';\n\t\t\t\t\t\tel.style.flex = '1 1 auto';\n\t\t\t\t\t\tel.style.overflow = 'hidden';\n\t\t\t\t\t\tel.style.textOverflow = 'ellipsis';\n\t\t\t\t\t\tel.style.whiteSpace = 'nowrap';\n\t\t\t\t\t});\n\n\t\t\t\t\t// Grid masonry needs row-span calculation after layout.\n\t\t\t\t\trequestAnimationFrame(function() {\n\t\t\t\t\t\trequestAnimationFrame(function() {\n\t\t\t\t\t\t\tself.layoutAgentCardsGridMasonry();\n\t\t\t\t\t\t});\n\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t\tadjustAgentCardsGap() {\n\t\t\t\t\tif (!this.$el) return;\n\t\t\t\t\tconst container = this.$el.querySelector('.ui.cards.agent-cards');\n\t\t\t\t\tif (!container) return;\n\n\t\t\t\t\tconst cards = Array.from(container.querySelectorAll('.ui.card.agent-card'));\n\t\t\t\t\tif (!cards.length) return;\n\n\t\t\t\t\tconst tallCard = cards.find((c) => c.querySelector('.meta.agent-peer'));\n\t\t\t\t\tconst shortCard = cards.find((c) => !c.querySelector('.meta.agent-peer'));\n\t\t\t\t\tif (!tallCard || !shortCard) return;\n\n\t\t\t\t\tconst ht = tallCard.getBoundingClientRect().height;\n\t\t\t\t\tconst hs = shortCard.getBoundingClientRect().height;\n\t\t\t\t\tif (!ht || !hs) return;\n\n\t\t\t\t\t// From 2*(hs+g) = ht+g => g = ht - 2*hs\n\t\t\t\t\tlet gap = ht - 2 * hs;\n\t\t\t\t\tif (!Number.isFinite(gap)) return;\n\n\t\t\t\t\t// Clamp to sane range; negative means \"no extra gap needed\".\n\t\t\t\t\tgap = Math.max(0, Math.min(20, Math.round(gap)));\n\n\t\t\t\t\tcontainer.style.setProperty('--agent-card-gap', `${gap}px`);\n\t\t\t\t},\n\t\t\t\tadjustAgentCardsColumnCount() {\n\t\t\t\t\tif (!this.$el) return;\n\t\t\t\t\tconst container = this.$el.querySelector('.ui.cards.agent-cards.agent-cards-masonry');\n\t\t\t\t\tif (!container) return;\n\n\t\t\t\t\tconst w = container.clientWidth;\n\t\t\t\t\tif (!w) return;\n\n\t\t\t\t\t// Minimum acceptable card width in px (tune if needed)\n\t\t\t\t\tconst minCardWidth = 150;\n\n\t\t\t\t\tconst cs = window.getComputedStyle(container);\n\t\t\t\t\tconst gapRaw = cs.columnGap || cs.getPropertyValue('column-gap') || '16px';\n\t\t\t\t\tconst gapPx = parseFloat(gapRaw) || 16;\n\n\t\t\t\t\tconst count = Math.max(1, Math.min(12, Math.floor((w + gapPx) / (minCardWidth + gapPx))));\n\t\t\t\t\tcontainer.style.setProperty('--agent-card-col-count', String(count));\n\t\t\t\t},\n\t\t\t\tensureAgentCardsGridMasonry() {\n\t\t\t\t\tvar self = this;\n\t\t\t\t\tvar styleId = 'agent-cards-layout-style';\n\t\t\t\t\tvar styleEl = document.getElementById(styleId);\n\t\t\t\t\tif (!styleEl) {\n\t\t\t\t\t\tstyleEl = document.createElement('style');\n\t\t\t\t\t\tstyleEl.id = styleId;\n\t\t\t\t\t\tdocument.head.appendChild(styleEl);\n\t\t\t\t\t}\n\n\t\t\t\t\t// Grid masonry: fills left-to-right and can pack items into gaps.\n\t\t\t\t\t// minmax(240px, 1fr) - карточки минимум 240px, растягиваются равномерно\n\t\t\t\t\tstyleEl.textContent = '\\\n.ui.cards.agent-cards.agent-cards-grid {\\\n display: grid !important;\\\n grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));\\\n justify-content: start;\\\n gap: var(--agent-card-gap, 8px);\\\n grid-auto-rows: 1px;\\\n margin-bottom: 1em !important;\\\n}\\\n.ui.cards.agent-cards.agent-cards-grid > .ui.card.agent-card {\\\n width: 100% !important;\\\n min-width: 0;\\\n margin: 0 !important;\\\n overflow: hidden;\\\n align-self: start;\\\n}';\n\n\t\t\t\t\t// Process all agent card containers (one per queue block)\n\t\t\t\t\tvar cardsContainers = this.$el ? this.$el.querySelectorAll('.ui.cards.agent-cards') : [];\n\t\t\t\t\tcardsContainers.forEach(function(cardsContainer) {\n\t\t\t\t\t\tcardsContainer.classList.remove('agent-cards-masonry');\n\t\t\t\t\t\tcardsContainer.classList.remove('agent-cards-flex');\n\t\t\t\t\t\tcardsContainer.classList.add('agent-cards-grid');\n\t\t\t\t\t});\n\n\t\t\t\t\t// Bind once: relayout on resize.\n\t\t\t\t\tif (!this._agentCardsResizeBound) {\n\t\t\t\t\t\tthis._agentCardsResizeBound = true;\n\t\t\t\t\t\twindow.addEventListener('resize', function() {\n\t\t\t\t\t\t\tself.layoutAgentCardsGridMasonry();\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\tlayoutAgentCardsGridMasonry() {\n\t\t\t\t\tif (!this.$el) return;\n\t\t\t\t\tvar self = this;\n\n\t\t\t\t\t// Process all grid containers (one per queue block)\n\t\t\t\t\tvar grids = this.$el.querySelectorAll('.ui.cards.agent-cards.agent-cards-grid');\n\t\t\t\t\tgrids.forEach(function(grid) {\n\t\t\t\t\t\tself.layoutSingleGridMasonry(grid);\n\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t\tlayoutSingleGridMasonry(grid) {\n\t\t\t\t\tif (!grid) return;\n\n\t\t\t\t\tvar cs = window.getComputedStyle(grid);\n\t\t\t\t\tvar rowHeight = parseFloat(cs.getPropertyValue('grid-auto-rows')) || 1;\n\t\t\t\t\tvar rowGap = parseFloat(cs.getPropertyValue('row-gap')) || parseFloat(cs.getPropertyValue('gap')) || 8;\n\n\t\t\t\t\tvar items = Array.from(grid.querySelectorAll('.ui.card.agent-card'));\n\t\t\t\t\tif (!items.length) return;\n\n\t\t\t\t\t// Reset row spans and min-heights to measure natural heights.\n\t\t\t\t\titems.forEach(function(item) {\n\t\t\t\t\t\titem.style.gridRowEnd = '';\n\t\t\t\t\t\titem.style.minHeight = '';\n\t\t\t\t\t});\n\n\t\t\t\t\tvar tall = items.filter(function(c) { return c.querySelector('.meta.agent-peer'); });\n\t\t\t\t\tvar short = items.filter(function(c) { return !c.querySelector('.meta.agent-peer'); });\n\n\t\t\t\t\t// If we don't have both types, just do normal masonry spans.\n\t\t\t\t\tif (!tall.length || !short.length) {\n\t\t\t\t\t\titems.forEach(function(item) {\n\t\t\t\t\t\t\tvar h = item.getBoundingClientRect().height;\n\t\t\t\t\t\t\tvar span = Math.max(1, Math.ceil((h + rowGap) / (rowHeight + rowGap)));\n\t\t\t\t\t\t\titem.style.gridRowEnd = 'span ' + span;\n\t\t\t\t\t\t});\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tvar shortHeights = short.map(function(c) { return c.getBoundingClientRect().height; });\n\t\t\t\t\tvar tallHeights = tall.map(function(c) { return c.getBoundingClientRect().height; });\n\t\t\t\t\tvar hs = Math.max.apply(Math, shortHeights);\n\t\t\t\t\tvar ht = Math.max.apply(Math, tallHeights);\n\n\t\t\t\t\t// Want: 2*(hs + g) = (ht + g) => g = ht - 2*hs\n\t\t\t\t\tvar g = ht - 2 * hs;\n\t\t\t\t\tif (!Number.isFinite(g)) g = rowGap;\n\t\t\t\t\tg = Math.max(0, Math.min(24, Math.round(g)));\n\n\t\t\t\t\t// Apply gap and enforce min-heights so the relation holds visually.\n\t\t\t\t\tgrid.style.setProperty('--agent-card-gap', g + 'px');\n\n\t\t\t\t\tvar shortH = Math.round(hs);\n\t\t\t\t\tvar tallH = Math.round(Math.max(ht, 2 * hs + g));\n\t\t\t\t\tshort.forEach(function(c) { c.style.minHeight = shortH + 'px'; });\n\t\t\t\t\ttall.forEach(function(c) { c.style.minHeight = tallH + 'px'; });\n\n\t\t\t\t\t// Now compute row spans from final rendered heights.\n\t\t\t\t\tvar effectiveGap = g;\n\t\t\t\t\titems.forEach(function(item) {\n\t\t\t\t\t\tvar h = item.getBoundingClientRect().height;\n\t\t\t\t\t\tvar span = Math.max(1, Math.ceil((h + effectiveGap) / (rowHeight + effectiveGap)));\n\t\t\t\t\t\titem.style.gridRowEnd = 'span ' + span;\n\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t\tgetSrcNumForAgent(agentNumber) {\n\t\t\t\t\tlet result = '-';\n\t\t\t\t\tlet answeredFound = false;\n\t\t\t\t\tfor (const call of this.allCalls) {\n\t\t\t\t\t\tif(call.dst_num === agentNumber){\n\t\t\t\t\t\t\tansweredFound = true;\n\t\t\t\t\t\t\tresult = call.src_num;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (call.calledChannels && Array.isArray(call.calledChannels)) {\n\t\t\t\t\t\t\tconst match = call.calledChannels.find(ch => ch.number === agentNumber);\n\t\t\t\t\t\t\tif (match) {\n\t\t\t\t\t\t\t\tresult = call.src_num;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (call.bridgeChannels && Array.isArray(call.bridgeChannels)) {\n\t\t\t\t\t\t\tconst match = call.bridgeChannels.find(ch => (ch.src_num === agentNumber || ch.dst_num === agentNumber));\n\t\t\t\t\t\t\tif (match) {\n\t\t\t\t\t\t\t\tif(match.src_num === agentNumber){\n\t\t\t\t\t\t\t\t\tresult = match.dst_num;\n\t\t\t\t\t\t\t\t}else{\n\t\t\t\t\t\t\t\t\tresult = match.src_num;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tansweredFound = true;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif(answeredFound === false){\n\t\t\t\t\t\tfor (let i = 0; i < this.allCalls.length; i++) {\n\t\t\t\t\t\t\tconst tmpCall = this.allCalls[i];\n\t\t\t\t\t\t\tif(tmpCall.src_num === agentNumber){\n\t\t\t\t\t\t\t\t// Исходящий\n\t\t\t\t\t\t\t\tif(tmpCall.dst_num === ''){\n\t\t\t\t\t\t\t\t\t// не ответа, дозвон.\n\t\t\t\t\t\t\t\t\tif (tmpCall.calledChannels && Array.isArray(tmpCall.calledChannels) && tmpCall.calledChannels.length) {\n\t\t\t\t\t\t\t\t\t\tconst match = tmpCall.calledChannels.find(ch => ch.number !== agentNumber);\n\t\t\t\t\t\t\t\t\t\tif (match) {\n\t\t\t\t\t\t\t\t\t\t\tresult = match.number;\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}else if(tmpCall.spyer){\n\t\t\t\t\t\t\t\t\t\t// шпионит за номером.\n\t\t\t\t\t\t\t\t\t\tresult = tmpCall.spy_num;\n\t\t\t\t\t\t\t\t\t}else{\n\t\t\t\t\t\t\t\t\t\t// нет вызываемых каналов, возможно это вызов на приложение / ivr.\n\t\t\t\t\t\t\t\t\t\tresult = tmpCall.exten;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}else{\n\t\t\t\t\t\t\t\t\tresult = tmpCall.dst_num;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}else if(tmpCall.dst_num === agentNumber){\n\t\t\t\t\t\t\t\t// Входящий на агента, отвечен.\n\t\t\t\t\t\t\t\tresult = tmpCall.src_num;\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}else{\n\t\t\t\t\t\t\t\tif (tmpCall.calledChannels && Array.isArray(tmpCall.calledChannels)) {\n\t\t\t\t\t\t\t\t\tconst match = tmpCall.calledChannels.find(ch => ch.number === agentNumber);\n\t\t\t\t\t\t\t\t\tif (match) {\n\t\t\t\t\t\t\t\t\t\tresult = tmpCall.src_num;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\treturn result;\n\t\t\t\t},\n\t\t\t\thasPeerPhone(agentNumber) {\n\t\t\t\t\tconst phone = String(this.getSrcNumForAgent(agentNumber) || '').trim();\n\t\t\t\t\treturn phone !== '' && phone !== '-' && phone !== '—';\n\t\t\t\t},\n\t\t\t\tgetPeerPhoneLabel(agentNumber) {\n\t\t\t\t\tconst phone = String(this.getSrcNumForAgent(agentNumber) || '').trim();\n\t\t\t\t\treturn this.hasPeerPhone(agentNumber) ? phone : '—';\n\t\t\t\t},\n\t\t\t\tgetPeerNameLabel(agentNumber) {\n\t\t\t\t\t// Use cached contacts (WS + IndexedDB) to show client name for peer phone.\n\t\t\t\t\tconst phone = this.getPeerPhoneLabel(agentNumber);\n\t\t\t\t\tconst client = this.getClientNameByPhone(phone);\n\t\t\t\t\treturn client || '—';\n\t\t\t\t}\n\t\t\t},\n\t\t\tdata: {\n\t\t\t\t\"minWaitVisible\": 30,\n\t\t\t\t\"nowTick\": 0,\n\t\t\t\t\"queues\": {},\n\t\t\t\t\"allCalls\": [],\n\t\t\t\t\"selectedQueueIds\": [],\n\t\t\t\t\"lastActiveCallsPayload\": null,\n\t\t\t\t\"contactsByPhone10\": {}\n\t\t\t},\n\t\t});\n\t\twindow[className].applyContactsCacheToQueueWidget();\n\n\t\twindow[className].$callsWidget = new Vue({\n\t\t\tel: '#calls',\n\t\t\tdelimiters: [\"<%\",\"%>\"],\n\t\t\tdata: {\n\t\t\t\t\"minWaitVisible\": 30,\n\t\t\t\t\"nowTick\": 0,\n\t\t\t\tuserNumber: userNumber,\n\t\t\t\tfullAccess: ($('#fullAccess').val() === \"1\" || userNumber === ''),\n\t\t\t\tcalls: [\n\t\t\t\t]\n\t\t\t},\n\t\t\tmethods: {\n\t\t\t\tcallIsVisible(call){\n\t\t\t\t\tvoid this.nowTick;\n\t\t\t\t\tif(call.dst_chan==='' && call.queueData.EnterTime !== undefined ){\n\t\t\t\t\t\treturn this.minWaitVisible <= this.getWaitTime(call);\n\t\t\t\t\t}\n\t\t\t\t\treturn true;\n\t\t\t\t},\n\t\t\t\tformatTimestampToTime(timestamp) {\n\t\t\t\t\t// Если timestamp строка — приводим к числу\n\t\t\t\t\tconst ts = typeof timestamp === 'string' ? parseFloat(timestamp) : timestamp;\n\n\t\t\t\t\t// Если timestamp в секундах (меньше 1e10), умножаем на 1000\n\t\t\t\t\tconst ms = ts < 1e10 ? ts * 1000 : ts;\n\n\t\t\t\t\tconst date = new Date(ms);\n\n\t\t\t\t\tconst hours = String(date.getHours()).padStart(2, '0');\n\t\t\t\t\tconst minutes = String(date.getMinutes()).padStart(2, '0');\n\t\t\t\t\tconst seconds = String(date.getSeconds()).padStart(2, '0');\n\n\t\t\t\t\treturn `${hours}:${minutes}:${seconds}`;\n\t\t\t\t},\n\t\t\t\tgetWaitTime(call){\n\t\t\t\t\tvoid this.nowTick;\n\t\t\t\t\tlet answer = Math.floor(Date.now() / 1000);\n\t\t\t\t\tif(call.answer !== ''){\n\t\t\t\t\t\tanswer = call.answer\n\t\t\t\t\t}\n\t\t\t\t\treturn window[className].secondToTime(answer - call.start);\n\t\t\t\t},\n\t\t\t\tgetCallTime(call){\n\t\t\t\t\tvoid this.nowTick;\n\t\t\t\t\tif(call.answer === ''){\n\t\t\t\t\t\treturn '-';\n\t\t\t\t\t}\n\t\t\t\t\treturn window[className].formatElapsedTime(call.answer);\n\t\t\t\t},\n\t\t\t\tupdatedCallsFromResponse(data) {\n\t\t\t\t\tthis.minWaitVisible = 1*$('#minWaitVisibleValue').val();\n\t\t\t\t\t// Проходим по всем очередям\n\t\t\t\t\tfor (const queueId in data.queues) {\n\t\t\t\t\t\tconst queue = data.queues[queueId];\n\t\t\t\t\t\t// Проверяем, есть ли у очереди поле calls и является ли оно массивом\n\t\t\t\t\t\tif (Array.isArray(queue.calls)) {\n\t\t\t\t\t\t\t// Добавляем все вызовы из этой очереди в общий массив\n\t\t\t\t\t\t\tdata.calls.push(...queue.calls);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tthis.calls = data.calls;\n\t\t\t\t\tthis.$nextTick(() => {\n\t\t\t\t\t\tExtensions.updatePhonesRepresent('need-update');\n\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t\tformatElapsedTime(enterTime) {\n\t\t\t\t\treturn window[className].formatElapsedTime(enterTime);\n\t\t\t\t},\n\t\t\t\tgetClientHeader(phone) {\n\t\t\t\t\tconst q = window[className].$widgetQueues;\n\t\t\t\t\tif (q && typeof q.getClientHeader === 'function') {\n\t\t\t\t\t\treturn q.getClientHeader(phone);\n\t\t\t\t\t}\n\t\t\t\t\treturn phone;\n\t\t\t\t},\n\t\t\t\thangupAction(event) {\n\t\t\t\t\tlet target = $(event.target);\n\t\t\t\t\tif(target.attr('data-ch1') === undefined){\n\t\t\t\t\t\ttarget = $(event.target).parent();\n\t\t\t\t\t}\n\t\t\t\t\twindow[className].executeCallAction({action: 'hangup', ch1: target.attr('data-ch1'), ch2: target.attr('data-ch2')});\n\t\t\t\t},\n\t\t\t\tjoinAction(event) {\n\t\t\t\t\tlet target = $(event.target);\n\t\t\t\t\tif(target.attr('data-ch1') === undefined){\n\t\t\t\t\t\ttarget = $(event.target).parent();\n\t\t\t\t\t}\n\t\t\t\t\tif(this.userNumber === ''){\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\twindow[className].executeCallAction({action: 'join', ch1: target.attr('data-ch1'), ch2: target.attr('data-ch2'), number: this.userNumber});\n\t\t\t\t},\n\t\t\t\twhisperAction(event){\n\t\t\t\t\tlet target = $(event.target);\n\t\t\t\t\tif(target.attr('data-ch1') === undefined){\n\t\t\t\t\t\ttarget = $(event.target).parent();\n\t\t\t\t\t}\n\t\t\t\t\tif(this.userNumber === ''){\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tlet spChannel = target.attr('data-ch1');\n\t\t\t\t\tif('incoming' === target.attr('data-call-type')){\n\t\t\t\t\t\tspChannel = target.attr('data-ch2');\n\t\t\t\t\t}\n\t\t\t\t\twindow[className].executeCallAction({action: 'whisper', ch1: spChannel, ch2: '', number: this.userNumber});\n\t\t\t\t},\n\t\t\t\tlistenAction(event){\n\t\t\t\t\tlet target = $(event.target);\n\t\t\t\t\tif(target.attr('data-ch1') === undefined){\n\t\t\t\t\t\ttarget = $(event.target).parent();\n\t\t\t\t\t}\n\t\t\t\t\tif(this.userNumber === ''){\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\twindow[className].executeCallAction({action: 'listen', ch1: target.attr('data-ch1'), ch2: target.attr('data-ch2'), number: this.userNumber});\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\twindow[className].$widget = new Vue({\n\t\t\tel: '#app',\n\t\t\tdelimiters: [\"<%\",\"%>\"],\n\t\t\tdata: {\n\t\t\t\tuserNumber: userNumber,\n\t\t\t\tfullAccess: ($('#fullAccess').val() === \"1\" || userNumber === ''),\n\t\t\t\tcalls: [\n\t\t\t\t]\n\t\t\t},\n\t\t\tmethods: {\n\t\t\t\tupdatedCallsFromResponse(lines) {\n\t\t\t\t\tthis.calls = lines;\n\t\t\t\t\tthis.$nextTick(() => {\n\t\t\t\t\t\tExtensions.updatePhonesRepresent('need-update');\n\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t\thangupAction(event) {\n\t\t\t\t\tlet target = $(event.target);\n\t\t\t\t\tif(target.attr('data-ch1') === undefined){\n\t\t\t\t\t\ttarget = $(event.target).parent();\n\t\t\t\t\t}\n\t\t\t\t\twindow[className].executeCallAction({action: 'hangup', ch1: target.attr('data-ch1'), ch2: target.attr('data-ch2')});\n\t\t\t\t},\n\t\t\t\tjoinAction(event) {\n\t\t\t\t\tlet target = $(event.target);\n\t\t\t\t\tif(target.attr('data-ch1') === undefined){\n\t\t\t\t\t\ttarget = $(event.target).parent();\n\t\t\t\t\t}\n\t\t\t\t\tif(this.userNumber === ''){\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\twindow[className].executeCallAction({action: 'join', ch1: target.attr('data-ch1'), ch2: target.attr('data-ch2'), number: this.userNumber});\n\t\t\t\t},\n\t\t\t\twhisperAction(event){\n\t\t\t\t\tlet target = $(event.target);\n\t\t\t\t\tif(target.attr('data-ch1') === undefined){\n\t\t\t\t\t\ttarget = $(event.target).parent();\n\t\t\t\t\t}\n\t\t\t\t\tif(this.userNumber === ''){\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tlet spChannel = target.attr('data-ch1');\n\t\t\t\t\tif('incoming' === target.attr('data-call-type')){\n\t\t\t\t\t\tspChannel = target.attr('data-ch2');\n\t\t\t\t\t}\n\t\t\t\t\twindow[className].executeCallAction({action: 'whisper', ch1: spChannel, ch2: '', number: this.userNumber});\n\t\t\t\t},\n\t\t\t\tlistenAction(event){\n\t\t\t\t\tlet target = $(event.target);\n\t\t\t\t\tif(target.attr('data-ch1') === undefined){\n\t\t\t\t\t\ttarget = $(event.target).parent();\n\t\t\t\t\t}\n\t\t\t\t\tif(this.userNumber === ''){\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\twindow[className].executeCallAction({action: 'listen', ch1: target.attr('data-ch1'), ch2: target.attr('data-ch2'), number: this.userNumber});\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\twindow[className].$checkBoxes.checkbox();\n\t\twindow[className].$dropDowns.dropdown();\n\t\twindow[className].initializeForm();\n\t\t$('.menu .item').tab();\n\t\twindow[className].startUiTicker();\n\t\t//////\n\t\t// Удаляем отступы контейнера.\n\t\t$('#main-content-container').removeClass('container');\n\t\t$('#module-status-toggle-segment').hide();\n\t\t$('.ui.clearing.hidden.divider').remove();\n\t\t// Окончание форматирования базовой страницы\n\t\t//////\n\t\tthis.startPollingActiveCalls();\n\n\t\t// Allow settings to be saved after initialization\n\t\tsetTimeout(function() {\n\t\t\twindow[className].isInit = false;\n\t\t}, 1000);\n\t},\n\tstartUiTicker() {\n\t\tif (this._uiTicker) return;\n\t\tthis._uiTicker = setInterval(() => {\n\t\t\tconst now = Date.now();\n\t\t\tif (window[className].$widgetQueues) {\n\t\t\t\twindow[className].$widgetQueues.nowTick = now;\n\t\t\t}\n\t\t\tif (window[className].$callsWidget) {\n\t\t\t\twindow[className].$callsWidget.nowTick = now;\n\t\t\t}\n\t\t}, 1000);\n\t},\n\tstartPollingActiveCalls() {\n\t\tif (this._activeCallsPollTimer) return;\n\t\twindow[className].updateLines();\n\t\tthis._activeCallsPollTimer = setInterval(window[className].updateLines, 2000);\n\t},\n\tstopPollingActiveCalls() {\n\t\tif (!this._activeCallsPollTimer) return;\n\t\tclearInterval(this._activeCallsPollTimer);\n\t\tthis._activeCallsPollTimer = null;\n\t},\n\tasync initContactsCache() {\n\t\ttry {\n\t\t\tthis._contactsCacheByPhone10 = await this.idbLoadAllContacts();\n\t\t\tthis.applyContactsCacheToQueueWidget();\n\t\t} catch (e) {\n\t\t\tconsole.log('contacts cache init error', e);\n\t\t\tthis._contactsCacheByPhone10 = {};\n\t\t}\n\t},\n\tapplyContactsCacheToQueueWidget() {\n\t\tif (!this._contactsCacheByPhone10) return;\n\t\tif (!window[className].$widgetQueues) return;\n\t\tfor (const [phone10, client] of Object.entries(this._contactsCacheByPhone10)) {\n\t\t\tif (window[className].$widgetQueues.$set) {\n\t\t\t\twindow[className].$widgetQueues.$set(window[className].$widgetQueues.contactsByPhone10, phone10, client);\n\t\t\t} else {\n\t\t\t\twindow[className].$widgetQueues.contactsByPhone10[phone10] = client;\n\t\t\t}\n\t\t}\n\t},\n\tidbOpenContactsDb() {\n\t\treturn new Promise((resolve, reject) => {\n\t\t\ttry {\n\t\t\t\tconst req = indexedDB.open('ModuleMonitorActiveCalls', 1);\n\t\t\t\treq.onupgradeneeded = () => {\n\t\t\t\t\tconst db = req.result;\n\t\t\t\t\tif (!db.objectStoreNames.contains('contactsByPhone10')) {\n\t\t\t\t\t\tdb.createObjectStore('contactsByPhone10', { keyPath: 'phone10' });\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t\treq.onsuccess = () => resolve(req.result);\n\t\t\t\treq.onerror = () => reject(req.error);\n\t\t\t} catch (e) {\n\t\t\t\treject(e);\n\t\t\t}\n\t\t});\n\t},\n\tasync idbPutContact(phone10, client) {\n\t\tconst db = await this.idbOpenContactsDb();\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tconst tx = db.transaction('contactsByPhone10', 'readwrite');\n\t\t\tconst store = tx.objectStore('contactsByPhone10');\n\t\t\tstore.put({ phone10, client, updatedAt: Date.now() });\n\t\t\ttx.oncomplete = () => { db.close(); resolve(); };\n\t\t\ttx.onerror = () => { const err = tx.error; db.close(); reject(err); };\n\t\t});\n\t},\n\tasync idbLoadAllContacts() {\n\t\tconst db = await this.idbOpenContactsDb();\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tconst tx = db.transaction('contactsByPhone10', 'readwrite');\n\t\t\tconst store = tx.objectStore('contactsByPhone10');\n\t\t\tconst req = store.getAll();\n\t\t\treq.onsuccess = () => {\n\t\t\t\tconst map = {};\n\t\t\t\tconst now = Date.now();\n\t\t\t\tconst ttlMs = Number(this.contactsCacheTtlMs) || (120 * 60 * 1000);\n\t\t\t\tfor (const row of req.result || []) {\n\t\t\t\t\tconst phone10 = row?.phone10;\n\t\t\t\t\tconst client = row?.client;\n\t\t\t\t\tconst updatedAt = Number(row?.updatedAt) || 0;\n\t\t\t\t\tconst isFresh = phone10 && client && updatedAt > 0 && (now - updatedAt) <= ttlMs;\n\t\t\t\t\tif (isFresh) {\n\t\t\t\t\t\tmap[phone10] = client;\n\t\t\t\t\t} else if (phone10) {\n\t\t\t\t\t\t// Cleanup expired/broken records\n\t\t\t\t\t\ttry { store.delete(phone10); } catch (e) { /* ignore */ }\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\ttx.oncomplete = () => { db.close(); resolve(map); };\n\t\t\t\ttx.onerror = () => { const err = tx.error; db.close(); reject(err); };\n\t\t\t};\n\t\t\treq.onerror = () => { const err = req.error; db.close(); reject(err); };\n\t\t});\n\t},\n\trequestBackendEnable() {\n\t\t$.api({\n\t\t\turl: window[className].backendEnableUrl,\n\t\t\ton: 'now',\n\t\t\tmethod: 'POST',\n\t\t\tonSuccess(response) {\n\t\t\t\tconsole.log('backandEnable response', response);\n\t\t\t\tconst accessToken = response?.data?.access_token;\n\t\t\t\tconst refreshToken = response?.data?.refresh_token;\n\t\t\t\tif (accessToken && refreshToken) {\n\t\t\t\t\twindow[className].setAuthTokens(accessToken, refreshToken);\n\t\t\t\t\twindow[className].connectContactsWs();\n\t\t\t\t\twindow[className].connectActiveCallsWs();\n\t\t\t\t}\n\t\t\t},\n\t\t\tonFailure(response) {\n\t\t\t\tconsole.log('backandEnable failure', response);\n\t\t\t},\n\t\t\tonError(errorMessage, element, xhr) {\n\t\t\t\tconsole.log('backandEnable error', errorMessage, xhr);\n\t\t\t}\n\t\t});\n\t},\n\tsetAuthTokens(accessToken, refreshToken) {\n\t\tthis._authTokens = this._authTokens || {};\n\t\tthis._authTokens.access_token = accessToken;\n\t\tthis._authTokens.refresh_token = refreshToken;\n\t\tthis._authTokens.exp = this.getJwtExp(accessToken);\n\t},\n\trefreshAuthToken() {\n\t\tconst refreshToken = this._authTokens?.refresh_token;\n\t\tif (!refreshToken) {\n\t\t\tthis.requestBackendEnable();\n\t\t\treturn;\n\t\t}\n\t\t$.ajax({\n\t\t\turl: '/pbxcore/api/module-softphone-backend/v1/auth/refresh',\n\t\t\tmethod: 'POST',\n\t\t\theaders: {\n\t\t\t\t'Authorization': 'Bearer ' + refreshToken\n\t\t\t},\n\t\t\tsuccess: (response) => {\n\t\t\t\tconst accessToken = response?.access_token;\n\t\t\t\tconst newRefreshToken = response?.refresh_token;\n\t\t\t\tif (accessToken && newRefreshToken) {\n\t\t\t\t\tthis.setAuthTokens(accessToken, newRefreshToken);\n\t\t\t\t\tthis.scheduleContactsWsTokenRefresh();\n\t\t\t\t} else {\n\t\t\t\t\tthis.requestBackendEnable();\n\t\t\t\t}\n\t\t\t},\n\t\t\terror: () => {\n\t\t\t\tthis.requestBackendEnable();\n\t\t\t}\n\t\t});\n\t},\n\tgetJwtExp(token) {\n\t\ttry {\n\t\t\tif (!token || typeof token !== 'string') return 0;\n\t\t\tconst parts = token.split('.');\n\t\t\tif (parts.length < 2) return 0;\n\t\t\tconst payloadB64 = parts[1].replace(/-/g, '+').replace(/_/g, '/');\n\t\t\tconst padded = payloadB64 + '='.repeat((4 - (payloadB64.length % 4)) % 4);\n\t\t\tconst json = atob(padded);\n\t\t\tconst payload = JSON.parse(json);\n\t\t\treturn Number(payload?.exp) || 0;\n\t\t} catch (e) {\n\t\t\treturn 0;\n\t\t}\n\t},\n\tisAccessTokenExpired(skewSeconds = 0) {\n\t\tconst exp = Number(this._authTokens?.exp) || 0;\n\t\tif (!exp) return false; // unknown exp -> don't force refresh\n\t\tconst now = Math.floor(Date.now() / 1000);\n\t\treturn now + skewSeconds >= exp;\n\t},\n\tscheduleContactsWsTokenRefresh() {\n\t\tif (this._contactsWsTokenTimer) {\n\t\t\tclearTimeout(this._contactsWsTokenTimer);\n\t\t\tthis._contactsWsTokenTimer = null;\n\t\t}\n\t\tconst exp = Number(this._authTokens?.exp) || 0;\n\t\tif (!exp) return;\n\t\tconst now = Math.floor(Date.now() / 1000);\n\t\tconst refreshInSec = Math.max(1, exp - now - 15); // 15s before exp\n\t\tthis._contactsWsTokenTimer = setTimeout(() => {\n\t\t\tthis.refreshAuthToken();\n\t\t}, refreshInSec * 1000);\n\t},\n\tscheduleContactsWsReconnect(reason, forceReAuth = false) {\n\t\tif (this._contactsWsReconnectTimer) {\n\t\t\tclearTimeout(this._contactsWsReconnectTimer);\n\t\t\tthis._contactsWsReconnectTimer = null;\n\t\t}\n\t\tthis._contactsWsReconnectAttempt = (this._contactsWsReconnectAttempt || 0) + 1;\n\t\tconst delay = Math.min(30000, 1000 * Math.pow(2, Math.min(5, this._contactsWsReconnectAttempt - 1)));\n\t\tthis._contactsWsReconnectTimer = setTimeout(() => {\n\t\t\tif (forceReAuth || this.isAccessTokenExpired(5)) {\n\t\t\t\tthis.requestBackendEnable();\n\t\t\t} else {\n\t\t\t\tthis.connectContactsWs();\n\t\t\t}\n\t\t}, delay);\n\t\tconsole.log('contacts ws reconnect scheduled', { reason, delayMs: delay });\n\t},\n\tconnectContactsWs() {\n\t\ttry {\n\t\t\tconst accessToken = this._authTokens?.access_token;\n\t\t\tif (!accessToken) return;\n\n\t\t\t// Avoid reconnecting if already connected/connecting\n\t\t\tif (this._contactsWs && (this._contactsWs.readyState === WebSocket.OPEN || this._contactsWs.readyState === WebSocket.CONNECTING)) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t// Reset backoff on explicit connect attempt\n\t\t\tthis._contactsWsReconnectAttempt = 0;\n\n\t\t\tconst wsProto = window.location.protocol === 'https:' ? 'wss' : 'ws';\n\t\t\tconst wsHost = window.location.host; // host:port of current page\n\t\t\tconst tokenParam = encodeURIComponent(accessToken);\n\t\t\tconst wsUrl = `${wsProto}://${wsHost}/pbxcore/api/module-softphone-backend/v1/sub/contacts?authorization=${tokenParam}`;\n\n\t\t\tthis._contactsWs = new WebSocket(wsUrl);\n\t\t\tthis._contactsWs.onopen = () => {\n\t\t\t\tconsole.log('contacts ws connected');\n\t\t\t\tthis.scheduleContactsWsTokenRefresh();\n\t\t\t};\n\t\t\tthis._contactsWs.onmessage = (event) => {\n\t\t\t\tthis.handleContactsWsMessage(event?.data);\n\t\t\t};\n\t\t\tthis._contactsWs.onerror = (event) => {\n\t\t\t\tconsole.log('contacts ws error', event);\n\t\t\t};\n\t\t\tthis._contactsWs.onclose = (event) => {\n\t\t\t\tconst code = event?.code;\n\t\t\t\tconst reason = event?.reason;\n\t\t\t\tconsole.log('contacts ws closed', { code, reason });\n\n\t\t\t\tif (this._contactsWsTokenTimer) {\n\t\t\t\t\tclearTimeout(this._contactsWsTokenTimer);\n\t\t\t\t\tthis._contactsWsTokenTimer = null;\n\t\t\t\t}\n\n\t\t\t\t// 1000 = normal close -> reconnect; auth closes vary by server implementation.\n\t\t\t\tconst authCloseCodes = new Set([1008, 4001, 4401, 4403]);\n\t\t\t\tconst forceReAuth = authCloseCodes.has(code) || this.isAccessTokenExpired(0);\n\t\t\t\tthis.scheduleContactsWsReconnect('close', forceReAuth);\n\t\t\t};\n\t\t} catch (e) {\n\t\t\tconsole.log('contacts ws init error', e);\n\t\t\tthis.scheduleContactsWsReconnect('init_error', this.isAccessTokenExpired(0));\n\t\t}\n\t},\n\tscheduleActiveCallsWsReconnect(reason, forceReAuth = false) {\n\t\tif (this._activeCallsWsReconnectTimer) {\n\t\t\tclearTimeout(this._activeCallsWsReconnectTimer);\n\t\t\tthis._activeCallsWsReconnectTimer = null;\n\t\t}\n\t\tthis._activeCallsWsReconnectAttempt = (this._activeCallsWsReconnectAttempt || 0) + 1;\n\t\tconst delay = Math.min(30000, 1000 * Math.pow(2, Math.min(5, this._activeCallsWsReconnectAttempt - 1)));\n\t\tthis._activeCallsWsReconnectTimer = setTimeout(() => {\n\t\t\tif (forceReAuth || this.isAccessTokenExpired(5)) {\n\t\t\t\tthis.requestBackendEnable();\n\t\t\t} else {\n\t\t\t\tthis.connectActiveCallsWs();\n\t\t\t}\n\t\t}, delay);\n\t\tconsole.log('active-calls ws reconnect scheduled', { reason, delayMs: delay });\n\t},\n\tconnectActiveCallsWs() {\n\t\ttry {\n\t\t\tconst accessToken = this._authTokens?.access_token;\n\t\t\tif (!accessToken) return;\n\n\t\t\t// Avoid reconnecting if already connected/connecting\n\t\t\tif (this._activeCallsWs && (this._activeCallsWs.readyState === WebSocket.OPEN || this._activeCallsWs.readyState === WebSocket.CONNECTING)) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t// Reset backoff on explicit connect attempt\n\t\t\tthis._activeCallsWsReconnectAttempt = 0;\n\n\t\t\t// Token exists -> use WS, disable polling fallback\n\t\t\tthis.stopPollingActiveCalls();\n\n\t\t\tconst wsProto = window.location.protocol === 'https:' ? 'wss' : 'ws';\n\t\t\tconst wsHost = window.location.host; // host:port of current page\n\t\t\tconst tokenParam = encodeURIComponent(accessToken);\n\t\t\tconst wsUrl = `${wsProto}://${wsHost}/pbxcore/api/module-softphone-backend/v1/sub/active-calls?authorization=${tokenParam}`;\n\n\t\t\tthis._activeCallsWs = new WebSocket(wsUrl);\n\t\t\tthis._activeCallsWs.onopen = () => {\n\t\t\t\tconsole.log('active-calls ws connected');\n\t\t\t\t// Reuse the same token refresh timer (it triggers requestBackendEnable)\n\t\t\t\tthis.scheduleContactsWsTokenRefresh();\n\t\t\t};\n\t\t\tthis._activeCallsWs.onmessage = (event) => {\n\t\t\t\tthis.handleActiveCallsWsMessage(event?.data);\n\t\t\t};\n\t\t\tthis._activeCallsWs.onerror = (event) => {\n\t\t\t\tconsole.log('active-calls ws error', event);\n\t\t\t};\n\t\t\tthis._activeCallsWs.onclose = (event) => {\n\t\t\t\tconst code = event?.code;\n\t\t\t\tconst reason = event?.reason;\n\t\t\t\tconsole.log('active-calls ws closed', { code, reason });\n\n\t\t\t\t// Auth closes vary by server implementation.\n\t\t\t\tconst authCloseCodes = new Set([1008, 4001, 4401, 4403]);\n\t\t\t\tconst forceReAuth = authCloseCodes.has(code) || this.isAccessTokenExpired(0);\n\t\t\t\tthis.scheduleActiveCallsWsReconnect('close', forceReAuth);\n\t\t\t};\n\t\t} catch (e) {\n\t\t\tconsole.log('active-calls ws init error', e);\n\t\t\tthis.scheduleActiveCallsWsReconnect('init_error', this.isAccessTokenExpired(0));\n\t\t}\n\t},\n\thandleContactsWsMessage(data) {\n\t\ttry {\n\t\t\tif (!data) return;\n\t\t\tconst parsed = typeof data === 'string' ? JSON.parse(data) : data;\n\t\t\tconst items = Array.isArray(parsed) ? parsed : [parsed];\n\t\t\tfor (const item of items) {\n\t\t\t\tconst digits = String(item?.number || '').replace(/\\D+/g, '');\n\t\t\t\tconst phone10 = digits.length <= 10 ? digits : digits.slice(-10);\n\t\t\t\tconst displayName = String(item?.client || item?.contact || '').trim();\n\t\t\t\tif (phone10 && displayName) {\n\t\t\t\t\tthis._contactsCacheByPhone10 = this._contactsCacheByPhone10 || {};\n\t\t\t\t\tthis._contactsCacheByPhone10[phone10] = displayName;\n\t\t\t\t\tthis.idbPutContact(phone10, displayName).catch((e) => console.log('contacts cache save error', e));\n\t\t\t\t}\n\t\t\t\tif (window[className].$widgetQueues) {\n\t\t\t\t\twindow[className].$widgetQueues.updateContactFromWs(item);\n\t\t\t\t}\n\t\t\t\t// Calls table is a separate Vue instance and reads client name via $widgetQueues.\n\t\t\t\t// Vue can't track cross-instance dependency, so force re-render on contact update.\n\t\t\t\tif (window[className].$callsWidget && typeof window[className].$callsWidget.$forceUpdate === 'function') {\n\t\t\t\t\twindow[className].$callsWidget.$forceUpdate();\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (e) {\n\t\t\tconsole.log('contacts ws parse error', e);\n\t\t}\n\t},\n\thandleActiveCallsWsMessage(data) {\n\t\ttry {\n\t\t\tif (!data) return;\n\t\t\tconst parsed = typeof data === 'string' ? JSON.parse(data) : data;\n\t\t\tconst payload = parsed?.queues ? parsed : (parsed?.data?.queues ? parsed.data : null);\n\t\t\tif (!payload) return;\n\t\t\tif (!window[className].$widgetQueues || !window[className].$callsWidget) return;\n\n\t\t\twindow[className].$widgetQueues.updatedCallsFromResponse(payload);\n\t\t\twindow[className].$callsWidget.updatedCallsFromResponse(payload);\n\t\t} catch (e) {\n\t\t\tconsole.log('active-calls ws parse error', e);\n\t\t}\n\t},\n\tformatElapsedTime(enterTime) {\n\t\tif (!enterTime) return '—';\n\n\t\tconst now = Math.floor(Date.now() / 1000);\n\t\tconst diffSeconds = now - enterTime;\n\n\t\treturn window[className].secondToTime(diffSeconds);\n\t},\n\tsecondToTime(diffSeconds){\n\t\tif (diffSeconds < 0) return '0';\n\t\t// Форматируем: чч:мм:сс или мм:сс, или просто секунды\n\t\tconst hours = Math.floor(diffSeconds / 3600);\n\t\tconst minutes = Math.floor((diffSeconds % 3600) / 60);\n\t\tconst seconds = Math.round(diffSeconds % 60);\n\t\tif (hours > 0) {\n\t\t\treturn `${hours}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;\n\t\t} else if (minutes > 0) {\n\t\t\treturn `${minutes}:${seconds.toString().padStart(2, '0')}`;\n\t\t} else {\n\t\t\treturn `${seconds}`;\n\t\t}\n\t},\n\tonChangeSetting(settingName, value) {\n\t\tif(window[className].isInit){\n\t\t\treturn;\n\t\t}\n\t\tvar data = {};\n\t\tdata[settingName] = value;\n\t\t$.api({\n\t\t\turl: window[className].saveUserActionUrl,\n\t\t\ton: 'now',\n\t\t\tmethod: 'POST',\n\t\t\tdata: data,\n\t\t\tsuccessTest: function(response) {\n\t\t\t\treturn response !== undefined && Object.keys(response).length > 0 && response.success === true;\n\t\t\t},\n\t\t\tonSuccess: function(response) {\n\t\t\t\tif(settingName === 'queueIds'){\n\t\t\t\t\t// Update hidden input and Vue data\n\t\t\t\t\t$('#queueIds').val(value);\n\t\t\t\t\t// Re-render queue widget from last received payload (WS mode)\n\t\t\t\t\tif (window[className].$widgetQueues && typeof window[className].$widgetQueues.refreshFromLastPayload === 'function') {\n\t\t\t\t\t\twindow[className].$widgetQueues.refreshFromLastPayload();\n\t\t\t\t\t}\n\t\t\t\t}else if( settingName === 'adminUserId'){\n\t\t\t\t\twindow.location.href = window.location.href;\n\t\t\t\t}\n\t\t\t},\n\t\t\tonFailure: function(response) {\n\t\t\t\tconsole.log(response);\n\t\t\t},\n\t\t\tonError: function(errorMessage, element, xhr) {\n\t\t\t\tconsole.log(errorMessage,xhr);\n\t\t\t}\n\t\t});\n\t},\n\texecuteCallAction(data) {\n\t\t$.api({\n\t\t\turl: window[className].executeCallUrl,\n\t\t\ton: 'now',\n\t\t\tmethod: 'POST',\n\t\t\tdata: data,\n\t\t\tsuccessTest(response) {\n\t\t\t\treturn response !== undefined && Object.keys(response).length > 0 && response.success === true;\n\t\t\t},\n\t\t\tonSuccess(response) {\n\t\t\t\tconsole.log(response);\n\t\t\t},\n\t\t\tonFailure(response) {\n\t\t\t\tconsole.log(response);\n\t\t\t},\n\t\t\tonError(errorMessage, element, xhr) {\n\t\t\t\tconsole.log(errorMessage,xhr);\n\t\t\t}\n\t\t});\n\t},\n\tupdateLines() {\n\t\t$.api({\n\t\t\turl: window[className].activeChannelsUrlV2,\n\t\t\ton: 'now',\n\t\t\tmethod: 'POST',\n\t\t\tsuccessTest(response) {\n\t\t\t\treturn response !== undefined && Object.keys(response).length > 0 && response.success === true;\n\t\t\t},\n\t\t\tonSuccess(response) {\n\t\t\t\twindow[className].$widgetQueues.updatedCallsFromResponse(response);\n\t\t\t\twindow[className].$callsWidget.updatedCallsFromResponse(response);\n\t\t\t},\n\t\t\tonFailure(response) {\n\t\t\t\tconsole.log(response);\n\t\t\t},\n\t\t\tonError(errorMessage, element, xhr) {\n\t\t\t\tconsole.log(errorMessage,xhr);\n\t\t\t}\n\t\t});\n\t},\n\n\t/**\n\t * We can modify some data before form send\n\t * @param settings\n\t * @returns {*}\n\t */\n\tcbBeforeSendForm(settings) {\n\t\tconst result = settings;\n\t\tresult.data = window[className].$formObj.form('get values');\n\t\treturn result;\n\t},\n\t/**\n\t * Some actions after forms send\n\t */\n\tcbAfterSendForm() {\n\n\t},\n\t/**\n\t * Initialize form parameters\n\t */\n\tinitializeForm() {\n\t\tForm.$formObj = window[className].$formObj;\n\t\tForm.url = `${globalRootUrl}${idUrl}/save`;\n\t\tForm.validateRules = window[className].validateRules;\n\t\tForm.cbBeforeSendForm = window[className].cbBeforeSendForm;\n\t\tForm.cbAfterSendForm = window[className].cbAfterSendForm;\n\t\tForm.initialize();\n\t},\n};\n\n$(document).ready(() => {\n\twindow[className].initialize();\n});\n\n"],"mappings":"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","ignoreList":[]} \ No newline at end of file diff --git a/public/assets/js/src/module-monitor-active-calls-index.js b/public/assets/js/src/module-monitor-active-calls-index.js index 68d6229..4f35b32 100644 --- a/public/assets/js/src/module-monitor-active-calls-index.js +++ b/public/assets/js/src/module-monitor-active-calls-index.js @@ -858,6 +858,33 @@ const ModuleMonitorActiveCalls = { this._authTokens.refresh_token = refreshToken; this._authTokens.exp = this.getJwtExp(accessToken); }, + refreshAuthToken() { + const refreshToken = this._authTokens?.refresh_token; + if (!refreshToken) { + this.requestBackendEnable(); + return; + } + $.ajax({ + url: '/pbxcore/api/module-softphone-backend/v1/auth/refresh', + method: 'POST', + headers: { + 'Authorization': 'Bearer ' + refreshToken + }, + success: (response) => { + const accessToken = response?.access_token; + const newRefreshToken = response?.refresh_token; + if (accessToken && newRefreshToken) { + this.setAuthTokens(accessToken, newRefreshToken); + this.scheduleContactsWsTokenRefresh(); + } else { + this.requestBackendEnable(); + } + }, + error: () => { + this.requestBackendEnable(); + } + }); + }, getJwtExp(token) { try { if (!token || typeof token !== 'string') return 0; @@ -879,7 +906,6 @@ const ModuleMonitorActiveCalls = { return now + skewSeconds >= exp; }, scheduleContactsWsTokenRefresh() { - // Proactively refresh token shortly before expiry by re-requesting backendEnable. if (this._contactsWsTokenTimer) { clearTimeout(this._contactsWsTokenTimer); this._contactsWsTokenTimer = null; @@ -889,8 +915,7 @@ const ModuleMonitorActiveCalls = { const now = Math.floor(Date.now() / 1000); const refreshInSec = Math.max(1, exp - now - 15); // 15s before exp this._contactsWsTokenTimer = setTimeout(() => { - // Re-get tokens and reconnect WS - this.requestBackendEnable(); + this.refreshAuthToken(); }, refreshInSec * 1000); }, scheduleContactsWsReconnect(reason, forceReAuth = false) { From c63aaa3563ab5c58fa4f1dba4153eb89e38ad0cf Mon Sep 17 00:00:00 2001 From: Alexey Portnov Date: Mon, 16 Feb 2026 16:07:56 +0800 Subject: [PATCH 15/19] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B0=20=D0=BE=D1=88=D0=B8=D0=B1=D0=BA=D0=B0?= =?UTF-8?q?=20stream=5Fset=5Ftimeout=20=D0=BD=D0=B0=20=D0=BD=D0=B5=D0=B2?= =?UTF-8?q?=D0=B0=D0=BB=D0=B8=D0=B4=D0=BD=D0=BE=D0=BC=20=D1=81=D0=BE=D0=BA?= =?UTF-8?q?=D0=B5=D1=82=D0=B5=20AMI?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Lib/AsteriskManager.php | 2 +- bin/WorkerActiveCalls.php | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Lib/AsteriskManager.php b/Lib/AsteriskManager.php index e12897e..4faac09 100644 --- a/Lib/AsteriskManager.php +++ b/Lib/AsteriskManager.php @@ -570,7 +570,7 @@ public function ping(): array */ public function setSocketTimeout(int $seconds, int $microseconds = 0): bool { - if ($this->socket === false) { + if (!is_resource($this->socket)) { return false; } return stream_set_timeout($this->socket, $seconds, $microseconds); diff --git a/bin/WorkerActiveCalls.php b/bin/WorkerActiveCalls.php index 39a35a2..f1cf2d1 100644 --- a/bin/WorkerActiveCalls.php +++ b/bin/WorkerActiveCalls.php @@ -1135,7 +1135,11 @@ private function initManagerAsterisk():void $amiPort = PbxSettings::getValueByKey('AMIPort'); $this->amCustom = new CustomAsteriskManager(); // Оригинальный AsteriskManager работает плохо с BridgeList и BridgeInfo - $this->amCustom->connect("127.0.0.1:$amiPort", MonitorActiveCallsConf::AMI_USER, MonitorActiveCallsConf::AMI_USER); + $connected = $this->amCustom->connect("127.0.0.1:$amiPort", MonitorActiveCallsConf::AMI_USER, MonitorActiveCallsConf::AMI_USER); + if (!$connected) { + $this->logger->writeError("Failed to connect to AMI on port $amiPort"); + return; + } // Устанавливаем короткий timeout для быстрой отработки idle callback и отправки WebSocket updates // 300ms оптимально согласуется с STATE_UPDATE_DELAY (200ms) $this->amCustom->setSocketTimeout(0, 300000); From 065a17ac63e6b5de82d18408292d8a3e546117d5 Mon Sep 17 00:00:00 2001 From: Alexey Portnov Date: Wed, 18 Feb 2026 20:23:31 +0800 Subject: [PATCH 16/19] =?UTF-8?q?=D0=A3=D1=81=D1=82=D1=80=D0=B0=D0=BD?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=B7=D0=B0=D0=B2=D0=B8=D1=81=D0=B0?= =?UTF-8?q?=D0=BD=D0=B8=D0=B9=20WorkerAmiActions:=20reconnect=20=D0=BF?= =?UTF-8?q?=D0=BE=20=D1=82=D1=80=D0=B5=D0=B1=D0=BE=D0=B2=D0=B0=D0=BD=D0=B8?= =?UTF-8?q?=D1=8E,=20=D0=B7=D0=B0=D1=89=D0=B8=D1=82=D0=B0=20=D0=BE=D1=82?= =?UTF-8?q?=20=D0=BF=D0=B0=D0=B4=D0=B5=D0=BD=D0=B8=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - AMI reconnect только при потере соединения (ensureAmiConnected) вместо disconnect+connect на каждый запрос - Белый список разрешённых методов (ALLOWED_API_METHODS) вместо method_exists - try/catch в главном цикле start() с reconnect Beanstalk - try/catch и валидация данных в restAPICallback(), success после проверки ответа AMI - try/catch в onEvents() вокруг выполнения метода - WorkerActiveCalls: прямой вызов GetChannels вместо invokeApi --- bin/WorkerActiveCalls.php | 4 +- bin/WorkerAmiActions.php | 138 ++++++++++++++++++++++++-------------- 2 files changed, 90 insertions(+), 52 deletions(-) diff --git a/bin/WorkerActiveCalls.php b/bin/WorkerActiveCalls.php index f1cf2d1..57dd84c 100644 --- a/bin/WorkerActiveCalls.php +++ b/bin/WorkerActiveCalls.php @@ -169,7 +169,7 @@ private function channelAdditionalControl() } $this->logger->writeInfo('Start channelAdditionalControl...'); try{ - $channelsData = WorkerAmiActions::invokeApi('getChannels', []); + $channelsData = $this->amCustom->GetChannels(); if (!is_array($channelsData) || empty($channelsData)) { // Пустой массив может быть признаком некорректной работы $channelsData // Не обрабатывает такой вариант. @@ -390,7 +390,7 @@ private function printActiveCalls():void 'typeCall' => $this->callType[$linkedid]['type']??'', 'src_chan' => $srcChan, 'src_num' => $callData[$srcChan]['CallerIDNum']??'', - 'exten' => ($callData[$srcChan]['InApp']??false)?$callData[$srcChan]['Exten']??'':'', + 'exten' => $callData[$srcChan]['Exten']??'', 'dst_chan' => '', 'dst_num' => '', 'did' => $this->callType[$linkedid]['did']??'', diff --git a/bin/WorkerAmiActions.php b/bin/WorkerAmiActions.php index 8eb87bc..1731b7f 100644 --- a/bin/WorkerAmiActions.php +++ b/bin/WorkerAmiActions.php @@ -34,6 +34,8 @@ class WorkerAmiActions extends WorkerBase { + private const ALLOWED_API_METHODS = ['restAPICallback', 'getChannels']; + public Logger $logger; public int $countReq = 0; @@ -73,6 +75,19 @@ public function getAstManager(string $events = 'on'):CustomAsteriskManager return $am; } + /** + * Проверяет соединение с AMI, переподключает только при необходимости. + */ + private function ensureAmiConnected(): void + { + if ($this->amCustom->loggedIn()) { + return; + } + $this->logger->writeInfo('AMI reconnecting...'); + $this->amCustom->disconnect(); + $this->amCustom = $this->getAstManager(); + } + /** * Старт работы листнера. * @@ -83,11 +98,20 @@ public function start($argv):void $this->logger = new Logger('AmiActions', 'ModuleMonitorActiveCalls'); $this->logger->writeInfo('Starting...'); $this->amCustom = $this->getAstManager(); + if (!$this->amCustom->loggedIn()) { + $this->logger->writeError('AMI not available at start, will reconnect on first request'); + } $beanstalk = new BeanstalkClient(self::class); $beanstalk->subscribe(self::class, [$this, 'onEvents']); $beanstalk->subscribe($this->makePingTubeName(self::class), [$this, 'pingCallBack']); while ($this->needRestart === false) { - $beanstalk->wait(); + try { + $beanstalk->wait(); + } catch (\Throwable $e) { + $this->logger->writeError('Beanstalk error: ' . $e->getMessage()); + sleep(1); + $beanstalk->reconnect(); + } } $this->amCustom->disconnect(); } @@ -106,17 +130,22 @@ public function onEvents($tube): void } $this->logger->writeInfo($data, 'Events...'); $res_data = ''; - $funcName = $data['function']??''; - if(method_exists($this, $funcName)){ - if(count($data['args']) === 0){ - $res_data = $this->$funcName(); - }else{ - $res_data = $this->$funcName(...$data['args']??[]); + $funcName = $data['function'] ?? ''; + if (in_array($funcName, self::ALLOWED_API_METHODS, true)) { + try { + $args = $data['args'] ?? []; + if (count($args) === 0) { + $res_data = $this->$funcName(); + } else { + $res_data = $this->$funcName(...$args); + } + $this->logger->writeInfo($res_data, 'Result...'); + $res_data = $this->saveResultInTmpFile(serialize($res_data)); + } catch (\Throwable $e) { + $this->logger->writeError("Error in $funcName: " . $e->getMessage()); } - $this->logger->writeInfo($res_data, 'Result...'); - $res_data = $this->saveResultInTmpFile(serialize($res_data)); - }else{ - $this->logger->writeError('method not exists...'); + } else { + $this->logger->writeError("Method not allowed: $funcName"); } $tube->reply($res_data); } @@ -130,55 +159,64 @@ public function onEvents($tube): void */ public function restAPICallback(array $request): PBXApiResult { - $this->amCustom->disconnect(); - $this->amCustom = $this->getAstManager(); - - $res = new PBXApiResult(); + $res = new PBXApiResult(); $res->processor = __METHOD__; - $action = strtolower($request['action']); - $srcEndpoint = WorkerActiveCalls::getEndpointName($request['data']['ch1']); - $dstEndpoint = WorkerActiveCalls::getEndpointName($request['data']['ch2']); + try { + $this->ensureAmiConnected(); + $action = strtolower($request['action'] ?? ''); + $data = $request['data'] ?? []; + $ch1 = $data['ch1'] ?? ''; + $ch2 = $data['ch2'] ?? ''; + $number = $data['number'] ?? ''; - if(is_numeric($srcEndpoint)){ - $actionChannel = $request['data']['ch1']; - }elseif (is_numeric($dstEndpoint)){ - $actionChannel = $request['data']['ch2']; - }else{ - $actionChannel = $request['data']['ch2']; - } + $srcEndpoint = WorkerActiveCalls::getEndpointName($ch1); + $dstEndpoint = WorkerActiveCalls::getEndpointName($ch2); - if('join' === $action) { - $res->success = true; - $variable = "pt1c_cid=SPY-{$request['data']['number']},ALLOW_MULTY_ANSWER=1"; - $channel = "Local/{$request['data']['number']}@internal-originate"; - $this->amCustom->Originate($channel, null, null, null, 'ChanSpy', $actionChannel.',qBS', null, $request['data']['number'], $variable); - SystemMessages::sysLogMsg('SPY-ACTIVE-CHAN', "$action: {$request['data']['number']} to $actionChannel. mode 'qBS'"); - }elseif ('whisper' === $action){ - $res->success = true; - $variable = "pt1c_cid=SPY-{$request['data']['number']},ALLOW_MULTY_ANSWER=1"; - $channel = "Local/{$request['data']['number']}@internal-originate"; - $this->amCustom->Originate($channel, null, null, null, 'ChanSpy', $actionChannel.',qwS', null, $request['data']['number'], $variable); - SystemMessages::sysLogMsg('SPY-ACTIVE-CHAN', "$action: {$request['data']['number']} to $actionChannel. mode 'qw'"); - }elseif ('listen' === $action){ - $res->success = true; - $variable = "pt1c_cid=SPY-{$request['data']['number']},ALLOW_MULTY_ANSWER=1"; - $channel = "Local/{$request['data']['number']}@internal-originate"; - $this->amCustom->Originate($channel, null, null, null, 'ChanSpy', $actionChannel.',qS', null, $request['data']['number'], $variable); - SystemMessages::sysLogMsg('SPY-ACTIVE-CHAN', "$action: {$request['data']['number']} to $actionChannel. mode 'qoS'"); - }elseif ('hangup' === $action){ - $this->amCustom->Hangup($request['data']['ch1']??''); - }else{ - $res->success = false; - $res->messages[] = 'API action not found in moduleRestAPICallback ModuleMonitorActiveCalls '.$action; + if (is_numeric($srcEndpoint)) { + $actionChannel = $ch1; + } elseif (is_numeric($dstEndpoint)) { + $actionChannel = $ch2; + } else { + $actionChannel = $ch2; + } + + if ('join' === $action) { + $variable = "pt1c_cid=SPY-{$number},ALLOW_MULTY_ANSWER=1"; + $channel = "Local/{$number}@internal-originate"; + $amiResult = $this->amCustom->Originate($channel, null, null, null, 'ChanSpy', $actionChannel.',qBS', null, $number, $variable); + $res->success = ($amiResult['Response'] ?? '') === 'Success'; + SystemMessages::sysLogMsg('SPY-ACTIVE-CHAN', "$action: {$number} to $actionChannel. mode 'qBS'"); + } elseif ('whisper' === $action) { + $variable = "pt1c_cid=SPY-{$number},ALLOW_MULTY_ANSWER=1"; + $channel = "Local/{$number}@internal-originate"; + $amiResult = $this->amCustom->Originate($channel, null, null, null, 'ChanSpy', $actionChannel.',qwS', null, $number, $variable); + $res->success = ($amiResult['Response'] ?? '') === 'Success'; + SystemMessages::sysLogMsg('SPY-ACTIVE-CHAN', "$action: {$number} to $actionChannel. mode 'qw'"); + } elseif ('listen' === $action) { + $variable = "pt1c_cid=SPY-{$number},ALLOW_MULTY_ANSWER=1"; + $channel = "Local/{$number}@internal-originate"; + $amiResult = $this->amCustom->Originate($channel, null, null, null, 'ChanSpy', $actionChannel.',qS', null, $number, $variable); + $res->success = ($amiResult['Response'] ?? '') === 'Success'; + SystemMessages::sysLogMsg('SPY-ACTIVE-CHAN', "$action: {$number} to $actionChannel. mode 'qoS'"); + } elseif ('hangup' === $action) { + $this->amCustom->Hangup($ch1); + $res->success = true; + } else { + $res->success = false; + $res->messages[] = 'API action not found in moduleRestAPICallback ModuleMonitorActiveCalls ' . $action; + } + } catch (\Throwable $e) { + $res->success = false; + $res->messages[] = 'restAPICallback error: ' . $e->getMessage(); + $this->logger->writeError('restAPICallback error: ' . $e->getMessage()); } return $res; } public function getChannels() { - $this->amCustom->disconnect(); - $this->amCustom = $this->getAstManager(); + $this->ensureAmiConnected(); return $this->amCustom->getChannels(); } From 3943c9a2e2f3a22afb3d46cc5fe5e3966bd7c96a Mon Sep 17 00:00:00 2001 From: Alexey Portnov Date: Sun, 22 Feb 2026 16:20:55 +0800 Subject: [PATCH 17/19] =?UTF-8?q?=D0=9F=D0=B5=D1=80=D0=B8=D0=BE=D0=B4?= =?UTF-8?q?=D0=B8=D1=87=D0=B5=D1=81=D0=BA=D0=B8=D0=B9=20re-publish=20?= =?UTF-8?q?=D0=B2=20nchan=20=D0=B4=D0=BB=D1=8F=20=D0=BD=D0=BE=D0=B2=D1=8B?= =?UTF-8?q?=D1=85=20=D0=BF=D0=BE=D0=B4=D0=BF=D0=B8=D1=81=D1=87=D0=B8=D0=BA?= =?UTF-8?q?=D0=BE=D0=B2=20=D0=BF=D1=80=D0=B8=20=D0=BE=D1=82=D1=81=D1=83?= =?UTF-8?q?=D1=82=D1=81=D1=82=D0=B2=D0=B8=D0=B8=20=D0=B7=D0=B2=D0=BE=D0=BD?= =?UTF-8?q?=D0=BA=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bin/WorkerActiveCalls.php | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/bin/WorkerActiveCalls.php b/bin/WorkerActiveCalls.php index 57dd84c..107a524 100644 --- a/bin/WorkerActiveCalls.php +++ b/bin/WorkerActiveCalls.php @@ -63,6 +63,11 @@ class WorkerActiveCalls extends WorkerBase private float $lastStateUpdateSent = 0.0; // timestamp последней отправки (ms) private $pendingUserStatesData = null; // накопленные изменения + // Periodic nchan re-publish (для новых подписчиков при отсутствии событий) + private int $lastNchanPublishTime = 0; // timestamp последней публикации в nchan (unix) + private $lastPublishedCallData = null; // последние опубликованные данные active calls + private $lastPublishedStatesData = null; // последние опубликованные данные users states + public const ENDPOINT_TYPE_PEER = '1'; public const ENDPOINT_TYPE_PROVIDER = '2'; public const STATE_IDLE = 'Idle'; @@ -112,6 +117,7 @@ class WorkerActiveCalls extends WorkerBase private const MAX_BRIDGE_ITERATIONS = 200; private const AMI_REQUEST_TIMEOUT = 200000; private const STATE_UPDATE_DELAY = 200; // ms - задержка debounce для WebSocket обновлений + private const NCHAN_REPUBLISH_INTERVAL = 30; // секунды - интервал повторной публикации в nchan для новых подписчиков public const STATE_FILE = '/tmp/MonitorActiveCalls_worker.state'; @@ -296,6 +302,7 @@ public function start($argv):void $this->amCustom->setOnIdleCallback(function () { self::updateStateFile('running'); $this->flushPendingStateUpdate(); // Отправляем накопленные WS обновления, если есть + $this->republishToNchan(); // Переиздаём данные для новых подписчиков }, 1); // Проверка каждую секунду для быстрой отправки WS updates while ($this->needRestart === false) { try { @@ -307,6 +314,7 @@ public function start($argv):void $this->amCustom->setOnIdleCallback(function () { self::updateStateFile('running'); $this->flushPendingStateUpdate(); // Отправляем накопленные WS обновления, если есть + $this->republishToNchan(); // Переиздаём данные для новых подписчиков }, 1); // Проверка каждую секунду для быстрой отправки WS updates } } catch (\Throwable $e) { @@ -496,7 +504,9 @@ private function printActiveCalls():void CacheManager::setCacheData('getActiveChannelsV2Action', $callData, self::CACHE_TTL); if($this->backendExists) { BackendApiController::publishActiveCalls($callData); + $this->lastNchanPublishTime = time(); } + $this->lastPublishedCallData = $callData; } unset($callData); @@ -556,13 +566,38 @@ private function flushPendingStateUpdate(): void CacheManager::setCacheData('getUsersStates', $this->pendingUserStatesData, self::CACHE_TTL); if ($this->backendExists) { BackendApiController::publishUserStates($this->pendingUserStatesData); + $this->lastNchanPublishTime = time(); } + $this->lastPublishedStatesData = $this->pendingUserStatesData; $this->lastStateUpdateSent = $now; $this->pendingUserStatesData = null; } $this->stateUpdateScheduled = 0; } + /** + * Периодически переиздаёт данные в nchan, даже если состояние не изменилось. + * Гарантирует, что новые WebSocket-подписчики получат актуальные данные, + * когда на АТС нет активных звонков и AMI-события не приходят. + */ + private function republishToNchan(): void + { + if (!$this->backendExists) { + return; + } + if (time() - $this->lastNchanPublishTime < self::NCHAN_REPUBLISH_INTERVAL) { + return; + } + $this->lastNchanPublishTime = time(); + + if ($this->lastPublishedCallData !== null) { + BackendApiController::publishActiveCalls($this->lastPublishedCallData); + } + if ($this->lastPublishedStatesData !== null) { + BackendApiController::publishUserStates($this->lastPublishedStatesData); + } + } + /** * Обогащает states информацией о соединённых каналах. * Создаёт копию $this->states, не модифицируя оригинал. From fdc725c68bb5caa8670eef703ffe305a4b34a236 Mon Sep 17 00:00:00 2001 From: Alexey Portnov Date: Tue, 31 Mar 2026 17:26:44 +0800 Subject: [PATCH 18/19] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=B7=D0=B0=D0=B2=D0=B8=D1=81?= =?UTF-8?q?=D0=B0=D0=BD=D0=B8=D1=8F=20=D0=BA=D0=B0=D0=BD=D0=B0=D0=BB=D0=BE?= =?UTF-8?q?=D0=B2=20=D0=BF=D0=BE=D1=81=D0=BB=D0=B5=20=D0=BF=D0=B5=D1=80?= =?UTF-8?q?=D0=B5=D0=B7=D0=B0=D0=B3=D1=80=D1=83=D0=B7=D0=BA=D0=B8=20Asteri?= =?UTF-8?q?sk?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GetChannels() теперь возвращает null при ошибке AMI вместо пустого массива, channelAdditionalControl() различает "нет каналов" от "ошибка связи" и корректно очищает зависшие каналы при валидном пустом ответе. --- Lib/AsteriskManager.php | 7 +++++-- bin/WorkerActiveCalls.php | 8 +++++--- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/Lib/AsteriskManager.php b/Lib/AsteriskManager.php index 4faac09..a1b8bbe 100644 --- a/Lib/AsteriskManager.php +++ b/Lib/AsteriskManager.php @@ -855,11 +855,14 @@ public function ExtensionState(string $exten, string $context, ?string $actionid * Get the channels information. * * @param bool $group Indicates whether to group the channels by Linkedid (optional, default is true). - * @return array The channels information. + * @return array|null The channels information, or null on AMI communication error. */ - public function GetChannels(bool $group = true): array + public function GetChannels(bool $group = true) { $res = $this->sendRequestTimeout('CoreShowChannels'); + if (empty($res)) { + return null; + } $channels = null; if (isset($res['data']['CoreShowChannel'])) { $channels = $res['data']['CoreShowChannel']; diff --git a/bin/WorkerActiveCalls.php b/bin/WorkerActiveCalls.php index 107a524..ac5122d 100644 --- a/bin/WorkerActiveCalls.php +++ b/bin/WorkerActiveCalls.php @@ -176,9 +176,8 @@ private function channelAdditionalControl() $this->logger->writeInfo('Start channelAdditionalControl...'); try{ $channelsData = $this->amCustom->GetChannels(); - if (!is_array($channelsData) || empty($channelsData)) { - // Пустой массив может быть признаком некорректной работы $channelsData - // Не обрабатывает такой вариант. + if ($channelsData === null) { + // AMI communication error — skip cleanup to avoid false positives return; } @@ -960,6 +959,9 @@ private function collectQueuesInfo():void private function collectActiveChannels():void { $channelsData = $this->amCustom->GetChannels(); + if (!is_array($channelsData)) { + return; + } foreach ($channelsData as $linkedId => $channels) { foreach ($channels as $channel) { if(stripos($channel, 'local') !== false) { From 73c44477b51790754c744d383300c8b740de066c Mon Sep 17 00:00:00 2001 From: Alexey Portnov Date: Wed, 15 Apr 2026 21:17:15 +0800 Subject: [PATCH 19/19] =?UTF-8?q?=D0=9E=D0=B1=D0=BE=D0=B1=D1=89=D1=91?= =?UTF-8?q?=D0=BD=D0=BD=D0=B0=D1=8F=20=D0=BC=D0=B8=D0=B3=D1=80=D0=B0=D1=86?= =?UTF-8?q?=D0=B8=D1=8F=20Linkedid=20=D0=BF=D1=80=D0=B8=20BridgeEnter=20(i?= =?UTF-8?q?nterception-bridge)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Asterisk меняет Linkedid канала при входе в bridge в сценариях call pickup *8, interception-bridge (originate+merge) и attended transfer. Существующий механизм отслеживал только *8-pickup по Exten — из-за этого при перехвате через interception-bridge метаданные канала оставались под старым Linkedid, web-UI показывал фантомную строку «сотрудник разговаривает сам с собой» и в логах сыпались ERROR "Undefined array key" из printActiveCalls. Фикс: в обработчике BridgeEnter перед pickup-специфичной логикой детектируем смену Linkedid по наличию канала в activeChannels под другим linkedId и мигрируем метаданные через существующий migrateChannel(). Защита от цикла в linkedIdAliases и фильтр Local-каналов во избежание лишних обходов. Плюс defensive isset/?? в printActiveCalls как страховка от будущих экзотических сценариев. --- bin/WorkerActiveCalls.php | 39 +++++++++++++++++++++++++++++++++------ 1 file changed, 33 insertions(+), 6 deletions(-) diff --git a/bin/WorkerActiveCalls.php b/bin/WorkerActiveCalls.php index ac5122d..3991fa9 100644 --- a/bin/WorkerActiveCalls.php +++ b/bin/WorkerActiveCalls.php @@ -414,10 +414,10 @@ private function printActiveCalls():void $bridgeStart = time(); $chFound = $this->findBridgeChannel($linkedid,$dstChannel, $bridgeStart); - if($chFound){ + if($chFound && isset($callData[$dstChannel])){ // Активный разговор $call['dst_chan'] = $dstChannel; - $call['dst_num'] = $callData[$dstChannel]['CallerIDNum']; + $call['dst_num'] = $callData[$dstChannel]['CallerIDNum'] ?? ''; // Обновляем статус агента очереди $this->updateAgentState($queuesData, $call['dst_num'], self::STATE_UP); @@ -432,19 +432,19 @@ private function printActiveCalls():void $tmpDstChannel = $channel; $tmpBridgeStart = time(); $tmpChFound = $this->findBridgeChannel($linkedid,$tmpDstChannel, $tmpBridgeStart); - if(!$tmpChFound){ + if(!$tmpChFound || !isset($callData[$tmpDstChannel])){ // Идет дозвон. $call['calledChannels'][] = [ 'channel' => $channel, - 'number' => $channelData['CallerIDNum'], + 'number' => $channelData['CallerIDNum'] ?? '', ]; }elseif(!isset($bridgeChannels[$channel])){ // Вероятная переадресация с консультацией. Начальный канал в ожидании. $bridgeChannels[$channel] = true; $bridgeChannels[$tmpDstChannel] = true; - $tmpSrcNum = $channelData['CallerIDNum']; - $tmpDstNum = $callData[$tmpDstChannel]['CallerIDNum']; + $tmpSrcNum = $channelData['CallerIDNum'] ?? ''; + $tmpDstNum = $callData[$tmpDstChannel]['CallerIDNum'] ?? ''; $call['bridgeChannels'][] = [ 'answer' => $tmpBridgeStart, 'src_chan' => $channel, @@ -1378,6 +1378,33 @@ public function callEvents($parameters):void // Add channel to bridge first (required for subsequent lookups) $this->activeBridges[$linkedId][$bridgeUniqueid][$beChannel] = $parameters['Timestamp'] ?? time(); + // Обобщённая миграция linkedId: Asterisk может сменить Linkedid канала при входе в бридж + // (call pickup *8, interception-bridge, attended transfer и пр.). Если канал уже есть в + // activeChannels под другим linkedId — переносим его метаданные сюда, чтобы не создавать + // фантомные записи и не ловить Undefined array key в printActiveCalls. + // Local-каналы не хранятся в activeChannels, для них проверка бессмысленна. + if (stripos($beChannel, 'local/') !== 0 && !isset($this->activeChannels[$linkedId][$beChannel])) { + foreach ($this->activeChannels as $altLinkedId => $altChannels) { + if ($altLinkedId === $linkedId) { + continue; + } + if (isset($altChannels[$beChannel])) { + $this->logger->writeInfo( + "BridgeEnter linkedId change detected: $beChannel migrated from $altLinkedId to $linkedId " . + "(bridge=$bridgeUniqueid, context=" . ($parameters['Context'] ?? '') . ")" + ); + $this->migrateChannel($beChannel, $altLinkedId, $linkedId); + $this->channelLinkedIds[$beChannel] = $linkedId; + // Алиас нужен, чтобы findBridgeChannel мог резолвить исторические bridge'и под старым linkedId. + // Не перезаписываем обратный алиас, иначе получим цикл A→B→A в linkedIdAliases. + if (($this->linkedIdAliases[$linkedId] ?? '') !== $altLinkedId) { + $this->linkedIdAliases[$altLinkedId] = $linkedId; + } + break; + } + } + } + // Handle pickup channel entering bridge if (isset($this->pickupChannels[$beChannel])) { $initialLinkedId = $this->pickupChannels[$beChannel]['initial_linkedid'];