Compare commits

...
71 Commits
Author SHA1 Message Date
Florian Zevedei 396a524d94 BotBaseBuilder fix 2024-03-10 16:01:45 +01:00
Florian Zevedei 2db8e2cf63 Merge pull request #63 from MajMcCloud/development
Integrating latest developments into master branch
2024-03-10 15:51:29 +01:00
Florian Zevedei 6b7f2ca273 Merge branch 'master' into development 2024-03-10 15:50:42 +01:00
Florian Zevedei 31d0d9890d Merge pull request #61 from ramax495/RuLocalization
Fix typo
2024-01-31 14:15:44 +01:00
ramax495 b252829296 Fix typo 2024-01-31 10:01:13 +03:00
Florian Zevedei a483bc7c0b Update Russian.cs 2024-01-30 21:57:05 +01:00
Florian Zevedei 4635a96fc7 Removing old comments 2024-01-30 19:24:13 +01:00
Florian Zevedei 727375f7aa Localization fixes 2024-01-30 19:15:11 +01:00
Florian Zevedei 86792af214 Fixing incorrect behaviour with tags 2024-01-30 18:27:48 +01:00
Florian Zevedei b3edb95b55 Adding new total tag and checked tag count 2024-01-30 18:27:05 +01:00
Florian Zevedei 946420f9c6 Fixing InlineKeyboard use
- tags cant be change when using InlineKeyboard mode
2024-01-30 18:26:40 +01:00
Florian Zevedei c8c56a42c0 Bugfix for TaggedButtonGrid 2024-01-30 18:24:31 +01:00
Florian Zevedei 6f930eee8c Adding new constructor and additonal ToRowList method to ButtonForm 2024-01-30 18:23:29 +01:00
Florian Zevedei c4a589d476 Renaming TaggedButtonGrid specific localizations 2024-01-30 18:23:02 +01:00
Florian Zevedei 1b14b7b3cf Adding missing localization to german file 2024-01-30 18:21:25 +01:00
Florian Zevedei cb5fa35269 Fix #60 2024-01-30 16:07:08 +01:00
Florian Zevedei f60e1000c3 Changing datatype of port from string to int 2024-01-30 15:08:40 +01:00
Florian Zevedei 00dafd59a3 Update README.md 2024-01-30 14:47:52 +01:00
Florian Zevedei 848b5d29cf Add missing Russian localization due to PR #58 2024-01-30 14:47:23 +01:00
Florian Zevedei 9cac57b8e7 Update README.md 2024-01-28 19:32:26 +01:00
Florian Zevedei 52517534f8 Update README.md 2024-01-28 19:30:17 +01:00
Florian Zevedei 5e166db243 Adding readme note for IronSoftware extension 2024-01-28 19:25:59 +01:00
Florian Zevedei 71aa0c3d91 Adding nuget references and nuget specs 2024-01-28 19:24:32 +01:00
Florian Zevedei e34c71b42b Update README.md 2024-01-28 19:23:50 +01:00
Florian Zevedei 9927a034b0 First release of IronSoftware drawing extension 2024-01-28 19:22:30 +01:00
Florian Zevedei 2e454d7f5c Removing SQLClient dependency 2024-01-28 19:17:15 +01:00
Florian Zevedei 339319ef2a Update README.md 2024-01-28 18:50:27 +01:00
Florian Zevedei c8b12e9168 Adding credits 2024-01-28 18:49:52 +01:00
Florian Zevedei cbc31b8ff5 adding source path to Readme 2024-01-28 18:48:31 +01:00
Florian Zevedei c514ede03f Adding PostgreSQL extension to Readme 2024-01-28 18:45:17 +01:00
Florian Zevedei c91fbaa16c Downgrade target framework to.NET 6 2024-01-28 18:39:56 +01:00
Florian Zevedei b8125e9aed Merge pull request #58 from Kataane/add-postgresql-serializer
Add PostgreSQL serializer
2024-01-28 18:34:15 +01:00
Florian Zevedei d4af8797fb Add new PostgreSQL extension to solution file 2024-01-28 02:16:55 +01:00
Florian Zevedei 93fda551e1 Merge pull request #57 from Kataane/master
Russian translation added
2024-01-28 02:04:13 +01:00
Kataane b14913362e Update README.md 2024-01-27 13:21:46 +07:00
Kataanee 8b22fd7b74 Fast fix 2024-01-27 13:16:27 +07:00
Kataanee 254c002d4a Add Russian in ILanguageSelectionStage 2024-01-27 13:14:15 +07:00
Kataanee a632440efd Add PostgreSQL serializer 2024-01-26 18:48:43 +07:00
Kataanee 194d8ba317 Revert "Russian translation added"
This reverts commit d833b1e088.
2024-01-26 18:46:49 +07:00
Kataanee d833b1e088 Russian translation added 2024-01-26 12:42:41 +07:00
Florian Zevedei 084395e962 Bugfix, adding missing DeviceSession to UpdateResult 2024-01-24 22:15:16 +01:00
Florian Zevedei b20b3b2826 Bugfix, adding missing DeviceSession to UpdateResult 2024-01-24 22:12:26 +01:00
Florian Zevedei c1d1d5f543 Fix, add missing ThrowPendingUpdates 2024-01-24 17:48:25 +01:00
Florian Zevedei 078bdfb163 Making cancellationTokenSource protected 2024-01-24 17:43:44 +01:00
Florian Zevedei 94c680e036 Timeout fixes
- Fixing wrong timeout setter
- removing timeout setter in "Prepare" method in client which overrides setting
2024-01-24 17:39:18 +01:00
Florian Zevedei dbdc40582a Splitting MessageClient into 2 instances 2024-01-24 03:04:05 +01:00
Florian Zevedei 2d3393aa05 Update README.md 2024-01-17 22:55:11 +01:00
Florian Zevedei fdd814c88e Adding project reference links 2024-01-17 22:54:30 +01:00
Florian Zevedei 2b06fba8ff Adding readme note for IronSoftware extension 2024-01-17 22:51:40 +01:00
Florian Zevedei 8e4bc7a222 Bumps Microsoft.Data.SqlClient from 5.0.0 to 5.1.3. 2024-01-17 22:42:03 +01:00
Florian Zevedei cc4c743721 Merge pull request #56 from MajMcCloud/dependabot/nuget/TelegramBotBase.Extensions.Serializer.Database.MSSQL/Microsoft.Data.SqlClient-5.1.3
Bump Microsoft.Data.SqlClient from 5.0.0 to 5.1.3 in /TelegramBotBase.Extensions.Serializer.Database.MSSQL
2024-01-17 22:41:15 +01:00
dependabot[bot] 4e55fd42d6 Bump Microsoft.Data.SqlClient
Bumps [Microsoft.Data.SqlClient](https://github.com/dotnet/sqlclient) from 5.0.0 to 5.1.3.
- [Release notes](https://github.com/dotnet/sqlclient/releases)
- [Changelog](https://github.com/dotnet/SqlClient/blob/main/CHANGELOG.md)
- [Commits](https://github.com/dotnet/sqlclient/compare/v5.0.0...v5.1.3)

---
updated-dependencies:
- dependency-name: Microsoft.Data.SqlClient
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-16 19:28:47 +00:00
Florian Zevedei deef8dd086 Replacing project references with nuget reference 2024-01-04 14:17:09 +01:00
Florian Zevedei 3aff0aa94b Updating example projects for use of SingleThread/ThreadPool 2024-01-04 14:16:52 +01:00
Florian Zevedei 75064fd64c Adding nuget references and nuget specs 2024-01-04 14:11:21 +01:00
Florian Zevedei 0cafca9aee Update README.md 2024-01-04 13:46:58 +01:00
Florian Zevedei ad40675b87 First release of IronSoftware drawing extension 2024-01-04 13:44:18 +01:00
Florian Zevedei 83e09aec2a Removing System.Drawing.Common dependency 2023-12-29 14:38:11 +01:00
Florian Zevedei 26152ee348 Removing System.Drawing.Common dependency 2023-12-29 14:37:19 +01:00
Florian Zevedei d5ffa914f0 Update README.md 2023-12-28 15:17:43 +01:00
Florian Zevedei a63f71268c Update README.md 2023-12-28 15:17:14 +01:00
Florian Zevedei 7fcaa407ad Adding parallel processing via ThreadPool 2023-12-26 17:40:13 +01:00
Florian Zevedei d56b26a788 Merge pull request #55 from MajMcCloud/development
Integrating development branch into master
2023-12-26 17:32:51 +01:00
Florian Zevedei c1018ac5a0 Adding ThrowPendingUpdates option to builder and Message client. 2023-12-09 14:56:02 +01:00
Florian Zevedei 064399ed77 Replace nuget reference with project for development 2023-12-09 14:54:23 +01:00
Florian Zevedei 121a788c17 Update README.md 2023-12-08 01:52:47 +01:00
Florian Zevedei 40199a1d81 Merge pull request #51 from contributeless/feature/ability-to-log-exceptions-to-any-destination
Added the ReceiveError event to MessageClient to provide the ability to handle exceptions in client code
2023-12-07 17:50:57 +01:00
Florian Zevedei 794dee7f58 improved readability 2023-12-07 17:49:35 +01:00
Florian Zevedei 88309e607b Adding localization notes to Readme file 2023-12-06 15:44:59 +01:00
Florian Zevedei a71eed5054 Merge pull request #54 from MajMcCloud/master
Syncing master and development branch
2023-12-06 15:36:53 +01:00
TCSBANK\e.khudik 89332244e3 Added the ReceiveError event to MessageClient to provide the ability to handle exceptions in client code 2023-12-04 15:10:28 +03:00
33 changed files with 1123 additions and 181 deletions
+1
View File
@@ -25,6 +25,7 @@ namespace DependencyInjection
.NoCommands() .NoCommands()
.NoSerialization() .NoSerialization()
.DefaultLanguage() .DefaultLanguage()
.UseSingleThread()
.Build(); .Build();
await bot.Start(); await bot.Start();
+1
View File
@@ -18,6 +18,7 @@ var bot = BotBaseBuilder.Create()
.NoCommands() .NoCommands()
.NoSerialization() .NoSerialization()
.DefaultLanguage() .DefaultLanguage()
.UseSingleThread()
.Build(); .Build();
await bot.Start(); await bot.Start();
@@ -21,6 +21,7 @@ namespace InlineAndReplyCombination
.DefaultCommands() .DefaultCommands()
.UseJSON(Path.Combine(Directory.GetCurrentDirectory(), "states.json")) .UseJSON(Path.Combine(Directory.GetCurrentDirectory(), "states.json"))
.UseEnglish() .UseEnglish()
.UseSingleThread()
.Build(); .Build();
+79 -5
View File
@@ -1,6 +1,6 @@
# .NET Telegram Bot Framework - Context based addon # .NET Telegram Bot Framework - Context based addon
[![NuGet version (TelegramBotBase)](https://img.shields.io/nuget/vpre/TelegramBotBase.svg?style=flat-square)](https://www.nuget.org/packages/TelegramBotBase/) [![NuGet version (TelegramBotBase)](https://img.shields.io/nuget/v/TelegramBotBase.svg?style=flat-square)](https://www.nuget.org/packages/TelegramBotBase/)
[![Telegram chat](https://img.shields.io/badge/Support_Chat-Telegram-blue.svg?style=flat-square)](https://www.t.me/tgbotbase) [![Telegram chat](https://img.shields.io/badge/Support_Chat-Telegram-blue.svg?style=flat-square)](https://www.t.me/tgbotbase)
[![License](https://img.shields.io/github/license/MajMcCloud/telegrambotframework.svg?style=flat-square&maxAge=2592000&label=License)](https://raw.githubusercontent.com/MajMcCloud/TelegramBotFramework/master/LICENCE.md) [![License](https://img.shields.io/github/license/MajMcCloud/telegrambotframework.svg?style=flat-square&maxAge=2592000&label=License)](https://raw.githubusercontent.com/MajMcCloud/TelegramBotFramework/master/LICENCE.md)
@@ -10,7 +10,7 @@
**Support group: [@tgbotbase](https://t.me/tgbotbase)** **Support group: [@tgbotbase](https://t.me/tgbotbase)**
**Discord Server: [https://discord.gg/p3PPhYbTf](https://discord.gg/p3PPhYbTf)** **Discord Server: [https://discord.gg/V3PxreDYfE](https://discord.gg/V3PxreDYfE)**
**Releases: [GitHub](https://github.com/MajMcCloud/TelegramBotFramework/releases)** **Releases: [GitHub](https://github.com/MajMcCloud/TelegramBotFramework/releases)**
@@ -59,6 +59,7 @@ BitTorrent: `TYVZSykaVT1nKZnz9hjDgBRNB9VavU1bpW`
* [TaggedButtonGrid](#tagged-button-grid) * [TaggedButtonGrid](#tagged-button-grid)
* [CheckedButtonList](#checked-button-list) * [CheckedButtonList](#checked-button-list)
* [MultiToggleButton](#multi-toggle-button) * [MultiToggleButton](#multi-toggle-button)
- [Localizations](#localizations)
- [Groups](#groups) - [Groups](#groups)
* [SplitterForm](#splitter-form) * [SplitterForm](#splitter-form)
* [GroupForm](#group-form) * [GroupForm](#group-form)
@@ -714,6 +715,20 @@ Check the example project [TelegramBotBase.Test/Tests/Controls/CheckedButtonList
Check the example project [TelegramBotBase.Test/Tests/Controls/MultiToggleButtonForm.cs](TelegramBotBase.Test/Tests/Controls/MultiToggleButtonForm.cs) Check the example project [TelegramBotBase.Test/Tests/Controls/MultiToggleButtonForm.cs](TelegramBotBase.Test/Tests/Controls/MultiToggleButtonForm.cs)
## Localizations
The current available languages for controls are:
- English
- German
- Persian
- Russian
You can add other languages easily by creating a subclass of the [TelegramBotBase/Localizations/Localization.cs](TelegramBotBase/Localizations/Localization.cs) class.
To set the default language set the *Language* property on the static [TelegramBotBase/Localizations/Default.cs](TelegramBotBase/Localizations/Default.cs) instance.
## Groups ## Groups
For groups, there are multiple different tools which help to work with and allows bot also to manage For groups, there are multiple different tools which help to work with and allows bot also to manage
@@ -1025,6 +1040,8 @@ again at 1 (due to `PopAsync` or `PopToRootAsync` calls) it will replace the con
you you
have given to the constructor at the beginning.* have given to the constructor at the beginning.*
---
## Extensions ## Extensions
### TelegramBotBase.Extensions.Images ### TelegramBotBase.Extensions.Images
@@ -1034,7 +1051,43 @@ Extends the base package with some additional image methods like SendPhoto (usin
[![NuGet version (TelegramBotBase)](https://img.shields.io/nuget/v/TelegramBotBase.Extensions.Images.svg?style=flat-square)](https://www.nuget.org/packages/TelegramBotBase.Extensions.Images/) [![NuGet version (TelegramBotBase)](https://img.shields.io/nuget/v/TelegramBotBase.Extensions.Images.svg?style=flat-square)](https://www.nuget.org/packages/TelegramBotBase.Extensions.Images/)
[![Downloads](https://img.shields.io/nuget/dt/TelegramBotBase.Extensions.Images.svg?style=flat-square&label=Package%20Downloads)](https://www.nuget.org/packages/TelegramBotBase.Extensions.Images) [![Downloads](https://img.shields.io/nuget/dt/TelegramBotBase.Extensions.Images.svg?style=flat-square&label=Package%20Downloads)](https://www.nuget.org/packages/TelegramBotBase.Extensions.Images)
[https://www.nuget.org/packages/TelegramBotBase.Extensions.Images/](https://www.nuget.org/packages/TelegramBotBase.Extensions.Images/) Source code: [TelegramBotBase.Extensions.Images/](/TelegramBotBase.Extensions.Images/)
Nuget package: [https://www.nuget.org/packages/TelegramBotBase.Extensions.Images/](https://www.nuget.org/packages/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/)
Source code: [TelegramBotBase.Extensions.Images.IronSoftware/](TelegramBotBase.Extensions.Images.IronSoftware/)
Nuget package: [https://www.nuget.org/packages/TelegramBotBase.Extensions.IronSoftware/](https://www.nuget.org/packages/TelegramBotBase.Extensions.IronSoftware/)
---
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 ### TelegramBotBase.Extensions.Serializer.Database.MSSQL
@@ -1043,7 +1096,28 @@ A session serializer for Microsoft SQL Server.
[![NuGet version (TelegramBotBase)](https://img.shields.io/nuget/v/TelegramBotBase.Extensions.Serializer.Database.MSSQL.svg?style=flat-square)](https://www.nuget.org/packages/TelegramBotBase.Extensions.Serializer.Database.MSSQL/) [![NuGet version (TelegramBotBase)](https://img.shields.io/nuget/v/TelegramBotBase.Extensions.Serializer.Database.MSSQL.svg?style=flat-square)](https://www.nuget.org/packages/TelegramBotBase.Extensions.Serializer.Database.MSSQL/)
[![Downloads](https://img.shields.io/nuget/dt/TelegramBotBase.Extensions.Serializer.Database.MSSQL.svg?style=flat-square&label=Package%20Downloads)](https://www.nuget.org/packages/TelegramBotBase.Extensions.Serializer.Database.MSSQL) [![Downloads](https://img.shields.io/nuget/dt/TelegramBotBase.Extensions.Serializer.Database.MSSQL.svg?style=flat-square&label=Package%20Downloads)](https://www.nuget.org/packages/TelegramBotBase.Extensions.Serializer.Database.MSSQL)
[https://www.nuget.org/packages/TelegramBotBase.Extensions.Serializer.Database.MSSQL/](https://www.nuget.org/packages/TelegramBotBase.Extensions.Serializer.Database.MSSQL/) Source code: [TelegramBotBase.Extensions.Serializer.Database.MSSQL/](/TelegramBotBase.Extensions.Serializer.Database.MSSQL/)
Nuget package: [https://www.nuget.org/packages/TelegramBotBase.Extensions.Serializer.Database.MSSQL/](https://www.nuget.org/packages/TelegramBotBase.Extensions.Serializer.Database.MSSQL/)
---
### TelegramBotBase.Extensions.Serializer.Database.PostgreSql
A session serializer for PostgreSql Server.
[![NuGet version (TelegramBotBase)](https://img.shields.io/nuget/v/TelegramBotBase.Extensions.Serializer.Database.PostgreSql.svg?style=flat-square)](https://www.nuget.org/packages/TelegramBotBase.Extensions.Serializer.Database.PostgreSql/)
[![Downloads](https://img.shields.io/nuget/dt/TelegramBotBase.Extensions.Serializer.Database.PostgreSql.svg?style=flat-square&label=Package%20Downloads)](https://www.nuget.org/packages/TelegramBotBase.Extensions.Serializer.Database.PostgreSql)
Source code: [TelegramBotBase.Extensions.Serializer.Database.PostgreSql/](/TelegramBotBase.Extensions.Serializer.Database.PostgreSql/)
Nuget package: [https://www.nuget.org/packages/TelegramBotBase.Extensions.Serializer.Database.PostgreSql/](https://www.nuget.org/packages/TelegramBotBase.Extensions.Serializer.Database.PostgreSql/)
Credits: [@Kataane](https://github.com/Kataane)
---
Project: [open source](TelegramBotBase.Extensions.Serializer.Database.MSSQL/)
## Test Project ## Test Project
@@ -1087,4 +1161,4 @@ Want to use Inline- and ReplyMarkup at the same time ? Here is an example how yo
Alpha: Full Dependency Injection example within this framework. Alpha: Full Dependency Injection example within this framework.
- [Examples/DependencyInjection](Examples/DependencyInjection) - [Examples/DependencyInjection](Examples/DependencyInjection)
@@ -0,0 +1,73 @@
using IronSoftware.Drawing;
using SixLabors.ImageSharp;
using System.IO;
using System.Threading.Tasks;
using Telegram.Bot.Types;
using TelegramBotBase.Form;
using TelegramBotBase.Sessions;
using static IronSoftware.Drawing.AnyBitmap;
using SKImage = SixLabors.ImageSharp.Image;
namespace TelegramBotBase.Extensions.Images.IronSoftware
{
public static class ImageExtensions
{
public static Stream ToStream(this AnyBitmap image, ImageFormat format)
{
var stream = new MemoryStream();
image.ExportStream(stream, format);
stream.Position = 0;
return stream;
}
public static async Task<Stream> ToStream(this SKImage image)
{
var stream = new MemoryStream();
await image.SaveAsPngAsync(stream);
stream.Position = 0;
return stream;
}
/// <summary>
/// Sends an image
/// </summary>
/// <param name="image"></param>
/// <param name="name"></param>
/// <param name="buttons"></param>
/// <param name="replyTo"></param>
/// <param name="disableNotification"></param>
/// <returns></returns>
public static async Task<Message> SendPhoto(this DeviceSession session, AnyBitmap image, string name,
string caption, ButtonForm buttons = null, int replyTo = 0,
bool disableNotification = false)
{
using (var fileStream = ToStream(image, ImageFormat.Png))
{
var fts = InputFile.FromStream(fileStream, name);
return await session.SendPhoto(fts, caption, buttons, replyTo, disableNotification);
}
}
/// <summary>
/// Sends an image
/// </summary>
/// <param name="image"></param>
/// <param name="name"></param>
/// <param name="buttons"></param>
/// <param name="replyTo"></param>
/// <param name="disableNotification"></param>
/// <returns></returns>
public static async Task<Message> SendPhoto(this DeviceSession session, SKImage image, string name,
string caption, ButtonForm buttons = null, int replyTo = 0,
bool disableNotification = false)
{
using (var fileStream = await ToStream(image))
{
var fts = InputFile.FromStream(fileStream, name);
return await session.SendPhoto(fts, caption, buttons, replyTo, disableNotification);
}
}
}
}
@@ -0,0 +1,12 @@
# TelegramBotBase.Extensions.Images.IronSoftware
[![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/)
[![Telegram chat](https://img.shields.io/badge/Support_Chat-Telegram-blue.svg?style=flat-square)](https://www.t.me/tgbotbase)
[![License](https://img.shields.io/github/license/MajMcCloud/telegrambotframework.svg?style=flat-square&maxAge=2592000&label=License)](https://raw.githubusercontent.com/MajMcCloud/TelegramBotFramework/master/LICENCE.md)
[![Package 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)
This extension uses the [IronSoftware](https://ironsoftware.com/open-source/csharp/drawing/docs/) drawing library.
Check [https://ironsoftware.com/open-source/csharp/drawing/docs/](https://ironsoftware.com/open-source/csharp/drawing/docs/) for more details on this library.
@@ -0,0 +1,29 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard2.0;netcoreapp3.1;net6</TargetFrameworks>
<RepositoryUrl>https://github.com/MajMcCloud/TelegramBotFramework/tree/development/TelegramBotBase.Extensions.Images.IronSoftware</RepositoryUrl>
<PackageProjectUrl>https://github.com/MajMcCloud/TelegramBotFramework/tree/development/TelegramBotBase.Extensions.Images.IronSoftware</PackageProjectUrl>
<Copyright>MIT</Copyright>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
<AssemblyVersion>1.0.1</AssemblyVersion>
<FileVersion>1.0.1</FileVersion>
<Description>This is an extension for sending Bitmap/Images platform independent by using IronSoftware's drawing library via TelegramBotBase.</Description>
<Version>1.0.1</Version>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="IronSoftware.System.Drawing" Version="2024.1.1" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="TelegramBotBase" Version="6.0.0" />
</ItemGroup>
<ItemGroup>
<Folder Include="Properties\" />
</ItemGroup>
</Project>
@@ -2,11 +2,17 @@
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>netstandard2.0;netcoreapp3.1;net6</TargetFrameworks> <TargetFrameworks>netstandard2.0;netcoreapp3.1;net6</TargetFrameworks>
<RepositoryUrl>https://github.com/MajMcCloud/TelegramBotFramework</RepositoryUrl> <RepositoryUrl>https://github.com/MajMcCloud/TelegramBotFramework/tree/development/TelegramBotBase.Extensions.Images</RepositoryUrl>
<PackageProjectUrl>https://github.com/MajMcCloud/TelegramBotFramework</PackageProjectUrl> <PackageProjectUrl>https://github.com/MajMcCloud/TelegramBotFramework/tree/development/TelegramBotBase.Extensions.Images</PackageProjectUrl>
<Copyright>MIT</Copyright> <Copyright>MIT</Copyright>
<IncludeSymbols>true</IncludeSymbols> <IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat> <SymbolPackageFormat>snupkg</SymbolPackageFormat>
<GeneratePackageOnBuild>False</GeneratePackageOnBuild>
<Authors>$(AssemblyName)</Authors>
<Description>This is an extension for sending Bitmap/Images via TelegramBotBase.</Description>
<FileVersion>1.1.1</FileVersion>
<AssemblyVersion>1.1.1</AssemblyVersion>
<Version>1.1.1</Version>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
@@ -16,7 +16,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Data.SqlClient" Version="5.0.0" /> <PackageReference Include="Microsoft.Data.SqlClient" Version="5.1.3" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1"> <PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
@@ -0,0 +1,92 @@
using System;
using TelegramBotBase.Builder;
using TelegramBotBase.Builder.Interfaces;
namespace TelegramBotBase.Extensions.Serializer.Database.PostgreSql
{
/// <summary>
/// Provides extension methods for configuring the use of PostgreSQL Server Database for session serialization.
/// </summary>
public static class BotBaseBuilderExtensions
{
/// <summary>
/// Uses an PostgreSQL Server Database to save and restore sessions.
/// </summary>
/// <param name="builder">The session serialization stage builder.</param>
/// <param name="connectionString">The connection string to the PostgreSQL database.</param>
/// <param name="fallbackForm">The fallback form type.</param>
/// <param name="tablePrefix">The prefix for database table names (default is "tgb_").</param>
/// <returns>The language selection stage builder.</returns>
public static ILanguageSelectionStage UsePostgreSqlDatabase(
this ISessionSerializationStage builder,
string connectionString, Type fallbackForm = null,
string tablePrefix = "tgb_")
{
var serializer = new PostgreSqlSerializer(connectionString, tablePrefix, fallbackForm);
builder.UseSerialization(serializer);
return builder as BotBaseBuilder;
}
/// <summary>
/// Uses an PostgreSQL Server Database to save and restore sessions.
/// </summary>
/// <param name="builder">The session serialization stage builder.</param>
/// <param name="hostOrIp">The host or IP address of the PostgreSQL server.</param>
/// <param name="port">The port number for the PostgreSQL server.</param>
/// <param name="databaseName">The name of the PostgreSQL database.</param>
/// <param name="userId">The user ID for connecting to the PostgreSQL server.</param>
/// <param name="password">The password for connecting to the PostgreSQL server.</param>
/// <param name="fallbackForm">The fallback form type.</param>
/// <param name="tablePrefix">The prefix for database table names (default is "tgb_").</param>
/// <returns>The language selection stage builder.</returns>
public static ILanguageSelectionStage UsePostgreSqlDatabase(
this ISessionSerializationStage builder,
string hostOrIp, int port,
string databaseName, string userId,
string password, Type fallbackForm = null,
string tablePrefix = "tgb_")
{
var connectionString = $"Host={hostOrIp};Port={port};Database={databaseName};Username={userId};Password={password}";
var serializer = new PostgreSqlSerializer(connectionString, tablePrefix, fallbackForm);
builder.UseSerialization(serializer);
return builder as BotBaseBuilder;
}
/// <summary>
/// Uses an PostgreSQL Server Database with Windows Authentication to save and restore sessions.
/// </summary>
/// <param name="builder">The session serialization stage builder.</param>
/// <param name="hostOrIp">The host or IP address of the PostgreSQL server.</param>
/// <param name="port">The port number for the PostgreSQL server.</param>
/// <param name="databaseName">The name of the PostgreSQL database.</param>
/// <param name="integratedSecurity">A flag indicating whether to use Windows Authentication (true) or not (false).</param>
/// <param name="fallbackForm">The fallback form type.</param>
/// <param name="tablePrefix">The prefix for database table names (default is "tgb_").</param>
/// <returns>The language selection stage builder.</returns>
public static ILanguageSelectionStage UsePostgreSqlDatabase(
this ISessionSerializationStage builder,
string hostOrIp, int port,
string databaseName, bool integratedSecurity = true,
Type fallbackForm = null, string tablePrefix = "tgb_")
{
if (!integratedSecurity)
{
throw new ArgumentOutOfRangeException();
}
var connectionString = $"Host={hostOrIp};Port={port};Database={databaseName};Integrated Security=true;";
var serializer = new PostgreSqlSerializer(connectionString, tablePrefix, fallbackForm);
builder.UseSerialization(serializer);
return builder as BotBaseBuilder;
}
}
}
@@ -0,0 +1,252 @@
using Npgsql;
using System;
using System.Data;
using NpgsqlTypes;
using TelegramBotBase.Args;
using TelegramBotBase.Base;
using TelegramBotBase.Form;
using TelegramBotBase.Interfaces;
namespace TelegramBotBase.Extensions.Serializer.Database.PostgreSql
{
/// <summary>
/// Represents a PostgreSQL implementation of the <see cref="IStateMachine"/> for saving and loading form states.
/// </summary>
public class PostgreSqlSerializer : IStateMachine
{
private readonly string insertIntoSessionSql;
private readonly string insertIntoSessionsDataSql;
private readonly string selectAllDevicesSessionsSql;
private readonly string selectAllDevicesSessionsDataSql;
/// <summary>
/// Initializes a new instance of the <see cref="PostgreSqlSerializer"/> class.
/// </summary>
/// <param name="connectionString">The connection string to the PostgreSQL database.</param>
/// <param name="tablePrefix">The prefix for database table names (default is "tgb_").</param>
/// <param name="fallbackStateForm">The fallback state form type.</param>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="connectionString"/> is null.</exception>
/// <exception cref="ArgumentException">Thrown when <paramref name="fallbackStateForm"/> is not a subclass of <see cref="FormBase"/>.</exception>
public PostgreSqlSerializer(string connectionString, string tablePrefix = "tgb_", Type fallbackStateForm = null)
{
ConnectionString = connectionString ?? throw new ArgumentNullException(nameof(connectionString));
TablePrefix = tablePrefix;
FallbackStateForm = fallbackStateForm;
if (FallbackStateForm != null && !FallbackStateForm.IsSubclassOf(typeof(FormBase)))
{
throw new ArgumentException($"{nameof(FallbackStateForm)} is not a subclass of {nameof(FormBase)}");
}
insertIntoSessionSql = "INSERT INTO " + TablePrefix +
"devices_sessions (deviceId, deviceTitle, \"FormUri\", \"QualifiedName\") VALUES (@deviceId, @deviceTitle, @FormUri, @QualifiedName)";
insertIntoSessionsDataSql = "INSERT INTO " + TablePrefix + "devices_sessions_data (deviceId, key, value, type) VALUES (@deviceId, @key, @value, @type)";
selectAllDevicesSessionsSql = "SELECT * FROM " + TablePrefix + "devices_sessions";
selectAllDevicesSessionsDataSql = "SELECT * FROM " + TablePrefix + "devices_sessions_data WHERE deviceId = @deviceId";
}
/// <summary>
/// Gets the connection string to the PostgreSQL database.
/// </summary>
public string ConnectionString { get; }
/// <summary>
/// Gets or sets the table name prefix for database tables.
/// </summary>
public string TablePrefix { get; set; }
/// <summary>
/// Gets or sets the fallback state form type.
/// </summary>
public Type FallbackStateForm { get; set; }
/// <inheritdoc/>
/// <summary>
/// Saves form states to the PostgreSQL database.
/// </summary>
/// <param name="e">The <see cref="SaveStatesEventArgs"/> containing the states to be saved.</param>
public void SaveFormStates(SaveStatesEventArgs e)
{
var container = e.States;
//Cleanup old Session data
Cleanup();
using (var connection = new NpgsqlConnection(ConnectionString))
{
connection.Open();
//Store session data in database
foreach (var state in container.States)
{
using (var sessionCommand = connection.CreateCommand())
{
sessionCommand.CommandText = insertIntoSessionSql;
sessionCommand.Parameters.Add(new NpgsqlParameter("@deviceId", NpgsqlDbType.Bigint){Value = state.DeviceId });
sessionCommand.Parameters.Add(new NpgsqlParameter("@deviceTitle", DbType.StringFixedLength){Value = state.ChatTitle ?? string.Empty});
sessionCommand.Parameters.Add(new NpgsqlParameter("@FormUri", DbType.StringFixedLength) {Value = state.FormUri});
sessionCommand.Parameters.Add(new NpgsqlParameter("@QualifiedName", DbType.StringFixedLength){Value = state.QualifiedName });
sessionCommand.ExecuteNonQuery();
}
}
}
using (var connection = new NpgsqlConnection(ConnectionString))
{
connection.Open();
foreach (var state in container.States)
{
SaveSessionsData(state, connection);
}
}
}
/// <inheritdoc/>
/// <summary>
/// Loads form states from the PostgreSQL database.
/// </summary>
/// <returns>A <see cref="StateContainer"/> containing the loaded form states.</returns>
public StateContainer LoadFormStates()
{
var stateContainer = new StateContainer();
using (var connection = new NpgsqlConnection(ConnectionString))
{
connection.Open();
using (var sessionCommand = connection.CreateCommand())
{
sessionCommand.CommandText = selectAllDevicesSessionsSql;
var sessionTable = new DataTable();
using (var dataAdapter = new NpgsqlDataAdapter(sessionCommand))
{
dataAdapter.Fill(sessionTable);
foreach (DataRow row in sessionTable.Rows)
{
var stateEntry = new StateEntry
{
DeviceId = (long)row["deviceId"],
ChatTitle = row["deviceTitle"].ToString(),
FormUri = row["FormUri"].ToString(),
QualifiedName = row["QualifiedName"].ToString()
};
stateContainer.States.Add(stateEntry);
if (stateEntry.DeviceId > 0)
{
stateContainer.ChatIds.Add(stateEntry.DeviceId);
}
else
{
stateContainer.GroupIds.Add(stateEntry.DeviceId);
}
LoadDataTable(connection, row, stateEntry);
}
}
}
}
return stateContainer;
}
/// <summary>
/// Cleans up old session data in the PostgreSQL database.
/// </summary>
private void Cleanup()
{
using (var connection = new NpgsqlConnection(ConnectionString))
{
connection.Open();
using (var clearCommand = connection.CreateCommand())
{
clearCommand.CommandText = $"DELETE FROM {TablePrefix}devices_sessions_data";
clearCommand.ExecuteNonQuery();
}
using (var clearCommand = connection.CreateCommand())
{
clearCommand.CommandText = $"DELETE FROM {TablePrefix}devices_sessions";
clearCommand.ExecuteNonQuery();
}
}
}
/// <summary>
/// Saves session data to the PostgreSQL database.
/// </summary>
/// <param name="state">The state entry containing session data to be saved.</param>
/// <param name="connection">The NpgsqlConnection used for the database interaction.</param>
private void SaveSessionsData(StateEntry state, NpgsqlConnection connection)
{
foreach (var data in state.Values)
{
using (var dataCommand = connection.CreateCommand())
{
dataCommand.CommandText = insertIntoSessionsDataSql;
dataCommand.Parameters.Add(new NpgsqlParameter("@deviceId", NpgsqlDbType.Bigint) { Value = state.DeviceId });
dataCommand.Parameters.Add(new NpgsqlParameter("@key", DbType.StringFixedLength) { Value = data.Key });
var type = data.Value.GetType();
if (type.IsPrimitive || type == typeof(string))
{
dataCommand.Parameters.Add(new NpgsqlParameter("@value", NpgsqlDbType.Text) { Value = data.Value });
}
else
{
var json = System.Text.Json.JsonSerializer.Serialize(data.Value);
dataCommand.Parameters.Add(new NpgsqlParameter("@value", NpgsqlDbType.Text) { Value = json });
}
dataCommand.Parameters.Add(new NpgsqlParameter("@type", DbType.StringFixedLength) { Value = type.AssemblyQualifiedName });
dataCommand.ExecuteNonQuery();
}
}
}
/// <summary>
/// Loads session data from the PostgreSQL database.
/// </summary>
/// <param name="connection">The NpgsqlConnection used for the database interaction.</param>
/// <param name="row">The DataRow representing a session entry in the main sessions table.</param>
/// <param name="stateEntry">The StateEntry object to which session data will be loaded.</param>
private void LoadDataTable(NpgsqlConnection connection, DataRow row, StateEntry stateEntry)
{
using (var sessionCommand = connection.CreateCommand())
{
sessionCommand.CommandText = selectAllDevicesSessionsDataSql;
sessionCommand.Parameters.Add(new NpgsqlParameter("@deviceId", row["deviceId"]));
var dataCommandTable = new DataTable();
using (var npgSqlDataAdapter = new NpgsqlDataAdapter(sessionCommand))
{
npgSqlDataAdapter.Fill(dataCommandTable);
foreach (DataRow dataRow in dataCommandTable.Rows)
{
var key = dataRow["key"].ToString();
var type = Type.GetType(dataRow["type"].ToString());
var value = System.Text.Json.JsonSerializer.Deserialize(dataRow["value"].ToString(), type);
stateEntry.Values.Add(key, value);
}
}
}
}
}
}
@@ -0,0 +1,27 @@
# TelegramBotBase.Extensions.Serializer.Database.PostgreSQL
[![NuGet version (TelegramBotBase)](https://img.shields.io/nuget/v/TelegramBotBase.Extensions.Serializer.Database.PostgreSQL.svg?style=flat-square)](https://www.nuget.org/packages/TelegramBotBase.Extensions.Serializer.Database.PostgreSQL/)
[![Telegram chat](https://img.shields.io/badge/Support_Chat-Telegram-blue.svg?style=flat-square)](https://www.t.me/tgbotbase)
[![License](https://img.shields.io/github/license/MajMcCloud/telegrambotframework.svg?style=flat-square&maxAge=2592000&label=License)](https://raw.githubusercontent.com/MajMcCloud/TelegramBotFramework/master/LICENCE.md)
[![Package Downloads](https://img.shields.io/nuget/dt/TelegramBotBase.Extensions.Serializer.Database.PostgreSQL.svg?style=flat-square&label=Package%20Downloads)](https://www.nuget.org/packages/TelegramBotBase.Extensions.Serializer.Database.PostgreSQL)
## How to use
```csharp
using TelegramBotBase.Extensions.Serializer.Database.PostgreSQL;
var bot = BotBaseBuilder
.Create()
.WithAPIKey(APIKey)
.DefaultMessageLoop()
.WithStartForm<Start>()
.NoProxy()
.OnlyStart()
.UsePostgreSqlDatabase("localhost", "8181", "telegram_bot")
.UseEnglish()
.Build();
bot.Start();
```
@@ -0,0 +1,28 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard2.0;netcoreapp3.1;net6</TargetFrameworks>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<RepositoryUrl>https://github.com/MajMcCloud/TelegramBotFramework</RepositoryUrl>
<PackageProjectUrl>https://github.com/MajMcCloud/TelegramBotFramework</PackageProjectUrl>
<Copyright>MIT</Copyright>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
<AssemblyVersion>1.0.1</AssemblyVersion>
<FileVersion>1.0.1</FileVersion>
<Version>1.0.1</Version>
<Description>
A session serializer for PostgreSQL Server.
</Description>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="TelegramBotBase" Version="6.0.0" />
<PackageReference Include="Npgsql" Version="8.0.1" />
</ItemGroup>
</Project>
@@ -0,0 +1,25 @@
-- Enable uuid-ossp extension
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
-- Create table tgb_devices_sessions
CREATE TABLE tgb_devices_sessions (
deviceId bigint NOT NULL,
deviceTitle character varying(512) NOT NULL,
"FormUri" character varying(512) NOT NULL,
"QualifiedName" character varying(512) NOT NULL,
CONSTRAINT PK_tgb_devices_sessions_1 PRIMARY KEY (deviceId)
);
-- Create table tgb_devices_sessions_data
CREATE TABLE tgb_devices_sessions_data (
Id uuid DEFAULT uuid_generate_v4() NOT NULL,
deviceId bigint NOT NULL,
key character varying(512) NOT NULL,
"value" text NOT NULL,
"type" character varying(512) NOT NULL,
CONSTRAINT PK_tgb_devices_session_data PRIMARY KEY (Id)
);
-- Add default constraint for Id column in tgb_devices_sessions_data
ALTER TABLE tgb_devices_sessions_data
ALTER COLUMN Id SET DEFAULT uuid_generate_v4();
+1
View File
@@ -32,6 +32,7 @@ internal class Program
}) })
.NoSerialization() .NoSerialization()
.UseEnglish() .UseEnglish()
.UseThreadPool()
.Build(); .Build();
@@ -8,7 +8,10 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <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" /> <ProjectReference Include="..\TelegramBotBase\TelegramBotBase.csproj" />
</ItemGroup> </ItemGroup>
+14
View File
@@ -0,0 +1,14 @@
using System;
namespace TelegramBotBase.Base
{
public class ErrorResult : EventArgs
{
public ErrorResult(Exception exception)
{
Exception = exception;
}
public Exception Exception { get; }
}
}
+68 -44
View File
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.Net; using System.Net;
using System.Net.Http; using System.Net.Http;
using System.Runtime.InteropServices;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Telegram.Bot; using Telegram.Bot;
@@ -10,6 +11,7 @@ using Telegram.Bot.Exceptions;
using Telegram.Bot.Polling; using Telegram.Bot.Polling;
using Telegram.Bot.Types; using Telegram.Bot.Types;
namespace TelegramBotBase.Base; namespace TelegramBotBase.Base;
/// <summary> /// <summary>
@@ -17,31 +19,38 @@ namespace TelegramBotBase.Base;
/// </summary> /// </summary>
public class MessageClient public class MessageClient
{ {
private EventHandlerList Events { get; } = new();
private static readonly object EvOnMessageLoop = 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; }
private static object __evCallbackQuery = new(); public ITelegramBotClient TelegramClient { get; set; }
private CancellationTokenSource _cancellationTokenSource;
/// <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 bool ThrowPendingUpdates { get; set; }
public MessageClient(string apiKey) public MessageClient(string apiKey)
{ {
ApiKey = apiKey; ApiKey = apiKey;
TelegramClient = new TelegramBotClient(apiKey); TelegramClient = new TelegramBotClient(apiKey);
Prepare();
} }
public MessageClient(string apiKey, HttpClient proxy) public MessageClient(string apiKey, HttpClient proxy)
{ {
ApiKey = apiKey; ApiKey = apiKey;
TelegramClient = new TelegramBotClient(apiKey, proxy); TelegramClient = new TelegramBotClient(apiKey, proxy);
Prepare();
} }
@@ -59,8 +68,6 @@ public class MessageClient
); );
TelegramClient = new TelegramBotClient(apiKey, httpClient); TelegramClient = new TelegramBotClient(apiKey, httpClient);
Prepare();
} }
/// <summary> /// <summary>
@@ -80,8 +87,6 @@ public class MessageClient
); );
TelegramClient = new TelegramBotClient(apiKey, httpClient); TelegramClient = new TelegramBotClient(apiKey, httpClient);
Prepare();
} }
@@ -89,61 +94,41 @@ public class MessageClient
{ {
ApiKey = apiKey; ApiKey = apiKey;
TelegramClient = client; TelegramClient = client;
Prepare();
} }
public string ApiKey { get; } public virtual void StartReceiving()
public ITelegramBotClient TelegramClient { get; set; }
private EventHandlerList Events { get; } = new();
public void Prepare()
{
TelegramClient.Timeout = new TimeSpan(0, 0, 30);
}
public void StartReceiving()
{ {
_cancellationTokenSource = new CancellationTokenSource(); _cancellationTokenSource = new CancellationTokenSource();
var receiverOptions = new ReceiverOptions(); var receiverOptions = new ReceiverOptions();
TelegramClient.StartReceiving(HandleUpdateAsync, HandleErrorAsync, receiverOptions, receiverOptions.ThrowPendingUpdates = ThrowPendingUpdates;
_cancellationTokenSource.Token);
TelegramClient.StartReceiving(HandleUpdateAsync, HandleErrorAsync, receiverOptions, _cancellationTokenSource.Token);
} }
public void StopReceiving()
public virtual void StopReceiving()
{ {
_cancellationTokenSource.Cancel(); _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)); await OnMessageLoop(new UpdateResult(update, null));
} }
public Task HandleErrorAsync(ITelegramBotClient botClient, Exception exception,
private async Task HandleErrorAsync(ITelegramBotClient botClient, Exception exception,
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
if (exception is ApiRequestException exApi) await OnReceiveError(new ErrorResult(exception));
{
Console.WriteLine($"Telegram API Error:\n[{exApi.ErrorCode}]\n{exApi.Message}");
}
else
{
Console.WriteLine(exception.ToString());
}
return Task.CompletedTask;
} }
#region "BotCommands"
/// <summary> /// <summary>
/// This will return the current list of bot commands. /// This will return the current list of bot commands.
/// </summary> /// </summary>
@@ -175,6 +160,8 @@ public class MessageClient
await TelegramClient.DeleteMyCommandsAsync(scope, languageCode); await TelegramClient.DeleteMyCommandsAsync(scope, languageCode);
} }
#endregion
#region "Events" #region "Events"
@@ -184,10 +171,47 @@ public class MessageClient
remove => Events.RemoveHandler(EvOnMessageLoop, value); remove => Events.RemoveHandler(EvOnMessageLoop, value);
} }
public async Task OnMessageLoop(UpdateResult update) public async Task OnMessageLoop(UpdateResult update)
{ {
await (Events[EvOnMessageLoop] as Async.AsyncEventHandler<UpdateResult>)?.Invoke(this, update); var eventHandlers = (Events[EvOnMessageLoop] as Async.AsyncEventHandler<UpdateResult>)?.Invoke(this, update);
if (eventHandlers != null)
{
await eventHandlers;
}
}
public event Async.AsyncEventHandler<ErrorResult> ReceiveError
{
add => Events.AddHandler(EvOnReceiveError, value);
remove => Events.RemoveHandler(EvOnReceiveError, value);
}
public async Task OnReceiveError(ErrorResult update)
{
var eventHandlers = (Events[EvOnReceiveError] as Async.AsyncEventHandler<ErrorResult>)?.Invoke(this, update);
if (eventHandlers != null)
{
await eventHandlers;
return;
}
//Fallback when no event handler is used.
if (update.Exception is ApiRequestException exApi)
{
Console.WriteLine($"Telegram API Error:\n[{exApi.ErrorCode}]\n{exApi.Message}");
}
else
{
Console.WriteLine(update.Exception.ToString());
}
} }
#endregion #endregion
} }
@@ -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;
}
}
+2 -1
View File
@@ -115,12 +115,13 @@ public sealed class BotBase
if (ds == null) if (ds == null)
{ {
ds = await Sessions.StartSession(e.DeviceId); ds = await Sessions.StartSession(e.DeviceId);
e.Device = ds;
ds.LastMessage = e.RawData.Message; ds.LastMessage = e.RawData.Message;
OnSessionBegins(new SessionBeginEventArgs(e.DeviceId, ds)); OnSessionBegins(new SessionBeginEventArgs(e.DeviceId, ds));
} }
e.Device = ds;
var mr = new MessageResult(e.RawData); var mr = new MessageResult(e.RawData);
var i = 0; var i = 0;
+65 -16
View File
@@ -18,7 +18,7 @@ namespace TelegramBotBase.Builder;
public class BotBaseBuilder : IAPIKeySelectionStage, IMessageLoopSelectionStage, IStartFormSelectionStage, public class BotBaseBuilder : IAPIKeySelectionStage, IMessageLoopSelectionStage, IStartFormSelectionStage,
IBuildingStage, INetworkingSelectionStage, IBotCommandsStage, ISessionSerializationStage, IBuildingStage, INetworkingSelectionStage, IBotCommandsStage, ISessionSerializationStage,
ILanguageSelectionStage ILanguageSelectionStage, IThreadingStage
{ {
private string _apiKey; private string _apiKey;
@@ -32,6 +32,7 @@ public class BotBaseBuilder : IAPIKeySelectionStage, IMessageLoopSelectionStage,
private BotBaseBuilder() private BotBaseBuilder()
{ {
} }
/// <summary> /// <summary>
@@ -87,6 +88,8 @@ public class BotBaseBuilder : IAPIKeySelectionStage, IMessageLoopSelectionStage,
DefaultLanguage(); DefaultLanguage();
UseSingleThread();
return this; return this;
} }
@@ -107,6 +110,8 @@ public class BotBaseBuilder : IAPIKeySelectionStage, IMessageLoopSelectionStage,
DefaultLanguage(); DefaultLanguage();
UseSingleThread();
return this; return this;
} }
@@ -125,6 +130,8 @@ public class BotBaseBuilder : IAPIKeySelectionStage, IMessageLoopSelectionStage,
DefaultLanguage(); DefaultLanguage();
UseSingleThread();
return this; return this;
} }
@@ -207,67 +214,72 @@ public class BotBaseBuilder : IAPIKeySelectionStage, IMessageLoopSelectionStage,
#region "Step 4 (Network Settings)" #region "Step 4 (Network Settings)"
public IBotCommandsStage WithProxy(string proxyAddress) public IBotCommandsStage WithProxy(string proxyAddress, bool throwPendingUpdates = false, int timeoutInSeconds = 60)
{ {
var url = new Uri(proxyAddress); var url = new Uri(proxyAddress);
_client = new MessageClient(_apiKey, url) _client = new MessageClient(_apiKey, url)
{ {
TelegramClient = TelegramClient =
{ {
Timeout = new TimeSpan(0, 1, 0) Timeout = TimeSpan.FromSeconds(timeoutInSeconds)
}, },
}; };
_client.ThrowPendingUpdates = throwPendingUpdates;
return this; return this;
} }
public IBotCommandsStage NoProxy() public IBotCommandsStage NoProxy(bool throwPendingUpdates = false, int timeoutInSeconds = 60)
{ {
_client = new MessageClient(_apiKey) _client = new MessageClient(_apiKey)
{ {
TelegramClient = TelegramClient =
{ {
Timeout = new TimeSpan(0, 1, 0) Timeout = TimeSpan.FromSeconds(timeoutInSeconds)// new TimeSpan(0, 1, 0)
} }
}; };
_client.ThrowPendingUpdates = throwPendingUpdates;
return this; return this;
} }
public IBotCommandsStage WithBotClient(TelegramBotClient tgclient) public IBotCommandsStage WithBotClient(TelegramBotClient tgclient, bool throwPendingUpdates = false, int timeoutInSeconds = 60)
{ {
_client = new MessageClient(_apiKey, tgclient) _client = new MessageClient(_apiKey, tgclient)
{ {
TelegramClient = TelegramClient =
{ {
Timeout = new TimeSpan(0, 1, 0) Timeout = TimeSpan.FromSeconds(timeoutInSeconds)// new TimeSpan(0, 1, 0)
} }
}; };
_client.ThrowPendingUpdates = throwPendingUpdates;
return this; return this;
} }
public IBotCommandsStage WithHostAndPort(string proxyHost, int proxyPort) public IBotCommandsStage WithHostAndPort(string proxyHost, int proxyPort, bool throwPendingUpdates = false, int timeoutInSeconds = 60)
{ {
_client = new MessageClient(_apiKey, proxyHost, proxyPort) _client = new MessageClient(_apiKey, proxyHost, proxyPort)
{ {
TelegramClient = TelegramClient =
{ {
Timeout = new TimeSpan(0, 1, 0) Timeout = TimeSpan.FromSeconds(timeoutInSeconds)// new TimeSpan(0, 1, 0)
} }
}; };
_client.ThrowPendingUpdates = throwPendingUpdates;
return this; return this;
} }
public IBotCommandsStage WithHttpClient(HttpClient tgclient) public IBotCommandsStage WithHttpClient(HttpClient tgclient, bool throwPendingUpdates = false, int timeoutInSeconds = 60)
{ {
_client = new MessageClient(_apiKey, tgclient) _client = new MessageClient(_apiKey, tgclient)
{ {
TelegramClient = TelegramClient =
{ {
Timeout = new TimeSpan(0, 1, 0) Timeout = TimeSpan.FromSeconds(timeoutInSeconds)// new TimeSpan(0, 1, 0)
} }
}; };
_client.ThrowPendingUpdates = throwPendingUpdates;
return this; return this;
} }
@@ -386,34 +398,71 @@ public class BotBaseBuilder : IAPIKeySelectionStage, IMessageLoopSelectionStage,
#region "Step 7 (Language)" #region "Step 7 (Language)"
public IBuildingStage DefaultLanguage() /// <inheritdoc cref="ILanguageSelectionStage.DefaultLanguage"/>
public IThreadingStage DefaultLanguage()
{ {
return this; return this;
} }
public IBuildingStage UseEnglish() /// <inheritdoc cref="ILanguageSelectionStage.UseEnglish"/>
public IThreadingStage UseEnglish()
{ {
Default.Language = new English(); Default.Language = new English();
return this; return this;
} }
public IBuildingStage UseGerman() /// <inheritdoc cref="ILanguageSelectionStage.UseGerman"/>
public IThreadingStage UseGerman()
{ {
Default.Language = new German(); Default.Language = new German();
return this; return this;
} }
public IBuildingStage UsePersian() /// <inheritdoc cref="ILanguageSelectionStage.UsePersian"/>
public IThreadingStage UsePersian()
{ {
Default.Language = new Persian(); Default.Language = new Persian();
return this; return this;
} }
public IBuildingStage Custom(Localization language) /// <inheritdoc cref="ILanguageSelectionStage.UseRussian"/>
public IThreadingStage UseRussian()
{
Default.Language = new Russian();
return this;
}
/// <inheritdoc cref="ILanguageSelectionStage.Custom"/>
public IThreadingStage Custom(Localization language)
{ {
Default.Language = language; Default.Language = language;
return this; return this;
} }
#endregion #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
} }
@@ -2,35 +2,44 @@
namespace TelegramBotBase.Builder.Interfaces; namespace TelegramBotBase.Builder.Interfaces;
/// <summary>
/// Represents the language selection stage in the localization process.
/// </summary>
public interface ILanguageSelectionStage public interface ILanguageSelectionStage
{ {
/// <summary> /// <summary>
/// Selects the default language for control usage. (English) /// Selects the default language for control usage. (English)
/// </summary> /// </summary>
/// <returns></returns> /// <returns>The next stage in the building process.</returns>
IBuildingStage DefaultLanguage(); IThreadingStage DefaultLanguage();
/// <summary> /// <summary>
/// Selects english as the default language for control labels. /// Selects english as the default language for control labels.
/// </summary> /// </summary>
/// <returns></returns> /// <returns>The next stage in the building process.</returns>
IBuildingStage UseEnglish(); IThreadingStage UseEnglish();
/// <summary> /// <summary>
/// Selects german as the default language for control labels. /// Selects german as the default language for control labels.
/// </summary> /// </summary>
/// <returns></returns> /// <returns>The next stage in the building process.</returns>
IBuildingStage UseGerman(); IThreadingStage UseGerman();
/// <summary> /// <summary>
/// Selects persian as the default language for control labels. /// Selects persian as the default language for control labels.
/// </summary> /// </summary>
/// <returns></returns> /// <returns>The next stage in the building process.</returns>
IBuildingStage UsePersian(); IThreadingStage UsePersian();
/// <summary>
/// Selects russian as the default language for control labels.
/// </summary>
/// <returns>The next stage in the building process.</returns>
IThreadingStage UseRussian();
/// <summary> /// <summary>
/// Selects a custom language as the default language for control labels. /// Selects a custom language as the default language for control labels.
/// </summary> /// </summary>
/// <returns></returns> /// <returns>The next stage in the building process.</returns>
IBuildingStage Custom(Localization language); IThreadingStage Custom(Localization language);
} }
@@ -9,22 +9,25 @@ public interface INetworkingSelectionStage
/// Chooses a proxy as network configuration. /// Chooses a proxy as network configuration.
/// </summary> /// </summary>
/// <param name="proxyAddress"></param> /// <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> /// <returns></returns>
IBotCommandsStage WithProxy(string proxyAddress); IBotCommandsStage WithProxy(string proxyAddress, bool throwPendingUpdates = false, int timeoutInSeconds = 60);
/// <summary> /// <summary>
/// Do not choose a proxy as network configuration. /// Do not choose a proxy as network configuration.
/// </summary> /// </summary>
/// <param name="throwPendingUpdates">Indicates if all pending Telegram.Bot.Types.Updates should be thrown out before start polling.</param>
/// <returns></returns> /// <returns></returns>
IBotCommandsStage NoProxy(); IBotCommandsStage NoProxy(bool throwPendingUpdates = false, int timeoutInSeconds = 60);
/// <summary> /// <summary>
/// Chooses a custom instance of TelegramBotClient. /// Chooses a custom instance of TelegramBotClient.
/// </summary> /// </summary>
/// <param name="client"></param> /// <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> /// <returns></returns>
IBotCommandsStage WithBotClient(TelegramBotClient client); IBotCommandsStage WithBotClient(TelegramBotClient client, bool throwPendingUpdates = false, int timeoutInSeconds = 60);
/// <summary> /// <summary>
@@ -32,13 +35,15 @@ public interface INetworkingSelectionStage
/// </summary> /// </summary>
/// <param name="proxyHost"></param> /// <param name="proxyHost"></param>
/// <param name="Port"></param> /// <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> /// <returns></returns>
IBotCommandsStage WithHostAndPort(string proxyHost, int Port); IBotCommandsStage WithHostAndPort(string proxyHost, int Port, bool throwPendingUpdates = false, int timeoutInSeconds = 60);
/// <summary> /// <summary>
/// Uses a custom http client. /// Uses a custom http client.
/// </summary> /// </summary>
/// <param name="client"></param> /// <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> /// <returns></returns>
IBotCommandsStage WithHttpClient(HttpClient client); IBotCommandsStage WithHttpClient(HttpClient client, bool throwPendingUpdates = false, int timeoutInSeconds = 60);
} }
@@ -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);
}
}
@@ -426,8 +426,6 @@ public class ButtonGrid : ControlBase
return; return;
} }
await result.ConfirmAction(ConfirmationText ?? "");
ButtonRow match = null; ButtonRow match = null;
var index = -1; var index = -1;
@@ -462,6 +460,8 @@ public class ButtonGrid : ControlBase
check: check:
if (match != null) if (match != null)
{ {
await result.ConfirmAction(ConfirmationText ?? "");
await OnButtonClicked(new ButtonClickedEventArgs(match.GetButtonMatch(result.RawData, false), index, await OnButtonClicked(new ButtonClickedEventArgs(match.GetButtonMatch(result.RawData, false), index,
match)); match));
@@ -29,8 +29,6 @@ public class TaggedButtonGrid : MultiView
public string BackLabel = Default.Language["ButtonGrid_Back"]; public string BackLabel = Default.Language["ButtonGrid_Back"];
public string CheckAllLabel = Default.Language["ButtonGrid_CheckAll"];
public string NextPageLabel = Default.Language["ButtonGrid_NextPage"]; public string NextPageLabel = Default.Language["ButtonGrid_NextPage"];
public string NoItemsLabel = Default.Language["ButtonGrid_NoItems"]; public string NoItemsLabel = Default.Language["ButtonGrid_NoItems"];
@@ -39,11 +37,17 @@ public class TaggedButtonGrid : MultiView
public string SearchLabel = Default.Language["ButtonGrid_SearchFeature"]; public string SearchLabel = Default.Language["ButtonGrid_SearchFeature"];
public string UncheckAllLabel = Default.Language["ButtonGrid_UncheckAll"]; public string TotalTagsLabel = Default.Language["TaggedButtonGrid_TotalTags"];
public string CheckedTagsLabel = Default.Language["TaggedButtonGrid_CheckedTags"];
public string CheckAllLabel = Default.Language["TaggedButtonGrid_CheckAll"];
public string UncheckAllLabel = Default.Language["TaggedButtonGrid_UncheckAll"];
public string SearchIcon = Default.Language["ButtonGrid_SearchIcon"]; public string SearchIcon = Default.Language["ButtonGrid_SearchIcon"];
public string TagIcon = Default.Language["ButtonGrid_TagIcon"]; public string TagIcon = Default.Language["TaggedButtonGrid_TagIcon"];
public TaggedButtonGrid() public TaggedButtonGrid()
{ {
@@ -379,14 +383,6 @@ public class TaggedButtonGrid : MultiView
index = br.Item2; index = br.Item2;
} }
//var button = HeadLayoutButtonRow?. .FirstOrDefault(a => a.Text.Trim() == result.MessageText)
// ?? SubHeadLayoutButtonRow?.FirstOrDefault(a => a.Text.Trim() == result.MessageText);
// bf.ToList().FirstOrDefault(a => a.Text.Trim() == result.MessageText)
//var index = bf.FindRowByButton(button);
check: check:
@@ -490,10 +486,16 @@ public class TaggedButtonGrid : MultiView
if (result.MessageText == CheckAllLabel) if (result.MessageText == CheckAllLabel)
{ {
CheckAllTags(); CheckAllTags();
Updated();
result.Handled = true;
return;
} }
else if (result.MessageText == UncheckAllLabel) else if (result.MessageText == UncheckAllLabel)
{ {
UncheckAllTags(); UncheckAllTags();
Updated();
result.Handled = true;
return;
} }
var i = result.MessageText.LastIndexOf(" "); var i = result.MessageText.LastIndexOf(" ");
@@ -569,15 +571,6 @@ public class TaggedButtonGrid : MultiView
index = br.Item2; index = br.Item2;
} }
//var bf = DataSource.ButtonForm;
//var button = HeadLayoutButtonRow?.FirstOrDefault(a => a.Value == result.RawData)
// ?? SubHeadLayoutButtonRow?.FirstOrDefault(a => a.Value == result.RawData)
// ?? bf.ToList().FirstOrDefault(a => a.Value == result.RawData);
//var index = bf.FindRowByButton(button);
check: check:
if (match != null) if (match != null)
{ {
@@ -619,22 +612,47 @@ public class TaggedButtonGrid : MultiView
break; break;
case "$back$":
SelectedViewIndex = 0; default:
if (SelectedViewIndex != 1)
return;
switch (result.RawData)
{
case "$back$":
SelectedViewIndex = 0;
Updated();
return;
case "$checkall$":
CheckAllTags();
return;
case "$uncheckall$":
UncheckAllTags();
return;
}
if (SelectedTags.Contains(result.RawData))
{
SelectedTags.Remove(result.RawData);
}
else
{
SelectedTags.Add(result.RawData);
}
Updated(); Updated();
break;
case "$checkall$":
CheckAllTags();
break;
case "$uncheckall$":
UncheckAllTags();
break; break;
} }
@@ -723,8 +741,10 @@ public class TaggedButtonGrid : MultiView
if (EnableCheckAllTools) if (EnableCheckAllTools)
{ {
TagsSubHeadLayoutButtonRow = new ButtonRow(new ButtonBase(CheckAllLabel, "$checkall$"), TagsSubHeadLayoutButtonRow = new ButtonRow(new ButtonBase(string.Format(TotalTagsLabel, Tags.Count), "$total$"),
new ButtonBase(UncheckAllLabel, "$uncheckall$")); new ButtonBase(CheckAllLabel, "$checkall$"),
new ButtonBase(UncheckAllLabel, "$uncheckall$"),
new ButtonBase(string.Format(CheckedTagsLabel, SelectedTags.Count), "checked$"));
bf.AddButtonRow(TagsSubHeadLayoutButtonRow); bf.AddButtonRow(TagsSubHeadLayoutButtonRow);
} }
@@ -756,10 +776,6 @@ public class TaggedButtonGrid : MultiView
return; return;
} }
//if (bf.Count == 0)
// return;
var rkm = (ReplyKeyboardMarkup)bf; var rkm = (ReplyKeyboardMarkup)bf;
rkm.ResizeKeyboard = ResizeKeyboard; rkm.ResizeKeyboard = ResizeKeyboard;
rkm.OneTimeKeyboard = OneTimeKeyboard; rkm.OneTimeKeyboard = OneTimeKeyboard;
@@ -881,21 +897,20 @@ public class TaggedButtonGrid : MultiView
{ {
Message m = null; Message m = null;
var form = DataSource.PickItems(CurrentPageIndex * ItemRowsPerPage, ItemRowsPerPage, ButtonForm form = null;
EnableSearch ? SearchQuery : null);
//if (this.EnableSearch && this.SearchQuery != null && this.SearchQuery != "")
//{
// form = form.FilterDuplicate(this.SearchQuery, true);
//}
//else
//{
// form = form.Duplicate();
//}
if (Tags != null && SelectedTags != null) if (Tags != null && SelectedTags != null)
{ {
form = DataSource.PickAllItems(EnableSearch ? SearchQuery : null); //CurrentPageIndex * ItemRowsPerPage, ItemRowsPerPage,
form = form.TagDuplicate(SelectedTags); form = form.TagDuplicate(SelectedTags);
form = new ButtonForm(form.ToRowList().Skip(CurrentPageIndex * ItemRowsPerPage).Take(ItemRowsPerPage));
}
else
{
form = DataSource.PickItems(CurrentPageIndex * ItemRowsPerPage, ItemRowsPerPage,
EnableSearch ? SearchQuery : null);
} }
if (EnablePaging) if (EnablePaging)
+10
View File
@@ -23,6 +23,11 @@ public class ButtonForm
DependencyControl = control; DependencyControl = control;
} }
public ButtonForm(IEnumerable<ButtonRow> rows)
{
_buttons = rows.ToList();
}
public IReplyMarkup Markup { get; set; } public IReplyMarkup Markup { get; set; }
@@ -149,6 +154,11 @@ public class ButtonForm
.Aggregate((a, b) => a.Union(b).ToList()); .Aggregate((a, b) => a.Union(b).ToList());
} }
public List<ButtonRow> ToRowList()
{
return _buttons;
}
public InlineKeyboardButton[][] ToInlineButtonArray() public InlineKeyboardButton[][] ToInlineButtonArray()
{ {
var ikb = _buttons.Select(a => a.ToArray().Select(b => b.ToInlineButton(this)).ToArray()).ToArray(); var ikb = _buttons.Select(a => a.ToArray().Select(b => b.ToInlineButton(this)).ToArray()).ToArray();
+5 -3
View File
@@ -12,8 +12,10 @@ public sealed class English : Localization
Values["ButtonGrid_CurrentPage"] = "Page {0} of {1}"; Values["ButtonGrid_CurrentPage"] = "Page {0} of {1}";
Values["ButtonGrid_SearchFeature"] = "💡 Send a message to filter the list. Click the 🔍 to reset the filter."; Values["ButtonGrid_SearchFeature"] = "💡 Send a message to filter the list. Click the 🔍 to reset the filter.";
Values["ButtonGrid_Back"] = "Back"; Values["ButtonGrid_Back"] = "Back";
Values["ButtonGrid_CheckAll"] = "Check all"; Values["TaggedButtonGrid_TotalTags"] = "Total: {0}";
Values["ButtonGrid_UncheckAll"] = "Uncheck all"; Values["TaggedButtonGrid_CheckedTags"] = "Checked: {0}";
Values["TaggedButtonGrid_CheckAll"] = "Check all";
Values["TaggedButtonGrid_UncheckAll"] = "Uncheck all";
Values["CalendarPicker_Title"] = "Pick date"; Values["CalendarPicker_Title"] = "Pick date";
Values["CalendarPicker_PreviousPage"] = "◀️"; Values["CalendarPicker_PreviousPage"] = "◀️";
Values["CalendarPicker_NextPage"] = "▶️"; Values["CalendarPicker_NextPage"] = "▶️";
@@ -31,6 +33,6 @@ public sealed class English : Localization
Values["PromptDialog_Back"] = "Back"; Values["PromptDialog_Back"] = "Back";
Values["ToggleButton_Changed"] = "Setting changed"; Values["ToggleButton_Changed"] = "Setting changed";
Values["ButtonGrid_SearchIcon"] = "🔍"; Values["ButtonGrid_SearchIcon"] = "🔍";
Values["ButtonGrid_TagIcon"] = "📁"; Values["TaggedButtonGrid_TagIcon"] = "📁";
} }
} }
+7 -4
View File
@@ -10,11 +10,12 @@ public sealed class German : Localization
Values["ButtonGrid_PreviousPage"] = "◀️"; Values["ButtonGrid_PreviousPage"] = "◀️";
Values["ButtonGrid_NextPage"] = "▶️"; Values["ButtonGrid_NextPage"] = "▶️";
Values["ButtonGrid_CurrentPage"] = "Seite {0} von {1}"; Values["ButtonGrid_CurrentPage"] = "Seite {0} von {1}";
Values["ButtonGrid_SearchFeature"] = Values["ButtonGrid_SearchFeature"] = "💡 Sende eine Nachricht um die Liste zu filtern. Klicke die 🔍 um den Filter zurückzusetzen.";
"💡 Sende eine Nachricht um die Liste zu filtern. Klicke die 🔍 um den Filter zurückzusetzen.";
Values["ButtonGrid_Back"] = "Zurück"; Values["ButtonGrid_Back"] = "Zurück";
Values["ButtonGrid_CheckAll"] = "Alle auswählen"; Values["TaggedButtonGrid_TotalTags"] = "Gesamt: {0}";
Values["ButtonGrid_UncheckAll"] = "Keine auswählen"; Values["TaggedButtonGrid_CheckedTags"] = "Ausgewählt: {0}";
Values["TaggedButtonGrid_CheckAll"] = "Alle auswählen";
Values["TaggedButtonGrid_UncheckAll"] = "Keine auswählen";
Values["CalendarPicker_Title"] = "Datum auswählen"; Values["CalendarPicker_Title"] = "Datum auswählen";
Values["CalendarPicker_PreviousPage"] = "◀️"; Values["CalendarPicker_PreviousPage"] = "◀️";
Values["CalendarPicker_NextPage"] = "▶️"; Values["CalendarPicker_NextPage"] = "▶️";
@@ -31,5 +32,7 @@ public sealed class German : Localization
Values["MultiToggleButton_Changed"] = "Ausgewählt"; Values["MultiToggleButton_Changed"] = "Ausgewählt";
Values["PromptDialog_Back"] = "Zurück"; Values["PromptDialog_Back"] = "Zurück";
Values["ToggleButton_Changed"] = "Einstellung geändert"; Values["ToggleButton_Changed"] = "Einstellung geändert";
Values["ButtonGrid_SearchIcon"] = "🔍";
Values["TaggedButtonGrid_TagIcon"] = "📁";
} }
} }
+35 -33
View File
@@ -1,37 +1,39 @@
namespace TelegramBotBase.Localizations namespace TelegramBotBase.Localizations;
public sealed class Persian : Localization
{ {
public sealed class Persian : Localization public Persian()
{ {
public Persian() Values["Language"] = "فارسی";
{ Values["ButtonGrid_Title"] = "منو";
Values["Language"] = "فارسی"; Values["ButtonGrid_NoItems"] = "هیچ آیتمی وجود ندارد.";
Values["ButtonGrid_Title"] = "منو"; Values["ButtonGrid_PreviousPage"] = "◀️";
Values["ButtonGrid_NoItems"] = "هیچ آیتمی وجود ندارد."; Values["ButtonGrid_NextPage"] = "▶️";
Values["ButtonGrid_PreviousPage"] = "◀️"; Values["ButtonGrid_CurrentPage"] = "صفحه ی {0} از {1}";
Values["ButtonGrid_NextPage"] = "▶️"; Values["ButtonGrid_SearchFeature"] = "💡 برای فیلتر کردن لیست پیام ارسال کنید. برای بازنشانی فیلتر روی 🔍 کلیک کنید.";
Values["ButtonGrid_CurrentPage"] = "صفحه ی {0} از {1}"; Values["ButtonGrid_Back"] = "بازگشت";
Values["ButtonGrid_SearchFeature"] = "💡 برای فیلتر کردن لیست پیام ارسال کنید. برای بازنشانی فیلتر روی 🔍 کلیک کنید."; Values["TaggedButtonGrid_TotalTags"] = "Total: {0}";
Values["ButtonGrid_Back"] = "بازگشت"; Values["TaggedButtonGrid_CheckedTags"] = "Checked: {0}";
Values["ButtonGrid_CheckAll"] = "بررسی کردن همه"; Values["TaggedButtonGrid_CheckAll"] = "بررسی کردن همه";
Values["ButtonGrid_UncheckAll"] = "بررسی نکردن همه"; Values["TaggedButtonGrid_UncheckAll"] = "بررسی نکردن همه";
Values["CalendarPicker_Title"] = "تاریخ را انتخاب کنید"; Values["CalendarPicker_Title"] = "تاریخ را انتخاب کنید";
Values["CalendarPicker_PreviousPage"] = "◀️"; Values["CalendarPicker_PreviousPage"] = "◀️";
Values["CalendarPicker_NextPage"] = "▶️"; Values["CalendarPicker_NextPage"] = "▶️";
Values["TreeView_Title"] = "گره را انتخاب کنید"; Values["TreeView_Title"] = "گره را انتخاب کنید";
Values["TreeView_LevelUp"] = "🔼 سطح بالا"; Values["TreeView_LevelUp"] = "🔼 سطح بالا";
Values["ToggleButton_On"] = "روشن"; Values["ToggleButton_On"] = "روشن";
Values["ToggleButton_Off"] = "خاموش"; Values["ToggleButton_Off"] = "خاموش";
Values["ToggleButton_OnIcon"] = "⚫"; Values["ToggleButton_OnIcon"] = "⚫";
Values["ToggleButton_OffIcon"] = "⚪"; Values["ToggleButton_OffIcon"] = "⚪";
Values["ToggleButton_Title"] = "تغییر وضعیت"; Values["ToggleButton_Title"] = "تغییر وضعیت";
Values["ToggleButton_Changed"] = "انتخاب شده"; Values["ToggleButton_Changed"] = "انتخاب شده";
Values["MultiToggleButton_SelectedIcon"] = "✅"; Values["MultiToggleButton_SelectedIcon"] = "✅";
Values["MultiToggleButton_Title"] = "چند تعویض"; Values["MultiToggleButton_Title"] = "چند تعویض";
Values["MultiToggleButton_Changed"] = "انتخاب شده"; Values["MultiToggleButton_Changed"] = "انتخاب شده";
Values["PromptDialog_Back"] = "بازگشت"; Values["PromptDialog_Back"] = "بازگشت";
Values["ToggleButton_Changed"] = "تنظیمات تغییر کرد"; Values["ToggleButton_Changed"] = "تنظیمات تغییر کرد";
Values["ButtonGrid_SearchIcon"] = "🔍"; Values["ButtonGrid_SearchIcon"] = "🔍";
Values["ButtonGrid_TagIcon"] = "📁"; Values["TaggedButtonGrid_TagIcon"] = "📁";
}
} }
} }
+38
View File
@@ -0,0 +1,38 @@
namespace TelegramBotBase.Localizations;
public sealed class Russian : Localization
{
public Russian()
{
Values["Language"] = "Русский (Russian)";
Values["ButtonGrid_Title"] = "Меню";
Values["ButtonGrid_NoItems"] = "Нет доступных элементов.";
Values["ButtonGrid_PreviousPage"] = "◀️";
Values["ButtonGrid_NextPage"] = "▶️";
Values["ButtonGrid_CurrentPage"] = "Страница {0} из {1}";
Values["ButtonGrid_SearchFeature"] = "💡 Отправьте сообщение, чтобы отфильтровать список. Нажмите на 🔍, чтобы сбросить фильтр.";
Values["ButtonGrid_Back"] = "Назад";
Values["TaggedButtonGrid_TotalTags"] = "Всего: {0}";
Values["TaggedButtonGrid_CheckedTags"] = "Отмечено: {0}";
Values["TaggedButtonGrid_CheckAll"] = "Выделить все";
Values["TaggedButtonGrid_UncheckAll"] = "Отменить выбор";
Values["CalendarPicker_Title"] = "Календарь / Выберите дату";
Values["CalendarPicker_PreviousPage"] = "◀️";
Values["CalendarPicker_NextPage"] = "▶️";
Values["TreeView_Title"] = "Выберите пункт";
Values["TreeView_LevelUp"] = "🔼 Обратно";
Values["ToggleButton_On"] = "Вкл";
Values["ToggleButton_Off"] = "Выкл";
Values["ToggleButton_OnIcon"] = "⚫";
Values["ToggleButton_OffIcon"] = "⚪";
Values["ToggleButton_Title"] = "Переключить";
Values["ToggleButton_Changed"] = "Выбрано";
Values["MultiToggleButton_SelectedIcon"] = "✅";
Values["MultiToggleButton_Title"] = "Множественный выбор";
Values["MultiToggleButton_Changed"] = "Выбрано";
Values["PromptDialog_Back"] = "Назад";
Values["ToggleButton_Changed"] = "Настройки изменены";
Values["ButtonGrid_SearchIcon"] = "🔍";
Values["TaggedButtonGrid_TagIcon"] = "📁";
}
}
-2
View File
@@ -14,12 +14,10 @@
<dependencies> <dependencies>
<group targetFramework=".NETFramework4.6.1"> <group targetFramework=".NETFramework4.6.1">
<dependency id="Newtonsoft.Json" version="13.0.1" exclude="Build,Analyzers" /> <dependency id="Newtonsoft.Json" version="13.0.1" exclude="Build,Analyzers" />
<dependency id="System.Drawing.Common" version="4.6.0" exclude="Build,Analyzers" />
<dependency id="Telegram.Bot" version="19.0.0" exclude="Build,Analyzers" /> <dependency id="Telegram.Bot" version="19.0.0" exclude="Build,Analyzers" />
</group> </group>
<group targetFramework=".NETStandard2.0"> <group targetFramework=".NETStandard2.0">
<dependency id="Newtonsoft.Json" version="13.0.1" exclude="Build,Analyzers" /> <dependency id="Newtonsoft.Json" version="13.0.1" exclude="Build,Analyzers" />
<dependency id="System.Drawing.Common" version="4.6.0" exclude="Build,Analyzers" />
<dependency id="Telegram.Bot" version="19.0.0" exclude="Build,Analyzers" /> <dependency id="Telegram.Bot" version="19.0.0" exclude="Build,Analyzers" />
</group> </group>
</dependencies> </dependencies>
+15 -1
View File
@@ -32,7 +32,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BotAndWebApplication", "Exa
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "InlineAndReplyCombination", "Examples\InlineAndReplyCombination\InlineAndReplyCombination.csproj", "{067E8EBE-F90A-4AFF-A0FF-20578216486E}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "InlineAndReplyCombination", "Examples\InlineAndReplyCombination\InlineAndReplyCombination.csproj", "{067E8EBE-F90A-4AFF-A0FF-20578216486E}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DependencyInjection", "Examples\DependencyInjection\DependencyInjection.csproj", "{689B16BC-200E-4C68-BB2E-8B209070849B}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DependencyInjection", "Examples\DependencyInjection\DependencyInjection.csproj", "{689B16BC-200E-4C68-BB2E-8B209070849B}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TelegramBotBase.Extensions.Serializer.Database.PostgreSql", "TelegramBotBase.Extensions.Serializer.Database.PostgreSql\TelegramBotBase.Extensions.Serializer.Database.PostgreSql.csproj", "{7C55D9FF-7DC1-41D0-809C-469EBFA20992}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TelegramBotBase.Extensions.Images.IronSoftware", "TelegramBotBase.Extensions.Images.IronSoftware\TelegramBotBase.Extensions.Images.IronSoftware.csproj", "{DC521A4C-7446-46F7-845B-AAF10EDCF8C6}"
EndProject EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -84,6 +88,14 @@ Global
{689B16BC-200E-4C68-BB2E-8B209070849B}.Debug|Any CPU.Build.0 = Debug|Any CPU {689B16BC-200E-4C68-BB2E-8B209070849B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{689B16BC-200E-4C68-BB2E-8B209070849B}.Release|Any CPU.ActiveCfg = Release|Any CPU {689B16BC-200E-4C68-BB2E-8B209070849B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{689B16BC-200E-4C68-BB2E-8B209070849B}.Release|Any CPU.Build.0 = Release|Any CPU {689B16BC-200E-4C68-BB2E-8B209070849B}.Release|Any CPU.Build.0 = Release|Any CPU
{7C55D9FF-7DC1-41D0-809C-469EBFA20992}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7C55D9FF-7DC1-41D0-809C-469EBFA20992}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7C55D9FF-7DC1-41D0-809C-469EBFA20992}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7C55D9FF-7DC1-41D0-809C-469EBFA20992}.Release|Any CPU.Build.0 = Release|Any CPU
{DC521A4C-7446-46F7-845B-AAF10EDCF8C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DC521A4C-7446-46F7-845B-AAF10EDCF8C6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DC521A4C-7446-46F7-845B-AAF10EDCF8C6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DC521A4C-7446-46F7-845B-AAF10EDCF8C6}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
@@ -98,6 +110,8 @@ Global
{52EA3201-02E8-46F5-87C4-B4752C8A815C} = {BFA71E3F-31C0-4FC1-A320-4DCF704768C5} {52EA3201-02E8-46F5-87C4-B4752C8A815C} = {BFA71E3F-31C0-4FC1-A320-4DCF704768C5}
{067E8EBE-F90A-4AFF-A0FF-20578216486E} = {BFA71E3F-31C0-4FC1-A320-4DCF704768C5} {067E8EBE-F90A-4AFF-A0FF-20578216486E} = {BFA71E3F-31C0-4FC1-A320-4DCF704768C5}
{689B16BC-200E-4C68-BB2E-8B209070849B} = {BFA71E3F-31C0-4FC1-A320-4DCF704768C5} {689B16BC-200E-4C68-BB2E-8B209070849B} = {BFA71E3F-31C0-4FC1-A320-4DCF704768C5}
{7C55D9FF-7DC1-41D0-809C-469EBFA20992} = {E3193182-6FDA-4FA3-AD26-A487291E7681}
{DC521A4C-7446-46F7-845B-AAF10EDCF8C6} = {E3193182-6FDA-4FA3-AD26-A487291E7681}
EndGlobalSection EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {59CB40E1-9FA7-4867-A56F-4F418286F057} SolutionGuid = {59CB40E1-9FA7-4867-A56F-4F418286F057}