diff --git a/Directory.Packages.props b/Directory.Packages.props index 9e51085..1c1c502 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -1,6 +1,8 @@ net8.0 + enable + enable 8.0.0 0.16.0 @@ -15,5 +17,6 @@ + diff --git a/Nocr.TelegramClient.sln b/Nocr.TelegramClient.sln index 351c1f4..e9e6909 100644 --- a/Nocr.TelegramClient.sln +++ b/Nocr.TelegramClient.sln @@ -8,9 +8,29 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution README.md = README.md EndProjectSection EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nocr.TelegramClient.Core", "src\Nocr.TelegramClient.Core\Nocr.TelegramClient.Core.csproj", "{3E87693C-78DF-469B-BAA3-CB103F8EA80B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nocr.TelegramClient.AppServices", "src\Nocr.TelegramClient.AppServices\Nocr.TelegramClient.AppServices.csproj", "{5CCB085C-860A-4C4C-907C-10183ABCEA9B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nocr.TelegramClient.Host", "src\Nocr.TelegramClient.Host\Nocr.TelegramClient.Host.csproj", "{58D5C9FD-75A9-4FFB-9FBD-BE8E9FCE3016}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {3E87693C-78DF-469B-BAA3-CB103F8EA80B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3E87693C-78DF-469B-BAA3-CB103F8EA80B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3E87693C-78DF-469B-BAA3-CB103F8EA80B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3E87693C-78DF-469B-BAA3-CB103F8EA80B}.Release|Any CPU.Build.0 = Release|Any CPU + {5CCB085C-860A-4C4C-907C-10183ABCEA9B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5CCB085C-860A-4C4C-907C-10183ABCEA9B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5CCB085C-860A-4C4C-907C-10183ABCEA9B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5CCB085C-860A-4C4C-907C-10183ABCEA9B}.Release|Any CPU.Build.0 = Release|Any CPU + {58D5C9FD-75A9-4FFB-9FBD-BE8E9FCE3016}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {58D5C9FD-75A9-4FFB-9FBD-BE8E9FCE3016}.Debug|Any CPU.Build.0 = Debug|Any CPU + {58D5C9FD-75A9-4FFB-9FBD-BE8E9FCE3016}.Release|Any CPU.ActiveCfg = Release|Any CPU + {58D5C9FD-75A9-4FFB-9FBD-BE8E9FCE3016}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection EndGlobal diff --git a/src/Nocr.TelegramClient.AppServices/BotClient.cs b/src/Nocr.TelegramClient.AppServices/BotClient.cs new file mode 100644 index 0000000..224b900 --- /dev/null +++ b/src/Nocr.TelegramClient.AppServices/BotClient.cs @@ -0,0 +1,31 @@ +using Insight.TelegramBot; +using Insight.TelegramBot.Models; +using Microsoft.Extensions.Logging; +using Telegram.Bot; +using Telegram.Bot.Types; + +namespace Nocr.TelegramClient.AppServices; + +public sealed class BotClient : Bot +{ + private readonly ILogger _logger; + + public BotClient(ILogger logger, ITelegramBotClient client) : base(client) + { + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + } + + public override Task SendMessageAsync(TextMessage message, + CancellationToken cancellationToken = default) + { + try + { + return base.SendMessageAsync(message, cancellationToken); + } + catch (Exception ex) + { + _logger.LogError(ex, "Bot can't initiate conversation with a user: {message}", message); + throw; + } + } +} \ No newline at end of file diff --git a/src/Nocr.TelegramClient.AppServices/Handlers/Messages/StartMessageHandler.cs b/src/Nocr.TelegramClient.AppServices/Handlers/Messages/StartMessageHandler.cs new file mode 100644 index 0000000..7c6c4b7 --- /dev/null +++ b/src/Nocr.TelegramClient.AppServices/Handlers/Messages/StartMessageHandler.cs @@ -0,0 +1,28 @@ +using Insight.TelegramBot; +using Insight.TelegramBot.Handling.Handlers; +using Insight.TelegramBot.Models; +using Telegram.Bot.Types; +using Telegram.Bot.Types.Enums; + +namespace Nocr.TelegramClient.AppServices.Handlers.Messages; + +public class StartMessageHandler : IMatchingUpdateHandler +{ + private readonly IBot _bot; + + public StartMessageHandler(IBot bot) + { + _bot = bot ?? throw new ArgumentNullException(nameof(bot)); + } + + public Task Handle(Update update, CancellationToken cancellationToken = default) + { + var message = new TextMessage(update.Message.Chat) + { + Text = "Привет! Я _bot_name_", + ParseMode = ParseMode.Html + }; + + return _bot.SendMessageAsync(message, cancellationToken); + } +} \ No newline at end of file diff --git a/src/Nocr.TelegramClient.AppServices/Handlers/Messages/StartMessageMatcher.cs b/src/Nocr.TelegramClient.AppServices/Handlers/Messages/StartMessageMatcher.cs new file mode 100644 index 0000000..da696d1 --- /dev/null +++ b/src/Nocr.TelegramClient.AppServices/Handlers/Messages/StartMessageMatcher.cs @@ -0,0 +1,11 @@ +using Insight.TelegramBot.Handling.Matchers.TextMatchers; + +namespace Nocr.TelegramClient.AppServices.Handlers.Messages; + +public sealed class StartMessageMatcher : TextStartWithUpdateMatcher +{ + public StartMessageMatcher() + { + Template = "/start"; + } +} \ No newline at end of file diff --git a/src/Nocr.TelegramClient.AppServices/Nocr.TelegramClient.AppServices.csproj b/src/Nocr.TelegramClient.AppServices/Nocr.TelegramClient.AppServices.csproj new file mode 100644 index 0000000..cc9779f --- /dev/null +++ b/src/Nocr.TelegramClient.AppServices/Nocr.TelegramClient.AppServices.csproj @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/Nocr.TelegramClient.Core/Dates/DefaultCurrentDateProvider.cs b/src/Nocr.TelegramClient.Core/Dates/DefaultCurrentDateProvider.cs new file mode 100644 index 0000000..03ea00b --- /dev/null +++ b/src/Nocr.TelegramClient.Core/Dates/DefaultCurrentDateProvider.cs @@ -0,0 +1,6 @@ +namespace Nocr.TelegramClient.Core.Dates; + +public sealed class DefaultCurrentDateProvider : ICurrentDateProvider +{ + public DateTimeOffset UtcNow => DateTimeOffset.UtcNow; +} \ No newline at end of file diff --git a/src/Nocr.TelegramClient.Core/Dates/ICurrentDateProvider.cs b/src/Nocr.TelegramClient.Core/Dates/ICurrentDateProvider.cs new file mode 100644 index 0000000..ffeee21 --- /dev/null +++ b/src/Nocr.TelegramClient.Core/Dates/ICurrentDateProvider.cs @@ -0,0 +1,6 @@ +namespace Nocr.TelegramClient.Core.Dates; + +public interface ICurrentDateProvider +{ + public DateTimeOffset UtcNow { get; } +} \ No newline at end of file diff --git a/src/Nocr.TelegramClient.Core/Nocr.TelegramClient.Core.csproj b/src/Nocr.TelegramClient.Core/Nocr.TelegramClient.Core.csproj new file mode 100644 index 0000000..2ef1a36 --- /dev/null +++ b/src/Nocr.TelegramClient.Core/Nocr.TelegramClient.Core.csproj @@ -0,0 +1 @@ + diff --git a/src/Nocr.TelegramClient.Host/Infrastructure/HostBuilderFactory.cs b/src/Nocr.TelegramClient.Host/Infrastructure/HostBuilderFactory.cs new file mode 100644 index 0000000..a7fa601 --- /dev/null +++ b/src/Nocr.TelegramClient.Host/Infrastructure/HostBuilderFactory.cs @@ -0,0 +1,24 @@ +using Serilog; + +namespace Nocr.TelegramClient.Host.Infrastructure; + +public class HostBuilderFactory where TStartup : class +{ + public IHostBuilder CreateHostBuilder(string[] args, string? baseDirectory = null) + { + var builder = Microsoft.Extensions.Hosting.Host.CreateDefaultBuilder(args) + .ConfigureAppConfiguration((_, configurationBuilder) => + { + if (!string.IsNullOrWhiteSpace(baseDirectory)) + configurationBuilder.SetBasePath(baseDirectory); + }) + .ConfigureWebHostDefaults(host => { host.UseStartup(); }) + .UseSerilog((ctx, logBuilder) => + { + logBuilder.ReadFrom.Configuration(ctx.Configuration) + .Enrich.FromLogContext(); + }); + + return builder; + } +} \ No newline at end of file diff --git a/src/Nocr.TelegramClient.Host/Infrastructure/Startup.cs b/src/Nocr.TelegramClient.Host/Infrastructure/Startup.cs new file mode 100644 index 0000000..fa4ee6c --- /dev/null +++ b/src/Nocr.TelegramClient.Host/Infrastructure/Startup.cs @@ -0,0 +1,37 @@ +using System.Data; +using Insight.TelegramBot.Handling.Infrastructure; +using Insight.TelegramBot.Hosting.DependencyInjection.Infrastructure; +using Insight.TelegramBot.Hosting.Polling.ExceptionHandlers; +using Microsoft.Extensions.Options; +using Nocr.TelegramClient.AppServices; +using Nocr.TelegramClient.Core.Dates; + +namespace Nocr.TelegramClient.Host.Infrastructure; + +public class Startup +{ + public IConfiguration Configuration { get; } + + public Startup(IConfiguration configuration) + { + Configuration = configuration; + } + + public void ConfigureServices(IServiceCollection services) + { + services.AddSingleton(); + + services.AddTelegramBot(bot => + bot.WithBot(ServiceLifetime.Transient) + .WithTelegramBotClient(client => client + .WithLifetime(ServiceLifetime.Singleton) + .WithMicrosoftHttpClientFactory()) + .WithOptions(opt => opt.FromConfiguration(Configuration)) + .WithPolling(polling => polling.WithExceptionHandler())); + services.AddTelegramBotHandling(typeof(BotClient).Assembly); + } + + public void Configure(IApplicationBuilder app) + { + } +} \ No newline at end of file diff --git a/src/Nocr.TelegramClient.Host/Nocr.TelegramClient.Host.csproj b/src/Nocr.TelegramClient.Host/Nocr.TelegramClient.Host.csproj new file mode 100644 index 0000000..2819e51 --- /dev/null +++ b/src/Nocr.TelegramClient.Host/Nocr.TelegramClient.Host.csproj @@ -0,0 +1,17 @@ + + + + Linux + + + + + + + + + + + + + diff --git a/src/Nocr.TelegramClient.Host/Program.cs b/src/Nocr.TelegramClient.Host/Program.cs new file mode 100644 index 0000000..702ac32 --- /dev/null +++ b/src/Nocr.TelegramClient.Host/Program.cs @@ -0,0 +1,7 @@ +using Nocr.TelegramClient.Host.Infrastructure; + +var host = new HostBuilderFactory() + .CreateHostBuilder(args) + .Build(); + +await host.RunAsync(); \ No newline at end of file diff --git a/src/Nocr.TelegramClient.Host/appsettings.Development.json b/src/Nocr.TelegramClient.Host/appsettings.Development.json new file mode 100644 index 0000000..f924f4e --- /dev/null +++ b/src/Nocr.TelegramClient.Host/appsettings.Development.json @@ -0,0 +1,20 @@ +{ + "Serilog": { + "WriteTo": [ + { + "Name": "Console", + "Args": { + "outputTemplate": "[{Level:u3}] {Timestamp:MM-dd HH:mm:ss} {TraceId} {SourceContext:l} {Message:lj}{NewLine}{Exception}" + } + } + ] + }, + "TelegramBotOptions": { + "WebHook": { + "UseWebHook": false, + "WebHookBaseUrl": "http://localhost", + "WebHookPath": "update/TokenPartAfterDoubleDot" + }, + "Token": "" + } +} diff --git a/src/Nocr.TelegramClient.Host/appsettings.Production.json b/src/Nocr.TelegramClient.Host/appsettings.Production.json new file mode 100644 index 0000000..93dc056 --- /dev/null +++ b/src/Nocr.TelegramClient.Host/appsettings.Production.json @@ -0,0 +1,25 @@ +{ + "Serilog": { + "MinimumLevel": { + "Default": "Information" + }, + "WriteTo": [ + { + "Name": "Console", + "Args": { + "outputTemplate": "[{Level:u3}] {Timestamp:MM-dd HH:mm:ss} {TraceId} {SourceContext:l} {Message:lj}{NewLine}{Exception}" + } + }, + { + "Name": "File", + "Args": { + "path": "/var/log/nocr/telegram-client/nocr-telegram-client-.log", + "outputTemplate": "[{Level:u3}] {Timestamp:dd-MM-yyyy HH:mm:ss} {TraceId} {SourceContext:l} {Message:lj}{NewLine}{Exception}", + "fileSizeLimitBytes": 104857600, + "rollingInterval": "Day", + "rollOnFileSizeLimit": true + } + } + ] + } +} diff --git a/src/Nocr.TelegramClient.Host/appsettings.json b/src/Nocr.TelegramClient.Host/appsettings.json new file mode 100644 index 0000000..ff9ad20 --- /dev/null +++ b/src/Nocr.TelegramClient.Host/appsettings.json @@ -0,0 +1,12 @@ +{ + "Serilog": { + "MinimumLevel": { + "Default": "Debug", + "Override": { + "Microsoft": "Information", + "Microsoft.AspNetCore": "Error", + "System.Net.Http.HttpClient": "Warning" + } + } + } +}