Skip to content

Commit a37a80f

Browse files
authored
Merge pull request #611 from wcao-lessen/feature/twilio
improve the workflow
2 parents 84ae258 + e6f03fc commit a37a80f

File tree

4 files changed

+59
-7
lines changed

4 files changed

+59
-7
lines changed

src/Plugins/BotSharp.Plugin.Twilio/Controllers/TwilioVoiceController.cs

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,13 @@ public TwiMLResult InitiateConversation(VoiceRequest request, [FromQuery] string
3636
string conversationId = $"TwilioVoice_{request.CallSid}";
3737
var twilio = _services.GetRequiredService<TwilioService>();
3838
var url = $"twilio/voice/{conversationId}/receive/0?states={states}";
39-
var response = twilio.ReturnInstructions(new List<string> { "twilio/welcome.mp3" }, url, true);
39+
var response = twilio.ReturnNoninterruptedInstructions(new List<string> { "twilio/welcome.mp3" }, url, true);
4040
return TwiML(response);
4141
}
4242

4343
[ValidateRequest]
4444
[HttpPost("twilio/voice/{conversationId}/receive/{seqNum}")]
45-
public async Task<TwiMLResult> ReceiveCallerMessage([FromRoute] string conversationId, [FromRoute] int seqNum, [FromQuery] string states, VoiceRequest request)
45+
public async Task<TwiMLResult> ReceiveCallerMessage([FromRoute] string conversationId, [FromRoute] int seqNum, [FromQuery] string states, [FromQuery] int attempts, VoiceRequest request)
4646
{
4747
var twilio = _services.GetRequiredService<TwilioService>();
4848
var messageQueue = _services.GetRequiredService<TwilioMessageQueue>();
@@ -81,7 +81,21 @@ public async Task<TwiMLResult> ReceiveCallerMessage([FromRoute] string conversat
8181
}
8282
else
8383
{
84-
response = twilio.ReturnInstructions(null, $"twilio/voice/{conversationId}/receive/{seqNum}?states={states}", true);
84+
if (attempts >= 3)
85+
{
86+
var speechPaths = new List<string>();
87+
if (seqNum == 0)
88+
{
89+
speechPaths.Add("twilio/welcome.mp3");
90+
}
91+
else
92+
{
93+
var lastRepy = await sessionManager.GetAssistantReplyAsync(conversationId, seqNum - 1);
94+
speechPaths.Add($"twilio/voice/speeches/{conversationId}/{lastRepy.SpeechFileName}");
95+
}
96+
response = twilio.ReturnInstructions(speechPaths, $"twilio/voice/{conversationId}/receive/{seqNum}?states={states}", true);
97+
}
98+
response = twilio.ReturnInstructions(null, $"twilio/voice/{conversationId}/receive/{seqNum}?states={states}&attempts={++attempts}", true);
8599
}
86100

87101
return TwiML(response);
@@ -90,7 +104,7 @@ public async Task<TwiMLResult> ReceiveCallerMessage([FromRoute] string conversat
90104
[ValidateRequest]
91105
[HttpPost("twilio/voice/{conversationId}/reply/{seqNum}")]
92106
public async Task<TwiMLResult> ReplyCallerMessage([FromRoute] string conversationId, [FromRoute] int seqNum,
93-
[FromQuery] string states, [FromQuery] string play, VoiceRequest request)
107+
[FromQuery] string states, VoiceRequest request)
94108
{
95109
var nextSeqNum = seqNum + 1;
96110
var sessionManager = _services.GetRequiredService<ITwilioSessionManager>();
@@ -107,6 +121,7 @@ public async Task<TwiMLResult> ReplyCallerMessage([FromRoute] string conversatio
107121
if (indication != null)
108122
{
109123
var speechPaths = new List<string>();
124+
int segIndex = 0;
110125
foreach (var text in indication.Split('|'))
111126
{
112127
var seg = text.Trim();
@@ -119,12 +134,14 @@ public async Task<TwiMLResult> ReplyCallerMessage([FromRoute] string conversatio
119134
var textToSpeechService = CompletionProvider.GetTextToSpeech(_services, "openai", "tts-1");
120135
var fileService = _services.GetRequiredService<IFileStorageService>();
121136
var data = await textToSpeechService.GenerateSpeechFromTextAsync(seg);
122-
var fileName = $"indication_{seqNum}.mp3";
137+
var fileName = $"indication_{seqNum}_{segIndex}.mp3";
123138
await fileService.SaveSpeechFileAsync(conversationId, fileName, data);
124139
speechPaths.Add($"twilio/voice/speeches/{conversationId}/{fileName}");
140+
segIndex++;
125141
}
126142
}
127143
response = twilio.ReturnInstructions(speechPaths, $"twilio/voice/{conversationId}/reply/{seqNum}?states={states}", true);
144+
await sessionManager.RemoveReplyIndicationAsync(conversationId, seqNum);
128145
}
129146
else
130147
{

src/Plugins/BotSharp.Plugin.Twilio/Services/ITwilioSessionManager.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,6 @@ public interface ITwilioSessionManager
1111
Task<List<string>> RetrieveStagedCallerMessagesAsync(string conversationId, int seqNum);
1212
Task SetReplyIndicationAsync(string conversationId, int seqNum, string indication);
1313
Task<string> GetReplyIndicationAsync(string conversationId, int seqNum);
14+
Task RemoveReplyIndicationAsync(string conversationId, int seqNum);
1415
}
1516
}

src/Plugins/BotSharp.Plugin.Twilio/Services/TwilioService.cs

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,8 @@ public VoiceResponse ReturnInstructions(List<string> speechPaths, string callbac
7676
},
7777
Action = new Uri($"{_settings.CallbackHost}/{callbackPath}"),
7878
SpeechModel = Gather.SpeechModelEnum.PhoneCall,
79-
SpeechTimeout = timeout > 0 ? timeout.ToString() : "3",
80-
Timeout = timeout > 0 ? timeout : 3,
79+
SpeechTimeout = timeout > 0 ? timeout.ToString() : "2",
80+
Timeout = timeout > 0 ? timeout : 2,
8181
ActionOnEmptyResult = actionOnEmptyResult
8282
};
8383
if (speechPaths != null && speechPaths.Any())
@@ -91,6 +91,33 @@ public VoiceResponse ReturnInstructions(List<string> speechPaths, string callbac
9191
return response;
9292
}
9393

94+
public VoiceResponse ReturnNoninterruptedInstructions(List<string> speechPaths, string callbackPath, bool actionOnEmptyResult, int timeout = 2)
95+
{
96+
var response = new VoiceResponse();
97+
if (speechPaths != null && speechPaths.Any())
98+
{
99+
foreach (var speechPath in speechPaths)
100+
{
101+
response.Play(new Uri($"{_settings.CallbackHost}/{speechPath}"));
102+
}
103+
}
104+
var gather = new Gather()
105+
{
106+
Input = new List<Gather.InputEnum>()
107+
{
108+
Gather.InputEnum.Speech,
109+
Gather.InputEnum.Dtmf
110+
},
111+
Action = new Uri($"{_settings.CallbackHost}/{callbackPath}"),
112+
SpeechModel = Gather.SpeechModelEnum.PhoneCall,
113+
SpeechTimeout = timeout > 0 ? timeout.ToString() : "2",
114+
Timeout = timeout > 0 ? timeout : 2,
115+
ActionOnEmptyResult = actionOnEmptyResult
116+
};
117+
response.Append(gather);
118+
return response;
119+
}
120+
94121
public VoiceResponse HangUp(string speechPath)
95122
{
96123
var response = new VoiceResponse();

src/Plugins/BotSharp.Plugin.Twilio/Services/TwilioSessionManager.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,5 +59,12 @@ public async Task<string> GetReplyIndicationAsync(string conversationId, int seq
5959
var key = $"{conversationId}:Indication:{seqNum}";
6060
return await db.StringGetAsync(key);
6161
}
62+
63+
public async Task RemoveReplyIndicationAsync(string conversationId, int seqNum)
64+
{
65+
var db = _redis.GetDatabase();
66+
var key = $"{conversationId}:Indication:{seqNum}";
67+
await db.KeyDeleteAsync(key);
68+
}
6269
}
6370
}

0 commit comments

Comments
 (0)