From b278015dd490a81c645da3adaafdf3c5478e10db Mon Sep 17 00:00:00 2001 From: Florian Zevedei Date: Fri, 29 Sep 2023 18:26:39 +0200 Subject: [PATCH 01/17] Replacing static names in exception messages with nameof literal --- .../MSSQLSerializer.cs | 2 +- TelegramBotBase/Factories/DefaultStartFormFactory.cs | 2 +- TelegramBotBase/Factories/ServiceProviderStartFormFactory.cs | 2 +- TelegramBotBase/States/JSONStateMachine.cs | 2 +- TelegramBotBase/States/SimpleJSONStateMachine.cs | 2 +- TelegramBotBase/States/XMLStateMachine.cs | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/TelegramBotBase.Extensions.Serializer.Database.MSSQL/MSSQLSerializer.cs b/TelegramBotBase.Extensions.Serializer.Database.MSSQL/MSSQLSerializer.cs index ed08d95..a25cbcd 100644 --- a/TelegramBotBase.Extensions.Serializer.Database.MSSQL/MSSQLSerializer.cs +++ b/TelegramBotBase.Extensions.Serializer.Database.MSSQL/MSSQLSerializer.cs @@ -31,7 +31,7 @@ namespace TelegramBotBase.Extensions.Serializer.Database.MSSQL if (FallbackStateForm != null && !FallbackStateForm.IsSubclassOf(typeof(FormBase))) { - throw new ArgumentException("FallbackStateForm is not a subclass of FormBase"); + throw new ArgumentException($"{nameof(FallbackStateForm)} is not a subclass of FormBase"); } } diff --git a/TelegramBotBase/Factories/DefaultStartFormFactory.cs b/TelegramBotBase/Factories/DefaultStartFormFactory.cs index 7ce2988..a908c85 100644 --- a/TelegramBotBase/Factories/DefaultStartFormFactory.cs +++ b/TelegramBotBase/Factories/DefaultStartFormFactory.cs @@ -12,7 +12,7 @@ public class DefaultStartFormFactory : IStartFormFactory { if (!typeof(FormBase).IsAssignableFrom(startFormClass)) { - throw new ArgumentException("startFormClass argument must be a FormBase type"); + throw new ArgumentException($"{nameof(startFormClass)} argument must be a {nameof(FormBase)} type"); } _startFormClass = startFormClass; diff --git a/TelegramBotBase/Factories/ServiceProviderStartFormFactory.cs b/TelegramBotBase/Factories/ServiceProviderStartFormFactory.cs index 4ee81db..fbe4394 100644 --- a/TelegramBotBase/Factories/ServiceProviderStartFormFactory.cs +++ b/TelegramBotBase/Factories/ServiceProviderStartFormFactory.cs @@ -14,7 +14,7 @@ public class ServiceProviderStartFormFactory : IStartFormFactory { if (!typeof(FormBase).IsAssignableFrom(startFormClass)) { - throw new ArgumentException("startFormClass argument must be a FormBase type"); + throw new ArgumentException($"{nameof(startFormClass)} argument must be a {nameof(FormBase)} type"); } _startFormClass = startFormClass; diff --git a/TelegramBotBase/States/JSONStateMachine.cs b/TelegramBotBase/States/JSONStateMachine.cs index ada7fe4..5c764a6 100644 --- a/TelegramBotBase/States/JSONStateMachine.cs +++ b/TelegramBotBase/States/JSONStateMachine.cs @@ -29,7 +29,7 @@ public class JsonStateMachine : IStateMachine if (FallbackStateForm != null && !FallbackStateForm.IsSubclassOf(typeof(FormBase))) { - throw new ArgumentException("FallbackStateForm is not a subclass of FormBase"); + throw new ArgumentException($"{nameof(FallbackStateForm)} is not a subclass of {nameof(FormBase)}"); } FilePath = file ?? throw new ArgumentNullException(nameof(file)); diff --git a/TelegramBotBase/States/SimpleJSONStateMachine.cs b/TelegramBotBase/States/SimpleJSONStateMachine.cs index 7e8fa58..e722305 100644 --- a/TelegramBotBase/States/SimpleJSONStateMachine.cs +++ b/TelegramBotBase/States/SimpleJSONStateMachine.cs @@ -30,7 +30,7 @@ public class SimpleJsonStateMachine : IStateMachine if (FallbackStateForm != null && !FallbackStateForm.IsSubclassOf(typeof(FormBase))) { - throw new ArgumentException("FallbackStateForm is not a subclass of FormBase"); + throw new ArgumentException($"{nameof(FallbackStateForm)} is not a subclass of {nameof(FormBase)}"); } FilePath = file ?? throw new ArgumentNullException(nameof(file)); diff --git a/TelegramBotBase/States/XMLStateMachine.cs b/TelegramBotBase/States/XMLStateMachine.cs index 1a569eb..fdaea1a 100644 --- a/TelegramBotBase/States/XMLStateMachine.cs +++ b/TelegramBotBase/States/XMLStateMachine.cs @@ -27,7 +27,7 @@ public class XmlStateMachine : IStateMachine if (FallbackStateForm != null && !FallbackStateForm.IsSubclassOf(typeof(FormBase))) { - throw new ArgumentException("FallbackStateForm is not a subclass of FormBase"); + throw new ArgumentException($"{nameof(FallbackStateForm)} is not a subclass of {nameof(FormBase)}"); } FilePath = file ?? throw new ArgumentNullException(nameof(file)); From e6a48115a6c2f5667a8d550c03dfa893665c988d Mon Sep 17 00:00:00 2001 From: Florian Zevedei Date: Fri, 29 Sep 2023 18:28:55 +0200 Subject: [PATCH 02/17] Adding xml comments to BotBase --- TelegramBotBase/Builder/BotBaseBuilder.cs | 5 ++++- TelegramBotBase/Builder/Interfaces/IBuildingStage.cs | 4 ++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/TelegramBotBase/Builder/BotBaseBuilder.cs b/TelegramBotBase/Builder/BotBaseBuilder.cs index a936b6e..87b37d6 100644 --- a/TelegramBotBase/Builder/BotBaseBuilder.cs +++ b/TelegramBotBase/Builder/BotBaseBuilder.cs @@ -39,7 +39,10 @@ public class BotBaseBuilder : IAPIKeySelectionStage, IMessageLoopSelectionStage, /// private Dictionary> BotCommandScopes { get; } = new(); - + /// + /// Creates a full BotBase instance with all parameters previously set. + /// + /// public BotBase Build() { var bot = new BotBase(_apiKey, _client) diff --git a/TelegramBotBase/Builder/Interfaces/IBuildingStage.cs b/TelegramBotBase/Builder/Interfaces/IBuildingStage.cs index 17e49cc..1e92cc4 100644 --- a/TelegramBotBase/Builder/Interfaces/IBuildingStage.cs +++ b/TelegramBotBase/Builder/Interfaces/IBuildingStage.cs @@ -2,5 +2,9 @@ public interface IBuildingStage { + /// + /// Creates a full BotBase instance with all parameters previously set. + /// + /// BotBase Build(); } \ No newline at end of file From 30222d640d7e524413a923a4c36d0541260bfb6e Mon Sep 17 00:00:00 2001 From: Florian Zevedei Date: Fri, 29 Sep 2023 18:34:10 +0200 Subject: [PATCH 03/17] Adding full DI navigation example for testing purposes. - adding internal private field into FormBase class - adding Extension methods into DependencyInjection namespace for use - adding 2 NavigateTo extension methods for DI using ServiceProvider - adding example project - adding to readme as example --- .../Database/BotDbContext.cs | 12 +++ Examples/DependencyInjection/Database/User.cs | 7 ++ .../Forms/ConfirmationForm.cs | 67 +++++++++++++++ Examples/DependencyInjection/StartForm.cs | 79 +++++++++++++++++ README.md | 5 ++ TelegramBotBase/Base/FormBase.cs | 2 + .../DependencyInjection/Extensions.cs | 86 +++++++++++++++++++ .../ServiceProviderStartFormFactory.cs | 10 ++- 8 files changed, 266 insertions(+), 2 deletions(-) create mode 100644 Examples/DependencyInjection/Database/BotDbContext.cs create mode 100644 Examples/DependencyInjection/Database/User.cs create mode 100644 Examples/DependencyInjection/Forms/ConfirmationForm.cs create mode 100644 Examples/DependencyInjection/StartForm.cs create mode 100644 TelegramBotBase/DependencyInjection/Extensions.cs diff --git a/Examples/DependencyInjection/Database/BotDbContext.cs b/Examples/DependencyInjection/Database/BotDbContext.cs new file mode 100644 index 0000000..b7e20f5 --- /dev/null +++ b/Examples/DependencyInjection/Database/BotDbContext.cs @@ -0,0 +1,12 @@ +using Microsoft.EntityFrameworkCore; + +namespace DependencyInjection.Database; + +public class BotDbContext : DbContext +{ + public BotDbContext(DbContextOptions options) : base(options) + { + } + + public DbSet Users { get; set; } +} \ No newline at end of file diff --git a/Examples/DependencyInjection/Database/User.cs b/Examples/DependencyInjection/Database/User.cs new file mode 100644 index 0000000..3c01b63 --- /dev/null +++ b/Examples/DependencyInjection/Database/User.cs @@ -0,0 +1,7 @@ +namespace DependencyInjection.Database; + +public class User +{ + public long Id { get; set; } + public string LastMessage { get; set; } +} \ No newline at end of file diff --git a/Examples/DependencyInjection/Forms/ConfirmationForm.cs b/Examples/DependencyInjection/Forms/ConfirmationForm.cs new file mode 100644 index 0000000..9152242 --- /dev/null +++ b/Examples/DependencyInjection/Forms/ConfirmationForm.cs @@ -0,0 +1,67 @@ +using DependencyInjection.Database; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using TelegramBotBase.Base; +using TelegramBotBase.Form; +using TelegramBotBase.DependencyInjection; + +namespace DependencyInjection.Forms +{ + public class ConfirmationForm : FormBase + { + private readonly BotDbContext _dbContext; + + public ConfirmationForm(BotDbContext dbContext) + { + _dbContext = dbContext; + } + + public override async Task Load(MessageResult message) + { + var user = await _dbContext.Users.FindAsync(Device.DeviceId); + + if (user == null) + { + await this.NavigateTo(); + return; + } + } + + public override async Task Action(MessageResult message) + { + await message.ConfirmAction("Go back"); + + switch (message.RawData) + { + + case "back": + + await this.NavigateTo(); + + break; + + } + + + } + + public override async Task Render(MessageResult message) + { + var user = await _dbContext.Users.FindAsync(Device.DeviceId); + if (user == null) + return; + + var bf = new ButtonForm(); + bf.AddButtonRow("Back", "back"); + + await Device.Send($"ConfirmationForm: Your last message was: {user.LastMessage}. Click \"Back\" to get back.", bf); + } + + + + + } +} diff --git a/Examples/DependencyInjection/StartForm.cs b/Examples/DependencyInjection/StartForm.cs new file mode 100644 index 0000000..92c6ec0 --- /dev/null +++ b/Examples/DependencyInjection/StartForm.cs @@ -0,0 +1,79 @@ +using DependencyInjection.Database; +using DependencyInjection.Forms; +using TelegramBotBase.Base; +using TelegramBotBase.Form; +using TelegramBotBase.DependencyInjection; + +namespace DependencyInjection; + +public class StartForm : FormBase +{ + private readonly BotDbContext _dbContext; + + public StartForm(BotDbContext dbContext) + { + _dbContext = dbContext; + } + + public override async Task Load(MessageResult message) + { + var user = await _dbContext.Users.FindAsync(Device.DeviceId); + if (user is null) + { + user = new User + { + Id = Device.DeviceId, + LastMessage = "" + }; + + _dbContext.Users.Add(user); + await _dbContext.SaveChangesAsync(); + } + + if (message.IsAction) + return; + + + user.LastMessage = string.IsNullOrWhiteSpace(message.MessageText) ? "" : message.MessageText; + await _dbContext.SaveChangesAsync(); + } + + public override async Task Action(MessageResult message) + { + await message.ConfirmAction("Ok"); + + switch(message.RawData) + { + + case "open": + + await this.NavigateTo(typeof(ConfirmationForm)); + + var new_form = await this.NavigateTo(); + + if (new_form == null) + { + await Device.Send("Cant open ConfirmationForm"); + } + + break; + + } + + + } + + public override async Task Render(MessageResult message) + { + var user = await _dbContext.Users.FindAsync(Device.DeviceId); + if (user == null) + return; + + var bf = new ButtonForm(); + + bf.AddButtonRow("Open confirmation", "open"); + + await Device.Send($"Your last message's text was: `{user.LastMessage}`", bf); + } + +} diff --git a/README.md b/README.md index 644e992..ca629cb 100644 --- a/README.md +++ b/README.md @@ -1072,3 +1072,8 @@ Having already a web application and want to add a TelegramBot side-by-side with Want to use Inline- and ReplyMarkup at the same time ? Here is an example how you can do that: - [Examples/InlineAndReplyCombination](Examples/InlineAndReplyCombination) + + +Alpha: Full Dependency Injection example within this framework. + +- [Examples/DependencyInjection](Examples/DependencyInjection) \ No newline at end of file diff --git a/TelegramBotBase/Base/FormBase.cs b/TelegramBotBase/Base/FormBase.cs index 479e5ee..160952f 100644 --- a/TelegramBotBase/Base/FormBase.cs +++ b/TelegramBotBase/Base/FormBase.cs @@ -32,6 +32,8 @@ public class FormBase : IDisposable public MessageClient Client { get; set; } + IServiceProvider _serviceProvider = null; + /// /// has this formular already been disposed ? /// diff --git a/TelegramBotBase/DependencyInjection/Extensions.cs b/TelegramBotBase/DependencyInjection/Extensions.cs new file mode 100644 index 0000000..f9a0bfc --- /dev/null +++ b/TelegramBotBase/DependencyInjection/Extensions.cs @@ -0,0 +1,86 @@ +using Microsoft.Extensions.DependencyInjection; +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; +using TelegramBotBase.Form; + +namespace TelegramBotBase.DependencyInjection +{ + public static class Extensions + { + internal static FieldInfo _ServiceProviderField = typeof(FormBase).GetField("_serviceProvider", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic); + + /// + /// Use Dependency Injection to create new form and inject parameters. (Main variant) + /// + /// + /// + /// + public static async Task NavigateTo(this FormBase current_form, params object[] args) + where NewForm : FormBase + { + var _serviceProvider = current_form.GetServiceProvider(); + + var instance = ActivatorUtilities.CreateInstance(_serviceProvider, typeof(NewForm)) as NewForm; + + if (instance == null) + return null; //throw new Exception("Could not instantiate new form via DI."); + + instance.SetServiceProvider(_serviceProvider); + + await current_form.NavigateTo(instance, args); + + return instance; + } + + /// + /// Use Dependency Injection to create new form and inject parameters. (Alternative variant) + /// + /// + /// + /// + /// + /// + public static async Task NavigateTo(this FormBase current_form, Type formBaseType, params object[] args) + { + if (!typeof(FormBase).IsAssignableFrom(formBaseType)) + throw new ArgumentException($"{nameof(formBaseType)} argument must be a {nameof(FormBase)} type"); + + var _serviceProvider = current_form.GetServiceProvider(); + + var instance = ActivatorUtilities.CreateInstance(_serviceProvider, formBaseType) as FormBase; + + if (instance == null) + return null; //throw new Exception("Could not instantiate new form via DI."); + + instance.SetServiceProvider(_serviceProvider); + + await current_form.NavigateTo(instance, args); + + return instance; + } + + /// + /// Sets the internal service provider field. + /// + /// + /// + public static void SetServiceProvider(this FormBase form, IServiceProvider serviceProvider) + { + _ServiceProviderField?.SetValue(form, serviceProvider); + } + + /// + /// Gets the internal service provider field value. + /// + /// + /// + public static IServiceProvider GetServiceProvider(this FormBase form) + { + var sp = _ServiceProviderField?.GetValue(form) as IServiceProvider; + return sp; + } + } +} diff --git a/TelegramBotBase/Factories/ServiceProviderStartFormFactory.cs b/TelegramBotBase/Factories/ServiceProviderStartFormFactory.cs index fbe4394..8bac651 100644 --- a/TelegramBotBase/Factories/ServiceProviderStartFormFactory.cs +++ b/TelegramBotBase/Factories/ServiceProviderStartFormFactory.cs @@ -1,5 +1,6 @@ using System; using Microsoft.Extensions.DependencyInjection; +using TelegramBotBase.DependencyInjection; using TelegramBotBase.Form; using TelegramBotBase.Interfaces; @@ -16,14 +17,19 @@ public class ServiceProviderStartFormFactory : IStartFormFactory { throw new ArgumentException($"{nameof(startFormClass)} argument must be a {nameof(FormBase)} type"); } - + _startFormClass = startFormClass; _serviceProvider = serviceProvider; } public FormBase CreateForm() { - return (FormBase)ActivatorUtilities.CreateInstance(_serviceProvider, _startFormClass); + var fb = (FormBase)ActivatorUtilities.CreateInstance(_serviceProvider, _startFormClass); + + //Sets an internal field for future ServiceProvider navigation + fb.SetServiceProvider(_serviceProvider); + + return fb; } } From 571049eeb46a5b659e539865d429545b364c33ca Mon Sep 17 00:00:00 2001 From: Florian Zevedei Date: Fri, 29 Sep 2023 18:34:33 +0200 Subject: [PATCH 04/17] Replacing exception text with nameof litteral --- .../MSSQLSerializer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TelegramBotBase.Extensions.Serializer.Database.MSSQL/MSSQLSerializer.cs b/TelegramBotBase.Extensions.Serializer.Database.MSSQL/MSSQLSerializer.cs index a25cbcd..4dd69bb 100644 --- a/TelegramBotBase.Extensions.Serializer.Database.MSSQL/MSSQLSerializer.cs +++ b/TelegramBotBase.Extensions.Serializer.Database.MSSQL/MSSQLSerializer.cs @@ -31,7 +31,7 @@ namespace TelegramBotBase.Extensions.Serializer.Database.MSSQL if (FallbackStateForm != null && !FallbackStateForm.IsSubclassOf(typeof(FormBase))) { - throw new ArgumentException($"{nameof(FallbackStateForm)} is not a subclass of FormBase"); + throw new ArgumentException($"{nameof(FallbackStateForm)} is not a subclass of {nameof(FormBase)}"); } } From e3b1a32535698f78ae094f7d6e4b863772168fb9 Mon Sep 17 00:00:00 2001 From: Florian Zevedei Date: Fri, 29 Sep 2023 18:40:20 +0200 Subject: [PATCH 05/17] Adding missing Program.cs --- Examples/DependencyInjection/Program.cs | 35 +++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 Examples/DependencyInjection/Program.cs diff --git a/Examples/DependencyInjection/Program.cs b/Examples/DependencyInjection/Program.cs new file mode 100644 index 0000000..f33bf53 --- /dev/null +++ b/Examples/DependencyInjection/Program.cs @@ -0,0 +1,35 @@ +using DependencyInjection.Database; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using TelegramBotBase.Builder; + +namespace DependencyInjection +{ + internal class Program + { + static async Task Main(string[] args) + { + + + var serviceCollection = new ServiceCollection() + .AddDbContext(x => x.UseInMemoryDatabase("TelegramBotBase")); + + var serviceProvider = serviceCollection.BuildServiceProvider(); + + var bot = BotBaseBuilder.Create() + .WithAPIKey(Environment.GetEnvironmentVariable("API_KEY") ?? + throw new Exception("API_KEY is not set")) + .DefaultMessageLoop() + .WithServiceProvider(serviceProvider) + .NoProxy() + .NoCommands() + .NoSerialization() + .DefaultLanguage() + .Build(); + + await bot.Start(); + await Task.Delay(-1); + + } + } +} \ No newline at end of file From 73863c4154373f226e050c55840e8eadf7fc9d95 Mon Sep 17 00:00:00 2001 From: Florian Zevedei Date: Fri, 29 Sep 2023 18:40:56 +0200 Subject: [PATCH 06/17] Adding project files --- .../DependencyInjection.csproj | 19 +++++++++++++++++++ TelegramBotBase/TelegramBotBase.csproj | 2 +- TelegramBotFramework.sln | 9 ++++++++- 3 files changed, 28 insertions(+), 2 deletions(-) create mode 100644 Examples/DependencyInjection/DependencyInjection.csproj diff --git a/Examples/DependencyInjection/DependencyInjection.csproj b/Examples/DependencyInjection/DependencyInjection.csproj new file mode 100644 index 0000000..eab1f5b --- /dev/null +++ b/Examples/DependencyInjection/DependencyInjection.csproj @@ -0,0 +1,19 @@ + + + + Exe + net7.0 + enable + enable + + + + + + + + + + + + diff --git a/TelegramBotBase/TelegramBotBase.csproj b/TelegramBotBase/TelegramBotBase.csproj index 2e16171..0967e33 100644 --- a/TelegramBotBase/TelegramBotBase.csproj +++ b/TelegramBotBase/TelegramBotBase.csproj @@ -1,7 +1,7 @@  - netstandard2.0;netcoreapp3.1;net6 + netstandard2.0;netcoreapp3.1;net6;net7 10 false False diff --git a/TelegramBotFramework.sln b/TelegramBotFramework.sln index 885b8a0..8fd0e98 100644 --- a/TelegramBotFramework.sln +++ b/TelegramBotFramework.sln @@ -30,7 +30,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EFCoreBot", "Examples\EFCor EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BotAndWebApplication", "Examples\BotAndWebApplication\BotAndWebApplication.csproj", "{52EA3201-02E8-46F5-87C4-B4752C8A815C}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "InlineAndReplyCombination", "Examples\InlineAndReplyCombination\InlineAndReplyCombination.csproj", "{067E8EBE-F90A-4AFF-A0FF-20578216486E}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "InlineAndReplyCombination", "Examples\InlineAndReplyCombination\InlineAndReplyCombination.csproj", "{067E8EBE-F90A-4AFF-A0FF-20578216486E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DependencyInjection", "Examples\DependencyInjection\DependencyInjection.csproj", "{689B16BC-200E-4C68-BB2E-8B209070849B}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -78,6 +80,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 + {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 + {689B16BC-200E-4C68-BB2E-8B209070849B}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -91,6 +97,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} + {689B16BC-200E-4C68-BB2E-8B209070849B} = {BFA71E3F-31C0-4FC1-A320-4DCF704768C5} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {59CB40E1-9FA7-4867-A56F-4F418286F057} From ca473f78edb68f6cf55468f375f5a6895cf62965 Mon Sep 17 00:00:00 2001 From: Florian Zevedei Date: Thu, 2 Nov 2023 14:52:05 +0100 Subject: [PATCH 07/17] Adding /start command handling to test project --- Examples/InlineAndReplyCombination/Program.cs | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/Examples/InlineAndReplyCombination/Program.cs b/Examples/InlineAndReplyCombination/Program.cs index 00372c7..fab456d 100644 --- a/Examples/InlineAndReplyCombination/Program.cs +++ b/Examples/InlineAndReplyCombination/Program.cs @@ -27,6 +27,8 @@ namespace InlineAndReplyCombination await BotBaseInstance.UploadBotCommands(); + BotBaseInstance.BotCommand += BotBaseInstance_BotCommand; + await BotBaseInstance.Start(); @@ -37,5 +39,28 @@ namespace InlineAndReplyCombination await BotBaseInstance.Stop(); } + + private static async Task BotBaseInstance_BotCommand(object sender, TelegramBotBase.Args.BotCommandEventArgs e) + { + + switch(e.Command) + { + case "/start": + + + var start = new StartForm(); + + await e.Device.ActiveForm.NavigateTo(start); + + + break; + + + + } + + + + } } } \ No newline at end of file From 40ba9bd8ab119c9cc8569acf1e083b8c9f58e3af Mon Sep 17 00:00:00 2001 From: Florian Zevedei Date: Sun, 26 Nov 2023 15:07:43 +0100 Subject: [PATCH 08/17] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 778f4f7..79f4deb 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # .NET Telegram Bot Framework - Context based addon -[![NuGet version (TelegramBotBase)](https://img.shields.io/nuget/v/TelegramBotBase.svg?style=flat-square)](https://www.nuget.org/packages/TelegramBotBase/) +[![NuGet version (TelegramBotBase)](https://img.shields.io/nuget/vpre/TelegramBotBase.svg?style=flat-square)](https://www.nuget.org/packages/TelegramBotBase/) [![Telegram chat](https://img.shields.io/badge/Support_Chat-Telegram-blue.svg?style=flat-square)](https://www.t.me/tgbotbase) [![License](https://img.shields.io/github/license/MajMcCloud/telegrambotframework.svg?style=flat-square&maxAge=2592000&label=License)](https://raw.githubusercontent.com/MajMcCloud/TelegramBotFramework/master/LICENCE.md) From f447b058d0434fe7ca968dc1ea76f6eb2e22f673 Mon Sep 17 00:00:00 2001 From: Florian Zevedei Date: Sun, 26 Nov 2023 15:08:40 +0100 Subject: [PATCH 09/17] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 79f4deb..778f4f7 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # .NET Telegram Bot Framework - Context based addon -[![NuGet version (TelegramBotBase)](https://img.shields.io/nuget/vpre/TelegramBotBase.svg?style=flat-square)](https://www.nuget.org/packages/TelegramBotBase/) +[![NuGet version (TelegramBotBase)](https://img.shields.io/nuget/v/TelegramBotBase.svg?style=flat-square)](https://www.nuget.org/packages/TelegramBotBase/) [![Telegram chat](https://img.shields.io/badge/Support_Chat-Telegram-blue.svg?style=flat-square)](https://www.t.me/tgbotbase) [![License](https://img.shields.io/github/license/MajMcCloud/telegrambotframework.svg?style=flat-square&maxAge=2592000&label=License)](https://raw.githubusercontent.com/MajMcCloud/TelegramBotFramework/master/LICENCE.md) From 43f4ab106174c834b8bf04b08c8b7531ee62edb9 Mon Sep 17 00:00:00 2001 From: Florian Zevedei Date: Sun, 26 Nov 2023 15:09:42 +0100 Subject: [PATCH 10/17] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e29b7a9..4a20ed5 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # .NET Telegram Bot Framework - Context based addon -[![NuGet version (TelegramBotBase)](https://img.shields.io/nuget/v/TelegramBotBase.svg?style=flat-square)](https://www.nuget.org/packages/TelegramBotBase/) +[![NuGet version (TelegramBotBase)](https://img.shields.io/nuget/vpre/TelegramBotBase.svg?style=flat-square)](https://www.nuget.org/packages/TelegramBotBase/) [![Telegram chat](https://img.shields.io/badge/Support_Chat-Telegram-blue.svg?style=flat-square)](https://www.t.me/tgbotbase) [![License](https://img.shields.io/github/license/MajMcCloud/telegrambotframework.svg?style=flat-square&maxAge=2592000&label=License)](https://raw.githubusercontent.com/MajMcCloud/TelegramBotFramework/master/LICENCE.md) From 89332244e3b1b6cc8ebe47ac81f40eb77ad6d685 Mon Sep 17 00:00:00 2001 From: "TCSBANK\\e.khudik" Date: Mon, 4 Dec 2023 15:10:28 +0300 Subject: [PATCH 11/17] Added the ReceiveError event to MessageClient to provide the ability to handle exceptions in client code --- TelegramBotBase/Base/ErrorResult.cs | 14 ++++++++ TelegramBotBase/Base/MessageClient.cs | 50 ++++++++++++++++++++------- 2 files changed, 51 insertions(+), 13 deletions(-) create mode 100644 TelegramBotBase/Base/ErrorResult.cs diff --git a/TelegramBotBase/Base/ErrorResult.cs b/TelegramBotBase/Base/ErrorResult.cs new file mode 100644 index 0000000..00d7c04 --- /dev/null +++ b/TelegramBotBase/Base/ErrorResult.cs @@ -0,0 +1,14 @@ +using System; + +namespace TelegramBotBase.Base +{ + public class ErrorResult : EventArgs + { + public ErrorResult(Exception exception) + { + Exception = exception; + } + + public Exception Exception { get; } + } +} diff --git a/TelegramBotBase/Base/MessageClient.cs b/TelegramBotBase/Base/MessageClient.cs index b5f9b3a..b95131f 100644 --- a/TelegramBotBase/Base/MessageClient.cs +++ b/TelegramBotBase/Base/MessageClient.cs @@ -18,6 +18,7 @@ namespace TelegramBotBase.Base; public class MessageClient { private static readonly object EvOnMessageLoop = new(); + private static readonly object EvOnReceiveError = new(); private static object __evOnMessage = new(); @@ -128,22 +129,12 @@ public class MessageClient await OnMessageLoop(new UpdateResult(update, null)); } - public Task HandleErrorAsync(ITelegramBotClient botClient, Exception exception, + public async Task HandleErrorAsync(ITelegramBotClient botClient, Exception exception, CancellationToken cancellationToken) { - if (exception is ApiRequestException exApi) - { - Console.WriteLine($"Telegram API Error:\n[{exApi.ErrorCode}]\n{exApi.Message}"); - } - else - { - Console.WriteLine(exception.ToString()); - } - - return Task.CompletedTask; + await OnReceiveError(new ErrorResult(exception)); } - /// /// This will return the current list of bot commands. /// @@ -186,7 +177,40 @@ public class MessageClient public async Task OnMessageLoop(UpdateResult update) { - await (Events[EvOnMessageLoop] as Async.AsyncEventHandler)?.Invoke(this, update); + var eventHandlers = (Events[EvOnMessageLoop] as Async.AsyncEventHandler)?.Invoke(this, update); + + if (eventHandlers != null) + { + await eventHandlers; + } + } + + + public event Async.AsyncEventHandler ReceiveError + { + add => Events.AddHandler(EvOnReceiveError, value); + remove => Events.RemoveHandler(EvOnReceiveError, value); + } + + public async Task OnReceiveError(ErrorResult update) + { + var eventHandlers = (Events[EvOnReceiveError] as Async.AsyncEventHandler)?.Invoke(this, update); + + if (eventHandlers != null) + { + await eventHandlers; + } + else + { + if (update.Exception is ApiRequestException exApi) + { + Console.WriteLine($"Telegram API Error:\n[{exApi.ErrorCode}]\n{exApi.Message}"); + } + else + { + Console.WriteLine(update.Exception.ToString()); + } + } } #endregion From 62b6bafbd3a851a5eef8bb83997d610c87a045bb Mon Sep 17 00:00:00 2001 From: "TCSBANK\\e.khudik" Date: Tue, 5 Dec 2023 13:02:53 +0300 Subject: [PATCH 12/17] Fix: get chat id from edited message --- TelegramBotBase/Base/UpdateResult.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/TelegramBotBase/Base/UpdateResult.cs b/TelegramBotBase/Base/UpdateResult.cs index 82d46b3..de41c0a 100644 --- a/TelegramBotBase/Base/UpdateResult.cs +++ b/TelegramBotBase/Base/UpdateResult.cs @@ -16,6 +16,7 @@ public class UpdateResult : ResultBase /// public override long DeviceId => RawData?.Message?.Chat?.Id + ?? RawData?.EditedMessage?.Chat?.Id ?? RawData?.CallbackQuery?.Message?.Chat?.Id ?? Device?.DeviceId ?? 0; From f401216effc952932c7a04cbd97053be03c41c9e Mon Sep 17 00:00:00 2001 From: Florian Zevedei Date: Wed, 6 Dec 2023 15:19:28 +0100 Subject: [PATCH 13/17] Fixing EditMessage behaviour --- TelegramBotBase/Base/UpdateResult.cs | 1 + TelegramBotBase/MessageLoops/FormBaseMessageLoop.cs | 6 ++++++ TelegramBotBase/MessageLoops/FullMessageLoop.cs | 6 ++++++ 3 files changed, 13 insertions(+) diff --git a/TelegramBotBase/Base/UpdateResult.cs b/TelegramBotBase/Base/UpdateResult.cs index 82d46b3..de41c0a 100644 --- a/TelegramBotBase/Base/UpdateResult.cs +++ b/TelegramBotBase/Base/UpdateResult.cs @@ -16,6 +16,7 @@ public class UpdateResult : ResultBase /// public override long DeviceId => RawData?.Message?.Chat?.Id + ?? RawData?.EditedMessage?.Chat?.Id ?? RawData?.CallbackQuery?.Message?.Chat?.Id ?? Device?.DeviceId ?? 0; diff --git a/TelegramBotBase/MessageLoops/FormBaseMessageLoop.cs b/TelegramBotBase/MessageLoops/FormBaseMessageLoop.cs index 0069c1b..4bec2a0 100644 --- a/TelegramBotBase/MessageLoops/FormBaseMessageLoop.cs +++ b/TelegramBotBase/MessageLoops/FormBaseMessageLoop.cs @@ -72,6 +72,12 @@ public class FormBaseMessageLoop : IMessageLoopFactory } } + //Message edited ? + if(update.Type == UpdateType.EditedMessage) + { + await activeForm.Edited(mr); + } + //Action Event if (!session.FormSwitched && mr.IsAction) { diff --git a/TelegramBotBase/MessageLoops/FullMessageLoop.cs b/TelegramBotBase/MessageLoops/FullMessageLoop.cs index 2c4b1b9..1affd19 100644 --- a/TelegramBotBase/MessageLoops/FullMessageLoop.cs +++ b/TelegramBotBase/MessageLoops/FullMessageLoop.cs @@ -65,6 +65,12 @@ public class FullMessageLoop : IMessageLoopFactory } } + //Message edited ? + if (update.Type == UpdateType.EditedMessage) + { + await activeForm.Edited(mr); + } + //Action Event if (!session.FormSwitched && mr.IsAction) { From 88309e607b4fcf5f42e597ba1537fe1276663340 Mon Sep 17 00:00:00 2001 From: Florian Zevedei Date: Wed, 6 Dec 2023 15:44:59 +0100 Subject: [PATCH 14/17] Adding localization notes to Readme file --- README.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/README.md b/README.md index 4a20ed5..ec0f265 100644 --- a/README.md +++ b/README.md @@ -59,6 +59,7 @@ BitTorrent: `TYVZSykaVT1nKZnz9hjDgBRNB9VavU1bpW` * [TaggedButtonGrid](#tagged-button-grid) * [CheckedButtonList](#checked-button-list) * [MultiToggleButton](#multi-toggle-button) +- [Localizations](#localizations) - [Groups](#groups) * [SplitterForm](#splitter-form) * [GroupForm](#group-form) @@ -714,6 +715,19 @@ Check the example project [TelegramBotBase.Test/Tests/Controls/CheckedButtonList Check the example project [TelegramBotBase.Test/Tests/Controls/MultiToggleButtonForm.cs](TelegramBotBase.Test/Tests/Controls/MultiToggleButtonForm.cs) + +## Localizations + +The current available languages for controls are: + +- English +- German +- Persian + +You can add other languages easily by creating a subclass of the [TelegramBotBase/Localizations/Localization.cs](TelegramBotBase/Localizations/Localization.cs) class. + +To set the default language set the *Language* property on the static [TelegramBotBase/Localizations/Default.cs](TelegramBotBase/Localizations/Default.cs) instance. + ## Groups For groups, there are multiple different tools which help to work with and allows bot also to manage From 794dee7f580e7788d859ae5289dcce44d58702dc Mon Sep 17 00:00:00 2001 From: Florian Zevedei Date: Thu, 7 Dec 2023 17:49:35 +0100 Subject: [PATCH 15/17] improved readability --- TelegramBotBase/Base/MessageClient.cs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/TelegramBotBase/Base/MessageClient.cs b/TelegramBotBase/Base/MessageClient.cs index b95131f..17ec7f3 100644 --- a/TelegramBotBase/Base/MessageClient.cs +++ b/TelegramBotBase/Base/MessageClient.cs @@ -199,18 +199,19 @@ public class MessageClient if (eventHandlers != null) { await eventHandlers; + return; + } + + //Fallback when no event handler is used. + if (update.Exception is ApiRequestException exApi) + { + Console.WriteLine($"Telegram API Error:\n[{exApi.ErrorCode}]\n{exApi.Message}"); } else { - if (update.Exception is ApiRequestException exApi) - { - Console.WriteLine($"Telegram API Error:\n[{exApi.ErrorCode}]\n{exApi.Message}"); - } - else - { - Console.WriteLine(update.Exception.ToString()); - } + Console.WriteLine(update.Exception.ToString()); } + } #endregion From 064399ed77be926445d8b35b54f8862385c48667 Mon Sep 17 00:00:00 2001 From: Florian Zevedei Date: Sat, 9 Dec 2023 14:54:23 +0100 Subject: [PATCH 16/17] Replace nuget reference with project for development --- .../TelegramBotBase.Extensions.Images.csproj | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/TelegramBotBase.Extensions.Images/TelegramBotBase.Extensions.Images.csproj b/TelegramBotBase.Extensions.Images/TelegramBotBase.Extensions.Images.csproj index c181fa8..347e740 100644 --- a/TelegramBotBase.Extensions.Images/TelegramBotBase.Extensions.Images.csproj +++ b/TelegramBotBase.Extensions.Images/TelegramBotBase.Extensions.Images.csproj @@ -15,11 +15,14 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + + + + From c1018ac5a0079d8f116966907b04b2deb8c1f6d1 Mon Sep 17 00:00:00 2001 From: Florian Zevedei Date: Sat, 9 Dec 2023 14:56:02 +0100 Subject: [PATCH 17/17] Adding ThrowPendingUpdates option to builder and Message client. --- TelegramBotBase/Base/MessageClient.cs | 10 ++++++++++ TelegramBotBase/Builder/BotBaseBuilder.cs | 15 ++++++++++----- .../Interfaces/INetworkingSelectionStage.cs | 15 ++++++++++----- 3 files changed, 30 insertions(+), 10 deletions(-) diff --git a/TelegramBotBase/Base/MessageClient.cs b/TelegramBotBase/Base/MessageClient.cs index 17ec7f3..10a724e 100644 --- a/TelegramBotBase/Base/MessageClient.cs +++ b/TelegramBotBase/Base/MessageClient.cs @@ -28,6 +28,14 @@ public class MessageClient private CancellationTokenSource _cancellationTokenSource; + /// + /// Indicates if all pending Telegram.Bot.Types.Updates should be thrown out before + // start polling. If set to true Telegram.Bot.Polling.ReceiverOptions.AllowedUpdates + // should be set to not null, otherwise Telegram.Bot.Polling.ReceiverOptions.AllowedUpdates + // will effectively be set to receive all Telegram.Bot.Types.Updates. + /// + public bool ThrowPendingUpdates { get; set; } + public MessageClient(string apiKey) { @@ -114,6 +122,8 @@ public class MessageClient var receiverOptions = new ReceiverOptions(); + receiverOptions.ThrowPendingUpdates = ThrowPendingUpdates; + TelegramClient.StartReceiving(HandleUpdateAsync, HandleErrorAsync, receiverOptions, _cancellationTokenSource.Token); } diff --git a/TelegramBotBase/Builder/BotBaseBuilder.cs b/TelegramBotBase/Builder/BotBaseBuilder.cs index 497bed0..c3d9394 100644 --- a/TelegramBotBase/Builder/BotBaseBuilder.cs +++ b/TelegramBotBase/Builder/BotBaseBuilder.cs @@ -207,7 +207,7 @@ public class BotBaseBuilder : IAPIKeySelectionStage, IMessageLoopSelectionStage, #region "Step 4 (Network Settings)" - public IBotCommandsStage WithProxy(string proxyAddress) + public IBotCommandsStage WithProxy(string proxyAddress, bool throwPendingUpdates = false) { var url = new Uri(proxyAddress); _client = new MessageClient(_apiKey, url) @@ -217,11 +217,12 @@ public class BotBaseBuilder : IAPIKeySelectionStage, IMessageLoopSelectionStage, Timeout = new TimeSpan(0, 1, 0) }, }; + _client.ThrowPendingUpdates = throwPendingUpdates; return this; } - public IBotCommandsStage NoProxy() + public IBotCommandsStage NoProxy(bool throwPendingUpdates = false) { _client = new MessageClient(_apiKey) { @@ -230,11 +231,12 @@ public class BotBaseBuilder : IAPIKeySelectionStage, IMessageLoopSelectionStage, Timeout = new TimeSpan(0, 1, 0) } }; + _client.ThrowPendingUpdates = throwPendingUpdates; return this; } - public IBotCommandsStage WithBotClient(TelegramBotClient tgclient) + public IBotCommandsStage WithBotClient(TelegramBotClient tgclient, bool throwPendingUpdates = false) { _client = new MessageClient(_apiKey, tgclient) { @@ -243,11 +245,12 @@ public class BotBaseBuilder : IAPIKeySelectionStage, IMessageLoopSelectionStage, Timeout = new TimeSpan(0, 1, 0) } }; + _client.ThrowPendingUpdates = throwPendingUpdates; return this; } - public IBotCommandsStage WithHostAndPort(string proxyHost, int proxyPort) + public IBotCommandsStage WithHostAndPort(string proxyHost, int proxyPort, bool throwPendingUpdates = false) { _client = new MessageClient(_apiKey, proxyHost, proxyPort) { @@ -256,10 +259,11 @@ public class BotBaseBuilder : IAPIKeySelectionStage, IMessageLoopSelectionStage, Timeout = new TimeSpan(0, 1, 0) } }; + _client.ThrowPendingUpdates = throwPendingUpdates; return this; } - public IBotCommandsStage WithHttpClient(HttpClient tgclient) + public IBotCommandsStage WithHttpClient(HttpClient tgclient, bool throwPendingUpdates = false) { _client = new MessageClient(_apiKey, tgclient) { @@ -268,6 +272,7 @@ public class BotBaseBuilder : IAPIKeySelectionStage, IMessageLoopSelectionStage, Timeout = new TimeSpan(0, 1, 0) } }; + _client.ThrowPendingUpdates = throwPendingUpdates; return this; } diff --git a/TelegramBotBase/Builder/Interfaces/INetworkingSelectionStage.cs b/TelegramBotBase/Builder/Interfaces/INetworkingSelectionStage.cs index 3e48b21..cd17e9d 100644 --- a/TelegramBotBase/Builder/Interfaces/INetworkingSelectionStage.cs +++ b/TelegramBotBase/Builder/Interfaces/INetworkingSelectionStage.cs @@ -9,22 +9,25 @@ public interface INetworkingSelectionStage /// Chooses a proxy as network configuration. /// /// + /// Indicates if all pending Telegram.Bot.Types.Updates should be thrown out before start polling. /// - IBotCommandsStage WithProxy(string proxyAddress); + IBotCommandsStage WithProxy(string proxyAddress, bool throwPendingUpdates = false); /// /// Do not choose a proxy as network configuration. /// + /// Indicates if all pending Telegram.Bot.Types.Updates should be thrown out before start polling. /// - IBotCommandsStage NoProxy(); + IBotCommandsStage NoProxy(bool throwPendingUpdates = false); /// /// Chooses a custom instance of TelegramBotClient. /// /// + /// Indicates if all pending Telegram.Bot.Types.Updates should be thrown out before start polling. /// - IBotCommandsStage WithBotClient(TelegramBotClient client); + IBotCommandsStage WithBotClient(TelegramBotClient client, bool throwPendingUpdates = false); /// @@ -32,13 +35,15 @@ public interface INetworkingSelectionStage /// /// /// + /// Indicates if all pending Telegram.Bot.Types.Updates should be thrown out before start polling. /// - IBotCommandsStage WithHostAndPort(string proxyHost, int Port); + IBotCommandsStage WithHostAndPort(string proxyHost, int Port, bool throwPendingUpdates = false); /// /// Uses a custom http client. /// /// + /// Indicates if all pending Telegram.Bot.Types.Updates should be thrown out before start polling. /// - IBotCommandsStage WithHttpClient(HttpClient client); + IBotCommandsStage WithHttpClient(HttpClient client, bool throwPendingUpdates = false); } \ No newline at end of file