* Added missing summaries

This commit is contained in:
2026-03-09 04:38:03 +04:00
parent 0e445dd586
commit 5f9c640930
12 changed files with 967 additions and 7 deletions
@@ -4,6 +4,10 @@ using Telegrator.Core.States;
namespace Telegrator.States;
/// <summary>
/// Provides a Redis-based implementation of the <see cref="IStateStorage"/> interface.
/// Serializes state objects to JSON format before storing them in the Redis database.
/// </summary>
public class RedisStateStorage(IConnectionMultiplexer redis) : IStateStorage
{
private readonly IDatabase _db = redis.GetDatabase();
@@ -30,6 +34,6 @@ public class RedisStateStorage(IConnectionMultiplexer redis) : IStateStorage
/// <inheritdoc/>
public async Task DeleteAsync(string key, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
await _db.KeyDeleteAsync(key);
}
}
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<TargetFramework>netstandard2.1</TargetFramework>
<LangVersion>latest</LangVersion>
</PropertyGroup>
@@ -6,14 +6,25 @@ using Telegrator.Filters;
namespace Telegrator.Annotations;
/// <summary>
/// Attribute for filtering updates where resolved state matches target value.
/// </summary>
/// <typeparam name="TKey"></typeparam>
/// <typeparam name="TValue"></typeparam>
/// <param name="value"></param>
public class StateAttribute<TKey, TValue>(TValue? value) : UpdateFilterAttribute<Update>(new StateKeyFilter<TKey, TValue>(value))
where TKey : IStateKeyResolver, new()
where TValue : IEquatable<TValue>
{
/// <summary>
/// The targetting state value.
/// </summary>
public TValue? Value => value;
/// <inheritdoc/>
public override UpdateType[] AllowedTypes => Update.AllTypes;
/// <inheritdoc/>
public override Update? GetFilterringTarget(Update update)
{
return update;
+36 -1
View File
@@ -1,9 +1,44 @@
namespace Telegrator.Core.States;
/// <summary>
/// Defines a contract for a state machine that manages transitions and retrieves states for specific updates.
/// </summary>
/// <typeparam name="TState">The type of the state. Must implement <see cref="IEquatable{T}"/>.</typeparam>
public interface IStateMachine<TState> where TState : IEquatable<TState>
{
/// <summary>
/// Gets the current state associated with the specified update key.
/// </summary>
/// <param name="storage">The storage mechanism used to persist the state.</param>
/// <param name="updateKey">The unique key identifying the current update context (e.g., chat and user ID).</param>
/// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
/// <returns>A task that represents the asynchronous operation. The task result contains the current state, or the default value if no state is found.</returns>
Task<TState?> Current(IStateStorage storage, string updateKey, CancellationToken cancellationToken = default);
/// <summary>
/// Advances the state machine to the next state in the sequence.
/// </summary>
/// <param name="storage">The storage mechanism used to persist the state.</param>
/// <param name="updateKey">The unique key identifying the current update context.</param>
/// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
/// <returns>A task that represents the asynchronous transition operation.</returns>
Task Advance(IStateStorage storage, string updateKey, CancellationToken cancellationToken = default);
/// <summary>
/// Moves the state machine backward to the previous state in the sequence.
/// </summary>
/// <param name="storage">The storage mechanism used to persist the state.</param>
/// <param name="updateKey">The unique key identifying the current update context.</param>
/// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
/// <returns>A task that represents the asynchronous transition operation.</returns>
Task Retreat(IStateStorage storage, string updateKey, CancellationToken cancellationToken = default);
/// <summary>
/// Resets the state machine to its initial or default state.
/// </summary>
/// <param name="storage">The storage mechanism used to persist the state.</param>
/// <param name="updateKey">The unique key identifying the current update context.</param>
/// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
/// <returns>A task that represents the asynchronous reset operation.</returns>
Task Reset(IStateStorage storage, string updateKey, CancellationToken cancellationToken = default);
}
}
+27 -1
View File
@@ -1,8 +1,34 @@
namespace Telegrator.Core.States;
/// <summary>
/// Defines a contract for an asynchronous state storage mechanism.
/// </summary>
public interface IStateStorage
{
/// <summary>
/// Saves or updates a state value associated with the specified key.
/// </summary>
/// <typeparam name="T">The type of the state object.</typeparam>
/// <param name="key">The unique identifier for the state.</param>
/// <param name="state">The state object to store.</param>
/// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
/// <returns>A task that represents the asynchronous save operation.</returns>
Task SetAsync<T>(string key, T state, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves a state value associated with the specified key.
/// </summary>
/// <typeparam name="T">The type of the state object to retrieve.</typeparam>
/// <param name="key">The unique identifier for the state.</param>
/// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
/// <returns>A task that represents the asynchronous retrieve operation. The task result contains the state object if found; otherwise, the default value of <typeparamref name="T"/>.</returns>
Task<T?> GetAsync<T>(string key, CancellationToken cancellationToken = default);
/// <summary>
/// Deletes the state value associated with the specified key.
/// </summary>
/// <param name="key">The unique identifier for the state to remove.</param>
/// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
/// <returns>A task that represents the asynchronous delete operation.</returns>
Task DeleteAsync(string key, CancellationToken cancellationToken = default);
}
}
@@ -61,6 +61,7 @@ namespace Telegrator.Handlers
/// <param name="extraData"></param>
/// <param name="filters"></param>
/// <param name="awaitingProvider"></param>
/// <param name="stateStorage"></param>
public HandlerContainer(TUpdate actualUpdate, Update handlingUpdate, ITelegramBotClient client, Dictionary<string, object> extraData, CompletedFiltersList filters, IAwaitingProvider awaitingProvider, IStateStorage stateStorage)
{
ActualUpdate = actualUpdate;
@@ -3,10 +3,14 @@ using Telegrator.Core.States;
namespace Telegrator.States;
/// <summary>
/// Defines default in-memory state storage
/// </summary>
public class DefaultStateStorage : IStateStorage
{
private readonly ConcurrentDictionary<string, object> storage = [];
/// <inheritdoc/>
public Task DeleteAsync(string key, CancellationToken cancellationToken = default)
{
if (key is null)
@@ -18,6 +22,7 @@ public class DefaultStateStorage : IStateStorage
return Task.CompletedTask;
}
/// <inheritdoc/>
public Task<T?> GetAsync<T>(string key, CancellationToken ccancellationTokent = default)
{
if (key is null)
@@ -29,6 +34,7 @@ public class DefaultStateStorage : IStateStorage
return Task.FromResult(default(T));
}
/// <inheritdoc/>
public Task SetAsync<T>(string key, T state, CancellationToken cancellationToken = default)
{
if (key is null)
+8
View File
@@ -3,6 +3,7 @@ using Telegrator.Core.States;
namespace Telegrator.States;
/// <inheritdoc cref="IStateMachine{TState}"/>
public class StateMachine<TMachine, TState>(IStateStorage stateStorage, Update handlingUpdate)
where TMachine : IStateMachine<TState>, new()
where TState : IEquatable<TState>
@@ -11,8 +12,12 @@ public class StateMachine<TMachine, TState>(IStateStorage stateStorage, Update h
private readonly Update _handlingUpdate = handlingUpdate;
private readonly IStateMachine<TState> _stateMachine = new TMachine();
/// <summary>
/// Chosen key resolver
/// </summary>
public IStateKeyResolver? KeyResolver;
/// <inheritdoc cref="IStateMachine{TState}.Advance(IStateStorage, string, CancellationToken)"/>
public async Task Advance(CancellationToken cancellationToken = default)
{
if (KeyResolver is null)
@@ -25,6 +30,7 @@ public class StateMachine<TMachine, TState>(IStateStorage stateStorage, Update h
await _stateMachine.Advance(_stateStorage, key, cancellationToken);
}
/// <inheritdoc cref="IStateMachine{TState}.Current(IStateStorage, string, CancellationToken)"/>
public async Task<TState?> Current(CancellationToken cancellationToken = default)
{
if (KeyResolver is null)
@@ -37,6 +43,7 @@ public class StateMachine<TMachine, TState>(IStateStorage stateStorage, Update h
return await _stateMachine.Current(_stateStorage, key, cancellationToken);
}
/// <inheritdoc cref="IStateMachine{TState}.Reset(IStateStorage, string, CancellationToken)"/>
public async Task Reset(CancellationToken cancellationToken = default)
{
if (KeyResolver is null)
@@ -49,6 +56,7 @@ public class StateMachine<TMachine, TState>(IStateStorage stateStorage, Update h
await _stateMachine.Reset(_stateStorage, key, cancellationToken);
}
/// <inheritdoc cref="IStateMachine{TState}.Retreat(IStateStorage, string, CancellationToken)"/>
public async Task Retreat(CancellationToken cancellationToken = default)
{
if (KeyResolver is null)
+51
View File
@@ -284,30 +284,74 @@ namespace Telegrator
}
}
/// <summary>
/// Provides extension methods for <see cref="IStateStorage"/> to easily initialize state machines.
/// </summary>
public static class StateStorageExtensions
{
/// <summary>
/// Initializes a state machine using the default <see cref="EnumStateMachine{TState}"/> for the specified update.
/// </summary>
/// <typeparam name="TState">The enum type representing the state.</typeparam>
/// <param name="stateStorage">The storage mechanism used to persist the state.</param>
/// <param name="handlingUpdate">The update context to resolve the state key from.</param>
/// <returns>A new instance of <see cref="StateMachine{TMachine, TState}"/>.</returns>
public static StateMachine<EnumStateMachine<TState>, TState> GetStateMachine<TState>(this IStateStorage stateStorage, Update handlingUpdate)
where TState : struct, Enum, IEquatable<TState>
=> new StateMachine<EnumStateMachine<TState>, TState>(stateStorage, handlingUpdate);
/// <summary>
/// Initializes a specific custom state machine for the specified update.
/// </summary>
/// <typeparam name="TMachine">The type of the state machine logic implementation.</typeparam>
/// <typeparam name="TState">The type of the state.</typeparam>
/// <param name="stateStorage">The storage mechanism used to persist the state.</param>
/// <param name="handlingUpdate">The update context to resolve the state key from.</param>
/// <returns>A new instance of <see cref="StateMachine{TMachine, TState}"/>.</returns>
public static StateMachine<TMachine, TState> GetStateMachine<TMachine, TState>(this IStateStorage stateStorage, Update handlingUpdate)
where TMachine : IStateMachine<TState>, new()
where TState : IEquatable<TState>
=> new StateMachine<TMachine, TState>(stateStorage, handlingUpdate);
/// <summary>
/// Initializes a state machine and explicitly configures it to resolve keys by the chat ID.
/// </summary>
/// <typeparam name="TMachine">The type of the state machine logic implementation.</typeparam>
/// <typeparam name="TState">The type of the state.</typeparam>
/// <param name="stateStorage">The storage mechanism used to persist the state.</param>
/// <param name="handlingUpdate">The update context to resolve the state key from.</param>
/// <returns>A configured instance of <see cref="StateMachine{TMachine, TState}"/>.</returns>
public static StateMachine<TMachine, TState> ByChatId<TMachine, TState>(this IStateStorage stateStorage, Update handlingUpdate)
where TMachine : IStateMachine<TState>, new()
where TState : IEquatable<TState>
=> new StateMachine<TMachine, TState>(stateStorage, handlingUpdate).ByChatId();
/// <summary>
/// Initializes a state machine and explicitly configures it to resolve keys by the sender (user) ID.
/// </summary>
/// <typeparam name="TMachine">The type of the state machine logic implementation.</typeparam>
/// <typeparam name="TState">The type of the state.</typeparam>
/// <param name="stateStorage">The storage mechanism used to persist the state.</param>
/// <param name="handlingUpdate">The update context to resolve the state key from.</param>
/// <returns>A configured instance of <see cref="StateMachine{TMachine, TState}"/>.</returns>
public static StateMachine<TMachine, TState> BySenderId<TMachine, TState>(this IStateStorage stateStorage, Update handlingUpdate)
where TMachine : IStateMachine<TState>, new()
where TState : IEquatable<TState>
=> new StateMachine<TMachine, TState>(stateStorage, handlingUpdate).BySenderId();
}
/// <summary>
/// Provides fluent extension methods for configuring <see cref="StateMachine{TMachine, TState}"/> instances.
/// </summary>
public static class StateMachineExtensions
{
/// <summary>
/// Configures the state machine to use a <see cref="ChatIdResolver"/> for state key resolution.
/// </summary>
/// <typeparam name="TMachine">The type of the state machine logic implementation.</typeparam>
/// <typeparam name="TState">The type of the state.</typeparam>
/// <param name="stateMachine">The state machine instance to configure.</param>
/// <returns>The same state machine instance for method chaining.</returns>
public static StateMachine<TMachine, TState> ByChatId<TMachine, TState>(this StateMachine<TMachine, TState> stateMachine)
where TMachine : IStateMachine<TState>, new()
where TState : IEquatable<TState>
@@ -316,6 +360,13 @@ namespace Telegrator
return stateMachine;
}
/// <summary>
/// Configures the state machine to use a <see cref="SenderIdResolver"/> for state key resolution.
/// </summary>
/// <typeparam name="TMachine">The type of the state machine logic implementation.</typeparam>
/// <typeparam name="TState">The type of the state.</typeparam>
/// <param name="stateMachine">The state machine instance to configure.</param>
/// <returns>The same state machine instance for method chaining.</returns>
public static StateMachine<TMachine, TState> BySenderId<TMachine, TState>(this StateMachine<TMachine, TState> stateMachine)
where TMachine : IStateMachine<TState>, new()
where TState : IEquatable<TState>