using FastEndpoints; using FrameworkBLL.EIPConversation.EIPHandlers.EIPConversationBLL; using FrameworkDAL.DTO.EIPConversation; using GB5Shared.DTO.Framework.Login; using GB5Shared.DTO.Framework.ResponseStandard; using GB5Shared.FastEndPoint; using Microsoft.Extensions.Logging; using System.Diagnostics; using static GB5Shared.GB5Constant.Constant; namespace FrameworkSL.Endpoints.EIPConversation { /// /// Inbound entry point for the Voice channel (Twilio, Azure Bot, Google Dialogflow, etc.). /// The calling voice platform is responsible for its own speech-to-text; it posts the /// transcribed text here exactly like any other channel, with ChannelType=Voice (7). /// The response's ResponseMessage/VoiceHint fields carry the TTS/SSML text back for the /// platform to synthesise — mirrors VoiceChannelHandler's AdditionalData shape. /// public class ReceiveVoice : BaseEndpoint> { private readonly IEIPConversationBLL _conversationService; private readonly ILogger _logger; public ReceiveVoice( IEIPConversationBLL conversationService, ILogger logger) { _conversationService = conversationService; _logger = logger; } public override void Configure() { Post("/EIPConversation/ReceiveVoice"); AllowAnonymous(); // Voice platform webhook entry } public record ReceiveVoiceParameters( [property: FromHeader] string Login, [property: FromBody] EIPConversationDTO Payload ); protected override async Task> ExecuteAsync( ReceiveVoiceParameters req, LoginDTO loginDTO, CancellationToken ct) { var correlationId = Guid.NewGuid().ToString("N"); var stopwatch = Stopwatch.StartNew(); try { if (req.Payload == null) throw new ArgumentNullException(nameof(req.Payload)); // Voice platforms don't know about EIPChannelType — force it here rather than // trusting the caller to set it, same reasoning as AppChat/DirectAction channels. req.Payload.ChannelType = EIPChannelType.Voice; _logger.LogInformation( "EIP VOICE START | CorrelationId={CorrelationId} | Tenant={Tenant} | User={User} | Message={Message}", correlationId, req.Payload.TenantId, req.Payload.UserIdentifier, req.Payload.Message); var result = await _conversationService .HandleIncomingConversationAsync(req.Payload, loginDTO!, ct); stopwatch.Stop(); _logger.LogInformation( "EIP VOICE END | CorrelationId={CorrelationId} | Tenant={Tenant} | FinalStep={Step} | DurationMs={Duration}", correlationId, req.Payload.TenantId, result?.CurrentStepCode, stopwatch.ElapsedMilliseconds); return await GB5Shared.ResponseStandard.Response .CreateSuccessResponse(result, CacheKeyLevel.NOT_REQUIRED, loginDTO); } catch (Exception ex) { stopwatch.Stop(); _logger.LogError( ex, "EIP VOICE ERROR | CorrelationId={CorrelationId} | Tenant={Tenant} | DurationMs={Duration}", correlationId, req?.Payload?.TenantId, stopwatch.ElapsedMilliseconds); return await GB5Shared.ResponseStandard.Response .CreateExceptionError( ex, CacheKeyLevel.NOT_REQUIRED, loginDTO, ex.Message, 500); } } } }