Im trying to transcribe vocal response of a caller and programmaticaly read out the users vocal response via twilio.
So, when user initially calls to the twilio number the call gets hooked to below action method (looked in to https://www.twilio.com/docs/voice/twiml/record?code-sample=code-record-a-voicemail&code-language=C%23&code-sdk-version=5.x) of the ASP.NET MVC Application.
[HttpPost]
public TwiMLResult Welcome()
{
var response = new VoiceResponse();
try
{
response.Say("Please say your user Id, example ABC123, \n and press star when done", Say.VoiceEnum.Alice, null, Say.LanguageEnum.EnGb);
// record and transcribe users voice
response.Record(
transcribe: true,
transcribeCallback: new Uri("https://35eb31e3.ngrok.io/Ivr/HandleTranscribedVrn"),
finishOnKey: "*");
response.Say("I did not receive a recording");
}
catch (Exception e)
{
ErrorLog.LogError(e, "Error within ivr/Welcome");
response = RejectCall();
}
return TwiML(response);
}
Note - https://35eb31e3.ngrok.io/Ivr/HandleTranscribedVrn is the ngRok tunneled public URL to call back method.
So, Im trying to record the users voice input after user says his/her user Id and then presses * key. So, after pressing * , I expect twilio to transcribe and respond to below callback action method (https://35eb31e3.ngrok.io/Ivr/HandleTranscribedVrn) with the transcription text and other transcribed information.
[HttpPost]
public TwiMLResult HandleTranscribedVrn()
{
var response = new VoiceResponse();
try
{
// get the transcribed result - https://www.twilio.com/docs/voice/twiml/record#transcribe
var result = new TranscribedResult
{
TranscriptionSid = Request.Params["TranscriptionSid"],
TranscriptionText = Request.Params["TranscriptionText"],
TranscriptionUrl = Request.Params["TranscriptionUrl"],
TranscriptionStatus = Request.Params["TranscriptionStatus"],
RecordingSid = Request.Params["RecordingSid"],
RecordingUrl = Request.Params["RecordingUrl"],
AccountSid = Request.Params["AccountSid"]
};
// reading the transcibed result
response.Say("You said,\n {0}", result.TranscriptionText);
// done
response.Say("Good Bye", Say.VoiceEnum.Alice, null, Say.LanguageEnum.EnGb);
}
catch (Exception e)
{
ErrorLog.LogError(e, "Error within ivr/HandleTranscribedVrn");
response.Say(ConversationHelper.NothingReceived, ConversationHelper.SpeakVoice, 1, ConversationHelper.SpeakLanguage);
}
return TwiML(response);
}
In brief, I want above callback action to grab the transcript to user voice input and read it out, like
You said, {Users Voice Transcript - example - abc123}, Good Bye
The Problem
When user calls to twilio number it executes Welcome() action controller, and says
"Please say your user Id, example ABC123, \n and press star when done"
The user says his/her user Id - EFG456 and presses * key as usual.
Then it again says (infinitely till user disconnects call), without going to transcribed call back action - HandleTranscribedVrn - "Please say your user Id, example ABC123, \n and press star when done"
Any help will be much appreciated.
With the help of the Twilio support we managed to find this solution. So, instead of <record> we have to use <gather> feature provided by Twilio. On gather we could either use speech, DTMF tones (keyboard inputs) or both.
The gather complete callback method will be executed when the speech transcription is ready. More information can be found on https://www.twilio.com/docs/voice/twiml/gather
Below is the sample code. Hope it would be helpful to anyone who faces a similar issue.
[HttpPost]
public ActionResult Welcome()
{
var response = new VoiceResponse();
try
{
var gatherOptionsList = new List<Gather.InputEnum>
{
Gather.InputEnum.Speech,
//Gather.InputEnum.Dtmf
};
var gather = new Gather(
input: gatherOptionsList,
timeout: 60,
finishOnKey:"*",
action: Url.ActionUri("OnGatherComplete", "Ivr")
);
gather.Say("Please say \n", Say.VoiceEnum.Alice, 1, Say.LanguageEnum.EnGb);
response.Append(gather);
}
catch (Exception e)
{
ErrorLog.LogError(e, "Error within ivr/Welcome");
}
return TwiML(response);
}
[HttpPost]
public TwiMLResult OnGatherComplete(string SpeechResult, double Confidence)
{
var response = new VoiceResponse();
try
{
var identifyingConfidence = Math.Round(Confidence * 100, 2);
var transcript = $"You said {SpeechResult} with Confidence {identifyingConfidence}.\n Good Bye";
var say = new Say(transcript);
response.Append(say);
}
catch (Exception e)
{
ErrorLog.LogError(e, "Error within ivr/OnGatherComplete");
}
return TwiML(response);
}
Related
I'm fairly new coding and thought I'd take a shot at making a discord bot. So far it's been easy enough to follow and I've started to try to make commands for my bot. But I can't figure out how to get my bot to DM a person I ping ex. !warn #person (reason). I've tried looking it up and can't find out how.
[Command("warn")]
[RequireUserPermission(GuildPermission.KickMembers, ErrorMessage = "You don't have the persmission ''warn_member''!")]
public async Task WarnMember(IGuildUser user = null, [Remainder] string reason = null)
{
if (user == null)
{
await ReplyAsync("Please Specify A User"); return;
}
if (reason == null) reason = "Not Specified";
this is where I'm trying to send the DM but it sends it to the person who ran the command and not who I pinged
await Context.User.SendMessageAsync("You have been warned for " + reason);
var EmbedBuilder = new EmbedBuilder()
.WithDescription($":white_check_mark: {user.Mention} was warned\n**Reason **{reason}")
.WithFooter(footer =>
{
footer
.WithText("User Warn Log");
});
Embed embed = EmbedBuilder.Build();
await ReplyAsync(embed: embed);
}
Context.User always refers to the user who is executing the command. To send a message to the person mentioned in the command, you need to call the SendMessageAsync() function on your user argument.
await user.SendMessageAsync(...)
Keep in mind that users can have direct messages disabled for the server your bot is in, this could result in an exception.
if (OkToCall)
{
if (CallCount == 0)
{
voiceResponse.Say(sayMessage);
voiceResponse.Dial(number: number1, timeout: 24, action: new Uri("/OnCall/?Location=" + Location + "&CallCount=1", UriKind.Relative));
}
else if (CallCount == 1)
{
sayMessage = "We're sorry. There was no answer. You are now being redirected to the on call manager. One moment. ";
voiceResponse.Say(sayMessage);
voiceResponse.Dial(number: number2, timeout: 24, action: new Uri("/OnCall/?Location=" + Location + "&CallCount=2", UriKind.Relative));
}
else
{
sayMessage = "We're sorry. We were unable to connect your call. Please try again later.";
voiceResponse.Say(sayMessage);
voiceResponse.Hangup();
}
}
else
{
sayMessage = "We're sorry. An error occured while redirecting your call. Please try again later.";
voiceResponse.Say(sayMessage);
voiceResponse.Hangup();
}
The process above works great, except if the callee hangs up before the caller does, then the caller is routed to the next person. I want to check call status to see if it needs to be routed or not. How do I do that?
The general logic is covered in this Twilio Function.
Implement voicemail
/**
* Voicemail Action URL
*
* This Function is used in as the action URL in a Dial. If the call isn't answered or the line is busy,
* the call is forwarded to a specified Voicemail URL.
*/
exports.handler = function(context, event, callback) {
// set-up the variables
// generate the TwiML to tell Twilio how to forward this call
let twiml = new Twilio.twiml.VoiceResponse();
if (event.DialCallStatus === "completed") {
twiml.hangup();
}
else {
twiml.redirect('<URL-TO-VOICEMAIL-TWIML>');
}
// return the TwiML
callback(null, twiml);
};
I’m relatively new to c# and working with API’s. I’ve created a simple windows form in VS and I’m trying to connect t a rest service to retrieve information based on a search condition (e.g ID number) and display everything in a data grid. I’ve been looking for examples of what I’m trying to achieve with very little success. The idea is to enable a user to enter an ID number inside a text box and click a “search” button which will then connect to the rest service and retrieve all the information related to that specific ID number and display it all in a data grid with column names. Is this possible? Can anyone advise me on how to establish my connection to the rest service?
Try this. Path1 is your connection:
The Class ApiResult is just a class with a List of ArticleApiModel.
public static List<ArticleApiModel> GetArticles (int id)
{
try
{
var task = Task<List<ArticleApiModel>>.Run(async () =>
{
using (HttpClient client = new HttpClient())
{
var response = await client.GetAsync(path1 + "/api/articles/",id);
if (response != null)
{
var jsonString = await response.Content.ReadAsStringAsync();
var result = JsonConvert.DeserializeObject<ApiResult>(jsonString);
return result.Result;
}
}
return null;
});
task.Wait();
return task.Result;
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
return null;
}
I am using below code to place a call from agent(Web Browser interface) to customer(phone).
var response = new VoiceResponse();
var dial = new Dial(callerId: callerId,action : "https://ba2d9819.ngrok.io/voice/CallEnd");
if (Regex.IsMatch(to, "^[\\d\\+\\-\\(\\) ]+$"))
{
dial.Number(to);
}
else
{
dial.Client(to);
}
response.Dial(dial);
This works fine when customer picks up the call. I have a requirement to play a prerecorded audio message when customer do not pickup the call and it goes to voicemail. How can i achieve this?
I am working on an ASP.NET Webform project (legacy code).On my button_click event i am sending sms message to all the datas populated in this.
var customerSMS = BusinessLayer.SMS.SmsSetup.GetAllCustomerSMS(OfficeId);
This takes around 15seconds to do all the computing and get the data(1000rows)
from the Db.And for each data it runs through the loop and does validation and
sends the sms and it does take time.I want to do this task in background and
redirect the user to the index page and the background process continues till it
gets out of the loop.I am new to this and still learning this beautiful
language C#.I did go through this amazing Asynchronous Programming async/await
and Multithreading approach and got hold of it only in simple WindowsForm
applications.Any reference/code snippet/best approach with a simple explanation for my case would be helpful.
My button click event code :
protected void ReturntoDashboard_Click(object sender, EventArgs e)
{
sms = Everest.Net.BusinessLayer.SMS.SmsSetup.GetSmsSetUp(OfficeId);
if (sms.EnableSmsData && sms.SmsCount > 0)
{
#region Loan Section
var smsLoan = Everest.Net.BusinessLayer.SMS.SmsSetup.GetLoanId(s.Sms_AccountNumber);
var loanId =
BusinessLayer.SMS.SmsSetup.GetLoanIdValue(s.Sms_AccountNumber);
var dateexceeded =
BusinessLayer.SMS.SmsSetup.IsDateExceeded(loanId);
if (smsLoan != null && dateexceeded == true)
{
foreach (Common.SMS.SMSSetup sm in smsLoan)
{
var smsClosingBalanceLoan = BusinessLayer.SMS.SmsSetup.GetAmountForLoanAlert( sm.LoanId,
BusinessLayer.Core.DateConversion
.GetCurrentServerDate()
.AddDays(sms.DaysbeforeLoanalerts).ToString());
if (smsClosingBalanceLoan != null)
{
if (smsClosingBalanceLoan.LoanAmountToPay > 0)
{
int smsSentAlertCount = sms.LoanAlertCount;
var logCount = BusinessLayer.SMS.SmsSetup.GetLoanSmsAlertSentCount(DateTime.Now.AddDays(-smsSentAlertCount).ToString("yyyy-MM-dd"), DateTime.Now.ToString("yyyy-MM-dd"), sm.LoanAccountNumber);
if (logCount < smsSentAlertCount)
{
smsLog = new Everest.Net.Common.SMS.SMSSetup();
finalMessage = "Dear Member, Your Loan accnt " + sm.LoanAccountNumber + " with Principal"+ "+" + "Int Amnt: Rs." + smsClosingBalanceLoan.LoanAmountToPay + " need to be payed.Thank You," + officeName.OfficeName;
smsLog.LogServiceType = "Loan";
smsLog.LogSmsType = s.Sms_SmsType;
smsLog.LogSmsMessage = finalMessage;
smsLog.LogCustomerId = s.CustomerId.ToString();
smsLog.LogAccountNumber = s.Sms_AccountNumber;
smsLog.LogAccountType = s.Sms_AccountType;
smsLog.LogSmsSentDate = BusinessLayer.Core.DateConversion.GetCurrentServerDate();
smsLog.LogSmsFailedDate = "";
smsLog.LogSentStatus = true;
smsLog.LogUserId = UserId;
smsLog.LogSmsFailedMessage = "";
try
{
var result = Everest.Net.BusinessLayer.SMS.smsParameters.SendSMS(sms.FromNum, sms.Token, sms.Url, cellNum, finalMessage);
}
catch (Exception ex)
{
smsLog.LogSmsFailedDate = System.DateTime.Now.ToString("MM/dd/yyyy HHmmss");
smsLog.LogSentStatus = false;
smsLog.LogSmsFailedMessage = ex.Message;
Everest.Net.BusinessLayer.SMS.SmsSetup.InsertSMSLog(smsLog);
}
sms = Everest.Net.BusinessLayer.SMS.SmsSetup.GetSmsSetUp(OfficeId);
sms.SmsCount = sms.SmsCount - 1;
Everest.Net.BusinessLayer.SMS.SmsSetup.UpdateSmsSetup(sms);
Everest.Net.BusinessLayer.SMS.SmsSetup.InsertSMSLog(smsLog);
}
}
}
}
}
}
}
}
catch (Exception ex)
The ideal solution would remove the responsibility of sending the SMS from the web application itself. Instead, the web application should create a database record containing the message and recipient addresses, and a separate background job (e.g. a Windows Service) should poll the database and send SMS messages when neeeded. This is the best solution in terms of fault tolerance and auditability, because there is a permanent record of the messaging job which can be resumed if the system fails.
That being said, maybe you don't want to go to all that trouble. If you feel strongly that you wish to send the SMS directly from the ASP.NET application, you will need to create a Task and queue it to run using QueueBackgroundWorkitem. You will need to refactor your code a bit.
Move all the logic for sending the SMS into a separate function that accepts all the information needed as parameters. For example,
static void SendSMS(string[] addresses, string messagetext)
{
//Put your SMS code here
}
When you need to call the function, queue it as a background item
HostingEnvironment.QueueBackgroundWorkItem(a => SendSMS(addresses, messageText));
If your worker task needs to access its own cancellation token (e.g. if it is supposed to loop until cancelled), it is passed as an argument to the lambda expression. So you could modify the prototype
static void SendSMS(string[] addresses, string messagetext, CancellationToken token)
{
while (!token.IsCancellationRequested)
{
//Put your code here
}
}
and pass it thus:
HostingEnvironment.QueueBackgroundWorkItem(token => SendSMS(addresses, messageText, token));
Placing the task in the background queue ensures that ASP.NET keeps track of the thread, doesn't try to garbage collect it, and shuts it down properly when the application pool needs to shut down.
After queuing the background operation, your page can render is content per usual and conclude the HTTP response while the task continues to execute.