Обработка занято / нет ответа в кольцевой системе
Я провел некоторый поиск по этой теме и ознакомился с соответствующими ссылками на API, но я все еще изо всех сил пытаюсь реализовать это полностью.
Я пытаюсь создать в Python/Flask с библиотекой python-twilio систему, которая будет делать следующее:
- Пользователь звонит, слышит сообщение
- Приложение одновременно подключается к первой из n групп, где группа представляет собой список номеров с метаинформацией (например, именем). Представляется в коде как dict со значениями списков чисел. (k[v] = [0,1,n])
- Если кто-то (агент) в группе отвечает, попросите его ввести цифру подтверждения, чтобы продолжить. После подтверждения подключите их к пользователю.
- Если агент в группе не поднял трубку, попробуйте следующий агент в группе (позвоните по его номерам) и вернитесь к шагу 3.
Хорошо, так далее к коду. Это было сокращено до базовой логики. К сожалению, я не смог разобраться с состоянием приложения Flask, поэтому я использовал Python для доступа к глобальным переменным. Я хотел бы отойти от этого, так как приложение, скорее всего, будет работать некорректно, если в приложение будет сделано два вызова.
# excluding flask import/setup
from twilio import twiml
from twilio.rest import TwilioRestClient
agents = OrderedDict()
agents['Foo'] = ["+440000000000", "+440000000001"]
agents['Bar'] = ["+440000000002", "+440000000003"]
caller = None
@app.route('/notify', methods=['POST'])
def notify():
"""Log all call status callbacks"""
global caller
called = request.values.get('Called')
call_status = request.values.get('CallStatus')
timestamp = request.values.get('Timestamp') # e.g. Wed, 02 Mar 2016 15:42:30 +0000
call_statuses = {
"initiated": "[%s@%s] INITIATED",
"ringing": "[%s@%s] RINGING",
"in-progress": "[%s@%s] IN-PROGRESS",
"answered": "[%s@%s] ANSWERED",
"failed": "[%s@%s] CALL FAILED",
"no-answer": "[%s@%s] NO ANSWER",
"busy": "[%s@%s] BUSY",
"completed": "[%s@%s] COMPLETED"
}
called_name = [i for i in agents.items() if called in i][0][0]
print("--%s-- %s" % (timestamp, call_statuses[call_status] % (called_name, called)))
return jsonify({"status": 200})
def new_twilio_client():
_ = TwilioRestClient(app.config['TWILIO_ACCOUNT_SID'], app.config['TWILIO_AUTH_TOKEN'])
return _
@app.route('/welcome', methods=['GET'])
def welcome():
global caller
response = twiml.Response()
response.say("Welcome message", voice="alice")
caller = request.values.get("Caller")
# log the call/caller here
response.redirect(url_for(".start"), _external=True, method="GET")
return str(response)
@app.route('/start', methods=['GET'])
def start():
response = twiml.Response()
response.say("Trying first agent")
response.enqueue("q")
client = new_twilio_client()
for number in agents['Foo']:
client.calls.create(to=number, Timeout=12, ifMachine="Hangup", statusCallbackEvent="initiated ringing answered completed", StatusCallback=url_for(".notify", _external=True), from_=app.config['TWILIO_CALLER_ID'], url=url_for(".whisper", _external=True), method="GET")
return str(response)
@app.route("/whisper", methods=['GET'])
def whisper():
"""Ask agent if they want to accept the call"""
global call_sid
response = twiml.Response()
client = new_twilio_client()
response.say("Press 1 to accept this call")
response.gather(action=url_for(".did_agent_accept", _external=True), numDigits=1, timeout=3)
return str(response)
@app.route("/did_agent_accept", methods=['POST'])
def did_agent_accept():
"""Determine if the agent accepted the call"""
response = twiml.Response()
digit = request.values.get('Digits', None)
if digit == "1":
with response.dial() as dial:
# drop the agent into the queue we made earlier
dial.queue("q", url=url_for(".thanks_for_waiting", _external=True))
else:
response.reject()
return str(response)
@app.route("/thanks_for_waiting", methods=['GET'])
def thanks_for_waiting():
"""Thank the user"""
response = twiml.Response()
response.say("Thanks for waiting. You will now be connected.")
return str(response)
@app.route('/sorry', methods=['GET'])
def sorry():
response = twiml.Response()
response.say("Sorry, trying again")
response.redirect(url=url_for(".start", _external=True), method="GET")
return str(response)
Итак, я использовал функцию организации очередей Twilio здесь, чтобы поместить абонента в очередь, а затем я использую REST API для выполнения вызовов на номера, связанные с первым агентом. Когда они поднимают и отвечают на собирательный глагол, они попадают в очередь.
Контрольно-пропускной пункт, с которым я столкнулся:
- Управление статусами " занято" или " нет ответа" (или другими состояниями без ответа), чтобы определить, следует ли приложению использовать следующую группу номеров, затруднено из-за асинхронного характера функции обратного вызова. Я пробовал решение, в котором я поддерживаю глобальный список, который обновляется всякий раз, когда вызывается notify(), и последний номер, который дал статус отсутствия ответа, добавляется в указанный список. Затем я сравниваю длину списка операторов [агента] с этим глобальным списком, и, если он совпадает, я изменяю вызов, чтобы попробовать следующую группу, однако это привело к фантомным / неправильным вызовам.
У меня вопрос: как мне выполнить эту модификацию живого звонка, чтобы я убрал все существующие звонки предыдущей группе? Как мне улучшить дизайн этого кода? Есть что-то очевидное, что я пропустил? Любые указатели будут оценены.