Merge pull request #63 from MajMcCloud/development

Integrating latest developments into master branch
This commit is contained in:
Florian Zevedei 2024-03-10 15:51:29 +01:00 committed by GitHub
commit 2db8e2cf63
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 228 additions and 50 deletions

View File

@ -25,6 +25,7 @@ namespace DependencyInjection
.NoCommands()
.NoSerialization()
.DefaultLanguage()
.UseSingleThread()
.Build();
await bot.Start();

View File

@ -18,6 +18,7 @@ var bot = BotBaseBuilder.Create()
.NoCommands()
.NoSerialization()
.DefaultLanguage()
.UseSingleThread()
.Build();
await bot.Start();

View File

@ -21,6 +21,7 @@ namespace InlineAndReplyCombination
.DefaultCommands()
.UseJSON(Path.Combine(Directory.GetCurrentDirectory(), "states.json"))
.UseEnglish()
.UseSingleThread()
.Build();

View File

@ -1074,6 +1074,21 @@ Nuget package: [https://www.nuget.org/packages/TelegramBotBase.Extensions.IronSo
---
Project: [open source](TelegramBotBase.Extensions.Images/)
### TelegramBotBase.Extensions.Images.IronSoftware
Extends the base package with some additional image methods like SendPhoto (using Bitmap)
Important: This extension uses the IronSoftware drawing library.
[![NuGet version (TelegramBotBase)](https://img.shields.io/nuget/v/TelegramBotBase.Extensions.Images.IronSoftware.svg?style=flat-square)](https://www.nuget.org/packages/TelegramBotBase.Extensions.Images.IronSoftware/)
[![Downloads](https://img.shields.io/nuget/dt/TelegramBotBase.Extensions.Images.IronSoftware.svg?style=flat-square&label=Package%20Downloads)](https://www.nuget.org/packages/TelegramBotBase.Extensions.Images.IronSoftware)
[https://www.nuget.org/packages/TelegramBotBase.Extensions.Images.IronSoftware/](https://www.nuget.org/packages/TelegramBotBase.Extensions.Images.IronSoftware/)
Project: [open source](TelegramBotBase.Extensions.Images.IronSoftware/)
### TelegramBotBase.Extensions.Serializer.Database.MSSQL
A session serializer for Microsoft SQL Server.
@ -1102,6 +1117,8 @@ Credits: [@Kataane](https://github.com/Kataane)
---
Project: [open source](TelegramBotBase.Extensions.Serializer.Database.MSSQL/)
## Test Project
There is a "TelegramBotBase.Test" project inside the repository which includes minimal examples for all controls available.

View File

@ -32,6 +32,7 @@ internal class Program
})
.NoSerialization()
.UseEnglish()
.UseThreadPool()
.Build();

View File

@ -8,7 +8,10 @@
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\TelegramBotBase.Extensions.Images\TelegramBotBase.Extensions.Images.csproj" />
<PackageReference Include="TelegramBotBase.Extensions.Images" Version="1.1.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\TelegramBotBase\TelegramBotBase.csproj" />
</ItemGroup>

View File

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.ComponentModel;
using System.Net;
using System.Net.Http;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using Telegram.Bot;
@ -10,6 +11,7 @@ using Telegram.Bot.Exceptions;
using Telegram.Bot.Polling;
using Telegram.Bot.Types;
namespace TelegramBotBase.Base;
/// <summary>
@ -17,16 +19,18 @@ namespace TelegramBotBase.Base;
/// </summary>
public class MessageClient
{
private EventHandlerList Events { get; } = new();
private static readonly object EvOnMessageLoop = new();
private static readonly object EvOnReceiveError = new();
private static object __evOnMessage = new();
protected CancellationTokenSource _cancellationTokenSource;
private static object __evOnMessageEdit = new();
public string ApiKey { get; }
public ITelegramBotClient TelegramClient { get; set; }
private static object __evCallbackQuery = new();
private CancellationTokenSource _cancellationTokenSource;
/// <summary>
/// Indicates if all pending Telegram.Bot.Types.Updates should be thrown out before
@ -41,16 +45,12 @@ public class MessageClient
{
ApiKey = apiKey;
TelegramClient = new TelegramBotClient(apiKey);
Prepare();
}
public MessageClient(string apiKey, HttpClient proxy)
{
ApiKey = apiKey;
TelegramClient = new TelegramBotClient(apiKey, proxy);
Prepare();
}
@ -68,8 +68,6 @@ public class MessageClient
);
TelegramClient = new TelegramBotClient(apiKey, httpClient);
Prepare();
}
/// <summary>
@ -89,8 +87,6 @@ public class MessageClient
);
TelegramClient = new TelegramBotClient(apiKey, httpClient);
Prepare();
}
@ -98,25 +94,10 @@ public class MessageClient
{
ApiKey = apiKey;
TelegramClient = client;
Prepare();
}
public string ApiKey { get; }
public ITelegramBotClient TelegramClient { get; set; }
private EventHandlerList Events { get; } = new();
public void Prepare()
{
TelegramClient.Timeout = new TimeSpan(0, 0, 30);
}
public void StartReceiving()
public virtual void StartReceiving()
{
_cancellationTokenSource = new CancellationTokenSource();
@ -124,27 +105,30 @@ public class MessageClient
receiverOptions.ThrowPendingUpdates = ThrowPendingUpdates;
TelegramClient.StartReceiving(HandleUpdateAsync, HandleErrorAsync, receiverOptions,
_cancellationTokenSource.Token);
TelegramClient.StartReceiving(HandleUpdateAsync, HandleErrorAsync, receiverOptions, _cancellationTokenSource.Token);
}
public void StopReceiving()
public virtual void StopReceiving()
{
_cancellationTokenSource.Cancel();
}
public async Task HandleUpdateAsync(ITelegramBotClient botClient, Update update, CancellationToken cancellationToken)
private async Task HandleUpdateAsync(ITelegramBotClient botClient, Update update, CancellationToken cancellationToken)
{
await OnMessageLoop(new UpdateResult(update, null));
}
public async Task HandleErrorAsync(ITelegramBotClient botClient, Exception exception,
private async Task HandleErrorAsync(ITelegramBotClient botClient, Exception exception,
CancellationToken cancellationToken)
{
await OnReceiveError(new ErrorResult(exception));
}
#region "BotCommands"
/// <summary>
/// This will return the current list of bot commands.
/// </summary>
@ -176,6 +160,8 @@ public class MessageClient
await TelegramClient.DeleteMyCommandsAsync(scope, languageCode);
}
#endregion
#region "Events"
@ -185,6 +171,7 @@ public class MessageClient
remove => Events.RemoveHandler(EvOnMessageLoop, value);
}
public async Task OnMessageLoop(UpdateResult update)
{
var eventHandlers = (Events[EvOnMessageLoop] as Async.AsyncEventHandler<UpdateResult>)?.Invoke(this, update);
@ -202,6 +189,7 @@ public class MessageClient
remove => Events.RemoveHandler(EvOnReceiveError, value);
}
public async Task OnReceiveError(ErrorResult update)
{
var eventHandlers = (Events[EvOnReceiveError] as Async.AsyncEventHandler<ErrorResult>)?.Invoke(this, update);
@ -225,4 +213,5 @@ public class MessageClient
}
#endregion
}

View File

@ -0,0 +1,109 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Telegram.Bot;
using Telegram.Bot.Exceptions;
using Telegram.Bot.Polling;
using Telegram.Bot.Types;
using TelegramBotBase.Interfaces;
namespace TelegramBotBase.Base;
/// <summary>
/// Base class for message handling
/// </summary>
public class ThreadPoolMessageClient : MessageClient
{
/// <summary>
/// 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.
/// </summary>
public int ThreadPool_WorkerThreads { get; set; } = 1;
public int ThreadPool_IOThreads { get; set; } = 1;
public ThreadPoolMessageClient(string apiKey) : base(apiKey)
{
}
public ThreadPoolMessageClient(string apiKey, HttpClient proxy) : base(apiKey, proxy)
{
}
public ThreadPoolMessageClient(string apiKey, Uri proxyUrl, NetworkCredential credential = null) : base(apiKey, proxyUrl, credential)
{
}
/// <summary>
/// Initializes the client with a proxy
/// </summary>
/// <param name="apiKey"></param>
/// <param name="proxyHost">i.e. 127.0.0.1</param>
/// <param name="proxyPort">i.e. 10000</param>
public ThreadPoolMessageClient(string apiKey, string proxyHost, int proxyPort) : base(apiKey, proxyHost, proxyPort)
{
}
public ThreadPoolMessageClient(string apiKey, TelegramBotClient client) : base(apiKey, client)
{
}
public override void StartReceiving()
{
_cancellationTokenSource = new CancellationTokenSource();
var receiverOptions = new ReceiverOptions();
receiverOptions.ThrowPendingUpdates = ThrowPendingUpdates;
ThreadPool.SetMaxThreads(ThreadPool_WorkerThreads, ThreadPool_IOThreads);
TelegramClient.StartReceiving(HandleUpdateAsyncThreadPool, HandleErrorAsyncThreadPool, receiverOptions, _cancellationTokenSource.Token);
}
public override void StopReceiving()
{
_cancellationTokenSource.Cancel();
}
public Task HandleUpdateAsyncThreadPool(ITelegramBotClient botClient, Update update, CancellationToken cancellationToken)
{
ThreadPool.QueueUserWorkItem(async a =>
{
await OnMessageLoop(new UpdateResult(update, null));
});
return Task.CompletedTask;
}
public Task HandleErrorAsyncThreadPool(ITelegramBotClient botClient, Exception exception,
CancellationToken cancellationToken)
{
ThreadPool.QueueUserWorkItem(async a =>
{
await OnReceiveError(new ErrorResult(exception));
});
return Task.CompletedTask;
}
}

View File

@ -18,7 +18,7 @@ namespace TelegramBotBase.Builder;
public class BotBaseBuilder : IAPIKeySelectionStage, IMessageLoopSelectionStage, IStartFormSelectionStage,
IBuildingStage, INetworkingSelectionStage, IBotCommandsStage, ISessionSerializationStage,
ILanguageSelectionStage
ILanguageSelectionStage, IThreadingStage
{
private string _apiKey;
@ -32,6 +32,7 @@ public class BotBaseBuilder : IAPIKeySelectionStage, IMessageLoopSelectionStage,
private BotBaseBuilder()
{
}
/// <summary>
@ -87,6 +88,8 @@ public class BotBaseBuilder : IAPIKeySelectionStage, IMessageLoopSelectionStage,
DefaultLanguage();
UseSingleThread();
return this;
}
@ -107,6 +110,8 @@ public class BotBaseBuilder : IAPIKeySelectionStage, IMessageLoopSelectionStage,
DefaultLanguage();
UseSingleThread();
return this;
}
@ -125,6 +130,8 @@ public class BotBaseBuilder : IAPIKeySelectionStage, IMessageLoopSelectionStage,
DefaultLanguage();
UseSingleThread();
return this;
}
@ -207,14 +214,14 @@ public class BotBaseBuilder : IAPIKeySelectionStage, IMessageLoopSelectionStage,
#region "Step 4 (Network Settings)"
public IBotCommandsStage WithProxy(string proxyAddress, bool throwPendingUpdates = false)
public IBotCommandsStage WithProxy(string proxyAddress, bool throwPendingUpdates = false, int timeoutInSeconds = 60)
{
var url = new Uri(proxyAddress);
_client = new MessageClient(_apiKey, url)
{
TelegramClient =
{
Timeout = new TimeSpan(0, 1, 0)
Timeout = TimeSpan.FromSeconds(timeoutInSeconds)
},
};
_client.ThrowPendingUpdates = throwPendingUpdates;
@ -222,13 +229,13 @@ public class BotBaseBuilder : IAPIKeySelectionStage, IMessageLoopSelectionStage,
}
public IBotCommandsStage NoProxy(bool throwPendingUpdates = false)
public IBotCommandsStage NoProxy(bool throwPendingUpdates = false, int timeoutInSeconds = 60)
{
_client = new MessageClient(_apiKey)
{
TelegramClient =
{
Timeout = new TimeSpan(0, 1, 0)
Timeout = TimeSpan.FromSeconds(timeoutInSeconds)// new TimeSpan(0, 1, 0)
}
};
_client.ThrowPendingUpdates = throwPendingUpdates;
@ -236,13 +243,13 @@ public class BotBaseBuilder : IAPIKeySelectionStage, IMessageLoopSelectionStage,
}
public IBotCommandsStage WithBotClient(TelegramBotClient tgclient, bool throwPendingUpdates = false)
public IBotCommandsStage WithBotClient(TelegramBotClient tgclient, bool throwPendingUpdates = false, int timeoutInSeconds = 60)
{
_client = new MessageClient(_apiKey, tgclient)
{
TelegramClient =
{
Timeout = new TimeSpan(0, 1, 0)
Timeout = TimeSpan.FromSeconds(timeoutInSeconds)// new TimeSpan(0, 1, 0)
}
};
_client.ThrowPendingUpdates = throwPendingUpdates;
@ -250,26 +257,26 @@ public class BotBaseBuilder : IAPIKeySelectionStage, IMessageLoopSelectionStage,
}
public IBotCommandsStage WithHostAndPort(string proxyHost, int proxyPort, bool throwPendingUpdates = false)
public IBotCommandsStage WithHostAndPort(string proxyHost, int proxyPort, bool throwPendingUpdates = false, int timeoutInSeconds = 60)
{
_client = new MessageClient(_apiKey, proxyHost, proxyPort)
{
TelegramClient =
{
Timeout = new TimeSpan(0, 1, 0)
Timeout = TimeSpan.FromSeconds(timeoutInSeconds)// new TimeSpan(0, 1, 0)
}
};
_client.ThrowPendingUpdates = throwPendingUpdates;
return this;
}
public IBotCommandsStage WithHttpClient(HttpClient tgclient, bool throwPendingUpdates = false)
public IBotCommandsStage WithHttpClient(HttpClient tgclient, bool throwPendingUpdates = false, int timeoutInSeconds = 60)
{
_client = new MessageClient(_apiKey, tgclient)
{
TelegramClient =
{
Timeout = new TimeSpan(0, 1, 0)
Timeout = TimeSpan.FromSeconds(timeoutInSeconds)// new TimeSpan(0, 1, 0)
}
};
_client.ThrowPendingUpdates = throwPendingUpdates;
@ -432,5 +439,30 @@ public class BotBaseBuilder : IAPIKeySelectionStage, IMessageLoopSelectionStage,
return this;
}
#endregion
#region "Step 8 (Threading)"
public IBuildingStage UseSingleThread()
{
return this;
}
public IBuildingStage UseThreadPool(int workerThreads = 2, int ioThreads = 1)
{
var c = new ThreadPoolMessageClient(_apiKey, (TelegramBotClient)_client.TelegramClient);
c.ThreadPool_WorkerThreads = workerThreads;
c.ThreadPool_IOThreads = ioThreads;
c.ThrowPendingUpdates = _client.ThrowPendingUpdates;
_client = c;
return this;
}
#endregion
}

View File

@ -11,14 +11,14 @@ public interface INetworkingSelectionStage
/// <param name="proxyAddress"></param>
/// <param name="throwPendingUpdates">Indicates if all pending Telegram.Bot.Types.Updates should be thrown out before start polling.</param>
/// <returns></returns>
IBotCommandsStage WithProxy(string proxyAddress, bool throwPendingUpdates = false);
IBotCommandsStage WithProxy(string proxyAddress, bool throwPendingUpdates = false, int timeoutInSeconds = 60);
/// <summary>
/// Do not choose a proxy as network configuration.
/// </summary>
/// <param name="throwPendingUpdates">Indicates if all pending Telegram.Bot.Types.Updates should be thrown out before start polling.</param>
/// <returns></returns>
IBotCommandsStage NoProxy(bool throwPendingUpdates = false);
IBotCommandsStage NoProxy(bool throwPendingUpdates = false, int timeoutInSeconds = 60);
/// <summary>
@ -27,7 +27,7 @@ public interface INetworkingSelectionStage
/// <param name="client"></param>
/// <param name="throwPendingUpdates">Indicates if all pending Telegram.Bot.Types.Updates should be thrown out before start polling.</param>
/// <returns></returns>
IBotCommandsStage WithBotClient(TelegramBotClient client, bool throwPendingUpdates = false);
IBotCommandsStage WithBotClient(TelegramBotClient client, bool throwPendingUpdates = false, int timeoutInSeconds = 60);
/// <summary>
@ -37,7 +37,7 @@ public interface INetworkingSelectionStage
/// <param name="Port"></param>
/// <param name="throwPendingUpdates">Indicates if all pending Telegram.Bot.Types.Updates should be thrown out before start polling.</param>
/// <returns></returns>
IBotCommandsStage WithHostAndPort(string proxyHost, int Port, bool throwPendingUpdates = false);
IBotCommandsStage WithHostAndPort(string proxyHost, int Port, bool throwPendingUpdates = false, int timeoutInSeconds = 60);
/// <summary>
/// Uses a custom http client.
@ -45,5 +45,5 @@ public interface INetworkingSelectionStage
/// <param name="client"></param>
/// <param name="throwPendingUpdates">Indicates if all pending Telegram.Bot.Types.Updates should be thrown out before start polling.</param>
/// <returns></returns>
IBotCommandsStage WithHttpClient(HttpClient client, bool throwPendingUpdates = false);
IBotCommandsStage WithHttpClient(HttpClient client, bool throwPendingUpdates = false, int timeoutInSeconds = 60);
}

View File

@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace TelegramBotBase.Builder.Interfaces
{
public interface IThreadingStage
{
/// <summary>
/// Uses one single thread for message loop. (Default)
/// </summary>
/// <returns></returns>
public IBuildingStage UseSingleThread();
/// <summary>
/// Using the threadpool for managing requests.
/// </summary>
/// <param name="workerThreads">Number of concurrent working threads.</param>
/// <param name="ioThreads">Number of concurrent I/O threads.</param>
/// <returns></returns>
public IBuildingStage UseThreadPool(int workerThreads = 2, int ioThreads = 1);
}
}