Обработка занято / нет ответа в кольцевой системе

Я провел некоторый поиск по этой теме и ознакомился с соответствующими ссылками на API, но я все еще изо всех сил пытаюсь реализовать это полностью.

Я пытаюсь создать в Python/Flask с библиотекой python-twilio систему, которая будет делать следующее:

  1. Пользователь звонит, слышит сообщение
  2. Приложение одновременно подключается к первой из n групп, где группа представляет собой список номеров с метаинформацией (например, именем). Представляется в коде как dict со значениями списков чисел. (k[v] = [0,1,n])
  3. Если кто-то (агент) в группе отвечает, попросите его ввести цифру подтверждения, чтобы продолжить. После подтверждения подключите их к пользователю.
  4. Если агент в группе не поднял трубку, попробуйте следующий агент в группе (позвоните по его номерам) и вернитесь к шагу 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(), и последний номер, который дал статус отсутствия ответа, добавляется в указанный список. Затем я сравниваю длину списка операторов [агента] с этим глобальным списком, и, если он совпадает, я изменяю вызов, чтобы попробовать следующую группу, однако это привело к фантомным / неправильным вызовам.

У меня вопрос: как мне выполнить эту модификацию живого звонка, чтобы я убрал все существующие звонки предыдущей группе? Как мне улучшить дизайн этого кода? Есть что-то очевидное, что я пропустил? Любые указатели будут оценены.

0 ответов

Другие вопросы по тегам