diff --git a/Examples/MiddlewareBaseBot/Forms/StartForm.cs b/Examples/MiddlewareBaseBot/Forms/StartForm.cs
new file mode 100644
index 0000000..1395a3a
--- /dev/null
+++ b/Examples/MiddlewareBaseBot/Forms/StartForm.cs
@@ -0,0 +1,47 @@
+using TelegramBotBase.Base;
+using TelegramBotBase.Form;
+
+internal sealed class StartForm : FormBase
+{
+ public override async Task PreLoad(MessageResult message)
+ {
+ await this.Device.Send("PreLoad");
+
+ await Task.Delay(200);
+ }
+
+ public override async Task Load(MessageResult message)
+ {
+ await this.Device.Send("Load");
+
+ await Task.Delay(200);
+ }
+
+ public override async Task Edited(MessageResult message)
+ {
+ await this.Device.Send("Edited");
+
+ await Task.Delay(200);
+ }
+
+ public override async Task Action(MessageResult message)
+ {
+ await this.Device.Send("Action");
+
+ await Task.Delay(200);
+ }
+
+ public override async Task SentData(DataResult data)
+ {
+ await this.Device.Send("SentData");
+
+ await Task.Delay(200);
+ }
+
+ public override async Task Render(MessageResult message)
+ {
+ await this.Device.Send("Render");
+
+ await Task.Delay(200);
+ }
+}
\ No newline at end of file
diff --git a/Examples/MiddlewareBaseBot/MiddlewareBaseBot.csproj b/Examples/MiddlewareBaseBot/MiddlewareBaseBot.csproj
new file mode 100644
index 0000000..7134144
--- /dev/null
+++ b/Examples/MiddlewareBaseBot/MiddlewareBaseBot.csproj
@@ -0,0 +1,14 @@
+
+
+
+ Exe
+ net7.0
+ enable
+ enable
+
+
+
+
+
+
+
diff --git a/Examples/MiddlewareBaseBot/Program.cs b/Examples/MiddlewareBaseBot/Program.cs
new file mode 100644
index 0000000..559e529
--- /dev/null
+++ b/Examples/MiddlewareBaseBot/Program.cs
@@ -0,0 +1,195 @@
+using Telegram.Bot.Types.Enums;
+using TelegramBotBase;
+using TelegramBotBase.Builder;
+using TelegramBotBase.MessageLoops.Extensions;
+
+public class Program
+{
+ private static async Task Main(string[] args)
+ {
+ var bot = GetPhotoBot();
+
+ await bot.Start();
+
+ Console.WriteLine("Bot started :)");
+
+ Console.ReadLine();
+ }
+
+ ///
+ /// Creates a bot with middleware message loop and authentication for admin user
+ ///
+ private static BotBase GetAdminBot()
+ {
+ var bot = BotBaseBuilder
+ .Create()
+ .WithAPIKey(Environment.GetEnvironmentVariable("API_KEY") ?? throw new Exception("API_KEY is not set"))
+ .MiddlewareMessageLoop(
+ messageLoop =>
+ messageLoop
+ .Use(async (container, next) =>
+ {
+ var updateResult = container.UpdateResult;
+ if (updateResult.Message is not null)
+ {
+ if (updateResult.Message.From is not null)
+ {
+ var fromId = updateResult.Message.From.Id;
+
+ if (fromId == 1)
+ {
+ await next();
+ }
+ }
+ }
+
+ return;
+ })
+ .UseValidUpdateTypes(
+ UpdateType.Message,
+ UpdateType.EditedMessage,
+ UpdateType.CallbackQuery)
+ .UseBotCommands()
+ .UseForms())
+ .WithStartForm()
+ .NoProxy()
+ .DefaultCommands()
+ .NoSerialization()
+ .UsePersian()
+ .Build();
+
+ return bot;
+ }
+
+ ///
+ /// Creates a bot with middleware message loop for handle inline queries
+ ///
+ private static BotBase GetInlineQueryBot()
+ {
+ var bot = BotBaseBuilder
+ .Create()
+ .WithAPIKey(Environment.GetEnvironmentVariable("API_KEY") ?? throw new Exception("API_KEY is not set"))
+ .MiddlewareMessageLoop(
+ messageLoop =>
+ messageLoop
+ .UseValidUpdateTypes(UpdateType.InlineQuery)
+ .Use(async (container, next) =>
+ {
+ var query = container.UpdateResult.RawData.InlineQuery.Query;
+
+ if (!string.IsNullOrWhiteSpace(query))
+ {
+ // logic
+
+ await next();
+ }
+
+ return;
+ }))
+ .WithStartForm()
+ .NoProxy()
+ .DefaultCommands()
+ .NoSerialization()
+ .UsePersian()
+ .Build();
+
+ return bot;
+ }
+
+ ///
+ /// Creates a bot with middleware message loop like form base message loop
+ ///
+ private static BotBase GetFormBaseBot()
+ {
+ var bot = BotBaseBuilder
+ .Create()
+ .WithAPIKey(Environment.GetEnvironmentVariable("API_KEY") ?? throw new Exception("API_KEY is not set"))
+ .MiddlewareMessageLoop(
+ messageLoop =>
+ messageLoop
+ .UseValidUpdateTypes(
+ UpdateType.Message,
+ UpdateType.EditedMessage,
+ UpdateType.CallbackQuery)
+ .UseBotCommands()
+ .UseForms())
+ // OR instead of UseForms
+ // .UsePreLoad()
+ // .UseLoad()
+ // .UseAllAttachments()
+ // .UseActions()
+ // .UseRender()
+ .WithStartForm()
+ .NoProxy()
+ .DefaultCommands()
+ .NoSerialization()
+ .UsePersian()
+ .Build();
+
+ return bot;
+ }
+
+ ///
+ /// Creates a bot with middleware message loop like form base message loop
+ ///
+ private static BotBase GetFullBot()
+ {
+ var bot = BotBaseBuilder
+ .Create()
+ .WithAPIKey(Environment.GetEnvironmentVariable("API_KEY") ?? throw new Exception("API_KEY is not set"))
+ .MiddlewareMessageLoop(
+ messageLoop =>
+ messageLoop
+ .UseBotCommands()
+ .UseForms())
+ .WithStartForm()
+ .NoProxy()
+ .DefaultCommands()
+ .NoSerialization()
+ .UsePersian()
+ .Build();
+
+ return bot;
+ }
+
+ ///
+ /// Creates a bot with middleware message loop like minimal message loop
+ ///
+ private static BotBase GetMinimalBot()
+ {
+ var bot = BotBaseBuilder
+ .Create()
+ .WithAPIKey(Environment.GetEnvironmentVariable("API_KEY") ?? throw new Exception("API_KEY is not set"))
+ .MiddlewareMessageLoop(
+ messageLoop =>
+ messageLoop
+ .UseLoad())
+ .WithStartForm()
+ .NoProxy()
+ .DefaultCommands()
+ .NoSerialization()
+ .UsePersian()
+ .Build();
+
+ return bot;
+ }
+
+ private static BotBase GetPhotoBot()
+ {
+ var bot = BotBaseBuilder
+ .Create()
+ .WithAPIKey(Environment.GetEnvironmentVariable("API_KEY") ?? throw new Exception("API_KEY is not set"))
+ .MiddlewareMessageLoop(
+ messageLoop =>
+ messageLoop
+ .UseAttachments(MessageType.Photo))
+ .WithStartForm()
+ .NoProxy()
+ .DefaultCommands()
+ .NoSerialization()
+ .UsePersian()
+ .Build();
+
+ return bot;
+ }
+}
\ No newline at end of file
diff --git a/TelegramBotBase/Builder/BotBaseBuilder.cs b/TelegramBotBase/Builder/BotBaseBuilder.cs
index 85b5621..61750b6 100644
--- a/TelegramBotBase/Builder/BotBaseBuilder.cs
+++ b/TelegramBotBase/Builder/BotBaseBuilder.cs
@@ -146,6 +146,12 @@ public class BotBaseBuilder : IAPIKeySelectionStage, IMessageLoopSelectionStage,
return this;
}
+ public IStartFormSelectionStage MiddlewareMessageLoop(Func messageLoopConfiguration)
+ {
+ _messageLoopFactory = messageLoopConfiguration(new MiddlewareBaseMessageLoop());
+
+ return this;
+ }
public IStartFormSelectionStage MinimalMessageLoop()
{
diff --git a/TelegramBotBase/Builder/Interfaces/IMessageLoopSelectionStage.cs b/TelegramBotBase/Builder/Interfaces/IMessageLoopSelectionStage.cs
index 21223e7..422aadc 100644
--- a/TelegramBotBase/Builder/Interfaces/IMessageLoopSelectionStage.cs
+++ b/TelegramBotBase/Builder/Interfaces/IMessageLoopSelectionStage.cs
@@ -1,4 +1,6 @@
-using TelegramBotBase.Interfaces;
+using System;
+using TelegramBotBase.Interfaces;
+using TelegramBotBase.MessageLoops;
namespace TelegramBotBase.Builder.Interfaces;
@@ -12,6 +14,12 @@ public interface IMessageLoopSelectionStage
IStartFormSelectionStage DefaultMessageLoop();
+ ///
+ /// Choses a fully customizable middleware-based message loop.
+ ///
+ IStartFormSelectionStage MiddlewareMessageLoop(Func messageLoopConfiguration);
+
+
///
/// Chooses a minimalistic message loop, which catches all update types and only calls the Load function.
///
diff --git a/TelegramBotBase/Localizations/Persian.cs b/TelegramBotBase/Localizations/Persian.cs
index 919e355..6157f77 100644
--- a/TelegramBotBase/Localizations/Persian.cs
+++ b/TelegramBotBase/Localizations/Persian.cs
@@ -1,37 +1,36 @@
-namespace TelegramBotBase.Localizations
+namespace TelegramBotBase.Localizations;
+
+public sealed class Persian : Localization
{
- public sealed class Persian : Localization
+ public Persian()
{
- public Persian()
- {
- Values["Language"] = "فارسی";
- Values["ButtonGrid_Title"] = "منو";
- Values["ButtonGrid_NoItems"] = "هیچ آیتمی وجود ندارد.";
- Values["ButtonGrid_PreviousPage"] = "◀️";
- Values["ButtonGrid_NextPage"] = "▶️";
- Values["ButtonGrid_CurrentPage"] = "صفحه ی {0} از {1}";
- Values["ButtonGrid_SearchFeature"] = "💡 برای فیلتر کردن لیست پیام ارسال کنید. برای بازنشانی فیلتر روی 🔍 کلیک کنید.";
- Values["ButtonGrid_Back"] = "بازگشت";
- Values["ButtonGrid_CheckAll"] = "بررسی کردن همه";
- Values["ButtonGrid_UncheckAll"] = "بررسی نکردن همه";
- Values["CalendarPicker_Title"] = "تاریخ را انتخاب کنید";
- Values["CalendarPicker_PreviousPage"] = "◀️";
- Values["CalendarPicker_NextPage"] = "▶️";
- Values["TreeView_Title"] = "گره را انتخاب کنید";
- Values["TreeView_LevelUp"] = "🔼 سطح بالا";
- Values["ToggleButton_On"] = "روشن";
- Values["ToggleButton_Off"] = "خاموش";
- Values["ToggleButton_OnIcon"] = "⚫";
- Values["ToggleButton_OffIcon"] = "⚪";
- Values["ToggleButton_Title"] = "تغییر وضعیت";
- Values["ToggleButton_Changed"] = "انتخاب شده";
- Values["MultiToggleButton_SelectedIcon"] = "✅";
- Values["MultiToggleButton_Title"] = "چند تعویض";
- Values["MultiToggleButton_Changed"] = "انتخاب شده";
- Values["PromptDialog_Back"] = "بازگشت";
- Values["ToggleButton_Changed"] = "تنظیمات تغییر کرد";
- Values["ButtonGrid_SearchIcon"] = "🔍";
- Values["ButtonGrid_TagIcon"] = "📁";
- }
+ Values["Language"] = "فارسی";
+ Values["ButtonGrid_Title"] = "منو";
+ Values["ButtonGrid_NoItems"] = "هیچ آیتمی وجود ندارد.";
+ Values["ButtonGrid_PreviousPage"] = "◀️";
+ Values["ButtonGrid_NextPage"] = "▶️";
+ Values["ButtonGrid_CurrentPage"] = "صفحه ی {0} از {1}";
+ Values["ButtonGrid_SearchFeature"] = "💡 برای فیلتر کردن لیست پیام ارسال کنید. برای بازنشانی فیلتر روی 🔍 کلیک کنید.";
+ Values["ButtonGrid_Back"] = "بازگشت";
+ Values["ButtonGrid_CheckAll"] = "بررسی کردن همه";
+ Values["ButtonGrid_UncheckAll"] = "بررسی نکردن همه";
+ Values["CalendarPicker_Title"] = "تاریخ را انتخاب کنید";
+ Values["CalendarPicker_PreviousPage"] = "◀️";
+ Values["CalendarPicker_NextPage"] = "▶️";
+ Values["TreeView_Title"] = "گره را انتخاب کنید";
+ Values["TreeView_LevelUp"] = "🔼 سطح بالا";
+ Values["ToggleButton_On"] = "روشن";
+ Values["ToggleButton_Off"] = "خاموش";
+ Values["ToggleButton_OnIcon"] = "⚫";
+ Values["ToggleButton_OffIcon"] = "⚪";
+ Values["ToggleButton_Title"] = "تغییر وضعیت";
+ Values["ToggleButton_Changed"] = "انتخاب شده";
+ Values["MultiToggleButton_SelectedIcon"] = "✅";
+ Values["MultiToggleButton_Title"] = "چند تعویض";
+ Values["MultiToggleButton_Changed"] = "انتخاب شده";
+ Values["PromptDialog_Back"] = "بازگشت";
+ Values["ToggleButton_Changed"] = "تنظیمات تغییر کرد";
+ Values["ButtonGrid_SearchIcon"] = "🔍";
+ Values["ButtonGrid_TagIcon"] = "📁";
}
}
diff --git a/TelegramBotBase/MessageLoops/Extensions/MiddlewareBaseMessageLoopExtensions.cs b/TelegramBotBase/MessageLoops/Extensions/MiddlewareBaseMessageLoopExtensions.cs
new file mode 100644
index 0000000..e6cf105
--- /dev/null
+++ b/TelegramBotBase/MessageLoops/Extensions/MiddlewareBaseMessageLoopExtensions.cs
@@ -0,0 +1,310 @@
+using System;
+using System.Linq;
+using System.Threading.Tasks;
+using Telegram.Bot.Types;
+using Telegram.Bot.Types.Enums;
+using TelegramBotBase.Args;
+using TelegramBotBase.Base;
+using static System.Collections.Specialized.BitVector32;
+
+namespace TelegramBotBase.MessageLoops.Extensions;
+
+public static class MiddlewareBaseMessageLoopExtensions
+{
+ ///
+ /// Adds middleware to the message loop then returns the message loop
+ ///
+ public static MiddlewareBaseMessageLoop Use(this MiddlewareBaseMessageLoop messageLoop, Func, Task> middleware)
+ {
+ messageLoop.AddMiddleware(middleware);
+
+ return messageLoop;
+ }
+
+ ///
+ /// Adds update type validator middleware to the message loop then returns message loop
+ ///
+ public static MiddlewareBaseMessageLoop UseValidUpdateTypes(this MiddlewareBaseMessageLoop messageLoop, params UpdateType[] updateTypes)
+ {
+ messageLoop.Use(async (container, next) =>
+ {
+ var updateType = container.UpdateResult.RawData.Type;
+
+ if (updateTypes.Contains(updateType))
+ {
+ await next();
+ }
+ });
+
+ return messageLoop;
+ }
+
+ ///
+ /// Adds bot commands handler middleware to the message loop then returns message loop
+ ///
+ public static MiddlewareBaseMessageLoop UseBotCommands(this MiddlewareBaseMessageLoop messageLoop)
+ {
+ messageLoop.Use(async (container, next) =>
+ {
+ var botBase = container.BotBase;
+ var messageResult = container.MessageResult;
+
+ if (messageResult.IsFirstHandler &&
+ messageResult.IsBotCommand &&
+ botBase.IsKnownBotCommand(messageResult.BotCommand))
+ {
+ var deviceSession = container.DeviceSession;
+
+ var sce =
+ new BotCommandEventArgs(messageResult.BotCommand,
+ messageResult.BotCommandParameters,
+ messageResult.Message,
+ deviceSession.DeviceId,
+ deviceSession);
+
+ await botBase.OnBotCommand(sce);
+
+ if (sce.Handled)
+ {
+ return;
+ }
+ }
+
+ await next();
+ });
+
+ return messageLoop;
+ }
+
+ ///
+ /// Adds forms handler middleware to the message loop then returns message loop
+ ///
+ public static MiddlewareBaseMessageLoop UseForms(this MiddlewareBaseMessageLoop messageLoop)
+ {
+ messageLoop.Use(async (container, next) =>
+ {
+ var activeForm = container.DeviceSession.ActiveForm;
+ var messageResult = container.MessageResult;
+
+ //Pre Loading Event
+ await activeForm.PreLoad(messageResult);
+
+ //Send Load event to controls
+ await activeForm.LoadControls(messageResult);
+
+ //Loading Event
+ await activeForm.Load(messageResult);
+
+ var updateType = container.UpdateResult.RawData.Type;
+
+ //Is Attachment ? (Photo, Audio, Video, Contact, Location, Document) (Ignore Callback Queries)
+ if (updateType == UpdateType.Message)
+ {
+ if ((messageResult.MessageType == MessageType.Contact)
+ | (messageResult.MessageType == MessageType.Document)
+ | (messageResult.MessageType == MessageType.Location)
+ | (messageResult.MessageType == MessageType.Photo)
+ | (messageResult.MessageType == MessageType.Video)
+ | (messageResult.MessageType == MessageType.Audio))
+ {
+ var updateResult = container.UpdateResult;
+
+ await activeForm.SentData(new DataResult(updateResult));
+ }
+ }
+
+ var deviceSession = container.DeviceSession;
+
+ //Action Event
+ if (!deviceSession.FormSwitched && messageResult.IsAction)
+ {
+ //Send Action event to controls
+ await activeForm.ActionControls(messageResult);
+
+ //Send Action event to form itself
+ await activeForm.Action(messageResult);
+
+ if (!messageResult.Handled)
+ {
+ messageResult.Handled = true;
+ return;
+ }
+ }
+
+ if (!deviceSession.FormSwitched)
+ {
+ //Render Event
+ await activeForm.RenderControls(messageResult);
+
+ await activeForm.Render(messageResult);
+ }
+
+ await next();
+ });
+
+ return messageLoop;
+ }
+
+ ///
+ /// Adds forms pre loads handler middleware to the message loop then returns message loop
+ ///
+ public static MiddlewareBaseMessageLoop UsePreLoad(this MiddlewareBaseMessageLoop messageLoop)
+ {
+ messageLoop.Use(async (container, next) =>
+ {
+ var activeForm = container.DeviceSession.ActiveForm;
+ var messageResult = container.MessageResult;
+
+ await activeForm.PreLoad(messageResult);
+
+ await next();
+ });
+
+ return messageLoop;
+ }
+
+ ///
+ /// Adds forms loads handler middleware to the message loop then returns message loop
+ ///
+ public static MiddlewareBaseMessageLoop UseLoad(this MiddlewareBaseMessageLoop messageLoop)
+ {
+ messageLoop.Use(async (container, next) =>
+ {
+ var activeForm = container.DeviceSession.ActiveForm;
+ var messageResult = container.MessageResult;
+
+ await activeForm.LoadControls(messageResult);
+ await activeForm.Load(messageResult);
+
+ await next();
+ });
+
+ return messageLoop;
+ }
+
+ ///
+ /// Adds forms custom attachments handler middleware to the message loop then returns message loop
+ ///
+ public static MiddlewareBaseMessageLoop UseAttachments(this MiddlewareBaseMessageLoop messageLoop, params MessageType[] validAttachments)
+ {
+ messageLoop.Use(async (container, next) =>
+ {
+ var activeForm = container.DeviceSession.ActiveForm;
+ var messageResult = container.MessageResult;
+ var updateType = container.UpdateResult.RawData.Type;
+
+ //Is Attachment ? (Photo, Audio, Video, Contact, Location, Document) (Ignore Callback Queries)
+ if (updateType == UpdateType.Message)
+ {
+ if ((messageResult.MessageType == MessageType.Contact)
+ | (messageResult.MessageType == MessageType.Document)
+ | (messageResult.MessageType == MessageType.Location)
+ | (messageResult.MessageType == MessageType.Photo)
+ | (messageResult.MessageType == MessageType.Video)
+ | (messageResult.MessageType == MessageType.Audio))
+ {
+ if (validAttachments.Contains(messageResult.MessageType))
+ {
+ var updateResult = container.UpdateResult;
+
+ await activeForm.SentData(new DataResult(updateResult));
+ }
+ }
+ }
+
+ await next();
+ });
+
+ return messageLoop;
+ }
+
+ ///
+ /// Adds forms all attachments handler middleware to the message loop then returns message loop
+ ///
+ public static MiddlewareBaseMessageLoop UseAllAttachments(this MiddlewareBaseMessageLoop messageLoop)
+ {
+ messageLoop.Use(async (container, next) =>
+ {
+ var activeForm = container.DeviceSession.ActiveForm;
+ var messageResult = container.MessageResult;
+ var updateType = container.UpdateResult.RawData.Type;
+
+ //Is Attachment ? (Photo, Audio, Video, Contact, Location, Document) (Ignore Callback Queries)
+ if (updateType == UpdateType.Message)
+ {
+ if ((messageResult.MessageType == MessageType.Contact)
+ | (messageResult.MessageType == MessageType.Document)
+ | (messageResult.MessageType == MessageType.Location)
+ | (messageResult.MessageType == MessageType.Photo)
+ | (messageResult.MessageType == MessageType.Video)
+ | (messageResult.MessageType == MessageType.Audio))
+ {
+ var updateResult = container.UpdateResult;
+
+ await activeForm.SentData(new DataResult(updateResult));
+ }
+ }
+
+ await next();
+ });
+
+ return messageLoop;
+ }
+
+ ///
+ /// Adds forms actions handler middleware to the message loop then returns message loop
+ ///
+ public static MiddlewareBaseMessageLoop UseActions(this MiddlewareBaseMessageLoop messageLoop)
+ {
+ messageLoop.Use(async (container, next) =>
+ {
+ var messageResult = container.MessageResult;
+ var deviceSession = container.DeviceSession;
+ var activeForm = deviceSession.ActiveForm;
+
+ //Action Event
+ if (!deviceSession.FormSwitched && messageResult.IsAction)
+ {
+ //Send Action event to controls
+ await activeForm.ActionControls(messageResult);
+
+ //Send Action event to form itself
+ await activeForm.Action(messageResult);
+
+ if (!messageResult.Handled)
+ {
+ messageResult.Handled = true;
+ return;
+ }
+ }
+
+ await next();
+ });
+
+ return messageLoop;
+ }
+
+ ///
+ /// Adds forms renders handler middleware to the message loop then returns message loop
+ ///
+ public static MiddlewareBaseMessageLoop UseRender(this MiddlewareBaseMessageLoop messageLoop)
+ {
+ messageLoop.Use(async (container, next) =>
+ {
+ var messageResult = container.MessageResult;
+ var deviceSession = container.DeviceSession;
+ var activeForm = deviceSession.ActiveForm;
+
+ if (!deviceSession.FormSwitched)
+ {
+ await activeForm.RenderControls(messageResult);
+
+ await activeForm.Render(messageResult);
+ }
+
+ await next();
+ });
+
+ return messageLoop;
+ }
+}
diff --git a/TelegramBotBase/MessageLoops/FormBaseMessageLoop.cs b/TelegramBotBase/MessageLoops/FormBaseMessageLoop.cs
index 4bec2a0..e5ee6af 100644
--- a/TelegramBotBase/MessageLoops/FormBaseMessageLoop.cs
+++ b/TelegramBotBase/MessageLoops/FormBaseMessageLoop.cs
@@ -12,7 +12,7 @@ namespace TelegramBotBase.MessageLoops;
///
/// Thats the default message loop which reacts to Message, EditMessage and CallbackQuery.
///
-public class FormBaseMessageLoop : IMessageLoopFactory
+public sealed class FormBaseMessageLoop : IMessageLoopFactory
{
private static readonly object EvUnhandledCall = new();
@@ -22,7 +22,6 @@ public class FormBaseMessageLoop : IMessageLoopFactory
{
var update = ur.RawData;
-
if (update.Type != UpdateType.Message
&& update.Type != UpdateType.EditedMessage
&& update.Type != UpdateType.CallbackQuery)
diff --git a/TelegramBotBase/MessageLoops/FullMessageLoop.cs b/TelegramBotBase/MessageLoops/FullMessageLoop.cs
index 1affd19..7e76bc7 100644
--- a/TelegramBotBase/MessageLoops/FullMessageLoop.cs
+++ b/TelegramBotBase/MessageLoops/FullMessageLoop.cs
@@ -12,7 +12,7 @@ namespace TelegramBotBase.MessageLoops;
///
/// This message loop reacts to all update types.
///
-public class FullMessageLoop : IMessageLoopFactory
+public sealed class FullMessageLoop : IMessageLoopFactory
{
private static readonly object EvUnhandledCall = new();
@@ -20,9 +20,6 @@ public class FullMessageLoop : IMessageLoopFactory
public async Task MessageLoop(BotBase bot, DeviceSession session, UpdateResult ur, MessageResult mr)
{
- var update = ur.RawData;
-
-
//Is this a bot command ?
if (mr.IsFirstHandler && mr.IsBotCommand && bot.IsKnownBotCommand(mr.BotCommand))
{
@@ -50,6 +47,7 @@ public class FullMessageLoop : IMessageLoopFactory
//Loading Event
await activeForm.Load(mr);
+ var update = ur.RawData;
//Is Attachment ? (Photo, Audio, Video, Contact, Location, Document) (Ignore Callback Queries)
if (update.Type == UpdateType.Message)
diff --git a/TelegramBotBase/MessageLoops/MiddlewareBaseMessageLoop.cs b/TelegramBotBase/MessageLoops/MiddlewareBaseMessageLoop.cs
new file mode 100644
index 0000000..13c19f7
--- /dev/null
+++ b/TelegramBotBase/MessageLoops/MiddlewareBaseMessageLoop.cs
@@ -0,0 +1,77 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using TelegramBotBase.Args;
+using TelegramBotBase.Base;
+using TelegramBotBase.Interfaces;
+using TelegramBotBase.Sessions;
+
+namespace TelegramBotBase.MessageLoops;
+
+///
+/// This message loop based on middleware pattern
+///
+public sealed class MiddlewareBaseMessageLoop : IMessageLoopFactory
+{
+ private List, Task>> Middlewares = new();
+
+ public event EventHandler UnhandledCall;
+
+ public async Task MessageLoop(BotBase bot, DeviceSession session, UpdateResult ur, MessageResult mr)
+ {
+ ur.Device = session;
+ mr.Device = session;
+
+ var messageContainer = new MessageContainer(bot, session, ur, mr);
+
+ if (Middlewares.Any())
+ {
+ int middlewareIndex = 0;
+
+ await InvokeMiddleware(messageContainer, middlewareIndex);
+ }
+ }
+
+ ///
+ /// Invokes middleware with index
+ ///
+ private async Task InvokeMiddleware(MessageContainer messageContainer, int middlewareIndex)
+ {
+ await Middlewares[middlewareIndex]
+ .Invoke(messageContainer, async () =>
+ {
+ int nextMiddlewareIndex = middlewareIndex + 1;
+ if (nextMiddlewareIndex < Middlewares.Count)
+ {
+ await InvokeMiddleware(messageContainer, nextMiddlewareIndex);
+ }
+ });
+ }
+
+ ///
+ /// Adds a new middleware
+ ///
+ public void AddMiddleware(Func, Task> middleware)
+ {
+ Middlewares.Add(middleware);
+ }
+}
+
+public struct MessageContainer
+{
+ public BotBase BotBase { get; private set; }
+ public DeviceSession DeviceSession { get; private set; }
+ public UpdateResult UpdateResult { get; private set; }
+ public MessageResult MessageResult { get; private set; }
+
+ public MessageContainer(BotBase botBase, DeviceSession deviceSession, UpdateResult updateResult, MessageResult messageResult)
+ {
+ BotBase = botBase;
+ DeviceSession = deviceSession;
+ UpdateResult = updateResult;
+ MessageResult = messageResult;
+ }
+}
\ No newline at end of file
diff --git a/TelegramBotBase/MessageLoops/MinimalMessageLoop.cs b/TelegramBotBase/MessageLoops/MinimalMessageLoop.cs
index 8f72f15..187e5d0 100644
--- a/TelegramBotBase/MessageLoops/MinimalMessageLoop.cs
+++ b/TelegramBotBase/MessageLoops/MinimalMessageLoop.cs
@@ -11,7 +11,7 @@ namespace TelegramBotBase.MessageLoops;
///
/// This is a minimal message loop which will react to all update types and just calling the Load method.
///
-public class MinimalMessageLoop : IMessageLoopFactory
+public sealed class MinimalMessageLoop : IMessageLoopFactory
{
private static readonly object EvUnhandledCall = new();
@@ -19,9 +19,6 @@ public class MinimalMessageLoop : IMessageLoopFactory
public async Task MessageLoop(BotBase bot, DeviceSession session, UpdateResult ur, MessageResult mr)
{
- var update = ur.RawData;
-
-
mr.Device = session;
var activeForm = session.ActiveForm;
diff --git a/TelegramBotFramework.sln b/TelegramBotFramework.sln
index 8fd0e98..c13f793 100644
--- a/TelegramBotFramework.sln
+++ b/TelegramBotFramework.sln
@@ -32,6 +32,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BotAndWebApplication", "Exa
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "InlineAndReplyCombination", "Examples\InlineAndReplyCombination\InlineAndReplyCombination.csproj", "{067E8EBE-F90A-4AFF-A0FF-20578216486E}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MiddlewareBaseBot", "Examples\MiddlewareBaseBot\MiddlewareBaseBot.csproj", "{8D053824-966C-4F82-B184-4840DB36D5F2}"
+EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DependencyInjection", "Examples\DependencyInjection\DependencyInjection.csproj", "{689B16BC-200E-4C68-BB2E-8B209070849B}"
EndProject
Global
@@ -80,6 +82,10 @@ Global
{067E8EBE-F90A-4AFF-A0FF-20578216486E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{067E8EBE-F90A-4AFF-A0FF-20578216486E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{067E8EBE-F90A-4AFF-A0FF-20578216486E}.Release|Any CPU.Build.0 = Release|Any CPU
+ {8D053824-966C-4F82-B184-4840DB36D5F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {8D053824-966C-4F82-B184-4840DB36D5F2}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {8D053824-966C-4F82-B184-4840DB36D5F2}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {8D053824-966C-4F82-B184-4840DB36D5F2}.Release|Any CPU.Build.0 = Release|Any CPU
{689B16BC-200E-4C68-BB2E-8B209070849B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{689B16BC-200E-4C68-BB2E-8B209070849B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{689B16BC-200E-4C68-BB2E-8B209070849B}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -97,6 +103,7 @@ Global
{261BED47-0404-4A9A-86FC-047DE42A7D25} = {BFA71E3F-31C0-4FC1-A320-4DCF704768C5}
{52EA3201-02E8-46F5-87C4-B4752C8A815C} = {BFA71E3F-31C0-4FC1-A320-4DCF704768C5}
{067E8EBE-F90A-4AFF-A0FF-20578216486E} = {BFA71E3F-31C0-4FC1-A320-4DCF704768C5}
+ {8D053824-966C-4F82-B184-4840DB36D5F2} = {BFA71E3F-31C0-4FC1-A320-4DCF704768C5}
{689B16BC-200E-4C68-BB2E-8B209070849B} = {BFA71E3F-31C0-4FC1-A320-4DCF704768C5}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution