Conversation
added 19 commits
February 3, 2026 22:19
…al-каналов enrichStatesWithConnections теперь проходит цепочку бриджей через findBridgeChannel вместо прямого lookup партнёра, что корректно разрешает номер при переводах через очереди.
… цикл перезапусков воркера - collectActiveChannels: добавлена проверка Uniqueid === Linkedid для корректного определения инициатора звонка при follow-me на мобильный (порядок обхода каналов не гарантирован, исходящий транк мог ошибочно стать src_chan) - Главный цикл: while(true) заменён на while($this->needRestart === false), чтобы воркер корректно завершался по SIGUSR1 без 3-минутного ожидания SIGTERM
Проблема: 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
…редей - Добавлено поле direction (incoming/outgoing) в структуру channels - Очереди теперь включены в общий список states с автоматическим расчётом состояния на основе доступности агентов - Защита состояния очередей от перезаписи событием ExtensionStatus
Устранена проблема "зависших звонков" в веб-интерфейсе. Проблема: При attended transfer, bridge masquerade или call pickup Asterisk может изменить Linkedid канала. Событие Hangup приходит с новым Linkedid, но канал хранится под старым — удаление не происходило. Решение: В обработке Hangup добавлен fallback-поиск канала во всех активных linkedId, если канал не найден в указанном linkedId из события.
Задача #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'а событий
…з idle callback Проблема: - Throttle механизм задерживает WebSocket обновления на 200ms - Проверка "пора ли отправлять" выполнялась только при новых AMI событиях - Если после Hangup событий больше не приходило, обновление зависало - В UI завершённые звонки продолжали отображаться как активные Решение: - Выделена логика отправки в метод flushPendingStateUpdate() - Добавлен вызов этого метода в idle callback (каждую секунду) - Теперь даже без новых событий обновления отправляются через макс. 1 сек Изменения: - bin/WorkerActiveCalls.php: * Новый метод flushPendingStateUpdate() для отправки pending updates * Idle callback интервал уменьшен с 30 до 1 сек для быстрой отправки * Вызов flushPendingStateUpdate() добавлен в idle callback
Проблема: - 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()
Оптимизация: - Socket timeout 300ms согласуется с STATE_UPDATE_DELAY (200ms) - Максимальная задержка WebSocket обновлений: 200-500ms (было 200-1200ms) - Среднее время отклика: ~350ms (было ~600ms) - Idle callback срабатывает ~3-4 раза в секунду (было ~1 раз в секунду) Результат: - UI обновляется в 2 раза быстрее после завершения звонков - Минимальная дополнительная нагрузка на CPU
…ройств Когда на одном номере зарегистрировано несколько устройств (например, PJSIP/233-00001603&PJSIP/233-00001604), и отвечает не первое устройство, первый канал уничтожается Hangup'ом. Теперь модуль автоматически обновляет src_chan на активный канал с тем же endpoint, предотвращая ссылки на несуществующие каналы.
…p *8XXX) 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().
Проблема: При перехвате вызова (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
Критические изменения: - Метод migrateChannel теперь переносит callType, spyerChannels, activeBridges - Добавлена очистка пустых linkedId для предотвращения дублирования звонков - Добавлена проверка $oldLinkedId === $newLinkedId для оптимизации Code cleanup: - Все комментарии переписаны на английский - Убрано избыточное debug логирование в BridgeEnter - Улучшена читаемость логики обработки pickup Результат: перехваченные звонки (*8XXX) теперь корректно отображаются как один звонок с полными данными о pickup-канале.
- Заменён createLoginResponse('1','admin') на createServiceToken() (требует добавления метода в ModuleSoftphoneBackend)
- JS: добавлен refreshAuthToken() через /auth/refresh вместо повторной генерации токенов
- Старые refresh-токены теперь попадают в blacklist на сервере
…а от падений - 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
GetChannels() теперь возвращает null при ошибке AMI вместо пустого массива, channelAdditionalControl() различает "нет каналов" от "ошибка связи" и корректно очищает зависшие каналы при валидном пустом ответе.
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 как страховка от будущих экзотических сценариев.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.