2025-01-22 23:36:32 +00:00
|
|
|
using System.Reflection;
|
|
|
|
|
using Rougamo;
|
|
|
|
|
using Rougamo.Context;
|
|
|
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
|
|
|
using BotSharp.Abstraction.Shared;
|
|
|
|
|
|
2024-11-04 22:44:41 +00:00
|
|
|
namespace BotSharp.Abstraction.SideCar.Attributes;
|
|
|
|
|
|
|
|
|
|
[AttributeUsage(AttributeTargets.Method, Inherited = true)]
|
2025-01-22 23:36:32 +00:00
|
|
|
public class SideCarAttribute : AsyncMoAttribute
|
2024-11-04 22:44:41 +00:00
|
|
|
{
|
|
|
|
|
public SideCarAttribute()
|
|
|
|
|
{
|
2025-01-22 23:36:32 +00:00
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override async ValueTask OnEntryAsync(MethodContext context)
|
|
|
|
|
{
|
|
|
|
|
object? value = null;
|
|
|
|
|
var isHandled = false;
|
|
|
|
|
var methodName = context.Method.Name;
|
|
|
|
|
var methodArgs = context.Arguments ?? [];
|
|
|
|
|
var instance = context.Target;
|
|
|
|
|
var retType = context.ReturnType;
|
|
|
|
|
|
|
|
|
|
var serviceProvider = ((IHaveServiceProvider)instance).ServiceProvider;
|
|
|
|
|
var (sidecar, sidecarMethod) = GetSideCarMethod(serviceProvider, methodName, retType, methodArgs);
|
|
|
|
|
if (sidecar == null || sidecarMethod == null)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (typeof(Task).IsAssignableFrom(retType))
|
|
|
|
|
{
|
|
|
|
|
var syncResultType = retType.IsConstructedGenericType ? retType.GenericTypeArguments[0] : typeof(void);
|
2025-01-25 09:58:06 +00:00
|
|
|
(isHandled, value) = await CallAsyncMethod(sidecar, sidecarMethod, syncResultType, methodArgs);
|
2025-01-22 23:36:32 +00:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
(isHandled, value) = CallSyncMethod(sidecar, sidecarMethod, retType, methodArgs);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (isHandled)
|
|
|
|
|
{
|
|
|
|
|
context.ReplaceReturnValue(this, value);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static MethodInfo GetMethod(string name)
|
|
|
|
|
{
|
|
|
|
|
return typeof(SideCarAttribute).GetMethod(name, BindingFlags.NonPublic | BindingFlags.Static);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private (IConversationSideCar?, MethodInfo?) GetSideCarMethod(IServiceProvider serviceProvider, string methodName, Type retType, object[] args)
|
|
|
|
|
{
|
|
|
|
|
var sidecar = serviceProvider.GetService<IConversationSideCar>();
|
|
|
|
|
var argTypes = args.Select(x => x.GetType()).ToArray();
|
|
|
|
|
var sidecarMethod = sidecar?.GetType()?.GetMethods(BindingFlags.Public | BindingFlags.Instance)
|
|
|
|
|
.FirstOrDefault(x => x.Name == methodName
|
|
|
|
|
&& x.ReturnType == retType
|
|
|
|
|
&& x.GetParameters().Length == argTypes.Length
|
|
|
|
|
&& x.GetParameters().Select(p => p.ParameterType)
|
|
|
|
|
.Zip(argTypes, (paramType, argType) => paramType.IsAssignableFrom(argType)).All(y => y));
|
2024-11-04 22:44:41 +00:00
|
|
|
|
2025-01-22 23:36:32 +00:00
|
|
|
return (sidecar, sidecarMethod);
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-25 09:58:06 +00:00
|
|
|
private async Task<(bool, object?)> CallAsyncMethod(IConversationSideCar instance, MethodInfo method, Type retType, object[] args)
|
2025-01-22 23:36:32 +00:00
|
|
|
{
|
|
|
|
|
object? value = null;
|
2025-01-25 09:58:06 +00:00
|
|
|
object? res = null;
|
2025-01-22 23:36:32 +00:00
|
|
|
var isHandled = false;
|
|
|
|
|
|
|
|
|
|
var enabled = instance != null && instance.IsEnabled() && method != null;
|
|
|
|
|
if (!enabled)
|
|
|
|
|
{
|
|
|
|
|
return (isHandled, value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
isHandled = true;
|
|
|
|
|
if (retType == typeof(void))
|
|
|
|
|
{
|
2025-01-25 09:58:06 +00:00
|
|
|
res = GetMethod(nameof(CallAsync)).Invoke(this, [instance, method, args]);
|
2025-01-22 23:36:32 +00:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2025-01-25 09:58:06 +00:00
|
|
|
res = GetMethod(nameof(CallGenericAsync)).MakeGenericMethod(retType).Invoke(this, [instance, method, args]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (res != null && res is Task task)
|
|
|
|
|
{
|
|
|
|
|
await task;
|
|
|
|
|
if (method.ReturnType.IsGenericType && method.ReturnType.GetGenericTypeDefinition() == typeof(Task<>))
|
|
|
|
|
{
|
2025-01-25 09:59:23 +00:00
|
|
|
value = task?.GetType()?.GetProperty("Result")?.GetValue(task);
|
2025-01-25 09:58:06 +00:00
|
|
|
}
|
2025-01-22 23:36:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (isHandled, value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private (bool, object?) CallSyncMethod(IConversationSideCar instance, MethodInfo method, Type retType, object[] args)
|
|
|
|
|
{
|
|
|
|
|
object? value = null;
|
|
|
|
|
var isHandled = false;
|
|
|
|
|
|
|
|
|
|
var enabled = instance != null && instance.IsEnabled() && method != null;
|
|
|
|
|
if (!enabled)
|
|
|
|
|
{
|
|
|
|
|
return (isHandled, value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
isHandled = true;
|
|
|
|
|
if (retType == typeof(void))
|
|
|
|
|
{
|
|
|
|
|
value = GetMethod(nameof(CallSync)).Invoke(this, [instance, method, args]);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
value = GetMethod(nameof(CallGenericSync)).MakeGenericMethod(retType).Invoke(this, [instance, method, args]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (isHandled, value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#region Call Side car method
|
|
|
|
|
private static async Task<T> CallGenericAsync<T>(object instance, MethodInfo method, object[] args)
|
|
|
|
|
{
|
|
|
|
|
var res = await (Task<T>)method.Invoke(instance, args);
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static async Task CallAsync(object instance, MethodInfo method, object[] args)
|
|
|
|
|
{
|
|
|
|
|
await (Task)method.Invoke(instance, args);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static T CallGenericSync<T>(object instance, MethodInfo method, object[] args)
|
|
|
|
|
{
|
|
|
|
|
var res = (T)method.Invoke(instance, args);
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static void CallSync(object instance, MethodInfo method, object[] args)
|
|
|
|
|
{
|
|
|
|
|
method.Invoke(instance, args);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
#endregion
|
2024-11-04 22:44:41 +00:00
|
|
|
}
|