Адаптер событий и записей между Yeastar S-Series и ReMarked CRM.
Быстрый запуск · Архитектура · Эксплуатация · Диагностика · Участие в проекте · Безопасность · Лицензия
ATS Proxy принимает HTTP-события звонка, преобразует их в контракт CRM,
скачивает запись через Yeastar Open API v2 и публикует стабильную ссылку
record_link. Он полезен, когда АТС находится во внутренней сети, а CRM не
поддерживает формат событий Yeastar напрямую.
Note
Основная цепочка проверена на длительно работающем production-развёртывании:
события поступают в CRM, WAV сохраняются локально и отдаются через
/download/<filename>. Push в этот репозиторий не обновляет production
автоматически.
flowchart LR
PBX["Yeastar S-Series"]
Proxy["ATS Proxy"]
CRM["ReMarked CRM"]
Storage[("records/")]
PBX -->|"HTTP events: Invite, CallStatus, NewCdr"| Proxy
Proxy -->|"JSON: invite, answer, hangup, cdr"| CRM
Proxy -->|"login, get_random, download"| PBX
Proxy --> Storage
CRM -->|"GET record_link"| Proxy
Storage --> Proxy
После NewCdr сервис ждёт появления записи, получает одноразовый ключ через
recording/get_random, скачивает WAV через recording/download и только затем
отправляет итоговый CDR со ссылкой на файл. Если файл не появился после всех
попыток, CDR всё равно отправляется, но с пустым record_link.
8088является стандартным HTTPS-портом веб-интерфейса Yeastar, а не отдельным обязательным портом API. Open API использует протокол и порт, указанные вats.url.
Подробная схема: docs/architecture.md.
- события
invite,answer,hangupиcdrдля ReMarked; - связь событий одного звонка по
callid; - нормализация номеров Yeastar;
- загрузка записей с повторными попытками;
- безопасная раздача файлов через
GET /download/<filename>; - автоматическое удаление устаревших записей;
- повторные попытки доставки событий в CRM;
- health-check и просмотр активных звонков;
- маскирование токенов, паролей и одноразовых ключей в debug-логах.
Требования: Docker с Compose либо Python 3.11+.
cp config.example.json config.json
docker compose up -d --build
curl http://127.0.0.1:9001/healthconfig.json содержит секреты и исключён из Git. Каталог records/ также не
попадает в репозиторий.
Основные параметры находятся в config.json:
| Параметр | Назначение |
|---|---|
ats.url |
Внутренний base URL веб-интерфейса и Open API АТС |
ats.api_version |
Версия API, обычно v2.0.0 |
ats.user / ats.password |
Учётные данные API; Yeastar может требовать MD5 пароля |
ats.verify_tls |
Проверка сертификата при HTTPS; включайте с доверенным сертификатом |
ats.download_attempts |
Максимальное число попыток получить запись |
ats.download_retry_delay |
Пауза между попытками в секундах |
network.local_port |
Порт приложения внутри контейнера |
network.heartbeat_ip |
Адрес сервера, на который АТС отправляет HTTP events |
network.heartbeat_port |
Порт сервера, доступный АТС для /webhook |
network.external_domain_or_ip |
Адрес, доступный серверу CRM |
network.external_port |
Внешний порт, добавляемый в record_link |
crm.url / crm.token |
Endpoint и токен ReMarked |
storage.path |
Каталог записей внутри контейнера |
storage.cleanup_days |
Срок хранения записей |
Разделение local_port, heartbeat_port и external_port важно, когда Docker
или маршрутизатор публикует один внутренний порт под несколькими внешними
портами.
Полный пример: config.example.json.
{
"token": "crm-token",
"caller": "998901234567",
"callee": "1000",
"direction": "in",
"event": "cdr",
"start_timestamp": 1767439729,
"state": "answered",
"duration": 120,
"talk_diuration": 115,
"record_link": "http://proxy.example.com:9001/download/recording.wav",
"call_uuid": "1767439729.6402",
"date_start": "2026-01-03 16:17:00",
"wait_duration": 5
}Поле talk_diuration содержит историческую опечатку намеренно: это часть
существующего контракта ReMarked.
| Метод и путь | Назначение |
|---|---|
POST /webhook |
Приём HTTP events Yeastar |
GET /download/<filename> |
Получение сохранённой записи |
GET /health |
Состояние сервиса и подключения к АТС |
GET /calls |
Активные звонки в памяти процесса |
/calls содержит номера телефонов и не должен публиковаться для произвольных
адресов. Внешний доступ к сервису рекомендуется ограничивать firewall-правилами
или reverse proxy.
python -m compileall -q app.py ats_client.py
python -m unittest discover -s tests -vЛокальный запуск без Docker:
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
ATS_PROXY_CONFIG=config.example.json python app.pyДля PowerShell переменная задаётся так:
$env:ATS_PROXY_CONFIG = "config.example.json"
python app.py- Архитектура и границы ответственности
- Развёртывание и безопасная проверка
- Диагностика типовых проблем
- История изменений
- Участие в проекте
- Политика безопасности
Работающий production-контейнер не обновляется автоматически после push в Git.
Новая версия должна выкатываться отдельно, с резервной копией config.json и
каталога записей.
Проект распространяется по лицензии MIT.
