fix(asterisk): update telecom route and recording fallback

This commit is contained in:
Yera All
2026-04-17 12:16:51 +05:00
parent 4adaf192fb
commit feb0ce01e2
5 changed files with 140 additions and 1 deletions
+6
View File
@@ -70,5 +70,11 @@ Default lab SIP accounts from `pjsip.conf`:
- `2001` (operator A)
- `2002` (operator B)
Telecom.kz note:
- `87273901485 / 86205414@sip.telecom.kz` should be configured as a normal SIP account with `type=registration`.
- Do not treat this number as an IP trunk unless Telecom provisions a separate IP-trunk product for it.
- Inbound calls from this account are expected in `from-telecom` and should route to `7100`.
- Keep the real Telecom password out of git and set it on the target host before reload.
Track 14 note:
- `2001` and `2002` are allowed to keep multiple contacts so browser registration can coexist with `MicroSIP/Zoiper` in QA.
+13
View File
@@ -117,3 +117,16 @@ exten => h,1,StopMixMonitor()
[mvpcc-operators]
exten => _X.,1,Dial(PJSIP/${EXTEN},30)
same => n,Hangup()
[from-telecom]
; Incoming calls from Telecom.kz SIP account 86205414 / DID 87273901485.
; Route them directly to the AI-first entrypoint 7100.
exten => 86205414,1,NoOp(Incoming from telecom.kz DID 87273901485 -> AI)
same => n,Set(CALLERID(dnid)=87273901485)
same => n,Goto(from-softphones,7100,1)
exten => 87273901485,1,NoOp(Incoming from telecom.kz DID 87273901485 -> AI)
same => n,Goto(from-softphones,7100,1)
exten => _X.,1,NoOp(Incoming from telecom.kz, DID=${EXTEN})
same => n,Goto(from-softphones,7100,1)
+52
View File
@@ -82,3 +82,55 @@ type=aor
max_contacts=1
remove_existing=yes
support_path=yes
;========================================================================
; Telecom.kz SIP account (REGISTER auth, not IP trunk)
; DID: 87273901485
; SIP account: 86205414@sip.telecom.kz
;========================================================================
[telecom-kz-aor]
type=aor
max_contacts=1
remove_existing=yes
qualify_frequency=60
support_path=yes
[telecom-kz-auth]
type=auth
auth_type=userpass
username=86205414
password=CHANGE_ME_TELECOM_KZ_PASSWORD
[telecom-kz]
type=endpoint
transport=transport-udp
context=from-telecom
disallow=all
allow=alaw,ulaw
aors=telecom-kz-aor
outbound_auth=telecom-kz-auth
from_user=86205414
from_domain=sip.telecom.kz
direct_media=no
rtp_symmetric=yes
force_rport=yes
rewrite_contact=yes
trust_id_inbound=yes
send_rpid=yes
identify_by=username
language=ru
[telecom-kz-registration]
type=registration
transport=transport-udp
outbound_auth=telecom-kz-auth
server_uri=sip:sip.telecom.kz:5060
client_uri=sip:86205414@sip.telecom.kz
contact_user=86205414
retry_interval=60
forbidden_retry_interval=300
expiration=300
line=yes
endpoint=telecom-kz
support_path=yes
auth_rejection_permanent=no
@@ -497,7 +497,7 @@ def upload_recording(
url=f"{bridge._recording_service_url()}/recordings/import-upload",
data=data,
files={"file": (file_name, handle, mime_type or "application/octet-stream")},
retry_reset=handle.seek(0),
retry_reset=lambda: handle.seek(0),
)
return response.json()
except httpx.HTTPStatusError as exc:
+68
View File
@@ -205,6 +205,74 @@ def test_post_json_bearer_first_falls_back_to_legacy(monkeypatch):
assert DummyClient.seen_headers[1]["X-Role"] == "admin"
def test_upload_recording_bearer_first_fallback_rewinds_file(monkeypatch, tmp_path):
recording_path = tmp_path / "call.wav"
recording_bytes = b"recording-payload"
recording_path.write_bytes(recording_bytes)
class DummyClient:
def __init__(self, **kwargs):
self.kwargs = kwargs
def __enter__(self):
return self
def __exit__(self, exc_type, exc, tb):
return False
class DummyResponse:
def raise_for_status(self):
return None
def json(self):
return {"recording_id": "rec_uploaded"}
attempts: list[dict[str, object]] = []
def _fake_request_with_bridge_auth(client, *, retry_reset=None, headers=None, files=None, **kwargs):
del client, headers, kwargs
handle = files["file"][1]
attempts.append(
{
"chunk": handle.read(),
"has_retry_reset": callable(retry_reset),
}
)
assert callable(retry_reset)
retry_reset()
attempts.append(
{
"chunk": handle.read(),
"has_retry_reset": callable(retry_reset),
}
)
return DummyResponse()
monkeypatch.setenv("ASTERISK_BRIDGE_AUTH_MODE", "bearer_first")
monkeypatch.setenv("ASTERISK_BRIDGE_AUTH_FALLBACK_LEGACY", "1")
monkeypatch.setenv("ASTERISK_BRIDGE_AUTH_USER", "ast-bridge")
monkeypatch.setenv("ASTERISK_BRIDGE_AUTH_ROLE", "admin")
monkeypatch.setenv("APP_TOKEN_SECRET", "track9-test-secret")
monkeypatch.setattr(bridge_module.httpx, "Client", DummyClient)
monkeypatch.setattr(bridge_module, "_request_with_bridge_auth", _fake_request_with_bridge_auth)
result = bridge_module._upload_recording(
local_path=recording_path,
call_id="call_upload_fallback",
interaction_id="int_upload_fallback",
source_event_id="src_upload_fallback",
file_name="call.wav",
mime_type="audio/wav",
duration_seconds=3,
)
assert result == {"recording_id": "rec_uploaded"}
assert attempts == [
{"chunk": recording_bytes, "has_retry_reset": True},
{"chunk": recording_bytes, "has_retry_reset": True},
]
def test_assign_interaction_uses_patch(monkeypatch):
class DummyResponse:
status_code = 200