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

172 lines
6.6 KiB
C#
Raw Normal View History

2024-08-08 19:54:32 +00:00
using BotSharp.Abstraction.Files;
using BotSharp.Abstraction.Routing;
using BotSharp.Core.Infrastructures;
using BotSharp.Plugin.Twilio.Models;
using BotSharp.Plugin.Twilio.Services;
2023-11-09 20:02:57 +00:00
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
2023-12-06 22:55:55 +00:00
using System.IdentityModel.Tokens.Jwt;
2023-11-09 20:02:57 +00:00
namespace BotSharp.Plugin.Twilio.Controllers;
[AllowAnonymous]
2024-08-08 19:54:32 +00:00
[Route("[controller]")]
2023-11-09 20:02:57 +00:00
public class TwilioVoiceController : TwilioController
{
private readonly TwilioSetting _settings;
private readonly IServiceProvider _services;
public TwilioVoiceController(TwilioSetting settings, IServiceProvider services)
{
_settings = settings;
_services = services;
}
2023-12-06 22:55:55 +00:00
[Authorize]
[HttpGet("/twilio/token")]
public Token GetAccessToken()
{
var twilio = _services.GetRequiredService<TwilioService>();
var accessToken = twilio.GetAccessToken();
var jwt = new JwtSecurityTokenHandler().ReadJwtToken(accessToken);
return new Token
{
AccessToken = accessToken,
ExpireTime = jwt.Payload.Exp.Value,
TokenType = "Bearer",
Scope = "api"
};
}
2023-11-09 20:02:57 +00:00
[HttpPost("/twilio/voice/welcome")]
public async Task<TwiMLResult> StartConversation(VoiceRequest request)
{
string sessionId = $"TwilioVoice_{request.CallSid}";
2023-12-06 22:55:55 +00:00
var twilio = _services.GetRequiredService<TwilioService>();
var response = twilio.ReturnInstructions("Hello, how may I help you?");
2023-11-09 20:02:57 +00:00
return TwiML(response);
}
[HttpPost("/twilio/voice/{agentId}")]
public async Task<TwiMLResult> ReceivedVoiceMessage([FromRoute] string agentId, VoiceRequest input)
{
string sessionId = $"TwilioVoice_{input.CallSid}";
2024-03-27 18:50:27 +00:00
var inputMsg = new RoleDialogModel(AgentRole.User, input.SpeechResult);
2023-11-09 20:02:57 +00:00
var conv = _services.GetRequiredService<IConversationService>();
2024-03-27 18:50:27 +00:00
var routing = _services.GetRequiredService<IRoutingService>();
routing.Context.SetMessageId(sessionId, inputMsg.MessageId);
conv.SetConversationId(sessionId, new List<MessageState>
2023-11-09 20:02:57 +00:00
{
2024-03-27 18:50:27 +00:00
new MessageState("channel", ConversationChannel.Phone),
new MessageState("calling_phone", input.DialCallSid)
2023-11-09 20:02:57 +00:00
});
2023-12-06 22:55:55 +00:00
var twilio = _services.GetRequiredService<TwilioService>();
2023-11-09 20:02:57 +00:00
VoiceResponse response = default;
2023-11-27 03:04:48 +00:00
var result = await conv.SendMessage(agentId,
2024-03-27 18:50:27 +00:00
inputMsg,
2024-03-16 14:43:00 +00:00
replyMessage: null,
2023-11-27 03:04:48 +00:00
async msg =>
{
2023-12-06 22:55:55 +00:00
response = twilio.ReturnInstructions(msg.Content);
if (msg.FunctionName == "conversation_end")
{
response = twilio.HangUp(msg.Content);
}
2023-11-27 03:04:48 +00:00
}, async functionExecuting =>
{
}, async functionExecuted =>
{
});
2023-11-09 20:02:57 +00:00
return TwiML(response);
}
2024-08-08 19:54:32 +00:00
2024-08-09 03:28:39 +00:00
[HttpPost("start")]
2024-08-08 19:54:32 +00:00
public TwiMLResult InitiateConversation(VoiceRequest request)
{
if (request?.CallSid == null) throw new ArgumentNullException(nameof(VoiceRequest.CallSid));
string sessionId = $"TwilioVoice_{request.CallSid}";
var twilio = _services.GetRequiredService<TwilioService>();
2024-08-09 03:28:39 +00:00
var url = $"twiliovoice/{sessionId}/send/0";
var response = twilio.ReturnInstructions("twilio/welcome.mp3", url, false);
2024-08-08 19:54:32 +00:00
return TwiML(response);
}
2024-08-09 03:28:39 +00:00
[HttpPost("{sessionId}/send/{seqNum}")]
2024-08-08 19:54:32 +00:00
public async Task<TwiMLResult> SendCallerMessage([FromRoute] string sessionId, [FromRoute] int seqNum, VoiceRequest request)
{
var twilio = _services.GetRequiredService<TwilioService>();
var messageQueue = _services.GetRequiredService<TwilioMessageQueue>();
var sessionManager = _services.GetRequiredService<ITwilioSessionManager>();
2024-08-09 03:28:39 +00:00
var url = $"twiliovoice/{sessionId}/reply/{seqNum}";
2024-08-08 19:54:32 +00:00
var messages = await sessionManager.RetrieveStagedCallerMessagesAsync(sessionId, seqNum);
if (!string.IsNullOrWhiteSpace(request.SpeechResult))
{
messages.Add(request.SpeechResult);
}
var messageContent = string.Join("\r\n", messages);
VoiceResponse response;
if (!string.IsNullOrWhiteSpace(messageContent))
{
var callerMessage = new CallerMessage()
{
SessionId = sessionId,
SeqNumber = seqNum,
Content = messageContent,
From = request.From
};
await messageQueue.EnqueueAsync(callerMessage);
2024-08-09 03:28:39 +00:00
response = twilio.ReturnInstructions("twilio/holdon.mp3", url, true);
2024-08-08 19:54:32 +00:00
}
else
{
2024-08-09 03:28:39 +00:00
response = twilio.HangUp("twilio/holdon.mp3");
2024-08-08 19:54:32 +00:00
}
return TwiML(response);
}
2024-08-09 03:28:39 +00:00
[HttpPost("{sessionId}/reply/{seqNum}")]
2024-08-08 19:54:32 +00:00
public async Task<TwiMLResult> ReplyCallerMessage([FromRoute] string sessionId, [FromRoute] int seqNum, VoiceRequest request)
{
var nextSeqNum = seqNum + 1;
var sessionManager = _services.GetRequiredService<ITwilioSessionManager>();
var twilio = _services.GetRequiredService<TwilioService>();
if (request.SpeechResult != null)
{
await sessionManager.StageCallerMessageAsync(sessionId, nextSeqNum, request.SpeechResult);
}
var reply = await sessionManager.GetAssistantReplyAsync(sessionId, seqNum);
VoiceResponse response;
if (string.IsNullOrEmpty(reply))
{
2024-08-09 03:28:39 +00:00
response = twilio.ReturnInstructions(null, $"twiliovoice/{sessionId}/reply/{seqNum}", true);
2024-08-08 19:54:32 +00:00
}
else
{
var textToSpeechService = CompletionProvider.GetTextToSpeech(_services, "openai", "tts-1");
2024-08-09 03:28:39 +00:00
var fileService = _services.GetRequiredService<IFileStorageService>();
2024-08-08 19:54:32 +00:00
var data = await textToSpeechService.GenerateSpeechFromTextAsync(reply);
var fileName = $"{seqNum}.mp3";
await fileService.SaveSpeechFileAsync(sessionId, fileName, data);
2024-08-09 03:28:39 +00:00
response = twilio.ReturnInstructions($"twiliovoice/speeches/{sessionId}/{fileName}", $"twiliovoice/{sessionId}/send/{nextSeqNum}", true);
2024-08-08 19:54:32 +00:00
}
return TwiML(response);
}
2024-08-09 03:28:39 +00:00
[HttpGet("speeches/{conversationId}/{fileName}")]
2024-08-08 19:54:32 +00:00
public async Task<FileContentResult> RetrieveSpeechFile([FromRoute] string conversationId, [FromRoute] string fileName)
{
2024-08-09 03:28:39 +00:00
var fileService = _services.GetRequiredService<IFileStorageService>();
2024-08-08 19:54:32 +00:00
var data = await fileService.RetrieveSpeechFileAsync(conversationId, fileName);
2024-08-09 03:28:39 +00:00
var result = new FileContentResult(data.ToArray(), "audio/mpeg");
2024-08-08 19:54:32 +00:00
result.FileDownloadName = fileName;
return result;
}
2023-11-09 20:02:57 +00:00
}