Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5f9c640930 | |||
| 0e445dd586 | |||
| da090627ff | |||
| 88bd12aadd | |||
| 162d4a1d05 |
@@ -91,13 +91,12 @@ bot.Handlers.AddHandler<StartCommandHandler>();
|
|||||||
using Telegrator.Handlers;
|
using Telegrator.Handlers;
|
||||||
using Telegrator.Annotations;
|
using Telegrator.Annotations;
|
||||||
|
|
||||||
[CommandHandler, CommandAlias("first"), NumericState(SpecialState.NoState)]
|
[CommandHandler, CommandAlias("first"), State<SetupWizard>(null)]
|
||||||
public class StateKeepFirst : CommandHandler
|
public class StateKeepFirst : CommandHandler
|
||||||
{
|
{
|
||||||
public override async Task<Result> Execute(IHandlerContainer<Message> container, CancellationToken cancellation)
|
public override async Task<Result> Execute(IHandlerContainer<Message> container, CancellationToken cancellation)
|
||||||
{
|
{
|
||||||
container.CreateNumericState();
|
StateStorage.GetStateMachine<SetupWizard>().BysenderId().Advance();
|
||||||
container.ForwardNumericState();
|
|
||||||
await Reply("first state moved (1)", cancellationToken: cancellation);
|
await Reply("first state moved (1)", cancellationToken: cancellation);
|
||||||
return Result.Ok();
|
return Result.Ok();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
<Solution>
|
<Solution>
|
||||||
<Project Path="dev/Telegrator.RoslynGenerators/Telegrator.RoslynGenerators.csproj" />
|
<Project Path="dev/Telegrator.RoslynGenerators/Telegrator.RoslynGenerators.csproj" />
|
||||||
|
<Project Path="src/Telegartor.RedisStateStorage/Telegartor.RedisStateStorage.csproj" />
|
||||||
<Project Path="src/Telegrator.Analyzers/Telegrator.Analyzers.csproj" />
|
<Project Path="src/Telegrator.Analyzers/Telegrator.Analyzers.csproj" />
|
||||||
<Project Path="src/Telegrator.Hosting.Web/Telegrator.Hosting.Web.csproj" />
|
<Project Path="src/Telegrator.Hosting.Web/Telegrator.Hosting.Web.csproj" />
|
||||||
<Project Path="src/Telegrator.Hosting/Telegrator.Hosting.csproj" />
|
<Project Path="src/Telegrator.Hosting/Telegrator.Hosting.csproj" />
|
||||||
|
|||||||
@@ -5,10 +5,6 @@ using System.Collections.Immutable;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using Telegrator.RoslynGenerators.RoslynExtensions;
|
using Telegrator.RoslynGenerators.RoslynExtensions;
|
||||||
|
|
||||||
#if DEBUG
|
|
||||||
using System.Diagnostics;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace Telegrator.RoslynGenerators;
|
namespace Telegrator.RoslynGenerators;
|
||||||
|
|
||||||
[Generator(LanguageNames.CSharp)]
|
[Generator(LanguageNames.CSharp)]
|
||||||
@@ -72,6 +68,9 @@ public class ImplicitHandlerBuilderExtensionsGenerator : IIncrementalGenerator
|
|||||||
if (className == "FilterAnnotation")
|
if (className == "FilterAnnotation")
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
if (className == "StateAttribute")
|
||||||
|
continue;
|
||||||
|
|
||||||
MethodDeclarationSyntax? targeter = classDeclaration.Members.OfType<MethodDeclarationSyntax>().SingleOrDefault(IsTargeterMethod);
|
MethodDeclarationSyntax? targeter = classDeclaration.Members.OfType<MethodDeclarationSyntax>().SingleOrDefault(IsTargeterMethod);
|
||||||
if (targeter != null)
|
if (targeter != null)
|
||||||
{
|
{
|
||||||
@@ -101,7 +100,13 @@ public class ImplicitHandlerBuilderExtensionsGenerator : IIncrementalGenerator
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
usings.UnionAdd(classDeclaration.FindAncestor<CompilationUnitSyntax>().Usings, UsingEqualityComparer);
|
usings.UnionAdd(classDeclaration.FindAncestor<CompilationUnitSyntax>().Usings, UsingEqualityComparer);
|
||||||
MethodDeclarationSyntax targeter = FindTargetterMethod(targetters, classDeclaration);
|
MethodDeclarationSyntax? targeter = FindTargetterMethod(targetters, classDeclaration);
|
||||||
|
|
||||||
|
if (targeter == null)
|
||||||
|
{
|
||||||
|
debugExport.AppendLine("Targetter not found");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (classDeclaration.ParameterList != null && classDeclaration.BaseList != null)
|
if (classDeclaration.ParameterList != null && classDeclaration.BaseList != null)
|
||||||
{
|
{
|
||||||
@@ -140,16 +145,15 @@ public class ImplicitHandlerBuilderExtensionsGenerator : IIncrementalGenerator
|
|||||||
{
|
{
|
||||||
ClassDeclarationSyntax extensionsClass = SyntaxFactory.ClassDeclaration("HandlerBuilderExtensions")
|
ClassDeclarationSyntax extensionsClass = SyntaxFactory.ClassDeclaration("HandlerBuilderExtensions")
|
||||||
.WithModifiers(Modifiers(SyntaxKind.PublicKeyword, SyntaxKind.StaticKeyword, SyntaxKind.PartialKeyword))
|
.WithModifiers(Modifiers(SyntaxKind.PublicKeyword, SyntaxKind.StaticKeyword, SyntaxKind.PartialKeyword))
|
||||||
.AddMembers([.. targetters.Values, .. extensions])
|
.AddMembers([.. targetters.Values, .. extensions]);
|
||||||
.DecorateType(1);
|
|
||||||
|
|
||||||
NamespaceDeclarationSyntax namespaceDeclaration = SyntaxFactory.NamespaceDeclaration(SyntaxFactory.ParseName("Telegrator"))
|
NamespaceDeclarationSyntax namespaceDeclaration = SyntaxFactory.NamespaceDeclaration(SyntaxFactory.ParseName("Telegrator"))
|
||||||
.WithMembers([extensionsClass])
|
.WithMembers([extensionsClass]);
|
||||||
.Decorate();
|
|
||||||
|
|
||||||
CompilationUnitSyntax compilationUnit = SyntaxFactory.CompilationUnit()
|
CompilationUnitSyntax compilationUnit = SyntaxFactory.CompilationUnit()
|
||||||
.WithUsings([.. usings])
|
.WithUsings([.. usings])
|
||||||
.WithMembers([namespaceDeclaration]);
|
.WithMembers([namespaceDeclaration])
|
||||||
|
.NormalizeWhitespace();
|
||||||
|
|
||||||
context.AddSource("GeneratedHandlerBuilderExtensions.cs", compilationUnit.ToFullString());
|
context.AddSource("GeneratedHandlerBuilderExtensions.cs", compilationUnit.ToFullString());
|
||||||
}
|
}
|
||||||
@@ -175,7 +179,7 @@ public class ImplicitHandlerBuilderExtensionsGenerator : IIncrementalGenerator
|
|||||||
if (targetterMethod.ExpressionBody != null)
|
if (targetterMethod.ExpressionBody != null)
|
||||||
method = method.WithExpressionBody(targetterMethod.ExpressionBody).WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken));
|
method = method.WithExpressionBody(targetterMethod.ExpressionBody).WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken));
|
||||||
|
|
||||||
return method.DecorateMember(2);
|
return method;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static MethodDeclarationSyntax GeneratedExtensionsMethod(ClassDeclarationSyntax classDeclaration, ParameterListSyntax methodParameters, ArgumentListSyntax invokerArguments, MethodDeclarationSyntax targetterMethod)
|
private static MethodDeclarationSyntax GeneratedExtensionsMethod(ClassDeclarationSyntax classDeclaration, ParameterListSyntax methodParameters, ArgumentListSyntax invokerArguments, MethodDeclarationSyntax targetterMethod)
|
||||||
@@ -204,11 +208,10 @@ public class ImplicitHandlerBuilderExtensionsGenerator : IIncrementalGenerator
|
|||||||
|
|
||||||
MethodDeclarationSyntax method = SyntaxFactory.MethodDeclaration(returnType, identifier)
|
MethodDeclarationSyntax method = SyntaxFactory.MethodDeclaration(returnType, identifier)
|
||||||
.WithParameterList(parameters)
|
.WithParameterList(parameters)
|
||||||
.WithBody(body.DecorateBlock(2))
|
.WithBody(body)
|
||||||
.WithTypeParameterList(typeParameters)
|
.WithTypeParameterList(typeParameters)
|
||||||
.WithModifiers(Modifiers(SyntaxKind.PublicKeyword, SyntaxKind.StaticKeyword))
|
.WithModifiers(Modifiers(SyntaxKind.PublicKeyword, SyntaxKind.StaticKeyword))
|
||||||
.WithConstraintClauses([typeParameterConstraint])
|
.WithConstraintClauses([typeParameterConstraint])
|
||||||
.DecorateMember(2)
|
|
||||||
.WithLeadingTrivia(xmlDoc);
|
.WithLeadingTrivia(xmlDoc);
|
||||||
|
|
||||||
return method;
|
return method;
|
||||||
@@ -230,7 +233,7 @@ public class ImplicitHandlerBuilderExtensionsGenerator : IIncrementalGenerator
|
|||||||
private static IEnumerable<ConstructorDeclarationSyntax> GetConstructors(ClassDeclarationSyntax classDeclaration)
|
private static IEnumerable<ConstructorDeclarationSyntax> GetConstructors(ClassDeclarationSyntax classDeclaration)
|
||||||
=> classDeclaration.Members.OfType<ConstructorDeclarationSyntax>().Where(ctor => ctor.Modifiers.HasModifiers("public"));
|
=> classDeclaration.Members.OfType<ConstructorDeclarationSyntax>().Where(ctor => ctor.Modifiers.HasModifiers("public"));
|
||||||
|
|
||||||
private static MethodDeclarationSyntax FindTargetterMethod(Dictionary<string, MethodDeclarationSyntax> targeters, ClassDeclarationSyntax classDeclaration)
|
private static MethodDeclarationSyntax? FindTargetterMethod(Dictionary<string, MethodDeclarationSyntax> targeters, ClassDeclarationSyntax classDeclaration)
|
||||||
{
|
{
|
||||||
if (targeters.TryGetValue(classDeclaration.Identifier.ValueText, out MethodDeclarationSyntax targeter))
|
if (targeters.TryGetValue(classDeclaration.Identifier.ValueText, out MethodDeclarationSyntax targeter))
|
||||||
return targeter;
|
return targeter;
|
||||||
@@ -238,7 +241,7 @@ public class ImplicitHandlerBuilderExtensionsGenerator : IIncrementalGenerator
|
|||||||
if (classDeclaration.BaseList != null && targeters.TryGetValue(classDeclaration.BaseList.Types.ElementAt(0).Type.ToString(), out targeter))
|
if (classDeclaration.BaseList != null && targeters.TryGetValue(classDeclaration.BaseList.Types.ElementAt(0).Type.ToString(), out targeter))
|
||||||
return targeter;
|
return targeter;
|
||||||
|
|
||||||
throw new TargteterNotFoundException();
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static SyntaxTriviaList BuildExtensionXmlDocTrivia(ClassDeclarationSyntax classDeclaration, ParameterListSyntax methodParameters)
|
private static SyntaxTriviaList BuildExtensionXmlDocTrivia(ClassDeclarationSyntax classDeclaration, ParameterListSyntax methodParameters)
|
||||||
|
|||||||
@@ -1,38 +0,0 @@
|
|||||||
using Microsoft.CodeAnalysis;
|
|
||||||
using Microsoft.CodeAnalysis.CSharp;
|
|
||||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
|
||||||
|
|
||||||
namespace Telegrator.RoslynGenerators.RoslynExtensions
|
|
||||||
{
|
|
||||||
public static class MemberDeclarationSyntaxExtensions
|
|
||||||
{
|
|
||||||
private static SyntaxTrivia TabulationTrivia => SyntaxFactory.SyntaxTrivia(SyntaxKind.WhitespaceTrivia, "\t");
|
|
||||||
private static SyntaxTrivia WhitespaceTrivia => SyntaxFactory.SyntaxTrivia(SyntaxKind.WhitespaceTrivia, " ");
|
|
||||||
private static SyntaxTrivia NewLineTrivia => SyntaxFactory.SyntaxTrivia(SyntaxKind.EndOfLineTrivia, "\n");
|
|
||||||
|
|
||||||
public static SyntaxTokenList Decorate(this SyntaxTokenList tokens)
|
|
||||||
=> new SyntaxTokenList(tokens.Select(token => token.WithoutTrivia().WithTrailingTrivia(WhitespaceTrivia)).ToArray());
|
|
||||||
|
|
||||||
public static BlockSyntax DecorateBlock(this BlockSyntax block, int times) => block
|
|
||||||
.WithStatements([.. block.Statements.Select(statement => statement.DecorateStatememnt(times + 1))])
|
|
||||||
.WithOpenBraceToken(SyntaxFactory.Token(SyntaxKind.OpenBraceToken).WithLeadingTrivia(WhitespaceTrivia).WithTrailingTrivia(NewLineTrivia))
|
|
||||||
.WithCloseBraceToken(SyntaxFactory.Token(SyntaxKind.CloseBraceToken).WithLeadingTrivia(TabulationTrivia.Repeat(times)).WithTrailingTrivia(NewLineTrivia));
|
|
||||||
|
|
||||||
public static T DecorateStatememnt<T>(this T statememnt, int times) where T : StatementSyntax => statememnt
|
|
||||||
.WithoutTrivia().WithLeadingTrivia(TabulationTrivia.Repeat(times)).WithTrailingTrivia(NewLineTrivia);
|
|
||||||
|
|
||||||
public static T DecorateMember<T>(this T typeDeclaration, int times) where T : MemberDeclarationSyntax => typeDeclaration
|
|
||||||
.WithoutTrivia().WithLeadingTrivia(TabulationTrivia.Repeat(times)).WithTrailingTrivia(NewLineTrivia);
|
|
||||||
|
|
||||||
public static NamespaceDeclarationSyntax Decorate(this NamespaceDeclarationSyntax namespaceDeclaration) => namespaceDeclaration
|
|
||||||
.WithName(namespaceDeclaration.Name.WithoutTrivia().WithLeadingTrivia(WhitespaceTrivia))
|
|
||||||
.WithOpenBraceToken(SyntaxFactory.Token(SyntaxKind.OpenBraceToken).WithLeadingTrivia(NewLineTrivia).WithTrailingTrivia(NewLineTrivia))
|
|
||||||
.WithCloseBraceToken(SyntaxFactory.Token(SyntaxKind.CloseBraceToken));
|
|
||||||
|
|
||||||
public static T DecorateType<T>(this T typeDeclaration, int times = 1) where T : TypeDeclarationSyntax => (T)typeDeclaration
|
|
||||||
.WithoutTrivia().WithLeadingTrivia(TabulationTrivia.Repeat(times))
|
|
||||||
.WithIdentifier(typeDeclaration.Identifier.WithoutTrivia().WithLeadingTrivia(WhitespaceTrivia).WithTrailingTrivia(NewLineTrivia))
|
|
||||||
.WithOpenBraceToken(SyntaxFactory.Token(SyntaxKind.OpenBraceToken).WithLeadingTrivia(TabulationTrivia.Repeat(times)).WithTrailingTrivia(NewLineTrivia))
|
|
||||||
.WithCloseBraceToken(SyntaxFactory.Token(SyntaxKind.CloseBraceToken).WithLeadingTrivia(TabulationTrivia.Repeat(times)).WithTrailingTrivia(NewLineTrivia));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+29
-35
@@ -47,7 +47,7 @@ This guide will walk you through the core concepts and advanced features of **Te
|
|||||||
**Telegrator** is distributed as a NuGet package. You can install it using the .NET CLI, the NuGet Package Manager Console, or by managing NuGet packages in Visual Studio.
|
**Telegrator** is distributed as a NuGet package. You can install it using the .NET CLI, the NuGet Package Manager Console, or by managing NuGet packages in Visual Studio.
|
||||||
|
|
||||||
### Prerequisites
|
### Prerequisites
|
||||||
- .NET >= 5.0 `or` .NET Core >= 2.0 `or` Framework >= 4.6.1 (.NET Standard 2.0 compatible)
|
- .NET >= 5.0 `or` .NET Core >= 2.0 `or` Framework >= 4.6.1 (.NET Standard 2.1 compatible)
|
||||||
- A Telegram Bot Token from [@BotFather](https://t.me/BotFather).
|
- A Telegram Bot Token from [@BotFather](https://t.me/BotFather).
|
||||||
|
|
||||||
### .NET CLI
|
### .NET CLI
|
||||||
@@ -61,7 +61,7 @@ Install-Package Telegrator
|
|||||||
```
|
```
|
||||||
|
|
||||||
### Hosting Integrations
|
### Hosting Integrations
|
||||||
- .NET Core >= 8.0
|
- .NET Core >= 10.0
|
||||||
- `Telegrator.Hosting`: For console/background services
|
- `Telegrator.Hosting`: For console/background services
|
||||||
- `Telegrator.Hosting.Web`: For ASP.NET Core/Webhook
|
- `Telegrator.Hosting.Web`: For ASP.NET Core/Webhook
|
||||||
|
|
||||||
@@ -325,7 +325,7 @@ builder.Handlers.AddMethod<CallbackQuery>(Option1Handler);
|
|||||||
```csharp
|
```csharp
|
||||||
public enum UserState
|
public enum UserState
|
||||||
{
|
{
|
||||||
Start = SpecialState.NoState,
|
Start,
|
||||||
WaitingForName,
|
WaitingForName,
|
||||||
WaitingForAge
|
WaitingForAge
|
||||||
}
|
}
|
||||||
@@ -333,39 +333,40 @@ public enum UserState
|
|||||||
// Start conversation
|
// Start conversation
|
||||||
[CommandHandler]
|
[CommandHandler]
|
||||||
[CommandAlias("register")]
|
[CommandAlias("register")]
|
||||||
[EnumState<UserState>(UserState.Start)]
|
[State<UserState>(UserState.Start)]
|
||||||
private static async Task<Result> StartRegistration(IHandlerContainer<Message> container, CancellationToken cancellationToken)
|
private static async Task<Result> StartRegistration(IHandlerContainer<Message> container, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
container.ForwardEnumState<UserState>();
|
StateStorage.GetStateMachine<UserState>().BySenderId().Advance();
|
||||||
await container.Reply("Please enter your name:", cancellationToken: cancellationToken);
|
await container.Reply("Please enter your name:", cancellationToken: cancellationToken);
|
||||||
return Ok;
|
return Ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle name input
|
// Handle name input
|
||||||
[MessageHandler]
|
[MessageHandler]
|
||||||
[EnumState<UserState>(UserState.WaitingForName)]
|
[State<UserState>(UserState.WaitingForName)]
|
||||||
private static async Task<Result> HandleName(IHandlerContainer<Message> container, CancellationToken cancellationToken)
|
private static async Task<Result> HandleName(IHandlerContainer<Message> container, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var name = container.Input.Text;
|
var name = container.Input.Text;
|
||||||
container.ForwardEnumState<UserState>();
|
StateStorage.GetStateMachine<UserState>().BySenderId().Advance();
|
||||||
await container.Reply($"Hello {name}! Please enter your age:", cancellationToken: cancellationToken);
|
await container.Reply($"Hello {name}! Please enter your age:", cancellationToken: cancellationToken);
|
||||||
return Ok;
|
return Ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle age input
|
// Handle age input
|
||||||
[MessageHandler]
|
[MessageHandler]
|
||||||
[EnumState<UserState>(UserState.WaitingForAge)]
|
[State<UserState>(UserState.WaitingForAge)]
|
||||||
private static async Task<Result> HandleAge(IHandlerContainer<Message> container, CancellationToken cancellationToken)
|
private static async Task<Result> HandleAge(IHandlerContainer<Message> container, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (int.TryParse(container.Input.Text, out int age))
|
if (int.TryParse(container.Input.Text, out int age))
|
||||||
{
|
{
|
||||||
container.DeleteEnumState<UserState>();
|
StateStorage.GetStateMachine<UserState>().BySenderId().Reset();
|
||||||
await container.Reply($"Registration complete! Name: {name}, Age: {age}", cancellationToken: cancellationToken);
|
await container.Reply($"Registration complete! Name: {name}, Age: {age}", cancellationToken: cancellationToken);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
await container.Reply("Please enter a valid age (number):", cancellationToken: cancellationToken);
|
await container.Reply("Please enter a valid age (number):", cancellationToken: cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok;
|
return Ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -497,50 +498,43 @@ public class RestrictedHandler : MessageHandler
|
|||||||
|
|
||||||
### 3.3. State Management
|
### 3.3. State Management
|
||||||
|
|
||||||
Telegrator provides built-in state management for multi-step conversations (wizards, forms, quizzes) without a database.
|
Telegrator provides built-in state management for multi-step conversations (wizards, forms, quizzes) with or without a database.
|
||||||
|
|
||||||
> [!NOTE]
|
> [!NOTE]
|
||||||
> Each type of `StateKeeper`'s (EnumStateKeeper, NumericStateKeeper) is shared between **EVERY** handler in project.
|
> Each type of `StateKeeper`'s keys and states are shared between **EVERY** handler in project.
|
||||||
|
|
||||||
**Types of State:**
|
|
||||||
- **NumericState**: Integer-based steps
|
|
||||||
- **StringState**: Named steps
|
|
||||||
- **EnumState**: Enum-based scenarios
|
|
||||||
|
|
||||||
**How to Use:**
|
**How to Use:**
|
||||||
1. Define your state (enum/int/string)
|
1. Define your state (enum/int/string)
|
||||||
2. Use a state filter attribute on your handler:
|
2. Use a state filter attribute on your handler:
|
||||||
- `[EnumState<MyEnum>(MyEnum.Step1)]`
|
- `[State<MyEnum>(MyEnum.Step1)]`
|
||||||
- `[NumericState(1)]`
|
|
||||||
- `[StringState("waiting_input")]`
|
|
||||||
3. Change state inside the handler using extension methods:
|
3. Change state inside the handler using extension methods:
|
||||||
- `container.ForwardEnumState<MyEnum>()`
|
- `StateStorage.GetStateMachine<MyEnum>().BySenderId().Current()`
|
||||||
- `container.ForwardNumericState()`
|
- `StateStorage.GetStateMachine<MyEnum>().BySenderId().Advance()`
|
||||||
- `container.ForwardStringState()`
|
- `StateStorage.GetStateMachine<MyEnum>().BySenderId().Retreat()`
|
||||||
- `container.DeleteEnumState<MyEnum>()`
|
- `StateStorage.GetStateMachine<MyEnum>().BySenderId().Reset()`
|
||||||
|
|
||||||
**Example:**
|
**Example:**
|
||||||
```csharp
|
```csharp
|
||||||
public enum QuizState
|
public enum QuizState
|
||||||
{
|
{
|
||||||
Start = SpecialState.NoState, Q1, Q2
|
Start, Q1, Q2
|
||||||
}
|
}
|
||||||
|
|
||||||
[CommandHandler]
|
[CommandHandler]
|
||||||
[CommandAlias("quiz")]
|
[CommandAlias("quiz")]
|
||||||
[EnumState<QuizState>(QuizState.Start)]
|
[State<QuizState>(QuizState.Start)]
|
||||||
public class StartQuizHandler : CommandHandler
|
public class StartQuizHandler : CommandHandler
|
||||||
{
|
{
|
||||||
public override async Task<Result> Execute(IHandlerContainer<Message> container, CancellationToken cancellation)
|
public override async Task<Result> Execute(IHandlerContainer<Message> container, CancellationToken cancellation)
|
||||||
{
|
{
|
||||||
container.ForwardEnumState<QuizState>();
|
StateStorage.GetStateMachine<QuizState>().BySenderId().Advance();
|
||||||
await Reply("Quiz started! Question 1: What is the capital of France?");
|
await Reply("Quiz started! Question 1: What is the capital of France?");
|
||||||
return Ok;
|
return Ok;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[MessageHandler]
|
[MessageHandler]
|
||||||
[EnumState<QuizState>(QuizState.Q1)]
|
[State<QuizState>(QuizState.Q1)]
|
||||||
public class Q1Handler : MessageHandler
|
public class Q1Handler : MessageHandler
|
||||||
{
|
{
|
||||||
public override async Task<Result> Execute(IHandlerContainer<Message> container, CancellationToken cancellation)
|
public override async Task<Result> Execute(IHandlerContainer<Message> container, CancellationToken cancellation)
|
||||||
@@ -550,7 +544,7 @@ public class Q1Handler : MessageHandler
|
|||||||
else
|
else
|
||||||
await Reply("Incorrect. The answer is Paris.");
|
await Reply("Incorrect. The answer is Paris.");
|
||||||
|
|
||||||
container.ForwardEnumState<QuizState>();
|
StateStorage.GetStateMachine<QuizState>().BySenderId().Advance();
|
||||||
await Reply("Question 2: What is 2 + 2?");
|
await Reply("Question 2: What is 2 + 2?");
|
||||||
return Ok;
|
return Ok;
|
||||||
}
|
}
|
||||||
@@ -559,8 +553,8 @@ public class Q1Handler : MessageHandler
|
|||||||
|
|
||||||
> **How is it working?**
|
> **How is it working?**
|
||||||
> 1. **Enum State Definition**: `QuizState` enum defines the conversation flow with `Start = SpecialState.NoState` indicating no initial state.
|
> 1. **Enum State Definition**: `QuizState` enum defines the conversation flow with `Start = SpecialState.NoState` indicating no initial state.
|
||||||
> 2. **State Filter**: `[EnumState<QuizState>(QuizState.Start)]` ensures the handler only runs when the user is in the "Start" state.
|
> 2. **State Filter**: `[State<QuizState>(QuizState.Start)]` ensures the handler only runs when the user is in the "Start" state.
|
||||||
> 3. **State Transition**: `container.ForwardEnumState<QuizState>()` moves the user to the next state (Q1).
|
> 3. **State Transition**: `StateStorage.GetStateMachine<QuizState>().BySenderId().Advance()` moves the user to the next state (Q1).
|
||||||
> 4. **Next Handler**: The `Q1Handler` will only run when the user is in state `QuizState.Q1`.
|
> 4. **Next Handler**: The `Q1Handler` will only run when the user is in state `QuizState.Q1`.
|
||||||
> 5. **State Management**: Each handler manages its own state transition, creating a clear conversation flow.
|
> 5. **State Management**: Each handler manages its own state transition, creating a clear conversation flow.
|
||||||
|
|
||||||
@@ -1425,28 +1419,28 @@ public enum UserState
|
|||||||
// Start conversation
|
// Start conversation
|
||||||
[CommandHandler]
|
[CommandHandler]
|
||||||
[CommandAlias("register")]
|
[CommandAlias("register")]
|
||||||
[EnumState<UserState>(UserState.Start)]
|
[State<UserState>(UserState.Start)]
|
||||||
private static async Task<Result> StartRegistration(IHandlerContainer<Message> container, CancellationToken cancellationToken)
|
private static async Task<Result> StartRegistration(IHandlerContainer<Message> container, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
container.ForwardEnumState<UserState>();
|
StateStorage.GetStateMachine<UserState>().BySenderId().Advance();
|
||||||
await container.Reply("Please enter your name:", cancellationToken: cancellationToken);
|
await container.Reply("Please enter your name:", cancellationToken: cancellationToken);
|
||||||
return Ok;
|
return Ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle name input
|
// Handle name input
|
||||||
[MessageHandler]
|
[MessageHandler]
|
||||||
[EnumState<UserState>(UserState.WaitingForName)]
|
[State<UserState>(UserState.WaitingForName)]
|
||||||
private static async Task<Result> HandleName(IHandlerContainer<Message> container, CancellationToken cancellationToken)
|
private static async Task<Result> HandleName(IHandlerContainer<Message> container, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var name = container.Input.Text;
|
var name = container.Input.Text;
|
||||||
container.ForwardEnumState<UserState>();
|
StateStorage.GetStateMachine<UserState>().BySenderId().Advance();
|
||||||
await container.Reply($"Hello {name}! Please enter your age:", cancellationToken: cancellationToken);
|
await container.Reply($"Hello {name}! Please enter your age:", cancellationToken: cancellationToken);
|
||||||
return Ok;
|
return Ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle age input
|
// Handle age input
|
||||||
[MessageHandler]
|
[MessageHandler]
|
||||||
[EnumState<UserState>(UserState.WaitingForAge)]
|
[State<UserState>(UserState.WaitingForAge)]
|
||||||
private static async Task<Result> HandleAge(IHandlerContainer<Message> container, CancellationToken cancellationToken)
|
private static async Task<Result> HandleAge(IHandlerContainer<Message> container, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (int.TryParse(container.Input.Text, out int age))
|
if (int.TryParse(container.Input.Text, out int age))
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<doc>
|
||||||
|
<assembly>
|
||||||
|
<name>Telegartor.RedisStateStorage</name>
|
||||||
|
</assembly>
|
||||||
|
<members>
|
||||||
|
<member name="T:Telegrator.States.RedisStateStorage">
|
||||||
|
<summary>
|
||||||
|
Provides a Redis-based implementation of the <see cref="T:Telegrator.Core.States.IStateStorage"/> interface.
|
||||||
|
Serializes state objects to JSON format before storing them in the Redis database.
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Telegrator.States.RedisStateStorage.#ctor(StackExchange.Redis.IConnectionMultiplexer)">
|
||||||
|
<summary>
|
||||||
|
Provides a Redis-based implementation of the <see cref="T:Telegrator.Core.States.IStateStorage"/> interface.
|
||||||
|
Serializes state objects to JSON format before storing them in the Redis database.
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Telegrator.States.RedisStateStorage.SetAsync``1(System.String,``0,System.Threading.CancellationToken)">
|
||||||
|
<inheritdoc/>
|
||||||
|
</member>
|
||||||
|
<member name="M:Telegrator.States.RedisStateStorage.GetAsync``1(System.String,System.Threading.CancellationToken)">
|
||||||
|
<inheritdoc/>
|
||||||
|
</member>
|
||||||
|
<member name="M:Telegrator.States.RedisStateStorage.DeleteAsync(System.String,System.Threading.CancellationToken)">
|
||||||
|
<inheritdoc/>
|
||||||
|
</member>
|
||||||
|
</members>
|
||||||
|
</doc>
|
||||||
@@ -203,7 +203,7 @@
|
|||||||
<see cref="T:Microsoft.Extensions.Logging.ILogger"/> of this router
|
<see cref="T:Microsoft.Extensions.Logging.ILogger"/> of this router
|
||||||
</summary>
|
</summary>
|
||||||
</member>
|
</member>
|
||||||
<member name="M:Telegrator.Polling.HostUpdateRouter.#ctor(Telegrator.Core.IHandlersProvider,Telegrator.Core.IAwaitingProvider,Microsoft.Extensions.Options.IOptions{Telegrator.TelegratorOptions},Telegrator.Core.ITelegramBotInfo,Microsoft.Extensions.Logging.ILogger{Telegrator.Polling.HostUpdateRouter})">
|
<member name="M:Telegrator.Polling.HostUpdateRouter.#ctor(Telegrator.Core.IHandlersProvider,Telegrator.Core.IAwaitingProvider,Telegrator.Core.States.IStateStorage,Microsoft.Extensions.Options.IOptions{Telegrator.TelegratorOptions},Telegrator.Core.ITelegramBotInfo,Microsoft.Extensions.Logging.ILogger{Telegrator.Polling.HostUpdateRouter})">
|
||||||
<inheritdoc/>
|
<inheritdoc/>
|
||||||
</member>
|
</member>
|
||||||
<member name="M:Telegrator.Polling.HostUpdateRouter.HandleUpdateAsync(Telegram.Bot.ITelegramBotClient,Telegram.Bot.Types.Update,System.Threading.CancellationToken)">
|
<member name="M:Telegrator.Polling.HostUpdateRouter.HandleUpdateAsync(Telegram.Bot.ITelegramBotClient,Telegram.Bot.Types.Update,System.Threading.CancellationToken)">
|
||||||
|
|||||||
+475
-890
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,57 @@
|
|||||||
|
# Telegrator.RedisStateStorage
|
||||||
|
|
||||||
|
**Telegrator.RedisStateStorage** is an extension for the Telegrator framework that provides Redis powered IStateStorage implementation.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
- .NET standart 2.1 or later
|
||||||
|
- [Telegrator](https://github.com/Rikitav/Telegrator)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
```shell
|
||||||
|
dotnet add package Telegrator.RedisStateStorage
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Quick Start Example
|
||||||
|
|
||||||
|
**Program.cs:**
|
||||||
|
```csharp
|
||||||
|
using Telegrator.Hosting;
|
||||||
|
|
||||||
|
// Creating builder
|
||||||
|
TelegramBotHostBuilder builder = TelegramBotHost.CreateBuilder(new HostApplicationBuilderSettings()
|
||||||
|
{
|
||||||
|
Args = args,
|
||||||
|
ApplicationName = "TelegramBotHost example",
|
||||||
|
});
|
||||||
|
|
||||||
|
// Registerring handlers
|
||||||
|
builder.Handlers.CollectHandlersAssemblyWide();
|
||||||
|
|
||||||
|
// Register your services and
|
||||||
|
builder.Services.AddService<IStateStorage, RedisStateStorage>(services =>
|
||||||
|
new RedisStateStorage(ConnectionMultiplexer.Connect("server1:6379, server2:6379")));
|
||||||
|
|
||||||
|
// Building and running application
|
||||||
|
TelegramBotHost telegramBot = builder.Build();
|
||||||
|
telegramBot.SetBotCommands();
|
||||||
|
telegramBot.Run();
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
- [Telegrator Main Docs](https://github.com/Rikitav/Telegrator)
|
||||||
|
- [Getting Started Guide](https://github.com/Rikitav/Telegrator/wiki/Getting-started)
|
||||||
|
- [Annotation Overview](https://github.com/Rikitav/Telegrator/wiki/Annotation-overview)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## License
|
||||||
|
GPLv3
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
using StackExchange.Redis;
|
||||||
|
using System.Text.Json;
|
||||||
|
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();
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public async Task SetAsync<T>(string key, T state, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
string json = JsonSerializer.Serialize(state);
|
||||||
|
await _db.StringSetAsync(key, json);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public async Task<T?> GetAsync<T>(string key, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
RedisValue json = await _db.StringGetAsync(key);
|
||||||
|
string? jsonStr = json;
|
||||||
|
|
||||||
|
if (jsonStr is null)
|
||||||
|
return default;
|
||||||
|
|
||||||
|
return JsonSerializer.Deserialize<T?>(json: jsonStr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public async Task DeleteAsync(string key, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
await _db.KeyDeleteAsync(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>netstandard2.1</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<LangVersion>latest</LangVersion>
|
||||||
|
<RootNamespace>Telegrator</RootNamespace>
|
||||||
|
<BaseOutputPath>..\..\bin</BaseOutputPath>
|
||||||
|
<DocumentationFile>..\..\docs\$(AssemblyName).xml</DocumentationFile>
|
||||||
|
|
||||||
|
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
|
||||||
|
<GenerateDocumentationFile>True</GenerateDocumentationFile>
|
||||||
|
<EnableNETAnalyzers>True</EnableNETAnalyzers>
|
||||||
|
<EnforceCodeStyleInBuild>True</EnforceCodeStyleInBuild>
|
||||||
|
|
||||||
|
<Title>Telegrator.RedisStateStorage</Title>
|
||||||
|
<Version>1.16.4</Version>
|
||||||
|
<Authors>Rikitav Tim4ik</Authors>
|
||||||
|
<Company>Rikitav Tim4ik</Company>
|
||||||
|
<RepositoryUrl>https://github.com/Rikitav/Telegrator</RepositoryUrl>
|
||||||
|
<PackageTags>telegram;bot;mediator;attributes;aspect;hosting;host;framework;easy;simple;handlers</PackageTags>
|
||||||
|
|
||||||
|
<PackageIcon>telegrator_nuget.png</PackageIcon>
|
||||||
|
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||||
|
<PackageLicenseFile>LICENSE</PackageLicenseFile>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Telegrator\Telegrator.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="StackExchange.Redis" Version="2.11.8 " />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include=".\README.md" Pack="True" PackagePath="\" />
|
||||||
|
<None Include="..\..\LICENSE" Pack="True" PackagePath="\" />
|
||||||
|
<None Include="..\..\resources\telegrator_nuget.png" Pack="True" PackagePath="\" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
<EnforceCodeStyleInBuild>True</EnforceCodeStyleInBuild>
|
<EnforceCodeStyleInBuild>True</EnforceCodeStyleInBuild>
|
||||||
|
|
||||||
<Title>Telegrator.Hosting.Web</Title>
|
<Title>Telegrator.Hosting.Web</Title>
|
||||||
<Version>1.16.3</Version>
|
<Version>1.16.4</Version>
|
||||||
<Authors>Rikitav Tim4ik</Authors>
|
<Authors>Rikitav Tim4ik</Authors>
|
||||||
<Company>Rikitav Tim4ik</Company>
|
<Company>Rikitav Tim4ik</Company>
|
||||||
<RepositoryUrl>https://github.com/Rikitav/Telegrator</RepositoryUrl>
|
<RepositoryUrl>https://github.com/Rikitav/Telegrator</RepositoryUrl>
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ using Telegram.Bot;
|
|||||||
using Telegram.Bot.Polling;
|
using Telegram.Bot.Polling;
|
||||||
using Telegram.Bot.Types;
|
using Telegram.Bot.Types;
|
||||||
using Telegrator.Core;
|
using Telegrator.Core;
|
||||||
|
using Telegrator.Core.States;
|
||||||
using Telegrator.Mediation;
|
using Telegrator.Mediation;
|
||||||
|
|
||||||
namespace Telegrator.Polling
|
namespace Telegrator.Polling
|
||||||
@@ -20,9 +21,10 @@ namespace Telegrator.Polling
|
|||||||
public HostUpdateRouter(
|
public HostUpdateRouter(
|
||||||
IHandlersProvider handlersProvider,
|
IHandlersProvider handlersProvider,
|
||||||
IAwaitingProvider awaitingProvider,
|
IAwaitingProvider awaitingProvider,
|
||||||
|
IStateStorage stateStorage,
|
||||||
IOptions<TelegratorOptions> options,
|
IOptions<TelegratorOptions> options,
|
||||||
ITelegramBotInfo botInfo,
|
ITelegramBotInfo botInfo,
|
||||||
ILogger<HostUpdateRouter> logger) : base(handlersProvider, awaitingProvider, options.Value, botInfo)
|
ILogger<HostUpdateRouter> logger) : base(handlersProvider, awaitingProvider, stateStorage, options.Value, botInfo)
|
||||||
{
|
{
|
||||||
Logger = logger;
|
Logger = logger;
|
||||||
ExceptionHandler = new DefaultRouterExceptionHandler(HandleException);
|
ExceptionHandler = new DefaultRouterExceptionHandler(HandleException);
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
<EnforceCodeStyleInBuild>True</EnforceCodeStyleInBuild>
|
<EnforceCodeStyleInBuild>True</EnforceCodeStyleInBuild>
|
||||||
|
|
||||||
<Title>Telegrator.Hosting</Title>
|
<Title>Telegrator.Hosting</Title>
|
||||||
<Version>1.16.3</Version>
|
<Version>1.16.4</Version>
|
||||||
<Authors>Rikitav Tim4ik</Authors>
|
<Authors>Rikitav Tim4ik</Authors>
|
||||||
<Company>Rikitav Tim4ik</Company>
|
<Company>Rikitav Tim4ik</Company>
|
||||||
<RepositoryUrl>https://github.com/Rikitav/Telegrator</RepositoryUrl>
|
<RepositoryUrl>https://github.com/Rikitav/Telegrator</RepositoryUrl>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netstandard2.0</TargetFramework>
|
<TargetFramework>netstandard2.1</TargetFramework>
|
||||||
<LangVersion>latest</LangVersion>
|
<LangVersion>latest</LangVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
using Telegram.Bot.Types;
|
||||||
|
using Telegram.Bot.Types.Enums;
|
||||||
|
using Telegrator.Attributes;
|
||||||
|
using Telegrator.Core.States;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
using Telegrator.StateKeeping;
|
|
||||||
using Telegrator.Attributes;
|
|
||||||
using Telegrator.Core.StateKeeping;
|
|
||||||
|
|
||||||
namespace Telegrator.Annotations.StateKeeping
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Attribute for managing enum-based states in Telegram bot handlers.
|
|
||||||
/// Provides a convenient way to associate enum values with state management functionality.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="TEnum">The enum type to be used for state management.</typeparam>
|
|
||||||
public class EnumStateAttribute<TEnum> : StateKeeperAttribute<long, TEnum, EnumStateKeeper<TEnum>> where TEnum : Enum
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the EnumStateAttribute with a special state and custom key resolver.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="specialState">The special state to be managed.</param>
|
|
||||||
/// <param name="keyResolver">The resolver for extracting keys from updates.</param>
|
|
||||||
public EnumStateAttribute(SpecialState specialState, IStateKeyResolver<long> keyResolver)
|
|
||||||
: base(specialState, keyResolver) { }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the EnumStateAttribute with a specific enum state and custom key resolver.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="myState">The specific enum state to be managed.</param>
|
|
||||||
/// <param name="keyResolver">The resolver for extracting keys from updates.</param>
|
|
||||||
public EnumStateAttribute(TEnum myState, IStateKeyResolver<long> keyResolver)
|
|
||||||
: base(myState, keyResolver) { }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the EnumStateAttribute with a special state and default sender ID resolver.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="specialState">The special state to be managed.</param>
|
|
||||||
public EnumStateAttribute(SpecialState specialState)
|
|
||||||
: base(specialState, new SenderIdResolver()) { }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the EnumStateAttribute with a specific enum state and default sender ID resolver.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="myState">The specific enum state to be managed.</param>
|
|
||||||
public EnumStateAttribute(TEnum myState)
|
|
||||||
: this(myState, new SenderIdResolver()) { }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
using Telegrator.StateKeeping;
|
|
||||||
using Telegrator.Attributes;
|
|
||||||
using Telegrator.Core.StateKeeping;
|
|
||||||
|
|
||||||
namespace Telegrator.Annotations.StateKeeping
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Attribute for associating a handler or method with a numeric (integer) state keeper.
|
|
||||||
/// Provides constructors for flexible state and key resolver configuration.
|
|
||||||
/// </summary>
|
|
||||||
public class NumericStateAttribute : StateKeeperAttribute<long, int, NumericStateKeeper>
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes the attribute with a special state and a custom key resolver.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="specialState">The special state to associate</param>
|
|
||||||
/// <param name="keyResolver">The key resolver for state keeping</param>
|
|
||||||
public NumericStateAttribute(SpecialState specialState, IStateKeyResolver<long> keyResolver)
|
|
||||||
: base(specialState, keyResolver) { }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes the attribute with a specific numeric state and a custom key resolver.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="myState">The integer state to associate</param>
|
|
||||||
/// <param name="keyResolver">The key resolver for state keeping</param>
|
|
||||||
public NumericStateAttribute(int myState, IStateKeyResolver<long> keyResolver)
|
|
||||||
: base(myState, keyResolver) { }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes the attribute with a special state and the default sender ID resolver.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="specialState">The special state to associate</param>
|
|
||||||
public NumericStateAttribute(SpecialState specialState)
|
|
||||||
: base(specialState, new SenderIdResolver()) { }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes the attribute with a specific numeric state and the default sender ID resolver.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="myState">The integer state to associate</param>
|
|
||||||
public NumericStateAttribute(int myState)
|
|
||||||
: this(myState, new SenderIdResolver()) { }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
namespace Telegrator.Annotations.StateKeeping
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Represents special states for state keeping logic.
|
|
||||||
/// </summary>
|
|
||||||
public enum SpecialState
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// No special state.
|
|
||||||
/// </summary>
|
|
||||||
None,
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates that no state is present.
|
|
||||||
/// </summary>
|
|
||||||
NoState,
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates that any state is acceptable.
|
|
||||||
/// </summary>
|
|
||||||
AnyState
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
using Telegrator.StateKeeping;
|
|
||||||
using Telegrator.Attributes;
|
|
||||||
using Telegrator.Core.StateKeeping;
|
|
||||||
|
|
||||||
namespace Telegrator.Annotations.StateKeeping
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Attribute for associating a handler or method with a string-based state keeper.
|
|
||||||
/// Provides various constructors for flexible state and key resolver configuration.
|
|
||||||
/// </summary>
|
|
||||||
public class StringStateAttribute : StateKeeperAttribute<long, string, StringStateKeeper>
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes the attribute with a special state and a custom key resolver.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="specialState">The special state to associate</param>
|
|
||||||
/// <param name="keyResolver">The key resolver for state keeping</param>
|
|
||||||
public StringStateAttribute(SpecialState specialState, IStateKeyResolver<long> keyResolver)
|
|
||||||
: base(specialState, keyResolver) { }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes the attribute with a specific state and a custom key resolver.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="myState">The string state to associate</param>
|
|
||||||
/// <param name="keyResolver">The key resolver for state keeping</param>
|
|
||||||
public StringStateAttribute(string myState, IStateKeyResolver<long> keyResolver)
|
|
||||||
: base(myState, keyResolver) { }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes the attribute with a special state and the default sender ID resolver.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="specialState">The special state to associate</param>
|
|
||||||
public StringStateAttribute(SpecialState specialState)
|
|
||||||
: base(specialState, new SenderIdResolver()) { }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes the attribute with a specific state and the default sender ID resolver.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="myState">The string state to associate</param>
|
|
||||||
public StringStateAttribute(string myState)
|
|
||||||
: base(myState, new SenderIdResolver()) { }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+1
-1
@@ -2,7 +2,7 @@
|
|||||||
using Telegram.Bot.Types.Enums;
|
using Telegram.Bot.Types.Enums;
|
||||||
using Telegrator.Filters;
|
using Telegrator.Filters;
|
||||||
|
|
||||||
namespace Telegrator.Annotations.Targetted
|
namespace Telegrator.Annotations
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Attribute for filtering message with command "start" in bot's private chats.
|
/// Attribute for filtering message with command "start" in bot's private chats.
|
||||||
@@ -1,86 +0,0 @@
|
|||||||
using Telegram.Bot.Types;
|
|
||||||
using Telegrator.Annotations.StateKeeping;
|
|
||||||
using Telegrator.Core.Attributes;
|
|
||||||
using Telegrator.Core.Filters;
|
|
||||||
using Telegrator.Core.StateKeeping;
|
|
||||||
|
|
||||||
namespace Telegrator.Attributes
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Abstract attribute for associating a handler or method with a state keeper.
|
|
||||||
/// Provides logic for state-based filtering and state management.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="TKey">The type of the key used for state keeping (e.g., chat ID).</typeparam>
|
|
||||||
/// <typeparam name="TState">The type of the state value (e.g., string, int).</typeparam>
|
|
||||||
/// <typeparam name="TKeeper">The type of the state keeper implementation.</typeparam>
|
|
||||||
public abstract class StateKeeperAttribute<TKey, TState, TKeeper> : StateKeeperAttributeBase where TKey : notnull where TState : notnull where TKeeper : StateKeeperBase<TKey, TState>, new()
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
private static readonly TKeeper _shared = new TKeeper();
|
|
||||||
private static readonly Dictionary<TKey, TKeeper> _keyed = [];
|
|
||||||
*/
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the singleton instance of the state keeper for this attribute type.
|
|
||||||
/// </summary>
|
|
||||||
public static TKeeper Shared { get; } = new TKeeper();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the default state value of this statekeeper.
|
|
||||||
/// </summary>
|
|
||||||
public static TState DefaultState => Shared.DefaultState;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the state value associated with this attribute instance.
|
|
||||||
/// </summary>
|
|
||||||
public TState MyState { get; private set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the special state mode for this attribute instance.
|
|
||||||
/// </summary>
|
|
||||||
public SpecialState SpecialState { get; private set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes the attribute with a specific state and a custom key resolver.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="myState">The state value to associate</param>
|
|
||||||
/// <param name="keyResolver">The key resolver for state keeping</param>
|
|
||||||
protected StateKeeperAttribute(TState myState, IStateKeyResolver<TKey> keyResolver) : base(typeof(TKeeper))
|
|
||||||
{
|
|
||||||
Shared.KeyResolver = keyResolver;
|
|
||||||
MyState = myState;
|
|
||||||
SpecialState = SpecialState.None;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes the attribute with a special state and a custom key resolver.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="specialState">The special state mode</param>
|
|
||||||
/// <param name="keyResolver">The key resolver for state keeping</param>
|
|
||||||
protected StateKeeperAttribute(SpecialState specialState, IStateKeyResolver<TKey> keyResolver) : base(typeof(TKeeper))
|
|
||||||
{
|
|
||||||
Shared.KeyResolver = keyResolver;
|
|
||||||
MyState = Shared.DefaultState;
|
|
||||||
SpecialState = specialState;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Determines whether the current update context passes the state filter.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="context">The filter execution context</param>
|
|
||||||
/// <returns>True if the state matches the filter; otherwise, false.</returns>
|
|
||||||
public override bool CanPass(FilterExecutionContext<Update> context)
|
|
||||||
{
|
|
||||||
if (SpecialState == SpecialState.AnyState)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
if (!Shared.TryGetState(context.Input, out TState? state))
|
|
||||||
return SpecialState == SpecialState.NoState;
|
|
||||||
|
|
||||||
if (state == null)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return MyState.Equals(state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
using Telegram.Bot.Types;
|
|
||||||
using Telegrator.Core.Filters;
|
|
||||||
using Telegrator.Core.Handlers;
|
|
||||||
using Telegrator.Core.StateKeeping;
|
|
||||||
|
|
||||||
namespace Telegrator.Core.Attributes
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Sets the state in which the <see cref="UpdateHandlerBase"/> can be executed
|
|
||||||
/// </summary>
|
|
||||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
|
|
||||||
public abstract class StateKeeperAttributeBase : Attribute, IFilter<Update>
|
|
||||||
{
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public bool IsCollectible => GetType().HasPublicProperties();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a new instance <see cref="StateKeeperBase{TKey, TState}"/>
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="stateKeeperType"></param>
|
|
||||||
/// <exception cref="ArgumentException"></exception>
|
|
||||||
protected StateKeeperAttributeBase(Type stateKeeperType)
|
|
||||||
{
|
|
||||||
if (!stateKeeperType.IsAssignableToGenericType(typeof(StateKeeperBase<,>)))
|
|
||||||
throw new ArgumentException(stateKeeperType + " is not a StateKeeperBase", nameof(stateKeeperType));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Realizes a <see cref="IFilter{T}"/> for validation of the current <see cref="StateKeeperBase{TKey, TState}"/> in the polling routing
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="context"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public abstract bool CanPass(FilterExecutionContext<Update> context);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -24,7 +24,7 @@ namespace Telegrator.Core.Descriptors
|
|||||||
public MethodHandlerDescriptor(AbstractHandlerAction<TUpdate> action) : base(DescriptorType.General, typeof(MethodHandler), true)
|
public MethodHandlerDescriptor(AbstractHandlerAction<TUpdate> action) : base(DescriptorType.General, typeof(MethodHandler), true)
|
||||||
{
|
{
|
||||||
UpdateHandlerAttributeBase handlerAttribute = HandlerInspector.GetHandlerAttribute(action.Method);
|
UpdateHandlerAttributeBase handlerAttribute = HandlerInspector.GetHandlerAttribute(action.Method);
|
||||||
StateKeeperAttributeBase? stateKeeperAttribute = HandlerInspector.GetStateKeeperAttribute(action.Method);
|
IFilter<Update>? stateKeeperAttribute = HandlerInspector.GetStateKeeperAttribute(action.Method);
|
||||||
IFilter<Update>[] filters = HandlerInspector.GetFilterAttributes(action.Method, handlerAttribute.Type).ToArray();
|
IFilter<Update>[] filters = HandlerInspector.GetFilterAttributes(action.Method, handlerAttribute.Type).ToArray();
|
||||||
|
|
||||||
UpdateType = handlerAttribute.Type;
|
UpdateType = handlerAttribute.Type;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
using Telegram.Bot.Types;
|
using Telegram.Bot.Types;
|
||||||
using Telegrator.Core.Filters;
|
using Telegrator.Core.Filters;
|
||||||
using Telegrator.Core.Handlers;
|
using Telegrator.Core.Handlers;
|
||||||
|
using Telegrator.Core.States;
|
||||||
|
|
||||||
namespace Telegrator.Core.Descriptors
|
namespace Telegrator.Core.Descriptors
|
||||||
{
|
{
|
||||||
@@ -27,6 +28,11 @@ namespace Telegrator.Core.Descriptors
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public IAwaitingProvider AwaitingProvider { get; }
|
public IAwaitingProvider AwaitingProvider { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The state storage to handling state machines
|
||||||
|
/// </summary>
|
||||||
|
public IStateStorage StateStorage { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The Telegram bot client used for this handler.
|
/// The Telegram bot client used for this handler.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -73,6 +79,7 @@ namespace Telegrator.Core.Descriptors
|
|||||||
/// <param name="fromDescriptor">The descriptor from which this handler was described.</param>
|
/// <param name="fromDescriptor">The descriptor from which this handler was described.</param>
|
||||||
/// <param name="updateRouter">The update router.</param>
|
/// <param name="updateRouter">The update router.</param>
|
||||||
/// <param name="awaitingProvider">The awaiting provider.</param>
|
/// <param name="awaitingProvider">The awaiting provider.</param>
|
||||||
|
/// <param name="stateStorage">The state storage.</param>
|
||||||
/// <param name="client">The Telegram bot client.</param>
|
/// <param name="client">The Telegram bot client.</param>
|
||||||
/// <param name="handlerInstance">The handler instance.</param>
|
/// <param name="handlerInstance">The handler instance.</param>
|
||||||
/// <param name="filterContext">The filter execution context.</param>
|
/// <param name="filterContext">The filter execution context.</param>
|
||||||
@@ -81,6 +88,7 @@ namespace Telegrator.Core.Descriptors
|
|||||||
HandlerDescriptor fromDescriptor,
|
HandlerDescriptor fromDescriptor,
|
||||||
IUpdateRouter updateRouter,
|
IUpdateRouter updateRouter,
|
||||||
IAwaitingProvider awaitingProvider,
|
IAwaitingProvider awaitingProvider,
|
||||||
|
IStateStorage stateStorage,
|
||||||
ITelegramBotClient client,
|
ITelegramBotClient client,
|
||||||
UpdateHandlerBase handlerInstance,
|
UpdateHandlerBase handlerInstance,
|
||||||
FilterExecutionContext<Update> filterContext,
|
FilterExecutionContext<Update> filterContext,
|
||||||
@@ -89,6 +97,7 @@ namespace Telegrator.Core.Descriptors
|
|||||||
From = fromDescriptor;
|
From = fromDescriptor;
|
||||||
UpdateRouter = updateRouter;
|
UpdateRouter = updateRouter;
|
||||||
AwaitingProvider = awaitingProvider;
|
AwaitingProvider = awaitingProvider;
|
||||||
|
StateStorage = stateStorage;
|
||||||
Client = client;
|
Client = client;
|
||||||
HandlerInstance = handlerInstance;
|
HandlerInstance = handlerInstance;
|
||||||
ExtraData = filterContext.Data;
|
ExtraData = filterContext.Data;
|
||||||
|
|||||||
@@ -167,7 +167,7 @@ namespace Telegrator.Core.Descriptors
|
|||||||
if (handlerAttribute.ExpectingHandlerType != null && !handlerAttribute.ExpectingHandlerType.Contains(handlerType.BaseType))
|
if (handlerAttribute.ExpectingHandlerType != null && !handlerAttribute.ExpectingHandlerType.Contains(handlerType.BaseType))
|
||||||
throw new ArgumentException(string.Format("This handler attribute cannot be attached to this class. Attribute can be attached on next handlers : {0}", string.Join(", ", handlerAttribute.ExpectingHandlerType.AsEnumerable())));
|
throw new ArgumentException(string.Format("This handler attribute cannot be attached to this class. Attribute can be attached on next handlers : {0}", string.Join(", ", handlerAttribute.ExpectingHandlerType.AsEnumerable())));
|
||||||
|
|
||||||
StateKeeperAttributeBase? stateKeeperAttribute = HandlerInspector.GetStateKeeperAttribute(handlerType);
|
IFilter<Update>? stateKeeperAttribute = HandlerInspector.GetStateKeeperAttribute(handlerType);
|
||||||
IFilter<Update>[] filters = HandlerInspector.GetFilterAttributes(handlerType, handlerAttribute.Type).ToArray();
|
IFilter<Update>[] filters = HandlerInspector.GetFilterAttributes(handlerType, handlerAttribute.Type).ToArray();
|
||||||
|
|
||||||
UpdateType = handlerAttribute.Type;
|
UpdateType = handlerAttribute.Type;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using Telegram.Bot.Types;
|
using Telegram.Bot.Types;
|
||||||
using Telegram.Bot.Types.Enums;
|
using Telegram.Bot.Types.Enums;
|
||||||
|
using Telegrator.Annotations;
|
||||||
using Telegrator.Aspects;
|
using Telegrator.Aspects;
|
||||||
using Telegrator.Core.Attributes;
|
using Telegrator.Core.Attributes;
|
||||||
using Telegrator.Core.Filters;
|
using Telegrator.Core.Filters;
|
||||||
@@ -42,13 +43,13 @@ namespace Telegrator.Core.Descriptors
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="handlerType">The member info representing the handler type.</param>
|
/// <param name="handlerType">The member info representing the handler type.</param>
|
||||||
/// <returns>The state keeper attribute, or null if not present.</returns>
|
/// <returns>The state keeper attribute, or null if not present.</returns>
|
||||||
public static StateKeeperAttributeBase? GetStateKeeperAttribute(MemberInfo handlerType)
|
public static IFilter<Update>? GetStateKeeperAttribute(MemberInfo handlerType)
|
||||||
{
|
{
|
||||||
// Getting polling handler attribute
|
// Getting polling handler attribute
|
||||||
IEnumerable<StateKeeperAttributeBase> handlerAttrs = handlerType.GetCustomAttributes<StateKeeperAttributeBase>();
|
Attribute stateAttr = handlerType.GetCustomAttribute(typeof(StateAttribute<,>));
|
||||||
|
|
||||||
//
|
//
|
||||||
return handlerAttrs.Any() ? handlerAttrs.Single() : null;
|
return stateAttr as IFilter<Update>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -9,6 +9,11 @@ namespace Telegrator.Core.Filters
|
|||||||
/// <typeparam name="T">The type of the input for the filter.</typeparam>
|
/// <typeparam name="T">The type of the input for the filter.</typeparam>
|
||||||
public class FilterExecutionContext<T> where T : class
|
public class FilterExecutionContext<T> where T : class
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the <see cref="ITelegramBotInfo"/> for the current context.
|
||||||
|
/// </summary>
|
||||||
|
public IUpdateRouter UpdateRouter { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the <see cref="ITelegramBotInfo"/> for the current context.
|
/// Gets the <see cref="ITelegramBotInfo"/> for the current context.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -42,13 +47,15 @@ namespace Telegrator.Core.Filters
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="FilterExecutionContext{T}"/> class with all parameters.
|
/// Initializes a new instance of the <see cref="FilterExecutionContext{T}"/> class with all parameters.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="router">The router, that invoked filter.</param>
|
||||||
/// <param name="botInfo">The bot info.</param>
|
/// <param name="botInfo">The bot info.</param>
|
||||||
/// <param name="update">The update.</param>
|
/// <param name="update">The update.</param>
|
||||||
/// <param name="input">The input object.</param>
|
/// <param name="input">The input object.</param>
|
||||||
/// <param name="data">The additional data dictionary.</param>
|
/// <param name="data">The additional data dictionary.</param>
|
||||||
/// <param name="completedFilters">The list of completed filters.</param>
|
/// <param name="completedFilters">The list of completed filters.</param>
|
||||||
public FilterExecutionContext(ITelegramBotInfo botInfo, Update update, T input, Dictionary<string, object> data, CompletedFiltersList completedFilters)
|
public FilterExecutionContext(IUpdateRouter router, ITelegramBotInfo botInfo, Update update, T input, Dictionary<string, object> data, CompletedFiltersList completedFilters)
|
||||||
{
|
{
|
||||||
|
UpdateRouter = router;
|
||||||
BotInfo = botInfo;
|
BotInfo = botInfo;
|
||||||
Data = data;
|
Data = data;
|
||||||
CompletedFilters = completedFilters;
|
CompletedFilters = completedFilters;
|
||||||
@@ -60,11 +67,12 @@ namespace Telegrator.Core.Filters
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="FilterExecutionContext{T}"/> class with default data and filters.
|
/// Initializes a new instance of the <see cref="FilterExecutionContext{T}"/> class with default data and filters.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="router">The router, that invoked filter.</param>
|
||||||
/// <param name="botInfo">The bot info.</param>
|
/// <param name="botInfo">The bot info.</param>
|
||||||
/// <param name="update">The update.</param>
|
/// <param name="update">The update.</param>
|
||||||
/// <param name="input">The input object.</param>
|
/// <param name="input">The input object.</param>
|
||||||
public FilterExecutionContext(ITelegramBotInfo botInfo, Update update, T input)
|
public FilterExecutionContext(IUpdateRouter router, ITelegramBotInfo botInfo, Update update, T input)
|
||||||
: this(botInfo, update, input, [], []) { }
|
: this(router, botInfo, update, input, [], []) { }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a child context for a different input type, sharing the same data and completed filters.
|
/// Creates a child context for a different input type, sharing the same data and completed filters.
|
||||||
@@ -73,6 +81,6 @@ namespace Telegrator.Core.Filters
|
|||||||
/// <param name="input">The new input object.</param>
|
/// <param name="input">The new input object.</param>
|
||||||
/// <returns>A new <see cref="FilterExecutionContext{C}"/> instance.</returns>
|
/// <returns>A new <see cref="FilterExecutionContext{C}"/> instance.</returns>
|
||||||
public FilterExecutionContext<C> CreateChild<C>(C input) where C : class
|
public FilterExecutionContext<C> CreateChild<C>(C input) where C : class
|
||||||
=> new FilterExecutionContext<C>(BotInfo, Update, input, Data, CompletedFilters);
|
=> new FilterExecutionContext<C>(UpdateRouter, BotInfo, Update, input, Data, CompletedFilters);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ using Telegram.Bot.Types;
|
|||||||
using Telegram.Bot.Types.Enums;
|
using Telegram.Bot.Types.Enums;
|
||||||
using Telegrator.Core.Descriptors;
|
using Telegrator.Core.Descriptors;
|
||||||
using Telegrator.Core.Filters;
|
using Telegrator.Core.Filters;
|
||||||
|
using Telegrator.Core.States;
|
||||||
using Telegrator.Handlers;
|
using Telegrator.Handlers;
|
||||||
|
|
||||||
namespace Telegrator.Core.Handlers
|
namespace Telegrator.Core.Handlers
|
||||||
@@ -47,6 +48,11 @@ namespace Telegrator.Core.Handlers
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
protected IAwaitingProvider AwaitingProvider => Container.AwaitingProvider;
|
protected IAwaitingProvider AwaitingProvider => Container.AwaitingProvider;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Storage of bot states.
|
||||||
|
/// </summary>
|
||||||
|
protected IStateStorage StateStorage => Container.StateStorage;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance and checks that the update type matches <typeparamref name="TUpdate"/>.
|
/// Initializes a new instance and checks that the update type matches <typeparamref name="TUpdate"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
using Telegram.Bot.Types;
|
using Telegram.Bot.Types;
|
||||||
using Telegram.Bot.Types.Enums;
|
using Telegram.Bot.Types.Enums;
|
||||||
using Telegrator.Annotations.StateKeeping;
|
|
||||||
using Telegrator.Core.Descriptors;
|
using Telegrator.Core.Descriptors;
|
||||||
using Telegrator.Core.Filters;
|
using Telegrator.Core.Filters;
|
||||||
using Telegrator.Core.StateKeeping;
|
using Telegrator.Core.States;
|
||||||
|
using Telegrator.Filters;
|
||||||
|
|
||||||
namespace Telegrator.Core.Handlers.Building
|
namespace Telegrator.Core.Handlers.Building
|
||||||
{
|
{
|
||||||
@@ -140,35 +140,15 @@ namespace Telegrator.Core.Handlers.Building
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sets a state keeper for the handler using a specific state and key resolver.
|
/// Sets a state keeper for the handler using a specific state and key resolver.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="TKey">The type of the key.</typeparam>
|
/// <typeparam name="TKey">The key resolver.</typeparam>
|
||||||
/// <typeparam name="TState">The type of the state.</typeparam>
|
/// <typeparam name="TValue">The state value.</typeparam>
|
||||||
/// <typeparam name="TKeeper">The type of the state keeper.</typeparam>
|
/// <param name="state">The state value.</param>
|
||||||
/// <param name="myState">The state value.</param>
|
|
||||||
/// <param name="keyResolver">The key resolver.</param>
|
|
||||||
/// <returns>The builder instance.</returns>
|
/// <returns>The builder instance.</returns>
|
||||||
public void SetStateKeeper<TKey, TState, TKeeper>(TState myState, IStateKeyResolver<TKey> keyResolver)
|
public void SetState<TKey, TValue>(TValue? state)
|
||||||
where TKey : notnull
|
where TKey : IStateKeyResolver, new()
|
||||||
where TState : IEquatable<TState>
|
where TValue : IEquatable<TValue>
|
||||||
where TKeeper : StateKeeperBase<TKey, TState>, new()
|
|
||||||
{
|
{
|
||||||
StateKeeper = new StateKeepFilter<TKey, TState, TKeeper>(myState, keyResolver);
|
StateKeeper = new StateKeyFilter<TKey, TValue>(state);
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Sets a state keeper for the handler using a special state and key resolver.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="TKey">The type of the key.</typeparam>
|
|
||||||
/// <typeparam name="TState">The type of the state.</typeparam>
|
|
||||||
/// <typeparam name="TKeeper">The type of the state keeper.</typeparam>
|
|
||||||
/// <param name="specialState">The special state value.</param>
|
|
||||||
/// <param name="keyResolver">The key resolver.</param>
|
|
||||||
/// <returns>The builder instance.</returns>
|
|
||||||
public void SetStateKeeper<TKey, TState, TKeeper>(SpecialState specialState, IStateKeyResolver<TKey> keyResolver)
|
|
||||||
where TKey : notnull
|
|
||||||
where TState : IEquatable<TState>
|
|
||||||
where TKeeper : StateKeeperBase<TKey, TState>, new()
|
|
||||||
{
|
|
||||||
StateKeeper = new StateKeepFilter<TKey, TState, TKeeper>(specialState, keyResolver);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using Telegrator.Core.StateKeeping;
|
using Telegrator.Core.States;
|
||||||
|
|
||||||
namespace Telegrator.Core.Handlers.Building
|
namespace Telegrator.Core.Handlers.Building
|
||||||
{
|
{
|
||||||
@@ -11,9 +11,9 @@ namespace Telegrator.Core.Handlers.Building
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Awaits an update using the specified key resolver and cancellation token.
|
/// Awaits an update using the specified key resolver and cancellation token.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="keyResolver">The <see cref="IStateKeyResolver{TKey}"/> to resolve the key.</param>
|
/// <param name="keyResolver">The <see cref="IStateKeyResolver"/> to resolve the key.</param>
|
||||||
/// <param name="cancellationToken">The cancellation token.</param>
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
/// <returns>A <see cref="Task{TUpdate}"/> representing the awaited update.</returns>
|
/// <returns>A <see cref="Task{TUpdate}"/> representing the awaited update.</returns>
|
||||||
public Task<TUpdate> Await(IStateKeyResolver<long> keyResolver, CancellationToken cancellationToken = default);
|
public Task<TUpdate> Await(IStateKeyResolver keyResolver, CancellationToken cancellationToken = default);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
using Telegram.Bot.Types;
|
using Telegram.Bot.Types;
|
||||||
using Telegrator.Annotations.StateKeeping;
|
|
||||||
using Telegrator.Core.Filters;
|
using Telegrator.Core.Filters;
|
||||||
using Telegrator.Core.StateKeeping;
|
using Telegrator.Core.States;
|
||||||
|
|
||||||
namespace Telegrator.Core.Handlers.Building
|
namespace Telegrator.Core.Handlers.Building
|
||||||
{
|
{
|
||||||
@@ -56,30 +55,13 @@ namespace Telegrator.Core.Handlers.Building
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sets a state keeper for the handler using a specific state and key resolver.
|
/// Sets a state keeper for the handler using a specific state and key resolver.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="TKey">The type of the key.</typeparam>
|
/// <typeparam name="TKey">The key resolver.</typeparam>
|
||||||
/// <typeparam name="TState">The type of the state.</typeparam>
|
/// <typeparam name="TValue">The state value.</typeparam>
|
||||||
/// <typeparam name="TKeeper">The type of the state keeper.</typeparam>
|
/// <param name="state">The state value.</param>
|
||||||
/// <param name="myState">The state value.</param>
|
|
||||||
/// <param name="keyResolver">The key resolver.</param>
|
|
||||||
/// <returns>The builder instance.</returns>
|
/// <returns>The builder instance.</returns>
|
||||||
public void SetStateKeeper<TKey, TState, TKeeper>(TState myState, IStateKeyResolver<TKey> keyResolver)
|
public void SetState<TKey, TValue>(TValue? state)
|
||||||
where TKey : notnull
|
where TKey : IStateKeyResolver, new()
|
||||||
where TState : IEquatable<TState>
|
where TValue : IEquatable<TValue>;
|
||||||
where TKeeper : StateKeeperBase<TKey, TState>, new();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Sets a state keeper for the handler using a special state and key resolver.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="TKey">The type of the key.</typeparam>
|
|
||||||
/// <typeparam name="TState">The type of the state.</typeparam>
|
|
||||||
/// <typeparam name="TKeeper">The type of the state keeper.</typeparam>
|
|
||||||
/// <param name="specialState">The special state value.</param>
|
|
||||||
/// <param name="keyResolver">The key resolver.</param>
|
|
||||||
/// <returns>The builder instance.</returns>
|
|
||||||
public void SetStateKeeper<TKey, TState, TKeeper>(SpecialState specialState, IStateKeyResolver<TKey> keyResolver)
|
|
||||||
where TKey : notnull
|
|
||||||
where TState : IEquatable<TState>
|
|
||||||
where TKeeper : StateKeeperBase<TKey, TState>, new();
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds a targeted filter for a specific filter target type.
|
/// Adds a targeted filter for a specific filter target type.
|
||||||
|
|||||||
@@ -1,80 +0,0 @@
|
|||||||
using Telegram.Bot.Types;
|
|
||||||
using Telegrator.Annotations.StateKeeping;
|
|
||||||
using Telegrator.Core.Filters;
|
|
||||||
using Telegrator.Core.StateKeeping;
|
|
||||||
using Telegrator.Filters;
|
|
||||||
|
|
||||||
namespace Telegrator.Core.Handlers.Building
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Filter for state keeping logic, allowing filtering based on state and special state conditions.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="TKey">The type of the key for state resolution.</typeparam>
|
|
||||||
/// <typeparam name="TState">The type of the state.</typeparam>
|
|
||||||
/// <typeparam name="TKeeper">The type of the state keeper.</typeparam>
|
|
||||||
public class StateKeepFilter<TKey, TState, TKeeper> : Filter<Update>
|
|
||||||
where TKey : notnull
|
|
||||||
where TState : IEquatable<TState>
|
|
||||||
where TKeeper : StateKeeperBase<TKey, TState>, new()
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the state keeper instance.
|
|
||||||
/// </summary>
|
|
||||||
public static TKeeper StateKeeper { get; internal set; } = null!;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the state value for this filter.
|
|
||||||
/// </summary>
|
|
||||||
public TState MyState { get; private set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the special state value for this filter.
|
|
||||||
/// </summary>
|
|
||||||
public SpecialState SpecialState { get; private set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="StateKeepFilter{TKey, TState, TKeeper}"/> class with a specific state.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="myState">The state value.</param>
|
|
||||||
/// <param name="keyResolver">The key resolver.</param>
|
|
||||||
public StateKeepFilter(TState myState, IStateKeyResolver<TKey> keyResolver)
|
|
||||||
{
|
|
||||||
StateKeeper ??= new TKeeper();
|
|
||||||
StateKeeper.KeyResolver = keyResolver;
|
|
||||||
MyState = myState;
|
|
||||||
SpecialState = SpecialState.None;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="StateKeepFilter{TKey, TState, TKeeper}"/> class with a special state.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="specialState">The special state value.</param>
|
|
||||||
/// <param name="keyResolver">The key resolver.</param>
|
|
||||||
public StateKeepFilter(SpecialState specialState, IStateKeyResolver<TKey> keyResolver)
|
|
||||||
{
|
|
||||||
StateKeeper ??= new TKeeper();
|
|
||||||
StateKeeper.KeyResolver = keyResolver;
|
|
||||||
MyState = StateKeeper.DefaultState;
|
|
||||||
SpecialState = specialState;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Determines whether the filter can pass for the given context based on state logic.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="context">The filter execution context.</param>
|
|
||||||
/// <returns>True if the filter passes; otherwise, false.</returns>
|
|
||||||
public override bool CanPass(FilterExecutionContext<Update> context)
|
|
||||||
{
|
|
||||||
if (SpecialState == SpecialState.AnyState)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
if (!StateKeeper.TryGetState(context.Input, out TState? state))
|
|
||||||
return SpecialState == SpecialState.NoState;
|
|
||||||
|
|
||||||
if (state == null)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return MyState.Equals(state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
using Telegram.Bot;
|
using Telegram.Bot;
|
||||||
using Telegram.Bot.Types;
|
using Telegram.Bot.Types;
|
||||||
using Telegrator.Core.Filters;
|
using Telegrator.Core.Filters;
|
||||||
|
using Telegrator.Core.States;
|
||||||
|
|
||||||
namespace Telegrator.Core.Handlers
|
namespace Telegrator.Core.Handlers
|
||||||
{
|
{
|
||||||
@@ -23,5 +24,8 @@ namespace Telegrator.Core.Handlers
|
|||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public IAwaitingProvider AwaitingProvider => throw new NotImplementedException();
|
public IAwaitingProvider AwaitingProvider => throw new NotImplementedException();
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public IStateStorage StateStorage => throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using Telegram.Bot;
|
using Telegram.Bot;
|
||||||
using Telegram.Bot.Types;
|
using Telegram.Bot.Types;
|
||||||
using Telegrator.Core.Filters;
|
using Telegrator.Core.Filters;
|
||||||
|
using Telegrator.Core.States;
|
||||||
|
|
||||||
namespace Telegrator.Core.Handlers
|
namespace Telegrator.Core.Handlers
|
||||||
{
|
{
|
||||||
@@ -34,5 +35,10 @@ namespace Telegrator.Core.Handlers
|
|||||||
/// Gets the <see cref="IAwaitingProvider"/> for awaiting operations.
|
/// Gets the <see cref="IAwaitingProvider"/> for awaiting operations.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IAwaitingProvider AwaitingProvider { get; }
|
public IAwaitingProvider AwaitingProvider { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the <see cref="IStateStorage"/> for state managment.
|
||||||
|
/// </summary>
|
||||||
|
public IStateStorage StateStorage { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -125,7 +125,7 @@ namespace Telegrator.Core.Handlers
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal IHandlerContainer GetContainer(DescribedHandlerDescriptor handlerInfo)
|
private IHandlerContainer GetContainer(DescribedHandlerDescriptor handlerInfo)
|
||||||
{
|
{
|
||||||
if (this is IHandlerContainerFactory handlerDefainedContainerFactory)
|
if (this is IHandlerContainerFactory handlerDefainedContainerFactory)
|
||||||
return handlerDefainedContainerFactory.CreateContainer(handlerInfo);
|
return handlerDefainedContainerFactory.CreateContainer(handlerInfo);
|
||||||
|
|||||||
@@ -1,19 +0,0 @@
|
|||||||
namespace Telegrator.Core
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Interface for polling providers that manage both regular and awaiting handlers.
|
|
||||||
/// Provides access to handlers for different types of update processing during polling operations.
|
|
||||||
/// </summary>
|
|
||||||
public interface IPollingProvider
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the <see cref="IHandlersProvider"/> that manages handlers for polling.
|
|
||||||
/// </summary>
|
|
||||||
public IHandlersProvider HandlersProvider { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the <see cref="IAwaitingProvider"/> that manages awaiting handlers for polling.
|
|
||||||
/// </summary>
|
|
||||||
public IAwaitingProvider AwaitingProvider { get; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
using Telegram.Bot.Polling;
|
using Telegram.Bot.Polling;
|
||||||
using Telegrator.Core.Handlers;
|
using Telegrator.Core.Handlers;
|
||||||
|
using Telegrator.Core.States;
|
||||||
|
|
||||||
namespace Telegrator.Core
|
namespace Telegrator.Core
|
||||||
{
|
{
|
||||||
@@ -7,7 +8,7 @@ namespace Telegrator.Core
|
|||||||
/// Interface for update routers that handle incoming updates and manage handler execution.
|
/// Interface for update routers that handle incoming updates and manage handler execution.
|
||||||
/// Combines update handling capabilities with polling provider functionality and exception handling.
|
/// Combines update handling capabilities with polling provider functionality and exception handling.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface IUpdateRouter : IUpdateHandler, IPollingProvider
|
public interface IUpdateRouter : IUpdateHandler
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the <see cref="TelegratorOptions"/> for the router.
|
/// Gets the <see cref="TelegratorOptions"/> for the router.
|
||||||
@@ -19,6 +20,21 @@ namespace Telegrator.Core
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public IUpdateHandlersPool HandlersPool { get; }
|
public IUpdateHandlersPool HandlersPool { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the <see cref="IHandlersProvider"/> that manages handlers for polling.
|
||||||
|
/// </summary>
|
||||||
|
public IHandlersProvider HandlersProvider { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the <see cref="IAwaitingProvider"/> that manages awaiting handlers for polling.
|
||||||
|
/// </summary>
|
||||||
|
public IAwaitingProvider AwaitingProvider { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the <see cref="IStateStorage"/> that manages storing of handlers state.
|
||||||
|
/// </summary>
|
||||||
|
public IStateStorage StateStorage { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the <see cref="IRouterExceptionHandler"/> for handling exceptions.
|
/// Gets or sets the <see cref="IRouterExceptionHandler"/> for handling exceptions.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -1,18 +0,0 @@
|
|||||||
using Telegram.Bot.Types;
|
|
||||||
|
|
||||||
namespace Telegrator.Core.StateKeeping
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Defines a resolver for extracting a key from an update for state keeping purposes.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="TKey">The type of the key.</typeparam>
|
|
||||||
public interface IStateKeyResolver<TKey> where TKey : notnull
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Resolves a key from the specified <see cref="Update"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="keySource">The update to resolve the key from.</param>
|
|
||||||
/// <returns>The resolved key.</returns>
|
|
||||||
public TKey ResolveKey(Update keySource);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,150 +0,0 @@
|
|||||||
using Telegram.Bot.Types;
|
|
||||||
|
|
||||||
namespace Telegrator.Core.StateKeeping
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Base class for managing state associated with updates and keys.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="TKey">The type of the key used for state resolution.</typeparam>
|
|
||||||
/// <typeparam name="TState">The type of the state.</typeparam>
|
|
||||||
public abstract class StateKeeperBase<TKey, TState> where TState : notnull where TKey : notnull
|
|
||||||
{
|
|
||||||
private readonly Dictionary<TKey, TState> States = [];
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the key resolver used to resolve keys from updates.
|
|
||||||
/// </summary>
|
|
||||||
public IStateKeyResolver<TKey> KeyResolver { get; set; } = null!;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the default state value.
|
|
||||||
/// </summary>
|
|
||||||
public abstract TState DefaultState { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Sets the state for the specified update.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="keySource">The update to use as a key source.</param>
|
|
||||||
/// <param name="newState">The new state value.</param>
|
|
||||||
public virtual void SetState(Update keySource, TState newState)
|
|
||||||
{
|
|
||||||
TKey key = KeyResolver.ResolveKey(keySource);
|
|
||||||
States.Set(key, newState, DefaultState);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the state for the specified update.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="keySource">The update to use as a key source.</param>
|
|
||||||
/// <returns>The state value.</returns>
|
|
||||||
public virtual TState GetState(Update keySource)
|
|
||||||
{
|
|
||||||
TKey key = KeyResolver.ResolveKey(keySource);
|
|
||||||
return States[key];
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Tries to get the state for the specified update.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="keySource">The update to use as a key source.</param>
|
|
||||||
/// <param name="state">When this method returns, contains the state value if found; otherwise, the default value.</param>
|
|
||||||
/// <returns>True if the state was found; otherwise, false.</returns>
|
|
||||||
public virtual bool TryGetState(Update keySource, out TState? state)
|
|
||||||
{
|
|
||||||
TKey key = KeyResolver.ResolveKey(keySource);
|
|
||||||
return States.TryGetValue(key, out state);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Determines whether a state exists for the specified update.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="keySource">The update to use as a key source.</param>
|
|
||||||
/// <returns>True if the state exists; otherwise, false.</returns>
|
|
||||||
public virtual bool HasState(Update keySource)
|
|
||||||
{
|
|
||||||
TKey key = KeyResolver.ResolveKey(keySource);
|
|
||||||
return States.ContainsKey(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a state for the specified update using the default state value.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="keySource">The update to use as a key source.</param>
|
|
||||||
public virtual void CreateState(Update keySource)
|
|
||||||
{
|
|
||||||
TKey key = KeyResolver.ResolveKey(keySource);
|
|
||||||
States.Set(key, DefaultState);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Deletes the state for the specified update.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="keySource">The update to use as a key source.</param>
|
|
||||||
public virtual void DeleteState(Update keySource)
|
|
||||||
{
|
|
||||||
TKey key = KeyResolver.ResolveKey(keySource);
|
|
||||||
States.Remove(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Moves the state forward for the specified update.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="keySource">The update to use as a key source.</param>
|
|
||||||
public virtual void MoveForward(Update keySource)
|
|
||||||
{
|
|
||||||
TKey key = KeyResolver.ResolveKey(keySource);
|
|
||||||
if (!States.TryGetValue(key, out TState currentState))
|
|
||||||
{
|
|
||||||
States.Set(key, DefaultState);
|
|
||||||
currentState = DefaultState;
|
|
||||||
}
|
|
||||||
|
|
||||||
TState newState = MoveForward(currentState, key);
|
|
||||||
States[key] = newState;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Moves the state backward for the specified update.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="keySource">The update to use as a key source.</param>
|
|
||||||
public virtual void MoveBackward(Update keySource)
|
|
||||||
{
|
|
||||||
TKey key = KeyResolver.ResolveKey(keySource);
|
|
||||||
if (!States.TryGetValue(key, out TState currentState))
|
|
||||||
{
|
|
||||||
States.Set(key, DefaultState);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
TState newState = MoveBackward(currentState, key);
|
|
||||||
States[key] = newState;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the state keeper for the specified key.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="TStateKeeper">The type of the state keeper.</typeparam>
|
|
||||||
/// <param name="key">The key.</param>
|
|
||||||
/// <returns>The state keeper instance.</returns>
|
|
||||||
protected virtual TStateKeeper GetKeeper<TStateKeeper>(TKey key) where TStateKeeper : StateKeeperBase<TKey, TState>
|
|
||||||
=> States[key] as TStateKeeper ?? throw new InvalidCastException();
|
|
||||||
*/
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Moves the state forward for the specified current state and key.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="currentState">The current state value.</param>
|
|
||||||
/// <param name="currentKey">The key.</param>
|
|
||||||
/// <returns>The new state value.</returns>
|
|
||||||
protected abstract TState MoveForward(TState currentState, TKey currentKey);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Moves the state backward for the specified current state and key.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="currentState">The current state value.</param>
|
|
||||||
/// <param name="currentKey">The key.</param>
|
|
||||||
/// <returns>The new state value.</returns>
|
|
||||||
protected abstract TState MoveBackward(TState currentState, TKey currentKey);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
using Telegram.Bot.Types;
|
||||||
|
|
||||||
|
namespace Telegrator.Core.States;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Defines a resolver for extracting a key from an update for state keeping purposes.
|
||||||
|
/// </summary>
|
||||||
|
public interface IStateKeyResolver
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Resolves a key from the specified <see cref="Update"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="keySource">The update to resolve the key from.</param>
|
||||||
|
/// <returns>The resolved key.</returns>
|
||||||
|
public string? ResolveKey(Update keySource);
|
||||||
|
}
|
||||||
@@ -0,0 +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);
|
||||||
|
}
|
||||||
@@ -0,0 +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);
|
||||||
|
}
|
||||||
@@ -1,31 +1,44 @@
|
|||||||
using Telegram.Bot.Types;
|
using Telegram.Bot.Types;
|
||||||
using Telegrator.Core.Filters;
|
using Telegrator.Core.Filters;
|
||||||
using Telegrator.Core.StateKeeping;
|
using Telegrator.Core.States;
|
||||||
|
|
||||||
namespace Telegrator.Filters
|
namespace Telegrator.Filters
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Filters updates by comparing a resolved state key with a target key.
|
/// Filters updates by comparing a resolved state key with a target key.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="TKey">The type of the key used for state resolution.</typeparam>
|
/// <typeparam name="TKey">The type of the key resolver used to get state key.</typeparam>
|
||||||
public class StateKeyFilter<TKey> : Filter<Update> where TKey : IEquatable<TKey>
|
/// <typeparam name="TValue">The type of the key used for state resolution.</typeparam>
|
||||||
|
public class StateKeyFilter<TKey, TValue> : Filter<Update>
|
||||||
|
where TKey : IStateKeyResolver, new()
|
||||||
|
where TValue : IEquatable<TValue>
|
||||||
{
|
{
|
||||||
private readonly IStateKeyResolver<TKey> KeyResolver;
|
private readonly TValue? TargetKey;
|
||||||
private readonly TKey TargetKey;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="StateKeyFilter{TKey}"/> class.
|
/// Initializes a new instance of the <see cref="StateKeyFilter{TKey, TValue}"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="keyResolver">The key resolver to extract the key from the update.</param>
|
|
||||||
/// <param name="targetKey">The target key to compare with.</param>
|
/// <param name="targetKey">The target key to compare with.</param>
|
||||||
public StateKeyFilter(IStateKeyResolver<TKey> keyResolver, TKey targetKey)
|
public StateKeyFilter(TValue? targetKey)
|
||||||
{
|
{
|
||||||
KeyResolver = keyResolver;
|
|
||||||
TargetKey = targetKey;
|
TargetKey = targetKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public override bool CanPass(FilterExecutionContext<Update> context)
|
public override bool CanPass(FilterExecutionContext<Update> context)
|
||||||
=> KeyResolver.ResolveKey(context.Input).Equals(TargetKey);
|
{
|
||||||
|
string? key = new TKey().ResolveKey(context.Input);
|
||||||
|
if (key is null)
|
||||||
|
return TargetKey is null;
|
||||||
|
|
||||||
|
TValue? value = context.UpdateRouter.StateStorage.GetAsync<TValue>(key).Result;
|
||||||
|
if (value is null)
|
||||||
|
return TargetKey is null;
|
||||||
|
|
||||||
|
if (TargetKey is null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return TargetKey.Equals(value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
using Telegram.Bot.Types;
|
using Telegram.Bot.Types;
|
||||||
using Telegram.Bot.Types.Enums;
|
using Telegram.Bot.Types.Enums;
|
||||||
using Telegrator.Filters;
|
|
||||||
using Telegrator.StateKeeping;
|
|
||||||
using Telegrator.Core;
|
using Telegrator.Core;
|
||||||
using Telegrator.Core.Handlers.Building;
|
|
||||||
using Telegrator.Core.Descriptors;
|
using Telegrator.Core.Descriptors;
|
||||||
using Telegrator.Core.StateKeeping;
|
using Telegrator.Core.Handlers.Building;
|
||||||
|
using Telegrator.Core.States;
|
||||||
|
using Telegrator.Filters;
|
||||||
|
using Telegrator.States;
|
||||||
|
|
||||||
namespace Telegrator.Handlers.Building
|
namespace Telegrator.Handlers.Building
|
||||||
{
|
{
|
||||||
@@ -56,9 +56,21 @@ namespace Telegrator.Handlers.Building
|
|||||||
/// <param name="keyResolver">The state key resolver to use for filtering updates.</param>
|
/// <param name="keyResolver">The state key resolver to use for filtering updates.</param>
|
||||||
/// <param name="cancellationToken">The cancellation token to cancel the wait operation.</param>
|
/// <param name="cancellationToken">The cancellation token to cancel the wait operation.</param>
|
||||||
/// <returns>The awaited update of type TUpdate.</returns>
|
/// <returns>The awaited update of type TUpdate.</returns>
|
||||||
public async Task<TUpdate> Await(IStateKeyResolver<long> keyResolver, CancellationToken cancellationToken = default)
|
public async Task<TUpdate> Await(IStateKeyResolver keyResolver, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
Filters.Add(new StateKeyFilter<long>(keyResolver, keyResolver.ResolveKey(HandlingUpdate)));
|
string? handlingKey = keyResolver.ResolveKey(HandlingUpdate);
|
||||||
|
if (handlingKey is null)
|
||||||
|
throw new InvalidOperationException("Cannot await update with resolved key as NULL");
|
||||||
|
|
||||||
|
Filters.Add(Filter<Update>.If(ctx =>
|
||||||
|
{
|
||||||
|
string? key = keyResolver.ResolveKey(ctx.Update);
|
||||||
|
if (key is null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return key == handlingKey;
|
||||||
|
}));
|
||||||
|
|
||||||
AwaiterHandler handlerInstance = new AwaiterHandler(UpdateType);
|
AwaiterHandler handlerInstance = new AwaiterHandler(UpdateType);
|
||||||
HandlerDescriptor descriptor = BuildImplicitDescriptor(handlerInstance);
|
HandlerDescriptor descriptor = BuildImplicitDescriptor(handlerInstance);
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
using Telegram.Bot.Types;
|
using Telegram.Bot.Types;
|
||||||
using Telegrator.Annotations.StateKeeping;
|
|
||||||
using Telegrator.Core.Filters;
|
using Telegrator.Core.Filters;
|
||||||
using Telegrator.Core.Handlers.Building;
|
using Telegrator.Core.Handlers.Building;
|
||||||
using Telegrator.Core.StateKeeping;
|
using Telegrator.Core.States;
|
||||||
using Telegrator.StateKeeping;
|
|
||||||
|
|
||||||
namespace Telegrator.Handlers.Building
|
namespace Telegrator.Handlers.Building
|
||||||
{
|
{
|
||||||
@@ -61,25 +59,13 @@ namespace Telegrator.Handlers.Building
|
|||||||
return handlerBuilder;
|
return handlerBuilder;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc cref="HandlerBuilderBase.SetStateKeeper{TKey, TState, TKeeper}(TState, IStateKeyResolver{TKey})"/>
|
/// <inheritdoc cref="HandlerBuilderBase.SetState{TKey, TValue}(TValue?)"/>
|
||||||
public static TBuilder SetStateKeeper<TBuilder, TKey, TState, TKeeper>(this TBuilder handlerBuilder, TState myState, IStateKeyResolver<TKey> keyResolver)
|
public static TBuilder SetState<TBuilder, TKey, TValue>(this TBuilder handlerBuilder, TValue? myState)
|
||||||
where TBuilder : HandlerBuilderBase
|
where TBuilder : HandlerBuilderBase
|
||||||
where TKey : notnull
|
where TKey : IStateKeyResolver, new()
|
||||||
where TState : IEquatable<TState>
|
where TValue : IEquatable<TValue>
|
||||||
where TKeeper : StateKeeperBase<TKey, TState>, new()
|
|
||||||
{
|
{
|
||||||
handlerBuilder.SetStateKeeper<TKey, TState, TKeeper>(myState, keyResolver);
|
handlerBuilder.SetState<TKey, TValue>(myState);
|
||||||
return handlerBuilder;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc cref="HandlerBuilderBase.SetStateKeeper{TKey, TState, TKeeper}(SpecialState, IStateKeyResolver{TKey})"/>
|
|
||||||
public static TBuilder SetStateKeeper<TBuilder, TKey, TState, TKeeper>(this TBuilder handlerBuilder, SpecialState specialState, IStateKeyResolver<TKey> keyResolver)
|
|
||||||
where TBuilder : HandlerBuilderBase
|
|
||||||
where TKey : notnull
|
|
||||||
where TState : IEquatable<TState>
|
|
||||||
where TKeeper : StateKeeperBase<TKey, TState>, new()
|
|
||||||
{
|
|
||||||
handlerBuilder.SetStateKeeper<TKey, TState, TKeeper>(specialState, keyResolver);
|
|
||||||
return handlerBuilder;
|
return handlerBuilder;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,129 +102,5 @@ namespace Telegrator.Handlers.Building
|
|||||||
handlerBuilder.AddTargetedFilters(getFilterringTarget, filters);
|
handlerBuilder.AddTargetedFilters(getFilterringTarget, filters);
|
||||||
return handlerBuilder;
|
return handlerBuilder;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Sets a numeric state keeper with a custom key resolver.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="TBuilder">The type of the handler builder.</typeparam>
|
|
||||||
/// <param name="handlerBuilder">The handler builder.</param>
|
|
||||||
/// <param name="myState">The numeric state value.</param>
|
|
||||||
/// <param name="keyResolver">The key resolver for the state.</param>
|
|
||||||
/// <returns>The handler builder for method chaining.</returns>
|
|
||||||
public static TBuilder SetNumericState<TBuilder>(this TBuilder handlerBuilder, int myState, IStateKeyResolver<long> keyResolver)
|
|
||||||
where TBuilder : HandlerBuilderBase
|
|
||||||
{
|
|
||||||
handlerBuilder.SetStateKeeper<long, int, NumericStateKeeper>(myState, keyResolver);
|
|
||||||
return handlerBuilder;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Sets a numeric state keeper with a special state and custom key resolver.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="TBuilder">The type of the handler builder.</typeparam>
|
|
||||||
/// <param name="handlerBuilder">The handler builder.</param>
|
|
||||||
/// <param name="specialState">The special state value.</param>
|
|
||||||
/// <param name="keyResolver">The key resolver for the state.</param>
|
|
||||||
/// <returns>The handler builder for method chaining.</returns>
|
|
||||||
public static TBuilder SetNumericState<TBuilder>(this TBuilder handlerBuilder, SpecialState specialState, IStateKeyResolver<long> keyResolver)
|
|
||||||
where TBuilder : HandlerBuilderBase
|
|
||||||
{
|
|
||||||
handlerBuilder.SetStateKeeper<long, int, NumericStateKeeper>(specialState, keyResolver);
|
|
||||||
return handlerBuilder;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Sets a numeric state keeper with the default sender ID resolver.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="TBuilder">The type of the handler builder.</typeparam>
|
|
||||||
/// <param name="handlerBuilder">The handler builder.</param>
|
|
||||||
/// <param name="myState">The numeric state value.</param>
|
|
||||||
/// <returns>The handler builder for method chaining.</returns>
|
|
||||||
public static TBuilder SetNumericState<TBuilder>(this TBuilder handlerBuilder, int myState)
|
|
||||||
where TBuilder : HandlerBuilderBase
|
|
||||||
{
|
|
||||||
handlerBuilder.SetStateKeeper<long, int, NumericStateKeeper>(myState, new SenderIdResolver());
|
|
||||||
return handlerBuilder;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Sets a numeric state keeper with a special state and the default sender ID resolver.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="TBuilder">The type of the handler builder.</typeparam>
|
|
||||||
/// <param name="handlerBuilder">The handler builder.</param>
|
|
||||||
/// <param name="specialState">The special state value.</param>
|
|
||||||
/// <returns>The handler builder for method chaining.</returns>
|
|
||||||
public static TBuilder SetNumericState<TBuilder>(this TBuilder handlerBuilder, SpecialState specialState)
|
|
||||||
where TBuilder : HandlerBuilderBase
|
|
||||||
{
|
|
||||||
handlerBuilder.SetStateKeeper<long, int, NumericStateKeeper>(specialState, new SenderIdResolver());
|
|
||||||
return handlerBuilder;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Sets an enum state keeper with a custom key resolver.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="TBuilder">The type of the handler builder.</typeparam>
|
|
||||||
/// <typeparam name="TEnum">The type of the enum state.</typeparam>
|
|
||||||
/// <param name="handlerBuilder">The handler builder.</param>
|
|
||||||
/// <param name="myState">The enum state value.</param>
|
|
||||||
/// <param name="keyResolver">The key resolver for the state.</param>
|
|
||||||
/// <returns>The handler builder for method chaining.</returns>
|
|
||||||
public static TBuilder SetEnumState<TBuilder, TEnum>(this TBuilder handlerBuilder, TEnum myState, IStateKeyResolver<long> keyResolver)
|
|
||||||
where TBuilder : HandlerBuilderBase
|
|
||||||
where TEnum : Enum, IEquatable<TEnum>
|
|
||||||
{
|
|
||||||
handlerBuilder.SetStateKeeper<long, TEnum, EnumStateKeeper<TEnum>>(myState, keyResolver);
|
|
||||||
return handlerBuilder;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Sets an enum state keeper with a special state and custom key resolver.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="TBuilder">The type of the handler builder.</typeparam>
|
|
||||||
/// <typeparam name="TEnum">The type of the enum state.</typeparam>
|
|
||||||
/// <param name="handlerBuilder">The handler builder.</param>
|
|
||||||
/// <param name="specialState">The special state value.</param>
|
|
||||||
/// <param name="keyResolver">The key resolver for the state.</param>
|
|
||||||
/// <returns>The handler builder for method chaining.</returns>
|
|
||||||
public static TBuilder SetEnumState<TBuilder, TEnum>(this TBuilder handlerBuilder, SpecialState specialState, IStateKeyResolver<long> keyResolver)
|
|
||||||
where TBuilder : HandlerBuilderBase
|
|
||||||
where TEnum : Enum, IEquatable<TEnum>
|
|
||||||
{
|
|
||||||
handlerBuilder.SetStateKeeper<long, TEnum, EnumStateKeeper<TEnum>>(specialState, keyResolver);
|
|
||||||
return handlerBuilder;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Sets an enum state keeper with the default sender ID resolver.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="TBuilder">The type of the handler builder.</typeparam>
|
|
||||||
/// <typeparam name="TEnum">The type of the enum state.</typeparam>
|
|
||||||
/// <param name="handlerBuilder">The handler builder.</param>
|
|
||||||
/// <param name="myState">The enum state value.</param>
|
|
||||||
/// <returns>The handler builder for method chaining.</returns>
|
|
||||||
public static TBuilder SetEnumState<TBuilder, TEnum>(this TBuilder handlerBuilder, TEnum myState)
|
|
||||||
where TBuilder : HandlerBuilderBase
|
|
||||||
where TEnum : Enum, IEquatable<TEnum>
|
|
||||||
{
|
|
||||||
handlerBuilder.SetStateKeeper<long, TEnum, EnumStateKeeper<TEnum>>(myState, new SenderIdResolver());
|
|
||||||
return handlerBuilder;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Sets an enum state keeper with a special state and the default sender ID resolver.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="TBuilder">The type of the handler builder.</typeparam>
|
|
||||||
/// <typeparam name="TEnum">The type of the enum state.</typeparam>
|
|
||||||
/// <param name="handlerBuilder">The handler builder.</param>
|
|
||||||
/// <param name="specialState">The special state value.</param>
|
|
||||||
/// <returns>The handler builder for method chaining.</returns>
|
|
||||||
public static TBuilder SetEnumState<TBuilder, TEnum>(this TBuilder handlerBuilder, SpecialState specialState)
|
|
||||||
where TBuilder : HandlerBuilderBase
|
|
||||||
where TEnum : Enum, IEquatable<TEnum>
|
|
||||||
{
|
|
||||||
handlerBuilder.SetStateKeeper<long, TEnum, EnumStateKeeper<TEnum>>(specialState, new SenderIdResolver());
|
|
||||||
return handlerBuilder;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,6 +43,9 @@ namespace Telegrator.Handlers
|
|||||||
{
|
{
|
||||||
string[] split = ReceivedCommand.Split('@');
|
string[] split = ReceivedCommand.Split('@');
|
||||||
ReceivedCommand = split[0];
|
ReceivedCommand = split[0];
|
||||||
|
|
||||||
|
if (!split.ElementAtOrDefault(1).Equals(context.BotInfo.User.Username))
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ using Telegram.Bot.Types;
|
|||||||
using Telegrator.Core;
|
using Telegrator.Core;
|
||||||
using Telegrator.Core.Descriptors;
|
using Telegrator.Core.Descriptors;
|
||||||
using Telegrator.Core.Filters;
|
using Telegrator.Core.Filters;
|
||||||
|
using Telegrator.Core.States;
|
||||||
|
|
||||||
namespace Telegrator.Handlers
|
namespace Telegrator.Handlers
|
||||||
{
|
{
|
||||||
@@ -33,6 +34,9 @@ namespace Telegrator.Handlers
|
|||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public IAwaitingProvider AwaitingProvider { get; }
|
public IAwaitingProvider AwaitingProvider { get; }
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public IStateStorage StateStorage { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes new instance of <see cref="HandlerContainer{TUpdate}"/>
|
/// Initializes new instance of <see cref="HandlerContainer{TUpdate}"/>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -45,6 +49,7 @@ namespace Telegrator.Handlers
|
|||||||
ExtraData = handlerInfo.ExtraData;
|
ExtraData = handlerInfo.ExtraData;
|
||||||
CompletedFilters = handlerInfo.CompletedFilters;
|
CompletedFilters = handlerInfo.CompletedFilters;
|
||||||
AwaitingProvider = handlerInfo.AwaitingProvider;
|
AwaitingProvider = handlerInfo.AwaitingProvider;
|
||||||
|
StateStorage = handlerInfo.StateStorage;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -56,7 +61,8 @@ namespace Telegrator.Handlers
|
|||||||
/// <param name="extraData"></param>
|
/// <param name="extraData"></param>
|
||||||
/// <param name="filters"></param>
|
/// <param name="filters"></param>
|
||||||
/// <param name="awaitingProvider"></param>
|
/// <param name="awaitingProvider"></param>
|
||||||
public HandlerContainer(TUpdate actualUpdate, Update handlingUpdate, ITelegramBotClient client, Dictionary<string, object> extraData, CompletedFiltersList filters, IAwaitingProvider awaitingProvider)
|
/// <param name="stateStorage"></param>
|
||||||
|
public HandlerContainer(TUpdate actualUpdate, Update handlingUpdate, ITelegramBotClient client, Dictionary<string, object> extraData, CompletedFiltersList filters, IAwaitingProvider awaitingProvider, IStateStorage stateStorage)
|
||||||
{
|
{
|
||||||
ActualUpdate = actualUpdate;
|
ActualUpdate = actualUpdate;
|
||||||
HandlingUpdate = handlingUpdate;
|
HandlingUpdate = handlingUpdate;
|
||||||
@@ -64,6 +70,7 @@ namespace Telegrator.Handlers
|
|||||||
ExtraData = extraData;
|
ExtraData = extraData;
|
||||||
CompletedFilters = filters;
|
CompletedFilters = filters;
|
||||||
AwaitingProvider = awaitingProvider;
|
AwaitingProvider = awaitingProvider;
|
||||||
|
StateStorage = stateStorage;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -75,8 +82,8 @@ namespace Telegrator.Handlers
|
|||||||
{
|
{
|
||||||
return new HandlerContainer<QUpdate>(
|
return new HandlerContainer<QUpdate>(
|
||||||
HandlingUpdate.GetActualUpdateObject<QUpdate>(),
|
HandlingUpdate.GetActualUpdateObject<QUpdate>(),
|
||||||
HandlingUpdate, Client, ExtraData,
|
HandlingUpdate, Client, ExtraData, CompletedFilters,
|
||||||
CompletedFilters, AwaitingProvider);
|
AwaitingProvider, StateStorage);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -89,8 +96,8 @@ namespace Telegrator.Handlers
|
|||||||
{
|
{
|
||||||
return new HandlerContainer<TUpdate>(
|
return new HandlerContainer<TUpdate>(
|
||||||
other.HandlingUpdate.GetActualUpdateObject<TUpdate>(),
|
other.HandlingUpdate.GetActualUpdateObject<TUpdate>(),
|
||||||
other.HandlingUpdate, other.Client, other.ExtraData,
|
other.HandlingUpdate, other.Client, other.ExtraData, other.CompletedFilters,
|
||||||
other.CompletedFilters, other.AwaitingProvider);
|
other.AwaitingProvider, other.StateStorage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ namespace Telegrator.Mediation
|
|||||||
{
|
{
|
||||||
_handler.Invoke(botClient, exception, source, cancellationToken);
|
_handler.Invoke(botClient, exception, source, cancellationToken);
|
||||||
}
|
}
|
||||||
finally
|
catch
|
||||||
{
|
{
|
||||||
_ = 0xBAD + 0xC0DE;
|
_ = 0xBAD + 0xC0DE;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ using Telegrator.Core;
|
|||||||
using Telegrator.Core.Descriptors;
|
using Telegrator.Core.Descriptors;
|
||||||
using Telegrator.Core.Filters;
|
using Telegrator.Core.Filters;
|
||||||
using Telegrator.Core.Handlers;
|
using Telegrator.Core.Handlers;
|
||||||
|
using Telegrator.Core.States;
|
||||||
using Telegrator.Handlers.Diagnostics;
|
using Telegrator.Handlers.Diagnostics;
|
||||||
using Telegrator.Logging;
|
using Telegrator.Logging;
|
||||||
|
|
||||||
@@ -21,6 +22,7 @@ namespace Telegrator.Mediation
|
|||||||
private readonly TelegratorOptions _options;
|
private readonly TelegratorOptions _options;
|
||||||
private readonly IHandlersProvider _handlersProvider;
|
private readonly IHandlersProvider _handlersProvider;
|
||||||
private readonly IAwaitingProvider _awaitingProvider;
|
private readonly IAwaitingProvider _awaitingProvider;
|
||||||
|
private readonly IStateStorage _stateStorage;
|
||||||
private readonly IUpdateHandlersPool _HandlersPool;
|
private readonly IUpdateHandlersPool _HandlersPool;
|
||||||
private readonly ITelegramBotInfo _botInfo;
|
private readonly ITelegramBotInfo _botInfo;
|
||||||
|
|
||||||
@@ -30,6 +32,9 @@ namespace Telegrator.Mediation
|
|||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public IAwaitingProvider AwaitingProvider => _awaitingProvider;
|
public IAwaitingProvider AwaitingProvider => _awaitingProvider;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public IStateStorage StateStorage => _stateStorage;
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public TelegratorOptions Options => _options;
|
public TelegratorOptions Options => _options;
|
||||||
|
|
||||||
@@ -47,13 +52,15 @@ namespace Telegrator.Mediation
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="handlersProvider">The provider for regular handlers.</param>
|
/// <param name="handlersProvider">The provider for regular handlers.</param>
|
||||||
/// <param name="awaitingProvider">The provider for awaiting handlers.</param>
|
/// <param name="awaitingProvider">The provider for awaiting handlers.</param>
|
||||||
|
/// <param name="stateStorage">The state storage.</param>
|
||||||
/// <param name="options">The bot configuration options.</param>
|
/// <param name="options">The bot configuration options.</param>
|
||||||
/// <param name="botInfo"></param>
|
/// <param name="botInfo"></param>
|
||||||
public UpdateRouter(IHandlersProvider handlersProvider, IAwaitingProvider awaitingProvider, TelegratorOptions options, ITelegramBotInfo botInfo)
|
public UpdateRouter(IHandlersProvider handlersProvider, IAwaitingProvider awaitingProvider, IStateStorage stateStorage, TelegratorOptions options, ITelegramBotInfo botInfo)
|
||||||
{
|
{
|
||||||
_options = options;
|
_options = options;
|
||||||
_handlersProvider = handlersProvider;
|
_handlersProvider = handlersProvider;
|
||||||
_awaitingProvider = awaitingProvider;
|
_awaitingProvider = awaitingProvider;
|
||||||
|
_stateStorage = stateStorage;
|
||||||
_HandlersPool = new UpdateHandlersPool(this, _options, _options.GlobalCancellationToken);
|
_HandlersPool = new UpdateHandlersPool(this, _options, _options.GlobalCancellationToken);
|
||||||
_botInfo = botInfo;
|
_botInfo = botInfo;
|
||||||
}
|
}
|
||||||
@@ -235,7 +242,7 @@ namespace Telegrator.Mediation
|
|||||||
};
|
};
|
||||||
|
|
||||||
UpdateHandlerBase handlerInstance = provider.GetHandlerInstance(descriptor, cancellationToken);
|
UpdateHandlerBase handlerInstance = provider.GetHandlerInstance(descriptor, cancellationToken);
|
||||||
FilterExecutionContext<Update> filterContext = new FilterExecutionContext<Update>(_botInfo, update, update, data, []);
|
FilterExecutionContext<Update> filterContext = new FilterExecutionContext<Update>(this, _botInfo, update, update, data, []);
|
||||||
|
|
||||||
if (descriptor.Filters != null)
|
if (descriptor.Filters != null)
|
||||||
{
|
{
|
||||||
@@ -254,7 +261,7 @@ namespace Telegrator.Mediation
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new DescribedHandlerDescriptor(descriptor, this, AwaitingProvider, client, handlerInstance, filterContext, descriptor.DisplayString);
|
return new DescribedHandlerDescriptor(descriptor, this, AwaitingProvider, StateStorage, client, handlerInstance, filterContext, descriptor.DisplayString);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -1,59 +0,0 @@
|
|||||||
using Telegrator.Core.StateKeeping;
|
|
||||||
|
|
||||||
namespace Telegrator.StateKeeping
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Abstract base class for state keepers that manage state transitions using an array of predefined states.
|
|
||||||
/// Provides forward and backward navigation through a fixed sequence of states.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="TKey">The type of the key used to identify state contexts.</typeparam>
|
|
||||||
/// <typeparam name="TState">The type of the state values. Must be non-null.</typeparam>
|
|
||||||
/// <param name="states">The array of states that define the allowed state sequence.</param>
|
|
||||||
public abstract class ArrayStateKeeper<TKey, TState>(params TState[] states) : StateKeeperBase<TKey, TState> where TState : notnull where TKey : notnull
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The array of states that defines the allowed state sequence for navigation.
|
|
||||||
/// </summary>
|
|
||||||
protected readonly TState[] ArrayStates = states;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Moves to the previous state in the array sequence.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="currentState">The current state to move backward from.</param>
|
|
||||||
/// <param name="_">The key parameter (unused in this implementation).</param>
|
|
||||||
/// <returns>The previous state in the array sequence.</returns>
|
|
||||||
/// <exception cref="ArgumentException">Thrown when the current state is not found in the array.</exception>
|
|
||||||
/// <exception cref="IndexOutOfRangeException">Thrown when trying to move backward from the first state.</exception>
|
|
||||||
protected override TState MoveBackward(TState currentState, TKey _)
|
|
||||||
{
|
|
||||||
int index = Array.IndexOf(ArrayStates, currentState);
|
|
||||||
if (index == -1)
|
|
||||||
throw new ArgumentException("Cannot resolve current state");
|
|
||||||
|
|
||||||
if (index == 0)
|
|
||||||
throw new IndexOutOfRangeException("This state cannot be moved backward");
|
|
||||||
|
|
||||||
return ArrayStates[index - 1];
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Moves to the next state in the array sequence.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="currentState">The current state to move forward from.</param>
|
|
||||||
/// <param name="_">The key parameter (unused in this implementation).</param>
|
|
||||||
/// <returns>The next state in the array sequence.</returns>
|
|
||||||
/// <exception cref="ArgumentException">Thrown when the current state is not found in the array.</exception>
|
|
||||||
/// <exception cref="IndexOutOfRangeException">Thrown when trying to move forward from the last state.</exception>
|
|
||||||
protected override TState MoveForward(TState currentState, TKey _)
|
|
||||||
{
|
|
||||||
int index = Array.IndexOf(ArrayStates, currentState);
|
|
||||||
if (index == -1)
|
|
||||||
throw new ArgumentException("Cannot resolve current state");
|
|
||||||
|
|
||||||
if (index == ArrayStates.Length - 1)
|
|
||||||
throw new IndexOutOfRangeException("This state cannot be moved forward");
|
|
||||||
|
|
||||||
return ArrayStates[index + 1];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,75 +0,0 @@
|
|||||||
using Telegrator.Annotations.StateKeeping;
|
|
||||||
using Telegrator.Core.Handlers;
|
|
||||||
|
|
||||||
namespace Telegrator.StateKeeping
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// State keeper implementation for enum-based states.
|
|
||||||
/// Automatically creates an array of all enum values for state navigation.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="TEnum">The enum type to be used for state management.</typeparam>
|
|
||||||
public class EnumStateKeeper<TEnum>() : ArrayStateKeeper<long, TEnum>(Enum.GetValues(typeof(TEnum)).Cast<TEnum>().ToArray()) where TEnum : Enum
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the default state, which is the first value in the enum.
|
|
||||||
/// </summary>
|
|
||||||
public override TEnum DefaultState => ArrayStates.ElementAt(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Extension methods for working with enum-based states in handler containers.
|
|
||||||
/// Provides convenient methods for state management operations.
|
|
||||||
/// </summary>
|
|
||||||
public static partial class StateHandlerContainerExtensions
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the enum state keeper for the specified enum type.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="TEnum">The enum type to get the state keeper for.</typeparam>
|
|
||||||
/// <param name="_">The handler container (unused parameter for extension method syntax).</param>
|
|
||||||
/// <returns>The enum state keeper instance.</returns>
|
|
||||||
public static EnumStateKeeper<TEnum> EnumStateKeeper<TEnum>(this IHandlerContainer _) where TEnum : Enum
|
|
||||||
=> EnumStateAttribute<TEnum>.Shared;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a new enum state for the current update.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="TEnum">The enum type for state management.</typeparam>
|
|
||||||
/// <param name="container">The handler container.</param>
|
|
||||||
public static void CreateEnumState<TEnum>(this IHandlerContainer container) where TEnum : Enum
|
|
||||||
=> container.EnumStateKeeper<TEnum>().CreateState(container.HandlingUpdate);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Deletes the enum state for the current update.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="TEnum">The enum type for state management.</typeparam>
|
|
||||||
/// <param name="container">The handler container.</param>
|
|
||||||
public static void DeleteEnumState<TEnum>(this IHandlerContainer container) where TEnum : Enum
|
|
||||||
=> container.EnumStateKeeper<TEnum>().DeleteState(container.HandlingUpdate);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Sets the enum state to a specific value for the current update.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="TEnum">The enum type for state management.</typeparam>
|
|
||||||
/// <param name="container">The handler container.</param>
|
|
||||||
/// <param name="newState">The new state value. If null, uses the default state.</param>
|
|
||||||
public static void SetEnumState<TEnum>(this IHandlerContainer container, TEnum? newState) where TEnum : Enum
|
|
||||||
=> container.EnumStateKeeper<TEnum>().SetState(container.HandlingUpdate, newState ?? EnumStateAttribute<TEnum>.DefaultState);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Moves the enum state forward to the next value in the enum sequence.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="TEnum">The enum type for state management.</typeparam>
|
|
||||||
/// <param name="container">The handler container.</param>
|
|
||||||
public static void ForwardEnumState<TEnum>(this IHandlerContainer container) where TEnum : Enum
|
|
||||||
=> container.EnumStateKeeper<TEnum>().MoveForward(container.HandlingUpdate);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Moves the enum state backward to the previous value in the enum sequence.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="TEnum">The enum type for state management.</typeparam>
|
|
||||||
/// <param name="container">The handler container.</param>
|
|
||||||
public static void BackwardEnumState<TEnum>(this IHandlerContainer container) where TEnum : Enum
|
|
||||||
=> container.EnumStateKeeper<TEnum>().MoveBackward(container.HandlingUpdate);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,92 +0,0 @@
|
|||||||
using Telegrator.Annotations.StateKeeping;
|
|
||||||
using Telegrator.Core.Handlers;
|
|
||||||
using Telegrator.Core.StateKeeping;
|
|
||||||
|
|
||||||
namespace Telegrator.StateKeeping
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// State keeper that manages numeric (integer) states for chat sessions.
|
|
||||||
/// Inherits from <see cref="StateKeeperBase{TKey, TState}"/> with long keys and int states.
|
|
||||||
/// Provides automatic increment/decrement functionality for state transitions.
|
|
||||||
/// </summary>
|
|
||||||
public class NumericStateKeeper : StateKeeperBase<long, int>
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the default state value, which is 1.
|
|
||||||
/// </summary>
|
|
||||||
public override int DefaultState => 1;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Moves the numeric state backward by decrementing the current state value.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="currentState">The current numeric state value</param>
|
|
||||||
/// <param name="_">The chat ID (unused in this implementation)</param>
|
|
||||||
/// <returns>The decremented state value</returns>
|
|
||||||
protected override int MoveBackward(int currentState, long _)
|
|
||||||
{
|
|
||||||
return currentState - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Moves the numeric state forward by incrementing the current state value.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="currentState">The current numeric state value</param>
|
|
||||||
/// <param name="_">The chat ID (unused in this implementation)</param>
|
|
||||||
/// <returns>The incremented state value</returns>
|
|
||||||
protected override int MoveForward(int currentState, long _)
|
|
||||||
{
|
|
||||||
return currentState + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Provides extension methods for managing numeric states in handler containers.
|
|
||||||
/// </summary>
|
|
||||||
public static partial class StateHandlerContainerExtensions
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the numeric state keeper instance associated with the handler container.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="_">The handler container instance</param>
|
|
||||||
/// <returns>The <see cref="NumericStateKeeper"/> instance</returns>
|
|
||||||
public static NumericStateKeeper NumericStateKeeper(this IHandlerContainer _)
|
|
||||||
=> NumericStateAttribute.Shared;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a new numeric state for the current update being handled.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="container">The handler container instance</param>
|
|
||||||
public static void CreateNumericState(this IHandlerContainer container)
|
|
||||||
=> container.NumericStateKeeper().CreateState(container.HandlingUpdate);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Deletes the numeric state for the current update being handled.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="container">The handler container instance</param>
|
|
||||||
public static void DeleteNumericState(this IHandlerContainer container)
|
|
||||||
=> container.NumericStateKeeper().DeleteState(container.HandlingUpdate);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Sets the numeric state for the current update being handled.
|
|
||||||
/// If the new state is null, uses the default state from the state keeper.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="container">The handler container instance</param>
|
|
||||||
/// <param name="newState">The new numeric state to set, or null to use default</param>
|
|
||||||
public static void SetNumericState(this IHandlerContainer container, int? newState)
|
|
||||||
=> container.NumericStateKeeper().SetState(container.HandlingUpdate, newState ?? NumericStateAttribute.DefaultState);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Moves the numeric state forward by incrementing the current value.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="container">The handler container instance</param>
|
|
||||||
public static void ForwardNumericState(this IHandlerContainer container)
|
|
||||||
=> container.NumericStateKeeper().MoveForward(container.HandlingUpdate);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Moves the numeric state backward by decrementing the current value.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="container">The handler container instance</param>
|
|
||||||
public static void BackwardNumericState(this IHandlerContainer container)
|
|
||||||
=> container.NumericStateKeeper().MoveBackward(container.HandlingUpdate);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
using Telegram.Bot.Types;
|
|
||||||
using Telegrator.Core.StateKeeping;
|
|
||||||
|
|
||||||
namespace Telegrator.StateKeeping
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Resolves sender ID from Telegram updates for state management purposes.
|
|
||||||
/// Extracts the sender identifier from various types of updates to provide a consistent key for state operations.
|
|
||||||
/// </summary>
|
|
||||||
public class SenderIdResolver : IStateKeyResolver<long>
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Resolves the sender ID from a Telegram update.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="keySource">The Telegram update to extract the sender ID from.</param>
|
|
||||||
/// <returns>The sender ID as a long value.</returns>
|
|
||||||
/// <exception cref="ArgumentException">Thrown when the update does not contain a valid sender ID.</exception>
|
|
||||||
public long ResolveKey(Update keySource)
|
|
||||||
=> keySource.GetSenderId() ?? throw new ArgumentException("Cannot resolve SenderID for this Update");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,87 +0,0 @@
|
|||||||
using Telegrator.Annotations.StateKeeping;
|
|
||||||
using Telegrator.Core.Handlers;
|
|
||||||
using Telegrator.Core.StateKeeping;
|
|
||||||
|
|
||||||
namespace Telegrator.StateKeeping
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// State keeper that manages string-based states for chat sessions.
|
|
||||||
/// </summary>
|
|
||||||
public class StringStateKeeper() : StateKeeperBase<long, string>()
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the default state value, which is an empty string.
|
|
||||||
/// </summary>
|
|
||||||
public override string DefaultState => string.Empty;
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
protected override string MoveBackward(string currentState, long currentKey)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
protected override string MoveForward(string currentState, long currentKey)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Provides extension methods for managing string states in handler containers.
|
|
||||||
/// </summary>
|
|
||||||
public static partial class StateHandlerContainerExtensions
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the string state keeper instance associated with the handler container.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="_">The handler container instance</param>
|
|
||||||
/// <returns>The <see cref="StringStateKeeper"/> instance</returns>
|
|
||||||
public static StringStateKeeper StringStateKeeper(this IHandlerContainer _)
|
|
||||||
=> StringStateAttribute.Shared;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a new string state for the current update being handled.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="container">The handler container instance</param>
|
|
||||||
public static void CreateStringState(this IHandlerContainer container)
|
|
||||||
=> container.StringStateKeeper().CreateState(container.HandlingUpdate);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Deletes the string state for the current update being handled.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="container">The handler container instance</param>
|
|
||||||
public static void DeleteStringState(this IHandlerContainer container)
|
|
||||||
=> container.StringStateKeeper().DeleteState(container.HandlingUpdate);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Sets the string state for the current update being handled.
|
|
||||||
/// If the new state is null, uses the default state from the state keeper.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="container">The handler container instance</param>
|
|
||||||
/// <param name="newState">The new string state to set, or null to use default</param>
|
|
||||||
public static void SetStringState(this IHandlerContainer container, string? newState)
|
|
||||||
=> container.StringStateKeeper().SetState(container.HandlingUpdate, newState ?? StringStateAttribute.DefaultState);
|
|
||||||
|
|
||||||
/*
|
|
||||||
public static string GetStringState(this IHandlerContainer container, string key)
|
|
||||||
=> container.StringStateKeeper().GetState()
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
/// <summary>
|
|
||||||
/// Moves the string state forward to the next state in the sequence.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="container">The handler container instance</param>
|
|
||||||
public static void ForwardStringState(this IHandlerContainer container)
|
|
||||||
=> container.StringStateKeeper().MoveForward(container.HandlingUpdate);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Moves the string state backward to the previous state in the sequence.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="container">The handler container instance</param>
|
|
||||||
public static void BackwardStringState(this IHandlerContainer container)
|
|
||||||
=> container.StringStateKeeper().MoveBackward(container.HandlingUpdate);
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+5
-5
@@ -1,13 +1,13 @@
|
|||||||
using Telegram.Bot.Types;
|
using Telegram.Bot.Types;
|
||||||
using Telegrator.Core.StateKeeping;
|
using Telegrator.Core.States;
|
||||||
|
|
||||||
namespace Telegrator.StateKeeping
|
namespace Telegrator.States
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Resolves chat ID from Telegram updates for state management purposes.
|
/// Resolves chat ID from Telegram updates for state management purposes.
|
||||||
/// Extracts the chat identifier from various types of updates to provide a consistent key for state operations.
|
/// Extracts the chat identifier from various types of updates to provide a consistent key for state operations.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class ChatIdResolver : IStateKeyResolver<long>
|
public class ChatIdResolver : IStateKeyResolver
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Resolves the chat ID from a Telegram update.
|
/// Resolves the chat ID from a Telegram update.
|
||||||
@@ -15,7 +15,7 @@ namespace Telegrator.StateKeeping
|
|||||||
/// <param name="keySource">The Telegram update to extract the chat ID from.</param>
|
/// <param name="keySource">The Telegram update to extract the chat ID from.</param>
|
||||||
/// <returns>The chat ID as a long value.</returns>
|
/// <returns>The chat ID as a long value.</returns>
|
||||||
/// <exception cref="ArgumentException">Thrown when the update does not contain a valid chat ID.</exception>
|
/// <exception cref="ArgumentException">Thrown when the update does not contain a valid chat ID.</exception>
|
||||||
public long ResolveKey(Update keySource)
|
public string? ResolveKey(Update keySource)
|
||||||
=> keySource.GetChatId() ?? throw new ArgumentException("Cannot resolve ChatID for this Update");
|
=> keySource.GetChatId()?.ToString() ?? throw new ArgumentException("Cannot resolve ChatID for this Update");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
using System.Collections.Concurrent;
|
||||||
|
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)
|
||||||
|
throw new ArgumentNullException(nameof(key));
|
||||||
|
|
||||||
|
if (!storage.TryRemove(key, out object value))
|
||||||
|
throw new Exception("Failed to remove key '" + key + "' from storage.");
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public Task<T?> GetAsync<T>(string key, CancellationToken ccancellationTokent = default)
|
||||||
|
{
|
||||||
|
if (key is null)
|
||||||
|
throw new ArgumentNullException(nameof(key));
|
||||||
|
|
||||||
|
if (storage.TryGetValue(key, out object value) && value is T)
|
||||||
|
return Task.FromResult((T?)value);
|
||||||
|
|
||||||
|
return Task.FromResult(default(T));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public Task SetAsync<T>(string key, T state, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
if (key is null)
|
||||||
|
throw new ArgumentNullException(nameof(key));
|
||||||
|
|
||||||
|
if (state is null)
|
||||||
|
throw new ArgumentNullException(nameof(state));
|
||||||
|
|
||||||
|
storage.Set(key, state);
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
using Telegrator.Core.States;
|
||||||
|
|
||||||
|
namespace Telegrator.States;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// State machine implementation for enum-based states.
|
||||||
|
/// Automatically creates an array of all enum values for state navigation.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TEnum">The enum type to be used for state management.</typeparam>
|
||||||
|
public class EnumStateMachine<TEnum> : IStateMachine<TEnum> where TEnum : struct, Enum, IEquatable<TEnum>
|
||||||
|
{
|
||||||
|
private readonly TEnum[] _states = Enum.GetValues(typeof(TEnum)).Cast<TEnum>().ToArray();
|
||||||
|
private TEnum _defaultState => _states.FirstOrDefault();
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public async Task<TEnum> Current(IStateStorage storage, string updateKey, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
string key = FormatKey(updateKey);
|
||||||
|
TEnum state = await storage.GetAsync<TEnum>(key, cancellationToken);
|
||||||
|
|
||||||
|
return EqualityComparer<TEnum>.Default.Equals(state, default)
|
||||||
|
? _defaultState : state;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public async Task Advance(IStateStorage storage, string updateKey, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
string key = FormatKey(updateKey);
|
||||||
|
TEnum currentState = await storage.GetAsync<TEnum>(key, cancellationToken);
|
||||||
|
|
||||||
|
int currentIndex = Array.IndexOf(_states, currentState);
|
||||||
|
if (currentIndex < _states.Length - 1)
|
||||||
|
{
|
||||||
|
var nextState = _states[currentIndex + 1];
|
||||||
|
await storage.SetAsync(key, nextState, cancellationToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public async Task Retreat(IStateStorage storage, string updateKey, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
string key = FormatKey(updateKey);
|
||||||
|
TEnum currentState = await storage.GetAsync<TEnum>(key, cancellationToken);
|
||||||
|
|
||||||
|
int currentIndex = Array.IndexOf(_states, currentState);
|
||||||
|
if (currentIndex > 0)
|
||||||
|
{
|
||||||
|
var nextState = _states[currentIndex - 1];
|
||||||
|
await storage.SetAsync(key, nextState, cancellationToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public async Task Reset(IStateStorage storage, string updateKey, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
string key = FormatKey(updateKey);
|
||||||
|
await storage.SetAsync(key, _defaultState, cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string FormatKey(string updateKey)
|
||||||
|
=> typeof(TEnum).Name + ":" + updateKey;
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
using Telegram.Bot.Types;
|
||||||
|
using Telegrator.Core.States;
|
||||||
|
|
||||||
|
namespace Telegrator.States;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Resolves sender ID from Telegram updates for state management purposes.
|
||||||
|
/// Extracts the sender identifier from various types of updates to provide a consistent key for state operations.
|
||||||
|
/// </summary>
|
||||||
|
public class SenderIdResolver : IStateKeyResolver
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Resolves the sender ID from a Telegram update.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="keySource">The Telegram update to extract the sender ID from.</param>
|
||||||
|
/// <returns>The sender ID as a long value.</returns>
|
||||||
|
/// <exception cref="ArgumentException">Thrown when the update does not contain a valid sender ID.</exception>
|
||||||
|
public string ResolveKey(Update keySource)
|
||||||
|
=> keySource.GetSenderId()?.ToString() ?? throw new ArgumentException("Cannot resolve SenderID for this Update");
|
||||||
|
}
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
using Telegram.Bot.Types;
|
||||||
|
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>
|
||||||
|
{
|
||||||
|
private readonly IStateStorage _stateStorage = stateStorage;
|
||||||
|
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)
|
||||||
|
throw new InvalidOperationException("KeyResolver is not set.");
|
||||||
|
|
||||||
|
string? key = KeyResolver.ResolveKey(_handlingUpdate);
|
||||||
|
if (key is null)
|
||||||
|
throw new InvalidOperationException("Failed to resolve Update key");
|
||||||
|
|
||||||
|
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)
|
||||||
|
throw new InvalidOperationException("KeyResolver is not set.");
|
||||||
|
|
||||||
|
string? key = KeyResolver.ResolveKey(_handlingUpdate);
|
||||||
|
if (key is null)
|
||||||
|
throw new InvalidOperationException("Failed to resolve Update key");
|
||||||
|
|
||||||
|
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)
|
||||||
|
throw new InvalidOperationException("KeyResolver is not set.");
|
||||||
|
|
||||||
|
string? key = KeyResolver.ResolveKey(_handlingUpdate);
|
||||||
|
if (key is null)
|
||||||
|
throw new InvalidOperationException("Failed to resolve Update key");
|
||||||
|
|
||||||
|
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)
|
||||||
|
throw new InvalidOperationException("KeyResolver is not set.");
|
||||||
|
|
||||||
|
string? key = KeyResolver.ResolveKey(_handlingUpdate);
|
||||||
|
if (key is null)
|
||||||
|
throw new InvalidOperationException("Failed to resolve Update key");
|
||||||
|
|
||||||
|
await _stateMachine.Retreat(_stateStorage, key, cancellationToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netstandard2.0</TargetFramework>
|
<TargetFramework>netstandard2.1</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<LangVersion>latest</LangVersion>
|
<LangVersion>latest</LangVersion>
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
<EnforceCodeStyleInBuild>True</EnforceCodeStyleInBuild>
|
<EnforceCodeStyleInBuild>True</EnforceCodeStyleInBuild>
|
||||||
|
|
||||||
<Title>Telegrator : Telegram.Bot mediator framework</Title>
|
<Title>Telegrator : Telegram.Bot mediator framework</Title>
|
||||||
<Version>1.16.3</Version>
|
<Version>1.16.4</Version>
|
||||||
<Authors>Rikitav Tim4ik</Authors>
|
<Authors>Rikitav Tim4ik</Authors>
|
||||||
<Company>Rikitav Tim4ik</Company>
|
<Company>Rikitav Tim4ik</Company>
|
||||||
<RepositoryUrl>https://github.com/Rikitav/Telegrator</RepositoryUrl>
|
<RepositoryUrl>https://github.com/Rikitav/Telegrator</RepositoryUrl>
|
||||||
@@ -44,4 +44,8 @@
|
|||||||
<ProjectReference Include="..\..\dev\Telegrator.RoslynGenerators\Telegrator.RoslynGenerators.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
|
<ProjectReference Include="..\..\dev\Telegrator.RoslynGenerators\Telegrator.RoslynGenerators.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Folder Include="Annotations\Targetted\" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ using Telegrator.Core;
|
|||||||
using Telegrator.Logging;
|
using Telegrator.Logging;
|
||||||
using Telegrator.Mediation;
|
using Telegrator.Mediation;
|
||||||
using Telegrator.Providers;
|
using Telegrator.Providers;
|
||||||
|
using Telegrator.States;
|
||||||
|
|
||||||
namespace Telegrator
|
namespace Telegrator
|
||||||
{
|
{
|
||||||
@@ -75,8 +76,9 @@ namespace Telegrator
|
|||||||
|
|
||||||
HandlersProvider handlerProvider = new HandlersProvider(Handlers, Options);
|
HandlersProvider handlerProvider = new HandlersProvider(Handlers, Options);
|
||||||
AwaitingProvider awaitingProvider = new AwaitingProvider(Options);
|
AwaitingProvider awaitingProvider = new AwaitingProvider(Options);
|
||||||
|
DefaultStateStorage stateStorage = new DefaultStateStorage();
|
||||||
|
|
||||||
updateRouter = new UpdateRouter(handlerProvider, awaitingProvider, Options, BotInfo);
|
updateRouter = new UpdateRouter(handlerProvider, awaitingProvider, stateStorage, Options, BotInfo);
|
||||||
|
|
||||||
// Log startup
|
// Log startup
|
||||||
TelegratorLogging.LogInformation($"Telegrator bot starting up - BotId: {BotInfo.User.Id}, Username: {BotInfo.User.Username}, MaxParallelHandlers: {Options.MaximumParallelWorkingHandlers ?? -1}");
|
TelegratorLogging.LogInformation($"Telegrator bot starting up - BotId: {BotInfo.User.Id}, Username: {BotInfo.User.Username}, MaxParallelHandlers: {Options.MaximumParallelWorkingHandlers ?? -1}");
|
||||||
|
|||||||
@@ -3,14 +3,13 @@ using Telegram.Bot.Types;
|
|||||||
using Telegram.Bot.Types.Enums;
|
using Telegram.Bot.Types.Enums;
|
||||||
using Telegram.Bot.Types.Payments;
|
using Telegram.Bot.Types.Payments;
|
||||||
using Telegrator.Annotations;
|
using Telegrator.Annotations;
|
||||||
using Telegrator.Attributes;
|
|
||||||
using Telegrator.Core;
|
using Telegrator.Core;
|
||||||
using Telegrator.Core.Descriptors;
|
using Telegrator.Core.Descriptors;
|
||||||
using Telegrator.Core.Handlers;
|
using Telegrator.Core.Handlers;
|
||||||
using Telegrator.Core.Handlers.Building;
|
using Telegrator.Core.Handlers.Building;
|
||||||
using Telegrator.Core.StateKeeping;
|
using Telegrator.Core.States;
|
||||||
using Telegrator.Handlers.Building;
|
using Telegrator.Handlers.Building;
|
||||||
using Telegrator.StateKeeping;
|
using Telegrator.States;
|
||||||
|
|
||||||
namespace Telegrator
|
namespace Telegrator
|
||||||
{
|
{
|
||||||
@@ -187,17 +186,6 @@ namespace Telegrator
|
|||||||
/// <returns>An awaiter builder for callback query updates.</returns>
|
/// <returns>An awaiter builder for callback query updates.</returns>
|
||||||
public static IAwaiterHandlerBuilder<CallbackQuery> AwaitCallbackQuery(this IHandlerContainer container)
|
public static IAwaiterHandlerBuilder<CallbackQuery> AwaitCallbackQuery(this IHandlerContainer container)
|
||||||
=> container.AwaitUpdate<CallbackQuery>(UpdateType.CallbackQuery);
|
=> container.AwaitUpdate<CallbackQuery>(UpdateType.CallbackQuery);
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a state keeper instance for the specified types.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="TKey">The type of the state key.</typeparam>
|
|
||||||
/// <typeparam name="TState">The type of the state value.</typeparam>
|
|
||||||
/// <typeparam name="TKeeper">The type of the state keeper.</typeparam>
|
|
||||||
/// <param name="_">The handler container (unused).</param>
|
|
||||||
/// <returns>The state keeper instance.</returns>
|
|
||||||
public static TKeeper GetStateKeeper<TKey, TState, TKeeper>(this IHandlerContainer _) where TKey : notnull where TState : IEquatable<TState> where TKeeper : StateKeeperBase<TKey, TState>, new()
|
|
||||||
=> StateKeeperAttribute<TKey, TState, TKeeper>.Shared;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -296,6 +284,98 @@ 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>
|
||||||
|
{
|
||||||
|
stateMachine.KeyResolver = new ChatIdResolver();
|
||||||
|
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>
|
||||||
|
{
|
||||||
|
stateMachine.KeyResolver = new SenderIdResolver();
|
||||||
|
return stateMachine;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Extension methods for handlers collections.
|
/// Extension methods for handlers collections.
|
||||||
/// Provides convenient methods for creating implicit handlers.
|
/// Provides convenient methods for creating implicit handlers.
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ public class FilterTests
|
|||||||
{
|
{
|
||||||
// Arrange (Given) - подготовка тестовых данных
|
// Arrange (Given) - подготовка тестовых данных
|
||||||
var anyFilter = Filter<Update>.Any();
|
var anyFilter = Filter<Update>.Any();
|
||||||
var context = new FilterExecutionContext<Update>(new TelegramBotInfo(null), new Update(), new Update(), new Dictionary<string, object>(), new CompletedFiltersList());
|
var context = new FilterExecutionContext<Update>(null, new TelegramBotInfo(null), new Update(), new Update(), new Dictionary<string, object>(), new CompletedFiltersList());
|
||||||
|
|
||||||
// Act (When) - выполнение тестируемого действия
|
// Act (When) - выполнение тестируемого действия
|
||||||
var result = anyFilter.CanPass(context);
|
var result = anyFilter.CanPass(context);
|
||||||
@@ -49,7 +49,7 @@ public class FilterTests
|
|||||||
// Arrange
|
// Arrange
|
||||||
var alwaysTrueFilter = Filter<Update>.Any();
|
var alwaysTrueFilter = Filter<Update>.Any();
|
||||||
var reverseFilter = alwaysTrueFilter.Not();
|
var reverseFilter = alwaysTrueFilter.Not();
|
||||||
var context = new FilterExecutionContext<Update>(new TelegramBotInfo(null), new Update(), new Update(), new Dictionary<string, object>(), new CompletedFiltersList());
|
var context = new FilterExecutionContext<Update>(null, new TelegramBotInfo(null), new Update(), new Update(), new Dictionary<string, object>(), new CompletedFiltersList());
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = reverseFilter.CanPass(context);
|
var result = reverseFilter.CanPass(context);
|
||||||
@@ -74,7 +74,7 @@ public class FilterTests
|
|||||||
var firstFilter = Filter<Update>.If(_ => firstResult);
|
var firstFilter = Filter<Update>.If(_ => firstResult);
|
||||||
var secondFilter = Filter<Update>.If(_ => secondResult);
|
var secondFilter = Filter<Update>.If(_ => secondResult);
|
||||||
var andFilter = firstFilter.And(secondFilter);
|
var andFilter = firstFilter.And(secondFilter);
|
||||||
var context = new FilterExecutionContext<Update>(new TelegramBotInfo(null), new Update(), new Update(), new Dictionary<string, object>(), new CompletedFiltersList());
|
var context = new FilterExecutionContext<Update>(null, new TelegramBotInfo(null), new Update(), new Update(), new Dictionary<string, object>(), new CompletedFiltersList());
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = andFilter.CanPass(context);
|
var result = andFilter.CanPass(context);
|
||||||
@@ -99,7 +99,7 @@ public class FilterTests
|
|||||||
var firstFilter = Filter<Update>.If(_ => firstResult);
|
var firstFilter = Filter<Update>.If(_ => firstResult);
|
||||||
var secondFilter = Filter<Update>.If(_ => secondResult);
|
var secondFilter = Filter<Update>.If(_ => secondResult);
|
||||||
var orFilter = firstFilter.Or(secondFilter);
|
var orFilter = firstFilter.Or(secondFilter);
|
||||||
var context = new FilterExecutionContext<Update>(new TelegramBotInfo(null), new Update(), new Update(), new Dictionary<string, object>(), new CompletedFiltersList());
|
var context = new FilterExecutionContext<Update>(null, new TelegramBotInfo(null), new Update(), new Update(), new Dictionary<string, object>(), new CompletedFiltersList());
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = orFilter.CanPass(context);
|
var result = orFilter.CanPass(context);
|
||||||
@@ -122,7 +122,7 @@ public class FilterTests
|
|||||||
var filter3 = Filter<Update>.If(_ => false);
|
var filter3 = Filter<Update>.If(_ => false);
|
||||||
|
|
||||||
var compiledFilter = new CompiledFilter<Update>(filter1, filter2, filter3);
|
var compiledFilter = new CompiledFilter<Update>(filter1, filter2, filter3);
|
||||||
var context = new FilterExecutionContext<Update>(new TelegramBotInfo(null), new Update(), new Update(), new Dictionary<string, object>(), new CompletedFiltersList());
|
var context = new FilterExecutionContext<Update>(null, new TelegramBotInfo(null), new Update(), new Update(), new Dictionary<string, object>(), new CompletedFiltersList());
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = compiledFilter.CanPass(context);
|
var result = compiledFilter.CanPass(context);
|
||||||
@@ -164,7 +164,7 @@ public class FilterTests
|
|||||||
wasCalled = true;
|
wasCalled = true;
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
var context = new FilterExecutionContext<Update>(new TelegramBotInfo(null), new Update(), new Update(), new Dictionary<string, object>(), new CompletedFiltersList());
|
var context = new FilterExecutionContext<Update>(null, new TelegramBotInfo(null), new Update(), new Update(), new Dictionary<string, object>(), new CompletedFiltersList());
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = functionFilter.CanPass(context);
|
var result = functionFilter.CanPass(context);
|
||||||
|
|||||||
Reference in New Issue
Block a user