Skip to content

Commit

Permalink
Webhook support (#179)
Browse files Browse the repository at this point in the history
* feat: internal update processing redesign

* feat: initial webhook implementation

* refactor: IUpdateSource -> IUpdateTarget, move queue to instance

also return option to turn webhooks on/off in config

* Properly stop

* fix: wait until update thread stops & code cleanup

* feat(webhook): secret token support

---------

Co-authored-by: Alexander <[email protected]>
  • Loading branch information
West14 and AleXr64 committed Jun 16, 2024
1 parent abb2b87 commit f20f8b9
Show file tree
Hide file tree
Showing 21 changed files with 452 additions and 117 deletions.
4 changes: 2 additions & 2 deletions TGBotFramework/BotFramework.Tests/BotFramework.Tests.csproj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
Expand All @@ -7,7 +7,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
Expand Down
17 changes: 17 additions & 0 deletions TGBotFramework/BotFramework.Webhook/BotFramework.Webhook.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<Nullable>enable</Nullable>
<LangVersion>latestMajor</LangVersion>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Watson" Version="6.0.2" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\BotFramework\BotFramework.csproj" />
</ItemGroup>

</Project>
18 changes: 18 additions & 0 deletions TGBotFramework/BotFramework.Webhook/ServiceCollectionExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using BotFramework.Abstractions.UpdateProvider;
using Microsoft.Extensions.DependencyInjection;

namespace BotFramework.Webhook;

public static class ServiceCollectionExtensions
{
public static void AddTelegramBotWebhookUpdateProvider(this IServiceCollection collection)
{
collection.AddSingleton<IWebhookProvider, WebhookUpdateProvider>();
}

public static void AddTelegramBotWebhook(this IServiceCollection collection)
{
collection.AddTelegramBotWebhookUpdateProvider();
collection.AddTelegramBot();
}
}
85 changes: 85 additions & 0 deletions TGBotFramework/BotFramework.Webhook/WebhookUpdateProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
using System;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using BotFramework.Abstractions.UpdateProvider;
using BotFramework.Config;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
using Telegram.Bot;
using Telegram.Bot.Types;
using WatsonWebserver.Core;

namespace BotFramework.Webhook;

public class WebhookUpdateProvider : IWebhookProvider
{
private const string SecretTokenHeader = "X-Telegram-Bot-Api-Secret-Token";

private readonly ITelegramBotClient _client;
private readonly IUpdateTarget _updateTarget;
private readonly BotConfig _config;

private readonly string? _secretToken;

private readonly CancellationTokenSource _canRunTokenSource = new ();

public WebhookUpdateProvider(ITelegramBotClient client, IUpdateTarget updateTarget, IOptions<BotConfig> config)
{
_client = client;
_updateTarget = updateTarget;
_config = config.Value;

_secretToken = _config.Webhook.SecretToken;
if (string.IsNullOrWhiteSpace(_secretToken))
{
_secretToken = null;
}
}

public async Task StartAsync(CancellationToken token)
{
token.ThrowIfCancellationRequested();
var server = new WatsonWebserver.Webserver(GetWebserverSettings(), Route);
_ = server.StartAsync(_canRunTokenSource.Token);

await _client.SetWebhookAsync(_config.Webhook.Url, secretToken: _secretToken, cancellationToken: token);
}

public async Task StopAsync(CancellationToken token)
{
_canRunTokenSource.Cancel();
await _client.DeleteWebhookAsync(cancellationToken: token);
}

private async Task Route(HttpContextBase ctx)
{
var request = ctx.Request;
if (_secretToken != null && request.RetrieveHeaderValue(SecretTokenHeader) != _secretToken)
{
ctx.Response.StatusCode = (int)HttpStatusCode.Forbidden;

await ctx.Response.Send("Missing or invalid secret_token");
return;
}

try
{
var obj = JsonConvert.DeserializeObject<Update>(request.DataAsString);
if (obj != null)
{
_updateTarget.Push(obj);
}
} catch(Exception e)
{
Console.WriteLine(e);
}

await ctx.Response.Send();
}

private WebserverSettings GetWebserverSettings()
{
return new WebserverSettings(_config.Webhook.Host, _config.Webhook.Port);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using System.Threading;
using System.Threading.Tasks;

namespace BotFramework.Abstractions.UpdateProvider;

public interface IUpdateProvider
{
Task StartAsync(CancellationToken token);
Task StopAsync(CancellationToken token);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using Telegram.Bot.Types;

namespace BotFramework.Abstractions.UpdateProvider;

public interface IUpdateTarget
{
public void Push(Update update);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace BotFramework.Abstractions.UpdateProvider;

public interface IWebhookProvider: IUpdateProvider
{

}
Loading

0 comments on commit f20f8b9

Please sign in to comment.