Compare commits

..
102 Commits
Author SHA1 Message Date
Максим Човнюк 07ac1502b4 Enchancement to override Label._renderNecessary
build nuget workflow for TelegramBotBase project / Build-TelegramBotBase (x64, linux) (push) Successful in 37s
2024-12-07 23:04:00 +05:00
Максим Човнюк 07cbb8bfa4 AutoCleanForm detect only is active form
build nuget workflow for TelegramBotBase project / Build-TelegramBotBase (x64, linux) (push) Successful in 37s
2024-12-07 22:09:18 +05:00
Максим Човнюк 139ccbb136 received handling
build nuget workflow for TelegramBotBase project / Build-TelegramBotBase (x64, linux) (push) Successful in 36s
2024-12-07 21:39:27 +05:00
Максим Човнюк f4bc2ca9b0 fix changing iterable collection
build nuget workflow for TelegramBotBase project / Build-TelegramBotBase (x64, linux) (push) Successful in 36s
2024-12-07 20:53:10 +05:00
Максим Човнюк 880878190d force update
build nuget workflow for TelegramBotBase project / Build-TelegramBotBase (x64, linux) (push) Successful in 40s
2024-12-07 20:02:00 +05:00
Максим Човнюк 3f0fc962a4 IFormState OR attributes
build nuget workflow for TelegramBotBase project / Build-TelegramBotBase (x64, linux) (push) Successful in 38s
2024-12-07 10:50:36 +05:00
Максим Човнюк 6a80ec66ad di and serialization support
build nuget workflow for TelegramBotBase project / Build-TelegramBotBase (x64, linux) (push) Successful in 37s
2024-12-06 16:13:46 +05:00
Максим Човнюк 0931147f5a pass version to dotnet on all steps
build nuget workflow for TelegramBotBase project / Build-TelegramBotBase (x64, linux) (push) Successful in 37s
2024-12-05 17:44:24 +05:00
Максим Човнюк a81d299010 version setup on build step
build nuget workflow for TelegramBotBase project / Build-TelegramBotBase (x64, linux) (push) Failing after 36s
2024-12-05 17:38:45 +05:00
Максим Човнюк 956fbd47f0 1
build nuget workflow for TelegramBotBase project / Build-TelegramBotBase (x64, linux) (push) Successful in 36s
2024-12-05 17:28:42 +05:00
Максим Човнюк 4210ecbb4f version in workflow
build nuget workflow for TelegramBotBase project / Build-TelegramBotBase (x64, linux) (push) Failing after 35s
2024-12-05 17:26:47 +05:00
Максим Човнюк 9dc7490015 version update
build nuget workflow for TelegramBotBase project / Build-TelegramBotBase (x64, linux) (push) Failing after 34s
2024-12-05 17:22:36 +05:00
Максим Човнюк 877d2daa30 name fix
build nuget workflow for TelegramBotBase project / Build-TelegramBotBase (x64, linux) (push) Successful in 36s
2024-12-05 16:54:31 +05:00
Максим Човнюк 55027b92aa optional step "disconnect source"
build nuget workflow for TelegramBotBase project / Build-TelegramBotBase (x64, linux) (push) Failing after 34s
2024-12-05 16:50:13 +05:00
Максим Човнюк ec4bb85d40 revert external action to cli commands
build nuget workflow for TelegramBotBase project / Build-TelegramBotBase (x64, linux) (push) Failing after 35s
2024-12-05 16:47:19 +05:00
Максим Човнюк db0afd4668 change interpolation type in path
build nuget workflow for TelegramBotBase project / Build-TelegramBotBase (x64, linux) (push) Failing after 43s
2024-12-05 16:32:21 +05:00
Максим Човнюк 3ab24dcd37 try to use new github action
build nuget workflow for TelegramBotBase project / Build-TelegramBotBase (x64, linux) (push) Failing after 5s
2024-12-05 16:31:04 +05:00
Максим Човнюк 4b42644593 fix missing path to uploading nuget package
build nuget workflow for TelegramBotBase project / Build-TelegramBotBase (x64, linux) (push) Failing after 35s
2024-12-05 16:23:25 +05:00
Максим Човнюк 3b792ee676 workflow move to "nuget push" api key
build nuget workflow for TelegramBotBase project / Build-TelegramBotBase (x64, linux) (push) Failing after 35s
2024-12-05 16:20:48 +05:00
Максим Човнюк 367de14a1a update dependencies and add workflow for publish nuget
build nuget workflow for TelegramBotBase project / Build-TelegramBotBase (x64, linux) (push) Failing after 41s
2024-12-05 16:16:49 +05:00
Florian Zevedei 82e6d79ec4 Updating examples 2024-07-12 13:40:47 +02:00
Florian Zevedei 5d2f3e7fe9 Merge pull request #67 from MajMcCloud/development
Updating master branch via development branch
2024-07-10 14:28:25 +02:00
Florian Zevedei 7aa40b3e2d Using Message instead RawData.Message on session creation 2024-07-10 14:25:51 +02:00
Florian Zevedei eb8fad444b Fix for bots in groups adding/removing 2024-07-10 14:25:08 +02:00
Florian Zevedei 5cda9e1fdc README.md aktualisieren 2024-07-06 17:56:33 +02:00
Florian Zevedei 41bdf52afe README.md aktualisieren 2024-07-06 17:56:13 +02:00
Florian Zevedei 9023875922 README.md aktualisieren 2024-07-06 17:55:36 +02:00
Florian Zevedei 05bfeb8d09 Update README.md 2024-07-06 17:28:23 +02:00
Florian Zevedei 1f1aa1f0ee Adding throwPendingUpdates to QuickStart in BBB 2024-07-03 15:56:50 +02:00
Florian Zevedei 3d97da75f1 Update README.md 2024-07-01 03:28:37 +02:00
Florian Zevedei 261fdafd96 Merge pull request #65 from MajMcCloud/development
Migrating development branch into master
2024-06-29 18:30:34 +02:00
Florian Zevedei 39e68a2089 Merge branch 'master' into development 2024-06-29 18:28:37 +02:00
Florian Zevedei 312690c002 Fix builder in sample 2024-06-25 13:43:33 +02:00
Florian Zevedei 99b888a7a2 Replacing project reference with nuget package in samples 2024-06-25 13:43:21 +02:00
Florian Zevedei 6adcc52ea2 Adding null/empty checks to some controls to prevent invalid behaviour 2024-06-09 14:19:04 +02:00
Florian Zevedei 834038ff44 Adding additional bot command checks toa void invalid configurations 2024-06-09 14:13:43 +02:00
Florian Zevedei e3d1652a02 Fixing on EditableMessage 2024-05-29 21:35:09 +02:00
Florian Zevedei a7411176e6 Fixing the prototype solution of group commands detection 2024-05-29 21:29:14 +02:00
Florian Zevedei 801c0b77f8 Introducing BotGroupCommand property. 2024-05-27 21:39:36 +02:00
Florian Zevedei 7a86ecdf32 Breakable change! Remove of most try/catch's in DeviceSession 2024-05-25 17:24:24 +02:00
Florian Zevedei 175927fbab Updating nuget dependencies 2 2024-05-19 14:16:18 +02:00
Florian Zevedei 34e4429b38 Updating nuget dependencies 2024-05-19 14:15:56 +02:00
Florian Zevedei dc28bde382 Fix Action method handling, if handled by control already 2024-05-19 14:06:01 +02:00
Florian Zevedei 87d57c471a Update CallbackDataTooLongException.cs 2024-05-12 19:07:33 +03:00
Florian Zevedei 5ce2360cc7 Adding callbackdata too long exception and checks 2024-05-12 19:01:50 +03:00
Florian Zevedei e345fc2948 Fixing message loop handling of Action methods 2024-05-12 18:45:18 +03:00
Florian Zevedei 652526ec59 Consolidate nuget references 2024-05-12 18:40:55 +03:00
Florian Zevedei b829f43c5d Adding a exception for invalid ServiceProviders #62
- adding an internal exception handler to catch InvalidOperationException and throw a new explicit one
2024-03-10 17:09:13 +01:00
Florian Zevedei 6e7acdbdba Bugfix in BBB 2024-03-10 16:49:18 +01:00
Florian Zevedei 396a524d94 BotBaseBuilder fix 2024-03-10 16:01:45 +01:00
Florian Zevedei 7d679649e4 Merge pull request #64 from MajMcCloud/master
Update development branch
2024-03-10 15:54:29 +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 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 83e09aec2a Removing System.Drawing.Common dependency 2023-12-29 14:38:11 +01:00
Florian Zevedei a63f71268c Update README.md 2023-12-28 15:17:14 +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 121a788c17 Update README.md 2023-12-08 01:52:47 +01:00
93 changed files with 1390 additions and 2969 deletions
@@ -0,0 +1,48 @@
name: build nuget workflow for TelegramBotBase project
on:
push:
branches:
- master
jobs:
Build-TelegramBotBase:
env:
APP_PROJECT_NAME: TelegramBotBase
PACKAGE_VERSION: "123.1.6"
strategy:
matrix:
os:
- linux
# - win
arch:
- x64
#- x32
#- arch64
runs-on: [ "${{ matrix.os }}" ]
steps:
- name: Check out repository code
uses: actions/checkout@v4
- name: Setup dotnet
uses: actions/setup-dotnet@v3
with:
dotnet-version: '8.0.x'
- name: Restore dependencies
run: dotnet restore $APP_PROJECT_NAME /p:Version=$PACKAGE_VERSION
- name: Build app
run: dotnet build -c Release --version-suffix $PACKAGE_VERSION --no-restore $APP_PROJECT_NAME /p:Version=$PACKAGE_VERSION
- name: Pack app
run: dotnet pack --no-build $APP_PROJECT_NAME /p:Version=$PACKAGE_VERSION
- name: disconnect old source
run: dotnet nuget remove source gitea
continue-on-error: true
- name: Connect source
run: dotnet nuget add source --name gitea https://git.kosyakmakc.ru/api/packages/kosyakmakc/nuget/index.json
- name: Upload nuget package
run: dotnet nuget push --source gitea --api-key ${{ secrets.kosyakmakc_nuget_publish }} ${{ gitea.workspace }}/${{ env.APP_PROJECT_NAME }}/bin/Release/$APP_PROJECT_NAME.$PACKAGE_VERSION.nupkg
@@ -8,7 +8,7 @@
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\TelegramBotBase\TelegramBotBase.csproj"/>
<PackageReference Include="TelegramBotBase" Version="6.4.1" />
</ItemGroup>
</Project>
@@ -7,7 +7,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="TelegramBotBase" Version="5.3.0" />
<PackageReference Include="TelegramBotBase" Version="6.4.1" />
</ItemGroup>
<ItemGroup>
@@ -16,6 +16,7 @@ namespace BotAndWebApplication.BotStuff.Tasks
.DefaultCommands()
.NoSerialization()
.UseEnglish()
.UseThreadPool()
.Build();
}
+3 -3
View File
@@ -8,12 +8,12 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.9"/>
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="6.0.9"/>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.11" />
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="7.0.11" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\TelegramBotBase\TelegramBotBase.csproj"/>
<ProjectReference Include="..\..\TelegramBotBase\TelegramBotBase.csproj" />
</ItemGroup>
</Project>
@@ -8,7 +8,7 @@
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\TelegramBotBase\TelegramBotBase.csproj" />
<PackageReference Include="TelegramBotBase" Version="6.4.1" />
</ItemGroup>
</Project>
+1 -1
View File
@@ -8,7 +8,7 @@
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\TelegramBotBase\TelegramBotBase.csproj"/>
<PackageReference Include="TelegramBotBase" Version="6.4.1" />
</ItemGroup>
</Project>
@@ -1,16 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\TelegramBotBase\TelegramBotBase.csproj" />
<ProjectReference Include="..\TelegramBotBase.Experiments\TelegramBotBase.Experiments.ActionManager.csproj" />
</ItemGroup>
</Project>
@@ -1,52 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.WebSockets;
using System.Text;
using System.Threading.Tasks;
using TelegramBotBase.Base;
using TelegramBotBase.Form;
namespace DemoBot.Forms
{
public class HiddenForm : AutoCleanForm
{
public String value { get; set; }
public HiddenForm() {
DeleteMode = TelegramBotBase.Enums.EDeleteMode.OnLeavingForm;
DeleteSide = TelegramBotBase.Enums.EDeleteSide.Both;
}
public override async Task Action(MessageResult message)
{
if (message.RawData != "start")
{
return;
}
await message.ConfirmAction("Lets go");
message.Handled = true;
var st = new StartForm();
await NavigateTo(st);
}
public override async Task Render(MessageResult message)
{
var bf = new ButtonForm();
bf.AddButtonRow("Goto Start", "start");
value = value.Replace("_", "\\_");
await Device.Send($"Welcome to Hidden form\n\nThe given value is {value}", bf);
}
}
}
@@ -1,52 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.WebSockets;
using System.Text;
using System.Threading.Tasks;
using TelegramBotBase.Base;
using TelegramBotBase.Form;
namespace DemoBot.Forms
{
public class HiddenForm_EndsWith : AutoCleanForm
{
public String value { get; set; }
public HiddenForm_EndsWith() {
DeleteMode = TelegramBotBase.Enums.EDeleteMode.OnLeavingForm;
DeleteSide = TelegramBotBase.Enums.EDeleteSide.Both;
}
public override async Task Action(MessageResult message)
{
if (message.RawData != "start")
{
return;
}
await message.ConfirmAction("Lets go");
message.Handled = true;
var st = new StartForm();
await NavigateTo(st);
}
public override async Task Render(MessageResult message)
{
var bf = new ButtonForm();
bf.AddButtonRow("Goto Start", "start");
value = value.Replace("_", "\\_");
await Device.Send($"Welcome to Hidden ends with form\n\nThe given value is {value}", bf);
}
}
}
@@ -1,53 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TelegramBotBase.Base;
using TelegramBotBase.Form;
namespace DemoBot.Forms
{
public class HiddenLetterForm : AutoCleanForm
{
public Guid letterId { get; set; }
public HiddenLetterForm()
{
DeleteMode = TelegramBotBase.Enums.EDeleteMode.OnLeavingForm;
DeleteSide = TelegramBotBase.Enums.EDeleteSide.Both;
}
public override async Task Action(MessageResult message)
{
if (message.RawData != "start")
{
return;
}
await message.ConfirmAction("Lets go");
message.Handled = true;
var st = new StartForm();
await NavigateTo(st);
}
public override async Task Render(MessageResult message)
{
var bf = new ButtonForm();
bf.AddButtonRow("Goto Start", "start");
await Device.Send($"Welcome to Hidden letter form\n\nYour letter Id is: {letterId}", bf);
}
}
}
@@ -1,52 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TelegramBotBase.Base;
using TelegramBotBase.Form;
namespace DemoBot.Forms
{
public class HiddenOpenForm : AutoCleanForm
{
public Guid guid { get; set; }
public HiddenOpenForm()
{
DeleteMode = TelegramBotBase.Enums.EDeleteMode.OnLeavingForm;
DeleteSide = TelegramBotBase.Enums.EDeleteSide.Both;
}
public override async Task Action(MessageResult message)
{
if (message.RawData != "start")
{
return;
}
await message.ConfirmAction("Lets go");
message.Handled = true;
var st = new StartForm();
await NavigateTo(st);
}
public override async Task Render(MessageResult message)
{
var bf = new ButtonForm();
bf.AddButtonRow("Goto Start", "start");
await Device.Send($"Welcome to Hidden open form\n\nYour guid is: {guid}", bf);
}
}
}
@@ -1,53 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TelegramBotBase.Base;
using TelegramBotBase.Form;
namespace DemoBot.Forms
{
public class HiddenTicketForm : AutoCleanForm
{
public Guid ticketId { get; set; }
public HiddenTicketForm()
{
DeleteMode = TelegramBotBase.Enums.EDeleteMode.OnLeavingForm;
DeleteSide = TelegramBotBase.Enums.EDeleteSide.Both;
}
public override async Task Action(MessageResult message)
{
if (message.RawData != "start")
{
return;
}
await message.ConfirmAction("Lets go");
message.Handled = true;
var st = new StartForm();
await NavigateTo(st);
}
public override async Task Render(MessageResult message)
{
var bf = new ButtonForm();
bf.AddButtonRow("Goto Start", "start");
await Device.Send($"Welcome to Hidden ticket form\n\nYour ticket Id is: {ticketId}", bf);
}
}
}
@@ -1,36 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TelegramBotBase.Base;
using TelegramBotBase.Form;
namespace DemoBot.Forms
{
internal class StartForm : AutoCleanForm
{
public StartForm()
{
DeleteMode = TelegramBotBase.Enums.EDeleteMode.OnLeavingForm;
DeleteSide = TelegramBotBase.Enums.EDeleteSide.Both;
Opened += StartForm_Opened;
}
private async Task StartForm_Opened(object sender, EventArgs e)
{
await Device.Send("Hey!\r\n\r\nChoose the /test command to get a message from outside.", disableNotification: true);
}
public override async Task Load(MessageResult message)
{
}
}
}
@@ -1,310 +0,0 @@
using DemoBot.Forms;
using Telegram.Bot;
using Telegram.Bot.Types.ReplyMarkups;
using TelegramBotBase.Builder;
using TelegramBotBase.Commands;
using TelegramBotBase.Form;
using TelegramBotBase.Base;
using TelegramBotBase.Experiments.ActionManager;
using TelegramBotBase.Experiments.ActionManager.Actions;
using TelegramBotBase.Experiments.ActionManager.Navigation;
using Telegram.Bot.Types;
namespace DemoBot
{
internal class Program
{
public static String Token = Environment.GetEnvironmentVariable("API_KEY") ?? throw new Exception("API_KEY is not set");
static async Task Main(string[] args)
{
//Using a custom FormBase message loop which is based on the original FormBaseMessageLoop within the framework.
//Would integrate this later into the BotBaseBuilder -> MessageLoop step.
//var cfb = new CustomFormBaseMessageLoop();
//Example 1: Long version
var eam = ExternalActionManager.Configure(config =>
{
//Waiting for input starting with 'n_'
config.AddStartsWithNavigation<HiddenForm>("n_", (a, b) =>
{
a.value = b;
});
//Minimal version, using reflection right now
config.AddStartsWithNavigation<HiddenForm>("a_", (a, b) =>
{
a.value = b;
});
//Waiting for input starting with 't_'
config.AddStartsWithNavigation(typeof(HiddenForm), "t_", (a, b) =>
{
var hf = a as HiddenForm;
if (hf == null)
return;
hf.value = b;
});
//Minimal version, using reflection right now
config.AddEndsWithNavigation<HiddenForm_EndsWith>("_u", (a, b) =>
{
a.value = b;
});
//Deserialize input and waiting for the method property to has value 'tickets'
config.AddGuidNavigation<HiddenTicketForm>("tickets", (a, b) =>
{
a.ticketId = b;
});
//Minimal version, using reflection right now
config.AddGuidNavigation<HiddenLetterForm>("letters", (a, b) =>
{
a.letterId = b;
});
//Deserialize input and waiting for the method property to has value 'open'
config.AddGuidNavigation<HiddenOpenForm>("open", (a, b) =>
{
a.guid = b;
});
//Do custom action
config.AddStartsWithAction("p_", StartsWithActionCallback_Demo);
//Do custom action
config.AddStartsWithAction("p2_", async (value, update, message) =>
{
if (message.UpdateData.CallbackQuery == null)
return;
await update.Device.ConfirmAction(message.UpdateData.CallbackQuery.Id, "Confirmed!");
});
});
//Example 2: Short version
var eam2 = ExternalActionManager.Configure(config =>
{
//Waiting for input starting with 'n_'
config.AddStartsWithNavigation<HiddenForm>("n_", a => a.value);
//Waiting for input starting with 'a_'
config.AddStartsWithNavigation<HiddenForm>("a_", a => a.value);
//Waiting for input starting with 't_' (Non generic version)
config.AddStartsWithNavigation(typeof(HiddenForm), "t_", a => ((HiddenForm)a).value);
//Waiting for input ending with 'n_'
config.AddEndsWithNavigation<HiddenForm_EndsWith>("_u", a => a.value);
//Deserialize input and waiting for the method property to has value 'tickets'
config.AddGuidNavigation<HiddenTicketForm>("tickets", a => a.ticketId);
//Minimal version, using reflection right now
config.AddGuidNavigation<HiddenLetterForm>("letters", a => a.letterId);
//Deserialize input and waiting for the method property to has value 'open'
config.AddGuidNavigation<HiddenOpenForm>("open", a => a.guid);
//Do custom action
config.AddStartsWithAction("p_", StartsWithActionCallback_Demo);
//Do custom action
config.AddStartsWithAction("p2_", async (value, update, message) =>
{
if (message.UpdateData.CallbackQuery == null)
return;
await update.Device.ConfirmAction(message.UpdateData.CallbackQuery.Id, "Confirmed!");
});
config.AddGuidAction("guid.test.too.long", async (g, c, u, m) =>
{
if (m.UpdateData.CallbackQuery == null)
return;
await u.Device.ConfirmAction(m.UpdateData.CallbackQuery.Id, "Confirmed!");
});
});
var bb = BotBaseBuilder.Create()
.WithAPIKey(Token)
.ActionMessageLoop(eam2)
.WithStartForm<Forms.StartForm>()
.NoProxy()
.CustomCommands(a =>
{
a.Start("Starts the bot");
a.Add("test", "Sends a test notification");
a.Add("invalid", "Try to send an invalid data message");
})
.NoSerialization()
.UseGerman()
.UseSingleThread()
.Build();
bb.BotCommand += Bb_BotCommand;
bb.UnhandledCall += Bb_UnhandledCall;
await bb.Start();
await bb.UploadBotCommands();
Console.WriteLine("Bot started.");
Console.ReadLine();
await bb.Stop();
}
private static async Task StartsWithActionCallback_Demo(String value, UpdateResult ur, MessageResult mr)
{
if (mr.UpdateData.CallbackQuery == null)
return;
await ur.Device.ConfirmAction(mr.UpdateData.CallbackQuery.Id, "Confirmed!");
}
private static void Bb_UnhandledCall(object? sender, TelegramBotBase.Args.UnhandledCallEventArgs e)
{
Console.WriteLine($"Unhandled call: {e.RawData}");
}
private static async Task Bb_BotCommand(object sender, TelegramBotBase.Args.BotCommandEventArgs e)
{
var current_form = e.Device.ActiveForm;
switch (e.Command)
{
case "/start":
//Already on start form
if (current_form.GetType() == typeof(Forms.StartForm))
{
return;
}
var st = new Forms.StartForm();
await current_form.NavigateTo(st);
break;
case "/test":
//Send test notification
//Test values
String max_value = "n_".PadRight(32, '1'); //Starts with
String max_value2 = "t_".PadRight(32, '2'); //Starts with
String max_value3 = "a_".PadRight(32, '3'); //Starts with
String max_value4 = "_u".PadLeft(32, '4'); //Ends with
String max_value5 = "p_".PadRight(32, '5'); //Ends with
String max_value6 = "p2_".PadRight(32, '6'); //Ends with
Guid test_value = Guid.NewGuid(); //Unhandled caller
var callback_guid = GuidNavigation.GetCallback("open", Guid.NewGuid()); //HiddenOpenForm
var callback_tickets = GuidNavigation.GetCallback("tickets", Guid.NewGuid()); //HiddenTicketForm
var callback_letters = GuidNavigation.GetCallback("letters", Guid.NewGuid()); //HiddenLetterForm
String message = $"Test notification from 'outside'\n\nTest values are:\n\nTest: {max_value}\nTest2: {max_value2}\nTest3: {max_value3}\nTest (Guid): {test_value.ToString()}\nTest (Callback Guid): {callback_guid.Value}\nTickets (Guid): {callback_tickets.Value}\nLetters (Guid): {callback_letters.Value}\n";
var tb = new TelegramBotClient(Token);
var bf = new ButtonForm();
bf.AddButtonRow(new ButtonBase("Ok", "n_ok"), new ButtonBase("Later", "n_later"));
bf.AddButtonRow("Test (n_)", max_value);
bf.AddButtonRow("Test2 (t_)", max_value2);
bf.AddButtonRow("Test3 (a_)", max_value3);
bf.AddButtonRow("Test4 (_u)", max_value4);
bf.AddButtonRow("Test5 (p_)", max_value5);
bf.AddButtonRow("Test6 (p2_)", max_value6);
bf.AddButtonRow("Test (Guid)", test_value.ToString());
bf.AddButtonRow("Test (Callback Gui)", callback_guid);
bf.AddButtonRow("Tickets", callback_tickets);
bf.AddButtonRow("Letters", callback_letters);
bf.AddButtonRow("Close", "close");
await tb.SendTextMessageAsync(e.DeviceId, message, disableNotification: true, replyMarkup: (InlineKeyboardMarkup)bf);
break;
case "/invalid":
Guid g = Guid.NewGuid();
var tb2 = new TelegramBotClient(Token);
var bf2 = new ButtonForm();
bf2.AddButtonRow("Test test", GuidAction.GetCallback("guid.test.too.long", g));
String message2 = "This is an invalid test message.";
await tb2.SendTextMessageAsync(e.DeviceId, message2, disableNotification: true, replyMarkup: (InlineKeyboardMarkup)bf2);
break;
}
}
}
}
@@ -1,29 +0,0 @@
# ExternalActionManager
Idea of this experiment is to find a good intuitive way for handling "Unhandled calls".
Right now they are "thrown" into an event handler and you have to take care over them on yourself.
Source of them would be in most cases Telegram Bot messages which has an Inlinekeyboard attached and comes from outside of the framework. (via webservice or other remote channels)
And the button press should navigate to a different form, or invoke a different action.
## How does it work ?
- Open the Bot
- Run the "/start" command
- Now you are on the only FormBase where you can get without interaction
- Use the "/test" command to invoke an "outside" message with some different buttons.
- Use them to navigate to different "hidden" forms
## Future ideas
- adding an Action for int and/or long datatypes
-
Begin: 18.01.2024
@@ -1,13 +0,0 @@
using TelegramBotBase.Base;
namespace TelegramBotBase.Experiments.ActionManager.Actions
{
public static class EndsWithAction_Extensions
{
public static void AddEndsWithAction(this ExternalActionManager manager, string value, Func<String, UpdateResult, MessageResult, Task> action)
{
manager.Add(new EndsWithAction(value, action));
}
}
}
@@ -1,29 +0,0 @@
using TelegramBotBase.Base;
using TelegramBotBase.Form;
namespace TelegramBotBase.Experiments.ActionManager.Actions
{
public class EndsWithAction : IExternalAction
{
public string SearchForString { get; set; }
public Action<FormBase, string> SetProperty { get; set; }
Func<String, UpdateResult, MessageResult, Task> Action;
public EndsWithAction(string searchFor, Func<String, UpdateResult, MessageResult, Task> action)
{
SearchForString = searchFor;
Action = action;
}
public bool DoesFit(string raw_data) => raw_data.EndsWith(SearchForString);
public async Task DoAction(UpdateResult ur, MessageResult mr) => await Action(mr.RawData, ur, mr);
}
}
@@ -1,14 +0,0 @@
using TelegramBotBase.Base;
using TelegramBotBase.Form;
namespace TelegramBotBase.Experiments.ActionManager.Actions
{
public static class GuidAction_Extensions
{
public static void AddGuidAction(this ExternalActionManager manager, string method, Func<Guid, CallbackData, UpdateResult, MessageResult, Task> action)
{
manager.Add(new GuidAction(method, action));
}
}
}
@@ -1,85 +0,0 @@
using TelegramBotBase.Base;
using TelegramBotBase.Form;
namespace TelegramBotBase.Experiments.ActionManager.Actions
{
public class GuidAction : IExternalAction
{
public string Method { get; set; }
CallbackData? _lastData { get; set; }
Guid? _lastValue { get; set; }
Func<Guid, CallbackData, UpdateResult, MessageResult, Task> Action;
public GuidAction(string method, Func<Guid, CallbackData, UpdateResult, MessageResult, Task> action)
{
Method = method;
Action = action;
}
public bool DoesFit(string raw_data)
{
var cd = CallbackData.Deserialize(raw_data);
if (cd == null)
return false;
if (cd.Method != Method)
return false;
Guid g;
if (Guid.TryParse(cd.Value, out g))
_lastValue = g;
_lastData = cd;
return true;
}
public async Task DoAction(UpdateResult ur, MessageResult mr) => await Action(_lastValue.Value, _lastData, ur, mr);
public static CallbackData GetCallback(string method, Guid guid) => new CallbackData(method, guid.ToString());
}
public class GuidAction<TForm> : IExternalAction
where TForm : FormBase
{
public string Method { get; set; }
Guid? _lastValue { get; set; }
Func<Guid, UpdateResult, MessageResult, Task> Action;
public GuidAction(string method, Func<Guid, UpdateResult, MessageResult, Task> action)
{
Method = method;
Action = action;
}
public bool DoesFit(string raw_data)
{
var cd = CallbackData.Deserialize(raw_data);
if (cd == null)
return false;
if (cd.Method != Method)
return false;
Guid g;
if (Guid.TryParse(cd.Value, out g))
_lastValue = g;
return true;
}
public async Task DoAction(UpdateResult ur, MessageResult mr) => await Action(_lastValue.Value, ur, mr);
}
}
@@ -1,14 +0,0 @@
using TelegramBotBase.Base;
using TelegramBotBase.Form;
namespace TelegramBotBase.Experiments.ActionManager.Actions
{
public static class Int32Action_Extensions
{
public static void AddInt32Action(this ExternalActionManager manager, string method, Func<int, CallbackData, UpdateResult, MessageResult, Task> action)
{
manager.Add(new Int32Action(method, action));
}
}
}
@@ -1,90 +0,0 @@
using TelegramBotBase.Base;
using TelegramBotBase.Form;
namespace TelegramBotBase.Experiments.ActionManager.Actions
{
public class Int32Action : IExternalAction
{
public string Method { get; set; }
CallbackData? _lastData { get; set; }
int? _lastValue { get; set; }
Func<int, CallbackData, UpdateResult, MessageResult, Task> Action;
public Int32Action(string method, Func<int, CallbackData, UpdateResult, MessageResult, Task> action)
{
Method = method;
Action = action;
}
public bool DoesFit(string raw_data)
{
var cd = CallbackData.Deserialize(raw_data);
if (cd == null)
return false;
if (cd.Method != Method)
return false;
int i;
if (int.TryParse(cd.Value, out i))
_lastValue = i;
_lastData = cd;
return true;
}
public async Task DoAction(UpdateResult ur, MessageResult mr) => await Action(_lastValue.Value, _lastData, ur, mr);
public static CallbackData GetCallback(string method, long l) => new CallbackData(method, l.ToString());
}
public class Int32Action<TForm> : IExternalAction
where TForm : FormBase
{
public string Method { get; set; }
CallbackData? _lastData { get; set; }
int? _lastValue { get; set; }
Func<int, CallbackData, UpdateResult, MessageResult, Task> Action;
public Int32Action(string method, Func<int, CallbackData, UpdateResult, MessageResult, Task> action)
{
Method = method;
Action = action;
}
public bool DoesFit(string raw_data)
{
var cd = CallbackData.Deserialize(raw_data);
if (cd == null)
return false;
if (cd.Method != Method)
return false;
int i;
if (int.TryParse(cd.Value, out i))
_lastValue = i;
_lastData = cd;
return true;
}
public async Task DoAction(UpdateResult ur, MessageResult mr) => await Action(_lastValue.Value, _lastData, ur, mr);
}
}
@@ -1,14 +0,0 @@
using TelegramBotBase.Base;
using TelegramBotBase.Form;
namespace TelegramBotBase.Experiments.ActionManager.Actions
{
public static class Int64Action_Extensions
{
public static void AddInt64Action(this ExternalActionManager manager, string method, Func<long, CallbackData, UpdateResult, MessageResult, Task> action)
{
manager.Add(new Int64Action(method, action));
}
}
}
@@ -1,89 +0,0 @@
using TelegramBotBase.Base;
using TelegramBotBase.Form;
namespace TelegramBotBase.Experiments.ActionManager.Actions
{
public class Int64Action : IExternalAction
{
public string Method { get; set; }
CallbackData? _lastData { get; set; }
long? _lastValue { get; set; }
Func<long, CallbackData, UpdateResult, MessageResult, Task> Action;
public Int64Action(string method, Func<long, CallbackData, UpdateResult, MessageResult, Task> action)
{
Method = method;
Action = action;
}
public bool DoesFit(string raw_data)
{
var cd = CallbackData.Deserialize(raw_data);
if (cd == null)
return false;
if (cd.Method != Method)
return false;
long l;
if (long.TryParse(cd.Value, out l))
_lastValue = l;
_lastData = cd;
return true;
}
public async Task DoAction(UpdateResult ur, MessageResult mr) => await Action(_lastValue.Value, _lastData, ur, mr);
public static CallbackData GetCallback(string method, long l) => new CallbackData(method, l.ToString());
}
public class Int64Action<TForm> : IExternalAction
where TForm : FormBase
{
public string Method { get; set; }
CallbackData? _lastData { get; set; }
long? _lastValue { get; set; }
Func<long, CallbackData, UpdateResult, MessageResult, Task> Action;
public Int64Action(string method, Func<long, CallbackData, UpdateResult, MessageResult, Task> action)
{
Method = method;
Action = action;
}
public bool DoesFit(string raw_data)
{
var cd = CallbackData.Deserialize(raw_data);
if (cd == null)
return false;
if (cd.Method != Method)
return false;
long g;
if (long.TryParse(cd.Value, out g))
_lastValue = g;
_lastData = cd;
return true;
}
public async Task DoAction(UpdateResult ur, MessageResult mr) => await Action(_lastValue.Value, _lastData, ur, mr);
}
}
@@ -1,13 +0,0 @@
using TelegramBotBase.Base;
namespace TelegramBotBase.Experiments.ActionManager.Actions
{
public static class StartWithAction_Extensions
{
public static void AddStartsWithAction(this ExternalActionManager manager, string value, Func<String, UpdateResult, MessageResult, Task> action)
{
manager.Add(new StartWithAction(value, action));
}
}
}
@@ -1,29 +0,0 @@
using TelegramBotBase.Base;
using TelegramBotBase.Form;
namespace TelegramBotBase.Experiments.ActionManager.Actions
{
public class StartWithAction : IExternalAction
{
public string SearchForString { get; set; }
public Action<FormBase, string> SetProperty { get; set; }
Func<String, UpdateResult, MessageResult, Task> Action;
public StartWithAction(string searchFor, Func<String, UpdateResult, MessageResult, Task> action)
{
SearchForString = searchFor;
Action = action;
}
public bool DoesFit(string raw_data) => raw_data.StartsWith(SearchForString);
public async Task DoAction(UpdateResult ur, MessageResult mr) => await Action(mr.RawData, ur, mr);
}
}
@@ -1,14 +0,0 @@
using TelegramBotBase.Base;
using TelegramBotBase.Form;
namespace TelegramBotBase.Experiments.ActionManager.Actions
{
public static class StringAction_Extensions
{
public static void AddStringAction(this ExternalActionManager manager, string method, Func<String, CallbackData, UpdateResult, MessageResult, Task> action)
{
manager.Add(new StringAction(method, action));
}
}
}
@@ -1,85 +0,0 @@
using TelegramBotBase.Base;
using TelegramBotBase.Form;
namespace TelegramBotBase.Experiments.ActionManager.Actions
{
public class StringAction : IExternalAction
{
public string Method { get; set; }
CallbackData? _lastData { get; set; }
String? _lastValue { get; set; }
Func<string, CallbackData, UpdateResult, MessageResult, Task> Action;
public StringAction(string method, Func<string, CallbackData, UpdateResult, MessageResult, Task> action)
{
Method = method;
Action = action;
}
public bool DoesFit(string raw_data)
{
var cd = CallbackData.Deserialize(raw_data);
if (cd == null)
return false;
if (cd.Method != Method)
return false;
_lastValue = cd.Value;
_lastData = cd;
return true;
}
public async Task DoAction(UpdateResult ur, MessageResult mr) => await Action(_lastValue, _lastData, ur, mr);
public static CallbackData GetCallback(string method, string str) => new CallbackData(method, str);
}
public class StringAction<TForm> : IExternalAction
where TForm : FormBase
{
public string Method { get; set; }
CallbackData? _lastData { get; set; }
String? _lastValue { get; set; }
Func<String, CallbackData, UpdateResult, MessageResult, Task> Action;
public StringAction(string method, Func<String, CallbackData, UpdateResult, MessageResult, Task> action)
{
Method = method;
Action = action;
}
public bool DoesFit(string raw_data)
{
var cd = CallbackData.Deserialize(raw_data);
if (cd == null)
return false;
if (cd.Method != Method)
return false;
_lastValue = cd.Value;
_lastData = cd;
return true;
}
public async Task DoAction(UpdateResult ur, MessageResult mr) => await Action(_lastValue, _lastData, ur, mr);
}
}
@@ -1,46 +0,0 @@
using TelegramBotBase.Base;
namespace TelegramBotBase.Experiments.ActionManager
{
public partial class ExternalActionManager
{
List<IExternalAction> actions = new List<IExternalAction>();
public void Add(IExternalAction action)
{
actions.Add(action);
}
public async Task<bool> ManageCall(UpdateResult ur, MessageResult mr)
{
foreach (var action in actions)
{
if (!action.DoesFit(mr.RawData))
continue;
await action.DoAction(ur, mr);
return true;
}
return false;
}
/// <summary>
/// Creates an instance of the ExternalActionManager for configuration.
/// </summary>
/// <param name="action"></param>
/// <returns></returns>
public static ExternalActionManager Configure(Action<ExternalActionManager> action)
{
var eam = new ExternalActionManager();
action(eam);
return eam;
}
}
}
@@ -1,13 +0,0 @@
using TelegramBotBase.Base;
namespace TelegramBotBase.Experiments.ActionManager
{
public interface IExternalAction
{
bool DoesFit(string raw_data);
Task DoAction(UpdateResult ur, MessageResult mr);
}
}
@@ -1,63 +0,0 @@
using System.Linq.Expressions;
using TelegramBotBase.Form;
namespace TelegramBotBase.Experiments.ActionManager.Navigation
{
public static class EndsWithNavigation_Extensions
{
public static void AddEndsWithNavigation<TForm>(this ExternalActionManager manager, string method, Expression<Func<TForm, string>> propertySelector)
where TForm : FormBase
{
if (!typeof(FormBase).IsAssignableFrom(typeof(TForm)))
{
throw new ArgumentException($"{nameof(TForm)} argument must be a {nameof(FormBase)} type");
}
var newValue = Expression.Parameter(propertySelector.Body.Type);
var assign = Expression.Lambda<Action<TForm, string>>(Expression.Assign(propertySelector.Body, newValue), propertySelector.Parameters[0], newValue);
var setter = assign.Compile(true);
manager.Add(new EndsWithNavigation<TForm>(method, setter));
}
public static void AddEndsWithNavigation<TForm>(this ExternalActionManager manager, string value, Action<TForm, string> setProperty)
where TForm : FormBase
{
if (!typeof(FormBase).IsAssignableFrom(typeof(TForm)))
{
throw new ArgumentException($"{nameof(TForm)} argument must be a {nameof(FormBase)} type");
}
manager.Add(new EndsWithNavigation<TForm>(value, setProperty));
}
public static void AddEndsWithNavigation(this ExternalActionManager manager, Type formType, string value, Expression<Func<FormBase, string>> propertySelector)
{
if (!typeof(FormBase).IsAssignableFrom(formType))
{
throw new ArgumentException($"{nameof(formType)} argument must be a {nameof(FormBase)} type");
}
var newValue = Expression.Parameter(propertySelector.Body.Type);
var assign = Expression.Lambda<Action<FormBase, string>>(Expression.Assign(propertySelector.Body, newValue), propertySelector.Parameters[0], newValue);
var setter = assign.Compile(true);
manager.Add(new EndsWithNavigation(formType, value, setter));
}
public static void AddEndsWithNavigation(this ExternalActionManager manager, Type formType, string value, Action<FormBase, string> setProperty)
{
if (!typeof(FormBase).IsAssignableFrom(formType))
{
throw new ArgumentException($"{nameof(formType)} argument must be a {nameof(FormBase)} type");
}
manager.Add(new EndsWithNavigation(formType, value, setProperty));
}
}
}
@@ -1,77 +0,0 @@
using TelegramBotBase.Base;
using TelegramBotBase.Form;
namespace TelegramBotBase.Experiments.ActionManager.Navigation
{
public class EndsWithNavigation : IExternalAction
{
public Type FormType { get; }
public string Value { get; set; }
public Action<FormBase, string> SetProperty { get; set; }
public EndsWithNavigation(Type formType, string value, Action<FormBase, string> setProperty)
{
FormType = formType;
Value = value;
SetProperty = setProperty;
}
public bool DoesFit(string raw_data) => raw_data.EndsWith(Value);
public async Task DoAction(UpdateResult ur, MessageResult mr)
{
await mr.ConfirmAction();
var new_form = FormType.GetConstructor(new Type[] { })?.Invoke(new object[] { }) as FormBase;
if (mr.RawData != null)
{
SetProperty(new_form, mr.RawData);
}
await ur.Device.ActiveForm.NavigateTo(new_form);
}
}
public class EndsWithNavigation<TForm> : IExternalAction
where TForm : FormBase
{
public string Value { get; set; }
public Action<TForm, string> SetProperty { get; set; }
public EndsWithNavigation(string value, Action<TForm, string> setProperty)
{
Value = value;
SetProperty = setProperty;
}
public bool DoesFit(string raw_data) => raw_data.EndsWith(Value);
public async Task DoAction(UpdateResult ur, MessageResult mr)
{
await mr.ConfirmAction();
var type = typeof(TForm);
TForm new_form = type.GetConstructor(new Type[] { })?.Invoke(new object[] { }) as TForm;
if (mr.RawData != null)
{
SetProperty(new_form, mr.RawData);
}
await ur.Device.ActiveForm.NavigateTo(new_form);
}
}
}
@@ -1,63 +0,0 @@
using System.Linq.Expressions;
using TelegramBotBase.Form;
namespace TelegramBotBase.Experiments.ActionManager.Navigation
{
public static class GuidNavigation_Extensions
{
public static void AddGuidNavigation<TForm>(this ExternalActionManager manager, string method, Expression<Func<TForm, Guid>> propertySelector)
where TForm : FormBase
{
if (!typeof(FormBase).IsAssignableFrom(typeof(TForm)))
{
throw new ArgumentException($"{nameof(TForm)} argument must be a {nameof(FormBase)} type");
}
var newValue = Expression.Parameter(propertySelector.Body.Type);
var assign = Expression.Lambda<Action<TForm, Guid>>(Expression.Assign(propertySelector.Body, newValue), propertySelector.Parameters[0], newValue);
var setter = assign.Compile(true);
manager.Add(new GuidNavigation<TForm>(method, setter));
}
public static void AddGuidNavigation<TForm>(this ExternalActionManager manager, string method, Action<TForm, Guid> action)
where TForm : FormBase
{
if (!typeof(FormBase).IsAssignableFrom(typeof(TForm)))
{
throw new ArgumentException($"{nameof(TForm)} argument must be a {nameof(FormBase)} type");
}
manager.Add(new GuidNavigation<TForm>(method, action));
}
public static void AddGuidNavigation(this ExternalActionManager manager, Type formType, string value, Expression<Func<FormBase, Guid>> propertySelector)
{
if (!typeof(FormBase).IsAssignableFrom(formType))
{
throw new ArgumentException($"{nameof(formType)} argument must be a {nameof(FormBase)} type");
}
var newValue = Expression.Parameter(propertySelector.Body.Type);
var assign = Expression.Lambda<Action<FormBase, Guid>>(Expression.Assign(propertySelector.Body, newValue), propertySelector.Parameters[0], newValue);
var setter = assign.Compile(true);
manager.Add(new GuidNavigation(formType, value, setter));
}
public static void AddGuidNavigation(this ExternalActionManager manager, Type formType, string method, Action<FormBase, Guid> action)
{
if (!typeof(FormBase).IsAssignableFrom(formType))
{
throw new ArgumentException($"{nameof(formType)} argument must be a {nameof(FormBase)} type");
}
manager.Add(new GuidNavigation(formType, method, action));
}
}
}
@@ -1,106 +0,0 @@
using TelegramBotBase.Base;
using TelegramBotBase.Form;
namespace TelegramBotBase.Experiments.ActionManager.Navigation
{
public class GuidNavigation : IExternalAction
{
public Type FormType { get; }
public string Method { get; set; }
public Action<FormBase, Guid> SetProperty { get; set; }
Guid? _lastValue { get; set; }
public GuidNavigation(Type formType, string method, Action<FormBase, Guid> setProperty)
{
FormType = formType;
Method = method;
SetProperty = setProperty;
}
public bool DoesFit(string raw_action)
{
var cd = CallbackData.Deserialize(raw_action);
if (cd == null)
return false;
if (cd.Method != Method)
return false;
Guid g;
if (Guid.TryParse(cd.Value, out g))
_lastValue = g;
return true;
}
public async Task DoAction(UpdateResult ur, MessageResult mr)
{
await mr.ConfirmAction();
var new_form = FormType.GetConstructor(new Type[] { })?.Invoke(new object[] { }) as FormBase;
if (_lastValue != null)
SetProperty(new_form, _lastValue.Value);
await ur.Device.ActiveForm.NavigateTo(new_form);
}
public static CallbackData GetCallback(string method, Guid guid) => new CallbackData(method, guid.ToString());
}
public class GuidNavigation<TForm> : IExternalAction
where TForm : FormBase
{
public string Method { get; set; }
public Action<TForm, Guid> SetProperty { get; set; }
Guid? _lastValue { get; set; }
public GuidNavigation(string method, Action<TForm, Guid> setProperty)
{
Method = method;
SetProperty = setProperty;
}
public bool DoesFit(string raw_data)
{
var cd = CallbackData.Deserialize(raw_data);
if (cd == null)
return false;
if (cd.Method != Method)
return false;
Guid g;
if (Guid.TryParse(cd.Value, out g))
_lastValue = g;
return true;
}
public async Task DoAction(UpdateResult ur, MessageResult mr)
{
await mr.ConfirmAction();
var type = typeof(TForm);
TForm new_form = type.GetConstructor(new Type[] { })?.Invoke(new object[] { }) as TForm;
if (_lastValue != null)
SetProperty(new_form, _lastValue.Value);
await ur.Device.ActiveForm.NavigateTo(new_form);
}
}
}
@@ -1,63 +0,0 @@
using System.Linq.Expressions;
using TelegramBotBase.Form;
namespace TelegramBotBase.Experiments.ActionManager.Navigation
{
public static class Int32Navigation_Extensions
{
public static void AddInt32Navigation<TForm>(this ExternalActionManager manager, string method, Expression<Func<TForm, int>> propertySelector)
where TForm : FormBase
{
if (!typeof(FormBase).IsAssignableFrom(typeof(TForm)))
{
throw new ArgumentException($"{nameof(TForm)} argument must be a {nameof(FormBase)} type");
}
var newValue = Expression.Parameter(propertySelector.Body.Type);
var assign = Expression.Lambda<Action<TForm, int>>(Expression.Assign(propertySelector.Body, newValue), propertySelector.Parameters[0], newValue);
var setter = assign.Compile(true);
manager.Add(new Int32Navigation<TForm>(method, setter));
}
public static void AddInt32Navigation<TForm>(this ExternalActionManager manager, string method, Action<TForm, int> action)
where TForm : FormBase
{
if (!typeof(FormBase).IsAssignableFrom(typeof(TForm)))
{
throw new ArgumentException($"{nameof(TForm)} argument must be a {nameof(FormBase)} type");
}
manager.Add(new Int32Navigation<TForm>(method, action));
}
public static void AddInt32Navigation(this ExternalActionManager manager, Type formType, string value, Expression<Func<FormBase, int>> propertySelector)
{
if (!typeof(FormBase).IsAssignableFrom(formType))
{
throw new ArgumentException($"{nameof(formType)} argument must be a {nameof(FormBase)} type");
}
var newValue = Expression.Parameter(propertySelector.Body.Type);
var assign = Expression.Lambda<Action<FormBase, int>>(Expression.Assign(propertySelector.Body, newValue), propertySelector.Parameters[0], newValue);
var setter = assign.Compile(true);
manager.Add(new Int32Navigation(formType, value, setter));
}
public static void AddInt32Navigation(this ExternalActionManager manager, Type formType, string method, Action<FormBase, int> action)
{
if (!typeof(FormBase).IsAssignableFrom(formType))
{
throw new ArgumentException($"{nameof(formType)} argument must be a {nameof(FormBase)} type");
}
manager.Add(new Int32Navigation(formType, method, action));
}
}
}
@@ -1,106 +0,0 @@
using TelegramBotBase.Base;
using TelegramBotBase.Form;
namespace TelegramBotBase.Experiments.ActionManager.Navigation
{
public class Int32Navigation : IExternalAction
{
public Type FormType { get; }
public string Method { get; set; }
public Action<FormBase, int> SetProperty { get; set; }
int? _lastValue { get; set; }
public Int32Navigation(Type formType, string method, Action<FormBase, int> setProperty)
{
FormType = formType;
Method = method;
SetProperty = setProperty;
}
public bool DoesFit(string raw_action)
{
var cd = CallbackData.Deserialize(raw_action);
if (cd == null)
return false;
if (cd.Method != Method)
return false;
int i;
if (int.TryParse(cd.Value, out i))
_lastValue = i;
return true;
}
public async Task DoAction(UpdateResult ur, MessageResult mr)
{
await mr.ConfirmAction();
var new_form = FormType.GetConstructor(new Type[] { })?.Invoke(new object[] { }) as FormBase;
if (_lastValue != null)
SetProperty(new_form, _lastValue.Value);
await ur.Device.ActiveForm.NavigateTo(new_form);
}
public static CallbackData GetCallback(string method, int i) => new CallbackData(method, i.ToString());
}
public class Int32Navigation<TForm> : IExternalAction
where TForm : FormBase
{
public string Method { get; set; }
public Action<TForm, int> SetProperty { get; set; }
int? _lastValue { get; set; }
public Int32Navigation(string method, Action<TForm, int> setProperty)
{
Method = method;
SetProperty = setProperty;
}
public bool DoesFit(string raw_data)
{
var cd = CallbackData.Deserialize(raw_data);
if (cd == null)
return false;
if (cd.Method != Method)
return false;
int g;
if (int.TryParse(cd.Value, out g))
_lastValue = g;
return true;
}
public async Task DoAction(UpdateResult ur, MessageResult mr)
{
await mr.ConfirmAction();
var type = typeof(TForm);
TForm new_form = type.GetConstructor(new Type[] { })?.Invoke(new object[] { }) as TForm;
if (_lastValue != null)
SetProperty(new_form, _lastValue.Value);
await ur.Device.ActiveForm.NavigateTo(new_form);
}
}
}
@@ -1,63 +0,0 @@
using System.Linq.Expressions;
using TelegramBotBase.Form;
namespace TelegramBotBase.Experiments.ActionManager.Navigation
{
public static class Int64Navigation_Extensions
{
public static void AddInt64Navigation<TForm>(this ExternalActionManager manager, string method, Expression<Func<TForm, long>> propertySelector)
where TForm : FormBase
{
if (!typeof(FormBase).IsAssignableFrom(typeof(TForm)))
{
throw new ArgumentException($"{nameof(TForm)} argument must be a {nameof(FormBase)} type");
}
var newValue = Expression.Parameter(propertySelector.Body.Type);
var assign = Expression.Lambda<Action<TForm, long>>(Expression.Assign(propertySelector.Body, newValue), propertySelector.Parameters[0], newValue);
var setter = assign.Compile(true);
manager.Add(new Int64Navigation<TForm>(method, setter));
}
public static void AddInt64Navigation<TForm>(this ExternalActionManager manager, string method, Action<TForm, long> action)
where TForm : FormBase
{
if (!typeof(FormBase).IsAssignableFrom(typeof(TForm)))
{
throw new ArgumentException($"{nameof(TForm)} argument must be a {nameof(FormBase)} type");
}
manager.Add(new Int64Navigation<TForm>(method, action));
}
public static void AddInt64Navigation(this ExternalActionManager manager, Type formType, string value, Expression<Func<FormBase, long>> propertySelector)
{
if (!typeof(FormBase).IsAssignableFrom(formType))
{
throw new ArgumentException($"{nameof(formType)} argument must be a {nameof(FormBase)} type");
}
var newValue = Expression.Parameter(propertySelector.Body.Type);
var assign = Expression.Lambda<Action<FormBase, long>>(Expression.Assign(propertySelector.Body, newValue), propertySelector.Parameters[0], newValue);
var setter = assign.Compile(true);
manager.Add(new Int64Navigation(formType, value, setter));
}
public static void AddInt64Navigation(this ExternalActionManager manager, Type formType, string method, Action<FormBase, long> action)
{
if (!typeof(FormBase).IsAssignableFrom(formType))
{
throw new ArgumentException($"{nameof(formType)} argument must be a {nameof(FormBase)} type");
}
manager.Add(new Int64Navigation(formType, method, action));
}
}
}
@@ -1,106 +0,0 @@
using TelegramBotBase.Base;
using TelegramBotBase.Form;
namespace TelegramBotBase.Experiments.ActionManager.Navigation
{
public class Int64Navigation : IExternalAction
{
public Type FormType { get; }
public string Method { get; set; }
public Action<FormBase, long> SetProperty { get; set; }
long? _lastValue { get; set; }
public Int64Navigation(Type formType, string method, Action<FormBase, long> setProperty)
{
FormType = formType;
Method = method;
SetProperty = setProperty;
}
public bool DoesFit(string raw_action)
{
var cd = CallbackData.Deserialize(raw_action);
if (cd == null)
return false;
if (cd.Method != Method)
return false;
long l;
if (long.TryParse(cd.Value, out l))
_lastValue = l;
return true;
}
public async Task DoAction(UpdateResult ur, MessageResult mr)
{
await mr.ConfirmAction();
var new_form = FormType.GetConstructor(new Type[] { })?.Invoke(new object[] { }) as FormBase;
if (_lastValue != null)
SetProperty(new_form, _lastValue.Value);
await ur.Device.ActiveForm.NavigateTo(new_form);
}
public static CallbackData GetCallback(string method, long l) => new CallbackData(method, l.ToString());
}
public class Int64Navigation<TForm> : IExternalAction
where TForm : FormBase
{
public string Method { get; set; }
public Action<TForm, long> SetProperty { get; set; }
long? _lastValue { get; set; }
public Int64Navigation(string method, Action<TForm, long> setProperty)
{
Method = method;
SetProperty = setProperty;
}
public bool DoesFit(string raw_data)
{
var cd = CallbackData.Deserialize(raw_data);
if (cd == null)
return false;
if (cd.Method != Method)
return false;
long l;
if (long.TryParse(cd.Value, out l))
_lastValue = l;
return true;
}
public async Task DoAction(UpdateResult ur, MessageResult mr)
{
await mr.ConfirmAction();
var type = typeof(TForm);
TForm new_form = type.GetConstructor(new Type[] { })?.Invoke(new object[] { }) as TForm;
if (_lastValue != null)
SetProperty(new_form, _lastValue.Value);
await ur.Device.ActiveForm.NavigateTo(new_form);
}
}
}
@@ -1,63 +0,0 @@
using System.Linq.Expressions;
using TelegramBotBase.Form;
namespace TelegramBotBase.Experiments.ActionManager.Navigation
{
public static class StartWithNavigation_Extensions
{
public static void AddStartsWithNavigation<TForm>(this ExternalActionManager manager, string method, Expression<Func<TForm, string>> propertySelector)
where TForm : FormBase
{
if (!typeof(FormBase).IsAssignableFrom(typeof(TForm)))
{
throw new ArgumentException($"{nameof(TForm)} argument must be a {nameof(FormBase)} type");
}
var newValue = Expression.Parameter(propertySelector.Body.Type);
var assign = Expression.Lambda<Action<TForm, string>>(Expression.Assign(propertySelector.Body, newValue), propertySelector.Parameters[0], newValue);
var setter = assign.Compile(true);
manager.Add(new StartWithNavigation<TForm>(method, setter));
}
public static void AddStartsWithNavigation<TForm>(this ExternalActionManager manager, string value, Action<TForm, string> setProperty)
where TForm : FormBase
{
if (!typeof(FormBase).IsAssignableFrom(typeof(TForm)))
{
throw new ArgumentException($"{nameof(TForm)} argument must be a {nameof(FormBase)} type");
}
manager.Add(new StartWithNavigation<TForm>(value, setProperty));
}
public static void AddStartsWithNavigation(this ExternalActionManager manager, Type formType, string value, Expression<Func<FormBase, string>> propertySelector)
{
if (!typeof(FormBase).IsAssignableFrom(formType))
{
throw new ArgumentException($"{nameof(formType)} argument must be a {nameof(FormBase)} type");
}
var newValue = Expression.Parameter(propertySelector.Body.Type);
var assign = Expression.Lambda<Action<FormBase, string>>(Expression.Assign(propertySelector.Body, newValue), propertySelector.Parameters[0], newValue);
var setter = assign.Compile(true);
manager.Add(new StartWithNavigation(formType, value, setter));
}
public static void AddStartsWithNavigation(this ExternalActionManager manager, Type formType, string value, Action<FormBase, string> setProperty)
{
if (!typeof(FormBase).IsAssignableFrom(formType))
{
throw new ArgumentException($"{nameof(formType)} argument must be a {nameof(FormBase)} type");
}
manager.Add(new StartWithNavigation(formType, value, setProperty));
}
}
}
@@ -1,74 +0,0 @@
using TelegramBotBase.Base;
using TelegramBotBase.Form;
namespace TelegramBotBase.Experiments.ActionManager.Navigation
{
public class StartWithNavigation : IExternalAction
{
public Type FormType { get; }
public string Value { get; set; }
public Action<FormBase, string> SetProperty { get; set; }
public StartWithNavigation(Type formType, string value, Action<FormBase, string> setProperty)
{
FormType = formType;
Value = value;
SetProperty = setProperty;
}
public bool DoesFit(string raw_data) => raw_data.StartsWith(Value);
public async Task DoAction(UpdateResult ur, MessageResult mr)
{
await mr.ConfirmAction();
var new_form = FormType.GetConstructor(new Type[] { })?.Invoke(new object[] { }) as FormBase;
if (mr.RawData != null)
{
SetProperty(new_form, mr.RawData);
}
await ur.Device.ActiveForm.NavigateTo(new_form);
}
}
public class StartWithNavigation<TForm> : IExternalAction
where TForm : FormBase
{
public string Value { get; set; }
public Action<TForm, string> SetProperty { get; set; }
public StartWithNavigation(string value, Action<TForm, string> setProperty)
{
Value = value;
SetProperty = setProperty;
}
public bool DoesFit(string raw_data) => raw_data.StartsWith(Value);
public async Task DoAction(UpdateResult ur, MessageResult mr)
{
await mr.ConfirmAction();
var type = typeof(TForm);
TForm new_form = type.GetConstructor(new Type[] { })?.Invoke(new object[] { }) as TForm;
if (mr.RawData != null)
{
SetProperty(new_form, mr.RawData);
}
await ur.Device.ActiveForm.NavigateTo(new_form);
}
}
}
@@ -1,63 +0,0 @@
using System.Linq.Expressions;
using TelegramBotBase.Form;
namespace TelegramBotBase.Experiments.ActionManager.Navigation
{
public static class StringNavigation_Extensions
{
public static void AddStringNavigation<TForm>(this ExternalActionManager manager, string method, Expression<Func<TForm, String>> propertySelector)
where TForm : FormBase
{
if (!typeof(FormBase).IsAssignableFrom(typeof(TForm)))
{
throw new ArgumentException($"{nameof(TForm)} argument must be a {nameof(FormBase)} type");
}
var newValue = Expression.Parameter(propertySelector.Body.Type);
var assign = Expression.Lambda<Action<TForm, String>>(Expression.Assign(propertySelector.Body, newValue), propertySelector.Parameters[0], newValue);
var setter = assign.Compile(true);
manager.Add(new StringNavigation<TForm>(method, setter));
}
public static void AddStringNavigation<TForm>(this ExternalActionManager manager, string method, Action<TForm, String> action)
where TForm : FormBase
{
if (!typeof(FormBase).IsAssignableFrom(typeof(TForm)))
{
throw new ArgumentException($"{nameof(TForm)} argument must be a {nameof(FormBase)} type");
}
manager.Add(new StringNavigation<TForm>(method, action));
}
public static void AddStringNavigation(this ExternalActionManager manager, Type formType, string value, Expression<Func<FormBase, String>> propertySelector)
{
if (!typeof(FormBase).IsAssignableFrom(formType))
{
throw new ArgumentException($"{nameof(formType)} argument must be a {nameof(FormBase)} type");
}
var newValue = Expression.Parameter(propertySelector.Body.Type);
var assign = Expression.Lambda<Action<FormBase, String>>(Expression.Assign(propertySelector.Body, newValue), propertySelector.Parameters[0], newValue);
var setter = assign.Compile(true);
manager.Add(new StringNavigation(formType, value, setter));
}
public static void AddStringNavigation(this ExternalActionManager manager, Type formType, string method, Action<FormBase, String> action)
{
if (!typeof(FormBase).IsAssignableFrom(formType))
{
throw new ArgumentException($"{nameof(formType)} argument must be a {nameof(FormBase)} type");
}
manager.Add(new StringNavigation(formType, method, action));
}
}
}
@@ -1,100 +0,0 @@
using TelegramBotBase.Base;
using TelegramBotBase.Form;
namespace TelegramBotBase.Experiments.ActionManager.Navigation
{
public class StringNavigation : IExternalAction
{
public Type FormType { get; }
public string Method { get; set; }
public Action<FormBase, String> SetProperty { get; set; }
String? _lastValue { get; set; }
public StringNavigation(Type formType, string method, Action<FormBase, String> setProperty)
{
FormType = formType;
Method = method;
SetProperty = setProperty;
}
public bool DoesFit(string raw_action)
{
var cd = CallbackData.Deserialize(raw_action);
if (cd == null)
return false;
if (cd.Method != Method)
return false;
_lastValue = cd.Value;
return true;
}
public async Task DoAction(UpdateResult ur, MessageResult mr)
{
await mr.ConfirmAction();
var new_form = FormType.GetConstructor(new Type[] { })?.Invoke(new object[] { }) as FormBase;
if (_lastValue != null)
SetProperty(new_form, _lastValue);
await ur.Device.ActiveForm.NavigateTo(new_form);
}
public static CallbackData GetCallback(string method, String str) => new CallbackData(method, str);
}
public class StringNavigation<TForm> : IExternalAction
where TForm : FormBase
{
public string Method { get; set; }
public Action<TForm, String> SetProperty { get; set; }
String? _lastValue { get; set; }
public StringNavigation(string method, Action<TForm, String> setProperty)
{
Method = method;
SetProperty = setProperty;
}
public bool DoesFit(string raw_data)
{
var cd = CallbackData.Deserialize(raw_data);
if (cd == null)
return false;
if (cd.Method != Method)
return false;
_lastValue = cd.Value;
return true;
}
public async Task DoAction(UpdateResult ur, MessageResult mr)
{
await mr.ConfirmAction();
var type = typeof(TForm);
TForm new_form = type.GetConstructor(new Type[] { })?.Invoke(new object[] { }) as TForm;
if (_lastValue != null)
SetProperty(new_form, _lastValue);
await ur.Device.ActiveForm.NavigateTo(new_form);
}
}
}
@@ -1,146 +0,0 @@
using System.ComponentModel;
using Telegram.Bot.Types.Enums;
using TelegramBotBase;
using TelegramBotBase.Args;
using TelegramBotBase.Base;
using TelegramBotBase.Interfaces;
using TelegramBotBase.Sessions;
namespace TelegramBotBase.Experiments.ActionManager
{
public class CustomFormBaseMessageLoop : IMessageLoopFactory
{
private static readonly object EvUnhandledCall = new();
private readonly EventHandlerList _events = new();
public ExternalActionManager ExternalActionManager { get; set; }
public async Task MessageLoop(BotBase bot, DeviceSession s, UpdateResult ur, MessageResult mr)
{
var update = ur.RawData;
if (update.Type != UpdateType.Message
&& update.Type != UpdateType.EditedMessage
&& update.Type != UpdateType.CallbackQuery)
{
return;
}
//Remove unecessary parameter from method call in the future
var session = ur.Device;
//Is this a bot command ?
if (mr.IsFirstHandler && mr.IsBotCommand && bot.IsKnownBotCommand(mr.BotCommand))
{
var sce = new BotCommandEventArgs(mr.BotCommand, mr.BotCommandParameters, mr.Message, session.DeviceId,
session);
await bot.OnBotCommand(sce);
if (sce.Handled)
{
return;
}
}
mr.Device = session;
ur.Device = session;
var activeForm = session.ActiveForm;
//Pre Loading Event
await activeForm.PreLoad(mr);
//Send Load event to controls
await activeForm.LoadControls(mr);
//Loading Event
await activeForm.Load(mr);
//Is Attachment ? (Photo, Audio, Video, Contact, Location, Document) (Ignore Callback Queries)
if (update.Type == UpdateType.Message)
{
if ((mr.MessageType == MessageType.Contact)
| (mr.MessageType == MessageType.Document)
| (mr.MessageType == MessageType.Location)
| (mr.MessageType == MessageType.Photo)
| (mr.MessageType == MessageType.Video)
| (mr.MessageType == MessageType.Audio))
{
await activeForm.SentData(new DataResult(ur));
}
}
//Message edited ?
if (update.Type == UpdateType.EditedMessage)
{
await activeForm.Edited(mr);
}
//Action Event
if (!session.FormSwitched && mr.IsAction)
{
//Send Action event to controls
await activeForm.ActionControls(mr);
//Send Action event to form itself
await activeForm.Action(mr);
if (!mr.Handled)
{
var handled = await ExternalActionManager?.ManageCall(ur, mr);
if (handled)
{
mr.Handled = true;
if (!session.FormSwitched)
{
return;
}
}
else
{
var uhc = new UnhandledCallEventArgs(ur.Message.Text, mr.RawData, session.DeviceId, mr.MessageId, ur.Message, session);
OnUnhandledCall(uhc);
if (uhc.Handled)
{
mr.Handled = true;
if (!session.FormSwitched)
{
return;
}
}
}
}
}
if (!session.FormSwitched)
{
//Render Event
await activeForm.RenderControls(mr);
await activeForm.Render(mr);
}
}
/// <summary>
/// Will be called if no form handled this call
/// </summary>
public event EventHandler<UnhandledCallEventArgs> UnhandledCall
{
add => _events.AddHandler(EvUnhandledCall, value);
remove => _events.RemoveHandler(EvUnhandledCall, value);
}
public void OnUnhandledCall(UnhandledCallEventArgs e)
{
(_events[EvUnhandledCall] as EventHandler<UnhandledCallEventArgs>)?.Invoke(this, e);
}
}
}
@@ -1,26 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TelegramBotBase.Builder.Interfaces;
using TelegramBotBase.Interfaces;
namespace TelegramBotBase.Experiments.ActionManager
{
public static class StaticExtensions
{
public static IStartFormSelectionStage ActionMessageLoop(this IMessageLoopSelectionStage loopSelection, ExternalActionManager managerInstance)
{
var cfb = new CustomFormBaseMessageLoop();
cfb.ExternalActionManager = managerInstance;
return loopSelection.CustomMessageLoop(cfb);
}
}
}
@@ -1,13 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\TelegramBotBase\TelegramBotBase.csproj" />
</ItemGroup>
</Project>
+94 -32
View File
@@ -1,27 +1,6 @@
# Development Lab
Attention: This is a lab for **experimental features**. Here we can try out different ideas and scenarios if they will work and what can be improved.
There is no guarantee that any of these ideas comes in the main project.
Please try everything on your own. Nothing can work, so everything can as well.
You will find all experiments within the ["Experiments"](Experiments) sub folder.
For feedback please join our Telegram group.
**Support group: [@tgbotbase](https://t.me/tgbotbase)**
## current experiments
- [ExternalActionManager](Experiments/ExternalActionManager/DemoBot) => Manager for unhandled actions from "outside"
---
# .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)
[![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)
@@ -35,6 +14,10 @@ For feedback please join our Telegram group.
**Releases: [GitHub](https://github.com/MajMcCloud/TelegramBotFramework/releases)**
**Need your own bot? Get in touch https://t.me/botbasebuilder**
**on X: @florian_zevedei**
## Donate
Paypal: [https://paypal.me/majmccloud](https://paypal.me/majmccloud)
@@ -123,6 +106,7 @@ var bot = BotBaseBuilder
})
.NoSerialization()
.UseEnglish()
.UseSingleThread()
.Build();
// Upload bot commands to BotFather
@@ -143,8 +127,32 @@ like ChatId and other stuff your carrying.
From there you build up your bots:
```csharp
public class StartForm : FormBase
public class Start : FormBase
{
public Start()
{
//Additional event handlers
Init += Start_Init;
Opened += Start_Opened;
Closed += Start_Closed;
}
// Gets invoked on initialization, before navigation
private async Task Start_Init(object sender, Args.InitEventArgs e)
{
}
// Gets invoked after opened
private async Task Start_Opened(object sender, EventArgs e)
{
}
// Gets invoked after form has been closed
private async Task Start_Closed(object sender, EventArgs e)
{
}
// Gets invoked during Navigation to this form
public override async Task PreLoad(MessageResult message)
{
@@ -241,6 +249,7 @@ var bot = BotBaseBuilder
})
.NoSerialization()
.UseEnglish()
.UseSingleThread()
.Build();
bot.BotCommand += async (s, en) =>
@@ -281,15 +290,15 @@ public class SimpleForm : AutoCleanForm
{
public SimpleForm()
{
this.DeleteSide = TelegramBotBase.Enums.eDeleteSide.Both;
this.DeleteMode = TelegramBotBase.Enums.eDeleteMode.OnLeavingForm;
DeleteSide = EDeleteSide.Both;
DeleteMode = EDeleteMode.OnLeavingForm;
this.Opened += SimpleForm_Opened;
Opened += SimpleForm_Opened;
}
private async Task SimpleForm_Opened(object sender, EventArgs e)
{
await this.Device.Send("Hello world! (send 'back' to get back to Start)\r\nOr\r\nhi, hello, maybe, bye and ciao");
await Device.Send("Hello world! (send 'back' to get back to Start)\r\nOr\r\nhi, hello, maybe, bye and ciao");
}
public override async Task Load(MessageResult message)
@@ -327,7 +336,13 @@ public class SimpleForm : AutoCleanForm
```csharp
public class ButtonTestForm : AutoCleanForm
{
public override async Task Opened()
public ButtonTestForm()
{
this.DeleteMode = eDeleteMode.OnLeavingForm;
Opened += ButtonTestForm_Opened;
}
private async Task ButtonTestForm_Opened(object sender, EventArgs e)
{
await this.Device.Send("Hello world! (Click 'back' to get back to Start)");
}
@@ -397,9 +412,10 @@ public class ProgressTest : AutoCleanForm
public ProgressTest()
{
this.DeleteMode = eDeleteMode.OnLeavingForm;
Opened += ProgressTest_Opened;
}
public override async Task Opened()
private async Task ProgressTest_Opened(object sender, EventArgs e)
{
await this.Device.Send("Welcome to ProgressTest");
}
@@ -744,6 +760,7 @@ 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.
@@ -878,6 +895,7 @@ var bot = BotBaseBuilder
})
.UseSimpleJSON(AppContext.BaseDirectory + "config\\states.json")
.UseEnglish()
.UseSingleThread()
.Build();
await bot.Start();
@@ -901,6 +919,7 @@ var bot = BotBaseBuilder
})
.UseJSON(AppContext.BaseDirectory + "config\\states.json")
.UseEnglish()
.UseSingleThread()
.Build();
await bot.Start();
@@ -923,6 +942,7 @@ var bot = BotBaseBuilder
})
.UseXML(AppContext.BaseDirectory + "config\\states.xml")
.UseEnglish()
.UseSingleThread()
.Build();
await bot.Start();
@@ -936,7 +956,7 @@ datatype and one for implementing into a form which should be invoked with event
#### IStateMachine
Is the basic StateMachine interface, it has two methods `SaveFormStates(SaveStatesEventArgs e)`
and `StateContainerLoadFormStates()`, nothing fancy, just simple calls. Implement both methods with your own
and `LoadFormStates()`, nothing fancy, just simple calls. Implement both methods with your own
serialization process.
```csharp
@@ -1060,6 +1080,8 @@ again at 1 (due to `PopAsync` or `PopToRootAsync` calls) it will replace the con
you
have given to the constructor at the beginning.*
---
## Extensions
### TelegramBotBase.Extensions.Images
@@ -1069,7 +1091,28 @@ 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/)
[![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/)
@@ -1093,7 +1136,26 @@ 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/)
[![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/)
@@ -14,7 +14,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="IronSoftware.System.Drawing" Version="2024.1.1" />
<PackageReference Include="IronSoftware.System.Drawing" Version="2024.5.1" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
@@ -16,7 +16,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Data.SqlClient" Version="5.1.3" />
<PackageReference Include="Microsoft.Data.SqlClient" Version="5.2.0" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1">
<PrivateAssets>all</PrivateAssets>
<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.3" />
</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();
@@ -8,8 +8,11 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="TelegramBotBase" Version="6.4.0-preview.1" />
<PackageReference Include="TelegramBotBase.Extensions.Images" Version="1.1.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\TelegramBotBase\TelegramBotBase.csproj" />
</ItemGroup>
</Project>
+14 -14
View File
@@ -55,7 +55,7 @@ public class DataResult : ResultBase
{
var encryptedContent = new MemoryStream();
encryptedContent.SetLength(Document.FileSize.Value);
var file = await Device.Client.TelegramClient.GetInfoAndDownloadFileAsync(Document.FileId,
var file = await Device.Client.TelegramClient.GetInfoAndDownloadFile(Document.FileId,
encryptedContent);
return InputFile.FromStream(encryptedContent, Document.FileName);
@@ -69,9 +69,9 @@ public class DataResult : ResultBase
/// <returns></returns>
public async Task DownloadDocument(string path)
{
var file = await Device.Client.TelegramClient.GetFileAsync(Document.FileId);
var file = await Device.Client.TelegramClient.GetFile(Document.FileId);
var fs = new FileStream(path, FileMode.Create);
await Device.Client.TelegramClient.DownloadFileAsync(file.FilePath, fs);
await Device.Client.TelegramClient.DownloadFile(file.FilePath, fs);
fs.Close();
fs.Dispose();
}
@@ -83,7 +83,7 @@ public class DataResult : ResultBase
public async Task<byte[]> DownloadRawDocument()
{
var ms = new MemoryStream();
await Device.Client.TelegramClient.GetInfoAndDownloadFileAsync(Document.FileId, ms);
await Device.Client.TelegramClient.GetInfoAndDownloadFile(Document.FileId, ms);
return ms.ToArray();
}
@@ -103,7 +103,7 @@ public class DataResult : ResultBase
public async Task<string> DownloadRawTextDocument(Encoding encoding)
{
var ms = new MemoryStream();
await Device.Client.TelegramClient.GetInfoAndDownloadFileAsync(Document.FileId, ms);
await Device.Client.TelegramClient.GetInfoAndDownloadFile(Document.FileId, ms);
ms.Position = 0;
@@ -116,16 +116,16 @@ public class DataResult : ResultBase
{
var encryptedContent = new MemoryStream();
encryptedContent.SetLength(Video.FileSize.Value);
var file = await Device.Client.TelegramClient.GetInfoAndDownloadFileAsync(Video.FileId, encryptedContent);
var file = await Device.Client.TelegramClient.GetInfoAndDownloadFile(Video.FileId, encryptedContent);
return InputFile.FromStream(encryptedContent, "");
}
public async Task DownloadVideo(string path)
{
var file = await Device.Client.TelegramClient.GetFileAsync(Video.FileId);
var file = await Device.Client.TelegramClient.GetFile(Video.FileId);
var fs = new FileStream(path, FileMode.Create);
await Device.Client.TelegramClient.DownloadFileAsync(file.FilePath, fs);
await Device.Client.TelegramClient.DownloadFile(file.FilePath, fs);
fs.Close();
fs.Dispose();
}
@@ -134,16 +134,16 @@ public class DataResult : ResultBase
{
var encryptedContent = new MemoryStream();
encryptedContent.SetLength(Audio.FileSize.Value);
var file = await Device.Client.TelegramClient.GetInfoAndDownloadFileAsync(Audio.FileId, encryptedContent);
var file = await Device.Client.TelegramClient.GetInfoAndDownloadFile(Audio.FileId, encryptedContent);
return InputFile.FromStream(encryptedContent, "");
}
public async Task DownloadAudio(string path)
{
var file = await Device.Client.TelegramClient.GetFileAsync(Audio.FileId);
var file = await Device.Client.TelegramClient.GetFile(Audio.FileId);
var fs = new FileStream(path, FileMode.Create);
await Device.Client.TelegramClient.DownloadFileAsync(file.FilePath, fs);
await Device.Client.TelegramClient.DownloadFile(file.FilePath, fs);
fs.Close();
fs.Dispose();
}
@@ -153,7 +153,7 @@ public class DataResult : ResultBase
var photo = Photos[index];
var encryptedContent = new MemoryStream();
encryptedContent.SetLength(photo.FileSize.Value);
var file = await Device.Client.TelegramClient.GetInfoAndDownloadFileAsync(photo.FileId, encryptedContent);
var file = await Device.Client.TelegramClient.GetInfoAndDownloadFile(photo.FileId, encryptedContent);
return InputFile.FromStream(encryptedContent, "");
}
@@ -161,9 +161,9 @@ public class DataResult : ResultBase
public async Task DownloadPhoto(int index, string path)
{
var photo = Photos[index];
var file = await Device.Client.TelegramClient.GetFileAsync(photo.FileId);
var file = await Device.Client.TelegramClient.GetFile(photo.FileId);
var fs = new FileStream(path, FileMode.Create);
await Device.Client.TelegramClient.DownloadFileAsync(file.FilePath, fs);
await Device.Client.TelegramClient.DownloadFile(file.FilePath, fs);
fs.Close();
fs.Dispose();
}
+1 -1
View File
@@ -446,8 +446,8 @@ public class FormBase : IDisposable
{
c.Cleanup().Wait();
Controls.Remove(c);
}
Controls.Clear();
}
/// <summary>
+20 -77
View File
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.ComponentModel;
using System.Net;
using System.Net.Http;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using Telegram.Bot;
@@ -10,6 +11,7 @@ using Telegram.Bot.Exceptions;
using Telegram.Bot.Polling;
using Telegram.Bot.Types;
namespace TelegramBotBase.Base;
/// <summary>
@@ -17,22 +19,18 @@ namespace TelegramBotBase.Base;
/// </summary>
public class MessageClient
{
private EventHandlerList Events { get; } = new();
private static readonly object EvOnMessageLoop = new();
private static readonly object EvOnReceiveError = new();
private static object __evOnMessage = new();
private static object __evOnMessageEdit = new();
private static object __evCallbackQuery = new();
private CancellationTokenSource _cancellationTokenSource;
protected CancellationTokenSource _cancellationTokenSource;
public string ApiKey { get; }
public ITelegramBotClient TelegramClient { get; set; }
private EventHandlerList Events { get; } = new();
/// <summary>
/// Indicates if all pending Telegram.Bot.Types.Updates should be thrown out before
@@ -42,27 +40,17 @@ public class MessageClient
/// </summary>
public bool ThrowPendingUpdates { get; set; }
public bool UseThreadPool { get; set; } = false;
public int ThreadPool_WorkerThreads { get; set; } = 1;
public int ThreadPool_IOThreads { get; set; } = 1;
public MessageClient(string apiKey)
{
ApiKey = apiKey;
TelegramClient = new TelegramBotClient(apiKey);
Prepare();
}
public MessageClient(string apiKey, HttpClient proxy)
{
ApiKey = apiKey;
TelegramClient = new TelegramBotClient(apiKey, proxy);
Prepare();
}
@@ -80,8 +68,6 @@ public class MessageClient
);
TelegramClient = new TelegramBotClient(apiKey, httpClient);
Prepare();
}
/// <summary>
@@ -101,8 +87,6 @@ public class MessageClient
);
TelegramClient = new TelegramBotClient(apiKey, httpClient);
Prepare();
}
@@ -110,87 +94,41 @@ public class MessageClient
{
ApiKey = apiKey;
TelegramClient = client;
Prepare();
}
public void Prepare()
{
TelegramClient.Timeout = new TimeSpan(0, 0, 30);
}
public void StartReceiving()
public virtual void StartReceiving()
{
_cancellationTokenSource = new CancellationTokenSource();
var receiverOptions = new ReceiverOptions();
receiverOptions.ThrowPendingUpdates = ThrowPendingUpdates;
if (UseThreadPool)
{
ThreadPool.SetMaxThreads(ThreadPool_WorkerThreads, ThreadPool_IOThreads);
TelegramClient.StartReceiving(HandleUpdateAsyncThreadPool, HandleErrorAsyncThreadPool, receiverOptions,
_cancellationTokenSource.Token);
}
else
{
TelegramClient.StartReceiving(HandleUpdateAsync, HandleErrorAsync, receiverOptions,
_cancellationTokenSource.Token);
}
receiverOptions.DropPendingUpdates = ThrowPendingUpdates;
TelegramClient.StartReceiving(HandleUpdateAsync, HandleErrorAsync, receiverOptions, _cancellationTokenSource.Token);
}
public void StopReceiving()
public virtual void StopReceiving()
{
_cancellationTokenSource.Cancel();
}
#region "Single Thread"
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));
}
#endregion
#region "Thread Pool"
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;
}
#endregion
#region "BotCommands"
/// <summary>
/// This will return the current list of bot commands.
/// </summary>
@@ -222,6 +160,8 @@ public class MessageClient
await TelegramClient.DeleteMyCommandsAsync(scope, languageCode);
}
#endregion
#region "Events"
@@ -231,6 +171,7 @@ public class MessageClient
remove => Events.RemoveHandler(EvOnMessageLoop, value);
}
public async Task OnMessageLoop(UpdateResult update)
{
var eventHandlers = (Events[EvOnMessageLoop] as Async.AsyncEventHandler<UpdateResult>)?.Invoke(this, update);
@@ -248,6 +189,7 @@ public class MessageClient
remove => Events.RemoveHandler(EvOnReceiveError, value);
}
public async Task OnReceiveError(ErrorResult update)
{
var eventHandlers = (Events[EvOnReceiveError] as Async.AsyncEventHandler<ErrorResult>)?.Invoke(this, update);
@@ -271,4 +213,5 @@ public class MessageClient
}
#endregion
}
+37 -22
View File
@@ -1,7 +1,8 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Newtonsoft.Json;
using System.Text.Json;
using Telegram.Bot.Types;
using Telegram.Bot.Types.Enums;
@@ -9,16 +10,36 @@ namespace TelegramBotBase.Base;
public class MessageResult : ResultBase
{
internal MessageResult()
{
}
public MessageResult(Update update)
{
UpdateData = update;
init();
}
public Update UpdateData { get; set; }
void init()
{
IsAction = UpdateData.CallbackQuery != null;
if (Message == null)
return;
IsBotCommand = Message.Entities?.Any(a => a.Type == MessageEntityType.BotCommand) ?? false;
if (!IsBotCommand)
return;
BotCommand = MessageText.Split(' ')[0];
IsBotGroupCommand = BotCommand.Contains("@");
if (IsBotGroupCommand)
{
BotCommand = BotCommand.Substring(0, BotCommand.LastIndexOf('@'));
}
}
public Update UpdateData { get; private set; }
/// <summary>
/// Returns the Device/ChatId
@@ -55,12 +76,17 @@ public class MessageResult : ResultBase
/// <summary>
/// Is this an action ? (i.e. button click)
/// </summary>
public bool IsAction => UpdateData.CallbackQuery != null;
public bool IsAction { get; private set; }
/// <summary>
/// Is this a command ? Starts with a slash '/' and a command
/// </summary>
public bool IsBotCommand => MessageText.StartsWith("/");
public bool IsBotCommand { get; private set; }
/// <summary>
/// Is this a bot command sent from a group via @BotId ?
/// </summary>
public bool IsBotGroupCommand { get; private set; }
/// <summary>
/// Returns a List of all parameters which has been sent with the command itself (i.e. /start 123 456 789 =>
@@ -83,18 +109,7 @@ public class MessageResult : ResultBase
/// <summary>
/// Returns just the command (i.e. /start 1 2 3 => /start)
/// </summary>
public string BotCommand
{
get
{
if (!IsBotCommand)
{
return null;
}
return MessageText.Split(' ')[0];
}
}
public string BotCommand { get; private set; }
/// <summary>
/// Returns if this message will be used on the first form or not.
@@ -111,7 +126,7 @@ public class MessageResult : ResultBase
T cd = null;
try
{
cd = JsonConvert.DeserializeObject<T>(RawData);
cd = JsonSerializer.Deserialize<T>(RawData);
return cd;
}
@@ -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.DropPendingUpdates = 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;
}
}
+13 -9
View File
@@ -8,6 +8,7 @@ using Telegram.Bot.Types;
using TelegramBotBase.Args;
using TelegramBotBase.Base;
using TelegramBotBase.Enums;
using TelegramBotBase.Exceptions;
using TelegramBotBase.Interfaces;
using TelegramBotBase.Sessions;
using Console = TelegramBotBase.Tools.Console;
@@ -115,7 +116,7 @@ public sealed class BotBase
if (ds == null)
{
ds = await Sessions.StartSession(e.DeviceId);
ds.LastMessage = e.RawData.Message;
ds.LastMessage = e.Message;
OnSessionBegins(new SessionBeginEventArgs(e.DeviceId, ds));
}
@@ -139,6 +140,13 @@ public sealed class BotBase
mr.IsFirstHandler = false;
} while (ds.FormSwitched && i < GetSetting(ESettings.NavigationMaximum, 10));
}
catch (InvalidServiceProviderConfiguration ex)
{
var ds = Sessions.GetSession(e.DeviceId);
OnException(new SystemExceptionEventArgs(e.Message.Text, e.DeviceId, ds, ex));
throw;
}
catch (Exception ex)
{
var ds = Sessions.GetSession(e.DeviceId);
@@ -189,13 +197,10 @@ public sealed class BotBase
/// <param name="deviceId">Contains the device/chat id of the device to update.</param>
public async Task InvokeMessageLoop(long deviceId)
{
var mr = new MessageResult
var mr = new MessageResult(new Update
{
UpdateData = new Update
{
Message = new Message()
}
};
Message = new Message()
});
await InvokeMessageLoop(deviceId, mr);
}
@@ -211,7 +216,6 @@ public sealed class BotBase
try
{
var ds = Sessions.GetSession(deviceId);
e.Device = ds;
await MessageLoopFactory.MessageLoop(this, ds, new UpdateResult(e.UpdateData, ds), e);
@@ -261,7 +265,7 @@ public sealed class BotBase
{
foreach (var scope in BotCommandScopes)
{
if (scope.Value.Any(a => "/" + a.Command == command))
if (scope.Value.Any(a => Constants.Telegram.BotCommandIndicator + a.Command == command))
{
return true;
}
+37 -23
View File
@@ -32,6 +32,7 @@ public class BotBaseBuilder : IAPIKeySelectionStage, IMessageLoopSelectionStage,
private BotBaseBuilder()
{
}
/// <summary>
@@ -72,14 +73,14 @@ public class BotBaseBuilder : IAPIKeySelectionStage, IMessageLoopSelectionStage,
}
public IBuildingStage QuickStart(string apiKey, Type startForm)
public IBuildingStage QuickStart(string apiKey, Type startForm, bool throwPendingUpdates = false)
{
_apiKey = apiKey;
_factory = new DefaultStartFormFactory(startForm);
DefaultMessageLoop();
NoProxy();
NoProxy(throwPendingUpdates);
OnlyStart();
@@ -93,7 +94,7 @@ public class BotBaseBuilder : IAPIKeySelectionStage, IMessageLoopSelectionStage,
}
public IBuildingStage QuickStart<T>(string apiKey)
public IBuildingStage QuickStart<T>(string apiKey, bool throwPendingUpdates = false)
where T : FormBase
{
_apiKey = apiKey;
@@ -101,7 +102,7 @@ public class BotBaseBuilder : IAPIKeySelectionStage, IMessageLoopSelectionStage,
DefaultMessageLoop();
NoProxy();
NoProxy(throwPendingUpdates);
OnlyStart();
@@ -114,14 +115,14 @@ public class BotBaseBuilder : IAPIKeySelectionStage, IMessageLoopSelectionStage,
return this;
}
public IBuildingStage QuickStart(string apiKey, IStartFormFactory startFormFactory)
public IBuildingStage QuickStart(string apiKey, IStartFormFactory startFormFactory, bool throwPendingUpdates = false)
{
_apiKey = apiKey;
_factory = startFormFactory;
DefaultMessageLoop();
NoProxy();
NoProxy(throwPendingUpdates);
OnlyStart();
@@ -213,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;
@@ -228,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;
@@ -242,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;
@@ -256,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;
@@ -397,29 +398,41 @@ public class BotBaseBuilder : IAPIKeySelectionStage, IMessageLoopSelectionStage,
#region "Step 7 (Language)"
/// <inheritdoc cref="ILanguageSelectionStage.DefaultLanguage"/>
public IThreadingStage DefaultLanguage()
{
return this;
}
/// <inheritdoc cref="ILanguageSelectionStage.UseEnglish"/>
public IThreadingStage UseEnglish()
{
Default.Language = new English();
return this;
}
/// <inheritdoc cref="ILanguageSelectionStage.UseGerman"/>
public IThreadingStage UseGerman()
{
Default.Language = new German();
return this;
}
/// <inheritdoc cref="ILanguageSelectionStage.UsePersian"/>
public IThreadingStage UsePersian()
{
Default.Language = new Persian();
return this;
}
/// <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;
@@ -429,26 +442,27 @@ public class BotBaseBuilder : IAPIKeySelectionStage, IMessageLoopSelectionStage,
#endregion
#region "Step 8 (Threading)"
public IBuildingStage UseSingleThread()
{
_client.UseThreadPool = false;
return this;
}
public IBuildingStage UseThreadPool(int workerThreads = 2, int ioThreads = 1)
{
_client.UseThreadPool = true;
_client.ThreadPool_WorkerThreads = workerThreads;
_client.ThreadPool_IOThreads = ioThreads;
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
}
@@ -20,16 +20,18 @@ public interface IAPIKeySelectionStage
/// </summary>
/// <param name="apiKey"></param>
/// <param name="StartForm"></param>
/// <param name="throwPendingUpdates">Indicates if all pending Telegram.Bot.Types.Updates should be thrown out before start polling.</param>
/// <returns></returns>
IBuildingStage QuickStart(string apiKey, Type StartForm);
IBuildingStage QuickStart(string apiKey, Type StartForm, bool throwPendingUpdates = false);
/// <summary>
/// Quick and easy way to create a BotBase instance.
/// Uses: DefaultMessageLoop, NoProxy, OnlyStart, NoSerialization, DefaultLanguage
/// </summary>
/// <param name="apiKey"></param>
/// <param name="throwPendingUpdates">Indicates if all pending Telegram.Bot.Types.Updates should be thrown out before start polling.</param>
/// <returns></returns>
IBuildingStage QuickStart<T>(string apiKey) where T : FormBase;
IBuildingStage QuickStart<T>(string apiKey , bool throwPendingUpdates = false) where T : FormBase;
/// <summary>
/// Quick and easy way to create a BotBase instance.
@@ -37,6 +39,7 @@ public interface IAPIKeySelectionStage
/// </summary>
/// <param name="apiKey"></param>
/// <param name="StartFormFactory"></param>
/// <param name="throwPendingUpdates">Indicates if all pending Telegram.Bot.Types.Updates should be thrown out before start polling.</param>
/// <returns></returns>
IBuildingStage QuickStart(string apiKey, IStartFormFactory StartFormFactory);
IBuildingStage QuickStart(string apiKey, IStartFormFactory StartFormFactory, bool throwPendingUpdates = false);
}
@@ -2,35 +2,44 @@
namespace TelegramBotBase.Builder.Interfaces;
/// <summary>
/// Represents the language selection stage in the localization process.
/// </summary>
public interface ILanguageSelectionStage
{
/// <summary>
/// Selects the default language for control usage. (English)
/// </summary>
/// <returns></returns>
/// <returns>The next stage in the building process.</returns>
IThreadingStage DefaultLanguage();
/// <summary>
/// Selects english as the default language for control labels.
/// </summary>
/// <returns></returns>
/// <returns>The next stage in the building process.</returns>
IThreadingStage UseEnglish();
/// <summary>
/// Selects german as the default language for control labels.
/// </summary>
/// <returns></returns>
/// <returns>The next stage in the building process.</returns>
IThreadingStage UseGerman();
/// <summary>
/// Selects persian as the default language for control labels.
/// </summary>
/// <returns></returns>
/// <returns>The next stage in the building process.</returns>
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>
/// Selects a custom language as the default language for control labels.
/// </summary>
/// <returns></returns>
/// <returns>The next stage in the building process.</returns>
IThreadingStage Custom(Localization language);
}
@@ -11,14 +11,14 @@ public interface INetworkingSelectionStage
/// <param name="proxyAddress"></param>
/// <param name="throwPendingUpdates">Indicates if all pending Telegram.Bot.Types.Updates should be thrown out before start polling.</param>
/// <returns></returns>
IBotCommandsStage WithProxy(string proxyAddress, bool throwPendingUpdates = false);
IBotCommandsStage WithProxy(string proxyAddress, bool throwPendingUpdates = false, int timeoutInSeconds = 60);
/// <summary>
/// Do not choose a proxy as network configuration.
/// </summary>
/// <param name="throwPendingUpdates">Indicates if all pending Telegram.Bot.Types.Updates should be thrown out before start polling.</param>
/// <returns></returns>
IBotCommandsStage NoProxy(bool throwPendingUpdates = false);
IBotCommandsStage NoProxy(bool throwPendingUpdates = false, int timeoutInSeconds = 60);
/// <summary>
@@ -27,7 +27,7 @@ public interface INetworkingSelectionStage
/// <param name="client"></param>
/// <param name="throwPendingUpdates">Indicates if all pending Telegram.Bot.Types.Updates should be thrown out before start polling.</param>
/// <returns></returns>
IBotCommandsStage WithBotClient(TelegramBotClient client, bool throwPendingUpdates = false);
IBotCommandsStage WithBotClient(TelegramBotClient client, bool throwPendingUpdates = false, int timeoutInSeconds = 60);
/// <summary>
@@ -37,7 +37,7 @@ public interface INetworkingSelectionStage
/// <param name="Port"></param>
/// <param name="throwPendingUpdates">Indicates if all pending Telegram.Bot.Types.Updates should be thrown out before start polling.</param>
/// <returns></returns>
IBotCommandsStage WithHostAndPort(string proxyHost, int Port, bool throwPendingUpdates = false);
IBotCommandsStage WithHostAndPort(string proxyHost, int Port, bool throwPendingUpdates = false, int timeoutInSeconds = 60);
/// <summary>
/// Uses a custom http client.
@@ -45,5 +45,5 @@ public interface INetworkingSelectionStage
/// <param name="client"></param>
/// <param name="throwPendingUpdates">Indicates if all pending Telegram.Bot.Types.Updates should be thrown out before start polling.</param>
/// <returns></returns>
IBotCommandsStage WithHttpClient(HttpClient client, bool throwPendingUpdates = false);
IBotCommandsStage WithHttpClient(HttpClient client, bool throwPendingUpdates = false, int timeoutInSeconds = 60);
}
+12 -1
View File
@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Linq;
using Telegram.Bot.Types;
@@ -20,6 +21,16 @@ public static class Extensions
scope = BotCommandScope.Default();
}
if (string.IsNullOrEmpty(command))
{
throw new ArgumentNullException(nameof(command), $"{nameof(command)} parameter can not be null or empty");
}
if(command.StartsWith(Constants.Telegram.BotCommandIndicator))
{
throw new ArgumentException($"{nameof(command)} parameter does not have to start with a slash, please remove.", $"{nameof(command)}");
}
var item = cmds.FirstOrDefault(a => a.Key.Type == scope.Type);
if (item.Value != null)
+6
View File
@@ -21,4 +21,10 @@ public static class Telegram
/// The maximum length of callback data. Will raise an exception of it exceeds it.
/// </summary>
public const int MaxCallBackDataBytes = 64;
/// <summary>
/// The slash constant which indicates a bot command.
/// </summary>
public const string BotCommandIndicator = "/";
}
+36 -19
View File
@@ -51,7 +51,24 @@ public class ButtonGrid : ControlBase
DataSource = new ButtonFormDataSource(form);
}
public string Title { get; set; } = Default.Language["ButtonGrid_Title"];
string m_Title = Default.Language["ButtonGrid_Title"];
public string Title
{
get
{
return m_Title;
}
set
{
if (string.IsNullOrEmpty(value))
{
throw new ArgumentNullException($"{nameof(Title)}", $"{nameof(Title)} property must have been a value unequal to null/empty");
}
m_Title = value;
}
}
public string ConfirmationText { get; set; } = "";
@@ -340,14 +357,14 @@ public class ButtonGrid : ControlBase
}
//var button = HeadLayoutButtonRow?. .FirstOrDefault(a => a.Text.Trim() == result.MessageText)
// ?? SubHeadLayoutButtonRow?.FirstOrDefault(a => a.Text.Trim() == result.MessageText);
//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)
// bf.ToList().FirstOrDefault(a => a.Text.Trim() == result.MessageText)
//var index = bf.FindRowByButton(button);
//var index = bf.FindRowByButton(button);
check:
check:
//Remove button click message
if (DeleteReplyMessage)
@@ -426,8 +443,6 @@ public class ButtonGrid : ControlBase
return;
}
await result.ConfirmAction(ConfirmationText ?? "");
ButtonRow match = null;
var index = -1;
@@ -451,17 +466,19 @@ public class ButtonGrid : ControlBase
}
//var bf = DataSource.ButtonForm;
//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 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);
//var index = bf.FindRowByButton(button);
check:
check:
if (match != null)
{
await result.ConfirmAction(ConfirmationText ?? "");
await OnButtonClicked(new ButtonClickedEventArgs(match.GetButtonMatch(result.RawData, false), index,
match));
@@ -506,13 +523,13 @@ public class ButtonGrid : ControlBase
if (DataSource.RowCount > Constants.Telegram.MaxInlineKeyBoardRows && !EnablePaging)
{
throw new MaximumRowsReachedException
{ Value = DataSource.RowCount, Maximum = Constants.Telegram.MaxInlineKeyBoardRows };
{ Value = DataSource.RowCount, Maximum = Constants.Telegram.MaxInlineKeyBoardRows };
}
if (DataSource.ColumnCount > Constants.Telegram.MaxInlineKeyBoardCols)
{
throw new MaximumColsException
{ Value = DataSource.ColumnCount, Maximum = Constants.Telegram.MaxInlineKeyBoardCols };
{ Value = DataSource.ColumnCount, Maximum = Constants.Telegram.MaxInlineKeyBoardCols };
}
break;
@@ -522,13 +539,13 @@ public class ButtonGrid : ControlBase
if (DataSource.RowCount > Constants.Telegram.MaxReplyKeyboardRows && !EnablePaging)
{
throw new MaximumRowsReachedException
{ Value = DataSource.RowCount, Maximum = Constants.Telegram.MaxReplyKeyboardRows };
{ Value = DataSource.RowCount, Maximum = Constants.Telegram.MaxReplyKeyboardRows };
}
if (DataSource.ColumnCount > Constants.Telegram.MaxReplyKeyboardCols)
{
throw new MaximumColsException
{ Value = DataSource.ColumnCount, Maximum = Constants.Telegram.MaxReplyKeyboardCols };
{ Value = DataSource.ColumnCount, Maximum = Constants.Telegram.MaxReplyKeyboardCols };
}
break;
@@ -693,7 +710,7 @@ public class ButtonGrid : ControlBase
Updated();
}
else
//Remove event handler
//Remove event handler
{
Device.MessageDeleted -= Device_MessageDeleted;
}
@@ -13,6 +13,7 @@ using TelegramBotBase.Enums;
using TelegramBotBase.Exceptions;
using TelegramBotBase.Form;
using TelegramBotBase.Localizations;
using static System.Net.Mime.MediaTypeNames;
using static TelegramBotBase.Base.Async;
namespace TelegramBotBase.Controls.Hybrid;
@@ -51,7 +52,24 @@ public class CheckedButtonList : ControlBase
DataSource = new ButtonFormDataSource(form);
}
public string Title { get; set; } = Default.Language["ButtonGrid_Title"];
string m_Title = Default.Language["ButtonGrid_Title"];
public string Title
{
get
{
return m_Title;
}
set
{
if (string.IsNullOrEmpty(value))
{
throw new ArgumentNullException($"{nameof(Title)}", $"{nameof(Title)} property must have been a value unequal to null/empty");
}
m_Title = value;
}
}
public string ConfirmationText { get; set; } = "";
@@ -29,8 +29,6 @@ public class TaggedButtonGrid : MultiView
public string BackLabel = Default.Language["ButtonGrid_Back"];
public string CheckAllLabel = Default.Language["ButtonGrid_CheckAll"];
public string NextPageLabel = Default.Language["ButtonGrid_NextPage"];
public string NoItemsLabel = Default.Language["ButtonGrid_NoItems"];
@@ -39,11 +37,17 @@ public class TaggedButtonGrid : MultiView
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 TagIcon = Default.Language["ButtonGrid_TagIcon"];
public string TagIcon = Default.Language["TaggedButtonGrid_TagIcon"];
public TaggedButtonGrid()
{
@@ -63,7 +67,24 @@ public class TaggedButtonGrid : MultiView
DataSource = new ButtonFormDataSource(form);
}
public string Title { get; set; } = Default.Language["ButtonGrid_Title"];
string m_Title = Default.Language["ButtonGrid_Title"];
public string Title
{
get
{
return m_Title;
}
set
{
if (string.IsNullOrEmpty(value))
{
throw new ArgumentNullException($"{nameof(Title)}", $"{nameof(Title)} property must have been a value unequal to null/empty");
}
m_Title = value;
}
}
public string ConfirmationText { get; set; }
@@ -379,14 +400,6 @@ public class TaggedButtonGrid : MultiView
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:
@@ -490,10 +503,16 @@ public class TaggedButtonGrid : MultiView
if (result.MessageText == CheckAllLabel)
{
CheckAllTags();
Updated();
result.Handled = true;
return;
}
else if (result.MessageText == UncheckAllLabel)
{
UncheckAllTags();
Updated();
result.Handled = true;
return;
}
var i = result.MessageText.LastIndexOf(" ");
@@ -569,15 +588,6 @@ public class TaggedButtonGrid : MultiView
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:
if (match != null)
{
@@ -619,22 +629,47 @@ public class TaggedButtonGrid : MultiView
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();
break;
case "$checkall$":
CheckAllTags();
break;
case "$uncheckall$":
UncheckAllTags();
break;
}
@@ -723,8 +758,10 @@ public class TaggedButtonGrid : MultiView
if (EnableCheckAllTools)
{
TagsSubHeadLayoutButtonRow = new ButtonRow(new ButtonBase(CheckAllLabel, "$checkall$"),
new ButtonBase(UncheckAllLabel, "$uncheckall$"));
TagsSubHeadLayoutButtonRow = new ButtonRow(new ButtonBase(string.Format(TotalTagsLabel, Tags.Count), "$total$"),
new ButtonBase(CheckAllLabel, "$checkall$"),
new ButtonBase(UncheckAllLabel, "$uncheckall$"),
new ButtonBase(string.Format(CheckedTagsLabel, SelectedTags.Count), "checked$"));
bf.AddButtonRow(TagsSubHeadLayoutButtonRow);
}
@@ -756,10 +793,6 @@ public class TaggedButtonGrid : MultiView
return;
}
//if (bf.Count == 0)
// return;
var rkm = (ReplyKeyboardMarkup)bf;
rkm.ResizeKeyboard = ResizeKeyboard;
rkm.OneTimeKeyboard = OneTimeKeyboard;
@@ -881,21 +914,20 @@ public class TaggedButtonGrid : MultiView
{
Message m = null;
var form = DataSource.PickItems(CurrentPageIndex * ItemRowsPerPage, ItemRowsPerPage,
EnableSearch ? SearchQuery : null);
//if (this.EnableSearch && this.SearchQuery != null && this.SearchQuery != "")
//{
// form = form.FilterDuplicate(this.SearchQuery, true);
//}
//else
//{
// form = form.Duplicate();
//}
ButtonForm form = null;
if (Tags != null && SelectedTags != null)
{
form = DataSource.PickAllItems(EnableSearch ? SearchQuery : null); //CurrentPageIndex * ItemRowsPerPage, ItemRowsPerPage,
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)
+8 -2
View File
@@ -14,9 +14,9 @@ namespace TelegramBotBase.Controls.Inline;
[DebuggerDisplay("{Text}")]
public class Label : ControlBase
{
private bool _renderNecessary = true;
protected bool _renderNecessary = true;
private string _text = string.Empty;
private string _text = Default.Language["Label_Text"];
public String Text
{
@@ -29,6 +29,10 @@ public class Label : ControlBase
if (_text == value)
return;
if (string.IsNullOrEmpty(value))
{
throw new ArgumentNullException($"{nameof(Text)}", $"{nameof(Text)} property must have been a value unequal to null/empty");
}
_text = value;
_renderNecessary = true;
@@ -36,6 +40,8 @@ public class Label : ControlBase
}
}
private ParseMode _parseMode = ParseMode.Markdown;
public ParseMode ParseMode
@@ -32,10 +32,32 @@ public class MultiToggleButton : ControlBase
/// </summary>
public string ChangedString { get; set; } = Default.Language["MultiToggleButton_Changed"];
private string _title = Default.Language["MultiToggleButton_Title"];
/// <summary>
/// This holds the title of the control.
/// </summary>
public string Title { get; set; } = Default.Language["MultiToggleButton_Title"];
public String Title
{
get
{
return _title;
}
set
{
if (_title == value)
return;
if (string.IsNullOrEmpty(value))
{
throw new ArgumentNullException($"{nameof(Title)}", $"{nameof(Title)} must have been a value unequal to null/empty");
}
_title = value;
_renderNecessary = true;
}
}
public int? MessageId { get; set; }
@@ -36,7 +36,31 @@ public class ToggleButton : ControlBase
public string ChangedString { get; set; } = Default.Language["ToggleButton_Changed"];
public string Title { get; set; } = Default.Language["ToggleButton_Title"];
private string _title = Default.Language["ToggleButton_Title"];
public String Title
{
get
{
return _title;
}
set
{
if (_title == value)
return;
if (string.IsNullOrEmpty(value))
{
throw new ArgumentNullException($"{nameof(Title)}", $"{nameof(Title)} property must have been a value unequal to null/empty");
}
_title = value;
_renderNecessary = true;
}
}
public int? MessageId { get; set; }
@@ -5,17 +5,31 @@ namespace TelegramBotBase.Exceptions;
public sealed class CallbackDataTooLongException : Exception
{
//public override string Message =>
// $"You have exceeded the maximum {Constants.Telegram.MaxCallBackDataBytes} bytes.";
static ApiRequestException _innerException = new Telegram.Bot.Exceptions.ApiRequestException("Bad Request: BUTTON_DATA_INVALID", 400);
static String _message = $"You have exceeded the maximum {Constants.Telegram.MaxCallBackDataBytes} bytes of callback data.\r\nThis is a pre-sending message from the TelegramBotBase framework.\r\nread more: https://core.telegram.org/bots/api#inlinekeyboardbutton";
static String _message_with_bytes = $"You have exceeded the maximum {Constants.Telegram.MaxCallBackDataBytes} bytes of callback data with @current_callback_bytes@ bytes.\r\nThis is a pre-sending message from the TelegramBotBase framework.\r\nread more: https://core.telegram.org/bots/api#inlinekeyboardbutton";
public CallbackDataTooLongException() : base(_message, _innerException)
{
}
/// <summary>
///
/// </summary>
/// <param name="current_callback_bytes">The amount of callback bytes generated.</param>
public CallbackDataTooLongException(int current_callback_bytes) : base(getMessage(current_callback_bytes), _innerException)
{
}
static String getMessage(int current_callback_bytes = -1)
{
if (current_callback_bytes == -1)
return _message;
return _message_with_bytes.Replace("@current_callback_bytes@", current_callback_bytes.ToString());
}
}
@@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace TelegramBotBase.Exceptions
{
public sealed class InvalidServiceProviderConfiguration : Exception
{
public InvalidServiceProviderConfiguration(string message, Exception innerException) : base(message, innerException) { }
}
}
@@ -1,6 +1,7 @@
using System;
using Microsoft.Extensions.DependencyInjection;
using TelegramBotBase.DependencyInjection;
using TelegramBotBase.Exceptions;
using TelegramBotBase.Form;
using TelegramBotBase.Interfaces;
@@ -22,9 +23,20 @@ public class ServiceProviderStartFormFactory : IStartFormFactory
_serviceProvider = serviceProvider;
}
public FormBase CreateForm()
public FormBase CreateForm() => CreateForm(null);
public FormBase CreateForm(Type? specifiedStartFrom = null)
{
var fb = (FormBase)ActivatorUtilities.CreateInstance(_serviceProvider, _startFormClass);
FormBase fb = null;
try
{
fb = (FormBase)ActivatorUtilities.CreateInstance(_serviceProvider, specifiedStartFrom ?? _startFormClass);
}
catch(InvalidOperationException ex)
{
throw new InvalidServiceProviderConfiguration(ex.Message, ex);
}
//Sets an internal field for future ServiceProvider navigation
fb.SetServiceProvider(_serviceProvider);
+10 -3
View File
@@ -24,7 +24,7 @@ public class AutoCleanForm : FormBase
DeleteMode = EDeleteMode.OnEveryCall;
DeleteSide = EDeleteSide.BotOnly;
Init += AutoCleanForm_Init;
Opened += AutoCleanForm_Init;
Closed += AutoCleanForm_Closed;
}
@@ -35,7 +35,7 @@ public class AutoCleanForm : FormBase
[SaveState] public EDeleteSide DeleteSide { get; set; }
private Task AutoCleanForm_Init(object sender, InitEventArgs e)
private Task AutoCleanForm_Init(object sender, EventArgs e)
{
if (Device == null)
{
@@ -70,7 +70,8 @@ public class AutoCleanForm : FormBase
private Task Device_MessageSent(object sender, MessageSentEventArgs e)
{
if (DeleteSide == EDeleteSide.UserOnly)
if (DeleteSide == EDeleteSide.UserOnly
|| Device.ActiveForm != this)
{
return Task.CompletedTask;
}
@@ -137,6 +138,12 @@ public class AutoCleanForm : FormBase
return Task.CompletedTask;
}
Device.MessageSent -= Device_MessageSent;
Device.MessageReceived -= Device_MessageReceived;
Device.MessageDeleted -= Device_MessageDeleted;
MessageCleanup().Wait();
return Task.CompletedTask;
}
+10
View File
@@ -23,6 +23,11 @@ public class ButtonForm
DependencyControl = control;
}
public ButtonForm(IEnumerable<ButtonRow> rows)
{
_buttons = rows.ToList();
}
public IReplyMarkup Markup { get; set; }
@@ -149,6 +154,11 @@ public class ButtonForm
.Aggregate((a, b) => a.Union(b).ToList());
}
public List<ButtonRow> ToRowList()
{
return _buttons;
}
public InlineKeyboardButton[][] ToInlineButtonArray()
{
var ikb = _buttons.Select(a => a.ToArray().Select(b => b.ToInlineButton(this)).ToArray()).ToArray();
+13 -31
View File
@@ -1,5 +1,6 @@
using Newtonsoft.Json;
using System.Text;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
using TelegramBotBase.Exceptions;
namespace TelegramBotBase.Form;
@@ -19,13 +20,13 @@ public class CallbackData
Value = value;
}
[JsonProperty("m")] public string Method { get; set; }
[JsonPropertyName("m")] public string Method { get; set; }
[JsonProperty("v")] public string Value { get; set; }
[JsonPropertyName("v")] public string Value { get; set; }
public static string Create(string method, string value)
{
return new CallbackData(method, value).Serialize();
return new CallbackData(method, value).Serialize(true);
}
/// <summary>
@@ -34,25 +35,17 @@ public class CallbackData
/// <returns></returns>
public string Serialize(bool throwExceptionOnOverflow = false)
{
var s = "";
try
{
s = JsonConvert.SerializeObject(this);
}
catch
{
}
var s = string.Empty;
#if DEBUG
s = JsonSerializer.Serialize(this);
//Data is over 64 bytes
if(throwExceptionOnOverflow && Encoding.UTF8.GetByteCount(s) > Constants.Telegram.MaxCallBackDataBytes)
//Is data over 64 bytes ?
int byte_count = Encoding.UTF8.GetByteCount(s);
if (throwExceptionOnOverflow && byte_count > Constants.Telegram.MaxCallBackDataBytes)
{
throw new CallbackDataTooLongException();
throw new CallbackDataTooLongException(byte_count);
}
#endif
return s;
}
@@ -63,18 +56,7 @@ public class CallbackData
/// <returns></returns>
public static CallbackData Deserialize(string data)
{
CallbackData cd = null;
try
{
cd = JsonConvert.DeserializeObject<CallbackData>(data);
return cd;
}
catch
{
}
return null;
return JsonSerializer.Deserialize<CallbackData>(data);
}
public static implicit operator string(CallbackData callbackData) => callbackData.Serialize(true);
+13 -13
View File
@@ -11,28 +11,28 @@ public class GroupForm : FormBase
{
switch (message.MessageType)
{
case MessageType.ChatMembersAdded:
case MessageType.NewChatMembers:
await OnMemberChanges(new MemberChangeEventArgs(MessageType.ChatMembersAdded, message,
await OnMemberChanges(new MemberChangeEventArgs(MessageType.NewChatMembers, message,
message.Message.NewChatMembers));
break;
case MessageType.ChatMemberLeft:
case MessageType.LeftChatMember:
await OnMemberChanges(new MemberChangeEventArgs(MessageType.ChatMemberLeft, message,
await OnMemberChanges(new MemberChangeEventArgs(MessageType.LeftChatMember, message,
message.Message.LeftChatMember));
break;
case MessageType.ChatPhotoChanged:
case MessageType.ChatPhotoDeleted:
case MessageType.ChatTitleChanged:
case MessageType.MigratedFromGroup:
case MessageType.MigratedToSupergroup:
case MessageType.MessagePinned:
case MessageType.GroupCreated:
case MessageType.SupergroupCreated:
case MessageType.ChannelCreated:
case MessageType.NewChatPhoto:
case MessageType.DeleteChatPhoto:
case MessageType.NewChatTitle:
case MessageType.MigrateFromChatId:
case MessageType.MigrateToChatId:
case MessageType.PinnedMessage:
case MessageType.GroupChatCreated:
case MessageType.SupergroupChatCreated:
case MessageType.ChannelChatCreated:
await OnGroupChanged(new GroupChangedEventArgs(message.MessageType, message));
+1 -1
View File
@@ -82,7 +82,7 @@ public class PromptDialog : ModalDialog
{
var bf = new ButtonForm();
bf.AddButtonRow(new ButtonBase(BackLabel, "back"));
await Device.Send(Message, (ReplyMarkupBase)bf);
await Device.Send(Message, (IReplyMarkup)bf);
return;
}
+6 -3
View File
@@ -12,8 +12,10 @@ public sealed class English : Localization
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_Back"] = "Back";
Values["ButtonGrid_CheckAll"] = "Check all";
Values["ButtonGrid_UncheckAll"] = "Uncheck all";
Values["TaggedButtonGrid_TotalTags"] = "Total: {0}";
Values["TaggedButtonGrid_CheckedTags"] = "Checked: {0}";
Values["TaggedButtonGrid_CheckAll"] = "Check all";
Values["TaggedButtonGrid_UncheckAll"] = "Uncheck all";
Values["CalendarPicker_Title"] = "Pick date";
Values["CalendarPicker_PreviousPage"] = "◀️";
Values["CalendarPicker_NextPage"] = "▶️";
@@ -31,6 +33,7 @@ public sealed class English : Localization
Values["PromptDialog_Back"] = "Back";
Values["ToggleButton_Changed"] = "Setting changed";
Values["ButtonGrid_SearchIcon"] = "🔍";
Values["ButtonGrid_TagIcon"] = "📁";
Values["TaggedButtonGrid_TagIcon"] = "📁";
Values["Label_Text"] = "Default label text";
}
}
+8 -4
View File
@@ -10,11 +10,12 @@ public sealed class German : Localization
Values["ButtonGrid_PreviousPage"] = "◀️";
Values["ButtonGrid_NextPage"] = "▶️";
Values["ButtonGrid_CurrentPage"] = "Seite {0} von {1}";
Values["ButtonGrid_SearchFeature"] =
"💡 Sende eine Nachricht um die Liste zu filtern. Klicke die 🔍 um den Filter zurückzusetzen.";
Values["ButtonGrid_SearchFeature"] = "💡 Sende eine Nachricht um die Liste zu filtern. Klicke die 🔍 um den Filter zurückzusetzen.";
Values["ButtonGrid_Back"] = "Zurück";
Values["ButtonGrid_CheckAll"] = "Alle auswählen";
Values["ButtonGrid_UncheckAll"] = "Keine auswählen";
Values["TaggedButtonGrid_TotalTags"] = "Gesamt: {0}";
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_PreviousPage"] = "◀️";
Values["CalendarPicker_NextPage"] = "▶️";
@@ -31,5 +32,8 @@ public sealed class German : Localization
Values["MultiToggleButton_Changed"] = "Ausgewählt";
Values["PromptDialog_Back"] = "Zurück";
Values["ToggleButton_Changed"] = "Einstellung geändert";
Values["ButtonGrid_SearchIcon"] = "🔍";
Values["TaggedButtonGrid_TagIcon"] = "📁";
Values["Label_Text"] = "Standard Label Text";
}
}
+36 -33
View File
@@ -1,37 +1,40 @@
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["ButtonGrid_NoItems"] = "هیچ آیتمی وجود ندارد.";
Values["ButtonGrid_PreviousPage"] = "◀️";
Values["ButtonGrid_NextPage"] = "▶️";
Values["ButtonGrid_CurrentPage"] = "صفحه ی {0} از {1}";
Values["ButtonGrid_SearchFeature"] = "💡 برای فیلتر کردن لیست پیام ارسال کنید. برای بازنشانی فیلتر روی 🔍 کلیک کنید.";
Values["ButtonGrid_Back"] = "بازگشت";
Values["ButtonGrid_CheckAll"] = "بررسی کردن همه";
Values["ButtonGrid_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["ButtonGrid_TagIcon"] = "📁";
}
Values["Language"] = "فارسی";
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"] = "Total: {0}";
Values["TaggedButtonGrid_CheckedTags"] = "Checked: {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"] = "📁";
Values["Label_Text"] = "متن برچسب پیش‌فرض";
}
}
+39
View File
@@ -0,0 +1,39 @@
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"] = "📁";
Values["Label_Text"] = "Текст метки по умолчанию";
}
}
@@ -46,6 +46,8 @@ public class FormBaseMessageLoop : IMessageLoopFactory
mr.Device = session;
ur.Device = session;
session.OnMessageReceived(new(mr.Message));
var activeForm = session.ActiveForm;
//Pre Loading Event
@@ -79,13 +81,16 @@ public class FormBaseMessageLoop : IMessageLoopFactory
}
//Action Event
if (!session.FormSwitched && mr.IsAction)
if (!session.FormSwitched && mr.IsAction && !mr.Handled)
{
//Send Action event to controls
await activeForm.ActionControls(mr);
//Send Action event to form itself
await activeForm.Action(mr);
if (!mr.Handled)
{
//Send Action event to form itself, if not already handled by a control
await activeForm.Action(mr);
}
if (!mr.Handled)
{
@@ -72,13 +72,16 @@ public class FullMessageLoop : IMessageLoopFactory
}
//Action Event
if (!session.FormSwitched && mr.IsAction)
if (!session.FormSwitched && mr.IsAction && !mr.Handled)
{
//Send Action event to controls
await activeForm.ActionControls(mr);
//Send Action event to form itself
await activeForm.Action(mr);
if (!mr.Handled)
{
//Send Action event to form itself, if not already handled by a control
await activeForm.Action(mr);
}
if (!mr.Handled)
{
+22 -11
View File
@@ -6,6 +6,7 @@ using System.Threading.Tasks;
using TelegramBotBase.Args;
using TelegramBotBase.Attributes;
using TelegramBotBase.Base;
using TelegramBotBase.Factories;
using TelegramBotBase.Form;
using TelegramBotBase.Interfaces;
using TelegramBotBase.Sessions;
@@ -145,9 +146,17 @@ public class SessionManager
{
continue;
}
FormBase form;
if (BotBase.StartFormFactory is ServiceProviderStartFormFactory diFactory)
{
form = diFactory.CreateForm(t);
}
//No default constructor, fallback
if (!(t.GetConstructor(new Type[] { })?.Invoke(new object[] { }) is FormBase form))
else if (t.GetConstructor(new Type[] { })?.Invoke(new object[] { }) is FormBase f)
{
form = f;
}
else
{
if (!statemachine.FallbackStateForm.IsSubclassOf(typeof(FormBase)))
{
@@ -293,17 +302,19 @@ public class SessionManager
se.Values = ssea.Values;
}
//Search for public properties with SaveState attribute
var fields = form.GetType()
.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
.Where(a => a.GetCustomAttributes(typeof(SaveState), true).Length != 0).ToList();
foreach (var f in fields)
else
{
var val = f.GetValue(form);
//Search for public properties with SaveState attribute
var fields = form.GetType()
.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
.Where(a => a.GetCustomAttributes(typeof(SaveState), true).Length != 0).ToList();
se.Values.Add("$" + f.Name, val);
foreach (var f in fields)
{
var val = f.GetValue(form);
se.Values.Add("$" + f.Name, val);
}
}
states.Add(se);
+99 -211
View File
@@ -131,13 +131,9 @@ public class DeviceSession : IDeviceSession
public async Task ConfirmAction(string callbackQueryId, string message = "", bool showAlert = false,
string urlToOpen = null)
{
try
{
await Client.TelegramClient.AnswerCallbackQueryAsync(callbackQueryId, message, showAlert, urlToOpen);
}
catch
{
}
await Client.TelegramClient.AnswerCallbackQueryAsync(callbackQueryId, message, showAlert, urlToOpen);
}
/// <summary>
@@ -157,17 +153,8 @@ public class DeviceSession : IDeviceSession
throw new MessageTooLongException(text.Length);
}
try
{
return await Api(a =>
a.EditMessageTextAsync(DeviceId, messageId, text, parseMode, replyMarkup: markup));
}
catch
{
}
return null;
return await Api(a => a.EditMessageTextAsync(DeviceId, messageId, text, parseMode, replyMarkup: markup));
}
/// <summary>
@@ -185,17 +172,9 @@ public class DeviceSession : IDeviceSession
throw new MessageTooLongException(text.Length);
}
try
{
return await Api(a =>
a.EditMessageTextAsync(DeviceId, messageId, text, parseMode, replyMarkup: markup));
}
catch
{
}
return await Api(a => a.EditMessageTextAsync(DeviceId, messageId, text, parseMode, replyMarkup: markup));
return null;
}
/// <summary>
@@ -215,18 +194,8 @@ public class DeviceSession : IDeviceSession
throw new MessageTooLongException(message.Text.Length);
}
try
{
return await Api(a =>
a.EditMessageTextAsync(DeviceId, message.MessageId, message.Text, parseMode,
replyMarkup: markup));
}
catch
{
}
return null;
return await Api(a => a.EditMessageTextAsync(DeviceId, message.MessageId, message.Text, parseMode,
replyMarkup: markup));
}
/// <summary>
@@ -237,15 +206,8 @@ public class DeviceSession : IDeviceSession
/// <returns></returns>
public async Task<Message> EditReplyMarkup(int messageId, ButtonForm bf)
{
try
{
return await Api(a => a.EditMessageReplyMarkupAsync(DeviceId, messageId, bf));
}
catch
{
}
return null;
return await Api(a => a.EditMessageReplyMarkupAsync(DeviceId, messageId, bf));
}
/// <summary>
@@ -277,21 +239,15 @@ public class DeviceSession : IDeviceSession
text = text.MarkdownV2Escape();
}
try
{
var t = Api(a => a.SendTextMessageAsync(deviceId, text, null, parseMode, replyToMessageId: replyTo,
replyMarkup: markup, disableNotification: disableNotification));
var t = Api(a => a.SendMessage(deviceId, text, messageThreadId: null, parseMode: parseMode, replyParameters: replyTo,
replyMarkup: markup, disableNotification: disableNotification));
var o = GetOrigin(new StackTrace());
var o = GetOrigin(new StackTrace());
await OnMessageSent(new MessageSentEventArgs(await t, o));
await OnMessageSent(new MessageSentEventArgs(await t, o));
return await t;
return await t;
}
catch
{
return null;
}
}
/// <summary>
@@ -336,20 +292,15 @@ public class DeviceSession : IDeviceSession
text = text.MarkdownV2Escape();
}
try
{
var t = Api(a => a.SendTextMessageAsync(DeviceId, text, null, parseMode, replyToMessageId: replyTo,
replyMarkup: markup, disableNotification: disableNotification));
var o = GetOrigin(new StackTrace());
await OnMessageSent(new MessageSentEventArgs(await t, o));
var t = Api(a => a.SendMessage(DeviceId, text, messageThreadId: null, parseMode: parseMode, replyParameters: replyTo,
replyMarkup: markup, disableNotification: disableNotification));
var o = GetOrigin(new StackTrace());
await OnMessageSent(new MessageSentEventArgs(await t, o));
return await t;
return await t;
}
catch
{
return null;
}
}
/// <summary>
@@ -360,7 +311,7 @@ public class DeviceSession : IDeviceSession
/// <param name="replyTo"></param>
/// <param name="disableNotification"></param>
/// <returns></returns>
public async Task<Message> Send(string text, ReplyMarkupBase markup, int replyTo = 0,
public async Task<Message> Send(string text, IReplyMarkup markup, int replyTo = 0,
bool disableNotification = false, ParseMode parseMode = ParseMode.Markdown,
bool markdownV2AutoEscape = true)
{
@@ -379,20 +330,15 @@ public class DeviceSession : IDeviceSession
text = text.MarkdownV2Escape();
}
try
{
var t = Api(a => a.SendTextMessageAsync(DeviceId, text, null, parseMode, replyToMessageId: replyTo,
replyMarkup: markup, disableNotification: disableNotification));
var o = GetOrigin(new StackTrace());
await OnMessageSent(new MessageSentEventArgs(await t, o));
var t = Api(a => a.SendMessage(DeviceId, text, messageThreadId: null, parseMode: parseMode, replyParameters: replyTo,
replyMarkup: markup, disableNotification: disableNotification));
var o = GetOrigin(new StackTrace());
await OnMessageSent(new MessageSentEventArgs(await t, o));
return await t;
return await t;
}
catch
{
return null;
}
}
/// <summary>
@@ -414,20 +360,15 @@ public class DeviceSession : IDeviceSession
InlineKeyboardMarkup markup = buttons;
try
{
var t = Api(a => a.SendPhotoAsync(DeviceId, file, null, caption, parseMode, replyToMessageId: replyTo,
replyMarkup: markup, disableNotification: disableNotification));
var o = GetOrigin(new StackTrace());
await OnMessageSent(new MessageSentEventArgs(await t, o));
var t = Api(a => a.SendPhoto(DeviceId, file, messageThreadId: null, caption: caption, parseMode: parseMode, replyParameters: replyTo,
replyMarkup: markup, disableNotification: disableNotification));
var o = GetOrigin(new StackTrace());
await OnMessageSent(new MessageSentEventArgs(await t, o));
return await t;
return await t;
}
catch
{
return null;
}
}
/// <summary>
@@ -449,21 +390,16 @@ public class DeviceSession : IDeviceSession
InlineKeyboardMarkup markup = buttons;
try
{
var t = Api(a => a.SendVideoAsync(DeviceId, file, caption: caption, parseMode: parseMode,
replyToMessageId: replyTo, replyMarkup: markup,
disableNotification: disableNotification));
var o = GetOrigin(new StackTrace());
await OnMessageSent(new MessageSentEventArgs(await t, o));
var t = Api(a => a.SendVideo(DeviceId, file, caption: caption, parseMode: parseMode,
replyParameters: replyTo, replyMarkup: markup,
disableNotification: disableNotification));
var o = GetOrigin(new StackTrace());
await OnMessageSent(new MessageSentEventArgs(await t, o));
return await t;
return await t;
}
catch
{
return null;
}
}
/// <summary>
@@ -484,21 +420,16 @@ public class DeviceSession : IDeviceSession
InlineKeyboardMarkup markup = buttons;
try
{
var t = Api(a => a.SendVideoAsync(DeviceId, InputFile.FromUri(url), parseMode: parseMode,
replyToMessageId: replyTo, replyMarkup: markup,
disableNotification: disableNotification));
var o = GetOrigin(new StackTrace());
await OnMessageSent(new MessageSentEventArgs(await t, o));
var t = Api(a => a.SendVideo(DeviceId, InputFile.FromUri(url), parseMode: parseMode,
replyParameters: replyTo, replyMarkup: markup,
disableNotification: disableNotification));
var o = GetOrigin(new StackTrace());
await OnMessageSent(new MessageSentEventArgs(await t, o));
return await t;
return await t;
}
catch
{
return null;
}
}
/// <summary>
@@ -520,24 +451,19 @@ public class DeviceSession : IDeviceSession
InlineKeyboardMarkup markup = buttons;
try
{
var ms = new MemoryStream(video);
var fts = InputFile.FromStream(ms, filename);
var ms = new MemoryStream(video);
var t = Api(a => a.SendVideoAsync(DeviceId, fts, parseMode: parseMode, replyToMessageId: replyTo,
replyMarkup: markup, disableNotification: disableNotification));
var fts = InputFile.FromStream(ms, filename);
var o = GetOrigin(new StackTrace());
await OnMessageSent(new MessageSentEventArgs(await t, o));
var t = Api(a => a.SendVideo(DeviceId, fts, parseMode: parseMode, replyParameters: replyTo,
replyMarkup: markup, disableNotification: disableNotification));
var o = GetOrigin(new StackTrace());
await OnMessageSent(new MessageSentEventArgs(await t, o));
return await t;
return await t;
}
catch
{
return null;
}
}
/// <summary>
@@ -560,26 +486,21 @@ public class DeviceSession : IDeviceSession
InlineKeyboardMarkup markup = buttons;
try
{
var fs = new FileStream(filepath, FileMode.Open);
var filename = Path.GetFileName(filepath);
var fs = new FileStream(filepath, FileMode.Open);
var fts = InputFile.FromStream(fs, filename);
var filename = Path.GetFileName(filepath);
var t = Api(a => a.SendVideoAsync(DeviceId, fts, parseMode: parseMode, replyToMessageId: replyTo,
replyMarkup: markup, disableNotification: disableNotification));
var fts = InputFile.FromStream(fs, filename);
var o = GetOrigin(new StackTrace());
await OnMessageSent(new MessageSentEventArgs(await t, o));
var t = Api(a => a.SendVideo(DeviceId, fts, parseMode: parseMode, replyParameters: replyTo,
replyMarkup: markup, disableNotification: disableNotification));
var o = GetOrigin(new StackTrace());
await OnMessageSent(new MessageSentEventArgs(await t, o));
return await t;
return await t;
}
catch
{
return null;
}
}
/// <summary>
@@ -650,20 +571,15 @@ public class DeviceSession : IDeviceSession
markup = buttons;
}
try
{
var t = Api(a => a.SendDocumentAsync(DeviceId, document, null, null, caption, replyMarkup: markup,
disableNotification: disableNotification, replyToMessageId: replyTo));
var o = GetOrigin(new StackTrace());
await OnMessageSent(new MessageSentEventArgs(await t, o));
var t = Api(a => a.SendDocument(DeviceId, document, messageThreadId: null, thumbnail: null, caption: caption, replyMarkup: markup,
disableNotification: disableNotification, replyParameters: replyTo));
var o = GetOrigin(new StackTrace());
await OnMessageSent(new MessageSentEventArgs(await t, o));
return await t;
return await t;
}
catch
{
return null;
}
}
/// <summary>
@@ -714,22 +630,16 @@ public class DeviceSession : IDeviceSession
public async Task<Message> HideReplyKeyboard(string closedMsg = "Closed", bool autoDeleteResponse = true)
{
try
{
var m = await Send(closedMsg, new ReplyKeyboardRemove());
if (autoDeleteResponse && m != null)
{
await DeleteMessage(m);
}
var m = await Send(closedMsg, new ReplyKeyboardRemove());
return m;
}
catch
if (autoDeleteResponse && m != null)
{
await DeleteMessage(m);
}
return null;
return m;
}
/// <summary>
@@ -759,13 +669,9 @@ public class DeviceSession : IDeviceSession
public virtual async Task ChangeChatPermissions(ChatPermissions permissions)
{
try
{
await Api(a => a.SetChatPermissionsAsync(DeviceId, permissions));
}
catch
{
}
await Api(a => a.SetChatPermissionsAsync(DeviceId, permissions));
}
private Type GetOrigin(StackTrace stackTrace)
@@ -865,11 +771,11 @@ public class DeviceSession : IDeviceSession
#region "Users"
public virtual async Task RestrictUser(long userId, ChatPermissions permissions, bool? useIndependentGroupPermission = null, DateTime until = default)
public virtual async Task RestrictUser(long userId, ChatPermissions permissions, bool useIndependentGroupPermission = false, DateTime until = default)
{
try
{
await Api(a => a.RestrictChatMemberAsync(DeviceId, userId, permissions, useIndependentGroupPermission, until));
await Api(a => a.RestrictChatMember(DeviceId, userId, permissions, useIndependentChatPermissions: useIndependentGroupPermission, untilDate: until));
}
catch
{
@@ -878,49 +784,31 @@ public class DeviceSession : IDeviceSession
public virtual async Task<ChatMember> GetChatUser(long userId)
{
try
{
return await Api(a => a.GetChatMemberAsync(DeviceId, userId));
}
catch
{
}
return null;
return await Api(a => a.GetChatMemberAsync(DeviceId, userId));
}
[Obsolete("User BanUser instead.")]
public virtual async Task KickUser(long userId, DateTime until = default)
{
try
{
await Api(a => a.BanChatMemberAsync(DeviceId, userId, until));
}
catch
{
}
await Api(a => a.BanChatMemberAsync(DeviceId, userId, until));
}
public virtual async Task BanUser(long userId, DateTime until = default)
{
try
{
await Api(a => a.BanChatMemberAsync(DeviceId, userId, until));
}
catch
{
}
await Api(a => a.BanChatMemberAsync(DeviceId, userId, until));
}
public virtual async Task UnbanUser(long userId)
{
try
{
await Api(a => a.UnbanChatMemberAsync(DeviceId, userId));
}
catch
{
}
await Api(a => a.UnbanChatMemberAsync(DeviceId, userId));
}
#endregion
+4 -9
View File
@@ -1,6 +1,6 @@
using System;
using System.IO;
using Newtonsoft.Json;
using System.Text.Json;
using TelegramBotBase.Args;
using TelegramBotBase.Base;
using TelegramBotBase.Form;
@@ -48,11 +48,7 @@ public class JsonStateMachine : IStateMachine
{
var content = File.ReadAllText(FilePath);
var sc = JsonConvert.DeserializeObject<StateContainer>(content, new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.All,
TypeNameAssemblyFormatHandling = TypeNameAssemblyFormatHandling.Simple
});
var sc = JsonSerializer.Deserialize<StateContainer>(content);
return sc;
}
@@ -77,10 +73,9 @@ public class JsonStateMachine : IStateMachine
try
{
var content = JsonConvert.SerializeObject(e.States, Formatting.Indented, new JsonSerializerSettings
var content = JsonSerializer.Serialize(e.States, new JsonSerializerOptions
{
TypeNameHandling = TypeNameHandling.All,
TypeNameAssemblyFormatHandling = TypeNameAssemblyFormatHandling.Simple
WriteIndented = true,
});
File.WriteAllText(FilePath, content);
@@ -1,6 +1,6 @@
using System;
using System.IO;
using Newtonsoft.Json;
using System.Text.Json;
using TelegramBotBase.Args;
using TelegramBotBase.Base;
using TelegramBotBase.Form;
@@ -49,7 +49,7 @@ public class SimpleJsonStateMachine : IStateMachine
{
var content = File.ReadAllText(FilePath);
var sc = JsonConvert.DeserializeObject<StateContainer>(content);
var sc = JsonSerializer.Deserialize<StateContainer>(content);
return sc;
}
@@ -74,7 +74,9 @@ public class SimpleJsonStateMachine : IStateMachine
try
{
var content = JsonConvert.SerializeObject(e.States, Formatting.Indented);
var content = JsonSerializer.Serialize(e.States, new JsonSerializerOptions() {
WriteIndented = true
});
File.WriteAllText(FilePath, content);
}
+2 -2
View File
@@ -22,7 +22,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.0" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All" />
</ItemGroup>
@@ -57,7 +57,7 @@
<ItemGroup>
<PackageReference Include="Telegram.Bot" Version="19.0.0" />
<PackageReference Include="Telegram.Bot" Version="22.2.0" />
</ItemGroup>
</Project>
+2 -2
View File
@@ -14,11 +14,11 @@
<dependencies>
<group targetFramework=".NETFramework4.6.1">
<dependency id="Newtonsoft.Json" version="13.0.1" exclude="Build,Analyzers" />
<dependency id="Telegram.Bot" version="19.0.0" exclude="Build,Analyzers" />
<dependency id="Telegram.Bot" version="22.2.0" exclude="Build,Analyzers" />
</group>
<group targetFramework=".NETStandard2.0">
<dependency id="Newtonsoft.Json" version="13.0.1" exclude="Build,Analyzers" />
<dependency id="Telegram.Bot" version="19.0.0" exclude="Build,Analyzers" />
<dependency id="Telegram.Bot" version="22.2.0" exclude="Build,Analyzers" />
</group>
</dependencies>
</metadata>
+11 -18
View File
@@ -34,15 +34,14 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "InlineAndReplyCombination",
EndProject
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
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Experiments", "Experiments", "{2E1FF127-4DC2-40A1-849C-ED1432204E8A}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DemoBot", "Experiments\ExternalActionManager\DemoBot\DemoBot.csproj", "{5184D3F8-8526-413D-8EB0-23636EA7A4EF}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TelegramBotBase.Experiments.ActionManager", "Experiments\ExternalActionManager\TelegramBotBase.Experiments\TelegramBotBase.Experiments.ActionManager.csproj", "{625F84FE-6809-4CA4-9C58-3453382D1C65}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ExternalActionManager", "ExternalActionManager", "{359F7D60-362C-4F42-9606-A138FB1293E9}"
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Элементы решения", "Элементы решения", "{040F54FA-B51F-475F-89F8-2DD23CDC2989}"
ProjectSection(SolutionItems) = preProject
.gitea\workflows\TelegramBotFramework.nuget.yaml = .gitea\workflows\TelegramBotFramework.nuget.yaml
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -94,18 +93,14 @@ Global
{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.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
{5184D3F8-8526-413D-8EB0-23636EA7A4EF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5184D3F8-8526-413D-8EB0-23636EA7A4EF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5184D3F8-8526-413D-8EB0-23636EA7A4EF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5184D3F8-8526-413D-8EB0-23636EA7A4EF}.Release|Any CPU.Build.0 = Release|Any CPU
{625F84FE-6809-4CA4-9C58-3453382D1C65}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{625F84FE-6809-4CA4-9C58-3453382D1C65}.Debug|Any CPU.Build.0 = Debug|Any CPU
{625F84FE-6809-4CA4-9C58-3453382D1C65}.Release|Any CPU.ActiveCfg = Release|Any CPU
{625F84FE-6809-4CA4-9C58-3453382D1C65}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -120,10 +115,8 @@ Global
{52EA3201-02E8-46F5-87C4-B4752C8A815C} = {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}
{7C55D9FF-7DC1-41D0-809C-469EBFA20992} = {E3193182-6FDA-4FA3-AD26-A487291E7681}
{DC521A4C-7446-46F7-845B-AAF10EDCF8C6} = {E3193182-6FDA-4FA3-AD26-A487291E7681}
{5184D3F8-8526-413D-8EB0-23636EA7A4EF} = {359F7D60-362C-4F42-9606-A138FB1293E9}
{625F84FE-6809-4CA4-9C58-3453382D1C65} = {359F7D60-362C-4F42-9606-A138FB1293E9}
{359F7D60-362C-4F42-9606-A138FB1293E9} = {2E1FF127-4DC2-40A1-849C-ED1432204E8A}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {59CB40E1-9FA7-4867-A56F-4F418286F057}