Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Π Π΄Π°Π½Π½ΠΎΠΌ ΡΠ°Π·Π΄Π΅Π»Π΅ ΠΏΡΠΎΠΈΡΡ ΠΎΠ΄ΠΈΡ Π½Π°ΡΡΡΠΎΠΉΠΊΠ° ΡΠ°ΡΠΎΠ² ΠΈ ΠΊΠ°Π»Π΅Π½Π΄Π°ΡΡ
ΠΠΏΠΈΡΠ°Π½ΠΈΠ΅ ΡΠ°Π·Π΄Π΅Π»Π° "Π‘ΠΈΡΡΠ΅ΠΌΠ°" Π² MikoPBX



ΠΠΏΠΈΡΠ°Π½ΠΈΠ΅ Π½Π°ΡΡΡΠΎΠ΅ΠΊ ΠΎΡΠ½ΠΎΠ²Π½ΡΡ ΠΏΠ°ΡΠ°ΠΌΠ΅ΡΡΠΎΠ² ΡΠΈΡΡΠ΅ΠΌΡ
ΠΠ°ΡΡΡΠΎΠΉΠΊΠ° ΠΏΠΎΡΡΡ Π΄Π»Ρ ΡΠ΅ΡΠ²ΠΈΡΠ° Yandex






















































https://192.168.100.60/pbxcore/api/v3/mail-settings/oauth2-callback








ΠΠ°ΡΡΡΠΎΠΉΠΊΠ° ΠΏΠΎΡΡΡ Π΄Π»Ρ ΡΠ΅ΡΠ²ΠΈΡΠ° Outlook (outlook.com; hotmail.com)
ΠΠΏΠΈΡΠ°Π½ΠΈΠ΅ Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎΡΡΠ΅ΠΉ ΡΠ°Π·Π΄Π΅Π»Π° "ΠΠ°ΡΡΠΎΠΌΠΈΠ·Π°ΡΠΈΡ ΡΠΈΡΡΠ΅ΠΌΠ½ΡΡ ΡΠ°ΠΉΠ»ΠΎΠ²"



















https://192.168.100.71/pbxcore/api/v3/mail-settings/oauth2-callback[general](+)
allowtransfer=yes[user2_pingtel]
type=friend
username=user2_pingtel
secret=blah
host=dynamic
qualify=1000 ; Π Π°ΡΡΠΌΠ°ΡΡΠΈΠ²Π°Π΅ΠΌ ΠΊΠ»ΠΈΠ΅Π½ΡΠ° ΠΊΠ°ΠΊ Π½Π΅ΡΠ°Π±ΠΎΡΠ°ΡΡΠ΅Π³ΠΎ,
; Π΅ΡΠ»ΠΈ ΠΎΡΠ²Π΅Ρ ΠΎΡ Π½Π΅Π³ΠΎ ΠΈΠ΄Π΅Ρ Π±ΠΎΠ»Π΅Π΅ 1 ΡΠ΅ΠΊ.
callgroup=1,3-4 ; ΠΠ»ΠΈΠ΅Π½Ρ ΡΠ²Π»ΡΠ΅ΡΡΡ ΡΠ»Π΅Π½ΠΎΠΌ Π³ΡΡΠΏΠΏ Π²ΡΠ·ΠΎΠ²ΠΎΠ²: 1, 3 ΠΈ 4
pickupgroup=1,3-4 ; ΠΡ ΠΌΠΎΠΆΠ΅ΠΌ ΡΠΎΠ²Π΅ΡΡΠ°ΡΡ "pick-up" Π²ΡΠ·ΠΎΠ²ΠΎΠ², Π½Π°ΠΆΠ°ΡΠΈΠ΅ΠΌ *8,
; Π΄Π»Ρ Π²ΡΠ·ΠΎΠ²ΠΎΠ² ΠΈΠ· Π³ΡΡΠΏΠΏ 1, 3 ΠΈ 4
defaultip=192.168.0.60
disallow=all
allow=ulaw
allow=alaw
allow=g729[outgoing-custom]
exten => _X!,1,NoOp(--- hangup - ${CHANNEL} ---)
same => n,returnΠΠ°ΡΡΡΠΎΠΉΠΊΠ° ΠΏΠΎΡΡΡ ΠΈ ΡΠ²Π΅Π΄ΠΎΠΌΠ»Π΅Π½ΠΈΠΉ








ΠΠΎΡΠΎΡΠΊΠΎΠ΅ ΠΎΠΏΠΈΡΠ°Π½ΠΈΠ΅ ARI (Asterisk REST Interface)





# REST API Π·Π°ΠΏΡΠΎΡ ΡΠ΅ΡΠ΅Π· curl
curl -u username:password https://your-mikopbx.com:8089/asterisk/ari/asterisk/infowscat -c "wss://username:password@your-mikopbx.com:8089/asterisk/ari/events?app=hello-world"1,Answer()
n,Stasis(hello-world)
n,Hangup(){
"type": "StasisStart",
"timestamp": "2026-03-25T07:18:27.423+0300",
"args": [],
"channel": {
"id": "mikopbx-1774412307.28",
"name": "Local/10003258@internal-incoming-0000000a;2",
"state": "Up",
"protocol_id": "",
"caller": {
"name": "79257184275",
"number": "79257184275"
},
"connected": {
"name": "",
"number": "252"
},
"accountcode": "",
"dialplan": {
"context": "internal",
"exten": "10003258",
"priority": 2,
"app_name": "Stasis",
"app_data": "hello-world"
},
"creationtime": "2026-03-25T07:18:27.371+0300",
"language": "en-en"
},
"asterisk_id": "82:6a:9e:68:10:11",
"application": "hello-world"
}curl -u username:password -X POST \
"https://your-mikopbx.com:8089/asterisk/ari/channels/mikopbx-1774412307.28/play?media=sound:/storage/usbdisk1/mikopbx/media/custom/miko_hello"{
"id": "a0ee0d43-2af5-4250-a303-43825507a06c",
"media_uri": "sound:/storage/usbdisk1/mikopbx/media/custom/miko_hello",
"target_uri": "channel:mikopbx-1774412307.28",
"language": "en-en",
"state": "playing"
}{
"type": "StasisEnd",
"timestamp": "2026-03-25T07:19:44.982+0300",
"channel": {
"id": "mikopbx-1774412307.28",
"name": "Local/10003258@internal-incoming-0000000a;2",
"state": "Up",
"protocol_id": "",
"caller": {
"name": "79257184275",
"number": "79257184275"
},
"connected": {
"name": "",
"number": "252"
},
"accountcode": "",
"dialplan": {
"context": "internal",
"exten": "10003258",
"priority": 2,
"app_name": "Stasis",
"app_data": "hello-world"
},
"creationtime": "2026-03-25T07:18:27.371+0300",
"language": "en-en"
},
"asterisk_id": "82:6a:9e:68:10:11",
"application": "hello-world"
}pip install requests websocketsimport asyncio
import websockets
import json
import os
from datetime import datetime
ARI_HOST = 'your-mikopbx.com'
ARI_USER = 'ari_user'
ARI_PASS = 'your-ari-password'
peers = {}
STATES = {
'NOT_INUSE': ('π’', 'Π‘Π²ΠΎΠ±ΠΎΠ΄Π΅Π½'),
'BUSY': ('π΄', 'ΠΠ°Π½ΡΡ'),
'UNAVAILABLE': ('β«', 'ΠΠ΅Π΄ΠΎΡΡΡΠΏΠ΅Π½'),
}
def draw():
print('\033[2J\033[H', end='')
now = datetime.now().strftime('%H:%M:%S')
print(f'MikoPBX β ΠΌΠΎΠ½ΠΈΡΠΎΡ ΠΏΡΠΈΡΡΡΡΡΠ²ΠΈΡ [{now}]')
print('β' * 50)
print(f' {"ΠΠΎΠΌΠ΅Ρ":<10} {"ΠΠΌΡ":<20} {"Π‘ΡΠ°ΡΡΡ":<15} {"ΠΠ±Π½ΠΎΠ²Π»ΡΠ½"}')
print('β' * 50)
for number, info in sorted(peers.items()):
icon, label = STATES.get(info['state'], ('β', info['state']))
print(f' {number:<10} {info["name"]:<20} {icon} {label:<12} {info["updated"]}')
print('β' * 50)
print(f' Π‘ΠΎΡΡΡΠ΄Π½ΠΈΠΊΠΎΠ²: {len(peers)}')
async def run():
uri = (
f"wss://{ARI_USER}:{ARI_PASS}@{ARI_HOST}:8089/asterisk/ari/events"
f"?app=auto-receptionist&subscribeAll=true"
)
async with websockets.connect(uri) as ws:
draw()
async for message in ws:
event = json.loads(message)
etype = event.get('type')
if etype == 'DeviceStateChanged':
ds = event.get('device_state', {})
name = ds.get('name', '')
state = ds.get('state', '')
if not name.startswith('PJSIP/'):
continue
number = name.replace('PJSIP/', '')
if number not in peers:
peers[number] = {'name': number, 'state': state, 'updated': 'β'}
peers[number]['state'] = state
peers[number]['updated'] = datetime.now().strftime('%H:%M:%S')
draw()
elif etype == 'PeerStatusChange':
ep = event.get('endpoint', {})
number = ep.get('resource', '')
state = ep.get('state', '')
if not number:
continue
if number not in peers:
peers[number] = {'name': number, 'state': 'unknown', 'updated': 'β'}
if state == 'online':
peers[number]['state'] = 'NOT_INUSE'
elif state == 'offline':
peers[number]['state'] = 'UNAVAILABLE'
peers[number]['updated'] = datetime.now().strftime('%H:%M:%S')
draw()
elif etype == 'ContactStatusChange':
ep = event.get('endpoint', {})
number = ep.get('resource', '')
ci = event.get('contact_info', {})
status = ci.get('contact_status', '')
if not number:
continue
if number not in peers:
peers[number] = {'name': number, 'state': 'unknown', 'updated': 'β'}
if status == 'Reachable':
peers[number]['state'] = 'NOT_INUSE'
elif status in ('Unreachable', 'NonQualified'):
peers[number]['state'] = 'UNAVAILABLE'
peers[number]['updated'] = datetime.now().strftime('%H:%M:%S')
draw()
asyncio.run(run())MikoPBX β ΠΌΠΎΠ½ΠΈΡΠΎΡ ΠΏΡΠΈΡΡΡΡΡΠ²ΠΈΡ [11:34:43]
ββββββββββββββββββββββββββββββββββββββββββββββββββ
ΠΠΎΠΌΠ΅Ρ ΠΠΌΡ Π‘ΡΠ°ΡΡΡ ΠΠ±Π½ΠΎΠ²Π»ΡΠ½
ββββββββββββββββββββββββββββββββββββββββββββββββββ
202 202 π’ Π‘Π²ΠΎΠ±ΠΎΠ΄Π΅Π½ 11:34:25
243 243 β« ΠΠ΅Π΄ΠΎΡΡΡΠΏΠ΅Π½ 11:34:43
252 252 π΄ ΠΠ°Π½ΡΡ 11:34:41
ββββββββββββββββββββββββββββββββββββββββββββββββββ
Π‘ΠΎΡΡΡΠ΄Π½ΠΈΠΊΠΎΠ²: 3ΠΠ½ΡΡΡΡΠΊΡΠΈΡ Ρ ΠΏΡΠΈΠΌΠ΅ΡΠ°ΠΌΠΈ ΠΏΠΎ ΡΠΎΠ·Π΄Π°Π½ΠΈΡ ΠΈ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΈΡ API-ΠΊΠ»ΡΡΠ΅ΠΉ




pip install requestsimport requests
BASE_URL = 'https://your-mikopbx.com/pbxcore/api/v3'
API_KEY = 'Π²Π°Ρ-api-ΠΊΠ»ΡΡ'
HEADERS = {
'Authorization': f'Bearer {API_KEY}',
'Content-Type': 'application/json',
}def create_employee(
number: str,
name: str,
sip_secret: str,
email: str = '',
mobile: str = '',
record_calls: bool = True,
fwd_ringlength: int = 45,
) -> dict:
payload = {
'number': number,
'user_username': name,
'sip_secret': sip_secret,
'sip_enableRecording': record_calls,
'fwd_ringlength': fwd_ringlength,
}
if email: payload['user_email'] = email
if mobile: payload['mobile_number'] = mobile
r = requests.post(f'{BASE_URL}/employees', headers=HEADERS, json=payload)
result = r.json()
if result.get('result'):
print(f" Π‘ΠΎΠ·Π΄Π°Π½: {number} ({name}), id={result['data']['id']}")
else:
print(f" ΠΡΠΈΠ±ΠΊΠ°: {result.get('messages', {}).get('error', [])}")
return result
# ΠΠΈΠ½ΠΈΠΌΠ°Π»ΡΠ½ΡΠΉ ΠΏΡΠΈΠΌΠ΅Ρ (ΡΠΎΠ»ΡΠΊΠΎ ΠΎΠ±ΡΠ·Π°ΡΠ΅Π»ΡΠ½ΡΠ΅ ΠΏΠΎΠ»Ρ)
create_employee(
number='243',
name='ΠΠ²Π°Π½ΠΎΠ² ΠΠ²Π°Π½',
sip_secret='Secure#Pass9201',
)
# ΠΠΎΠ»Π½ΡΠΉ ΠΏΡΠΈΠΌΠ΅Ρ
create_employee(
number='244',
name='ΠΠ΅ΡΡΠΎΠ²Π° ΠΠ½Π½Π°',
sip_secret='Secure#Pass9202',
email='[email protected]',
mobile='79001234567',
record_calls=True,
fwd_ringlength=30,
){
"result": true,
"data": {
"number": "201",
"user_username": "ΠΠ²Π°Π½ΠΎΠ² ΠΠ²Π°Π½",
"sip_secret": "Secure#Pass9201",
"sip_dtmfmode": "auto",
"sip_transport": "udp",
"sip_enableRecording": true,
"sip_networkfilterid": "none",
"fwd_ringlength": 45,
"id": "1",
"extensions_length": 3
},
"messages": {"error": [], "info": [], "warning": []}
} Π‘ΠΎΠ·Π΄Π°Π½: 243 (ΠΠ²Π°Π½ΠΎΠ² ΠΠ²Π°Π½), id=113
Π‘ΠΎΠ·Π΄Π°Π½: 244 (ΠΠ΅ΡΡΠΎΠ²Π° ΠΠ½Π½Π°), id=114
Process finished with exit code 0def list_employees(search: str = '', limit: int = 100, offset: int = 0) -> list:
params = {'limit': limit, 'offset': offset}
if search: params['search'] = search
r = requests.get(f'{BASE_URL}/employees', headers=HEADERS, params=params)
return r.json().get('data', {}).get('data', [])
for emp in list_employees():
print(f" {emp.get('number'):>6} {emp.get('user_username', '')}") 202 Brown Brandon
203 Collins Melanie
201 Smith James
243 ΠΠ²Π°Π½ΠΎΠ² ΠΠ²Π°Π½
244 ΠΠ΅ΡΡΠΎΠ²Π° ΠΠ½Π½Π°
Process finished with exit code 0import time
employees = [
{'number': '251', 'name': 'ΠΠ²Π°Π½ΠΎΠ² ΠΠ²Π°Π½', 'secret': 'Pass#9201'},
{'number': '252', 'name': 'ΠΠ΅ΡΡΠΎΠ²Π° ΠΠ½Π½Π°', 'secret': 'Pass#9202'},
{'number': '253', 'name': 'Π‘ΠΈΠ΄ΠΎΡΠΎΠ² ΠΡΡΡ', 'secret': 'Pass#9203'},
]
created, failed = [], []
for emp in employees:
r = requests.post(
f'{BASE_URL}/employees',
headers=HEADERS,
json={
'number': emp['number'],
'user_username': emp['name'],
'sip_secret': emp['secret'],
}
)
result = r.json()
if result.get('result'):
created.append(emp['number'])
print(f" {emp['number']} {emp['name']}")
else:
failed.append(emp['number'])
print(f" {emp['number']}: {result.get('messages', {}).get('error', [])}")
time.sleep(0.2) # Π½Π΅Π±ΠΎΠ»ΡΡΠ°Ρ ΠΏΠ°ΡΠ·Π° ΠΌΠ΅ΠΆΠ΄Ρ Π·Π°ΠΏΡΠΎΡΠ°ΠΌΠΈ
print(f'Π‘ΠΎΠ·Π΄Π°Π½ΠΎ: {len(created)}, ΠΡΠΈΠ±ΠΎΠΊ: {len(failed)}') 251 ΠΠ²Π°Π½ΠΎΠ² ΠΠ²Π°Π½
252 ΠΠ΅ΡΡΠΎΠ²Π° ΠΠ½Π½Π°
253 Π‘ΠΈΠ΄ΠΎΡΠΎΠ² ΠΡΡΡ
Π‘ΠΎΠ·Π΄Π°Π½ΠΎ: 3, ΠΡΠΈΠ±ΠΎΠΊ: 0
Process finished with exit code 0def create_sip_provider(
description: str,
host: str,
username: str = '',
password: str = '',
registration_type: str = 'outbound',
qualify: bool = True,
) -> dict:
payload = {
'description': description,
'host': host,
}
if username: payload['username'] = username
if password: payload['secret'] = password
if registration_type: payload['registration_type'] = registration_type
if not qualify: payload['qualify'] = qualify
r = requests.post(f'{BASE_URL}/sip-providers', headers=HEADERS, json=payload)
result = r.json()
if result.get('result'):
print(f" ΠΡΠΎΠ²Π°ΠΉΠ΄Π΅Ρ ΡΠΎΠ·Π΄Π°Π½: {description}")
else:
print(f" ΠΡΠΈΠ±ΠΊΠ°: {result.get('messages', {}).get('error', [])}")
return result
create_sip_provider(
description='Zadarma',
host='sip.zadarma.com',
username='316811',
password='mysecretpass',
) ΠΡΠΎΠ²Π°ΠΉΠ΄Π΅Ρ ΡΠΎΠ·Π΄Π°Π½: Zadarma
Process finished with exit code 0def list_providers() -> list:
r = requests.get(f'{BASE_URL}/sip-providers', headers=HEADERS)
return r.json().get('data', [])
for prov in list_providers():
print(f" {prov.get('id'):<20} {prov.get('description', '')} [{prov.get('type', '')}]") SIP-TRUNK-34F7CAFE [SIP]
SIP-TRUNK-7B5977ED [SIP]
Process finished with exit code 0from datetime import datetime, timedelta
def get_cdr(
offset: int = 0,
limit: int = 20,
date_from: str = None,
date_to: str = None,
src_num: str = None,
dst_num: str = None,
disposition: str = None,
) -> list:
params = {'offset': offset, 'limit': min(limit, 100)}
if date_from: params['dateFrom'] = date_from
if date_to: params['dateTo'] = date_to
if src_num: params['src_num'] = src_num
if dst_num: params['dst_num'] = dst_num
if disposition: params['disposition'] = disposition
r = requests.get(f'{BASE_URL}/cdr', headers=HEADERS, params=params)
return r.json().get('data', {}).get('records', [])
now = datetime.now()
then = now - timedelta(days=7)
for row in get_cdr(
date_from=then.strftime('%Y-%m-%dT%H:%M:%S'),
date_to=now.strftime('%Y-%m-%dT%H:%M:%S'),
):
print(
str(row.get('start', ''))[:16],
row.get('src_num', ''), 'β', row.get('dst_num', ''),
row.get('disposition', ''), row.get('totalBillsec', 0), 'Ρ'
)2026-03-17 13:30 252 β 202 ANSWERED 48 Ρ
2026-03-17 13:30 243 β 252 BUSY 0 Ρ
2026-03-17 13:30 243 β 89161111111 CHANUNAVAIL 0 Ρ
2026-03-17 13:29 202 β 243 NOANSWER 0 Ρ
2026-03-17 13:29 202 β 202 ANSWERED 2 Ρ
2026-03-17 13:29 202 β 243 NOANSWER 0 Ρ
2026-03-17 13:29 202 β 10003246 NOANSWER 0 Ρ
2026-03-17 13:28 202 β 243 NOANSWER 0 Ρ
Process finished with exit code 0def cdr_stats(days: int = 1) -> dict:
now = datetime.now()
then = now - timedelta(days=days)
records = get_cdr(
date_from=then.strftime('%Y-%m-%dT%H:%M:%S'),
date_to=now.strftime('%Y-%m-%dT%H:%M:%S'),
limit=100
)
answered = [r for r in records if r.get('disposition') == 'ANSWERED']
missed = [r for r in records if r.get('disposition') in ('NO ANSWER', 'NOANSWER')]
total_dur = sum(r.get('totalBillsec', 0) for r in answered)
return {
'total': len(records),
'answered': len(answered),
'missed': len(missed),
'avg_sec': total_dur // len(answered) if answered else 0,
}
stats = cdr_stats(days=7)
print(f"ΠΠ²ΠΎΠ½ΠΊΠΎΠ² Π·Π° 7 Π΄Π½Π΅ΠΉ: {stats['total']}")
print(f"ΠΡΠ²Π΅ΡΠ΅Π½ΠΎ: {stats['answered']}")
print(f"ΠΡΠΎΠΏΡΡΠ΅Π½ΠΎ: {stats['missed']}")
print(f"Π‘ΡΠ΅Π΄Π½ΡΡ Π΄Π»ΠΈΡ.: {stats['avg_sec']}Ρ")ΠΠ²ΠΎΠ½ΠΊΠΎΠ² Π·Π° 7 Π΄Π½Π΅ΠΉ: 13
ΠΡΠ²Π΅ΡΠ΅Π½ΠΎ: 2
ΠΡΠΎΠΏΡΡΠ΅Π½ΠΎ: 5
Π‘ΡΠ΅Π΄Π½ΡΡ Π΄Π»ΠΈΡ.: 25Ρ
Process finished with exit code 0from datetime import datetime
def show_employees():
r = requests.get(f'{BASE_URL}/sip:getStatuses', headers=HEADERS)
peers = r.json().get('data', {})
for number, info in peers.items():
icon = 'π’' if info.get('status') == 'Available' else 'π΄'
print(f" {icon} {number:>6} {info.get('callerid', '')} [{info.get('status', '')}]")
def show_providers():
r = requests.get(f'{BASE_URL}/sip-providers:getStatuses', headers=HEADERS)
providers = r.json().get('data', {}).get('sip', {})
for prov_id, info in providers.items():
icon = 'π’' if info.get('state') == 'registered' else 'π΄'
print(f" {icon} {info.get('description', prov_id):>20} {info.get('username', '')}@{info.get('host', '')} [{info.get('state', '')}]")
if __name__ == '__main__':
print(f'MikoPBX Monitor [{datetime.now().strftime("%Y-%m-%d %H:%M:%S")}]')
print('\nββ Π‘ΠΎΡΡΡΠ΄Π½ΠΈΠΊΠΈ ββββββββββββββββββββββββββββββ')
show_employees()
print('\nββ ΠΡΠΎΠ²Π°ΠΉΠ΄Π΅ΡΡ βββββββββββββββββββββββββββββββ')
show_providers()MikoPBX Monitor [2026-03-17 16:47:35]
ββ Π‘ΠΎΡΡΡΠ΄Π½ΠΈΠΊΠΈ ββββββββββββββββββββββββββββββ
π΄ 201 Smith James [Unavailable]
π’ 202 Brown Brandon [Available]
π΄ 203 Collins Melanie [Unavailable]
π΄ 243 ΠΠ²Π°Π½ΠΎΠ² ΠΠ²Π°Π½ [Unavailable]
π’ 244 ΠΠ΅ΡΡΠΎΠ²Π° ΠΠ½Π½Π° [Available]
π΄ 251 ΠΠ²Π°Π½ΠΎΠ² ΠΠ²Π°Π½ [Unavailable]
π’ 252 ΠΠ΅ΡΡΠΎΠ²Π° ΠΠ½Π½Π° [Available]
π΄ 253 Π‘ΠΈΠ΄ΠΎΡΠΎΠ² ΠeΡΡ [Unavailable]
ββ ΠΡΠΎΠ²Π°ΠΉΠ΄Π΅ΡΡ βββββββββββββββββββββββββββββββ
π΄ Demo provider [email protected] [rejected]
π’ Zadarma [email protected] [registered]
Process finished with exit code 0def get_active_calls() -> list:
r = requests.get(f'{BASE_URL}/pbx-status:getActiveCalls', headers=HEADERS)
return r.json().get('data', [])
calls = get_active_calls()
print(f'ΠΠΊΡΠΈΠ²Π½ΡΡ
Π·Π²ΠΎΠ½ΠΊΠΎΠ²: {len(calls)}')
for call in calls:
print(f" {call.get('src_num', '?')} β {call.get('dst_num', '?')} [{call.get('src_name', '')} β {call.get('dst_name', '')}]")ΠΠΊΡΠΈΠ²Π½ΡΡ
Π·Π²ΠΎΠ½ΠΊΠΎΠ²: 1
243 β 252 [ΠΠ²Π°Π½ΠΎΠ² ΠΠ²Π°Π½ β ΠΠ΅ΡΡΠΎΠ²Π° ΠΠ½Π½Π°]
Process finished with exit code 0ΠΠΏΠΈΡΠ°Π½ΠΈΠ΅ Π΄ΠΎΠΊΡΠΌΠ΅Π½ΡΠ°ΡΠΈΠΈ ΠΈ ΡΠ°Π±Π»ΠΈΡΡ ΡΠ½Π΄ΠΏΠΎΠΈΠ½ΡΠΎΠ² Π΄Π»Ρ ΡΠ°Π±ΠΎΡΡ Ρ REST API Π² MikoPBX






