diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 755cf05..8e4c1ab 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -12,9 +12,9 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup .NET - uses: actions/setup-dotnet@v3 + uses: actions/setup-dotnet@v4 with: dotnet-version: 7.0.x - name: Restore dependencies @@ -22,4 +22,4 @@ jobs: - name: Build run: dotnet build --no-restore /p:TreatWarningsAsErrors=false - name: Test - run: dotnet test --no-build --verbosity normal + run: dotnet test --no-build --verbosity normal -e telegram_test_token=${{secrets.TELEGRAM_TEST_TOKEN}} \ No newline at end of file diff --git a/Examples/Telegram.Examples/Telegram.Examples.csproj b/Examples/Telegram.Examples/Telegram.Examples.csproj index 1622ed8..2891280 100644 --- a/Examples/Telegram.Examples/Telegram.Examples.csproj +++ b/Examples/Telegram.Examples/Telegram.Examples.csproj @@ -5,6 +5,7 @@ net7.0 enable enable + true diff --git a/Telegram.Net/Services/TelegramHostedService.cs b/Telegram.Net/Services/TelegramHostedService.cs index 7793223..3f05c4f 100644 --- a/Telegram.Net/Services/TelegramHostedService.cs +++ b/Telegram.Net/Services/TelegramHostedService.cs @@ -14,14 +14,14 @@ namespace Telegram.Net.Services; public class TelegramHostedService : IHostedService { private IServiceCollection isc { get; } - private TelegramBotClient Client { get; } + internal TelegramBotClient Client { get; set; } private ITelegramBotConfig Config { get; } - internal static Dictionary> CommandHandler { get; } = new(); - internal static List> EditedMessageHandler { get; } = new(); - internal static Dictionary> CallbackQueryHandler { get; } = new(); - internal static Dictionary> InlineHandler { get; } = new(); - internal static Func? PreCheckoutHandler { get; set; } - internal static List> DefaultUpdateHandler { get; } = new(); + internal Dictionary> CommandHandler { get; } = new(); + internal List> EditedMessageHandler { get; } = new(); + internal Dictionary> CallbackQueryHandler { get; } = new(); + internal Dictionary> InlineHandler { get; } = new(); + internal Func? PreCheckoutHandler { get; set; } + internal List> DefaultUpdateHandler { get; } = new(); public TelegramHostedService(ITelegramBotConfig config, IServiceCollection isc) { @@ -47,10 +47,8 @@ public class TelegramHostedService : IHostedService internal async Task AddAttributes(CancellationToken cancellationToken) { - var mutex = new Mutex(); await Task.Run(async () => { - mutex.WaitOne(); var implementations = AppDomain.CurrentDomain.GetAssemblies() .SelectMany(a => a.GetTypes()) .Where(t => typeof(IUpdatePollingService).IsAssignableFrom(t) && !t.IsInterface); @@ -82,7 +80,7 @@ public class TelegramHostedService : IHostedService if (IsValidHandlerMethod(method, typeof(Message))) { var handler = CreateDelegate(method); - CommandHandler.Add(commandAttr.Command, handler); + CommandHandler.TryAdd(commandAttr.Command, handler); } continue; } @@ -93,7 +91,7 @@ public class TelegramHostedService : IHostedService if (IsValidHandlerMethod(method, typeof(CallbackQuery))) { var handler = CreateDelegate(method); - CallbackQueryHandler.Add(callbackAttr.QueryId, handler); + CallbackQueryHandler.TryAdd(callbackAttr.QueryId, handler); } continue; } @@ -115,7 +113,7 @@ public class TelegramHostedService : IHostedService if (IsValidHandlerMethod(method, typeof(InlineQuery))) { var handler = CreateDelegate(method); - InlineHandler.Add(inlineAttr.InlineId, handler); + InlineHandler.TryAdd(inlineAttr.InlineId, handler); } continue; } @@ -142,44 +140,44 @@ public class TelegramHostedService : IHostedService continue; } } - mutex.ReleaseMutex(); }, cancellationToken); } + internal async Task UpdateHandler(ITelegramBotClient client, Update update, CancellationToken ctx) + { + switch (update) + { + case { Message: { } message }: + await CommandHandler.FirstOrDefault(k => message.Text!.StartsWith(k.Key)).Value(client, message, ctx); + break; + case { EditedMessage: { } message }: + EditedMessageHandler.ForEach(async k => await k(client, message, ctx)); + break; + case { CallbackQuery: { } callbackQuery }: + await CallbackQueryHandler.FirstOrDefault(k => callbackQuery.Data!.StartsWith(k.Key)).Value(client, callbackQuery, ctx); + break; + case { InlineQuery: { } inlineQuery }: + await InlineHandler.FirstOrDefault(k => inlineQuery.Id.StartsWith(k.Key)).Value(client, inlineQuery, ctx); + break; + case { PreCheckoutQuery: { } preCheckoutQuery }: + if (PreCheckoutHandler != null) await PreCheckoutHandler(client, preCheckoutQuery, ctx); + break; + default: + DefaultUpdateHandler.ForEach(async k => await k(client, update, ctx)); + break; + } + } + [SuppressMessage("ReSharper", "AsyncVoidLambda")] public async Task StartAsync(CancellationToken cancellationToken) { await AddAttributes(cancellationToken); - - + + + Client.StartReceiving( - async (client, update, ctx) => - { - switch (update) - { - case { Message: { } message }: - await CommandHandler.FirstOrDefault(k => message.Text!.StartsWith(k.Key)).Value(client, message, cancellationToken); - break; - case { EditedMessage: { } message }: - EditedMessageHandler.ForEach(async k => await k(client, message, cancellationToken)); - break; - case { CallbackQuery: { } callbackQuery }: - await CallbackQueryHandler.FirstOrDefault(k => callbackQuery.Data!.StartsWith(k.Key)).Value(client, callbackQuery, cancellationToken); - break; - case { InlineQuery: { } inlineQuery }: - await InlineHandler.FirstOrDefault(k => inlineQuery.Id.StartsWith(k.Key)).Value(client, inlineQuery, cancellationToken); - break; - case {PreCheckoutQuery: { } preCheckoutQuery}: - if (PreCheckoutHandler != null) - await PreCheckoutHandler(client, preCheckoutQuery, cancellationToken); - break; - default: - DefaultUpdateHandler.ForEach(async k => await k(client, update, ctx)); - break; - } - - }, + UpdateHandler, Config.errorHandler ?? ((_, _, _) => Task.CompletedTask), Config.ReceiverOptions, cancellationToken); diff --git a/Telegram.Net/Telegram.Net.csproj b/Telegram.Net/Telegram.Net.csproj index eae3820..f612b35 100644 --- a/Telegram.Net/Telegram.Net.csproj +++ b/Telegram.Net/Telegram.Net.csproj @@ -1,6 +1,7 @@ + true net7.0 enable enable diff --git a/tests/Telegram.Tests/Telegram.Tests.csproj b/tests/Telegram.Tests/Telegram.Tests.csproj index 2eed84f..0b9b510 100644 --- a/tests/Telegram.Tests/Telegram.Tests.csproj +++ b/tests/Telegram.Tests/Telegram.Tests.csproj @@ -4,7 +4,7 @@ net7.0 enable enable - + true false true diff --git a/tests/Telegram.Tests/TestTelegramHostedService.cs b/tests/Telegram.Tests/TestTelegramHostedService.cs index c6d70d4..656485f 100644 --- a/tests/Telegram.Tests/TestTelegramHostedService.cs +++ b/tests/Telegram.Tests/TestTelegramHostedService.cs @@ -13,13 +13,14 @@ using Microsoft.Extensions.DependencyInjection; using Moq; using Telegram.Bot.Types.Enums; using Telegram.Bot.Types.ReplyMarkups; +using Telegram.Net.Models; namespace Telegram.Tests { [TestFixture] public class TelegramHostedServiceTests { - private Mock _configMock; + private TelegramBotConfig _configMock; private ServiceCollection _services; [SetUp] @@ -30,14 +31,14 @@ namespace Telegram.Tests .AddJsonFile("appsettings.json", false) .AddEnvironmentVariables() .Build(); - _configMock = new Mock(); - _configMock.Setup(c => c.Token).Returns(conf.GetValue("telegram_test_token")); + + _configMock = new TelegramBotConfig(conf.GetValue("telegram_test_token") ?? throw new Exception("Provide telegram token first")); _services = new ServiceCollection(); } public class TestHandler : IUpdatePollingService { - [Command("start")] + [Command("/start")] public async Task HandleStart(ITelegramBotClient client, Message message, CancellationToken ct) => await client.SendTextMessageAsync(message.Chat.Id, "Started", cancellationToken: ct); @@ -66,18 +67,18 @@ namespace Telegram.Tests public void AddAttributes_RegistersCommandHandlersCorrectly() { _services.AddSingleton(); - var service = new TelegramHostedService(_configMock.Object, _services); + var service = new TelegramHostedService(_configMock, _services); service.AddAttributes(CancellationToken.None).Wait(); Assert.Multiple(() => { - Assert.That(TelegramHostedService.CommandHandler, Contains.Key("start")); - Assert.That(TelegramHostedService.CallbackQueryHandler, Contains.Key("test_callback")); - Assert.That(TelegramHostedService.EditedMessageHandler, Has.Count.EqualTo(1)); - Assert.That(TelegramHostedService.InlineHandler, Contains.Key("search")); - Assert.That(TelegramHostedService.PreCheckoutHandler, Is.Not.Null); - Assert.That(TelegramHostedService.DefaultUpdateHandler, Has.Count.EqualTo(1)); + Assert.That(service.CommandHandler, Contains.Key("/start")); + Assert.That(service.CallbackQueryHandler, Contains.Key("test_callback")); + Assert.That(service.EditedMessageHandler, Has.Count.EqualTo(1)); + Assert.That(service.InlineHandler, Contains.Key("search")); + Assert.That(service.PreCheckoutHandler, Is.Not.Null); + Assert.That(service.DefaultUpdateHandler, Has.Count.EqualTo(1)); }); } @@ -118,8 +119,14 @@ namespace Telegram.Tests [SetUp] public void Setup() { + var conf = new ConfigurationBuilder() + .SetBasePath(AppContext.BaseDirectory) + .AddJsonFile("appsettings.json", false) + .AddEnvironmentVariables() + .Build(); + _configMock = new Mock(); - _configMock.Setup(c => c.Token).Returns("test_token"); + _configMock.Setup(c => c.Token).Returns(conf.GetValue("telegram_test_token") ?? throw new Exception("Provide token first")); _configMock.Setup(c => c.ReceiverOptions).Returns(new ReceiverOptions()); var services = new ServiceCollection(); @@ -137,23 +144,6 @@ namespace Telegram.Tests var message = new Message { Text = "/start", Chat = new Chat { Id = 123 } }; var update = new Update { Message = message }; - _botClientMock.Setup(b => b.SendMessage( - It.IsAny(), - "Started", - It.IsAny(), - It.IsAny(), - It.IsAny(), - It.IsAny(), - It.IsAny(), - It.IsAny>(), - It.IsAny(), - It.IsAny(), - It.IsAny(), - It.IsAny(), - It.IsAny(), - It.IsAny() - )).Verifiable(); - // Act await _hostedService.StartAsync(CancellationToken.None); await InvokeUpdateHandler(update); @@ -169,15 +159,6 @@ namespace Telegram.Tests var callback = new CallbackQuery { Data = "test_callback", Id = "cb_id" }; var update = new Update { CallbackQuery = callback }; - _botClientMock.Setup(b => b.AnswerCallbackQueryAsync( - "cb_id", - "Callback handled", - It.IsAny(), - It.IsAny(), - It.IsAny(), - It.IsAny() - )).Verifiable(); - // Act await _hostedService.StartAsync(CancellationToken.None); await InvokeUpdateHandler(update); @@ -188,14 +169,7 @@ namespace Telegram.Tests private async Task InvokeUpdateHandler(Update update) { - var clientField = typeof(TelegramHostedService).GetField("Client", BindingFlags.NonPublic | BindingFlags.Instance); - clientField.SetValue(_hostedService, _botClientMock.Object); - - var updateHandler = _botClientMock.Invocations - .First(i => i.Method.Name == "StartReceiving") - .Arguments[0] as Func; - - await updateHandler(_botClientMock.Object, update, CancellationToken.None); + await _hostedService.UpdateHandler(_botClientMock.Object, update, CancellationToken.None); } } } \ No newline at end of file