diff --git a/Console.Advanced/Console.Advanced.csproj b/Console.Advanced/Console.Advanced.csproj
index 5a0e1334..33ee6dde 100644
--- a/Console.Advanced/Console.Advanced.csproj
+++ b/Console.Advanced/Console.Advanced.csproj
@@ -14,8 +14,8 @@
-
- Always
+
+ PreserveNewest
diff --git a/Console.Advanced/Files/bot.gif b/Console.Advanced/Files/bot.gif
new file mode 100644
index 00000000..1dfad4e7
Binary files /dev/null and b/Console.Advanced/Files/bot.gif differ
diff --git a/Console.Advanced/Files/tux.png b/Console.Advanced/Files/tux.png
deleted file mode 100644
index efc8f175..00000000
Binary files a/Console.Advanced/Files/tux.png and /dev/null differ
diff --git a/Console.Advanced/Services/UpdateHandler.cs b/Console.Advanced/Services/UpdateHandler.cs
index 3e415435..b6691cc4 100644
--- a/Console.Advanced/Services/UpdateHandler.cs
+++ b/Console.Advanced/Services/UpdateHandler.cs
@@ -90,7 +90,7 @@ async Task SendPhoto(Message msg)
{
await _bot.SendChatActionAsync(msg.Chat, ChatAction.UploadPhoto);
await Task.Delay(2000); // simulate a long task
- await using var fileStream = new FileStream("Files/tux.png", FileMode.Open, FileAccess.Read);
+ await using var fileStream = new FileStream("Files/bot.gif", FileMode.Open, FileAccess.Read);
return await _bot.SendPhotoAsync(msg.Chat, fileStream, caption: "Read https://telegrambots.github.io/book/");
}
diff --git a/Console.Advanced/appsettings.json b/Console.Advanced/appsettings.json
index 6203dc3d..fbc2f844 100644
--- a/Console.Advanced/appsettings.json
+++ b/Console.Advanced/appsettings.json
@@ -6,6 +6,6 @@
}
},
"BotConfiguration": {
- "BotToken": "{BOT_TOKEN}"
+ "BotToken": "YOUR_BOT_TOKEN",
}
}
diff --git a/FSharp.Example/FSharp.Example.fsproj b/FSharp.Example/FSharp.Example.fsproj
index d6a4e249..b4495081 100644
--- a/FSharp.Example/FSharp.Example.fsproj
+++ b/FSharp.Example/FSharp.Example.fsproj
@@ -13,8 +13,8 @@
-
- Always
+
+ PreserveNewest
diff --git a/FSharp.Example/Files/bot.gif b/FSharp.Example/Files/bot.gif
new file mode 100644
index 00000000..1dfad4e7
Binary files /dev/null and b/FSharp.Example/Files/bot.gif differ
diff --git a/FSharp.Example/Files/tux.png b/FSharp.Example/Files/tux.png
deleted file mode 100644
index efc8f175..00000000
Binary files a/FSharp.Example/Files/tux.png and /dev/null differ
diff --git a/FSharp.Example/Services/Internal/UpdateHandlerFuncs.fs b/FSharp.Example/Services/Internal/UpdateHandlerFuncs.fs
index 2fd0b762..74c79147 100644
--- a/FSharp.Example/Services/Internal/UpdateHandlerFuncs.fs
+++ b/FSharp.Example/Services/Internal/UpdateHandlerFuncs.fs
@@ -86,7 +86,7 @@ module UpdateHandlerFuncs =
cancellationToken = cts)
|> Async.AwaitTask |> ignore
- let filePath = @"Files/tux.png"
+ let filePath = @"Files/bot.gif"
let fileName =
filePath.Split(Path.DirectorySeparatorChar)
|> Array.last
diff --git a/Serverless/AwsLambda.Webhook/lambda-bot/Properties/launchSettings.json b/Serverless/AwsLambda.Webhook/lambda-bot/Properties/launchSettings.json
new file mode 100644
index 00000000..60631d12
--- /dev/null
+++ b/Serverless/AwsLambda.Webhook/lambda-bot/Properties/launchSettings.json
@@ -0,0 +1,10 @@
+{
+ "profiles": {
+ "Mock Lambda Test Tool": {
+ "commandName": "Executable",
+ "commandLineArgs": "--port 5050",
+ "workingDirectory": ".\\bin\\$(Configuration)\\netcoreapp3.1",
+ "executablePath": "C:\\Users\\%USERNAME%\\.dotnet\\tools\\dotnet-lambda-test-tool-3.1.exe"
+ }
+ }
+}
\ No newline at end of file
diff --git a/Serverless/AzureFunctions.IsolatedProcess.Webhook/Properties/launchSettings.json b/Serverless/AzureFunctions.IsolatedProcess.Webhook/Properties/launchSettings.json
new file mode 100644
index 00000000..ab5547aa
--- /dev/null
+++ b/Serverless/AzureFunctions.IsolatedProcess.Webhook/Properties/launchSettings.json
@@ -0,0 +1,9 @@
+{
+ "profiles": {
+ "AzureFunctions_IsolatedProcess_Webhook": {
+ "commandName": "Project",
+ "commandLineArgs": "--port 7071",
+ "launchBrowser": false
+ }
+ }
+}
diff --git a/Webhook.Controllers/BotConfiguration.cs b/Webhook.Controllers/BotConfiguration.cs
new file mode 100644
index 00000000..8ecb54a0
--- /dev/null
+++ b/Webhook.Controllers/BotConfiguration.cs
@@ -0,0 +1,6 @@
+public class BotConfiguration
+{
+ public string BotToken { get; init; } = default!;
+ public Uri BotWebhookUrl { get; init; } = default!;
+ public string SecretToken { get; init; } = default!;
+}
diff --git a/Webhook.Controllers/Controllers/BotController.cs b/Webhook.Controllers/Controllers/BotController.cs
index 01f5dc3e..96ccea10 100644
--- a/Webhook.Controllers/Controllers/BotController.cs
+++ b/Webhook.Controllers/Controllers/BotController.cs
@@ -1,26 +1,36 @@
using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.Options;
+using Telegram.Bot;
using Telegram.Bot.Filters;
-using Telegram.Bot.Services;
using Telegram.Bot.Types;
+using Webhook.Controllers.Services;
-namespace Telegram.Bot.Controllers;
+namespace Webhook.Controllers.Controllers;
-public class BotController : ControllerBase
+[ApiController]
+[Route("[controller]")]
+public class BotController(IOptions Config) : ControllerBase
{
+ [HttpGet("setWebhook")]
+ public async Task SetWebHook([FromServices] TelegramBotClient bot, CancellationToken ct)
+ {
+ var webhookUrl = Config.Value.BotWebhookUrl.AbsoluteUri;
+ await bot.SetWebhookAsync(webhookUrl, allowedUpdates: [], secretToken: Config.Value.SecretToken, cancellationToken: ct);
+ return $"Webhook set to {webhookUrl}";
+ }
+
[HttpPost]
- [ValidateTelegramBot]
- public async Task Post(
- [FromBody] Update update,
- [FromServices] UpdateHandler handleUpdateService,
- CancellationToken cancellationToken)
+ public async Task Post([FromBody] Update update, [FromServices] UpdateHandler handleUpdateService, CancellationToken ct)
{
+ if (Request.Headers["X-Telegram-Bot-Api-Secret-Token"] != Config.Value.SecretToken)
+ return Forbid();
try
{
- await handleUpdateService.HandleUpdateAsync(update, cancellationToken);
+ await handleUpdateService.HandleUpdateAsync(update, ct);
}
catch (Exception exception)
{
- await handleUpdateService.HandleErrorAsync(exception, Polling.HandleErrorSource.HandleUpdateError, cancellationToken);
+ await handleUpdateService.HandleErrorAsync(exception, ct);
}
return Ok();
}
diff --git a/Webhook.Controllers/Extensions.cs b/Webhook.Controllers/Extensions.cs
deleted file mode 100644
index 100eda15..00000000
--- a/Webhook.Controllers/Extensions.cs
+++ /dev/null
@@ -1,31 +0,0 @@
-using Microsoft.Extensions.Options;
-
-#pragma warning disable CA1050 // Declare types in namespaces
-#pragma warning disable RCS1110 // Declare types in namespaces
-public static class WebhookExtensions
-#pragma warning restore RCS1110 // Declare types in namespaces
-#pragma warning restore CA1050 // Declare types in namespaces
-{
- public static T GetConfiguration(this IServiceProvider serviceProvider)
- where T : class
- {
- var o = serviceProvider.GetService>();
- if (o is null)
- throw new ArgumentNullException(nameof(T));
-
- return o.Value;
- }
-
- public static ControllerActionEndpointConventionBuilder MapBotWebhookRoute(
- this IEndpointRouteBuilder endpoints,
- string route)
- {
- var controllerName = typeof(T).Name.Replace("Controller", "", StringComparison.Ordinal);
- var actionName = typeof(T).GetMethods()[0].Name;
-
- return endpoints.MapControllerRoute(
- name: "bot_webhook",
- pattern: route,
- defaults: new { controller = controllerName, action = actionName });
- }
-}
diff --git a/Webhook.Controllers/Files/bot.gif b/Webhook.Controllers/Files/bot.gif
new file mode 100644
index 00000000..1dfad4e7
Binary files /dev/null and b/Webhook.Controllers/Files/bot.gif differ
diff --git a/Webhook.Controllers/Files/tux.png b/Webhook.Controllers/Files/tux.png
deleted file mode 100644
index efc8f175..00000000
Binary files a/Webhook.Controllers/Files/tux.png and /dev/null differ
diff --git a/Webhook.Controllers/Filters/ValidateTelegramBotRequestAttribute.cs b/Webhook.Controllers/Filters/ValidateTelegramBotRequestAttribute.cs
deleted file mode 100644
index c337b95a..00000000
--- a/Webhook.Controllers/Filters/ValidateTelegramBotRequestAttribute.cs
+++ /dev/null
@@ -1,52 +0,0 @@
-using Microsoft.AspNetCore.Mvc;
-using Microsoft.AspNetCore.Mvc.Filters;
-using Microsoft.Extensions.Options;
-
-namespace Telegram.Bot.Filters;
-
-///
-/// Check for "X-Telegram-Bot-Api-Secret-Token"
-/// Read more: "secret_token"
-///
-[AttributeUsage(AttributeTargets.Method)]
-public sealed class ValidateTelegramBotAttribute : TypeFilterAttribute
-{
- public ValidateTelegramBotAttribute()
- : base(typeof(ValidateTelegramBotFilter))
- {
- }
-
- private class ValidateTelegramBotFilter : IActionFilter
- {
- private readonly string _secretToken;
-
- public ValidateTelegramBotFilter(IOptions options)
- {
- var botConfiguration = options.Value;
- _secretToken = botConfiguration.SecretToken;
- }
-
- public void OnActionExecuted(ActionExecutedContext context)
- {
- }
-
- public void OnActionExecuting(ActionExecutingContext context)
- {
- if (!IsValidRequest(context.HttpContext.Request))
- {
- context.Result = new ObjectResult("\"X-Telegram-Bot-Api-Secret-Token\" is invalid")
- {
- StatusCode = 403
- };
- }
- }
-
- private bool IsValidRequest(HttpRequest request)
- {
- var isSecretTokenProvided = request.Headers.TryGetValue("X-Telegram-Bot-Api-Secret-Token", out var secretTokenHeader);
- if (!isSecretTokenProvided) return false;
-
- return string.Equals(secretTokenHeader, _secretToken, StringComparison.Ordinal);
- }
- }
-}
diff --git a/Webhook.Controllers/Program.cs b/Webhook.Controllers/Program.cs
index f1fd7203..5c5744ce 100644
--- a/Webhook.Controllers/Program.cs
+++ b/Webhook.Controllers/Program.cs
@@ -1,59 +1,25 @@
using Telegram.Bot;
-using Telegram.Bot.Controllers;
-using Telegram.Bot.Services;
var builder = WebApplication.CreateBuilder(args);
-// Setup Bot configuration
-var botConfigurationSection = builder.Configuration.GetSection(BotConfiguration.Configuration);
-builder.Services.Configure(botConfigurationSection);
-
-var botConfiguration = botConfigurationSection.Get();
-
-// Register named HttpClient to get benefits of IHttpClientFactory
-// and consume it with ITelegramBotClient typed client.
-// More read:
-// https://docs.microsoft.com/en-us/aspnet/core/fundamentals/http-requests#typed-clients
-// https://docs.microsoft.com/en-us/dotnet/architecture/microservices/implement-resilient-applications/use-httpclientfactory-to-implement-resilient-http-requests
-builder.Services.AddHttpClient("telegram_bot_client")
- .AddTypedClient((httpClient, sp) =>
- {
- BotConfiguration? botConfig = sp.GetConfiguration();
- TelegramBotClientOptions options = new(botConfig.BotToken);
- return new TelegramBotClient(options, httpClient);
- });
-
-// Dummy business-logic service
-builder.Services.AddScoped();
-
-// There are several strategies for completing asynchronous tasks during startup.
-// Some of them could be found in this article https://andrewlock.net/running-async-tasks-on-app-startup-in-asp-net-core-part-1/
-// We are going to use IHostedService to add and later remove Webhook
-builder.Services.AddHostedService();
+// Setup bot configuration
+var botConfigSection = builder.Configuration.GetSection("BotConfiguration");
+builder.Services.Configure(botConfigSection);
+builder.Services.AddHttpClient("tgwebhook").RemoveAllLoggers().AddTypedClient(
+ httpClient => new TelegramBotClient(botConfigSection.Get()!.BotToken, httpClient));
+builder.Services.AddScoped();
+builder.Services.ConfigureTelegramBotMvc();
builder.Services.AddControllers();
-// The Telegram.Bot library heavily depends on System.Text.Json library with special Json
-// settings to deserialize incoming webhook updates and send serialized responses back.
-builder.Services.ConfigureTelegramBotMvc();
-
var app = builder.Build();
-// Construct webhook route from the Route configuration parameter
-// It is expected that BotController has single method accepting Update
-app.MapBotWebhookRoute(route: botConfiguration.Route);
+
+// Configure the HTTP request pipeline.
+
+app.UseHttpsRedirection();
+
+app.UseAuthorization();
+
app.MapControllers();
-app.Run();
-#pragma warning disable CA1050 // Declare types in namespaces
-#pragma warning disable RCS1110 // Declare type inside namespace.
-public class BotConfiguration
-#pragma warning restore RCS1110 // Declare type inside namespace.
-#pragma warning restore CA1050 // Declare types in namespaces
-{
- public static readonly string Configuration = "BotConfiguration";
-
- public string BotToken { get; init; } = default!;
- public string HostAddress { get; init; } = default!;
- public string Route { get; init; } = default!;
- public string SecretToken { get; init; } = default!;
-}
+app.Run();
diff --git a/Webhook.Controllers/Properties/launchSettings.json b/Webhook.Controllers/Properties/launchSettings.json
new file mode 100644
index 00000000..454bd278
--- /dev/null
+++ b/Webhook.Controllers/Properties/launchSettings.json
@@ -0,0 +1,41 @@
+{
+ "$schema": "http://json.schemastore.org/launchsettings.json",
+ "iisSettings": {
+ "windowsAuthentication": false,
+ "anonymousAuthentication": true,
+ "iisExpress": {
+ "applicationUrl": "http://localhost:5131",
+ "sslPort": 44315
+ }
+ },
+ "profiles": {
+ "http": {
+ "commandName": "Project",
+ "dotnetRunMessages": true,
+ "launchBrowser": true,
+ "launchUrl": "bot/setWebhook",
+ "applicationUrl": "http://localhost:5227",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ },
+ "https": {
+ "commandName": "Project",
+ "dotnetRunMessages": true,
+ "launchBrowser": true,
+ "launchUrl": "bot/setWebhook",
+ "applicationUrl": "https://localhost:7057;http://localhost:5227",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ },
+ "IIS Express": {
+ "commandName": "IISExpress",
+ "launchBrowser": true,
+ "launchUrl": "bot/setWebhook",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ }
+ }
+}
diff --git a/Webhook.Controllers/Services/ConfigureWebhook.cs b/Webhook.Controllers/Services/ConfigureWebhook.cs
deleted file mode 100644
index f9fffca5..00000000
--- a/Webhook.Controllers/Services/ConfigureWebhook.cs
+++ /dev/null
@@ -1,54 +0,0 @@
-using Microsoft.Extensions.Options;
-using Telegram.Bot.Types.Enums;
-
-namespace Telegram.Bot.Services;
-
-public class ConfigureWebhook : IHostedService
-{
- private readonly ILogger _logger;
- private readonly IServiceProvider _serviceProvider;
- private readonly BotConfiguration _botConfig;
-
- public ConfigureWebhook(
- ILogger logger,
- IServiceProvider serviceProvider,
- IOptions botOptions)
- {
- _logger = logger;
- _serviceProvider = serviceProvider;
- _botConfig = botOptions.Value;
- }
-
- public async Task StartAsync(CancellationToken cancellationToken)
- {
- using var scope = _serviceProvider.CreateScope();
- var botClient = scope.ServiceProvider.GetRequiredService();
-
- // Configure custom endpoint per Telegram API recommendations:
- // https://core.telegram.org/bots/api#setwebhook
- // If you'd like to make sure that the webhook was set by you, you can specify secret data
- // in the parameter secret_token. If specified, the request will contain a header
- // "X-Telegram-Bot-Api-Secret-Token" with the secret token as content.
- var webhookAddress = $"{_botConfig.HostAddress}{_botConfig.Route}";
- _logger.LogInformation("Setting webhook: {WebhookAddress}", webhookAddress);
- await botClient.SetWebhookAsync(
- url: webhookAddress,
- allowedUpdates: Array.Empty(),
- secretToken: _botConfig.SecretToken,
- cancellationToken: cancellationToken);
- }
-
- public async Task StopAsync(CancellationToken cancellationToken)
- {
- /* You shouldn't delete webhook on app stop (typically when recycled by your host provider)
- otherwise your app will not be restarted by the next incoming Update
-
- using var scope = _serviceProvider.CreateScope();
- var botClient = scope.ServiceProvider.GetRequiredService();
-
- // Remove webhook on app shutdown
- _logger.LogInformation("Removing webhook");
- await botClient.DeleteWebhookAsync(cancellationToken: cancellationToken);
- */
- }
-}
diff --git a/Webhook.Controllers/Services/UpdateHandler.cs b/Webhook.Controllers/Services/UpdateHandler.cs
index 32add61d..ff4791de 100644
--- a/Webhook.Controllers/Services/UpdateHandler.cs
+++ b/Webhook.Controllers/Services/UpdateHandler.cs
@@ -1,25 +1,25 @@
+using Telegram.Bot;
using Telegram.Bot.Exceptions;
-using Telegram.Bot.Polling;
using Telegram.Bot.Types;
using Telegram.Bot.Types.Enums;
using Telegram.Bot.Types.InlineQueryResults;
using Telegram.Bot.Types.ReplyMarkups;
-namespace Telegram.Bot.Services;
+namespace Webhook.Controllers.Services;
public class UpdateHandler
{
- private readonly ITelegramBotClient _bot;
+ private readonly TelegramBotClient _bot;
private readonly ILogger _logger;
private static readonly InputPollOption[] PollOptions = ["Hello", "World!"];
- public UpdateHandler(ITelegramBotClient bot, ILogger logger)
+ public UpdateHandler(TelegramBotClient bot, ILogger logger)
{
_bot = bot;
_logger = logger;
}
- public async Task HandleErrorAsync(Exception exception, HandleErrorSource source, CancellationToken cancellationToken)
+ public async Task HandleErrorAsync(Exception exception, CancellationToken cancellationToken)
{
_logger.LogInformation("HandleError: {exception}", exception);
// Cooldown in case of network connection error
@@ -90,7 +90,7 @@ async Task SendPhoto(Message msg)
{
await _bot.SendChatActionAsync(msg.Chat, ChatAction.UploadPhoto);
await Task.Delay(2000); // simulate a long task
- await using var fileStream = new FileStream("Files/tux.png", FileMode.Open, FileAccess.Read);
+ await using var fileStream = new FileStream("Files/bot.gif", FileMode.Open, FileAccess.Read);
return await _bot.SendPhotoAsync(msg.Chat, fileStream, caption: "Read https://telegrambots.github.io/book/");
}
diff --git a/Webhook.Controllers/Webhook.Controllers.csproj b/Webhook.Controllers/Webhook.Controllers.csproj
index d05c1249..eff7b23c 100644
--- a/Webhook.Controllers/Webhook.Controllers.csproj
+++ b/Webhook.Controllers/Webhook.Controllers.csproj
@@ -11,12 +11,8 @@
-
-
-
-
-
- Always
+
+ PreserveNewest
diff --git a/Webhook.Controllers/appsettings.Development.json b/Webhook.Controllers/appsettings.Development.json
index 8c9190bd..0c208ae9 100644
--- a/Webhook.Controllers/appsettings.Development.json
+++ b/Webhook.Controllers/appsettings.Development.json
@@ -1,16 +1,8 @@
{
"Logging": {
- "IncludeScopes": false,
"LogLevel": {
- "Default": "Debug",
- "System": "Information",
- "Microsoft": "Information"
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
}
- },
- "BotConfiguration": {
- "BotToken": "{BOT_TOKEN}",
- "HostAddress": "https://mydomain.com",
- "Route": "/bot",
- "SecretToken": "SOME-SECRET-STRING"
}
}
diff --git a/Webhook.Controllers/appsettings.json b/Webhook.Controllers/appsettings.json
index af3ff015..55bd7658 100644
--- a/Webhook.Controllers/appsettings.json
+++ b/Webhook.Controllers/appsettings.json
@@ -1,14 +1,14 @@
{
"Logging": {
- "IncludeScopes": false,
"LogLevel": {
- "Default": "Warning"
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
}
},
+ "AllowedHosts": "*",
"BotConfiguration": {
- "BotToken": "{BOT_TOKEN}",
- "HostAddress": "https://mydomain.com",
- "Route": "/bot",
+ "BotToken": "YOUR_BOT_TOKEN",
+ "BotWebhookUrl": "https://your.public.host/bot",
"SecretToken": "SOME-SECRET-STRING"
}
}
diff --git a/Webhook.MinimalAPIs/Program.cs b/Webhook.MinimalAPIs/Program.cs
index 591b0552..decf9834 100644
--- a/Webhook.MinimalAPIs/Program.cs
+++ b/Webhook.MinimalAPIs/Program.cs
@@ -10,7 +10,7 @@
var app = builder.Build();
app.UseHttpsRedirection();
-app.MapGet("/setWebhook", async (TelegramBotClient bot) => { await bot.SetWebhookAsync(webhookUrl); return $"Webhook set to {webhookUrl}"; });
+app.MapGet("/bot/setWebhook", async (TelegramBotClient bot) => { await bot.SetWebhookAsync(webhookUrl); return $"Webhook set to {webhookUrl}"; });
app.MapPost("/bot", OnUpdate);
app.Run();
diff --git a/Webhook.MinimalAPIs/Properties/launchSettings.json b/Webhook.MinimalAPIs/Properties/launchSettings.json
index 0226867f..d3c9414c 100644
--- a/Webhook.MinimalAPIs/Properties/launchSettings.json
+++ b/Webhook.MinimalAPIs/Properties/launchSettings.json
@@ -13,7 +13,7 @@
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
- "launchUrl": "setWebhook",
+ "launchUrl": "bot/setWebhook",
"applicationUrl": "http://localhost:5225",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
@@ -23,7 +23,7 @@
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
- "launchUrl": "setWebhook",
+ "launchUrl": "bot/setWebhook",
"applicationUrl": "https://localhost:7262;http://localhost:5225",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
@@ -32,7 +32,7 @@
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
- "launchUrl": "setWebhook",
+ "launchUrl": "bot/setWebhook",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}