a

Issues Bridging Twilio Calls between Customer and Agent

We are seeing issues bridging Twilio calls between the customer and agent with IVR, whisper, wait music, and call recording. Scenario: We have clients calling in. They enter an id to the Twilio based IVR. We play wait music. We call the agent. When the agent picks up the phone, we play the whisper of […]

We are seeing issues bridging Twilio calls between the customer and agent with IVR, whisper, wait music, and call recording.

Scenario:
We have clients calling in. They enter an id to the Twilio based IVR. We play wait music. We call the agent. When the agent picks up the phone, we play the whisper of the ID. We give the agent the opportunity to look up the number. We then bridge the call and launch call recording.
Issue:
In certain situations, we lose the inbound call. We’re not entirely sure why. We don’t see any errors.
Call Flow:
1. Calls the Agent. Enqueue’s the current customer call.
File: CallAgentService.java

    public Verb callAgent(ServletRequest req, Category twilioSettings, JSONObject member) throws Exception {
        String numberCalled = getNumberCalled(req, member);
        String callSid = req.getParameter(Constants.TWILIO_PARAM_CALLSID);
        CallStatus status = callStatusRepository.findByCode(Constants.CALL_STATUS_KNOWN_ID);
        String digits = req.getParameter(Constants.TWILIO_PARAM_DIGITS);
        if(StringUtils.isNotBlank(digits)) {
            TwilioConfigUtils.saveCallStat(callLogStatRepository, callSid, status, digits);
        } else {
            TwilioConfigUtils.saveCallStat(callLogStatRepository, callSid, status);
        }
        CallLog callLog = callLogRepository.findOne(callSid);
        saveCallLog(callLog, member);
        Enqueue enqueue = new Enqueue(callSid);
        enqueue.setWaitUrl(Constants.ENQUEUE_WAIT_URL);
        enqueue.setWaitUrlMethod(HttpMethod.GET.toString());
        Call call = TwilioConfigUtils.callAgent(numberCalled, twilioSettings, env);
        if (call != null) {
            TwilioConfigUtils.saveNewCall(call, callLog, callLogRepository);
            status = callStatusRepository.findByCode(Constants.CALL_STATUS_AGENT_START);
            TwilioConfigUtils.saveCallStat(callLogStatRepository, callSid, status);
            return enqueue;
        } else {
            Say sayError = new Say("We are sorry an error has occurred.");
            sayError.setLanguage(Constants.SPOKEN_LANGUAGE);
            return sayError;
        }
    }

2. Creates the outbound call to the agent with the whisper.
File: TwilioConfigUtils.java

    public static Call callAgent(String called, Category twilioSettings, Environment env) {
        log.debug("Calling agent");
        try {
            String acctSid = getTwilioConfigByCode(twilioSettings, Constants.TWILIO_ACCOUNT_SID);
            String authToken = getTwilioConfigByCode(twilioSettings, Constants.TWILIO_AUTH_TOKEN);
            String phoneNumber = getTwilioConfigByCode(twilioSettings, Constants.TWILIO_AGENT_PHONE);
            TwilioRestClient client = new TwilioRestClient(acctSid, authToken);
            // Build a filter for the CallList
            List params = new ArrayList<>();
            String localhost = env.getProperty("rest.main.url");
            params.add(new BasicNameValuePair("Url", localhost + Constants.WHISPER_AGENT));
            params.add(new BasicNameValuePair("To", phoneNumber));
            params.add(new BasicNameValuePair("From", called));
            params.add(new BasicNameValuePair("Method", HttpMethod.GET.toString()));
            params.add(new BasicNameValuePair("Record", Boolean.TRUE.toString()));
            params.add(new BasicNameValuePair("StatusCallback", localhost + Constants.AUDIO_RECORD_CALLBACK));
            params.add(new BasicNameValuePair("StatusCallbackMethod", HttpMethod.GET.toString()));
            CallFactory callFactory = client.getAccount().getCallFactory();
            Call call = callFactory.create(params);
            return call;
        } catch (Exception e) {
            log.error(e);
        } finally {
            log.debug("Agent called");
        }
        return null;
    }


Agent Call on Twilio Logs

Twilio Log - Outbound Calls to Agent
Twilio Log - Outbound Calls to Agent


3. Whispers to the Agent
File: WhisperAgentServlet.java

 @Override
    public void service(ServletRequest req, ServletResponse response) throws ServletException, IOException {
        log.debug("Entering WhisperAgentServlet");
        String callSid = req.getParameter(Constants.TWILIO_PARAM_CALLSID);
        CallLog callLog = callLogRepository.findOne(callSid);
        CallStatus status = callStatusRepository.findByCode(Constants.CALL_STATUS_AGENT_CONNECT);
        TwilioConfigUtils.saveCallStat(callLogStatRepository, callLog.getParentCallSid(), status);
        TwiMLResponse twiml = new TwiMLResponse();
        Long clientId = callLog.getClientId();
        try {
            if (callLog == null) {
                log.debug(twiml.toXML());
                response.setContentType(Constants.CONTENT_TYPE_XML);
                response.getWriter().print(twiml.toXML());
                return;
            }
            Category twilioSettings = categoryRepository.findCategoryByCodeAndClientId(Constants.TWILIO_SETTINGS, clientId);
            String msg;
            if (StringUtils.isNotBlank(callLog.getMemberName()) && StringUtils.isNotBlank(callLog.getMemberLastName())) {
                msg = TwilioConfigUtils.getTwilioConfigByCode(twilioSettings, Constants.TWILIO_WHISPER_AGENT_MENU_MEMBER_INFO);
                Map<String, String> map = new HashMap<>();
                map.put(Constants.TWILIO_MSG_VAR_FIRSTNAME, callLog.getMemberName());
                map.put(Constants.TWILIO_MSG_VAR_LASTNAME, callLog.getMemberLastName());
                String memberId = callLog.getCsMemberId();
                if (StringUtils.isBlank(memberId)) {
                    memberId = callLog.getClientMemberId();
                } else {
                    memberId = "1111" + StringUtils.leftPad(memberId, 8, '0');
                }
                StringBuilder sbMemberId = new StringBuilder();
                for (char c : memberId.toCharArray()) {
                    sbMemberId.append(c).append(", ");
                }
                map.put(Constants.TWILIO_MSG_VAR_MEMBER_ID, sbMemberId.toString().trim());
                msg = StrSubstitutor.replace(msg, map);
            } else {
                msg = TwilioConfigUtils.getTwilioConfigByCode(twilioSettings, Constants.TWILIO_WHISPER_AGENT_MENU_NO_MEMBER_INFO);
            }
            Say incomingCall = new Say(msg);
            incomingCall.setLanguage(Constants.SPOKEN_LANGUAGE);
            Dial dial = new Dial();
            Queue queue = new Queue(callLog.getParentCallSid());
            dial.append(queue);
            twiml.append(incomingCall);
            twiml.append(dial);
        } catch (TwiMLException e) {
            log.error(e);
            Say error = new Say("We are sorry an error has occurred while connecting.");
            error.setLanguage(Constants.SPOKEN_LANGUAGE);
            try {
                twiml = new TwiMLResponse();
                twiml.append(error);
            } catch (TwiMLException e1) {
                log.error(e1);
            }
            callLog = callLogRepository.findOne(callLog.getParentCallSid());
            Category twilioSettings = categoryRepository.findCategoryByCodeAndClientId(Constants.TWILIO_SETTINGS, clientId);
            TwilioConfigUtils.releaseQueueWithError(callLog, twilioSettings, env);
        }
        log.debug(twiml.toXML());
        response.setContentType(Constants.CONTENT_TYPE_XML);
        response.getWriter().print(twiml.toXML());
    }

Customer Call on Twilio Logs
Where does the Hangup event come from? When we do our tests we are still on the call?!??!

Twilio Log of Inbound Client Call with IVR
Twilio Log of Inbound Client Call with IVR

Related Articles

3 Ways Gen AI and AWS can Enhance Your Business

3 Ways Gen AI and AWS can Enhance Your Business

Amazon is on the cutting edge of new technologies. They have been increasingly experimenting with AI and learning algorithms, culminating in their most recent breakthroughs in Generative AI. Developers and technology enthusiasts have access to their innovations through the tools available on AWS.

Business Owner’s Guide to DevOps Essentials

Business Owner’s Guide to DevOps Essentials

As a business owner, it’s essential to maximize workplace efficiency. DevOps is a methodology that unites various departments to achieve business goals swiftly. Maintaining a DevOps loop is essential for the health and upkeep of deployed applications.