diff --git a/Examples/DependencyInjection/Program.cs b/Examples/DependencyInjection/Program.cs
index f33bf53..1e32385 100644
--- a/Examples/DependencyInjection/Program.cs
+++ b/Examples/DependencyInjection/Program.cs
@@ -25,6 +25,7 @@ namespace DependencyInjection
.NoCommands()
.NoSerialization()
.DefaultLanguage()
+ .UseSingleThread()
.Build();
await bot.Start();
diff --git a/Examples/EFCoreBot/Program.cs b/Examples/EFCoreBot/Program.cs
index 37f5587..e49a1c1 100644
--- a/Examples/EFCoreBot/Program.cs
+++ b/Examples/EFCoreBot/Program.cs
@@ -18,6 +18,7 @@ var bot = BotBaseBuilder.Create()
.NoCommands()
.NoSerialization()
.DefaultLanguage()
+ .UseSingleThread()
.Build();
await bot.Start();
diff --git a/Examples/InlineAndReplyCombination/Program.cs b/Examples/InlineAndReplyCombination/Program.cs
index fab456d..7354ec7 100644
--- a/Examples/InlineAndReplyCombination/Program.cs
+++ b/Examples/InlineAndReplyCombination/Program.cs
@@ -21,6 +21,7 @@ namespace InlineAndReplyCombination
.DefaultCommands()
.UseJSON(Path.Combine(Directory.GetCurrentDirectory(), "states.json"))
.UseEnglish()
+ .UseSingleThread()
.Build();
diff --git a/README.md b/README.md
index b1f7706..903a138 100644
--- a/README.md
+++ b/README.md
@@ -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.
+
+[](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/](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.
diff --git a/TelegramBotBase.Test/Program.cs b/TelegramBotBase.Test/Program.cs
index 9276cbd..a9236a1 100644
--- a/TelegramBotBase.Test/Program.cs
+++ b/TelegramBotBase.Test/Program.cs
@@ -32,6 +32,7 @@ internal class Program
})
.NoSerialization()
.UseEnglish()
+ .UseThreadPool()
.Build();
diff --git a/TelegramBotBase.Test/TelegramBotBase.Example.csproj b/TelegramBotBase.Test/TelegramBotBase.Example.csproj
index 3e534ad..3affa0a 100644
--- a/TelegramBotBase.Test/TelegramBotBase.Example.csproj
+++ b/TelegramBotBase.Test/TelegramBotBase.Example.csproj
@@ -8,7 +8,10 @@
-
+
+
+
+
diff --git a/TelegramBotBase/Base/MessageClient.cs b/TelegramBotBase/Base/MessageClient.cs
index 10a724e..5d82f8d 100644
--- a/TelegramBotBase/Base/MessageClient.cs
+++ b/TelegramBotBase/Base/MessageClient.cs
@@ -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;
///
@@ -17,16 +19,18 @@ namespace TelegramBotBase.Base;
///
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;
///
/// 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();
}
///
@@ -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"
///
/// This will return the current list of bot commands.
///
@@ -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)?.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)?.Invoke(this, update);
@@ -225,4 +213,5 @@ public class MessageClient
}
#endregion
+
}
diff --git a/TelegramBotBase/Base/ThreadPoolMessageClient.cs b/TelegramBotBase/Base/ThreadPoolMessageClient.cs
new file mode 100644
index 0000000..41fb131
--- /dev/null
+++ b/TelegramBotBase/Base/ThreadPoolMessageClient.cs
@@ -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;
+
+///
+/// Base class for message handling
+///
+public class ThreadPoolMessageClient : MessageClient
+{
+
+ ///
+ /// 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.
+ ///
+
+ 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)
+ {
+
+ }
+
+ ///
+ /// Initializes the client with a proxy
+ ///
+ ///
+ /// i.e. 127.0.0.1
+ /// i.e. 10000
+ 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;
+ }
+
+}
diff --git a/TelegramBotBase/Builder/BotBaseBuilder.cs b/TelegramBotBase/Builder/BotBaseBuilder.cs
index 39bb317..64596b9 100644
--- a/TelegramBotBase/Builder/BotBaseBuilder.cs
+++ b/TelegramBotBase/Builder/BotBaseBuilder.cs
@@ -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()
{
+
}
///
@@ -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
+
}
diff --git a/TelegramBotBase/Builder/Interfaces/INetworkingSelectionStage.cs b/TelegramBotBase/Builder/Interfaces/INetworkingSelectionStage.cs
index cd17e9d..8ab58d4 100644
--- a/TelegramBotBase/Builder/Interfaces/INetworkingSelectionStage.cs
+++ b/TelegramBotBase/Builder/Interfaces/INetworkingSelectionStage.cs
@@ -11,14 +11,14 @@ public interface INetworkingSelectionStage
///
/// Indicates if all pending Telegram.Bot.Types.Updates should be thrown out before start polling.
///
- IBotCommandsStage WithProxy(string proxyAddress, bool throwPendingUpdates = false);
+ IBotCommandsStage WithProxy(string proxyAddress, bool throwPendingUpdates = false, int timeoutInSeconds = 60);
///
/// Do not choose a proxy as network configuration.
///
/// Indicates if all pending Telegram.Bot.Types.Updates should be thrown out before start polling.
///
- IBotCommandsStage NoProxy(bool throwPendingUpdates = false);
+ IBotCommandsStage NoProxy(bool throwPendingUpdates = false, int timeoutInSeconds = 60);
///
@@ -27,7 +27,7 @@ public interface INetworkingSelectionStage
///
/// Indicates if all pending Telegram.Bot.Types.Updates should be thrown out before start polling.
///
- IBotCommandsStage WithBotClient(TelegramBotClient client, bool throwPendingUpdates = false);
+ IBotCommandsStage WithBotClient(TelegramBotClient client, bool throwPendingUpdates = false, int timeoutInSeconds = 60);
///
@@ -37,7 +37,7 @@ public interface INetworkingSelectionStage
///
/// Indicates if all pending Telegram.Bot.Types.Updates should be thrown out before start polling.
///
- IBotCommandsStage WithHostAndPort(string proxyHost, int Port, bool throwPendingUpdates = false);
+ IBotCommandsStage WithHostAndPort(string proxyHost, int Port, bool throwPendingUpdates = false, int timeoutInSeconds = 60);
///
/// Uses a custom http client.
@@ -45,5 +45,5 @@ public interface INetworkingSelectionStage
///
/// Indicates if all pending Telegram.Bot.Types.Updates should be thrown out before start polling.
///
- IBotCommandsStage WithHttpClient(HttpClient client, bool throwPendingUpdates = false);
+ IBotCommandsStage WithHttpClient(HttpClient client, bool throwPendingUpdates = false, int timeoutInSeconds = 60);
}
\ No newline at end of file
diff --git a/TelegramBotBase/Builder/Interfaces/IThreadingStage.cs b/TelegramBotBase/Builder/Interfaces/IThreadingStage.cs
new file mode 100644
index 0000000..f53c087
--- /dev/null
+++ b/TelegramBotBase/Builder/Interfaces/IThreadingStage.cs
@@ -0,0 +1,24 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace TelegramBotBase.Builder.Interfaces
+{
+ public interface IThreadingStage
+ {
+ ///
+ /// Uses one single thread for message loop. (Default)
+ ///
+ ///
+ public IBuildingStage UseSingleThread();
+
+ ///
+ /// Using the threadpool for managing requests.
+ ///
+ /// Number of concurrent working threads.
+ /// Number of concurrent I/O threads.
+ ///
+ public IBuildingStage UseThreadPool(int workerThreads = 2, int ioThreads = 1);
+
+ }
+}