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