2025-02-07 17:18:47 +00:00
|
|
|
using BotSharp.Abstraction.Infrastructures;
|
|
|
|
|
using BotSharp.Core.Infrastructures;
|
|
|
|
|
using BotSharp.Plugin.Twilio.Interfaces;
|
|
|
|
|
using BotSharp.Plugin.Twilio.Models;
|
|
|
|
|
using BotSharp.Plugin.Twilio.Services;
|
|
|
|
|
using Microsoft.AspNetCore.Http;
|
|
|
|
|
using Microsoft.AspNetCore.Mvc;
|
2025-02-07 19:41:20 +00:00
|
|
|
using Twilio.TwiML.Voice;
|
|
|
|
|
using Conversation = BotSharp.Abstraction.Conversations.Models.Conversation;
|
2025-02-07 17:18:47 +00:00
|
|
|
using Task = System.Threading.Tasks.Task;
|
|
|
|
|
|
|
|
|
|
namespace BotSharp.Plugin.Twilio.Controllers;
|
|
|
|
|
|
|
|
|
|
public class TwilioStreamController : TwilioController
|
|
|
|
|
{
|
|
|
|
|
private readonly TwilioSetting _settings;
|
|
|
|
|
private readonly IServiceProvider _services;
|
|
|
|
|
private readonly IHttpContextAccessor _context;
|
|
|
|
|
private readonly ILogger _logger;
|
|
|
|
|
|
|
|
|
|
public TwilioStreamController(TwilioSetting settings, IServiceProvider services, IHttpContextAccessor context, ILogger<TwilioStreamController> logger)
|
|
|
|
|
{
|
|
|
|
|
_settings = settings;
|
|
|
|
|
_services = services;
|
|
|
|
|
_context = context;
|
|
|
|
|
_logger = logger;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[ValidateRequest]
|
|
|
|
|
[HttpPost("twilio/stream")]
|
|
|
|
|
public async Task<TwiMLResult> InitiateStreamConversation(ConversationalVoiceRequest request)
|
|
|
|
|
{
|
|
|
|
|
var text = JsonSerializer.Serialize(request);
|
|
|
|
|
if (request?.CallSid == null)
|
|
|
|
|
{
|
|
|
|
|
throw new ArgumentNullException(nameof(VoiceRequest.CallSid));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VoiceResponse response = null;
|
|
|
|
|
var instruction = new ConversationalVoiceResponse
|
|
|
|
|
{
|
2025-02-11 23:27:07 +00:00
|
|
|
SpeechPaths = [],
|
2025-02-07 17:18:47 +00:00
|
|
|
ActionOnEmptyResult = true
|
|
|
|
|
};
|
2025-02-10 23:28:03 +00:00
|
|
|
|
2025-02-11 23:27:07 +00:00
|
|
|
if (_context.HttpContext.Request.Query.ContainsKey("init_audio_file"))
|
|
|
|
|
{
|
|
|
|
|
instruction.SpeechPaths.Add(_context.HttpContext.Request.Query["init_audio_file"]);
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-10 23:28:03 +00:00
|
|
|
if (_context.HttpContext.Request.Query.ContainsKey("conversation_id"))
|
|
|
|
|
{
|
|
|
|
|
request.ConversationId = _context.HttpContext.Request.Query["conversation_id"];
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
request.ConversationId = request.CallSid;
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-07 17:18:47 +00:00
|
|
|
await HookEmitter.Emit<ITwilioSessionHook>(_services, async hook =>
|
|
|
|
|
{
|
|
|
|
|
await hook.OnSessionCreating(request, instruction);
|
|
|
|
|
}, new HookEmitOption
|
|
|
|
|
{
|
|
|
|
|
OnlyOnce = true
|
|
|
|
|
});
|
|
|
|
|
|
2025-02-07 22:40:57 +00:00
|
|
|
await InitConversation(request);
|
2025-02-07 17:18:47 +00:00
|
|
|
|
|
|
|
|
var twilio = _services.GetRequiredService<TwilioService>();
|
|
|
|
|
|
2025-02-10 23:28:03 +00:00
|
|
|
response = twilio.ReturnBidirectionalMediaStreamsInstructions(request.ConversationId, instruction);
|
2025-02-07 17:18:47 +00:00
|
|
|
|
2025-02-07 19:41:20 +00:00
|
|
|
await HookEmitter.Emit<ITwilioSessionHook>(_services, async hook =>
|
2025-02-07 17:18:47 +00:00
|
|
|
{
|
|
|
|
|
await hook.OnSessionCreated(request);
|
|
|
|
|
}, new HookEmitOption
|
|
|
|
|
{
|
|
|
|
|
OnlyOnce = true
|
2025-02-07 19:41:20 +00:00
|
|
|
});
|
2025-02-07 17:18:47 +00:00
|
|
|
|
|
|
|
|
return TwiML(response);
|
|
|
|
|
}
|
2025-02-07 19:41:20 +00:00
|
|
|
|
|
|
|
|
private async Task InitConversation(ConversationalVoiceRequest request)
|
|
|
|
|
{
|
|
|
|
|
var convService = _services.GetRequiredService<IConversationService>();
|
2025-02-10 23:28:03 +00:00
|
|
|
var conversation = await convService.GetConversation(request.ConversationId);
|
2025-02-11 23:27:07 +00:00
|
|
|
if (conversation == null)
|
2025-02-10 23:28:03 +00:00
|
|
|
{
|
2025-02-11 23:27:07 +00:00
|
|
|
var conv = new Conversation
|
|
|
|
|
{
|
|
|
|
|
Id = request.CallSid,
|
|
|
|
|
AgentId = _settings.AgentId,
|
|
|
|
|
Channel = ConversationChannel.Phone,
|
2025-02-26 19:16:57 +00:00
|
|
|
ChannelId = request.CallSid,
|
2025-02-11 23:27:07 +00:00
|
|
|
Title = $"Phone call from {request.From}",
|
|
|
|
|
Tags = [],
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
conversation = await convService.NewConversation(conv);
|
2025-02-10 23:28:03 +00:00
|
|
|
}
|
2025-02-07 19:41:20 +00:00
|
|
|
|
|
|
|
|
var states = new List<MessageState>
|
|
|
|
|
{
|
|
|
|
|
new("channel", ConversationChannel.Phone),
|
2025-02-26 19:16:57 +00:00
|
|
|
new("calling_phone", request.From),
|
|
|
|
|
new("twilio_call_sid", request.CallSid),
|
2025-02-07 19:41:20 +00:00
|
|
|
};
|
|
|
|
|
|
2025-02-11 23:27:07 +00:00
|
|
|
convService.SetConversationId(conversation.Id, states);
|
2025-02-07 19:41:20 +00:00
|
|
|
convService.SaveStates();
|
|
|
|
|
}
|
2025-02-07 17:18:47 +00:00
|
|
|
}
|