To set this up, here is the use case:
IVR system calls someone and reads a long-ish twiml prompt at them (about 251 characters)
at the end of the prompt gather user input
if a non-human answers the phone, I'm using asyncAMD callback to get the result of AMD and then leave a voice message
I am currently placing an outbound call using this code:
var call = CallResource.Create(
machineDetection: "DetectMessageEnd",
asyncAmd: "true",
asyncAmdStatusCallback: new Uri("[URI to callback]"),
asyncAmdStatusCallbackMethod: HttpMethod.Post,
twiml: new Twilio.Types.Twiml("<Response><Say>[MyMessage]</Say></Response>"),
from: new Twilio.Types.PhoneNumber(configuration["fromPhoneNumber"]),
to: new Twilio.Types.PhoneNumber(configuration["toPhoneNumber"])
);
where "MyMessage" is about 251 characters long.
The answering machine bit works wonderfully, and I'm able to leave a voice message in case a non-human answers (see my question and subsequent answer for How to leave a voicemail using Twilio AMD? for details).
However, I cannot for the life of me figure out how to prolong the asyncAMD callback long enough for the initial prompt to be finished in the case a human answers.
I've tried adding all of these optional API tuning parameters, and I still can't get it to work:
machineDetectionTimeout: 59,
machineDetectionSpeechThreshold: 6000,
machineDetectionSpeechEndThreshold: 5000,
machineDetectionSilenceTimeout: 10000,
What are my options here? Bail on asyncAMD and use blocking AMD? I need to be able to leave a voice message in case of a non-human answering, but I need to push the results of the asyncAMD invoking it's callback long enough for the initial response to be read in a human answers.
I'm going to take this approach. In the recipient answers and says "hello" (which is likely they'll say it), answeredBy will return "human", but if they don't and just listen to the initial prompt, I get "unknown" back. At this point, I'm going to treat an answeredBy "unknown" as a person answering and listening to the initial Twiml prompt.
Related
Given client code that makes an outgoing call like this:
var accountSid = configuration["accountSid"];
var authToken = configuration["authToken"];
TwilioClient.Init(accountSid, authToken);
var call = CallResource.Create(
machineDetection: "DetectMessageEnd",
asyncAmd: "true",
asyncAmdStatusCallback: new Uri("[URL]/callback/index"),
asyncAmdStatusCallbackMethod: HttpMethod.Post,
twiml: new Twilio.Types.Twiml("<Response><Say>Ahoy there!</Say></Response>"),
from: new Twilio.Types.PhoneNumber(configuration["fromPhoneNumber"]),
to: new Twilio.Types.PhoneNumber(configuration["toPhoneNumber"])
);
aka, asyncAmd is enabled and callback URL is specified, with my webhook controller action that looks like this:
[ApiController]
[Route("callback")]
public class CallbackController : TwilioController
{
[HttpPost]
[Route("index")]
public IActionResult Index()
{
var response = new VoiceResponse();
if (Request.Form.TryGetValue("AnsweredBy", out var answeredBy))
{
if (answeredBy != "human")
{
response.Say("this is the voice message");
}
}
return Content(response.ToString(), "text/xml");
}
}
why is it there is no voicemail being left?
Note: I am including the Twiml I want to say in CallResource.Create b/c I don't want a callback to get the message contents in case of a human answering.
I only need the callback performed for the results of AMD detection, and then to leave a voice message.
Do I do that with response.Say?
Thanks!
Twilio developer evangelist here.
Twilio answering machine detection can happen in synchronous or asynchronous mode. From this blog post:
With Async AMD on Twilio, AMD is done asynchronously (hence the name). When the call is answered, a call url is executed immediately, so if a person answers the call rather than voicemail, they can begin interacting with your application without any silence. Then “asynchronously”, or “in parallel”, AMD processes the call audio and determines what answered the call. When AMD processing is complete, the result (the AnsweredBy parameter) is sent to a second URL, the asyncAmdStatusCallback.
One key difference between standard AMD and async AMD is how you modify the call once receiving the AMD result. With standard AMD, you have one URL and the result is sent to this URL just like any other outbound-api call. When your URL receives the result, you can check the AnsweredBy parameter and update the call accordingly with TwiML. With Async AMD, your call is already executing TwiML and you instead need to update the call via API.
In your case you are using async AMD, but you are not updating the call via the API.
You have two options. You can choose to use synchronous AMD and you can then respond to the result using TwiML like you are doing so far.
Alternatively, you can continue to use async AMD, but instead of responding to the webhook with TwiML, use the REST API to update the call with the new TwiML or with a new webhook URL.
One thing I would look out for too. Your initial TwiML is very short, your example code shows that it sends <Response><Say>Ahoy there!</Say></Response>. It is entirely possible that this TwiML will be completed before an answering machine is detected and since it is the only TwiML for the call, the call would then hang up. You may want to consider using a longer message or pausing so that you can get the result of the AMD.
Figured I'd follow up here. Thanks #philnash for the help. You were indeed right
I was returning Twiml from the asyncAMD webhook instead of updating the call.
my very short Twiml in the initiating call was not long enough
I got through the first part and things were still failing even with Twiml that was longer:
<Response><Say>Hello there. This is a longer message that will be about as long as the real message asking you to confirm or cancel your appointment. Hopefully it's long enough!</Say></Response>
BUT, that still was not long enough! By the time the asyncAMD callback was invoked, and I tried to use CallResource.Update, the call was already ended.
I ended up dumping about 2,000 characters of lorem ipsum into the outgoing call Twiml and the asyncAMD callback using CallResource.Update worked perfectly.
I am using the following libraries to connect a bot to a Google Pub/Sub endpoint to perform a simple reply to a card click event.
Google.Apis.HangoutsChat.v1 1.34.0.1233
Google.Cloud.PubSub.V1 1.0.0-beta18
When I construct my card, everything looks normal in the UI, including the button that is supposed to raise the event.
The topic and subscription contain the default settings, following the guide here
I found the following information from the Google documentation about retries here
Responding synchronously
A bot can respond to an event synchronously by returning a
JSON-formatted message payload in the HTTP response. The deadline for
a synchronous response is 30 seconds.
A synchronous response from a bot is always posted in the thread that
generated the event to the bot.
After clicking the button, my subscriber receives 3 duplicate events. The events have the correct response with all of the right metadata, but are exact duplicates of each other, including the id of the message itself.
I don't feel there is a necessarily large delay in the response of the bot (it should happen in <1 second for this test), so I am not sure why these messages are being duplicated.
I've also tried setting the thread id for the card when responding (via the Thread property itself, or the ThreadKey property), but I always seem to get a new thread id when I post a message.
var cardMessage = MessageSender.Spaces.Messages.Create(new Message()
{
Space = new Space()
{
Name = inReplyToThisMessage.Space.Name
},
Thread = new Thread()
{
Name = inReplyToThisMessage.Thread.Name
},
Cards = new List<Card>()
{
card
},
}, inReplyToThisMessage.Space.Name);
var sendCardResult = await cardMessage.ExecuteAsync().ConfigureAwait(false);
//Thread id of sendCardResult does not match inReplyToThisMessage.Thread.Name no matter what
Interestingly enough, trying to create a new message in response to the click event causes the bot to display a "Unable to connect to bot. Try again later", but displays 3 new messages. Also, when specifying an arbitrary thread key, this key is never echoed back in the bot's response.
Make sure you are returning the main event method properly. What looks to be happening is that you are making an asynchronous call to the chat, but then the chat is looking for a response from the actual event method itself. Google will traditionally try three times before giving up (even if it doesn't take thirty seconds)
If you are indeed returning the event call correctly after you made your api request, then there is something in your code that is causing the Google Bot to think it is not getting a response, so it tries three times. Since the issue could be multi-faceted I would need to look at how you are accepting and returning the click response.
This bug has finally been fixed by Google.
Scenario: Call is received and caller is placed in a conference using the following code
var response = new TwilioResponse();
response
.Say("Please wait while we attempt to locate the person you were trying to reach.")
.DialConference(string.Format("{0} Waiting Room", digits), new { beep = "false"})
return TwiML(response);
Now I need to Dial out and connect to a mobile users phone and prompt them to accept or send caller to voice mail. I'm assuming once the DialConference is initiate the original caller is placed on hold and no more TwiML is processed. So the only way to initiate a new call is to use the RestAPI.
However I would like to use the .net helper's fluent syntax and it "should" be possible. If you know an example of doing a simultaneous Dial using the fluent syntax, it should also work with the Conference verb.
How do I initiate an outbound call while having a received call put into a conference room, all using TwiML?
Twilio evangelist here.
There isn't a way using only TwiML to put Caller A into the conference, then dial Caller B. With your example above, Twilio will stop executing the TwiML once it hits the Dial and not start again until Caller A leaves the conference.
However, if you just want call screening, then you could check out this HowTo, which shows you how without the conference:
http://www.twilio.com/docs/howto/callscreening
Hope that helps.
I have created a newsletter system that allows me to specify which members should receive the newsletter. I then loop through the list of members that meet the criteria and for each member, I generate a personalized message and send them the email asynchronously .
When I send out the email, I am using ThreadPool.QueueUserWorkItem.
For some reason, a subset of the members are getting the email twice. In my last batch, I was only sending out to 712 members, yet a total of 798 messages ended up being sent.
I am logging the messages that get sent out and I was able to tell that the first 86 members received the message twice. Here is the log (in the order the messages were sent)
No. Member Date
1. 163992 3/8/2012 12:28:13 PM
2. 163993 3/8/2012 12:28:13 PM
...
85. 164469 3/8/2012 12:28:37 PM
86. 163992 3/8/2012 12:28:44 PM
87. 163993 3/8/2012 12:28:44 PM
...
798. 167691 3/8/2012 12:32:36 PM
Each member should receive the newsletter once, however, as you can see member 163992 receives message #1 and #86; member 163993 received message #2 and #87; and so on.
The other thing to note is that there was a 7 second delay between sending message #85 and #86.
I have reviewed the code several times and ruled out pretty much all of the code as being the cause of it, except for possibly the ThreadPool.QueueUserWorkItem.
This is the first time I work with ThreadPool, so I am not that familiar with it. Is it possible to have some sort of race-condition that is causing this behavior?
=== --- Code Sample --- ===
foreach (var recipient in recipientsToEmail)
{
_emailSender.SendMemberRegistrationActivationReminder(eventArgs.Newsletter, eventArgs.RecipientNotificationInfo, previewEmail: string.Empty);
}
public void SendMemberRegistrationActivationReminder(DomainObjects.Newsletters.Newsletter newsletter, DomainObjects.Members.MemberEmailNotificationInfo recipient, string previewEmail)
{
//Build message here .....
//Send the message
this.SendEmailAsync(fromAddress: _settings.WebmasterEmail,
toAddress: previewEmail.IsEmailFormat()
? previewEmail
: recipientNotificationInfo.Email,
subject: emailSubject,
body: completeMessageBody,
memberId: previewEmail.IsEmailFormat()
? null //if this is a preview message, do not mark it as being sent to this member
: (int?)recipientNotificationInfo.RecipientMemberPhotoInfo.Id,
newsletterId: newsletter.Id,
newsletterTypeId: newsletter.NewsletterTypeId,
utmCampaign: utmCampaign,
languageCode: recipientNotificationInfo.LanguageCode);
}
private void SendEmailAsync(string fromAddress, string toAddress, string subject, MultiPartMessageBody body, int? memberId, string utmCampaign, string languageCode, int? newsletterId = null, DomainObjects.Newsletters.NewsletterTypeEnum? newsletterTypeId = null)
{
var urlHelper = UrlHelper();
var viewOnlineUrlFormat = urlHelper.RouteUrl("UtilityEmailRead", new { msgid = "msgid", hash = "hash" });
ThreadPool.QueueUserWorkItem(state => SendEmail(fromAddress, toAddress, subject, body, memberId, newsletterId, newsletterTypeId, utmCampaign, viewOnlineUrlFormat, languageCode));
}
Are you sure the query you are running to get the list of members to send the email to does not have duplicates in it? Are you joining to another table? What you could do is:
List<DomainObjects.Members.MemberEmailNotificationInfo> list = GetListFromDatabase();
list = list.Distinct().ToList();
Having 800+ threads running on the server is not a good practice!
Although you are using a ThreadPool, threads are being queued on the server and run whenever old threads return back to the pool and release the resource. This might take several minutes on the server and many situations like Race Conditions or Concurrencies might happen during that time.
You could instead queue one work item, over one protected list:
lock (recipientsToEmail)
{
ThreadPool.QueueUserWorkItem(t =>
{
// enumerate recipientsToEmail and send email
});
}
Things to check (I'm assuming you have a way to mock the sending of emails):
Is the number of duplicate emails always exactly the same? What if you increase/decrease the number of input values? Is it always the same user IDs which are duplicated?
Is SendEmail() doing anything of significance? (I don't see your code for it)
Is there a reason that you aren't using the framework's SendAsync() method?
Do you get the same behavior without multithreading?
For what it's worth, sending bulk email from your own site--even when it is completely legitimate--is not always worth the trouble. Spam blocking services are very aggressive and you don't want your domain to end up blacklisted. Third party services remove that risk, provide many tools, and also manage this part of the process for you.
If this code:
foreach (var recipient in recipientsToEmail)
{
_emailSender.SendMemberRegistrationActivationReminder(eventArgs.Newsletter
,eventArgs.RecipientNotificationInfo, previewEmail: string.Empty);
}
matches what you are actually doing... you have an obvious bug. namely that you are doing a foreach but not using the returned value, so you will send the same email to eventArgs.RecipientNotificationInfo for each entry in recipientsToEmail.
One common cause of tasks getting performed twice in code where you queue the task to a background thread is faulty error handling. You might double-check your code to make sure that if there's an error that you don't always retry, regardless of the type of error (some errors warrant a retry; others don't).
Having said that, the code you've posted doesn't include enough information to definitively answer your question; there are many possibilities.
FWIW, are you aware that the SmtpClient class has a SendAsync() method, that doesn't require the use of a separate worker thread?
In your code sample, we can't see where your logging takes place.
Maybe the mehod that sends the email erronously thought that something wrong occured then, the system tried again, which could result in an email sent twice.
Also, as written in other answers and comment, I would check again that I don't get duplicated entries in the list of recipients, and test it in a non-parallel context.
Background
Hi.
I write a program that analyzes the packets for specific words contained therein. I need to analyze outgoing email, jabber, ICQ. If the words are found, the packet is blocked.I did it, but I have a problem with the files and sending email through the web.
Problems
Simple code:
while (Ndisapi.ReadPacket(hNdisapi, ref Request))
{
// some work
switch (protocol)
{
//....
case "HTTP":
// parse packet(byte[])
HTTP.HttpField field = HTTP.ParseHttp(ret);
if (field != null && field.Method == HTTP.HttpMethod.POST)
{
// analyze packet and drop if needed
DoWork();
}
}
The problem is the following. For example, I attach to email the file of 500 KB. The file will be split approximately in 340 packets. In the code above, DoWork() only for first packet will be executed.
Ok, then I need to restore session completely and pass whole session to DoWork(). I did it. But I can't wait while session is finished, because other packet( http, arp, all packets) will be suspended (And after a couple of minutes the Internet is disconnected).
Therefore, the first question:
How to solve this problem (may be advice for design program)?
Now the email, suppose this code:
switch (protocol)
{
//....
case "HTTP":
// parse packet(byte[])
var httpMimeMessage = Mime.Parse(ret);
// analyze packet and drop if needed
DoSomeWork();
break;
}
For example, we are looking for word "Finance". Then, if we open any website and there will be a word finance then packet is blocked.
Second question: How do I determine that this is the e-mail?
Thanks and sorry for my English.
To be able to analyze more than one packet/stream at the same time, you'll need to refactor your solution to use threading or some other form of multitasking and since your task appears to be both compute and io-intensive, you'll probably want to take a hard look at how to leverage event-handling at the operating system level (select, epoll, or the equivalent for your target platform).
And to answer your second question regarding email, you'll need to be able to identify and track the tcp session used to deliver email messages from client to server, assuming the session hasn't been encrypted.
As I'm sure you already know, the problem you're trying to solve is a very complicated one, requiring very specialized skills like realtime programming, deep knowledge of networking protocols, etc.
Of course, there are several "deep packet inspection" solutions out there already that do all of this for you, (typically used by public companies to fulfill regulatory requirements like Sarbanes-Oxley), but they are quite expensive.