BotSharp/src/Infrastructure/BotSharp.Core/Templating/TemplateRender.cs

132 lines
4.2 KiB
C#
Raw Normal View History

2023-10-12 11:30:13 +00:00
using BotSharp.Abstraction.Models;
2023-08-28 03:50:10 +00:00
using BotSharp.Abstraction.Routing.Models;
using BotSharp.Abstraction.Templating;
2024-05-20 17:54:38 +00:00
using BotSharp.Abstraction.Translation.Models;
using Fluid;
2025-04-28 19:53:32 +00:00
using Fluid.Ast;
2024-11-25 22:13:40 +00:00
using System.Collections;
2025-05-05 04:20:52 +00:00
using System.IO;
2024-11-25 22:13:40 +00:00
using System.Reflection;
2025-05-05 04:20:52 +00:00
using System.Text.Encodings.Web;
namespace BotSharp.Core.Templating;
public class TemplateRender : ITemplateRender
{
private readonly IServiceProvider _services;
private readonly ILogger _logger;
private static readonly FluidParser _parser = new FluidParser();
private TemplateOptions _options;
public TemplateRender(IServiceProvider services, ILogger<TemplateRender> logger)
{
_services = services;
_logger = logger;
_options = new TemplateOptions();
2023-08-28 03:50:10 +00:00
_options.MemberAccessStrategy.MemberNameStrategy = MemberNameStrategies.SnakeCase;
2023-10-12 11:30:13 +00:00
_options.MemberAccessStrategy.Register<NameDesc>();
_options.MemberAccessStrategy.Register<ParameterPropertyDef>();
2023-10-20 03:47:14 +00:00
_options.MemberAccessStrategy.Register<RoleDialogModel>();
2023-10-12 11:30:13 +00:00
_options.MemberAccessStrategy.Register<Agent>();
_options.MemberAccessStrategy.Register<RoutableAgent>();
2023-09-22 20:38:58 +00:00
_options.MemberAccessStrategy.Register<RoutingHandlerDef>();
2024-05-02 22:07:12 +00:00
_options.MemberAccessStrategy.Register<FunctionDef>();
_options.MemberAccessStrategy.Register<FunctionParametersDef>();
2024-03-07 18:27:29 +00:00
_options.MemberAccessStrategy.Register<UserIdentity>();
2024-05-20 17:54:38 +00:00
_options.MemberAccessStrategy.Register<TranslationInput>();
2025-05-05 04:20:52 +00:00
_parser.RegisterIdentifierTag("link", RenderIdentifierTag);
}
2023-08-30 23:29:01 +00:00
public string Render(string template, Dictionary<string, object> dict)
{
if (_parser.TryParse(template, out var t, out var error))
{
var context = new TemplateContext(dict, _options);
2023-08-30 23:29:01 +00:00
template = t.Render(context);
}
else
{
2023-10-28 20:59:26 +00:00
_logger.LogWarning(error);
}
2025-04-28 19:53:32 +00:00
return template;
}
2024-11-25 22:13:40 +00:00
2025-04-28 19:53:32 +00:00
public void RegisterType(Type type)
2024-11-25 22:13:40 +00:00
{
if (type == null || IsStringType(type)) return;
if (IsListType(type))
{
if (type.IsGenericType)
{
var genericType = type.GetGenericArguments()[0];
2025-04-28 19:53:32 +00:00
RegisterType(genericType);
2024-11-25 22:13:40 +00:00
}
}
else if (IsTrackToNextLevel(type))
{
_options.MemberAccessStrategy.Register(type);
var props = type.GetProperties();
foreach (var prop in props)
{
2025-04-28 19:53:32 +00:00
RegisterType(prop.PropertyType);
2024-11-25 22:13:40 +00:00
}
}
}
#region Private methods
2025-05-05 04:20:52 +00:00
private static async ValueTask<Completion> RenderIdentifierTag(string identifier, TextWriter writer, TextEncoder encoder, TemplateContext context)
{
try
{
var value = await context.Model.GetValueAsync(TemplateRenderConstant.RENDER_AGENT, context);
var agent = value?.ToObjectValue() as Agent;
var found = agent?.Templates?.FirstOrDefault(x => x.Name.IsEqualTo(identifier));
var key = $"{agent?.Id} | {identifier}";
2025-05-05 21:55:50 +00:00
if (found == null || (context.AmbientValues.TryGetValue(key, out var visited) && (bool)visited))
2025-05-05 04:20:52 +00:00
{
writer.Write(string.Empty);
}
else if (_parser.TryParse(found.Content, out var t, out _))
{
context.AmbientValues[key] = true;
var rendered = t.Render(context);
writer.Write(rendered);
context.AmbientValues.Remove(key);
}
else
{
writer.Write(string.Empty);
}
}
catch
{
writer.Write(string.Empty);
}
return Completion.Normal;
}
2024-11-25 22:13:40 +00:00
private static bool IsStringType(Type type)
{
return type == typeof(string);
}
private static bool IsListType(Type type)
{
var interfaces = type.GetTypeInfo().ImplementedInterfaces;
return type.IsArray || interfaces.Any(x => x.Name == typeof(IEnumerable).Name);
}
private static bool IsTrackToNextLevel(Type type)
{
return type.IsClass || type.IsInterface || type.IsAbstract;
}
#endregion
}