User Tools

Site Tools


язык_программирования_python

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
язык_программирования_python [2025/05/24 17:58]
val [Zabbix API приложение]
язык_программирования_python [2026/09/06 13:05] (current)
val [ari_chat.py]
Line 1: Line 1:
 ====== Язык программирования Python ====== ====== Язык программирования Python ======
  
 +  * [[Все что должен знать DevOps про Python]]
 +
 +  * [[https://​habr.com/​ru/​articles/​277679/​|Пишем shell скрипты на Python и можно ли заменить им Bash]]
 +
 +  * [[https://​github.com/​rupertsworld/​frontend-backend|Basic Web Frontend/​Backend Demo]]
 +  * [[https://​code.tutsplus.com/​ru/​using-celery-with-django-for-background-task-processing--cms-28732t|Использование Celery с Django для обработки фоновых задач]]
 ===== Виртуальная среда Python ===== ===== Виртуальная среда Python =====
   * [[https://​blog.sedicomm.com/​2021/​06/​29/​chto-takoe-venv-i-virtualenv-v-python-i-kak-ih-ispolzovat/​|Что такое venv и virtualenv в Python, и как их использовать]]   * [[https://​blog.sedicomm.com/​2021/​06/​29/​chto-takoe-venv-i-virtualenv-v-python-i-kak-ih-ispolzovat/​|Что такое venv и virtualenv в Python, и как их использовать]]
-  * [[https://​packaging.python.org/​en/​latest/​guides/​installing-using-pip-and-virtual-environments/​|https://​packaging.python.org/en/latest/guides/installing-using-pip-and-virtual-environments/​]] +  * [[https://​packaging.python.org/​en/​latest/​guides/​installing-using-pip-and-virtual-environments/​|Install packages in a virtual environment using pip and venv]] 
-  * [[https://www.workroom-productions.com/serving-a-directory-with-flask/|Publishing a Directory with Flask]]+ 
 +  * [[https://habr.com/ru/articles/593529/|Poetry — прекрасная альтернатива ​pip (шпаргалка)]] 
 +  * [[https://habr.com/ru/​articles/​828016/|UV. Обзор пакетного менеджера Python]] 
 <​code>​ <​code>​
 +# python3 -V
  
 # apt install python3-venv # apt install python3-venv
Line 19: Line 29:
 </​code>​ </​code>​
  
 +
 +===== Интерактивная оболочка REPL (Read-Eval-Print Loop) =====
 +
 +<​code>​
 +(venv1) # python
 +</​code><​code>​
 +>>>​ import os
 +>>>​ print ("​Hello " + os.environ.get('​USER'​) + "​!"​)
 +</​code>​
 +
 +  * [[https://​ru.wikipedia.org/​wiki/​IPython]]
 +
 +<​code>​
 +(venv1) # pip install ipython
 +</​code>​
 +
 +  * [[https://​jupyter.org/​try-jupyter/​notebooks/?​path=notebooks/​Intro.ipynb|try-jupyter/​notebooks]]
 +  * [[https://​matplotlib.org/​stable/​tutorials/​pyplot.html|Pyplot tutorial]]
 ===== Web приложение ===== ===== Web приложение =====
 +
 +  * [[https://​webadventures.ru/​sravnenie-wsgi-serverov-uvicorn-i-gunicorn/​|Сравнение WSGI-серверов:​uvicorn и Gunicorn]]
 +
 +==== Flask Gunicorn ====
 +
 +  * [[#​Виртуальная среда Python]]
 +
 +  * [[https://​habr.com/​ru/​articles/​750312/​|Python декораторы на максималках. Универсальный рецепт по написанию и аннотированию от мала до велика]]
 +  * [[https://​www.workroom-productions.com/​serving-a-directory-with-flask/​|Publishing a Directory with Flask]]
  
   * [[https://​python.ivan-shamaev.ru/​run-install-deploy-flask-web-app-docker-dockerfile-compose/​|Создание Web-приложения Flask и деплой с помощью Docker Compose & Dockerfile]]   * [[https://​python.ivan-shamaev.ru/​run-install-deploy-flask-web-app-docker-dockerfile-compose/​|Создание Web-приложения Flask и деплой с помощью Docker Compose & Dockerfile]]
   * [[https://​blog.miguelgrinberg.com/​post/​running-your-flask-application-over-https|Running Your Flask Application Over HTTPS]]   * [[https://​blog.miguelgrinberg.com/​post/​running-your-flask-application-over-https|Running Your Flask Application Over HTTPS]]
   * [[https://​docs.python.org/​3/​library/​configparser.html|configparser — Configuration file parser]]   * [[https://​docs.python.org/​3/​library/​configparser.html|configparser — Configuration file parser]]
- 
-  * [[#​Виртуальная среда Python]] 
  
 <​code>​ <​code>​
Line 37: Line 72:
 import os import os
 import configparser import configparser
 +
 +config = configparser.ConfigParser()
 +config.read('/​etc/​pywebd/​pywebd.conf'​)
 +
 +if '​PYWEBD_DOC_ROOT'​ in os.environ:
 +  pywebd_doc_root = os.environ.get('​PYWEBD_DOC_ROOT'​)
 +else:
 +  pywebd_doc_root = config['​default'​]['​DocumentRoot'​]
  
 app = Flask(__name__) app = Flask(__name__)
Line 50: Line 93:
  
 if __name__ == "​__main__":​ if __name__ == "​__main__":​
-    config = configparser.ConfigParser() 
-    config.read('/​etc/​pywebd/​pywebd.conf'​) 
-#    pywebd_port = os.environ.get('​PYWEBD_PORT',​config['​default'​]['​Listen'​]) 
-#    pywebd_doc_root = os.environ.get('​PYWEBD_DOC_ROOT',​config['​default'​]['​DocumentRoot'​]) 
-    if '​PYWEBD_PORT'​ in os.environ: 
-      pywebd_port = os.environ.get('​PYWEBD_PORT'​) 
-    else: 
-      pywebd_port = config['​default'​]['​Listen'​] 
-    if '​PYWEBD_DOC_ROOT'​ in os.environ: 
-      pywebd_doc_root = os.environ.get('​PYWEBD_DOC_ROOT'​) 
-    else: 
-       ​pywebd_doc_root = config['​default'​]['​DocumentRoot'​] 
  
-    app.run(host="0.0.0.0", port=pywebd_port,​ debug=True+  if '​PYWEBD_PORT'​ in os.environ: pywebd_port ​os.environ.get('​PYWEBD_PORT'​
-#    app.run(ssl_context=('/​etc/​pywebd/​pywebd.crt''/​etc/​pywebd/​pywebd.key'), debug=True, host='​0.0.0.0',​ port=pywebd_port)+  else: pywebd_port ​config['default']['Listen']
  
 +  app.run(host="​0.0.0.0",​ port=pywebd_port,​ debug=True)
 +#  app.run(ssl_context=('/​etc/​pywebd/​pywebd.crt',​ '/​etc/​pywebd/​pywebd.key'​),​ debug=True, host='​0.0.0.0',​ port=pywebd_port)
 </​code><​code>​ </​code><​code>​
 # mkdir -p /​etc/​pywebd/​ # mkdir -p /​etc/​pywebd/​
Line 84: Line 117:
 (venv1) :~/pywebd$ #export PYWEBD_PORT=4443 (venv1) :~/pywebd$ #export PYWEBD_PORT=4443
 (venv1) :~/pywebd$ #export PYWEBD_DOC_ROOT=/​var/​www/​html/​ (venv1) :~/pywebd$ #export PYWEBD_DOC_ROOT=/​var/​www/​html/​
-(venv1) :~/pywebd$ #​PYWEBD_PORT=4443 PYWEBD_DOC_ROOT=/​var/​www/html/ python app.py+ 
 +(venv1) :~/pywebd$ #​PYWEBD_PORT=4080 PYWEBD_DOC_ROOT=/​var/​www/​ python app.py
  
 (venv1) :~/pywebd$ python app.py (venv1) :~/pywebd$ python app.py
  
 (venv1) :~/pywebd$ pip freeze | tee requirements.txt (venv1) :~/pywebd$ pip freeze | tee requirements.txt
 +
 +(venv1) :~/pywebd$ #pip install gunicorn
 +(venv1) :~/pywebd$ #gunicorn app:app --bind 0.0.0.0:​8000
 +(venv1) :~/pywebd$ #cat gunicorn.conf.py
 +</​code><​code>​
 +import os
 +import configparser
 +
 +myconfig = configparser.ConfigParser()
 +myconfig.read('/​etc/​pywebd/​pywebd.conf'​)
 +
 +if '​PYWEBD_PORT'​ in os.environ: pywebd_port = os.environ.get('​PYWEBD_PORT'​)
 +else: pywebd_port = myconfig['​default'​]['​Listen'​]
 +
 +#certfile = "/​etc/​pywebd/​pywebd.crt"​
 +#keyfile = "/​etc/​pywebd/​pywebd.key"​
 +
 +bind = '​0.0.0.0:'​ + pywebd_port
 +</​code><​code>​
 +(venv1) :~/pywebd$ # gunicorn -c gunicorn.conf.py app:app
 </​code>​ </​code>​
  
-===== CRUD Rest API приложение ===== 
  
 +==== FastAPI Uvicorn ====
  
-  * [[https://dev.to/francescoxx/python-crud-rest-api-using-flask-sqlalchemy-postgres-docker-docker-compose-3kh4|Python CRUD Rest API using Flask, SQLAlchemy, Postgres, Docker, Docker Compose]]+  * [[https://python-poetry.org/docs/basic-usage/]] 
 +  * [[https://​habr.com/​ru/​articles/​593529/​|Poetry — прекрасная альтернатива pip (шпаргалка)]]
  
 +<​code>​
 +server# ###docker run -ti --rm --name pywebd2 python:​3.11-alpine sh
 +
 +/ # ### pip install poetry
 +
 +server# apt install python3-poetry;​ export PYTHON_KEYRING_BACKEND=keyring.backends.null.Keyring #​https://​stackoverflow.com/​questions/​74438817/​poetry-failed-to-unlock-the-collection
 +
 +/ # poetry new pywebd2
 +
 +/ # cd pywebd2
 +
 +/pywebd2 # poetry add fastapi dotenv uvicorn ###​gunicorn ​
 +
 +/pywebd2 # more pyproject.toml
 +
 +/pywebd2 # cat app.py
 +</​code><​code>​
 +from fastapi import FastAPI
 +from dotenv import load_dotenv
 +import os
 +
 +CONST_VER = "​ver1.1"​
 +hostname = os.uname().nodename
 +
 +load_dotenv()
 +pywebd_message = os.environ.get('​PYWEBD_MESSAGE'​)
 +
 +app = FastAPI()
 +@app.get("/"​)
 +async def home():
 +   ​return {"​data":​ f"​{pywebd_message} from {hostname} app {CONST_VER}"​}
 +
 +if __name__ == "​__main__":​
 +    import uvicorn
 +    uvicorn.run(app,​ host="​0.0.0.0",​ port=8000)
 +</​code><​code>​
 +/pywebd2 # cat .env
 +</​code><​code>​
 +PYWEBD_MESSAGE="​Hello World!!!"​
 +</​code>​
 +
 +  * [[https://​tproger.ru/​articles/​testiruem-na-python-unittest-i-pytest-instrukcija-dlja-nachinajushhih|Тестируем на Python: unittest и pytest. Инструкция для начинающих]]
 +
 +  * [[https://​habr.com/​ru/​articles/​349860/​|Регулярные выражения в Python от простого к сложному. Подробности,​ примеры,​ картинки,​ упражнения]]
 +  * [[https://​regex101.com/​|Online Regular expression tester with syntax highlighting]]
 +
 +<​code>​
 +/pywebd2 # cat tests/​test_ver_format.py
 +</​code><​code>​
 +import app, re
 +
 +assert re.match(r'​^ver\d+\.\d+$',​ app.CONST_VER),"​Incorrect VER format"​
 +</​code><​code>​
 +/pywebd2 # poetry run python3 -m unittest
 +
 +/pywebd2 # #export PYWEBD_MESSAGE="​Another message"​
 +
 +/pywebd2 # poetry run python3 app.py
 +
 +/pywebd2 # poetry run uvicorn app:app --reload --host 0.0.0.0 --port 4080
 +</​code>​
 +  * [[https://​github.com/​benoitc/​gunicorn/​issues/​2154|TypeError:​ __call__() missing 1 required positional argument: '​send'​]]
 +<​code>​
 +/pywebd2 # ###poetry run gunicorn app:app --bind 0.0.0.0:​6080 -k uvicorn.workers.UvicornWorker
 +</​code>​
 +
 +===== Zabbix LLD приложение =====
 +
 +  * [[https://​askubuntu.com/​questions/​244378/​running-python-file-in-terminal|Running Python File in Terminal]]
 +
 +  * [[https://​pythonworld.ru/​moduli/​modul-subprocess.html|Модуль subprocess]]
 +
 +  * [[https://​habr.com/​ru/​articles/​488112/​|Понимание итераторов в Python]]
 +  * [[https://​www.geeksforgeeks.org/​python-find-dictionary-matching-value-in-list/​|Find dictionary matching value in list - Python]]
 +  * [[https://​docs-python.ru/​tutorial/​vstroennye-funktsii-interpretatora-python/​funktsija-next/​|Функция next() в Python, следующий элемент итератора]]
 +
 +==== Пример 1 ====
 +<​code>​
 +gate# apt install python3-xmltodict/​stable
 +
 +gate# cat /​etc/​zabbix/​dhcp-pools.py
 +</​code><​code>​
 +#​!/​usr/​bin/​env python3
 +
 +# Example usage:
 +# ./​dhcp-pools.py
 +# ./​dhcp-pools.py LAN1 defined|used
 +
 +from sys import argv
 +import subprocess, xmltodict, json
 +
 +p = subprocess.Popen("/​usr/​bin/​dhcpd-pools -f x", stdout=subprocess.PIPE,​ shell=True)
 +(output, err) = p.communicate()
 +p_status = p.wait()
 +
 +o = xmltodict.parse(output)
 +#print (o)
 +
 +if len(argv)==1:​
 +  r=[]
 +  for i in o['​dhcpstatus'​]['​shared-network'​]:​
 +    s={}
 +    s["​{#​POOLNAME}"​]=i['​location'​]
 +    r.insert(1,​s)
 +  print(json.dumps(r))
 +else:
 +  LANNAME=argv[1]
 +  USDEF=argv[2]
 +  res = next((sub for sub in o['​dhcpstatus'​]['​shared-network'​] if sub['​location'​] == LANNAME), None)
 +  print(res[USDEF])
 +</​code>​
 +
 +==== Пример 2 ====
 +<​code>​
 +# cat /​usr/​local/​bin/​asterisk.chansip.discovery.py
 +</​code><​code>​
 +#​!/​usr/​bin/​env python3
 +
 +import subprocess, io, json
 +
 +result=[]
 +
 +proc = subprocess.Popen('/​usr/​sbin/​asterisk -x "sip show users" | tail -n +2 | cut -d" " -f1', stdout=subprocess.PIPE,​ shell=True)
 +for line in io.TextIOWrapper(proc.stdout,​ encoding="​utf-8"​):​
 +  #​print(line.rstrip())
 +  s={}
 +  s["​{#​CHANNAME}"​]="​SIP/"​+line.rstrip()
 +  result.insert(1,​s)
 +print(json.dumps(result))
 +</​code>​
 +
 +Вариант от DeepSeek
 +
 +<​code>​
 +# cat /​usr/​local/​bin/​asterisk.chansip.discovery.ds.py
 +</​code><​code>​
 +#​!/​usr/​bin/​env python3
 +
 +import subprocess
 +import json
 +
 +# Запускаем команду и сразу получаем вывод
 +output = subprocess.check_output(
 +    '/​usr/​sbin/​asterisk -x "sip show users" | tail -n +2 | cut -d" " -f1',
 +    shell=True,
 +    text=True
 +)
 +
 +# Формируем список словарей в одну строку
 +result = [{"​{#​CHANNAME}":​ f"​SIP/​{line.strip()}"​} for line in output.splitlines() if line.strip()]
 +
 +# Выводим результат в формате JSON
 +print(json.dumps(result))
 +</​code>​
 ===== Zabbix API приложение ===== ===== Zabbix API приложение =====
  
 +  * [[https://​www.zabbix.com/​documentation/​current/​en/​manual/​api]]
   * [[https://​sbcode.net/​zabbix/​zabbix-api-python-example/​]]   * [[https://​sbcode.net/​zabbix/​zabbix-api-python-example/​]]
 +  * [[https://​forum.checkmk.com/​t/​rest-api-python-question-how-to-use-variables-in-json-post-in-key-and-value/​34652]]
   ​   ​
 <​code>​ <​code>​
 (venv1) server:~# pip install requests (venv1) server:~# pip install requests
  
-(venv1) server:~# cat zab_get_problem.py+(venv1) server:~# cat zab_set_map_name.py
 </​code><​code>​ </​code><​code>​
 +#​!/​usr/​bin/​env python3
 +
 import requests import requests
 import json import json
 +from sys import argv
 +
 +if len(argv) != 3 :
 +    print("​You must set argument!!!\nExample:​ python zab_set_map_name.py 2 \"ISP 1\""​)
 +    quit()
 +
 +MAPID = argv[1]
 +MAPNAME = argv[2]
  
 ZABBIX_API_URL = "​http://​127.0.0.1/​zabbix/​api_jsonrpc.php"​ ZABBIX_API_URL = "​http://​127.0.0.1/​zabbix/​api_jsonrpc.php"​
Line 112: Line 333:
 PWORD = "​zabbix"​ PWORD = "​zabbix"​
  
 +print("​\nLogin user {} to Zabbiz API"​.format(UNAME))
 r = requests.post(ZABBIX_API_URL,​ r = requests.post(ZABBIX_API_URL,​
                   json={                   json={
Line 126: Line 348:
 AUTHTOKEN = r.json()["​result"​] AUTHTOKEN = r.json()["​result"​]
  
-print(AUTHTOKEN)+#print("​Rename Map with ID", MAPID, "​to",​ MAPNAME) 
 +print(f"​Rename Map with ID {MAPID} to {MAPNAME}"​)
  
-# Retrieve a list of problems 
-print("​\nRetrieve a list of problems"​) 
 r = requests.post(ZABBIX_API_URL,​ r = requests.post(ZABBIX_API_URL,​
                   headers={'​Authorization':​ '​Bearer ' + AUTHTOKEN},                   headers={'​Authorization':​ '​Bearer ' + AUTHTOKEN},
                   json={                   json={
-                      ​"​jsonrpc":​ "​2.0",​ +                    ​"​jsonrpc":​ "​2.0",​ 
-                      "​method":​ "problem.get", +                    "​method":​ "map.update", 
-                      "​params":​ {}, +                    "​params":​ { 
-                      "​id":​ 2,+                        "​sysmapid":​ MAPID, 
 +                        "​name":​ MAPNAME 
 +                    ​}, 
 +                    "​id":​ 2
                   })                   })
  
 print(json.dumps(r.json(),​ indent=4, sort_keys=True)) print(json.dumps(r.json(),​ indent=4, sort_keys=True))
  
-#Logout user 
 print("​\nLogout user") print("​\nLogout user")
 r = requests.post(ZABBIX_API_URL,​ r = requests.post(ZABBIX_API_URL,​
Line 154: Line 377:
 print(json.dumps(r.json(),​ indent=4, sort_keys=True)) print(json.dumps(r.json(),​ indent=4, sort_keys=True))
 </​code><​code>​ </​code><​code>​
-(venv1) server:​~# ​python3 zab_get_problem.py+(venv1) server:​~# ​chmod +x zab_set_map_name.py 
 + 
 +(venv1) server:~# ./​zab_set_map_name.py 2 "ISP 1"
 </​code>​ </​code>​
 +
 +
 +===== CRUD Rest API приложение =====
 +
 +  * [[https://​dev.to/​francescoxx/​python-crud-rest-api-using-flask-sqlalchemy-postgres-docker-docker-compose-3kh4|Python CRUD Rest API using Flask, SQLAlchemy, Postgres, Docker, Docker Compose]]
 +
 ===== Дополнительные материалы ===== ===== Дополнительные материалы =====
 +
 +  * [[https://​pypi.org/​project/​ansible-output-parser/​]]
 +  * [[https://​www.cyberciti.biz/​faq/​python-run-external-command-and-get-output/​]]
 +  * [[https://​selectel.ru/​blog/​tutorials/​how-to-develop-fastapi-application/​]]
  
 ==== Доступ к каталогу по http ==== ==== Доступ к каталогу по http ====
Line 166: Line 401:
 </​code>​ </​code>​
  
 +==== wav_and_ogg.py ====
 +
 +  * [[Перекодировка звука#​Перекодировка в ogg формат]]
 +
 +<​code>​
 +$ pip install pydub pip #​audioop-lts
 +</​code><​code>​
 +import sys
 +from pydub import AudioSegment
 +
 +def wav_to_ogg(file_path_wo_ext):​
 +    audio = AudioSegment.from_wav(f"​{file_path_wo_ext}.wav"​)
 +    audio.export(f"​{file_path_wo_ext}.ogg",​ format="​ogg",​ codec="​libopus",​ bitrate="​24k"​)
 +    print(f"​Файл успешно конвертирован и сохранен как: {file_path_wo_ext}.ogg"​)
 +
 +def ogg_to_wav(file_path_wo_ext):​
 +    audio = AudioSegment.from_ogg(f"​{file_path_wo_ext}.ogg"​)
 +    audio = audio.set_channels(1)
 +    audio = audio.set_frame_rate(8000)
 +    audio = audio.set_sample_width(2)
 +    audio.export(f"​{file_path_wo_ext}.wav",​ format="​wav"​)
 +    print(f"​Файл успешно конвертирован и сохранен как: {file_path_wo_ext}.wav"​)
 +
 +if __name__ == "​__main__":​
 +    target_file = sys.argv[1]
 +
 +#    wav_to_ogg(target_file)
 +#    ogg_to_wav(target_file)
 +</​code><​code>​
 +$ python wav_and_ogg.py speech
 +</​code>​
 +
 +==== speech_and_text.py ====
 +
 +  * [[Yandex AI]]
 +
 +<​code>​
 +import os
 +import sys
 +import requests
 +
 +API_KEY = os.getenv("​YANDEX_API_KEY"​)
 +FOLDER_ID = os.getenv("​YANDEX_FOLDER_ID"​)
 +
 +def speech_to_text(file_path):​
 +
 +    url = f"​https://​stt.api.cloud.yandex.net/​speech/​v1/​stt:​recognize?​folderId={FOLDER_ID}"​
 +    headers = {
 +        "​Authorization":​ f"​Api-Key {API_KEY}",​
 +        "​Content-Type":​ "​audio/​ogg;​ codecs=opus",​
 +    }
 +    params = {"​lang":​ "​ru-RU"​}
 +
 +    with open(file_path,​ "​rb"​) as f:
 +        audio_data = f.read()
 +
 +    response = requests.post(
 +       url, headers=headers,​ params=params,​ data=audio_data,​ timeout=15
 +    )
 +
 +    result_json = response.json()
 +    text = result_json.get("​result",​ ""​)
 +    return text
 +
 +def text_to_speech(text,​ output_file_path):​
 +    url = "​https://​tts.api.cloud.yandex.net/​speech/​v1/​tts:​synthesize"​
 +    headers = {
 +        "​Authorization":​ f"​Api-Key {API_KEY}"​
 +    }
 +    data = {
 +        "​text":​ text,
 +        "​lang":​ "​ru-RU",​
 +        "​voice":​ "​marina",​
 +        "​format":​ "​oggopus",​
 +        "​folderId":​ FOLDER_ID
 +    }
 +
 +    response = requests.post(url,​ headers=headers,​ data=data, timeout=15)
 +
 +    if response.status_code == 200:
 +        with open(output_file_path,​ "​wb"​) as f:
 +            f.write(response.content)
 +        print(f"​Файл успешно сохранен:​ {output_file_path}"​)
 +    else:
 +        print(f"​Ошибка API: {response.status_code} - {response.text}"​)
 +
 +if __name__ == "​__main__":​
 +
 +    file_name = sys.argv[1]
 +
 +#    text = speech_to_text(file_name)
 +#    print(text)
 +
 +#    text = sys.argv[2]
 +#    text_to_speech(text,​ file_name)
 +</​code>​
 +
 +==== request_to_agent.py ====
 +<​code>​
 +import sys
 +import os
 +from yandex_ai_studio_sdk import AIStudio
 +
 +def request_to_agent(user_request,​ system_prompt,​ context_messages=None):​
 +
 +    sdk = AIStudio(
 +        folder_id=os.getenv("​YANDEX_FOLDER_ID"​),​
 +        auth=os.getenv("​YANDEX_API_KEY"​)
 +    )
 +    messages = [{"​role":​ "​system",​ "​text":​ system_prompt}]
 +
 +    if context_messages:​
 +        messages.extend(context_messages)
 +
 +    messages.append({"​role":​ "​user",​ "​text":​ user_request})
 +
 +    #model = sdk.chat.completions("​yandexgpt"​)
 +    model = sdk.chat.completions("​aliceai-llm"​)
 +    model = model.configure(temperature=0.3,​ max_tokens=1500)
 +
 +    response = model.run(messages)
 +    text = response.choices[0].text
 +    return text
 +
 +if __name__ == "​__main__":​
 +
 +    system_prompt = (
 +        "Ты — ассистент.\n"​
 +        "​Отвечай кратко.\n"​
 +    )
 +
 +    chat_context = []
 +    #​chat_context = [
 +    #    {"​role":​ "​user",​ "​text":​ "​Привет! Меня зовут Алексей,​ я программист."​},​
 +    #    {"​role":​ "​assistant",​ "​text":​ "​Привет,​ Алексей! Рад знакомству. Чем могу помочь?"​}
 +    #]
 +
 +    while True:
 +        try:
 +            user_request = input("​Введите ваш вопрос:​ ")
 +        except (KeyboardInterrupt,​ EOFError):
 +            print("​\nСессия завершена!"​)
 +            sys.exit(0)
 +
 +        reply_text = request_to_agent(user_request,​ system_prompt,​ chat_context)
 +        print(reply_text)
 +
 +        chat_context.append({"​role":​ "​user",​ "​text":​ user_request})
 +        chat_context.append({"​role":​ "​assistant",​ "​text":​ reply_text})
 +</​code>​
 +
 +==== ari_rec.py ====
 +<​code>​
 +root# 
 +mkdir -p /​var/​spool/​asterisk/​recording/​
 +chown -R asterisk:​asterisk /​var/​spool/​asterisk/​recording/​
 +chmod -R 775 /​var/​spool/​asterisk/​recording/​
 +
 +$ pip install websocket-client requests
 +</​code><​code>​
 +import json
 +import requests
 +import websocket
 +
 +ARI_USER = "​asterisk"​
 +ARI_PASS = "​asterisk"​
 +BASE_URL = "​http://​localhost:​8088/​ari"​
 +AUTH = (ARI_USER, ARI_PASS)
 +
 +def detect_speech(channel_id):​
 +    print(f"​Запускаем TALK_DETECT для канала {channel_id}..."​)
 +    talk_url = f"​{BASE_URL}/​channels/​{channel_id}/​variable"​
 +
 +    # 2000 мс тишины для завершения фразы, 500 мс речи для начала
 +    talk_payload = {
 +        "​variable":​ "​TALK_DETECT(set)",​
 +        "​value":​ "​2000,​500"​
 +    }
 +
 +    response = requests.post(talk_url,​ json=talk_payload,​ auth=AUTH)
 +    print(f"​Статус-код ответа:​ {response.status_code}"​)
 +    print(f"​Текст ответа:​ {response.text}"​)
 +
 +def undetect_speech(channel_id):​
 +    print(f"​Отключаем TALK_DETECT для канала {channel_id}..."​)
 +    talk_url = f"​{BASE_URL}/​channels/​{channel_id}/​variable"​
 +
 +    talk_payload = {
 +        "​variable":​ "​TALK_DETECT(set)",​
 +        "​value":​ "​remove"​
 +    }
 +
 +    response = requests.post(talk_url,​ json=talk_payload,​ auth=AUTH)
 +    print(f"​Статус-код ответа:​ {response.status_code}"​)
 +    print(f"​Текст ответа:​ {response.text}"​)
 +
 +def start_recording(channel_id):​
 +    print(f"​Включаем запись для канала {channel_id}"​)
 +    record_url = f"​{BASE_URL}/​channels/​{channel_id}/​record"​
 +    recording_name = f"​rec_{channel_id}"​
 +    record_payload = {
 +        "​name":​ recording_name,​
 +        "​format":​ "​wav",​
 +        "​ifExists":​ "​overwrite",​
 +        #"​maxDuration":​ 29,
 +        #"​terminateOn":​ "#",​
 +        #"​beep":​ True,
 +    }
 +    requests.post(record_url,​ params=record_payload,​ auth=AUTH)
 +
 +def stop_recording(channel_id):​
 +    print(f"​Останавливаем запись для канала {channel_id}"​)
 +    recording_name = f"​rec_{channel_id}"​
 +    stop_url = f"​{BASE_URL}/​recordings/​live/​{recording_name}/​stop"​
 +    requests.post(stop_url,​ auth=AUTH)
 +
 +def on_message(wsapp,​ message):
 +    event = json.loads(message)
 +    event_type = event.get("​type"​)
 +
 +    # Сценарий 1: Звонок поступил -> Отвечаем и воспроизводим beep
 +    if event_type == "​StasisStart":​
 +        channel_id = event.get("​channel",​ {}).get("​id"​)
 +        print(f"​Отвечаем на звонок {channel_id}"​)
 +        requests.post(f"​{BASE_URL}/​channels/​{channel_id}/​answer",​ auth=AUTH)
 +
 +        #​start_recording(channel_id)
 +        # or
 +        detect_speech(channel_id)
 +
 +    # Сценарий N
 +    elif event_type == "​ChannelTalkingStarted":​
 +        channel_id = event.get("​channel",​ {}).get("​id"​)
 +        print(f"​Обнаружена речь в канале {channel_id}! Начинаем запись..."​)
 +        start_recording(channel_id)
 +
 +    # Сценарий N
 +    elif event_type == "​ChannelTalkingFinished":​
 +        channel_id = event.get("​channel",​ {}).get("​id"​)
 +        print(f"​Обнаружено молчание в канале {channel_id}"​)
 +        stop_recording(channel_id)
 +        undetect_speech(channel_id)
 +
 +    # Сценарий N: Запись завершилась -> Проигрываем её обратно
 +    elif event_type == "​RecordingFinished":​
 +        recording_data = event.get("​recording",​ {})
 +        recording_name = recording_data.get("​name"​)
 +        target_uri = recording_data.get("​target_uri",​ ""​)
 +        channel_id = target_uri.split("​channel:"​)[1]
 +
 +        print(f"​Запись {recording_name} готова! Проигрываем её обратно в канал {channel_id}..."​)
 +        play_url = f"​{BASE_URL}/​channels/​{channel_id}/​play"​
 +        play_payload = {
 +            "​media":​ f"​recording:​{recording_name}"​
 +        }
 +        response = requests.post(play_url,​ params=play_payload,​ auth=AUTH)
 +        print("​Ответ ARI на воспроизведение:",​ response.status_code)
 +
 +    # Сценарий N: Воспроизведение завершено (бип или записанный голос)
 +    elif event_type == "​PlaybackFinished":​
 +        playback_data = event.get("​playback",​ {})
 +        media_uri = playback_data.get("​media_uri",​ ""​)
 +        target_uri = playback_data.get('​target_uri',​ ''​)
 +        channel_id = target_uri.split("​channel:"​)[1]
 +
 +        #​start_recording(channel_id)
 +        # or
 +        detect_speech(channel_id)
 +
 +    # Сценарий N: Абонент повесил трубку -> Удаляем файл
 +    elif event_type == "​StasisEnd":​
 +        channel_id = event.get("​channel",​ {}).get("​id"​)
 +        recording_name = f"​rec_{channel_id}"​
 +        print(f"​Звонок {channel_id} завершен абонентом. Удаляем файл записи..."​)
 +        delete_url = f"​{BASE_URL}/​recordings/​stored/​{recording_name}"​
 +        requests.delete(delete_url,​ auth=AUTH)
 +
 +def on_error(ws,​ error):
 +    print(f"​Произошла ошибка:​ {error}"​)
 +
 +wsapp = websocket.WebSocketApp(
 +    f"​ws://​localhost:​8088/​ari/​events?​api_key={ARI_USER}:​{ARI_PASS}&​app=my-first-app",​
 +    on_message=on_message,​
 +    on_error=on_error
 +)
 +wsapp.run_forever()
 +</​code>​
 +
 +==== ari_chat.py ====
 +<​code>​
 +import json
 +import requests
 +import websocket
 +import os
 +
 +ARI_USER = "​asterisk"​
 +ARI_PASS = "​asterisk"​
 +BASE_URL = "​http://​localhost:​8088/​ari"​
 +AUTH = (ARI_USER, ARI_PASS)
 +
 +REC_PATH = "/​var/​spool/​asterisk/​recording/"​
 +
 +SYSTEM_PROMPT = (
 +    "Ты — ассистент.\n"​
 +    "​Отвечай кратко.\n"​
 +    "Не спрашивай когда напомнить.\n"​
 +)
 +
 +COMPRESS_PROMPT = (
 +    "Ты — инструмент оптимизации контекста. Проанализируй историю диалога. "
 +    "​Удали выполненные дела, приветствия и воду. Сформулируй ОДНО лаконичное "
 +    "​сообщение от лица пользователя,​ в котором отражена только актуальная суть "
 +    "и невыполненные задачи/​оставшиеся вопросы на текущий момент."​
 +)
 +
 +# Структура:​ {channel_id:​ [{"​role":​ "​user",​ "​text":​ "​..."​},​ {"​role":​ "​assistant",​ "​text":​ "​..."​}]}
 +channel_contexts = {}
 +
 +channel_caller_numbers = {}
 +
 +def get_channel_context(channel_id):​
 +    if channel_id not in channel_contexts:​
 +        channel_contexts[channel_id] = []
 +    return channel_contexts[channel_id]
 +
 +def update_channel_context(channel_id,​ user_request,​ agent_response):​
 +    context = get_channel_context(channel_id)
 +    context.append({"​role":​ "​user",​ "​text":​ user_request})
 +    context.append({"​role":​ "​assistant",​ "​text":​ agent_response})
 +    return context
 +
 +def load_context(caller_number):​
 +    file_path = f"​{REC_PATH}{caller_number}.json"​
 +    if os.path.exists(file_path):​
 +        with open(file_path,​ '​r',​ encoding='​utf-8'​) as f:
 +            return json.load(f)
 +    return []
 +
 +def save_context(caller_number,​ context):
 +    file_path = f"​{REC_PATH}{caller_number}.json"​
 +    with open(file_path,​ '​w',​ encoding='​utf-8'​) as f:
 +        json.dump(context,​ f, ensure_ascii=False,​ indent=2)
 +
 +def start_recording(channel_id):​
 +    print(f"​Включаем запись для канала {channel_id} на 29 секунд..."​)
 +    record_url = f"​{BASE_URL}/​channels/​{channel_id}/​record"​
 +    recording_name = f"​rec_{channel_id}"​
 +    record_payload = {
 +        "​name":​ recording_name,​
 +        "​format":​ "​wav",​
 +        "​ifExists":​ "​overwrite",​
 +        "​maxDuration":​ 29,
 +        "​terminateOn":​ "#",​
 +        "​beep":​ True,
 +    }
 +    requests.post(record_url,​ params=record_payload,​ auth=AUTH)
 +
 +def get_caller_number(channel_id):​
 +    url = f"​{BASE_URL}/​channels/​{channel_id}"​
 +    response = requests.get(url,​ auth=AUTH, timeout=5)
 +    channel_info = response.json()
 +    caller_number = channel_info.get("​caller",​ {}).get("​number"​)
 +    return caller_number
 +
 +def on_message(wsapp,​ message):
 +    event = json.loads(message)
 +    event_type = event.get("​type"​)
 +
 +    # Сценарий 1: Звонок поступил
 +    if event_type == "​StasisStart":​
 +        channel_id = event.get("​channel",​ {}).get("​id"​)
 +
 +        print(f"​Отвечаем на звонок {channel_id}"​)
 +        requests.post(f"​{BASE_URL}/​channels/​{channel_id}/​answer",​ auth=AUTH)
 +
 +        channel_caller_numbers[channel_id]=get_caller_number(channel_id)
 +        channel_contexts[channel_id] = load_context(channel_caller_numbers[channel_id])
 +        start_recording(channel_id)
 +
 +    # Сценарий 2: Запись вопроса завершилась -> Проигрываем ответ
 +    elif event_type == "​RecordingFinished":​
 +        recording_data = event.get("​recording",​ {})
 +        recording_name = recording_data.get("​name"​)
 +        target_uri = recording_data.get("​target_uri",​ ""​)
 +        channel_id = target_uri.split("​channel:"​)[1]
 +
 +        caller_number = channel_caller_numbers[channel_id]
 +        print(f"​Номер звонящего в RecordingFinished:​ {caller_number}"​)
 +
 +        import wav_and_ogg
 +        import speech_and_text
 +        wav_and_ogg.wav_to_ogg(f"​{REC_PATH}{recording_name}"​)
 +
 +        user_request = speech_and_text.speech_to_text(f"​{REC_PATH}{recording_name}.ogg"​)
 +        print(f"​user_request:​ {user_request}"​)
 +
 +        if user_request:​
 +            context = get_channel_context(channel_id)
 +
 +            import request_to_agent
 +            agent_response = request_to_agent.request_to_agent(user_request,​ SYSTEM_PROMPT,​ context)
 +            #​agent_response = user_request
 +
 +            print(agent_response)
 +
 +            update_channel_context(channel_id,​ user_request,​ agent_response)
 +
 +            print("​Текущий контекст:"​)
 +            from pprint import pprint
 +            pprint(channel_contexts[channel_id])
 +
 +            speech_and_text.text_to_speech(agent_response,​f"​{REC_PATH}{recording_name}.response.ogg"​)
 +            wav_and_ogg.ogg_to_wav(f"​{REC_PATH}{recording_name}.response"​)
 +
 +            print(f"​Запись {recording_name} готова! Проигрываем её обратно в канал {channel_id}..."​)
 +            play_url = f"​{BASE_URL}/​channels/​{channel_id}/​play"​
 +            play_payload = {
 +                #"​media":​ f"​recording:​{recording_name}"​
 +                "​media":​ f"​recording:​{recording_name}.response"​
 +            }
 +            response = requests.post(play_url,​ params=play_payload,​ auth=AUTH)
 +            print("​Ответ ARI на воспроизведение:",​ response.status_code)
 +
 +    # Сценарий 3: Воспроизведение завершено
 +    elif event_type == "​PlaybackFinished":​
 +        playback_data = event.get("​playback",​ {})
 +        media_uri = playback_data.get("​media_uri",​ ""​)
 +        target_uri = playback_data.get('​target_uri',​ ''​)
 +        channel_id = target_uri.split("​channel:"​)[1]
 +
 +        start_recording(channel_id)
 +
 +    # Сценарий 4: Абонент повесил трубку
 +    elif event_type == "​StasisEnd":​
 +        channel_id = event.get("​channel",​ {}).get("​id"​)
 +
 +        context = get_channel_context(channel_id)
 +        caller_number = channel_caller_numbers[channel_id]
 +        print(f"​Номер звонящего в StasisEnd: номер {caller_number} канал {channel_id}"​)
 +
 +        print(f"​Сжатие контекста"​)
 +
 +        import request_to_agent
 +        agent_response = request_to_agent.request_to_agent("​Сожми контекст",​ COMPRESS_PROMPT,​ context)
 +        print(agent_response)
 +        context = [{"​role":​ "​user",​ "​text":​ agent_response}]
 +
 +        print(f"​Сохранение контекста и удаление медиа файлов"​)
 +
 +        save_context(caller_number,​context)
 +
 +        import glob
 +        recording_name = f"​rec_{channel_id}"​
 +        file_mask = f"​{REC_PATH}{recording_name}*"​
 +        matched_files = glob.glob(file_mask)
 +        for file_path in matched_files:​
 +            os.remove(file_path)
 +            print(f"​Файл удален:​ {file_path}"​)
 +
 +def on_error(ws,​ error):
 +    print(f"​Произошла ошибка:​ {error}"​)
 +
 +wsapp = websocket.WebSocketApp(
 +    f"​ws://​localhost:​8088/​ari/​events?​api_key={ARI_USER}:​{ARI_PASS}&​app=my-first-app",​
 +    on_message=on_message,​
 +    on_error=on_error
 +)
 +wsapp.run_forever()
 +</​code>​
 +
 +==== ari_chat_talk_detect.py ====
 +<​code>​
 +import os
 +import json
 +import requests
 +import websocket
 +
 +ARI_USER = "​asterisk"​
 +ARI_PASS = "​asterisk"​
 +BASE_URL = "​http://​localhost:​8088/​ari"​
 +AUTH = (ARI_USER, ARI_PASS)
 +
 +REC_PATH = "/​var/​spool/​asterisk/​recording/"​
 +
 +SYSTEM_PROMPT = (
 +    "Ты — ассистент.\n"​
 +    "​Отвечай кратко.\n"​
 +    "Не спрашивай когда напомнить.\n"​
 +)
 +
 +COMPRESS_PROMPT = (
 +    "Ты — инструмент оптимизации контекста. Проанализируй историю диалога. "
 +    "​Удали выполненные дела, приветствия и воду. Сформулируй ОДНО лаконичное "
 +    "​сообщение от лица пользователя,​ в котором отражена только актуальная суть "
 +    "и невыполненные задачи/​оставшиеся вопросы на текущий момент."​
 +)
 +
 +# Структура:​ {channel_id:​ [{"​role":​ "​user",​ "​text":​ "​..."​},​ {"​role":​ "​assistant",​ "​text":​ "​..."​}]}
 +channel_contexts = {}
 +
 +channel_caller_numbers = {}
 +
 +def load_context(caller_number):​
 +    file_path = f"​{REC_PATH}{caller_number}.json"​
 +    if os.path.exists(file_path):​
 +        with open(file_path,​ '​r',​ encoding='​utf-8'​) as f:
 +            return json.load(f)
 +    return []
 +
 +def save_context(caller_number,​ context):
 +    file_path = f"​{REC_PATH}{caller_number}.json"​
 +    with open(file_path,​ '​w',​ encoding='​utf-8'​) as f:
 +        json.dump(context,​ f, ensure_ascii=False,​ indent=2)
 +
 +def get_caller_number(channel_id):​
 +    url = f"​{BASE_URL}/​channels/​{channel_id}"​
 +    response = requests.get(url,​ auth=AUTH, timeout=5)
 +    channel_info = response.json()
 +    caller_number = channel_info.get("​caller",​ {}).get("​number"​)
 +    return caller_number
 +
 +def get_channel_context(channel_id):​
 +    if channel_id not in channel_contexts:​
 +        channel_contexts[channel_id] = []
 +    return channel_contexts[channel_id]
 +
 +def update_channel_context(channel_id,​ user_request,​ agent_response):​
 +    context = get_channel_context(channel_id)
 +    context.append({"​role":​ "​user",​ "​text":​ user_request})
 +    context.append({"​role":​ "​assistant",​ "​text":​ agent_response})
 +    return context
 +
 +def detect_speech(channel_id):​
 +    print(f"​Запускаем TALK_DETECT для канала {channel_id}..."​)
 +    talk_url = f"​{BASE_URL}/​channels/​{channel_id}/​variable"​
 +
 +    # 1000 мс тишины для завершения фразы, 500 мс речи для начала
 +    talk_payload = {
 +        "​variable":​ "​TALK_DETECT(set)",​
 +        "​value":​ "​1000,​500"​
 +    }
 +
 +    response = requests.post(talk_url,​ json=talk_payload,​ auth=AUTH)
 +    print(f"​Статус-код ответа:​ {response.status_code}"​)
 +    print(f"​Текст ответа:​ {response.text}"​)
 +
 +def undetect_speech(channel_id):​
 +    print(f"​Отключаем TALK_DETECT для канала {channel_id}..."​)
 +    talk_url = f"​{BASE_URL}/​channels/​{channel_id}/​variable"​
 +
 +    talk_payload = {
 +        "​variable":​ "​TALK_DETECT(set)",​
 +        "​value":​ "​remove"​
 +    }
 +
 +    response = requests.post(talk_url,​ json=talk_payload,​ auth=AUTH)
 +    print(f"​Статус-код ответа:​ {response.status_code}"​)
 +    print(f"​Текст ответа:​ {response.text}"​)
 +
 +def play_beep(channel_id):​
 +    print(f"​Проигрываем сигнал перед записью в канал {channel_id}..."​)
 +    play_url = f"​{BASE_URL}/​channels/​{channel_id}/​play"​
 +    play_payload = {"​media":​ "​sound:​beep"​}
 +    requests.post(play_url,​ params=play_payload,​ auth=AUTH)
 +
 +def start_recording(channel_id):​
 +    print(f"​Включаем запись для канала {channel_id}"​)
 +    record_url = f"​{BASE_URL}/​channels/​{channel_id}/​record"​
 +    recording_name = f"​rec_{channel_id}"​
 +    record_payload = {
 +        "​name":​ recording_name,​
 +        "​format":​ "​wav",​
 +        "​ifExists":​ "​overwrite",​
 +        #"​maxDuration":​ 29,
 +        #"​terminateOn":​ "#",​
 +        #"​beep":​ True,
 +    }
 +    requests.post(record_url,​ params=record_payload,​ auth=AUTH)
 +
 +def stop_recording(channel_id):​
 +    print(f"​Останавливаем запись для канала {channel_id}"​)
 +    recording_name = f"​rec_{channel_id}"​
 +    stop_url = f"​{BASE_URL}/​recordings/​live/​{recording_name}/​stop"​
 +    requests.post(stop_url,​ auth=AUTH)
 +
 +def on_message(wsapp,​ message):
 +    event = json.loads(message)
 +    event_type = event.get("​type"​)
 +
 +    # Сценарий 1: Звонок поступил -> Отвечаем и воспроизводим beep
 +    if event_type == "​StasisStart":​
 +        channel_id = event.get("​channel",​ {}).get("​id"​)
 +        print(f"​Отвечаем на звонок {channel_id}"​)
 +        requests.post(f"​{BASE_URL}/​channels/​{channel_id}/​answer",​ auth=AUTH)
 +
 +        channel_caller_numbers[channel_id]=get_caller_number(channel_id)
 +        channel_contexts[channel_id] = load_context(channel_caller_numbers[channel_id])
 +        play_beep(channel_id)
 +
 +        #​start_recording(channel_id)
 +        # or
 +        #​detect_speech(channel_id)
 +
 +    # Сценарий N
 +    elif event_type == "​ChannelTalkingStarted":​
 +        channel_id = event.get("​channel",​ {}).get("​id"​)
 +        print(f"​Обнаружена речь в канале {channel_id}! Начинаем запись..."​)
 +        start_recording(channel_id)
 +
 +    # Сценарий N
 +    elif event_type == "​ChannelTalkingFinished":​
 +        channel_id = event.get("​channel",​ {}).get("​id"​)
 +        print(f"​Обнаружено молчание в канале {channel_id}"​)
 +        stop_recording(channel_id)
 +        undetect_speech(channel_id)
 +
 +    # Сценарий N: Запись завершилась -> Проигрываем её обратно
 +    elif event_type == "​RecordingFinished":​
 +        recording_data = event.get("​recording",​ {})
 +        recording_name = recording_data.get("​name"​)
 +        target_uri = recording_data.get("​target_uri",​ ""​)
 +        channel_id = target_uri.split("​channel:"​)[1]
 +
 +        import wav_and_ogg
 +        import speech_and_text
 +        wav_and_ogg.wav_to_ogg(f"​{REC_PATH}{recording_name}"​)
 +
 +        user_request = speech_and_text.speech_to_text(f"​{REC_PATH}{recording_name}.ogg"​)
 +        print(f"​user_request:​ {user_request}"​)
 +
 +        if user_request:​
 +            context = get_channel_context(channel_id)
 +
 +            import request_to_agent
 +            agent_response = request_to_agent.request_to_agent(user_request,​ SYSTEM_PROMPT,​ context)
 +            #​agent_response = user_request
 +
 +            print(agent_response)
 +
 +            update_channel_context(channel_id,​ user_request,​ agent_response)
 +
 +            print("​Текущий контекст:"​)
 +            from pprint import pprint
 +            pprint(channel_contexts[channel_id])
 +
 +            speech_and_text.text_to_speech(agent_response,​f"​{REC_PATH}{recording_name}.response.ogg"​)
 +            wav_and_ogg.ogg_to_wav(f"​{REC_PATH}{recording_name}.response"​)
 +
 +            print(f"​Запись {recording_name} готова! Проигрываем её обратно в канал {channel_id}..."​)
 +            play_url = f"​{BASE_URL}/​channels/​{channel_id}/​play"​
 +            play_payload = {
 +                #"​media":​ f"​recording:​{recording_name}"​
 +                "​media":​ f"​recording:​{recording_name}.response"​
 +            }
 +            response = requests.post(play_url,​ params=play_payload,​ auth=AUTH)
 +            print("​Ответ ARI на воспроизведение:",​ response.status_code)
 +
 +    # Сценарий N: Воспроизведение завершено (бип или записанный голос)
 +    elif event_type == "​PlaybackFinished":​
 +        playback_data = event.get("​playback",​ {})
 +        media_uri = playback_data.get("​media_uri",​ ""​)
 +        target_uri = playback_data.get('​target_uri',​ ''​)
 +        channel_id = target_uri.split("​channel:"​)[1]
 +
 +        #​start_recording(channel_id)
 +        # or
 +        #​detect_speech(channel_id)
 +
 +        if media_uri == "​sound:​beep":​
 +            print(f"​Сигнал завершен. Начинаем запись для канала {channel_id}..."​)
 +            detect_speech(channel_id)
 +
 +        elif "​recording:​rec_"​ in media_uri:
 +            print(f"​Воспроизведение записи завершено. Идем на новый круг для канала {channel_id}..."​)
 +            play_beep(channel_id)
 +
 +    # Сценарий N: Абонент повесил трубку -> Удаляем файл
 +    elif event_type == "​StasisEnd":​
 +        channel_id = event.get("​channel",​ {}).get("​id"​)
 +
 +        #​print(f"​Звонок {channel_id} завершен абонентом. Удаляем файл записи..."​)
 +        #​recording_name = f"​rec_{channel_id}"​
 +        #delete_url = f"​{BASE_URL}/​recordings/​stored/​{recording_name}"​
 +        #​requests.delete(delete_url,​ auth=AUTH)
 +
 +        context = get_channel_context(channel_id)
 +        caller_number = channel_caller_numbers[channel_id]
 +        print(f"​Номер звонящего в StasisEnd: номер {caller_number} канал {channel_id}"​)
 +
 +        print(f"​Сжатие контекста"​)
 +
 +        import request_to_agent
 +        agent_response = request_to_agent.request_to_agent("​Сожми контекст",​ COMPRESS_PROMPT,​ context)
 +        print(agent_response)
 +        context = [{"​role":​ "​user",​ "​text":​ agent_response}]
 +
 +        print(f"​Сохранение контекста и удаление медиа файлов"​)
 +
 +        save_context(caller_number,​context)
 +
 +        import glob
 +        recording_name = f"​rec_{channel_id}"​
 +        file_mask = f"​{REC_PATH}{recording_name}*"​
 +        matched_files = glob.glob(file_mask)
 +        for file_path in matched_files:​
 +            os.remove(file_path)
 +            print(f"​Файл удален:​ {file_path}"​)
 +
 +def on_error(ws,​ error):
 +    print(f"​Произошла ошибка:​ {error}"​)
 +
 +wsapp = websocket.WebSocketApp(
 +    f"​ws://​localhost:​8088/​ari/​events?​api_key={ARI_USER}:​{ARI_PASS}&​app=my-first-app",​
 +    on_message=on_message,​
 +    on_error=on_error
 +)
 +wsapp.run_forever()
 +</​code>​
 +==== Черновик ====
  
язык_программирования_python.1748098694.txt.gz · Last modified: 2025/05/24 17:58 by val