Author Message
MichaelNorman
Joined: Jun 3, 2015
Messages: 448
Offline
Is there a mechanism to allow muting of one side of a conversation via breeze? Let's say I had an agent on a call with a customer and then that agent needs to warm transfer / conference the call to a particular internal number. Would breeze allow me to only the agent, customer, or the other member of the conference independently?

Alternatively, would the call.GetParticipantList() list each participant independently in this scenario so that a
collect(Participant participant, DigitOptions digitOptions, MediaListener mediaListener)
operation could be directed towards only one or the other members in the conference?
JoelEzell
Joined: Nov 15, 2013
Messages: 780
Offline
Sorry, there's not a way to do this currently.
MichaelNorman
Joined: Jun 3, 2015
Messages: 448
Offline
So then what is the purpose of this method if not to only allow one caller to enter the input?

collect(Participant participant, DigitOptions digitOptions, MediaListener mediaListener)
JoelEzell
Joined: Nov 15, 2013
Messages: 780
Offline
You are correct that this lets you collect digits from only one participant. It doesn't, however, prevent the other participant from hearing those digits. One way to circumvent this is to play an announcement (perhaps even a "silence" announcement) to the other participant in the call. If you play the announcement to the participant rather than the call, that will prevent the other participant's media from being heard.
MichaelNorman
Joined: Jun 3, 2015
Messages: 448
Offline
In this case I'm not as concerned about letting the other party hear, just that only one participant can enter the input.

In other words, if I do a collect towards ParticipantA, ParticipantB can push all the digits they want, but I'm only going to get the input from ParticipantA?

JoelEzell
Joined: Nov 15, 2013
Messages: 780
Offline
That's correct.
MichaelNorman
Joined: Jun 3, 2015
Messages: 448
Offline
Hi Joel. Since we did manage to finally get our app working in that I can dial it. As a refresher, what I'm trying to do is be able to distinguish who pressed DTMF or be able to target a collect towards one party.

So the scenario is--

I am an agent on an active call with a customer. I hit conference, then dial the breeze app. It plays an intro announcement, and waits for a key press to continue. Once this is done, I do a call.getActiveParties() and I would expect there to be two participants (agent and customer), however only the agent is listed.

Is there a scenario where this will work? This is the snippet of relevant code --





@Override
public void callIntercepted(final Call call)
{
// call.wasServiceCalled() tells the snap-in if the snap-in is invoked as a callable service.
if (call.wasServiceCalled())
{
logger.finer("callIntercepted - snap-in is configured as a callable service");
presentOptionsToUser(call);
}
else
{
logger.finer("callIntercepted - snap-in is sequenced.");
// Logic here is executed when snap-in is sequenced in Calling or Called phase.
}
}

private void presentOptionsToUser(final Call call)
{
try
{
final MyListener mediaListener = new MyListener();
final MediaService mediaService = MediaFactory.createMediaService();

myCall = call;
final PlayItem playItem = MediaFactory
.createPlayItem()
.setSource(url)
.setInterruptible(true).setIterateCount(1);

final DigitOptions digitOptions = MediaFactory.createDigitOptions()
.setNumberOfDigits(1).setTerminationKey("#")
.setTimeout(60000);

final Participant participant = call.getCallingParty();
mediaService.promptAndCollect(participant, playItem,
digitOptions, mediaListener);



}
catch (final URISyntaxException e)
{
logger.error("Bad file URI ", e);
}
}

private void getConferenceParticipants(final Call call)
{
logger.fine("Listing Participants --");
logger.fine("Participants found -- " + call.getActiveParties().size());

for (Participant party : call.getActiveParties()) {
logger.fine("Party Member -- " + party.getAddress());

}
}

private class MyListener extends MediaListenerAbstract
{
@Override
public void playCompleted(final UUID requestId,
final PlayOperationCause cause)
{
logger.fine(myCall.getId() + " Done playing prompt." + cause);
myCall.drop();
}

@Override
public void digitsCollected(final UUID requestId, final String digits,
final DigitCollectorOperationCause cause)
{

logger.fine(myCall.getId() + " Received digits. Digits = " + digits);
if (digits == null || digits.length() == 0)
{
logger.fine(myCall.getId() + " No selection was made and so the call will be diverted. ");
final String operatorExtension = "0";
myCall.divertTo(operatorExtension);
return;
}
else if (digits.startsWith("1"))
{
final String divertExtension = "3000";
myCall.divertTo(divertExtension);
}
else if (digits.startsWith("2"))
{
getConferenceParticipants(myCall);


myCall.divertTo(<<number>>);

logger.fine(myCall.getId() + "Conference complete...");


}
logger.fine(myCall.getId() + "Something besides 1 or 2 was pressed...");

}
}



Output is--


2018-04-19 10:57:44,840 [SipContainerPool : 4] TestService FINER - TestService-1.1.1.4.0 - callIntercepted - snap-in is configured as a callable service
2018-04-19 10:58:02,383 [SipContainerPool : 4] TestService FINE - TestService-1.1.1.4.0 - Received digits. Digits = 2
2018-04-19 10:58:02,383 [SipContainerPool : 4] TestService FINE - TestService-1.1.1.4.0 - Listing Participants --
2018-04-19 10:58:02,383 [SipContainerPool : 4] TestService FINE - TestService-1.1.1.4.0 - Participants found -- 1
2018-04-19 10:58:02,383 [SipContainerPool : 4] TestService FINE - TestService-1.1.1.4.0 - Party Member -- <<mynumber>>
2018-04-19 10:58:02,383 [SipContainerPool : 4] TestService FINE - TestService-1.1.1.4.0 - Attempting conference Consent IVR...
2018-04-19 10:58:02,387 [SipContainerPool : 4] TestService FINE - TestService-1.1.1.4.0 - Conference complete...
2018-04-19 10:58:02,387 [SipContainerPool : 4] TestService FINE - TestService-1.1.1.4.0 - Something besides 1 or 2 was pressed...
JoelEzell
Joined: Nov 15, 2013
Messages: 780
Offline
OK, I see what is going on here. The ability to prompt/collect on an individual participant only works if Breeze has been sequenced into the call like this:

Customer(trunk)-----Breeze-----CM------Agent

In this case, Breeze clearly sees 2 call legs and knows which one is which.

In your case, the topology looks like this:

Customer(trunk)------CM--------Agent
|
|
|
Breeze

Breeze only sees one call leg, so can't distinguish customer from agent. Breeze doesn't use CTI to interact with CM to understand all participants on the call. In this case, even if it did use CTI it wouldn't matter because there's only one mixed audio stream going between CM and the Breeze AAMS.

If you need to target this specifically to the customer, you'll need to sequence Breeze into all calls, and have the agent invoke your app through some other means such as HTTP. Alternatively I guess the agent could initiate a bogus call for the sole purpose of triggering your snap-in logic on the other sequenced call, but that seems a bit hacky.

By the way, you'd mentioned that you didn't care if the agent heard the digits. If you're doing something like a credit card collection here and want to be PCI compliant, the agent can't hear the digits.
JoelEzell
Joined: Nov 15, 2013
Messages: 780
Offline
I see that my attempt to use text to show call topology didn't work quite right. The vertical line coming down to Breeze should have originated from CM rather than Customer. Sorry for not catching that sooner.
MichaelNorman
Joined: Jun 3, 2015
Messages: 448
Offline
It's ok I knew what you were going for, and I was afraid of that being the case.

As far as sequenced app vs callable app, will the base maven zephy archetype support either scenario? I'm wondering if I have something configured incorrectly inside my test application that is prohibiting sequence from working properly. Another way to ask is, what would be minimum code requirements for a service to be a sequenced app? Simply extend CallListenerAbstract and override the callIntercepted method, or is there more to it?


JoelEzell
Joined: Nov 15, 2013
Messages: 780
Offline
The archetype should work equally well for sequenced as it does for callable. I'm puzzled as to why that didn't work.

Semantics for sequenced apps are very simple. As you said, you override the callIntercepted method. You'll also need to do a call.allow() to allow the call to continue on to its destination.
MichaelNorman
Joined: Jun 3, 2015
Messages: 448
Offline
Ok, making some headway. I changed the properties.xml to be a called party service instead of calling party service. I'm now able to call the app through sequencing. You mentioned sequencing the app, then calling the service through an HTTP web service. I can see that the template has these bare methods, but I can't find any documentation on how to call your app or when to use the HttpServlet vs extends Application, and how to invoke them. Can you advise where I can find documentation on this?
MichaelNorman
Joined: Jun 3, 2015
Messages: 448
Offline
We are really close on this I think. So I have a sequenced app that just logs out the UCID. Then I also have a web service in the listener package that I've posted the code below. Basically I want to hit a service that will obtain the call reference then trigger an announcement and collect to be played to that call. I'm getting an exception of " Invalid Call type received ProxyCall"

Also am I not able to log out when hitting the web service, is that possible as well?




final Call call;
try
{
call = CallFactory.getCall(ucid);
if (call.getActiveParties().size() > 0) {

response = "found participants, count = " + call.getActiveParties().size();
response += String.format("%n");
for (Participant party : call.getActiveParties()) {
response += String.format("Participant = " + party.getAddress());
}
logger.fine(response);

logger.fine("Opening service");

logger.fine("presenting options to call");
service.presentOptionsToUser(call);
}
else
{
response = "no participants found";
logger.fine(response);
}
}
catch (Exception e)
{
response = "Call not found, exception = " + e.getMessage();
logger.fine(response);
}


void presentOptionsToUser(final Call call)
{
try
{
final MyListener mediaListener = new MyListener();
final MediaService mediaService = MediaFactory.createMediaService();

myCall = call;
final PlayItem playItem = MediaFactory
.createPlayItem()
.setSource(url)
.setInterruptible(true).setIterateCount(1);

final DigitOptions digitOptions = MediaFactory.createDigitOptions()
.setNumberOfDigits(1).setTerminationKey("#")
.setTimeout(60000);

final Participant participant = call.getCallingParty();
mediaService.promptAndCollect(participant, playItem,
digitOptions, mediaListener);



JoelEzell
Joined: Nov 15, 2013
Messages: 780
Offline
I think I know what's going on. I think that your HTTP request is landing on a different Breeze node than the one that handled the call. It is possible to decorate the HTTP message with a query parameter containing the UCID of the call that you're trying to manipulate, with the end result being that our HTTP load balancer will send the request to the right node. I have a meeting coming up and don't have time to send you specifics right now. I think our Make Call sample app demonstrates how to do this, but I'm not 100% certain. You'd have to look at the JavaScript code to see.

I should have some time a little later in the morning to check back and provide further instructions if needed.
JoelEzell
Joined: Nov 15, 2013
Messages: 780
Offline
On second thought: don't bother looking at the Make Call code. That web app creates a new call rather than manipulating an existing call. I don't think it has this concern. I'll be back with you later.
Go to:   
Mobile view