Skip to content

Commit

Permalink
Merge pull request #15 from navigatorframework/feature/improvements-a…
Browse files Browse the repository at this point in the history
…nd-logs

feature: improvements and logs
  • Loading branch information
elementh authored Aug 22, 2024
2 parents e4d4fda + 1df11e1 commit 13c35d6
Show file tree
Hide file tree
Showing 9 changed files with 435 additions and 216 deletions.
2 changes: 1 addition & 1 deletion src/Navigator/Actions/BotAction.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
namespace Navigator.Actions;

/// <summary>
/// A <see cref="BotAction" /> is a representation of an action that can be executed by a navigator bot. It is used to encapsulate a
/// A <see cref="BotAction" /> is ah representation of an action that can be executed by a navigator bot. It is used to encapsulate a
/// condition and a handler. The condition is a delegate that is checked at runtime and if it evaluates to true, the handler is executed.
/// The condition delegate should return a boolean or a Task that resolves to a boolean. The handler delegate should return void or a Task
/// that resolves to void.
Expand Down
12 changes: 12 additions & 0 deletions src/Navigator/Actions/BotActionInformation.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using Telegram.Bot.Types.Enums;

namespace Navigator.Actions;

/// <summary>
Expand All @@ -10,6 +12,11 @@ public record BotActionInformation
/// </summary>
public required UpdateCategory Category;

/// <summary>
/// The <see cref="ChatAction" /> associtated with the <see cref="BotAction" />. Optional.
/// </summary>
public required ChatAction? ChatAction;

/// <summary>
/// The input types of the condition delegate of the <see cref="BotAction" />.
/// </summary>
Expand All @@ -25,6 +32,11 @@ public record BotActionInformation
/// </summary>
public required Type[] HandlerInputTypes;

/// <summary>
/// The name of the <see cref="BotAction" />. If no name is set, the id is used.
/// </summary>
public required string Name;

/// <summary>
/// The priority of the <see cref="BotAction" />. Optional.
/// </summary>
Expand Down
104 changes: 77 additions & 27 deletions src/Navigator/Actions/Builder/BotActionBuilder.cs
Original file line number Diff line number Diff line change
@@ -1,43 +1,32 @@
using Telegram.Bot.Types.Enums;

namespace Navigator.Actions.Builder;

/// <summary>
/// Builder for <see cref="BotAction" />.
/// </summary>
public class BotActionBuilder
{
private readonly Delegate _condition;
private readonly Type[] _conditionInputTypes;
private readonly Delegate _handler;
private readonly Type[] _handlerInputTypes;
private readonly Guid _id;

/// <summary>
/// Initializes a new instance of the <see cref="BotActionBuilder" /> class.
/// </summary>
/// <param name="condition">
/// A delegate representing the condition under which the handler should be invoked.
/// Must return <see cref="bool" /> or <see cref="Task{TResult}" /> where TResult is <see cref="bool" />.
/// </param>
/// <param name="handler">
/// A delegate representing the action to take when the condition is met.
/// </param>
public BotActionBuilder(Delegate condition, Delegate handler)
public BotActionBuilder()
{
_id = Guid.NewGuid();

if (!(condition.Method.ReturnType != typeof(Task<bool>) || condition.Method.ReturnType != typeof(bool)))
throw new NavigatorException("The condition delegate must return Task<bool> or bool");

_condition = condition;
_conditionInputTypes = condition.Method.GetParameters().Select(info => info.ParameterType).ToArray();
_handler = handler;
_handlerInputTypes = handler.Method.GetParameters().Select(info => info.ParameterType).ToArray();
Priority = Actions.Priority.Default;
}

private string? Name { get; set; }
private Delegate? Condition { get; set; }
private Type[] ConditionInputTypes { get; set; } = null!;
private Delegate? Handler { get; set; }
private Type[] HandlerInputTypes { get; set; } = null!;
private UpdateCategory Category { get; set; } = null!;
private ushort Priority { get; set; }
private TimeSpan? Cooldown { get; set; }
private ChatAction? ChatAction { get; set; }

/// <summary>
/// Builds the bot action.
Expand All @@ -47,14 +36,74 @@ public BotAction Build()
{
var information = new BotActionInformation
{
ChatAction = ChatAction,
Category = Category,
ConditionInputTypes = _conditionInputTypes,
HandlerInputTypes = _handlerInputTypes,
ConditionInputTypes = ConditionInputTypes,
HandlerInputTypes = HandlerInputTypes,
Name = Name ?? $"{_id}",
Priority = Priority,
Cooldown = Cooldown
};

return new BotAction(_id, information, _condition, _handler);
if (Condition is null || Handler is null)
throw new NavigatorException("Both condition and handler must be set");

if (!(Condition.Method.ReturnType != typeof(Task<bool>) || Condition.Method.ReturnType != typeof(bool)))
throw new NavigatorException("The condition delegate must return Task<bool> or bool");

if (Category is null)
throw new NavigatorException("The category must be set");

return new BotAction(_id, information, Condition, Handler);
}

/// <summary>
/// Sets the name of the <see cref="BotAction" />.
/// </summary>
/// <param name="name">The name to be set.</param>
/// <returns>An instance of <see cref="BotActionBuilder" /> to be able to continue configuring the <see cref="BotAction" />.</returns>
public BotActionBuilder WithName(string name)
{
Name = name;

return this;
}

/// <summary>
/// Sets the condition of the <see cref="BotAction" />.
/// </summary>
/// <param name="condition">The condition to be set.</param>
/// <returns>An instance of <see cref="BotActionBuilder" /> to be able to continue configuring the <see cref="BotAction" />.</returns>
public BotActionBuilder SetCondition(Delegate condition)
{
Condition = condition;
ConditionInputTypes = condition.Method.GetParameters().Select(info => info.ParameterType).ToArray();

return this;
}

/// <summary>
/// Sets the handler of the <see cref="BotAction" />.
/// </summary>
/// <param name="handler">The handler to be set.</param>
/// <returns>An instance of <see cref="BotActionBuilder" /> to be able to continue configuring the <see cref="BotAction" />.</returns>
public BotActionBuilder SetHandler(Delegate handler)
{
Handler = handler;
HandlerInputTypes = handler.Method.GetParameters().Select(info => info.ParameterType).ToArray();

return this;
}

/// <summary>
/// Sets the <see cref="UpdateCategory" /> of the <see cref="BotAction" />.
/// </summary>
/// <param name="category">The <see cref="UpdateCategory" /> to be set.</param>
/// <returns>An instance of <see cref="BotActionBuilder" /> to be able to continue configuring the <see cref="BotAction" />.</returns>
public BotActionBuilder SetCategory(UpdateCategory category)
{
Category = category;
return this;
}

/// <summary>
Expand All @@ -80,13 +129,14 @@ public BotActionBuilder WithCooldown(TimeSpan cooldown)
}

/// <summary>
/// Sets the <see cref="UpdateCategory" /> of the <see cref="BotAction" />.
/// Sets the <see cref="ChatAction" /> of the <see cref="BotAction" />.
/// </summary>
/// <param name="category">The <see cref="UpdateCategory" /> to be set.</param>
/// <param name="chatAction">The <see cref="ChatAction" /> to be set.</param>
/// <returns>An instance of <see cref="BotActionBuilder" /> to be able to continue configuring the <see cref="BotAction" />.</returns>
public BotActionBuilder SetCategory(UpdateCategory category)
public BotActionBuilder WithChatAction(ChatAction chatAction)
{
Category = category;
ChatAction = chatAction;

return this;
}
}
36 changes: 29 additions & 7 deletions src/Navigator/Catalog/Factory/BotActionCatalogFactory.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using Microsoft.Extensions.Logging;
using Navigator.Actions;
using Navigator.Actions.Builder;
using Telegram.Bot.Types.Enums;
Expand All @@ -9,6 +10,17 @@ namespace Navigator.Catalog.Factory;
/// </summary>
public class BotActionCatalogFactory
{
private readonly ILogger<BotActionCatalogFactory> _logger;

/// <summary>
/// Initializes a new instance of the <see cref="BotActionCatalogFactory" /> class.
/// </summary>
/// <param name="logger">An instance of <see cref="ILogger{TCategoryName}" />.</param>
public BotActionCatalogFactory(ILogger<BotActionCatalogFactory> logger)
{
_logger = logger;
}

private List<BotActionBuilder> Actions { get; } = [];
private BotActionCatalog? Catalog { get; set; }

Expand All @@ -22,12 +34,14 @@ public class BotActionCatalogFactory
/// <param name="handler">
/// A delegate representing the action to take when the condition is met.
/// </param>
public BotActionBuilder OnUpdate(Delegate condition, Delegate handler)
public BotActionBuilder OnUpdate(Delegate condition, Delegate? handler = default)
{
var id = Guid.NewGuid();
var actionBuilder = new BotActionBuilder(condition, handler);
var actionBuilder = new BotActionBuilder();

actionBuilder.SetCategory(new UpdateCategory(nameof(UpdateType), nameof(UpdateType.Unknown)));
actionBuilder
.SetCondition(condition)
.SetHandler(handler ?? (Action)(() => { }))
.SetCategory(new UpdateCategory(nameof(UpdateType), nameof(UpdateType.Unknown)));

Actions.Add(actionBuilder);

Expand All @@ -50,9 +64,17 @@ public BotActionCatalog Retrieve()
/// </summary>
private void Build()
{
var actions = Actions
.Select(actionBuilder => actionBuilder.Build())
.ToList();
_logger.LogInformation("Building BotActionCatalog with {ActionsCount} actions", Actions.Count);

var actions = new List<BotAction>();

foreach (var builtAction in Actions.Select(actionBuilder => actionBuilder.Build()))
{
_logger.LogDebug("Built action {ActionName} with priority {Priority} for category {Category}",
builtAction.Information.Name, builtAction.Information.Priority, builtAction.Information.Category);

actions.Add(builtAction);
}

Catalog = new BotActionCatalog(actions);
}
Expand Down
Loading

0 comments on commit 13c35d6

Please sign in to comment.