This commit is contained in:
Haiping Chen 2025-05-06 22:40:56 -05:00
commit a4d07d5609
3 changed files with 61 additions and 65 deletions

View file

@ -1,6 +1,11 @@
namespace BotSharp.Abstraction.Infrastructures; namespace BotSharp.Abstraction.Infrastructures;
public class HookEmitOption public class HookEmitOption<T>
{ {
public bool OnlyOnce { get; set; } public bool OnlyOnce { get; set; }
/// <summary>
/// Optional predicate to determine if the hook action should be executed for a specific hook instance.
/// </summary>
public Func<T, bool>? ShouldExecute { get; set; }
} }

View file

@ -4,7 +4,7 @@ namespace BotSharp.Core.Infrastructures;
public static class HookEmitter public static class HookEmitter
{ {
public static HookEmittedResult Emit<T>(IServiceProvider services, Action<T> action, HookEmitOption? option = null) public static HookEmittedResult Emit<T>(IServiceProvider services, Action<T> action, HookEmitOption<T>? option = null)
{ {
var logger = services.GetRequiredService<ILogger<T>>(); var logger = services.GetRequiredService<ILogger<T>>();
var result = new HookEmittedResult(); var result = new HookEmittedResult();
@ -15,12 +15,15 @@ public static class HookEmitter
{ {
try try
{ {
logger.LogDebug($"Emit hook action on {action.Method.Name}({hook.GetType().Name})"); if (option.ShouldExecute == null || option.ShouldExecute(hook))
action(hook);
if (option.OnlyOnce)
{ {
break; logger.LogDebug($"Emit hook action on {action.Method.Name}({hook.GetType().Name})");
action(hook);
if (option.OnlyOnce)
{
break;
}
} }
} }
catch (Exception ex) catch (Exception ex)
@ -32,7 +35,7 @@ public static class HookEmitter
return result; return result;
} }
public static async Task<HookEmittedResult> Emit<T>(IServiceProvider services, Func<T, Task> action, HookEmitOption? option = null) public static async Task<HookEmittedResult> Emit<T>(IServiceProvider services, Func<T, Task> action, HookEmitOption<T>? option = null)
{ {
var logger = services.GetRequiredService<ILogger<T>>(); var logger = services.GetRequiredService<ILogger<T>>();
var result = new HookEmittedResult(); var result = new HookEmittedResult();
@ -43,12 +46,15 @@ public static class HookEmitter
{ {
try try
{ {
logger.LogDebug($"Emit hook action on {action.Method.Name}({hook.GetType().Name})"); if (option.ShouldExecute == null || option.ShouldExecute(hook))
await action(hook);
if (option.OnlyOnce)
{ {
break; logger.LogDebug($"Emit hook action on {action.Method.Name}({hook.GetType().Name})");
await action(hook);
if (option.OnlyOnce)
{
break;
}
} }
} }
catch (Exception ex) catch (Exception ex)

View file

@ -7,6 +7,7 @@ using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Twilio.Http; using Twilio.Http;
using Task = System.Threading.Tasks.Task; using Task = System.Threading.Tasks.Task;
using BotSharp.Abstraction.Infrastructures;
namespace BotSharp.Plugin.Twilio.Controllers; namespace BotSharp.Plugin.Twilio.Controllers;
@ -341,62 +342,46 @@ public class TwilioVoiceController : TwilioController
public async Task<ActionResult> PhoneCallStatus(ConversationalVoiceRequest request) public async Task<ActionResult> PhoneCallStatus(ConversationalVoiceRequest request)
{ {
var twilio = _services.GetRequiredService<TwilioService>(); var twilio = _services.GetRequiredService<TwilioService>();
if (request.CallStatus == "completed")
// Define the options with the predicate
var emitOptions = new HookEmitOption<ITwilioCallStatusHook>
{ {
if (twilio.MachineDetected(request)) ShouldExecute = hook => hook.IsMatch(request)
{ };
// voicemail
await HookEmitter.Emit<ITwilioCallStatusHook>(_services, switch (request.CallStatus)
async hook =>
{
if (hook.IsMatch(request)) await hook.OnVoicemailLeft(request);
});
}
else
{
// phone call completed
await HookEmitter.Emit<ITwilioCallStatusHook>(_services,
async hook =>
{
if (hook.IsMatch(request)) await hook.OnUserDisconnected(request);
});
}
}
else if (request.CallStatus == "busy")
{ {
await HookEmitter.Emit<ITwilioCallStatusHook>(_services, case "completed":
async hook => if (twilio.MachineDetected(request))
{ {
if (hook.IsMatch(request)) await hook.OnCallBusyStatus(request); // voicemail
}); await HookEmitter.Emit<ITwilioCallStatusHook>(_services, hook => hook.OnVoicemailLeft(request), emitOptions);
} }
else if (request.CallStatus == "no-answer") else
{
await HookEmitter.Emit<ITwilioCallStatusHook>(_services,
async hook =>
{ {
if (hook.IsMatch(request)) await hook.OnCallNoAnswerStatus(request); // phone call completed
}); await HookEmitter.Emit<ITwilioCallStatusHook>(_services, hook => hook.OnUserDisconnected(request), emitOptions);
} }
else if (request.CallStatus == "canceled") break;
{
await HookEmitter.Emit<ITwilioCallStatusHook>(_services, case "busy":
async hook => await HookEmitter.Emit<ITwilioCallStatusHook>(_services, hook => hook.OnCallBusyStatus(request), emitOptions);
{ break;
if (hook.IsMatch(request)) await hook.OnCallCanceledStatus(request);
}); case "no-answer":
} await HookEmitter.Emit<ITwilioCallStatusHook>(_services, hook => hook.OnCallNoAnswerStatus(request), emitOptions);
else if (request.CallStatus == "failed") break;
{
await HookEmitter.Emit<ITwilioCallStatusHook>(_services, case "canceled":
async hook => await HookEmitter.Emit<ITwilioCallStatusHook>(_services, hook => hook.OnCallCanceledStatus(request), emitOptions);
{ break;
if (hook.IsMatch(request)) await hook.OnCallFailedStatus(request);
}); case "failed":
} await HookEmitter.Emit<ITwilioCallStatusHook>(_services, hook => hook.OnCallFailedStatus(request), emitOptions);
else break;
{ default:
_logger.LogError($"Unknown call status: {request.CallStatus}, {request.CallSid}"); _logger.LogError($"Unknown call status: {request.CallStatus}, {request.CallSid}");
break;
} }
return Ok(); return Ok();