Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8e29652148 | ||
|
|
5bb316d874 | ||
|
|
d2ae08771e | ||
|
|
9162c0894c | ||
|
|
e0ec133209 | ||
|
|
7004ee5963 | ||
|
|
ef8ca3c988 | ||
|
|
f021f12486 | ||
|
|
d06e1eaa52 | ||
|
|
23969881c1 | ||
|
|
9fa3df2889 | ||
|
|
6ed91224eb | ||
|
|
aabc7f04b7 | ||
|
|
71433c0e4e | ||
|
|
894d0799c8 | ||
|
|
6c2543630d | ||
|
|
84d18f6f45 | ||
|
|
5f49e25458 | ||
|
|
f28c9b1e22 | ||
|
|
0db0c00cd0 | ||
|
|
2158f53cde | ||
|
|
fd7d72b986 | ||
|
|
47a5b502b9 | ||
|
|
ce4c7b6f1c | ||
|
|
5bae650edc | ||
|
|
9eae63dae6 | ||
|
|
6f005114bc | ||
|
|
00440a50e8 | ||
|
|
ae9dd17ae3 | ||
|
|
be2660239a | ||
|
|
e88114049e | ||
|
|
177c198916 | ||
|
|
1f7a9900c4 | ||
|
|
d6051ce607 | ||
|
|
5613e84bc3 | ||
|
|
d52fd41467 | ||
|
|
8df6e4ce55 | ||
|
|
9012ec76b1 | ||
|
|
36fb40b6bd | ||
|
|
47aa8c2aaa | ||
|
|
1709631965 | ||
|
|
264a998052 | ||
|
|
a00aaacbfb | ||
|
|
bd908db867 | ||
|
|
e2c485aede | ||
|
|
c8936d0748 | ||
|
|
92f12735c8 | ||
|
|
8142b626b2 | ||
|
|
5dcbe04a73 | ||
|
|
e742ded371 | ||
|
|
706b0f522a | ||
|
|
61e3652edf | ||
|
|
c484edd427 | ||
|
|
181902336c | ||
|
|
dc364b5fd3 | ||
|
|
1fd4752c0e | ||
|
|
d84be50243 | ||
|
|
1caa71cd25 | ||
|
|
e81e5f1854 | ||
|
|
399e056a8f | ||
|
|
26c4f479fa | ||
|
|
72d94df907 | ||
|
|
31e0683699 | ||
|
|
676be49a62 | ||
|
|
5b67f334ff | ||
|
|
6a556af070 | ||
|
|
4910a04923 | ||
|
|
10f4e1b745 | ||
|
|
42b624c28f | ||
|
|
75013d51bd | ||
|
|
7f3bcfe418 | ||
|
|
f5e26924d8 | ||
|
|
884aa60fac | ||
|
|
adc18891e0 | ||
|
|
07eb4bef75 | ||
|
|
dfe64b22ab | ||
|
|
15a8b8897f | ||
|
|
91048b4f7c | ||
|
|
426bb078af | ||
|
|
63deb978a4 | ||
|
|
1a2b5818aa | ||
|
|
fc44b7d38c | ||
|
|
dc8cb81284 | ||
|
|
0202fc29a8 | ||
|
|
7e51126fd3 | ||
|
|
67e82acf20 | ||
|
|
c32fc2c3da | ||
|
|
4351fc7c68 | ||
|
|
80d43ac369 | ||
|
|
376ba68e8e | ||
|
|
6b404545df | ||
|
|
a524fef0d5 | ||
|
|
73ba36ea2b | ||
|
|
46160b54ae | ||
|
|
87690a3630 | ||
|
|
975a942993 | ||
|
|
4336ff1681 | ||
|
|
567a9f937f | ||
|
|
95b4ff77c5 | ||
|
|
ba82c11d66 | ||
|
|
c2e33da277 | ||
|
|
259fa54236 | ||
|
|
f790a16e85 | ||
|
|
97fea333f7 | ||
|
|
e1b31b0b9a | ||
|
|
7aca5a648f | ||
|
|
f24c309f63 | ||
|
|
ba9fd8062c | ||
|
|
b37c31f2bd | ||
|
|
2c34a178ee | ||
|
|
2f030f3c12 | ||
|
|
a364562fd3 | ||
|
|
8ef8733a2b | ||
|
|
049b2081ec | ||
|
|
48b76a84be | ||
|
|
31c8231b2d | ||
|
|
e9504f3b36 | ||
|
|
8592f4279d | ||
|
|
2457773a12 | ||
|
|
46e65faf31 | ||
|
|
193a4811bf | ||
|
|
08a0de8087 | ||
|
|
82469cff70 | ||
|
|
81eafe1219 | ||
|
|
183e89c36f | ||
|
|
ad6cf9539c | ||
|
|
46576eb819 | ||
|
|
cf9e2285f1 | ||
|
|
ecfacdadbb | ||
|
|
c00c2e8d8f | ||
|
|
bfd905d590 | ||
|
|
7b1bf75dde | ||
|
|
7a7a65c3a7 | ||
|
|
8a42a861cf | ||
|
|
47876681e0 | ||
|
|
9314b4c0e5 | ||
|
|
01c19b9900 | ||
|
|
0af587d27f | ||
|
|
97b9176c3a | ||
|
|
dfa479d409 | ||
|
|
07dec0dc9d | ||
|
|
2fc16b18da | ||
|
|
11eeb0dcab | ||
|
|
fbfcbe99fb | ||
|
|
948c1368d7 | ||
|
|
d66df4978d | ||
|
|
7d8895850a | ||
|
|
5e87d00c58 | ||
|
|
efe5fef7d0 | ||
|
|
eebb5cb6f5 | ||
|
|
992065d289 | ||
|
|
1acfeb873f | ||
|
|
de35ebb66e | ||
|
|
2c3e707673 | ||
|
|
6d39d2fb73 | ||
|
|
ef667d12d8 | ||
|
|
d24f8e7ac5 | ||
|
|
e5dc3fd2bc | ||
|
|
6ebb96bd93 | ||
|
|
a1b3b9246b | ||
|
|
25a0ff2e3e | ||
|
|
b74233d66d | ||
|
|
fb7b624ad8 | ||
|
|
965e6e18b9 | ||
|
|
b1c518cbd3 | ||
|
|
ab0ae5cda4 | ||
|
|
b850330f65 | ||
|
|
317dad7d24 | ||
|
|
eb61d40a38 | ||
|
|
2ed030b2fb | ||
|
|
fd6f6fef34 | ||
|
|
54176f9136 | ||
|
|
f80fda8ff9 | ||
|
|
8b66842d34 | ||
|
|
0cdb8c3a1a | ||
|
|
8b9929198a | ||
|
|
7a3e4721bd | ||
|
|
f914920919 | ||
|
|
0435c68281 | ||
|
|
5a1e050aec | ||
|
|
6a62d73fe0 | ||
|
|
519a6fe41c | ||
|
|
201dfd10dd | ||
|
|
78a412c630 | ||
|
|
7a7014cec3 | ||
|
|
64311d0cd9 | ||
|
|
762fb27294 | ||
|
|
164194ac66 | ||
|
|
321bd7c0ff | ||
|
|
37ef30ebee | ||
|
|
9b1461ad78 | ||
|
|
38fa0a4e99 | ||
|
|
fba922049f | ||
|
|
d5a9a5f7a4 | ||
|
|
5126074419 | ||
|
|
aa38492bde | ||
|
|
9c68914e9d | ||
|
|
3fe2779b1e | ||
|
|
8e6de4a0e1 | ||
|
|
382ae2de2d | ||
|
|
77fd3393de | ||
|
|
0f1da77aca | ||
|
|
1561c1dd08 | ||
|
|
5b38f2e1e2 | ||
|
|
2989456fe5 | ||
|
|
7e4174a3be | ||
|
|
73f81cc8a2 | ||
|
|
2071ba2eeb | ||
|
|
a08ba8f512 | ||
|
|
649b94dafe | ||
|
|
7be0f15ca4 | ||
|
|
e29be4f1f4 | ||
|
|
176107300b | ||
|
|
0ceab58059 | ||
|
|
6ebdd9f208 | ||
|
|
595efd2276 | ||
|
|
85a479d826 | ||
|
|
150e9f731d | ||
|
|
0eab45bdbb | ||
|
|
a33cf5b841 | ||
|
|
1f9539a23d | ||
|
|
9cbde4830e | ||
|
|
3bea2783b1 | ||
|
|
038a941d33 | ||
|
|
48c62e57f8 | ||
|
|
b0e6a8b981 | ||
|
|
2e0656dc06 | ||
|
|
d1456d5bd1 | ||
|
|
058539777f | ||
|
|
5c69289198 | ||
|
|
4c8b085655 | ||
|
|
94d22318f8 | ||
|
|
2028e2f9e6 | ||
|
|
394209b303 | ||
|
|
f0fa953819 | ||
|
|
1d44b50883 | ||
|
|
3c21af8f68 | ||
|
|
1b811e1786 | ||
|
|
94195ec5bf | ||
|
|
256cac825f | ||
|
|
69e5a6717b | ||
|
|
a6d22cdd82 | ||
|
|
ee4a3d765c | ||
|
|
703c99eb8d | ||
|
|
138fee9772 | ||
|
|
d60399514f | ||
|
|
ae0fa3b0e3 | ||
|
|
6a2ac7a309 | ||
|
|
1a76b2b3f8 | ||
|
|
5893108bd3 | ||
|
|
d668f57711 | ||
|
|
56c7754408 | ||
|
|
805ffb9ec7 | ||
|
|
aeddd82ea9 | ||
|
|
1ec14ce680 | ||
|
|
3a309baeae | ||
|
|
e02af0fc76 | ||
|
|
530e01f971 | ||
|
|
dcf3cf804f | ||
|
|
a42d3934b5 | ||
|
|
2126b45fed | ||
|
|
b6b9d5b6b1 | ||
|
|
eed5f012dc | ||
|
|
de4cc138ae | ||
|
|
e4da70d37d | ||
|
|
cb4a797b12 | ||
|
|
94d153e141 | ||
|
|
e13cb6d4ef | ||
|
|
c0e285d39e | ||
|
|
8338c5c066 | ||
|
|
52c01b3dfd | ||
|
|
fe1aafc9a5 | ||
|
|
022eee4b57 | ||
|
|
ea971979e2 | ||
|
|
9585a231bd | ||
|
|
c07aca96a4 | ||
|
|
01de9ec932 | ||
|
|
94fe263676 | ||
|
|
4f1eae543d | ||
|
|
1491fc1795 |
@@ -0,0 +1,2 @@
|
||||
github: #[MajMcCloud]
|
||||
custom: ["https://paypal.me/majmccloud", dahn.partners]
|
||||
+12
@@ -240,3 +240,15 @@ ModelManifest.xml
|
||||
|
||||
# FAKE - F# Make
|
||||
.fake/
|
||||
MigrationBackup
|
||||
TelegramBotBase/nuget.exe
|
||||
Material
|
||||
TelegramBotBase/cp.ps1
|
||||
TelegramBotBase/TelegramBotBase.nuspec
|
||||
Program.cs
|
||||
TelegramBotBase/Archive
|
||||
/TelegramBotBase/TelegramBotBase.1.5.0.zip
|
||||
/TelegramBotBase/TelegramBotBase.1.5.1.zip
|
||||
/TelegramBotBase/TelegramBotBase.1.5.2.zip
|
||||
/TelegramBotBase/cpush.ps1
|
||||
/TelegramBotBase/cpack.ps1
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<configuration>
|
||||
<startup>
|
||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
|
||||
</startup>
|
||||
</configuration>
|
||||
+17
-41
@@ -1,17 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{5817184C-0D59-4924-AC6C-6B943967811C}</ProjectGuid>
|
||||
<ProjectGuid>{673A56F5-6110-4AED-A68D-562FD6ED3EA6}</ProjectGuid>
|
||||
<OutputType>Exe</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>TelegramBaseTest</RootNamespace>
|
||||
<AssemblyName>TelegramBaseTest</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
|
||||
<RootNamespace>AsyncFormUpdates</RootNamespace>
|
||||
<AssemblyName>AsyncFormUpdates</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||
<Deterministic>true</Deterministic>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
@@ -33,61 +33,37 @@
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Newtonsoft.Json, Version=12.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Newtonsoft.Json.12.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
<Reference Include="Newtonsoft.Json, Version=11.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Newtonsoft.Json.11.0.2\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Drawing" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="Telegram.Bot, Version=14.10.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Telegram.Bot.14.10.0\lib\net45\Telegram.Bot.dll</HintPath>
|
||||
<Reference Include="Telegram.Bot, Version=15.7.1.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Telegram.Bot.15.7.1\lib\net45\Telegram.Bot.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="forms\AsyncFormEdit.cs" />
|
||||
<Compile Include="forms\AsyncFormUpdate.cs" />
|
||||
<Compile Include="forms\Start.cs" />
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Tests\ButtonTestForm.cs" />
|
||||
<Compile Include="Tests\DataForm.cs" />
|
||||
<Compile Include="Tests\ProgressTest.cs" />
|
||||
<Compile Include="Tests\Register\PerForm.cs" />
|
||||
<Compile Include="Tests\Register\PerStep.cs" />
|
||||
<Compile Include="Tests\Register\Start.cs" />
|
||||
<Compile Include="Tests\Register\Steps\Data.cs" />
|
||||
<Compile Include="Tests\Register\Steps\Step3.cs" />
|
||||
<Compile Include="Tests\Register\Steps\Step2.cs" />
|
||||
<Compile Include="Tests\Register\Steps\Step1.cs" />
|
||||
<Compile Include="Tests\SimpleForm.cs" />
|
||||
<Compile Include="Tests\Start.cs" />
|
||||
<Compile Include="Tests\TestForm.cs" />
|
||||
<Compile Include="Tests\TestForm2.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="App.config">
|
||||
<SubType>Designer</SubType>
|
||||
</None>
|
||||
<None Include="packages.config">
|
||||
<SubType>Designer</SubType>
|
||||
</None>
|
||||
<None Include="App.config" />
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\TelegramBotBase\TelegramBotBase.csproj">
|
||||
<Project>{a61eda27-cfdf-4bcb-b2d6-184f94904f2d}</Project>
|
||||
<ProjectReference Include="..\..\TelegramBotBase\TelegramBotBase.csproj">
|
||||
<Project>{0bd16fb9-7ed4-4ccb-83eb-5cee538e1b6c}</Project>
|
||||
<Name>TelegramBotBase</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup />
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
||||
@@ -0,0 +1,48 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Timers;
|
||||
|
||||
namespace AsyncFormUpdates
|
||||
{
|
||||
class Program
|
||||
{
|
||||
static TelegramBotBase.BotBase<forms.Start> bot = null;
|
||||
|
||||
static void Main(string[] args)
|
||||
{
|
||||
String apiKey = "APIKey";
|
||||
|
||||
bot = new TelegramBotBase.BotBase<forms.Start>(apiKey);
|
||||
|
||||
bot.Start();
|
||||
|
||||
var timer = new Timer(5000);
|
||||
|
||||
timer.Elapsed += Timer_Elapsed;
|
||||
timer.Start();
|
||||
|
||||
Console.ReadLine();
|
||||
|
||||
timer.Stop();
|
||||
bot.Stop();
|
||||
}
|
||||
|
||||
private static async void Timer_Elapsed(object sender, ElapsedEventArgs e)
|
||||
{
|
||||
|
||||
foreach(var s in bot.Sessions.SessionList)
|
||||
{
|
||||
//Only for AsyncUpdateForm
|
||||
if (s.Value.ActiveForm.GetType() != typeof(forms.AsyncFormUpdate) && s.Value.ActiveForm.GetType() != typeof(forms.AsyncFormEdit))
|
||||
continue;
|
||||
|
||||
await bot.InvokeMessageLoop(s.Key);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// Allgemeine Informationen über eine Assembly werden über die folgenden
|
||||
// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern,
|
||||
// die einer Assembly zugeordnet sind.
|
||||
[assembly: AssemblyTitle("AsyncFormUpdates")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("AsyncFormUpdates")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2021")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Durch Festlegen von ComVisible auf FALSE werden die Typen in dieser Assembly
|
||||
// für COM-Komponenten unsichtbar. Wenn Sie auf einen Typ in dieser Assembly von
|
||||
// COM aus zugreifen müssen, sollten Sie das ComVisible-Attribut für diesen Typ auf "True" festlegen.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird
|
||||
[assembly: Guid("673a56f5-6110-4aed-a68d-562fd6ed3ea6")]
|
||||
|
||||
// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten:
|
||||
//
|
||||
// Hauptversion
|
||||
// Nebenversion
|
||||
// Buildnummer
|
||||
// Revision
|
||||
//
|
||||
// Sie können alle Werte angeben oder Standardwerte für die Build- und Revisionsnummern verwenden,
|
||||
// indem Sie "*" wie unten gezeigt eingeben:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
@@ -0,0 +1,57 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using TelegramBotBase.Attributes;
|
||||
using TelegramBotBase.Base;
|
||||
using TelegramBotBase.Form;
|
||||
|
||||
namespace AsyncFormUpdates.forms
|
||||
{
|
||||
public class AsyncFormEdit : FormBase
|
||||
{
|
||||
[SaveState]
|
||||
int counter = 0;
|
||||
|
||||
int MessageId = 0;
|
||||
|
||||
public override async Task Load(MessageResult message)
|
||||
{
|
||||
counter++;
|
||||
}
|
||||
|
||||
public override async Task Action(MessageResult message)
|
||||
{
|
||||
await message.ConfirmAction("");
|
||||
|
||||
switch (message.RawData ?? "")
|
||||
{
|
||||
case "back":
|
||||
|
||||
var st = new Start();
|
||||
await NavigateTo(st);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public override async Task Render(MessageResult message)
|
||||
{
|
||||
var bf = new ButtonForm();
|
||||
bf.AddButtonRow("Back", "back");
|
||||
|
||||
if (MessageId != 0)
|
||||
{
|
||||
await Device.Edit(MessageId, $"Your current count is at: {counter}", bf);
|
||||
}
|
||||
else
|
||||
{
|
||||
var m = await Device.Send($"Your current count is at: {counter}", bf, disableNotification: true);
|
||||
MessageId = m.MessageId;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using TelegramBotBase.Attributes;
|
||||
using TelegramBotBase.Base;
|
||||
using TelegramBotBase.Form;
|
||||
|
||||
namespace AsyncFormUpdates.forms
|
||||
{
|
||||
public class AsyncFormUpdate : AutoCleanForm
|
||||
{
|
||||
[SaveState]
|
||||
int counter = 0;
|
||||
|
||||
|
||||
public override async Task Load(MessageResult message)
|
||||
{
|
||||
counter++;
|
||||
}
|
||||
|
||||
public override async Task Action(MessageResult message)
|
||||
{
|
||||
await message.ConfirmAction("");
|
||||
|
||||
switch (message.RawData ?? "")
|
||||
{
|
||||
case "back":
|
||||
|
||||
var st = new Start();
|
||||
await NavigateTo(st);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public override async Task Render(MessageResult message)
|
||||
{
|
||||
var bf = new ButtonForm();
|
||||
bf.AddButtonRow("Back", "back");
|
||||
|
||||
await Device.Send($"Your current count is at: {counter}", bf, disableNotification: true);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using TelegramBotBase.Base;
|
||||
using TelegramBotBase.Form;
|
||||
|
||||
namespace AsyncFormUpdates.forms
|
||||
{
|
||||
public class Start : AutoCleanForm
|
||||
{
|
||||
|
||||
|
||||
public override async Task Action(MessageResult message)
|
||||
{
|
||||
await message.ConfirmAction("");
|
||||
|
||||
switch (message.RawData ?? "")
|
||||
{
|
||||
case "async":
|
||||
|
||||
var afe = new AsyncFormEdit();
|
||||
await NavigateTo(afe);
|
||||
|
||||
|
||||
break;
|
||||
|
||||
case "async_del":
|
||||
|
||||
var afu = new AsyncFormUpdate();
|
||||
await NavigateTo(afu);
|
||||
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public override async Task Render(MessageResult message)
|
||||
{
|
||||
var bf = new ButtonForm();
|
||||
|
||||
bf.AddButtonRow("Open Async Form with AutoCleanupForm", "async_del");
|
||||
|
||||
bf.AddButtonRow("Open Async Form with Edit", "async");
|
||||
|
||||
await Device.Send("Choose your option", bf);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Newtonsoft.Json" version="11.0.2" targetFramework="net472" />
|
||||
<package id="System.Net.Requests" version="4.3.0" targetFramework="net472" />
|
||||
<package id="Telegram.Bot" version="15.7.1" targetFramework="net472" />
|
||||
</packages>
|
||||
@@ -0,0 +1,12 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="TelegramBotBase" Version="3.1.1" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,19 @@
|
||||
using System;
|
||||
|
||||
namespace JoinHiderBot
|
||||
{
|
||||
class Program
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
|
||||
String apiKey = "";
|
||||
|
||||
var bot = new TelegramBotBase.BotBase<forms.Start>(apiKey);
|
||||
|
||||
bot.Start();
|
||||
|
||||
Console.ReadLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using TelegramBotBase.Args;
|
||||
using TelegramBotBase.Form;
|
||||
|
||||
namespace JoinHiderBot.forms
|
||||
{
|
||||
public class GroupManageForm : GroupForm
|
||||
{
|
||||
|
||||
public override async Task OnMemberChanges(MemberChangeEventArgs e)
|
||||
{
|
||||
if (e.Type != Telegram.Bot.Types.Enums.MessageType.ChatMembersAdded && e.Type != Telegram.Bot.Types.Enums.MessageType.ChatMemberLeft)
|
||||
return;
|
||||
|
||||
|
||||
var m = e.Result.Message;
|
||||
|
||||
await this.Device.DeleteMessage(m);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using TelegramBotBase.Base;
|
||||
using TelegramBotBase.Form;
|
||||
|
||||
namespace JoinHiderBot.forms
|
||||
{
|
||||
public class Start : SplitterForm
|
||||
{
|
||||
public override async Task<bool> Open(MessageResult e)
|
||||
{
|
||||
await this.Device.Send("This bot works only in groups.");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override async Task<bool> OpenGroup(MessageResult e)
|
||||
{
|
||||
var gmf = new GroupManageForm();
|
||||
|
||||
await this.NavigateTo(gmf);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
using System;
|
||||
|
||||
namespace SystemCommandsBot
|
||||
{
|
||||
class Program
|
||||
{
|
||||
public static config.Config BotConfig { get; set; }
|
||||
|
||||
|
||||
static void Main(string[] args)
|
||||
{
|
||||
|
||||
BotConfig = config.Config.load();
|
||||
|
||||
if (BotConfig.ApiKey == null || BotConfig.ApiKey.Trim() == "")
|
||||
{
|
||||
Console.WriteLine("Config created...");
|
||||
Console.ReadLine();
|
||||
return;
|
||||
}
|
||||
|
||||
var bot = new TelegramBotBase.BotBase<forms.StartForm>(BotConfig.ApiKey);
|
||||
|
||||
bot.Start();
|
||||
|
||||
Console.WriteLine("Bot started");
|
||||
|
||||
Console.ReadLine();
|
||||
|
||||
|
||||
bot.Stop();
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||
<PackageReference Include="TelegramBotBase" Version="3.1.1" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,30 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace SystemCommandsBot.commands
|
||||
{
|
||||
public class Commando
|
||||
{
|
||||
public int ID { get; set; }
|
||||
|
||||
public String Title { get; set; }
|
||||
|
||||
public String ShellCmd { get; set; }
|
||||
|
||||
public bool Enabled { get; set; } = true;
|
||||
|
||||
public String Action { get; set; }
|
||||
|
||||
public bool UseShell { get; set; } = true;
|
||||
|
||||
|
||||
public int? MaxInstances { get; set; }
|
||||
|
||||
public String ProcName
|
||||
{
|
||||
get;set;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace SystemCommandsBot.config
|
||||
{
|
||||
public class Config
|
||||
{
|
||||
public String Password { get; set; }
|
||||
|
||||
public String ApiKey { get; set; }
|
||||
|
||||
public List<commands.Commando> Commandos { get; set; }
|
||||
|
||||
|
||||
public Config()
|
||||
{
|
||||
this.Commandos = new List<commands.Commando>();
|
||||
}
|
||||
|
||||
public void loadDefaultValues()
|
||||
{
|
||||
this.ApiKey = "";
|
||||
this.Commandos.Add(new commands.Commando() { ID = 0, Enabled = true, Title = "Test Befehl", ShellCmd = "explorer.exe", Action = "start", MaxInstances = 2 });
|
||||
}
|
||||
|
||||
|
||||
public static Config load()
|
||||
{
|
||||
try
|
||||
{
|
||||
return load(AppContext.BaseDirectory + "config\\default.cfg");
|
||||
|
||||
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
Console.WriteLine(ex.Message);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public static Config load(String path)
|
||||
{
|
||||
try
|
||||
{
|
||||
var cfg = Newtonsoft.Json.JsonConvert.DeserializeObject<Config>(File.ReadAllText(path)) as Config;
|
||||
return cfg;
|
||||
}
|
||||
catch (DirectoryNotFoundException ex)
|
||||
{
|
||||
DirectoryInfo di = new DirectoryInfo(path);
|
||||
|
||||
if (!Directory.Exists(di.Parent.FullName))
|
||||
{
|
||||
Directory.CreateDirectory(di.Parent.FullName);
|
||||
}
|
||||
|
||||
var cfg = new Config();
|
||||
cfg.loadDefaultValues();
|
||||
cfg.save(path);
|
||||
return cfg;
|
||||
}
|
||||
catch (FileNotFoundException ex)
|
||||
{
|
||||
var cfg = new Config();
|
||||
cfg.loadDefaultValues();
|
||||
cfg.save(path);
|
||||
return cfg;
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
Console.WriteLine(ex.Message);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void save(String path)
|
||||
{
|
||||
try
|
||||
{
|
||||
File.WriteAllText(path, Newtonsoft.Json.JsonConvert.SerializeObject(this));
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using TelegramBotBase.Base;
|
||||
using TelegramBotBase.Form;
|
||||
|
||||
namespace SystemCommandsBot.forms
|
||||
{
|
||||
public class CmdForm : TelegramBotBase.Form.AutoCleanForm
|
||||
{
|
||||
public DateTime ExpiresAt { get; set; }
|
||||
|
||||
public int? MessageId { get; set; }
|
||||
|
||||
public override async Task Load(MessageResult message)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public override async Task Action(MessageResult message)
|
||||
{
|
||||
var btn = message.RawData;
|
||||
|
||||
int id = -1;
|
||||
|
||||
if (!int.TryParse(btn, out id))
|
||||
{
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var cmd = Program.BotConfig.Commandos.Where(a => a.Enabled && a.ID == id).FirstOrDefault();
|
||||
if (cmd == null)
|
||||
{
|
||||
await this.Device.Send("Cmd nicht verfügbar.");
|
||||
return;
|
||||
}
|
||||
|
||||
message.Handled = true;
|
||||
|
||||
switch (cmd.Action)
|
||||
{
|
||||
case "start":
|
||||
|
||||
FileInfo fi = new FileInfo(cmd.ShellCmd);
|
||||
|
||||
if (cmd.MaxInstances != null && cmd.MaxInstances >= 0)
|
||||
{
|
||||
|
||||
if (Process.GetProcessesByName(cmd.ProcName).Count() >= cmd.MaxInstances)
|
||||
{
|
||||
await this.Device.Send("Anwendung läuft bereits.");
|
||||
await message.ConfirmAction("Anwendung läuft bereits.");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ProcessStartInfo psi = new ProcessStartInfo();
|
||||
psi.FileName = cmd.ShellCmd;
|
||||
psi.WorkingDirectory = fi.DirectoryName;
|
||||
psi.UseShellExecute = cmd.UseShell;
|
||||
|
||||
Process.Start(psi);
|
||||
|
||||
await this.Device.Send(fi.Name + " wurde gestarted.");
|
||||
|
||||
await message.ConfirmAction(fi.Name + " wurde gestarted.");
|
||||
|
||||
break;
|
||||
|
||||
case "kill":
|
||||
|
||||
FileInfo fi2 = new FileInfo(cmd.ShellCmd);
|
||||
|
||||
String pros = fi2.Name.Replace(fi2.Extension, "");
|
||||
|
||||
var proc = Process.GetProcessesByName(pros).ToList();
|
||||
|
||||
foreach (var p in proc)
|
||||
{
|
||||
try
|
||||
{
|
||||
p.Kill();
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
await this.Device.Send(fi2.Name + " wurde beendet.");
|
||||
|
||||
await message.ConfirmAction(fi2.Name + " wurde beendet.");
|
||||
|
||||
break;
|
||||
|
||||
case "restart":
|
||||
|
||||
FileInfo fi3 = new FileInfo(cmd.ShellCmd);
|
||||
|
||||
String pros2 = fi3.Name.Replace(fi3.Extension, "");
|
||||
|
||||
var proc2 = Process.GetProcessesByName(pros2).ToList();
|
||||
|
||||
foreach (var p in proc2)
|
||||
{
|
||||
try
|
||||
{
|
||||
p.Kill();
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
FileInfo fi4 = new FileInfo(cmd.ShellCmd);
|
||||
|
||||
ProcessStartInfo psi2 = new ProcessStartInfo();
|
||||
psi2.FileName = cmd.ShellCmd;
|
||||
psi2.WorkingDirectory = fi4.DirectoryName;
|
||||
psi2.FileName = cmd.ShellCmd;
|
||||
psi2.UseShellExecute = cmd.UseShell;
|
||||
|
||||
Process.Start(psi2);
|
||||
|
||||
await this.Device.Send(fi3.Name + " wurde neugestarted.");
|
||||
await message.ConfirmAction(fi3.Name + " wurde neugestarted.");
|
||||
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
await message.ConfirmAction();
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
public override async Task Render(MessageResult message)
|
||||
{
|
||||
if (this.MessageId == null)
|
||||
{
|
||||
var buttons = Program.BotConfig.Commandos.Where(a => a.Enabled).Select(a => new ButtonBase(a.Title, a.ID.ToString()));
|
||||
|
||||
ButtonForm bf = new ButtonForm();
|
||||
bf.AddSplitted(buttons, 1);
|
||||
await this.Device.Send("Deine Optionen", bf);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using TelegramBotBase.Base;
|
||||
|
||||
namespace SystemCommandsBot.forms
|
||||
{
|
||||
public class StartForm : TelegramBotBase.Form.FormBase
|
||||
{
|
||||
public String Password { get; set; }
|
||||
|
||||
public override async Task Load(MessageResult message)
|
||||
{
|
||||
var inp = message.MessageText;
|
||||
if (Program.BotConfig.Password == inp)
|
||||
{
|
||||
this.Password = inp;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
public override async Task Render(MessageResult message)
|
||||
{
|
||||
if (this.Password == null || this.Password.Trim() == "")
|
||||
{
|
||||
await this.Device.Send("Bitte gib dein Passwort an.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
var cmd = new forms.CmdForm();
|
||||
cmd.ExpiresAt = DateTime.Now.AddDays(14);
|
||||
|
||||
await this.NavigateTo(cmd);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,36 @@
|
||||
|
||||
|
||||
# .Net Telegram Bot Framework - Context based addon
|
||||
|
||||
[](https://www.nuget.org/packages/TelegramBotBase/)
|
||||
[](https://t.me/tgbotbase)
|
||||
[](https://www.t.me/tgbotbase)
|
||||
|
||||
|
||||
[](https://raw.githubusercontent.com/MajMcCloud/TelegramBotFramework/master/LICENCE.md)
|
||||
[](https://www.nuget.org/packages/TelegramBotBase)
|
||||
|
||||
Test the Testproject: [@TGBaseBot](https://t.me/TGBaseBot)
|
||||
Test the Testproject: [@TGBaseBot](https://www.t.me/TGBaseBot)
|
||||
|
||||
Join the Telegram Group: [https://www.t.me/tgbotbase](https://www.t.me/tgbotbase)
|
||||
|
||||
Download a release: [Releases](https://github.com/MajMcCloud/TelegramBotFramework/releases)
|
||||
|
||||
---
|
||||
|
||||
Donations
|
||||
|
||||
Bitcoin: 1NqyGWmZg8HLp9qQELbf6bChEoba3mxaFc
|
||||
|
||||
ETH: 0x795B70CFC27C69603ce115F2b450cbAC5a5460D0
|
||||
|
||||
Litecoin: LM5iCN6Nz22wAi8LSFnKgdGGWEtxWfJZXv
|
||||
|
||||
DASH: Xb2qyVefvbKTXusHoxZ4Soja2S6R4vLsSr
|
||||
|
||||
Paypal: https://paypal.me/majmccloud
|
||||
|
||||
|
||||
Thanks !
|
||||
|
||||
---
|
||||
|
||||
@@ -34,6 +56,67 @@ Test the Testproject: [@TGBaseBot](https://t.me/TGBaseBot)
|
||||
|
||||
* [PromptDialog](#prompt-dialog)
|
||||
|
||||
* [ConfirmDialog](#confirm-dialog)
|
||||
|
||||
- [Controls](#controls)
|
||||
* [ProgressBar](#progress-bar)
|
||||
|
||||
* [CalendarPicker](#calendar-picker)
|
||||
|
||||
* [MonthPicker](#month-picker)
|
||||
|
||||
* [TreeView](#tree-view)
|
||||
|
||||
* [ToggleButton](#toggle-button)
|
||||
|
||||
* [ButtonGrid](#button-grid)
|
||||
|
||||
* [Paging and Searching](#paging--searching)
|
||||
|
||||
* [TaggedButtonGrid](#tagged-button-grid)
|
||||
|
||||
* [CheckedButtonList](#checked-button-list)
|
||||
|
||||
* [MultiToggleButton](#multi-toggle-button)
|
||||
|
||||
- [Groups](#groups)
|
||||
* [SplitterForm](#splitter-form)
|
||||
|
||||
* [GroupForm](#group-form)
|
||||
|
||||
- [State Machine and Session Serialization (v3.0.0)](#statemachine-and-sessions)
|
||||
|
||||
* [StateMachines](#statemachines)
|
||||
|
||||
* [SimpleJSONStateMachine](#simplejsonstatemachine)
|
||||
|
||||
* [JSONStateMachine](#jsonstatemachine)
|
||||
|
||||
* [XMLStateMachine](#xmlstatemachine)
|
||||
|
||||
* [Interfaces](#interfaces)
|
||||
|
||||
* [IStateMachine](#istatemachine)
|
||||
|
||||
* [IStateForm](#istateform)
|
||||
|
||||
* [Attributes](#attributes)
|
||||
|
||||
* [SaveState](#savestate)
|
||||
|
||||
* [IgnoreState](#ignorestate)
|
||||
|
||||
|
||||
- [Navigation and NavigationController (v4.0.0)](#navigation-and-navigationcontroller)
|
||||
|
||||
* [As of Now](#as-of-now)
|
||||
|
||||
* [How to use](#how-to-use-)
|
||||
|
||||
|
||||
|
||||
|
||||
- [Examples](#examples)
|
||||
|
||||
---
|
||||
|
||||
@@ -53,7 +136,7 @@ It gives you features which will look/feel like WinForms or have a good way to c
|
||||
## How to start:
|
||||
|
||||
Within your empty App your need to put some initial lines including your APIKey to get things started.
|
||||
The "BotBase" Class will manage a lot of things for you, like system calls, action events and so on.
|
||||
The "BotBase" Class will manage a lot of things for you, like bot commands, action events and so on.
|
||||
"StartForm" is your first Formular which every user will get internally redirected to, like a start page, you could redirect from there later in code, so users won't recognize it.
|
||||
It needs to be a subclass of "FormBase" you will find in Namespace TelegramBotBase.Base
|
||||
|
||||
@@ -64,7 +147,10 @@ It needs to be a subclass of "FormBase" you will find in Namespace TelegramBotBa
|
||||
BotBase<StartForm> bb = new BotBase<StartForm>("{YOUR API KEY}");
|
||||
|
||||
//Add Systemcommands if you like, you could catch them later
|
||||
bb.SystemCalls.Add("/start");
|
||||
bb.BotCommands.Add(new BotCommand() { Command = "start", Description = "Starts the bot" });
|
||||
|
||||
//Update bot commands to botfather
|
||||
bb.UploadBotCommands().Wait();
|
||||
|
||||
//Start your Bot
|
||||
bb.Start();
|
||||
@@ -83,29 +169,26 @@ public class StartForm : FormBase
|
||||
|
||||
}
|
||||
|
||||
//Gets invoked during Navigation to this form
|
||||
public override async Task Init(params object[] param)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//Gets invoked if the form gets opened (with Form.NavigateTo(NewForm))
|
||||
public override async Task Opened()
|
||||
{
|
||||
//Gets invoked during Navigation to this form
|
||||
|
||||
}
|
||||
//Init() got replaced with event handler
|
||||
|
||||
//Gets invoked if the form gets leaved
|
||||
public override async Task Closed()
|
||||
{
|
||||
//Opened() got replaced with event handler
|
||||
|
||||
//Closed() got replaced with event handler
|
||||
|
||||
}
|
||||
|
||||
//Gets invoked on every Message/Action/Data in this context
|
||||
public override async Task Load(MessageResult message)
|
||||
{
|
||||
await this.Device.Send("Hello world!");
|
||||
}
|
||||
|
||||
//Gets invoked on edited messages
|
||||
public override async Task Edited(MessageResult message)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//Gets invoked on Button clicks
|
||||
public override async Task Action(MessageResult message)
|
||||
@@ -150,10 +233,10 @@ await this.NavigateTo(tf);
|
||||
|
||||
All examples are within the test project, so just try it out on your own.
|
||||
|
||||
### Add some system calls (Example #0 - System Calls)
|
||||
### Add some system calls (Example #0 - Bot Commands)
|
||||
|
||||
Inside of the BotFather you are able to add "Commands" to your TelegramBot. The user will see them, depending on the application as options he could choose.
|
||||
I'm calling them Systemcalls. Before start (and later for sure) you could add them to your BotBase. Every time a message comes in they will get checked if they are one of them.
|
||||
Before start (and later for sure) you could add them to your BotBase. Every time a message comes in they will get checked if they are one of them.
|
||||
If so, a special event Handler will get raised where you are easier able to manage the action behind.
|
||||
|
||||
Below we have 4 options.
|
||||
@@ -171,13 +254,12 @@ Below we have 4 options.
|
||||
```
|
||||
BotBase<Start> bb = new BotBase<Start>("{YOUR API KEY}");
|
||||
|
||||
bb.SystemCalls.Add("/start");
|
||||
bb.SystemCalls.Add("/form1");
|
||||
bb.SystemCalls.Add("/form2");
|
||||
bb.BotCommands.Add(new BotCommand() { Command = "start", Description = "Starts the bot" });
|
||||
bb.BotCommands.Add(new BotCommand() { Command = "form1", Description = "Opens test form 1" });
|
||||
bb.BotCommands.Add(new BotCommand() { Command = "form2", Description = "Opens test form 2" });
|
||||
bb.BotCommands.Add(new BotCommand() { Command = "params", Description = "Returns all send parameters as a message." });
|
||||
|
||||
bb.SystemCalls.Add("/params");
|
||||
|
||||
bb.SystemCall += async (s, en) =>
|
||||
bb.BotCommand += async (s, en) =>
|
||||
{
|
||||
switch (en.Command)
|
||||
{
|
||||
@@ -208,6 +290,9 @@ bb.SystemCall += async (s, en) =>
|
||||
|
||||
};
|
||||
|
||||
//Update Bot commands to botfather
|
||||
bb.UploadBotCommands().Wait();
|
||||
|
||||
bb.Start();
|
||||
|
||||
```
|
||||
@@ -217,19 +302,26 @@ On every input the user is sending back to the bot the Action event gets raised.
|
||||
|
||||
### Lets start with text messages (Example #1 - Simple Test)
|
||||
|
||||
|
||||
<img src="images/example1.PNG" />
|
||||
|
||||
```
|
||||
public class SimpleForm : FormBase
|
||||
public class SimpleForm : AutoCleanForm
|
||||
{
|
||||
public SimpleForm()
|
||||
{
|
||||
this.DeleteSide = TelegramBotBase.Enums.eDeleteSide.Both;
|
||||
this.DeleteMode = TelegramBotBase.Enums.eDeleteMode.OnLeavingForm;
|
||||
|
||||
public override async Task Opened()
|
||||
{
|
||||
await this.Device.Send("Hello world!");
|
||||
}
|
||||
this.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");
|
||||
}
|
||||
|
||||
|
||||
public override async Task Load(MessageResult message)
|
||||
public override async Task Load(MessageResult message)
|
||||
{
|
||||
//message.MessageText will work also, cause it is a string you could manage a lot different scenerios here
|
||||
|
||||
@@ -267,6 +359,7 @@ public class SimpleForm : FormBase
|
||||
|
||||
I using a different base class (AutoCleanForm) I created for a better "feeling" inside the bot which will delete "old" messages from this form. You have some settings within this class to manage when messages should be getting deleted.
|
||||
|
||||
<img src="images/example2.PNG" />
|
||||
|
||||
```
|
||||
public class ButtonTestForm : AutoCleanForm
|
||||
@@ -344,6 +437,8 @@ public class ButtonTestForm : AutoCleanForm
|
||||
|
||||
btn.AddButtonRow(new ButtonBase("Button 3", new CallbackData("a", "button3").Serialize()), new ButtonBase("Button 4", new CallbackData("a", "button4").Serialize()));
|
||||
|
||||
btn.AddButtonRow(new ButtonBase("Google.com", "google", "https://www.google.com"), new ButtonBase("Telegram", "telegram", "https://telegram.org/"));
|
||||
|
||||
btn.AddButtonRow(new ButtonBase("Back", new CallbackData("a", "back").Serialize()));
|
||||
|
||||
await this.Device.Send("Click a button", btn);
|
||||
@@ -359,6 +454,8 @@ public class ButtonTestForm : AutoCleanForm
|
||||
Sometimes it makes sense to show the end user a type of progressbar or status. For this i tried to make a simple control, which is useful for some situations.
|
||||
Maybe, if i got more ideas, i will add other "controls" in the future.
|
||||
|
||||
<img src="images/example3.PNG" />
|
||||
|
||||
```
|
||||
|
||||
public class ProgressTest : AutoCleanForm
|
||||
@@ -493,6 +590,14 @@ public override async Task Closed()
|
||||
I read it a lot in different Telegram groups that some developers are searching for easy solutions to create context based apps. For this is my project an ideal solution here.
|
||||
To give you an example about the possiblities, i added into the Test project an example for a registration form.
|
||||
|
||||
<img src="images/example4.1.PNG" />
|
||||
|
||||
<img src="images/example4.2.PNG" />
|
||||
|
||||
<img src="images/example4.3.PNG" />
|
||||
|
||||
<img src="images/example4.4.PNG" />
|
||||
|
||||
```
|
||||
|
||||
public class PerForm : AutoCleanForm
|
||||
@@ -591,7 +696,7 @@ public async override Task Render(MessageResult message)
|
||||
```
|
||||
|
||||
There is also a second example, where every of these 3 inputs gets requested by a different formular (class). Just for imagination of the possiblites.
|
||||
Cause its to much Text, i didnt have added it here. You will find it under [TelegramBaseTest/Tests/Register/PerStep.cs](TelegramBaseTest/Tests/Register/PerStep.cs)
|
||||
Cause its to much Text, i didnt have added it here. You will find it under [TelegramBotBaseTest/Tests/Register/PerStep.cs](TelegramBotBaseTest/Tests/Register/PerStep.cs)
|
||||
Beginn there and navigate your way through these Steps in the subfolder.
|
||||
|
||||
|
||||
@@ -611,15 +716,24 @@ For now we have the following:
|
||||
Makes sense to create a "feeling" of a clean environment for the user. For instance if you have a multilevel menu. This will remove the previously shown menu, and renders the new sub/top level.
|
||||
|
||||
- [PromptDialog](#prompt-dialog)
|
||||
A simple dialog which will show a message and then waits for a text input (response).
|
||||
|
||||
- [ConfirmDialog](#confirm-dialog)
|
||||
A simple dialog which is able to show multiple buttons and a Text message. The user could select one option and will get redirected to a different form, depending on the click.
|
||||
|
||||
### Alert Dialog
|
||||
|
||||
<img src="images/alertdialog.PNG" />
|
||||
|
||||
```
|
||||
|
||||
var fto = new TestForm2();
|
||||
AlertDialog ad = new AlertDialog("This is a message", "Ok");
|
||||
|
||||
AlertDialog ad = new AlertDialog("This is a message", "Ok", fto);
|
||||
ad.ButtonClicked += async (s, en) =>
|
||||
{
|
||||
var fto = new TestForm2();
|
||||
await this.NavigateTo(fto);
|
||||
};
|
||||
|
||||
await this.NavigateTo(ad);
|
||||
|
||||
@@ -628,47 +742,437 @@ await this.NavigateTo(ad);
|
||||
|
||||
### AutoCleanForm
|
||||
|
||||
|
||||
No example yet
|
||||
|
||||
### Prompt Dialog
|
||||
|
||||
#### Without Eventhandler (pre-init of form necessary)
|
||||
<img src="images/promptdialog.PNG" />
|
||||
|
||||
|
||||
|
||||
```
|
||||
|
||||
PromptDialog pd = new PromptDialog("Please confirm");
|
||||
PromptDialog pd = new PromptDialog("Please tell me your name ?");
|
||||
|
||||
pd.AddButton(new ButtonBase("Ok", "ok"));
|
||||
pd.AddButton(new ButtonBase("Cancel", "cancel"));
|
||||
pd.Completed += async (s, en) =>
|
||||
{
|
||||
await this.Device.Send("Hello " + pd.Value);
|
||||
};
|
||||
|
||||
var tf = new TestForm2();
|
||||
|
||||
pd.ButtonForms.Add("ok", tf);
|
||||
pd.ButtonForms.Add("cancel", tf);
|
||||
|
||||
await this.NavigateTo(pd);
|
||||
await this.OpenModal(pd);
|
||||
|
||||
```
|
||||
|
||||
### Confirm Dialog
|
||||
|
||||
<img src="images/confirmdialog.PNG" />
|
||||
|
||||
|
||||
#### With Eventhandler (no pre-init of form necessary)
|
||||
|
||||
```
|
||||
|
||||
PromptDialog pd = new PromptDialog("Please confirm", new ButtonBase("Ok", "ok"), new ButtonBase("Cancel", "cancel"));
|
||||
ConfirmDialog cd = new ConfirmDialog("Please confirm", new ButtonBase("Ok", "ok"), new ButtonBase("Cancel", "cancel"));
|
||||
|
||||
pd.ButtonClicked += async (s, en) =>
|
||||
cd.ButtonClicked += async (s, en) =>
|
||||
{
|
||||
var tf = new TestForm2();
|
||||
|
||||
//Remember only to navigate from the current running form. (here it is the prompt dialog, cause we have left the above already)
|
||||
await pd.NavigateTo(tf);
|
||||
await cd.NavigateTo(tf);
|
||||
};
|
||||
|
||||
await this.NavigateTo(pd);
|
||||
await this.NavigateTo(cd);
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Controls
|
||||
|
||||
### Progress Bar
|
||||
<img src="images/progressbar.PNG?raw=true" />
|
||||
|
||||
|
||||
### Calendar Picker
|
||||
<img src="images/calendarpicker.PNG?raw=true" />
|
||||
|
||||
<img src="images/calendarpicker.gif?raw=true" />
|
||||
|
||||
### Month Picker
|
||||
<img src="images/monthpicker1.PNG?raw=true" />
|
||||
<img src="images/monthpicker2.PNG?raw=true" />
|
||||
|
||||
|
||||
### Tree View
|
||||
<img src="images/treeview1.PNG?raw=true" />
|
||||
<img src="images/treeview2.PNG?raw=true" />
|
||||
<img src="images/treeview3.PNG?raw=true" />
|
||||
<img src="images/treeview4.PNG?raw=true" />
|
||||
|
||||
|
||||
### Toggle Button
|
||||
<img src="images/togglebutton.gif?raw=true" />
|
||||
|
||||
### Button Grid
|
||||
<img src="images/buttongrid.gif?raw=true" />
|
||||
|
||||
#### Paging & Searching
|
||||
<img src="images/buttongrid_pagingfilter.gif?raw=true" />
|
||||
|
||||
### Tagged Button Grid
|
||||
<img src="images/taggedbuttongrid.gif?raw=true" />
|
||||
|
||||
### Checked Button List
|
||||
<img src="images/checkedbuttonlist.gif?raw=true" />
|
||||
|
||||
### Multi Toggle Button
|
||||
<img src="images/multitogglebutton.gif?raw=true" />
|
||||
|
||||
## Groups
|
||||
|
||||
For working with groups, there are multiple different tools which helps to work with and allows bot also to manage "Single-User" chats and group chats.
|
||||
|
||||
### Splitter Form
|
||||
|
||||
|
||||
An easy way to switch between a "Single-User" form and one for managing a group is the SplitterForm base class.
|
||||
It calls special methods which you can override and then move from there to the form you need.
|
||||
|
||||
The OpenGroup method is the "backup" if OpenChannel or OpenSupergroup is not overridden. Same for Open, it is "backup" if none of the previous methods has been overridden.
|
||||
|
||||
|
||||
```
|
||||
|
||||
public class Start : SplitterForm
|
||||
{
|
||||
public override async Task<bool> Open(MessageResult e)
|
||||
{
|
||||
var st = new Menu();
|
||||
await this.NavigateTo(st);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public override async Task<bool> OpenGroup(MessageResult e)
|
||||
{
|
||||
var st = new Groups.LinkReplaceTest();
|
||||
await this.NavigateTo(st);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override Task<bool> OpenChannel(MessageResult e)
|
||||
{
|
||||
return base.OpenChannel(e);
|
||||
}
|
||||
|
||||
public override Task<bool> OpenSupergroup(MessageResult e)
|
||||
{
|
||||
return base.OpenSupergroup(e);
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
### Group Form
|
||||
|
||||
For managing groups im introducing a new base class called "GroupForm". This one has special events which should make it easier to work with groups and channels.
|
||||
In the Example project is a simple example for deleting a url written by a user and incrementing an internal counter. At every url he writes he got blocked for a small amount of time and the messages gots deleted. At 3 "failes" the user gets kicked of the group and blocked.
|
||||
|
||||
```
|
||||
public class GroupForm : FormBase
|
||||
{
|
||||
|
||||
|
||||
public override async Task Load(MessageResult message)
|
||||
{
|
||||
switch (message.MessageType)
|
||||
{
|
||||
case Telegram.Bot.Types.Enums.MessageType.ChatMembersAdded:
|
||||
|
||||
await OnMemberChanges(new MemberChangeEventArgs(Telegram.Bot.Types.Enums.MessageType.ChatMembersAdded, message, message.RawMessageData.Message.NewChatMembers));
|
||||
|
||||
break;
|
||||
case Telegram.Bot.Types.Enums.MessageType.ChatMemberLeft:
|
||||
|
||||
await OnMemberChanges(new MemberChangeEventArgs(Telegram.Bot.Types.Enums.MessageType.ChatMemberLeft, message, message.RawMessageData.Message.LeftChatMember));
|
||||
|
||||
break;
|
||||
|
||||
case Telegram.Bot.Types.Enums.MessageType.ChatPhotoChanged:
|
||||
case Telegram.Bot.Types.Enums.MessageType.ChatPhotoDeleted:
|
||||
case Telegram.Bot.Types.Enums.MessageType.ChatTitleChanged:
|
||||
case Telegram.Bot.Types.Enums.MessageType.MigratedFromGroup:
|
||||
case Telegram.Bot.Types.Enums.MessageType.MigratedToSupergroup:
|
||||
case Telegram.Bot.Types.Enums.MessageType.MessagePinned:
|
||||
case Telegram.Bot.Types.Enums.MessageType.GroupCreated:
|
||||
case Telegram.Bot.Types.Enums.MessageType.SupergroupCreated:
|
||||
case Telegram.Bot.Types.Enums.MessageType.ChannelCreated:
|
||||
|
||||
await OnGroupChanged(new GroupChangedEventArgs(message.MessageType, message));
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
OnMessage(message);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
public virtual async Task OnMemberChanges(MemberChangeEventArgs e)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
public virtual async Task OnGroupChanged(GroupChangedEventArgs e)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
public virtual async Task OnMessage(MessageResult e)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## Statemachine and Sessions
|
||||
|
||||
Depending on the usecases and the overall structure of a Telegram Bot it is essential to have some kind of session serialization or state machine to keep the user context after restarts of the bot (ie. due to updates) or crashes.
|
||||
For this I have created some easy to implement structures which fits into the current environment.
|
||||
|
||||
Below you find all possiblities.
|
||||
|
||||
### Statemachines
|
||||
|
||||
There are actually 3 types of example state machines you could use. A state machine is a kind of serializer which saves the important session data in a reusable structure like JSON or XML.
|
||||
|
||||
You could use one of the following state machines:
|
||||
|
||||
#### SimpleJSONStateMachine
|
||||
Is easy to use and useful for simple structures like basic datatypes. Did not work for complex ones like generics. Use the JSONStateMachine for them.
|
||||
In general you didn't need to do more then, to keep the actual form:
|
||||
|
||||
```
|
||||
//Prepare the System
|
||||
BotBase<StartForm> bb = new BotBase<StartForm>("{YOUR API KEY}");
|
||||
|
||||
//Add Systemcommands if you like, you could catch them later
|
||||
bb.SystemCalls.Add("/start");
|
||||
|
||||
//Set the statemachine and enable it
|
||||
bb.StateMachine = new TelegramBotBase.States.SimpleJSONStateMachine(AppContext.BaseDirectory + "config\\states.json");
|
||||
|
||||
//Start your Bot
|
||||
bb.Start();
|
||||
|
||||
```
|
||||
|
||||
#### JSONStateMachine
|
||||
Is easy to use too, but works for complex datatypes cause it saves there namespaces and additional type informations into the JSON file too.
|
||||
In general you didn't need to do more then, to keep the actual form:
|
||||
|
||||
```
|
||||
//Prepare the System
|
||||
BotBase<StartForm> bb = new BotBase<StartForm>("{YOUR API KEY}");
|
||||
|
||||
//Add Systemcommands if you like, you could catch them later
|
||||
bb.SystemCalls.Add("/start");
|
||||
|
||||
//Set the statemachine and enable it
|
||||
bb.StateMachine = new TelegramBotBase.States.JSONStateMachine(AppContext.BaseDirectory + "config\\states.json");
|
||||
|
||||
//Start your Bot
|
||||
bb.Start();
|
||||
|
||||
```
|
||||
|
||||
#### XMLStateMachine
|
||||
|
||||
The last one, should work like the others.
|
||||
In general you didn't need to do more then, to keep the actual form:
|
||||
|
||||
```
|
||||
//Prepare the System
|
||||
BotBase<StartForm> bb = new BotBase<StartForm>("{YOUR API KEY}");
|
||||
|
||||
//Add Systemcommands if you like, you could catch them later
|
||||
bb.SystemCalls.Add("/start");
|
||||
|
||||
//Set the statemachine and enable it
|
||||
bb.StateMachine = new TelegramBotBase.States.XMLStateMachine(AppContext.BaseDirectory + "config\\states.xml");
|
||||
|
||||
//Start your Bot
|
||||
bb.Start();
|
||||
|
||||
```
|
||||
|
||||
### Interfaces
|
||||
|
||||
There are two interfaces, one for the StateMachine itself, which is useful to build a custom one for a different datatype and one for implementing into a form which should be invoked with events.
|
||||
|
||||
#### IStateMachine
|
||||
|
||||
Is the basic StateMachine interface, it has two methods SaveFormStates(SaveStatesEventArgs e) and StateContainer LoadFormStates(), nothing fancy, just simple calls. Implement into both methods your own serialization process.
|
||||
|
||||
```
|
||||
|
||||
public interface IStateMachine
|
||||
{
|
||||
void SaveFormStates(SaveStatesEventArgs e);
|
||||
|
||||
StateContainer LoadFormStates();
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
#### IStateForm
|
||||
|
||||
When implemented, this will invoke one of these two methods: LoadState(LoadStateEventArgs e) or SaveState(SaveStateEventArgs e).
|
||||
They have methods to load or save data from the statemachine of the current form.
|
||||
|
||||
```
|
||||
|
||||
public interface IStateForm
|
||||
{
|
||||
void LoadState(LoadStateEventArgs e);
|
||||
|
||||
void SaveState(SaveStateEventArgs e);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Attributes
|
||||
|
||||
If you don't want to implement the IStateForm interface, cause there are maybe "just" one or two properties you want to keep and restore, use the following attribute:
|
||||
|
||||
#### SaveState
|
||||
|
||||
This will let the engine know, that you want too keep and restore this field automatically. Unlike the IStateForm methods, you have no option to manipulate data.
|
||||
|
||||
```
|
||||
|
||||
[SaveState]
|
||||
public long UserId { get; set; }
|
||||
|
||||
|
||||
```
|
||||
|
||||
#### IgnoreState
|
||||
|
||||
Due to the fact that Attribute implementation and interaction is optional, you want to let the engine maybe know, that you don't want to keep a specific form. So it should get "lost". This attribute will help you here, add it to the form class and it will not get serialized, even if it implements IStateForm or the SaveState attributes.
|
||||
|
||||
```
|
||||
|
||||
[IgnoreState]
|
||||
public class Registration : STForm
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Navigation and NavigationController
|
||||
|
||||
### As of now
|
||||
|
||||
As from earlier topics on this readme you already know the default way for (cross) navigation between Forms.
|
||||
It will look something like this:
|
||||
|
||||
```
|
||||
|
||||
var f = new FormBase();
|
||||
await this.NavigateTo(f);
|
||||
|
||||
```
|
||||
|
||||
Depending on the model and structure of your bot it can make sense, to have more linear navigation instead of "cross" navigation.
|
||||
|
||||
In example you have a bot which shows a list of football teams. And when clicking on it you want to open the team details and latest matches.
|
||||
|
||||
After the matches you want to maybe switch to a different teams and take a look at their statistics and matches.
|
||||
|
||||
At some point, you "just" want to get back to the first team so like on Android your clicking the "back" button multiple times.
|
||||
|
||||
This can become really complicated, when not having some controller below which handle these "Push/Pop" calls.
|
||||
|
||||
Thats why I introduced a NavigationController class which manages these situations and the stack.
|
||||
|
||||
|
||||
### How to use ?
|
||||
|
||||
First you need to create a NavigationController instance at the same position in code, where you want to start the navigation.
|
||||
|
||||
You will use the current FormBase instance as a root class within the constructor. So you can later come back to this one.
|
||||
|
||||
**Tip**: *You can add also a completely new instance of i.e. a main menu form here to get back to it then. So you are free to choose.*
|
||||
|
||||
We are using the same FormBase instance as above.
|
||||
|
||||
|
||||
```
|
||||
|
||||
var nc = new NavigationController(this);
|
||||
|
||||
var f = new FormBase();
|
||||
|
||||
//Replace the current form in the context with the controller.
|
||||
await this.NavigateTo(nc);
|
||||
|
||||
//Push the new from onto the stack to render it
|
||||
nc.PushAsync(f);
|
||||
|
||||
```
|
||||
|
||||
|
||||
Later to open a new form use PushAsync again:
|
||||
|
||||
```
|
||||
|
||||
await this.NavigationController.PushAsync(newForm);
|
||||
|
||||
```
|
||||
|
||||
When you want to go back one Form on the stack use PopAsync:
|
||||
|
||||
|
||||
```
|
||||
|
||||
await this.NavigationController.PopAsync();
|
||||
|
||||
```
|
||||
|
||||
**Notice**: *By default the NavigationController has ForceCleanupOnLastPop enabled, which means that when the stack is again at 1 (due to PopAsync or PopToRootAsync calls) it will replace the controller automatically with the root form you have given to the constructor at the beginning.*
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Examples
|
||||
|
||||
You find all example projects in the "Examples" subfolder.
|
||||
|
||||
- [Examples/SystemCommandsBot/](Examples/SystemCommandsBot/)
|
||||
|
||||
Will allow you to run specific system commands or run/kill processes via Bot. Has also a simple authentication mechanism with one password.
|
||||
|
||||
- [Examples/JoinHiderBot/](Examples/JoinHiderBot/)
|
||||
|
||||
Will delete Join and Leave messages automatically in groups.
|
||||
|
||||
- [Examples/AsyncFormUpdates/](Examples/AsyncFormUpdates/)
|
||||
|
||||
When you want to update forms async without any user interaction (message/action) before. Use the new InvokeMessageLoop method of BotBase.
|
||||
|
||||
---
|
||||
|
||||
I will add more notes to it soon, so stay tuned.
|
||||
|
||||
@@ -1,108 +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 TelegramBaseTest.Tests
|
||||
{
|
||||
public class Start : FormBase
|
||||
{
|
||||
|
||||
public override async Task Action(MessageResult message)
|
||||
{
|
||||
var call = message.GetData<CallbackData>();
|
||||
|
||||
await message.ConfirmAction();
|
||||
|
||||
|
||||
if (call == null)
|
||||
return;
|
||||
|
||||
switch(call.Value)
|
||||
{
|
||||
case "text":
|
||||
|
||||
var sf = new SimpleForm();
|
||||
|
||||
await this.NavigateTo(sf);
|
||||
|
||||
break;
|
||||
|
||||
case "buttons":
|
||||
|
||||
var bf = new ButtonTestForm();
|
||||
|
||||
await this.NavigateTo(bf);
|
||||
|
||||
break;
|
||||
|
||||
case "progress":
|
||||
|
||||
var pf = new ProgressTest();
|
||||
|
||||
await this.NavigateTo(pf);
|
||||
|
||||
break;
|
||||
|
||||
case "registration":
|
||||
|
||||
var reg = new Register.Start();
|
||||
|
||||
await this.NavigateTo(reg);
|
||||
|
||||
break;
|
||||
|
||||
case "form1":
|
||||
|
||||
var form1 = new TestForm();
|
||||
|
||||
await this.NavigateTo(form1);
|
||||
|
||||
break;
|
||||
|
||||
case "form2":
|
||||
|
||||
var form2 = new TestForm2();
|
||||
|
||||
await this.NavigateTo(form2);
|
||||
|
||||
break;
|
||||
|
||||
case "data":
|
||||
|
||||
message.Handled = true;
|
||||
|
||||
var data = new DataForm();
|
||||
|
||||
await this.NavigateTo(data);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
public override async Task Render(MessageResult message)
|
||||
{
|
||||
|
||||
ButtonForm btn = new ButtonForm();
|
||||
|
||||
btn.AddButtonRow(new ButtonBase("#1 Simple Text", new CallbackData("a", "text").Serialize()), new ButtonBase("#2 Button Test", new CallbackData("a", "buttons").Serialize()));
|
||||
btn.AddButtonRow(new ButtonBase("#3 Progress Bar", new CallbackData("a", "progress").Serialize()));
|
||||
btn.AddButtonRow(new ButtonBase("#4 Registration Example", new CallbackData("a", "registration").Serialize()));
|
||||
|
||||
btn.AddButtonRow(new ButtonBase("#5 Form1 Command", new CallbackData("a", "form1").Serialize()));
|
||||
|
||||
btn.AddButtonRow(new ButtonBase("#6 Form2 Command", new CallbackData("a", "form2").Serialize()));
|
||||
|
||||
btn.AddButtonRow(new ButtonBase("#7 Data Handling", new CallbackData("a", "data").Serialize()));
|
||||
|
||||
await this.Device.Send("Choose your test:", btn);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Newtonsoft.Json" version="12.0.1" targetFramework="net452" />
|
||||
<package id="System.Net.Requests" version="4.3.0" targetFramework="net452" />
|
||||
<package id="Telegram.Bot" version="14.10.0" targetFramework="net452" />
|
||||
</packages>
|
||||
+5
-5
@@ -6,12 +6,12 @@ using System.Threading.Tasks;
|
||||
using Telegram.Bot.Types;
|
||||
using TelegramBotBase.Sessions;
|
||||
|
||||
namespace TelegramBotBase.Base
|
||||
namespace TelegramBotBase.Args
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class for given system call results
|
||||
/// Base class for given bot command results
|
||||
/// </summary>
|
||||
public class SystemCallEventArgs : EventArgs
|
||||
public class BotCommandEventArgs : EventArgs
|
||||
{
|
||||
public String Command { get; set; }
|
||||
|
||||
@@ -26,13 +26,13 @@ namespace TelegramBotBase.Base
|
||||
public Message OriginalMessage { get; set; }
|
||||
|
||||
|
||||
public SystemCallEventArgs()
|
||||
public BotCommandEventArgs()
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
|
||||
public SystemCallEventArgs(String Command, List<String> Parameters, Message Message, long DeviceId, DeviceSession Device)
|
||||
public BotCommandEventArgs(String Command, List<String> Parameters, Message Message, long DeviceId, DeviceSession Device)
|
||||
{
|
||||
this.Command = Command;
|
||||
this.Parameters = Parameters;
|
||||
@@ -0,0 +1,49 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using TelegramBotBase.Controls.Hybrid;
|
||||
using TelegramBotBase.Form;
|
||||
|
||||
namespace TelegramBotBase.Args
|
||||
{
|
||||
/// <summary>
|
||||
/// Button get clicked event
|
||||
/// </summary>
|
||||
public class ButtonClickedEventArgs : EventArgs
|
||||
{
|
||||
public ButtonBase Button { get; set; }
|
||||
|
||||
public int Index { get; set; }
|
||||
|
||||
public object Tag { get; set; }
|
||||
|
||||
public ButtonRow Row { get; set; }
|
||||
|
||||
|
||||
public ButtonClickedEventArgs()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public ButtonClickedEventArgs(ButtonBase button)
|
||||
{
|
||||
this.Button = button;
|
||||
this.Index = -1;
|
||||
}
|
||||
|
||||
public ButtonClickedEventArgs(ButtonBase button, int Index)
|
||||
{
|
||||
this.Button = button;
|
||||
this.Index = Index;
|
||||
}
|
||||
|
||||
public ButtonClickedEventArgs(ButtonBase button, int Index, ButtonRow row)
|
||||
{
|
||||
this.Button = button;
|
||||
this.Index = Index;
|
||||
this.Row = row;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using TelegramBotBase.Controls.Hybrid;
|
||||
using TelegramBotBase.Form;
|
||||
|
||||
namespace TelegramBotBase.Args
|
||||
{
|
||||
public class CheckedChangedEventArgs : EventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains the index of the row where the button is inside.
|
||||
/// Contains -1 when it is a layout button or not found.
|
||||
/// </summary>
|
||||
public int Index { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Contains all buttons within this row, excluding the checkbox.
|
||||
/// </summary>
|
||||
public ButtonRow Row { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Contains the new checked status of the row.
|
||||
/// </summary>
|
||||
public bool Checked { get; set; }
|
||||
|
||||
|
||||
public CheckedChangedEventArgs()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public CheckedChangedEventArgs(ButtonRow row, int Index, bool Checked)
|
||||
{
|
||||
this.Row = row;
|
||||
this.Index = Index;
|
||||
this.Checked = Checked;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using Telegram.Bot.Types.Enums;
|
||||
using TelegramBotBase.Base;
|
||||
|
||||
namespace TelegramBotBase.Args
|
||||
{
|
||||
public class GroupChangedEventArgs : EventArgs
|
||||
{
|
||||
public MessageType Type { get; set; }
|
||||
|
||||
public MessageResult OriginalMessage { get; set; }
|
||||
|
||||
public GroupChangedEventArgs(MessageType type, MessageResult message)
|
||||
{
|
||||
this.Type = type;
|
||||
this.OriginalMessage = message;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace TelegramBotBase.Args
|
||||
{
|
||||
public class InitEventArgs : EventArgs
|
||||
{
|
||||
public object[] Args { get; set; }
|
||||
|
||||
public InitEventArgs(params object[] args)
|
||||
{
|
||||
this.Args = args;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace TelegramBotBase.Args
|
||||
{
|
||||
public class LoadStateEventArgs
|
||||
{
|
||||
public Dictionary<String,object> Values { get; set; }
|
||||
|
||||
public LoadStateEventArgs()
|
||||
{
|
||||
Values = new Dictionary<string, object>();
|
||||
}
|
||||
|
||||
public List<String> Keys
|
||||
{
|
||||
get
|
||||
{
|
||||
return Values.Keys.ToList();
|
||||
}
|
||||
}
|
||||
|
||||
public String Get(String key)
|
||||
{
|
||||
return Values[key].ToString();
|
||||
}
|
||||
|
||||
public int GetInt(String key)
|
||||
{
|
||||
int i = 0;
|
||||
if (int.TryParse(Values[key].ToString(), out i))
|
||||
return i;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public double GetDouble(String key)
|
||||
{
|
||||
double d = 0;
|
||||
if (double.TryParse(Values[key].ToString(), out d))
|
||||
return d;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public bool GetBool(String key)
|
||||
{
|
||||
bool b = false;
|
||||
if (bool.TryParse(Values[key].ToString(), out b))
|
||||
return b;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public object GetObject(String key)
|
||||
{
|
||||
return Values[key];
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Telegram.Bot.Types;
|
||||
using Telegram.Bot.Types.Enums;
|
||||
using TelegramBotBase.Base;
|
||||
|
||||
namespace TelegramBotBase.Args
|
||||
{
|
||||
public class MemberChangeEventArgs : EventArgs
|
||||
{
|
||||
public List<User> Members { get; set; }
|
||||
|
||||
public MessageType Type { get; set; }
|
||||
|
||||
public MessageResult Result { get; set; }
|
||||
|
||||
public MemberChangeEventArgs()
|
||||
{
|
||||
this.Members = new List<User>();
|
||||
|
||||
}
|
||||
|
||||
public MemberChangeEventArgs(MessageType type, MessageResult result, params User[] members)
|
||||
{
|
||||
this.Type = type;
|
||||
this.Result = result;
|
||||
this.Members = members.ToList();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Telegram.Bot.Types;
|
||||
|
||||
namespace TelegramBotBase.Args
|
||||
{
|
||||
public class MessageDeletedEventArgs
|
||||
{
|
||||
public int MessageId
|
||||
{
|
||||
get;set;
|
||||
}
|
||||
|
||||
public MessageDeletedEventArgs(int messageId)
|
||||
{
|
||||
this.MessageId = messageId;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -7,7 +7,7 @@ using TelegramBotBase.Sessions;
|
||||
|
||||
namespace TelegramBotBase.Base
|
||||
{
|
||||
public class MessageIncomeResult : EventArgs
|
||||
public class MessageIncomeEventArgs : EventArgs
|
||||
{
|
||||
|
||||
public long DeviceId { get; set; }
|
||||
@@ -16,7 +16,7 @@ namespace TelegramBotBase.Base
|
||||
|
||||
public MessageResult Message { get; set; }
|
||||
|
||||
public MessageIncomeResult(long DeviceId, DeviceSession Device, MessageResult message)
|
||||
public MessageIncomeEventArgs(long DeviceId, DeviceSession Device, MessageResult message)
|
||||
{
|
||||
this.DeviceId = DeviceId;
|
||||
this.Device = Device;
|
||||
@@ -0,0 +1,28 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Telegram.Bot.Types;
|
||||
|
||||
namespace TelegramBotBase.Args
|
||||
{
|
||||
public class MessageReceivedEventArgs
|
||||
{
|
||||
public int MessageId
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.Message.MessageId;
|
||||
}
|
||||
}
|
||||
|
||||
public Message Message { get; set; }
|
||||
|
||||
public MessageReceivedEventArgs(Message m)
|
||||
{
|
||||
this.Message = m;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Telegram.Bot.Types;
|
||||
|
||||
namespace TelegramBotBase.Args
|
||||
{
|
||||
public class MessageSentEventArgs
|
||||
{
|
||||
public int MessageId
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.Message.MessageId;
|
||||
}
|
||||
}
|
||||
|
||||
public Message Message { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Contains the element, which has called the method.
|
||||
/// </summary>
|
||||
public Type Origin { get; set; }
|
||||
|
||||
|
||||
public MessageSentEventArgs(Message message, Type Origin)
|
||||
{
|
||||
this.Message = message;
|
||||
this.Origin = Origin;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace TelegramBotBase.Args
|
||||
{
|
||||
public class PromptDialogCompletedEventArgs
|
||||
{
|
||||
public object Tag { get; set; }
|
||||
|
||||
public String Value { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace TelegramBotBase.Args
|
||||
{
|
||||
public class RenderViewEventArgs : EventArgs
|
||||
{
|
||||
public int CurrentView { get; set; }
|
||||
|
||||
|
||||
public RenderViewEventArgs(int ViewIndex)
|
||||
{
|
||||
|
||||
CurrentView = ViewIndex;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace TelegramBotBase.Args
|
||||
{
|
||||
public class SaveStateEventArgs
|
||||
{
|
||||
public Dictionary<String, object> Values { get; set; }
|
||||
|
||||
public SaveStateEventArgs()
|
||||
{
|
||||
Values = new Dictionary<string, object>();
|
||||
}
|
||||
|
||||
public void Set(String key, String value)
|
||||
{
|
||||
Values[key] = value;
|
||||
}
|
||||
|
||||
public void SetInt(String key, int value)
|
||||
{
|
||||
Values[key] = value;
|
||||
}
|
||||
|
||||
public void SetBool(String key, bool value)
|
||||
{
|
||||
Values[key] = value;
|
||||
}
|
||||
|
||||
public void SetDouble(String key, double value)
|
||||
{
|
||||
Values[key] = value;
|
||||
}
|
||||
public void SetObject(String key, object value)
|
||||
{
|
||||
Values[key] = value;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using TelegramBotBase.Base;
|
||||
using TelegramBotBase.Sessions;
|
||||
|
||||
namespace TelegramBotBase.Args
|
||||
{
|
||||
public class SaveStatesEventArgs
|
||||
{
|
||||
public StateContainer States { get; set; }
|
||||
|
||||
|
||||
public SaveStatesEventArgs(StateContainer states)
|
||||
{
|
||||
this.States = states;
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -7,13 +7,13 @@ using TelegramBotBase.Sessions;
|
||||
|
||||
namespace TelegramBotBase.Base
|
||||
{
|
||||
public class SessionBeginResult : EventArgs
|
||||
public class SessionBeginEventArgs : EventArgs
|
||||
{
|
||||
public long DeviceId { get; set; }
|
||||
|
||||
public DeviceSession Device { get; set; }
|
||||
|
||||
public SessionBeginResult(long DeviceId, DeviceSession Device)
|
||||
public SessionBeginEventArgs(long DeviceId, DeviceSession Device)
|
||||
{
|
||||
this.DeviceId = DeviceId;
|
||||
this.Device = Device;
|
||||
+1
-1
@@ -5,7 +5,7 @@ using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using TelegramBotBase.Sessions;
|
||||
|
||||
namespace TelegramBotBase.Base
|
||||
namespace TelegramBotBase.Args
|
||||
{
|
||||
public class SystemExceptionEventArgs : EventArgs
|
||||
{
|
||||
+1
-1
@@ -6,7 +6,7 @@ using System.Threading.Tasks;
|
||||
using Telegram.Bot.Types;
|
||||
using TelegramBotBase.Sessions;
|
||||
|
||||
namespace TelegramBotBase.Base
|
||||
namespace TelegramBotBase.Args
|
||||
{
|
||||
public class UnhandledCallEventArgs : EventArgs
|
||||
{
|
||||
@@ -0,0 +1,17 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace TelegramBotBase.Attributes
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Declares that this class should not be getting serialized
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public class IgnoreState : Attribute
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace TelegramBotBase.Attributes
|
||||
{
|
||||
/// <summary>
|
||||
/// Declares that the field or property should be save and recovered an restart.
|
||||
/// </summary>
|
||||
public class SaveState : Attribute
|
||||
{
|
||||
public String Key { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace TelegramBotBase.Base
|
||||
{
|
||||
public static class Async
|
||||
{
|
||||
public delegate Task AsyncEventHandler<TEventArgs>(object sender, TEventArgs e) where TEventArgs : EventArgs;
|
||||
|
||||
public static IEnumerable<AsyncEventHandler<TEventArgs>> GetHandlers<TEventArgs>(
|
||||
this AsyncEventHandler<TEventArgs> handler)
|
||||
where TEventArgs : EventArgs
|
||||
=> handler.GetInvocationList().Cast<AsyncEventHandler<TEventArgs>>();
|
||||
|
||||
public static Task InvokeAllAsync<TEventArgs>(this AsyncEventHandler<TEventArgs> handler, object sender, TEventArgs e)
|
||||
where TEventArgs : EventArgs
|
||||
=> Task.WhenAll(
|
||||
handler.GetHandlers()
|
||||
.Select(handleAsync => handleAsync(sender, e)));
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using TelegramBotBase.Form;
|
||||
|
||||
namespace TelegramBotBase.Base
|
||||
{
|
||||
/// <summary>
|
||||
/// Button get clicked event
|
||||
/// </summary>
|
||||
public class ButtonClickedEventArgs : EventArgs
|
||||
{
|
||||
public ButtonBase Button { get; set; }
|
||||
|
||||
|
||||
public ButtonClickedEventArgs()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public ButtonClickedEventArgs(ButtonBase button)
|
||||
{
|
||||
this.Button = button;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -13,14 +13,67 @@ namespace TelegramBotBase.Base
|
||||
{
|
||||
public Sessions.DeviceSession Device { get; set; }
|
||||
|
||||
public virtual async Task Render()
|
||||
public int ID { get; set; }
|
||||
|
||||
public String ControlID
|
||||
{
|
||||
get
|
||||
{
|
||||
return "#c" + this.ID.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Defines if the control should be rendered and invoked with actions
|
||||
/// </summary>
|
||||
public bool Enabled { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Get invoked when control will be added to a form and invoked.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public virtual void Init()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public virtual async Task Cleanup()
|
||||
public virtual async Task Load(MessageResult result)
|
||||
{
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
public virtual async Task Action(MessageResult result, String value = null)
|
||||
{
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
public virtual async Task Render(MessageResult result)
|
||||
{
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
public virtual async Task Hidden(bool FormClose)
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Will be called on a cleanup.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public virtual async Task Cleanup()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
@@ -97,17 +98,68 @@ namespace TelegramBotBase.Base
|
||||
this.RawMessageData = message.RawMessageData;
|
||||
this.Message = message.Message;
|
||||
|
||||
|
||||
this.Client = message.Client;
|
||||
}
|
||||
|
||||
public async Task<InputOnlineFile> DownloadDocument()
|
||||
{
|
||||
var encryptedContent = new System.IO.MemoryStream(this.Document.FileSize);
|
||||
var file = await this.Client.TelegramClient.GetInfoAndDownloadFileAsync(this.Document.FileId, encryptedContent);
|
||||
|
||||
|
||||
return new InputOnlineFile(encryptedContent, this.Document.FileName);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Downloads a file and saves it to the given path.
|
||||
/// </summary>
|
||||
/// <param name="path"></param>
|
||||
/// <returns></returns>
|
||||
public async Task DownloadDocument(String path)
|
||||
{
|
||||
var file = await this.Client.TelegramClient.GetFileAsync(this.Document.FileId);
|
||||
FileStream fs = new FileStream(path, FileMode.Create);
|
||||
await this.Client.TelegramClient.DownloadFileAsync(file.FilePath, fs);
|
||||
fs.Close();
|
||||
fs.Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Downloads the document and returns an byte array.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task<byte[]> DownloadRawDocument()
|
||||
{
|
||||
MemoryStream ms = new MemoryStream();
|
||||
await this.Client.TelegramClient.GetInfoAndDownloadFileAsync(this.Document.FileId, ms);
|
||||
return ms.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Downloads a document and returns it as string. (txt,csv,etc) Default encoding ist UTF8.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task<String> DownloadRawTextDocument()
|
||||
{
|
||||
return await DownloadRawTextDocument(Encoding.UTF8);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Downloads a document and returns it as string. (txt,csv,etc)
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task<String> DownloadRawTextDocument(Encoding encoding)
|
||||
{
|
||||
MemoryStream ms = new MemoryStream();
|
||||
await this.Client.TelegramClient.GetInfoAndDownloadFileAsync(this.Document.FileId, ms);
|
||||
|
||||
ms.Position = 0;
|
||||
|
||||
var sr = new StreamReader(ms, encoding);
|
||||
|
||||
return sr.ReadToEnd();
|
||||
}
|
||||
|
||||
public async Task<InputOnlineFile> DownloadVideo()
|
||||
{
|
||||
var encryptedContent = new System.IO.MemoryStream(this.Video.FileSize);
|
||||
@@ -116,14 +168,50 @@ namespace TelegramBotBase.Base
|
||||
return new InputOnlineFile(encryptedContent, "");
|
||||
}
|
||||
|
||||
public async Task DownloadVideo(String path)
|
||||
{
|
||||
var file = await this.Client.TelegramClient.GetFileAsync(this.Video.FileId);
|
||||
FileStream fs = new FileStream(path, FileMode.Create);
|
||||
await this.Client.TelegramClient.DownloadFileAsync(file.FilePath, fs);
|
||||
fs.Close();
|
||||
fs.Dispose();
|
||||
}
|
||||
|
||||
public async Task<InputOnlineFile> DownloadAudio()
|
||||
{
|
||||
var encryptedContent = new System.IO.MemoryStream(this.Audio.FileSize);
|
||||
var file = await this.Client.TelegramClient.GetInfoAndDownloadFileAsync(this.Audio.FileId, encryptedContent);
|
||||
|
||||
return new InputOnlineFile(encryptedContent, "");
|
||||
}
|
||||
|
||||
public async Task DownloadAudio(String path)
|
||||
{
|
||||
var file = await this.Client.TelegramClient.GetFileAsync(this.Audio.FileId);
|
||||
FileStream fs = new FileStream(path, FileMode.Create);
|
||||
await this.Client.TelegramClient.DownloadFileAsync(file.FilePath, fs);
|
||||
fs.Close();
|
||||
fs.Dispose();
|
||||
}
|
||||
|
||||
public async Task<InputOnlineFile> DownloadPhoto(int index)
|
||||
{
|
||||
var photo = this.Photos[index];
|
||||
var encryptedContent = new System.IO.MemoryStream(photo.FileSize);
|
||||
var file = await this.Client.TelegramClient.GetInfoAndDownloadFileAsync(photo.FileId, encryptedContent);
|
||||
|
||||
|
||||
return new InputOnlineFile(encryptedContent, "");
|
||||
}
|
||||
|
||||
public async Task DownloadPhoto(int index, String path)
|
||||
{
|
||||
var photo = this.Photos[index];
|
||||
var file = await this.Client.TelegramClient.GetFileAsync(photo.FileId);
|
||||
FileStream fs = new FileStream(path, FileMode.Create);
|
||||
await this.Client.TelegramClient.DownloadFileAsync(file.FilePath, fs);
|
||||
fs.Close();
|
||||
fs.Dispose();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using TelegramBotBase.Args;
|
||||
using TelegramBotBase.Base;
|
||||
using TelegramBotBase.Form.Navigation;
|
||||
using TelegramBotBase.Sessions;
|
||||
using static TelegramBotBase.Base.Async;
|
||||
|
||||
namespace TelegramBotBase.Form
|
||||
{
|
||||
@@ -13,51 +17,159 @@ namespace TelegramBotBase.Form
|
||||
/// </summary>
|
||||
public class FormBase : IDisposable
|
||||
{
|
||||
|
||||
public NavigationController NavigationController { get; set; }
|
||||
|
||||
public DeviceSession Device { get; set; }
|
||||
|
||||
public MessageClient Client { get; set; }
|
||||
|
||||
public bool CustomEventManagement { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// contains if the form has been switched (navigated)
|
||||
/// has this formular already been disposed ?
|
||||
/// </summary>
|
||||
public bool FormSwitched { get; set; } = false;
|
||||
public bool IsDisposed { get; set; } = false;
|
||||
|
||||
public List<ControlBase> Controls { get; set; }
|
||||
|
||||
|
||||
public EventHandlerList Events = new EventHandlerList();
|
||||
|
||||
|
||||
private static object __evInit = new object();
|
||||
|
||||
private static object __evOpened = new object();
|
||||
|
||||
private static object __evClosed = new object();
|
||||
|
||||
|
||||
public FormBase()
|
||||
{
|
||||
this.Controls = new List<Base.ControlBase>();
|
||||
}
|
||||
|
||||
public FormBase(MessageClient Client): this()
|
||||
public FormBase(MessageClient Client) : this()
|
||||
{
|
||||
this.Client = Client;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Will get called at the initialization (once per context)
|
||||
/// </summary>
|
||||
public virtual async Task Init(params object[] args)
|
||||
|
||||
public async Task OnInit(InitEventArgs e)
|
||||
{
|
||||
|
||||
if (this.Events[__evInit] == null)
|
||||
return;
|
||||
|
||||
var handler = this.Events[__evInit]?.GetInvocationList().Cast<AsyncEventHandler<InitEventArgs>>();
|
||||
if (handler == null)
|
||||
return;
|
||||
|
||||
foreach (var h in handler)
|
||||
{
|
||||
await Async.InvokeAllAsync<InitEventArgs>(h, this, e);
|
||||
}
|
||||
}
|
||||
|
||||
///// <summary>
|
||||
///// Will get called at the initialization (once per context)
|
||||
///// </summary>
|
||||
public event AsyncEventHandler<InitEventArgs> Init
|
||||
{
|
||||
add
|
||||
{
|
||||
this.Events.AddHandler(__evInit, value);
|
||||
}
|
||||
remove
|
||||
{
|
||||
this.Events.RemoveHandler(__evInit, value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public async Task OnOpened(EventArgs e)
|
||||
{
|
||||
if (this.Events[__evOpened] == null)
|
||||
return;
|
||||
|
||||
var handler = this.Events[__evOpened]?.GetInvocationList().Cast<AsyncEventHandler<EventArgs>>();
|
||||
if (handler == null)
|
||||
return;
|
||||
|
||||
foreach (var h in handler)
|
||||
{
|
||||
await Async.InvokeAllAsync<EventArgs>(h, this, e);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets invoked if gets navigated to this form
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public virtual async Task Opened()
|
||||
public event AsyncEventHandler<EventArgs> Opened
|
||||
{
|
||||
add
|
||||
{
|
||||
this.Events.AddHandler(__evOpened, value);
|
||||
}
|
||||
remove
|
||||
{
|
||||
this.Events.RemoveHandler(__evOpened, value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public async Task OnClosed(EventArgs e)
|
||||
{
|
||||
if (this.Events[__evClosed] == null)
|
||||
return;
|
||||
|
||||
var handler = this.Events[__evClosed]?.GetInvocationList().Cast<AsyncEventHandler<EventArgs>>();
|
||||
if (handler == null)
|
||||
return;
|
||||
|
||||
foreach (var h in handler)
|
||||
{
|
||||
await Async.InvokeAllAsync<EventArgs>(h, this, e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Form has been closed (left)
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public event AsyncEventHandler<EventArgs> Closed
|
||||
{
|
||||
add
|
||||
{
|
||||
this.Events.AddHandler(__evClosed, value);
|
||||
}
|
||||
remove
|
||||
{
|
||||
this.Events.RemoveHandler(__evClosed, value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get invoked when a modal child from has been closed.
|
||||
/// </summary>
|
||||
/// <param name="message"></param>
|
||||
/// <returns></returns>
|
||||
public virtual async Task ReturnFromModal(ModalDialog modal)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public virtual async Task Closed()
|
||||
|
||||
/// <summary>
|
||||
/// Pre to form close, cleanup all controls
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task CloseControls()
|
||||
{
|
||||
foreach (var b in this.Controls)
|
||||
{
|
||||
await b.Cleanup();
|
||||
b.Cleanup().Wait();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,6 +178,33 @@ namespace TelegramBotBase.Form
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets invoked if a message was sent or an action triggered
|
||||
/// </summary>
|
||||
/// <param name="message"></param>
|
||||
/// <returns></returns>
|
||||
public virtual async Task LoadControls(MessageResult message)
|
||||
{
|
||||
//Looking for the control by id, if not listened, raise event for all
|
||||
if (message.RawData?.StartsWith("#c") ?? false)
|
||||
{
|
||||
var c = this.Controls.FirstOrDefault(a => a.ControlID == message.RawData.Split('_')[0]);
|
||||
if (c != null)
|
||||
{
|
||||
await c.Load(message);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var b in this.Controls)
|
||||
{
|
||||
if (!b.Enabled)
|
||||
continue;
|
||||
|
||||
await b.Load(message);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets invoked if the form gets loaded and on every message belongs to this context
|
||||
/// </summary>
|
||||
@@ -76,6 +215,47 @@ namespace TelegramBotBase.Form
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets invoked, when a messages has been edited.
|
||||
/// </summary>
|
||||
/// <param name="message"></param>
|
||||
/// <returns></returns>
|
||||
public virtual async Task Edited(MessageResult message)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets invoked if the user clicked a button.
|
||||
/// </summary>
|
||||
/// <param name="message"></param>
|
||||
/// <returns></returns>
|
||||
public virtual async Task ActionControls(MessageResult message)
|
||||
{
|
||||
//Looking for the control by id, if not listened, raise event for all
|
||||
if (message.RawData.StartsWith("#c"))
|
||||
{
|
||||
var c = this.Controls.FirstOrDefault(a => a.ControlID == message.RawData.Split('_')[0]);
|
||||
if (c != null)
|
||||
{
|
||||
await c.Action(message, message.RawData.Split('_')[1]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var b in this.Controls)
|
||||
{
|
||||
if (!b.Enabled)
|
||||
continue;
|
||||
|
||||
await b.Action(message);
|
||||
|
||||
if (message.Handled)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets invoked if the user has clicked a button.
|
||||
/// </summary>
|
||||
@@ -96,6 +276,22 @@ namespace TelegramBotBase.Form
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets invoked at the end of the cycle to "Render" text, images, buttons, etc...
|
||||
/// </summary>
|
||||
/// <param name="message"></param>
|
||||
/// <returns></returns>
|
||||
public virtual async Task RenderControls(MessageResult message)
|
||||
{
|
||||
foreach (var b in this.Controls)
|
||||
{
|
||||
if (!b.Enabled)
|
||||
continue;
|
||||
|
||||
await b.Render(message);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets invoked at the end of the cycle to "Render" text, images, buttons, etc...
|
||||
/// </summary>
|
||||
@@ -106,28 +302,143 @@ namespace TelegramBotBase.Form
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Navigates to a new form
|
||||
/// </summary>
|
||||
/// <param name="newForm"></param>
|
||||
/// <returns></returns>
|
||||
public async Task NavigateTo(FormBase newForm, params object[] args)
|
||||
public virtual async Task NavigateTo(FormBase newForm, params object[] args)
|
||||
{
|
||||
DeviceSession ds = this.Device;
|
||||
if (ds == null)
|
||||
return;
|
||||
|
||||
this.FormSwitched = true;
|
||||
ds.FormSwitched = true;
|
||||
|
||||
ds.PreviousForm = ds.ActiveForm;
|
||||
|
||||
ds.ActiveForm = newForm;
|
||||
newForm.Client = this.Client;
|
||||
newForm.Device = ds;
|
||||
|
||||
await newForm.Init(args);
|
||||
//Notify prior to close
|
||||
foreach (var b in this.Controls)
|
||||
{
|
||||
if (!b.Enabled)
|
||||
continue;
|
||||
|
||||
await this.Closed();
|
||||
await b.Hidden(true);
|
||||
}
|
||||
|
||||
await newForm.Opened();
|
||||
this.CloseControls().Wait();
|
||||
|
||||
await this.OnClosed(new EventArgs());
|
||||
|
||||
await newForm.OnInit(new InitEventArgs(args));
|
||||
|
||||
await newForm.OnOpened(new EventArgs());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens this form modal, but don't closes the original ones
|
||||
/// </summary>
|
||||
/// <param name="newForm"></param>
|
||||
/// <returns></returns>
|
||||
public virtual async Task OpenModal(ModalDialog newForm, params object[] args)
|
||||
{
|
||||
DeviceSession ds = this.Device;
|
||||
if (ds == null)
|
||||
return;
|
||||
|
||||
var parentForm = this;
|
||||
|
||||
ds.FormSwitched = true;
|
||||
|
||||
ds.PreviousForm = ds.ActiveForm;
|
||||
|
||||
ds.ActiveForm = newForm;
|
||||
newForm.Client = parentForm.Client;
|
||||
newForm.Device = ds;
|
||||
newForm.ParentForm = parentForm;
|
||||
|
||||
newForm.Closed += async (s, en) =>
|
||||
{
|
||||
await CloseModal(newForm, parentForm);
|
||||
};
|
||||
|
||||
foreach (var b in this.Controls)
|
||||
{
|
||||
if (!b.Enabled)
|
||||
continue;
|
||||
|
||||
await b.Hidden(false);
|
||||
}
|
||||
|
||||
await newForm.OnInit(new InitEventArgs(args));
|
||||
|
||||
await newForm.OnOpened(new EventArgs());
|
||||
}
|
||||
|
||||
public async Task CloseModal(ModalDialog modalForm, FormBase oldForm)
|
||||
{
|
||||
DeviceSession ds = this.Device;
|
||||
if (ds == null)
|
||||
return;
|
||||
|
||||
if (modalForm == null)
|
||||
throw new Exception("No modal form");
|
||||
|
||||
ds.FormSwitched = true;
|
||||
|
||||
ds.PreviousForm = ds.ActiveForm;
|
||||
|
||||
ds.ActiveForm = oldForm;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a control to the formular and sets its ID and Device.
|
||||
/// </summary>
|
||||
/// <param name="control"></param>
|
||||
public void AddControl(ControlBase control)
|
||||
{
|
||||
//Duplicate check
|
||||
if (this.Controls.Contains(control))
|
||||
throw new ArgumentException("Control has been already added.");
|
||||
|
||||
control.ID = this.Controls.Count + 1;
|
||||
control.Device = this.Device;
|
||||
this.Controls.Add(control);
|
||||
|
||||
control.Init();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes control from the formular and runs a cleanup on it.
|
||||
/// </summary>
|
||||
/// <param name="control"></param>
|
||||
public void RemoveControl(ControlBase control)
|
||||
{
|
||||
if (!this.Controls.Contains(control))
|
||||
return;
|
||||
|
||||
control.Cleanup().Wait();
|
||||
|
||||
this.Controls.Remove(control);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes all controls.
|
||||
/// </summary>
|
||||
public void RemoveAllControls()
|
||||
{
|
||||
foreach(var c in this.Controls)
|
||||
{
|
||||
c.Cleanup().Wait();
|
||||
|
||||
this.Controls.Remove(c);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -137,7 +448,7 @@ namespace TelegramBotBase.Form
|
||||
{
|
||||
this.Client = null;
|
||||
this.Device = null;
|
||||
this.FormSwitched = false;
|
||||
this.IsDisposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Telegram.Bot.Types;
|
||||
|
||||
namespace TelegramBotBase.Base
|
||||
{
|
||||
@@ -15,6 +16,7 @@ namespace TelegramBotBase.Base
|
||||
public class MessageClient
|
||||
{
|
||||
|
||||
|
||||
public String APIKey { get; set; }
|
||||
|
||||
public Telegram.Bot.TelegramBotClient TelegramClient { get; set; }
|
||||
@@ -23,6 +25,8 @@ namespace TelegramBotBase.Base
|
||||
|
||||
private static object __evOnMessage = new object();
|
||||
|
||||
private static object __evOnMessageEdit = new object();
|
||||
|
||||
private static object __evCallbackQuery = new object();
|
||||
|
||||
|
||||
@@ -38,7 +42,7 @@ namespace TelegramBotBase.Base
|
||||
{
|
||||
this.APIKey = APIKey;
|
||||
this.TelegramClient = new Telegram.Bot.TelegramBotClient(APIKey, proxy);
|
||||
|
||||
|
||||
|
||||
Prepare();
|
||||
}
|
||||
@@ -84,13 +88,11 @@ namespace TelegramBotBase.Base
|
||||
{
|
||||
this.TelegramClient.Timeout = new TimeSpan(0, 0, 30);
|
||||
|
||||
|
||||
this.TelegramClient.OnMessage += TelegramClient_OnMessage;
|
||||
this.TelegramClient.OnMessageEdited += TelegramClient_OnMessageEdited;
|
||||
this.TelegramClient.OnCallbackQuery += TelegramClient_OnCallbackQuery;
|
||||
|
||||
}
|
||||
|
||||
|
||||
private async void TelegramClient_OnMessage(object sender, Telegram.Bot.Args.MessageEventArgs e)
|
||||
{
|
||||
//Skip empty messages by default
|
||||
@@ -109,6 +111,25 @@ namespace TelegramBotBase.Base
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private async void TelegramClient_OnMessageEdited(object sender, Telegram.Bot.Args.MessageEventArgs e)
|
||||
{
|
||||
//Skip empty messages by default
|
||||
if (e.Message == null)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
var mr = new MessageResult(e);
|
||||
mr.Client = this;
|
||||
OnMessageEdit(mr);
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private async void TelegramClient_OnCallbackQuery(object sender, Telegram.Bot.Args.CallbackQueryEventArgs e)
|
||||
{
|
||||
try
|
||||
@@ -123,6 +144,27 @@ namespace TelegramBotBase.Base
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This will return the current list of bot commands.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task<BotCommand[]> GetBotCommands()
|
||||
{
|
||||
return await this.TelegramClient.GetMyCommandsAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This will set your bot commands to the given list.
|
||||
/// </summary>
|
||||
/// <param name="botcommands"></param>
|
||||
/// <returns></returns>
|
||||
public async Task SetBotCommands(List<BotCommand> botcommands)
|
||||
{
|
||||
await this.TelegramClient.SetMyCommandsAsync(botcommands);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#region "Events"
|
||||
|
||||
@@ -143,6 +185,23 @@ namespace TelegramBotBase.Base
|
||||
(this.__Events[__evOnMessage] as EventHandler<MessageResult>)?.Invoke(this, result);
|
||||
}
|
||||
|
||||
public event EventHandler<MessageResult> MessageEdit
|
||||
{
|
||||
add
|
||||
{
|
||||
this.__Events.AddHandler(__evOnMessageEdit, value);
|
||||
}
|
||||
remove
|
||||
{
|
||||
this.__Events.RemoveHandler(__evOnMessageEdit, value);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnMessageEdit(MessageResult result)
|
||||
{
|
||||
(this.__Events[__evOnMessageEdit] as EventHandler<MessageResult>)?.Invoke(this, result);
|
||||
}
|
||||
|
||||
public event EventHandler<MessageResult> Action
|
||||
{
|
||||
add
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using TelegramBotBase.Sessions;
|
||||
|
||||
namespace TelegramBotBase.Base
|
||||
{
|
||||
@@ -19,10 +20,19 @@ namespace TelegramBotBase.Base
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.RawMessageData?.Message?.Chat.Id ?? this.RawCallbackData?.CallbackQuery.Message?.Chat.Id ?? 0;
|
||||
return this.RawMessageData?.Message?.Chat.Id
|
||||
?? this.RawCallbackData?.CallbackQuery.Message?.Chat.Id
|
||||
?? Device?.DeviceId
|
||||
?? 0;
|
||||
}
|
||||
}
|
||||
|
||||
public DeviceSession Device
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The message id
|
||||
/// </summary>
|
||||
@@ -30,7 +40,9 @@ namespace TelegramBotBase.Base
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.Message?.MessageId ?? this.RawCallbackData?.CallbackQuery?.Message?.MessageId ?? 0;
|
||||
return this.Message?.MessageId
|
||||
?? this.RawCallbackData?.CallbackQuery?.Message?.MessageId
|
||||
?? 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,6 +62,15 @@ namespace TelegramBotBase.Base
|
||||
}
|
||||
}
|
||||
|
||||
public Telegram.Bot.Types.Enums.MessageType MessageType
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.RawMessageData?.Message?.Type
|
||||
?? Telegram.Bot.Types.Enums.MessageType.Unknown;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Is this an action ? (i.e. button click)
|
||||
/// </summary>
|
||||
@@ -62,9 +83,9 @@ namespace TelegramBotBase.Base
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Is this a system call ? Starts with a slash '/' and a command
|
||||
/// Is this a command ? Starts with a slash '/' and a command
|
||||
/// </summary>
|
||||
public bool IsSystemCall
|
||||
public bool IsBotCommand
|
||||
{
|
||||
get
|
||||
{
|
||||
@@ -75,11 +96,11 @@ namespace TelegramBotBase.Base
|
||||
/// <summary>
|
||||
/// Returns a List of all parameters which has been sent with the command itself (i.e. /start 123 456 789 => 123,456,789)
|
||||
/// </summary>
|
||||
public List<String> SystemCallParameters
|
||||
public List<String> BotCommandParameters
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!IsSystemCall)
|
||||
if (!IsBotCommand)
|
||||
return new List<string>();
|
||||
|
||||
//Split by empty space and skip first entry (command itself), return as list
|
||||
@@ -88,19 +109,24 @@ namespace TelegramBotBase.Base
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns just the System call command (i.e. /start 1 2 3 => /start)
|
||||
/// Returns just the command (i.e. /start 1 2 3 => /start)
|
||||
/// </summary>
|
||||
public String SystemCommand
|
||||
public String BotCommand
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!IsSystemCall)
|
||||
if (!IsBotCommand)
|
||||
return null;
|
||||
|
||||
return this.MessageText.Split(' ')[0];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns if this message will be used on the first form or not.
|
||||
/// </summary>
|
||||
public bool IsFirstHandler { get; set; } = true;
|
||||
|
||||
public bool Handled { get; set; } = false;
|
||||
|
||||
public String RawData
|
||||
@@ -134,11 +160,11 @@ namespace TelegramBotBase.Base
|
||||
/// </summary>
|
||||
/// <param name="message"></param>
|
||||
/// <returns></returns>
|
||||
public async Task ConfirmAction(String message = "")
|
||||
public async Task ConfirmAction(String message = "", bool showAlert = false, String urlToOpen = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
await this.Client.TelegramClient.AnswerCallbackQueryAsync(this.RawCallbackData.CallbackQuery.Id, message);
|
||||
await this.Client.TelegramClient.AnswerCallbackQueryAsync(this.RawCallbackData.CallbackQuery.Id, message, showAlert, urlToOpen);
|
||||
}
|
||||
catch
|
||||
{
|
||||
@@ -158,6 +184,10 @@ namespace TelegramBotBase.Base
|
||||
}
|
||||
}
|
||||
|
||||
internal MessageResult()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public MessageResult(Telegram.Bot.Args.MessageEventArgs rawdata)
|
||||
{
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Telegram.Bot.Types;
|
||||
|
||||
namespace TelegramBotBase.Base
|
||||
{
|
||||
public class MessageSentEventArgs
|
||||
{
|
||||
public int MessageId { get; set; }
|
||||
|
||||
public Message Message { get; set; }
|
||||
|
||||
public MessageSentEventArgs()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public MessageSentEventArgs(int MessageId, Message message)
|
||||
{
|
||||
this.MessageId = MessageId;
|
||||
this.Message = message;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace TelegramBotBase.Base
|
||||
{
|
||||
public class ResultBase
|
||||
public class ResultBase : EventArgs
|
||||
{
|
||||
public MessageClient Client { get; set; }
|
||||
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace TelegramBotBase.Base
|
||||
{
|
||||
public partial class StateContainer
|
||||
{
|
||||
public List<StateEntry> States { get; set; }
|
||||
|
||||
public List<long> ChatIds
|
||||
{
|
||||
get
|
||||
{
|
||||
return States.Where(a => a.DeviceId > 0).Select(a => a.DeviceId).ToList();
|
||||
}
|
||||
}
|
||||
|
||||
public List<long> GroupIds
|
||||
{
|
||||
get
|
||||
{
|
||||
return States.Where(a => a.DeviceId < 0).Select(a => a.DeviceId).ToList();
|
||||
}
|
||||
}
|
||||
|
||||
public StateContainer()
|
||||
{
|
||||
this.States = new List<StateEntry>();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Text;
|
||||
|
||||
namespace TelegramBotBase.Base
|
||||
{
|
||||
|
||||
[DebuggerDisplay("Device: {DeviceId}, {FormUri}")]
|
||||
public class StateEntry
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains the DeviceId of the entry.
|
||||
/// </summary>
|
||||
public long DeviceId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Contains the Username (on privat chats) or Group title on groups/channels.
|
||||
/// </summary>
|
||||
public String ChatTitle { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Contains additional values to save.
|
||||
/// </summary>
|
||||
public Dictionary<String, object> Values { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Contains the full qualified namespace of the form to used for reload it via reflection.
|
||||
/// </summary>
|
||||
public String FormUri {get;set;}
|
||||
|
||||
/// <summary>
|
||||
/// Contains the assembly, where to find that form.
|
||||
/// </summary>
|
||||
public String QualifiedName { get; set; }
|
||||
|
||||
public StateEntry()
|
||||
{
|
||||
this.Values = new Dictionary<string, object>();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+433
-142
@@ -5,8 +5,13 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Telegram.Bot;
|
||||
using Telegram.Bot.Types;
|
||||
using TelegramBotBase.Args;
|
||||
using TelegramBotBase.Attributes;
|
||||
using TelegramBotBase.Base;
|
||||
using TelegramBotBase.Enums;
|
||||
using TelegramBotBase.Form;
|
||||
using TelegramBotBase.Interfaces;
|
||||
using TelegramBotBase.Sessions;
|
||||
|
||||
namespace TelegramBotBase
|
||||
@@ -28,12 +33,15 @@ namespace TelegramBotBase
|
||||
/// <summary>
|
||||
/// List of all running/active sessions
|
||||
/// </summary>
|
||||
public SessionBase Sessions { get; set; }
|
||||
public SessionBase<T> Sessions { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Contains System commands which will be available at everytime and didnt get passed to forms, i.e. /start
|
||||
/// </summary>
|
||||
public List<String> SystemCalls { get; set; }
|
||||
public List<BotCommand> BotCommands { get; set; }
|
||||
|
||||
|
||||
#region "Events"
|
||||
|
||||
private EventHandlerList __Events = new EventHandlerList();
|
||||
|
||||
@@ -43,41 +51,53 @@ namespace TelegramBotBase
|
||||
|
||||
private static object __evSystemCall = new object();
|
||||
|
||||
public delegate Task SystemCallEventHandler(object sender, SystemCallEventArgs e);
|
||||
public delegate Task BotCommandEventHandler(object sender, BotCommandEventArgs e);
|
||||
|
||||
private static object __evException = new object();
|
||||
|
||||
private static object __evUnhandledCall = new object();
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Skips all messages during running (good for big delay updates)
|
||||
/// Enable the SessionState (you need to implement on call forms the IStateForm interface)
|
||||
/// </summary>
|
||||
public bool SkipAllMessages { get; set; } = false;
|
||||
public IStateMachine StateMachine { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Loggs all messages and sent them to the event handler
|
||||
/// </summary>
|
||||
public bool LogAllMessages { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// How often could a form navigate to another (within one user action/call/message)
|
||||
/// </summary>
|
||||
private const int NavigationMaximum = 10;
|
||||
public Dictionary<eSettings, uint> SystemSettings { get; private set; }
|
||||
|
||||
private BotBase()
|
||||
{
|
||||
this.SystemSettings = new Dictionary<eSettings, uint>();
|
||||
|
||||
SetSetting(eSettings.MaxNumberOfRetries, 5);
|
||||
SetSetting(eSettings.NavigationMaximum, 10);
|
||||
SetSetting(eSettings.LogAllMessages, false);
|
||||
SetSetting(eSettings.SkipAllMessages, false);
|
||||
SetSetting(eSettings.SaveSessionsOnConsoleExit, false);
|
||||
|
||||
this.BotCommands = new List<BotCommand>();
|
||||
|
||||
this.Sessions = new SessionBase<T>();
|
||||
this.Sessions.BotBase = this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Simple start of your Bot with the APIKey
|
||||
/// </summary>
|
||||
/// <param name="apiKey"></param>
|
||||
public BotBase(String apiKey)
|
||||
public BotBase(String apiKey, bool initClient = true) : this()
|
||||
{
|
||||
this.APIKey = apiKey;
|
||||
|
||||
if (!initClient)
|
||||
return;
|
||||
|
||||
this.Client = new Base.MessageClient(this.APIKey);
|
||||
this.Client.TelegramClient.Timeout = new TimeSpan(0, 1, 0);
|
||||
|
||||
this.SystemCalls = new List<string>();
|
||||
|
||||
this.Sessions = new SessionBase();
|
||||
this.Sessions.Client = this.Client;
|
||||
}
|
||||
|
||||
@@ -86,15 +106,10 @@ namespace TelegramBotBase
|
||||
/// </summary>
|
||||
/// <param name="apiKey"></param>
|
||||
/// <param name="proxyBaseAddress">i.e. https://127.0.0.1:10000</param>
|
||||
public BotBase(String apiKey, System.Net.Http.HttpClient proxy)
|
||||
public BotBase(String apiKey, System.Net.Http.HttpClient proxy) : this(apiKey, false)
|
||||
{
|
||||
this.APIKey = apiKey;
|
||||
|
||||
this.Client = new Base.MessageClient(this.APIKey, proxy);
|
||||
|
||||
this.SystemCalls = new List<string>();
|
||||
|
||||
this.Sessions = new SessionBase();
|
||||
this.Sessions.Client = this.Client;
|
||||
}
|
||||
|
||||
@@ -103,15 +118,10 @@ namespace TelegramBotBase
|
||||
/// </summary>
|
||||
/// <param name="apiKey"></param>
|
||||
/// <param name="client"></param>
|
||||
public BotBase(String apiKey, TelegramBotClient client)
|
||||
public BotBase(String apiKey, TelegramBotClient client) : this(apiKey, false)
|
||||
{
|
||||
this.APIKey = apiKey;
|
||||
|
||||
this.Client = new Base.MessageClient(this.APIKey, client);
|
||||
|
||||
this.SystemCalls = new List<string>();
|
||||
|
||||
this.Sessions = new SessionBase();
|
||||
this.Sessions.Client = this.Client;
|
||||
}
|
||||
|
||||
@@ -120,17 +130,12 @@ namespace TelegramBotBase
|
||||
/// </summary>
|
||||
/// <param name="apiKey"></param>
|
||||
/// <param name="proxyBaseAddress">i.e. https://127.0.0.1:10000</param>
|
||||
public BotBase(String apiKey, String proxyBaseAddress)
|
||||
public BotBase(String apiKey, String proxyBaseAddress) : this(apiKey, false)
|
||||
{
|
||||
this.APIKey = apiKey;
|
||||
|
||||
var url = new Uri(proxyBaseAddress);
|
||||
|
||||
this.Client = new Base.MessageClient(this.APIKey, url);
|
||||
|
||||
this.SystemCalls = new List<string>();
|
||||
|
||||
this.Sessions = new SessionBase();
|
||||
this.Sessions.Client = this.Client;
|
||||
}
|
||||
|
||||
@@ -140,15 +145,10 @@ namespace TelegramBotBase
|
||||
/// <param name="apiKey"></param>
|
||||
/// <param name="proxyHost">i.e. 127.0.0.1</param>
|
||||
/// <param name="proxyPort">i.e. 10000</param>
|
||||
public BotBase(String apiKey, String proxyHost, int proxyPort)
|
||||
public BotBase(String apiKey, String proxyHost, int proxyPort) : this(apiKey, false)
|
||||
{
|
||||
this.APIKey = apiKey;
|
||||
|
||||
this.Client = new Base.MessageClient(this.APIKey, proxyHost, proxyPort);
|
||||
|
||||
this.SystemCalls = new List<string>();
|
||||
|
||||
this.Sessions = new SessionBase();
|
||||
this.Sessions.Client = this.Client;
|
||||
}
|
||||
|
||||
@@ -161,12 +161,29 @@ namespace TelegramBotBase
|
||||
return;
|
||||
|
||||
this.Client.Message += Client_Message;
|
||||
this.Client.MessageEdit += Client_MessageEdit;
|
||||
this.Client.Action += Client_Action;
|
||||
|
||||
if (this.StateMachine != null)
|
||||
{
|
||||
this.Sessions.LoadSessionStates(this.StateMachine);
|
||||
}
|
||||
|
||||
//Enable auto session saving
|
||||
if (this.GetSetting(eSettings.SaveSessionsOnConsoleExit, false))
|
||||
{
|
||||
TelegramBotBase.Tools.Console.SetHandler(() =>
|
||||
{
|
||||
this.Sessions.SaveSessionStates();
|
||||
});
|
||||
}
|
||||
|
||||
DeviceSession.MaxNumberOfRetries = this.GetSetting(eSettings.MaxNumberOfRetries, 5);
|
||||
|
||||
this.Client.TelegramClient.StartReceiving();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Stop your Bot
|
||||
/// </summary>
|
||||
@@ -175,7 +192,12 @@ namespace TelegramBotBase
|
||||
if (this.Client == null)
|
||||
return;
|
||||
|
||||
this.Client.Message -= Client_Message;
|
||||
this.Client.Action -= Client_Action;
|
||||
|
||||
this.Client.TelegramClient.StopReceiving();
|
||||
|
||||
this.Sessions.SaveSessionStates();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -194,21 +216,24 @@ namespace TelegramBotBase
|
||||
}
|
||||
}
|
||||
|
||||
private void Client_Message(object sender, MessageResult e)
|
||||
private async void Client_Message(object sender, MessageResult e)
|
||||
{
|
||||
if (this.SkipAllMessages)
|
||||
if (this.GetSetting(eSettings.SkipAllMessages, false))
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
if (LogAllMessages)
|
||||
DeviceSession ds = this.Sessions.GetSession(e.DeviceId);
|
||||
e.Device = ds;
|
||||
|
||||
if (this.GetSetting(eSettings.LogAllMessages, false))
|
||||
{
|
||||
DeviceSession ds2 = this.Sessions.GetSession(e.DeviceId);
|
||||
OnMessage(new MessageIncomeResult(e.DeviceId, ds2, e));
|
||||
OnMessage(new MessageIncomeEventArgs(e.DeviceId, ds, e));
|
||||
}
|
||||
|
||||
ds?.OnMessageReceived(new MessageReceivedEventArgs(e.Message));
|
||||
|
||||
Client_TryMessage(sender, e);
|
||||
await Client_Loop(sender, e);
|
||||
}
|
||||
catch (Telegram.Bot.Exceptions.ApiRequestException ex)
|
||||
{
|
||||
@@ -221,26 +246,99 @@ namespace TelegramBotBase
|
||||
}
|
||||
}
|
||||
|
||||
private async void Client_TryMessage(object sender, MessageResult e)
|
||||
|
||||
|
||||
//private async Task Client_TryMessage(object sender, MessageResult e)
|
||||
//{
|
||||
// DeviceSession ds = e.Device;
|
||||
// if (ds == null)
|
||||
// {
|
||||
// ds = await this.Sessions.StartSession<T>(e.DeviceId);
|
||||
// e.Device = ds;
|
||||
|
||||
// ds.LastMessage = e.Message;
|
||||
|
||||
// OnSessionBegins(new SessionBeginEventArgs(e.DeviceId, ds));
|
||||
// }
|
||||
|
||||
// ds.LastAction = DateTime.Now;
|
||||
// ds.LastMessage = e.Message;
|
||||
|
||||
// //Is this a bot command ?
|
||||
// if (e.IsBotCommand && this.BotCommands.Count(a => "/" + a.Command == e.BotCommand) > 0)
|
||||
// {
|
||||
// var sce = new BotCommandEventArgs(e.BotCommand, e.BotCommandParameters, e.Message, ds.DeviceId, ds);
|
||||
// await OnBotCommand(sce);
|
||||
|
||||
// if (sce.Handled)
|
||||
// return;
|
||||
// }
|
||||
|
||||
// FormBase activeForm = null;
|
||||
|
||||
// int i = 0;
|
||||
|
||||
// //Should formulars get navigated (allow maximum of 10, to dont get loops)
|
||||
// do
|
||||
// {
|
||||
// i++;
|
||||
|
||||
// //Reset navigation
|
||||
// ds.FormSwitched = false;
|
||||
|
||||
// activeForm = ds.ActiveForm;
|
||||
|
||||
// //Pre Loading Event
|
||||
// await activeForm.PreLoad(e);
|
||||
|
||||
// //Send Load event to controls
|
||||
// await activeForm.LoadControls(e);
|
||||
|
||||
// //Loading Event
|
||||
// await activeForm.Load(e);
|
||||
|
||||
// //Is Attachment ? (Photo, Audio, Video, Contact, Location, Document)
|
||||
// if (e.Message.Type == Telegram.Bot.Types.Enums.MessageType.Contact | e.Message.Type == Telegram.Bot.Types.Enums.MessageType.Document | e.Message.Type == Telegram.Bot.Types.Enums.MessageType.Location |
|
||||
// e.Message.Type == Telegram.Bot.Types.Enums.MessageType.Photo | e.Message.Type == Telegram.Bot.Types.Enums.MessageType.Video | e.Message.Type == Telegram.Bot.Types.Enums.MessageType.Audio)
|
||||
// {
|
||||
// await activeForm.SentData(new DataResult(e));
|
||||
// }
|
||||
|
||||
// //Render Event
|
||||
// if (!ds.FormSwitched)
|
||||
// {
|
||||
// await activeForm.RenderControls(e);
|
||||
|
||||
// await activeForm.Render(e);
|
||||
// }
|
||||
|
||||
// e.IsFirstHandler = false;
|
||||
|
||||
// } while (ds.FormSwitched && i < this.GetSetting(eSettings.NavigationMaximum, 10));
|
||||
|
||||
|
||||
//}
|
||||
|
||||
private async Task Client_Loop(object sender, MessageResult e)
|
||||
{
|
||||
DeviceSession ds = this.Sessions.GetSession(e.DeviceId);
|
||||
DeviceSession ds = e.Device;
|
||||
if (ds == null)
|
||||
{
|
||||
ds = await this.Sessions.StartSession<T>(e.DeviceId);
|
||||
|
||||
e.Device = ds;
|
||||
ds.LastMessage = e.Message;
|
||||
|
||||
OnSessionBegins(new SessionBeginResult(e.DeviceId, ds));
|
||||
OnSessionBegins(new SessionBeginEventArgs(e.DeviceId, ds));
|
||||
}
|
||||
|
||||
ds.LastAction = DateTime.Now;
|
||||
ds.LastMessage = e.Message;
|
||||
|
||||
//Is this a systemcall ?
|
||||
if (e.IsSystemCall && this.SystemCalls.Contains(e.SystemCommand))
|
||||
//Is this a bot command ?
|
||||
if (e.IsBotCommand && this.BotCommands.Count(a => "/" + a.Command == e.BotCommand) > 0)
|
||||
{
|
||||
var sce = new SystemCallEventArgs(e.SystemCommand, e.SystemCallParameters, e.Message, ds.DeviceId, ds);
|
||||
await OnSystemCall(sce);
|
||||
var sce = new BotCommandEventArgs(e.BotCommand, e.BotCommandParameters, e.Message, ds.DeviceId, ds);
|
||||
await OnBotCommand(sce);
|
||||
|
||||
if (sce.Handled)
|
||||
return;
|
||||
@@ -255,88 +353,34 @@ namespace TelegramBotBase
|
||||
{
|
||||
i++;
|
||||
|
||||
activeForm = ds.ActiveForm;
|
||||
//Reset navigation
|
||||
ds.FormSwitched = false;
|
||||
|
||||
//If the form manages the events by itselfs, skip here
|
||||
if (activeForm.CustomEventManagement)
|
||||
return;
|
||||
activeForm = ds.ActiveForm;
|
||||
|
||||
//Pre Loading Event
|
||||
await activeForm.PreLoad(e);
|
||||
|
||||
//Send Load event to controls
|
||||
await activeForm.LoadControls(e);
|
||||
|
||||
//Loading Event
|
||||
await activeForm.Load(e);
|
||||
|
||||
//Is Attachment ? (Photo, Audio, Video, Contact, Location, Document)
|
||||
if (e.Message.Type == Telegram.Bot.Types.Enums.MessageType.Contact | e.Message.Type == Telegram.Bot.Types.Enums.MessageType.Document | e.Message.Type == Telegram.Bot.Types.Enums.MessageType.Location |
|
||||
e.Message.Type == Telegram.Bot.Types.Enums.MessageType.Photo | e.Message.Type == Telegram.Bot.Types.Enums.MessageType.Video | e.Message.Type == Telegram.Bot.Types.Enums.MessageType.Audio)
|
||||
if (e.MessageType == Telegram.Bot.Types.Enums.MessageType.Contact | e.MessageType == Telegram.Bot.Types.Enums.MessageType.Document | e.MessageType == Telegram.Bot.Types.Enums.MessageType.Location |
|
||||
e.MessageType == Telegram.Bot.Types.Enums.MessageType.Photo | e.MessageType == Telegram.Bot.Types.Enums.MessageType.Video | e.MessageType == Telegram.Bot.Types.Enums.MessageType.Audio)
|
||||
{
|
||||
await activeForm.SentData(new DataResult(e));
|
||||
}
|
||||
|
||||
//Render Event
|
||||
if (!activeForm.FormSwitched)
|
||||
await activeForm.Render(e);
|
||||
|
||||
} while (activeForm.FormSwitched && i < NavigationMaximum);
|
||||
|
||||
}
|
||||
|
||||
private void Client_Action(object sender, MessageResult e)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (LogAllMessages)
|
||||
{
|
||||
DeviceSession ds2 = this.Sessions.GetSession(e.DeviceId);
|
||||
OnMessage(new MessageIncomeResult(e.DeviceId, ds2, e));
|
||||
}
|
||||
|
||||
Client_TryAction(sender, e);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
DeviceSession ds = this.Sessions.GetSession(e.DeviceId);
|
||||
OnException(new SystemExceptionEventArgs(e.Message.Text, ds?.DeviceId ?? -1, ds, ex));
|
||||
}
|
||||
}
|
||||
|
||||
private async void Client_TryAction(object sender, MessageResult e)
|
||||
{
|
||||
DeviceSession ds = this.Sessions.GetSession(e.DeviceId);
|
||||
if (ds == null)
|
||||
{
|
||||
ds = await this.Sessions.StartSession<T>(e.DeviceId);
|
||||
}
|
||||
|
||||
|
||||
ds.LastAction = DateTime.Now;
|
||||
ds.LastMessage = e.Message;
|
||||
|
||||
FormBase activeForm = null;
|
||||
|
||||
int i = 0;
|
||||
|
||||
//Should formulars get navigated (allow maximum of 10, to dont get loops)
|
||||
do
|
||||
{
|
||||
i++;
|
||||
|
||||
activeForm = ds.ActiveForm;
|
||||
|
||||
//If the form manages the events by itselfs, skip here
|
||||
if (activeForm.CustomEventManagement)
|
||||
return;
|
||||
|
||||
//Pre Loading Event
|
||||
await activeForm.PreLoad(e);
|
||||
|
||||
//Loading Event
|
||||
await activeForm.Load(e);
|
||||
|
||||
//Action Event
|
||||
if (!activeForm.FormSwitched)
|
||||
if (!ds.FormSwitched && e.IsAction)
|
||||
{
|
||||
//Send Action event to controls
|
||||
await activeForm.ActionControls(e);
|
||||
|
||||
//Send Action event to form itself
|
||||
await activeForm.Action(e);
|
||||
|
||||
if (!e.Handled)
|
||||
@@ -346,11 +390,8 @@ namespace TelegramBotBase
|
||||
|
||||
if (uhc.Handled)
|
||||
{
|
||||
if (activeForm.FormSwitched)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else
|
||||
e.Handled = true;
|
||||
if (!ds.FormSwitched)
|
||||
{
|
||||
break;
|
||||
}
|
||||
@@ -359,19 +400,266 @@ namespace TelegramBotBase
|
||||
|
||||
}
|
||||
|
||||
//Render Event
|
||||
if (!activeForm.FormSwitched)
|
||||
await activeForm.Render(e);
|
||||
if (!ds.FormSwitched)
|
||||
{
|
||||
//Render Event
|
||||
await activeForm.RenderControls(e);
|
||||
|
||||
await activeForm.Render(e);
|
||||
}
|
||||
|
||||
e.IsFirstHandler = false;
|
||||
|
||||
} while (ds.FormSwitched && i < this.GetSetting(eSettings.NavigationMaximum, 10));
|
||||
|
||||
} while (activeForm.FormSwitched && i < NavigationMaximum);
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This will invoke the full message loop for the device even when no "userevent" like message or action has been raised.
|
||||
/// </summary>
|
||||
/// <param name="DeviceId">Contains the device/chat id of the device to update.</param>
|
||||
public async Task InvokeMessageLoop(long DeviceId)
|
||||
{
|
||||
var mr = new MessageResult();
|
||||
|
||||
await InvokeMessageLoop(DeviceId, mr);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This will invoke the full message loop for the device even when no "userevent" like message or action has been raised.
|
||||
/// </summary>
|
||||
/// <param name="DeviceId">Contains the device/chat id of the device to update.</param>
|
||||
/// <param name="e"></param>
|
||||
public async Task InvokeMessageLoop(long DeviceId, MessageResult e)
|
||||
{
|
||||
try
|
||||
{
|
||||
DeviceSession ds = this.Sessions.GetSession(DeviceId);
|
||||
e.Device = ds;
|
||||
|
||||
await Client_Loop(this, e);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
DeviceSession ds = this.Sessions.GetSession(DeviceId);
|
||||
OnException(new SystemExceptionEventArgs(e.Message.Text, DeviceId, ds, ex));
|
||||
}
|
||||
}
|
||||
|
||||
private async void Client_MessageEdit(object sender, MessageResult e)
|
||||
{
|
||||
if (this.GetSetting(eSettings.SkipAllMessages, false))
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
DeviceSession ds = this.Sessions.GetSession(e.DeviceId);
|
||||
e.Device = ds;
|
||||
|
||||
if (this.GetSetting(eSettings.LogAllMessages, false))
|
||||
{
|
||||
OnMessage(new MessageIncomeEventArgs(e.DeviceId, ds, e));
|
||||
}
|
||||
|
||||
//Call same, to handle received liked edited
|
||||
ds?.OnMessageReceived(new MessageReceivedEventArgs(e.Message));
|
||||
|
||||
await Client_TryMessageEdit(sender, e);
|
||||
}
|
||||
catch (Telegram.Bot.Exceptions.ApiRequestException ex)
|
||||
{
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
DeviceSession ds = this.Sessions.GetSession(e.DeviceId);
|
||||
OnException(new SystemExceptionEventArgs(e.Message.Text, ds?.DeviceId ?? -1, ds, ex));
|
||||
}
|
||||
}
|
||||
|
||||
private async Task Client_TryMessageEdit(object sender, MessageResult e)
|
||||
{
|
||||
DeviceSession ds = e.Device;
|
||||
if (ds == null)
|
||||
{
|
||||
ds = await this.Sessions.StartSession<T>(e.DeviceId);
|
||||
e.Device = ds;
|
||||
}
|
||||
|
||||
ds.LastAction = DateTime.Now;
|
||||
ds.LastMessage = e.Message;
|
||||
|
||||
//Pre Loading Event
|
||||
await ds.ActiveForm.Edited(e);
|
||||
|
||||
//When form has been switched due navigation within the edit method, reopen Client_Message
|
||||
if (ds.FormSwitched)
|
||||
{
|
||||
await Client_Loop(sender, e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private async void Client_Action(object sender, MessageResult e)
|
||||
{
|
||||
try
|
||||
{
|
||||
DeviceSession ds = this.Sessions.GetSession(e.DeviceId);
|
||||
e.Device = ds;
|
||||
|
||||
if (this.GetSetting(eSettings.LogAllMessages, false))
|
||||
{
|
||||
OnMessage(new MessageIncomeEventArgs(e.DeviceId, ds, e));
|
||||
}
|
||||
|
||||
await Client_Loop(sender, e);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
DeviceSession ds = this.Sessions.GetSession(e.DeviceId);
|
||||
OnException(new SystemExceptionEventArgs(e.Message.Text, ds?.DeviceId ?? -1, ds, ex));
|
||||
}
|
||||
}
|
||||
|
||||
//private async void Client_TryAction(object sender, MessageResult e)
|
||||
//{
|
||||
// DeviceSession ds = e.Device;
|
||||
// if (ds == null)
|
||||
// {
|
||||
// ds = await this.Sessions.StartSession<T>(e.DeviceId);
|
||||
// e.Device = ds;
|
||||
// }
|
||||
|
||||
// ds.LastAction = DateTime.Now;
|
||||
// ds.LastMessage = e.Message;
|
||||
|
||||
// FormBase activeForm = null;
|
||||
|
||||
// int i = 0;
|
||||
|
||||
// //Should formulars get navigated (allow maximum of 10, to dont get loops)
|
||||
// do
|
||||
// {
|
||||
// i++;
|
||||
|
||||
// //Reset navigation
|
||||
// ds.FormSwitched = false;
|
||||
|
||||
// activeForm = ds.ActiveForm;
|
||||
|
||||
// //Pre Loading Event
|
||||
// await activeForm.PreLoad(e);
|
||||
|
||||
// //Send Load event to controls
|
||||
// await activeForm.LoadControls(e);
|
||||
|
||||
// //Loading Event
|
||||
// await activeForm.Load(e);
|
||||
|
||||
// //Action Event
|
||||
// if (!ds.FormSwitched)
|
||||
// {
|
||||
// //Send Action event to controls
|
||||
// await activeForm.ActionControls(e);
|
||||
|
||||
// //Send Action event to form itself
|
||||
// await activeForm.Action(e);
|
||||
|
||||
// if (!e.Handled)
|
||||
// {
|
||||
// var uhc = new UnhandledCallEventArgs(e.Message.Text, e.RawData, ds.DeviceId, e.MessageId, e.Message, ds);
|
||||
// OnUnhandledCall(uhc);
|
||||
|
||||
// if (uhc.Handled)
|
||||
// {
|
||||
// e.Handled = true;
|
||||
// if (!ds.FormSwitched)
|
||||
// {
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// }
|
||||
|
||||
// //Render Event
|
||||
// if (!ds.FormSwitched)
|
||||
// {
|
||||
// await activeForm.RenderControls(e);
|
||||
|
||||
// await activeForm.Render(e);
|
||||
// }
|
||||
|
||||
// e.IsFirstHandler = false;
|
||||
|
||||
// } while (ds.FormSwitched && i < this.GetSetting(eSettings.NavigationMaximum, 10));
|
||||
|
||||
//}
|
||||
|
||||
/// <summary>
|
||||
/// This method will update all local created bot commands to the botfather.
|
||||
/// </summary>
|
||||
public async Task UploadBotCommands()
|
||||
{
|
||||
await this.Client.SetBotCommands(this.BotCommands);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Could set a variety of settings to improve the bot handling.
|
||||
/// </summary>
|
||||
/// <param name="set"></param>
|
||||
/// <param name="Value"></param>
|
||||
public void SetSetting(eSettings set, uint Value)
|
||||
{
|
||||
this.SystemSettings[set] = Value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Could set a variety of settings to improve the bot handling.
|
||||
/// </summary>
|
||||
/// <param name="set"></param>
|
||||
/// <param name="Value"></param>
|
||||
public void SetSetting(eSettings set, bool Value)
|
||||
{
|
||||
this.SystemSettings[set] = (Value ? 1u : 0u);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Could get the current value of a setting
|
||||
/// </summary>
|
||||
/// <param name="set"></param>
|
||||
/// <param name="defaultValue"></param>
|
||||
/// <returns></returns>
|
||||
public uint GetSetting(eSettings set, uint defaultValue)
|
||||
{
|
||||
if (!this.SystemSettings.ContainsKey(set))
|
||||
return defaultValue;
|
||||
|
||||
return this.SystemSettings[set];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Could get the current value of a setting
|
||||
/// </summary>
|
||||
/// <param name="set"></param>
|
||||
/// <param name="defaultValue"></param>
|
||||
/// <returns></returns>
|
||||
public bool GetSetting(eSettings set, bool defaultValue)
|
||||
{
|
||||
if (!this.SystemSettings.ContainsKey(set))
|
||||
return defaultValue;
|
||||
|
||||
return this.SystemSettings[set] == 0u ? false : true;
|
||||
}
|
||||
|
||||
#region "Events"
|
||||
|
||||
/// <summary>
|
||||
/// Will be called if a session/context gets started
|
||||
/// </summary>
|
||||
|
||||
public event EventHandler<SessionBeginResult> SessionBegins
|
||||
public event EventHandler<SessionBeginEventArgs> SessionBegins
|
||||
{
|
||||
add
|
||||
{
|
||||
@@ -383,16 +671,16 @@ namespace TelegramBotBase
|
||||
}
|
||||
}
|
||||
|
||||
public void OnSessionBegins(SessionBeginResult e)
|
||||
public void OnSessionBegins(SessionBeginEventArgs e)
|
||||
{
|
||||
(this.__Events[__evSessionBegins] as EventHandler<SessionBeginResult>)?.Invoke(this, e);
|
||||
(this.__Events[__evSessionBegins] as EventHandler<SessionBeginEventArgs>)?.Invoke(this, e);
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Will be called on incomming message
|
||||
/// </summary>
|
||||
public event EventHandler<MessageIncomeResult> Message
|
||||
public event EventHandler<MessageIncomeEventArgs> Message
|
||||
{
|
||||
add
|
||||
{
|
||||
@@ -404,22 +692,22 @@ namespace TelegramBotBase
|
||||
}
|
||||
}
|
||||
|
||||
public void OnMessage(MessageIncomeResult e)
|
||||
public void OnMessage(MessageIncomeEventArgs e)
|
||||
{
|
||||
(this.__Events[__evMessage] as EventHandler<MessageIncomeResult>)?.Invoke(this, e);
|
||||
(this.__Events[__evMessage] as EventHandler<MessageIncomeEventArgs>)?.Invoke(this, e);
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Will be called if a system call gets raised
|
||||
/// Will be called if a bot command gets raised
|
||||
/// </summary>
|
||||
public event SystemCallEventHandler SystemCall;
|
||||
public event BotCommandEventHandler BotCommand;
|
||||
|
||||
|
||||
public async Task OnSystemCall(SystemCallEventArgs e)
|
||||
public async Task OnBotCommand(BotCommandEventArgs e)
|
||||
{
|
||||
if (this.SystemCall != null)
|
||||
await SystemCall(this, e);
|
||||
if (this.BotCommand != null)
|
||||
await BotCommand(this, e);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -463,5 +751,8 @@ namespace TelegramBotBase
|
||||
(this.__Events[__evUnhandledCall] as EventHandler<UnhandledCallEventArgs>)?.Invoke(this, e);
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using Telegram.Bot.Types;
|
||||
|
||||
namespace TelegramBotBase.Commands
|
||||
{
|
||||
public static class Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Adding the default /start command with a description.
|
||||
/// </summary>
|
||||
/// <param name="cmds"></param>
|
||||
/// <param name="description"></param>
|
||||
public static void AddStartCommand(this List<BotCommand> cmds, String description)
|
||||
{
|
||||
cmds.Add(new BotCommand() { Command = "start", Description = description });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adding the default /help command with a description.
|
||||
/// </summary>
|
||||
/// <param name="cmds"></param>
|
||||
/// <param name="description"></param>
|
||||
public static void AddHelpCommand(this List<BotCommand> cmds, String description)
|
||||
{
|
||||
cmds.Add(new BotCommand() { Command = "help", Description = description });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adding the default /settings command with a description.
|
||||
/// </summary>
|
||||
/// <param name="cmds"></param>
|
||||
/// <param name="description"></param>
|
||||
public static void AddSettingsCommand(this List<BotCommand> cmds, String description)
|
||||
{
|
||||
cmds.Add(new BotCommand() { Command = "settings", Description = description });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace TelegramBotBase.Constants
|
||||
{
|
||||
public static class Telegram
|
||||
{
|
||||
/// <summary>
|
||||
/// The maximum length of message text before the API throws an exception. (We will catch it before)
|
||||
/// </summary>
|
||||
public const int MaxMessageLength = 4096;
|
||||
|
||||
public const int MaxInlineKeyBoardRows = 13;
|
||||
|
||||
public const int MaxInlineKeyBoardCols = 8;
|
||||
|
||||
public const int MaxReplyKeyboardRows = 25;
|
||||
|
||||
public const int MaxReplyKeyboardCols = 12;
|
||||
|
||||
public const int MessageDeletionsPerSecond = 30;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,738 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics.SymbolStore;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Telegram.Bot.Types;
|
||||
using Telegram.Bot.Types.Enums;
|
||||
using Telegram.Bot.Types.ReplyMarkups;
|
||||
using TelegramBotBase.Args;
|
||||
using TelegramBotBase.Base;
|
||||
using TelegramBotBase.Datasources;
|
||||
using TelegramBotBase.Enums;
|
||||
using TelegramBotBase.Exceptions;
|
||||
using TelegramBotBase.Form;
|
||||
using static TelegramBotBase.Base.Async;
|
||||
|
||||
namespace TelegramBotBase.Controls.Hybrid
|
||||
{
|
||||
public class ButtonGrid : Base.ControlBase
|
||||
{
|
||||
|
||||
public String Title { get; set; } = Localizations.Default.Language["ButtonGrid_Title"];
|
||||
|
||||
public String ConfirmationText { get; set; } = "";
|
||||
|
||||
private bool RenderNecessary = true;
|
||||
|
||||
private static readonly object __evButtonClicked = new object();
|
||||
|
||||
private readonly EventHandlerList Events = new EventHandlerList();
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
[Obsolete("This property is obsolete. Please use the DataSource property instead.")]
|
||||
public ButtonForm ButtonsForm
|
||||
{
|
||||
get
|
||||
{
|
||||
return DataSource.ButtonForm;
|
||||
}
|
||||
set
|
||||
{
|
||||
DataSource = new ButtonFormDataSource(value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Data source of the items.
|
||||
/// </summary>
|
||||
public ButtonFormDataSource DataSource { get; set; }
|
||||
|
||||
public int? MessageId { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Optional. Requests clients to resize the keyboard vertically for optimal fit (e.g., make the keyboard smaller if there are just two rows of buttons). Defaults to false, in which case the custom keyboard is always of the same height as the app's standard keyboard.
|
||||
/// Source: https://core.telegram.org/bots/api#replykeyboardmarkup
|
||||
/// </summary>
|
||||
public bool ResizeKeyboard { get; set; } = false;
|
||||
|
||||
public bool OneTimeKeyboard { get; set; } = false;
|
||||
|
||||
public bool HideKeyboardOnCleanup { get; set; } = true;
|
||||
|
||||
public bool DeletePreviousMessage { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Removes the reply message from a user.
|
||||
/// </summary>
|
||||
public bool DeleteReplyMessage { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Parsemode of the message.
|
||||
/// </summary>
|
||||
public ParseMode MessageParseMode { get; set; } = ParseMode.Default;
|
||||
|
||||
/// <summary>
|
||||
/// Enables automatic paging of buttons when the amount of rows is exceeding the limits.
|
||||
/// </summary>
|
||||
public bool EnablePaging { get; set; } = false;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Enabled a search function.
|
||||
/// </summary>
|
||||
public bool EnableSearch { get; set; } = false;
|
||||
|
||||
public String SearchQuery { get; set; }
|
||||
|
||||
public eNavigationBarVisibility NavigationBarVisibility { get; set; } = eNavigationBarVisibility.always;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Index of the current page
|
||||
/// </summary>
|
||||
public int CurrentPageIndex { get; set; } = 0;
|
||||
|
||||
public String PreviousPageLabel = Localizations.Default.Language["ButtonGrid_PreviousPage"];
|
||||
|
||||
public String NextPageLabel = Localizations.Default.Language["ButtonGrid_NextPage"];
|
||||
|
||||
public String NoItemsLabel = Localizations.Default.Language["ButtonGrid_NoItems"];
|
||||
|
||||
public String SearchLabel = Localizations.Default.Language["ButtonGrid_SearchFeature"];
|
||||
|
||||
/// <summary>
|
||||
/// Layout of the buttons which should be displayed always on top.
|
||||
/// </summary>
|
||||
public ButtonRow HeadLayoutButtonRow { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Layout of columns which should be displayed below the header
|
||||
/// </summary>
|
||||
public ButtonRow SubHeadLayoutButtonRow { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Defines which type of Button Keyboard should be rendered.
|
||||
/// </summary>
|
||||
public eKeyboardType KeyboardType
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_eKeyboardType;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (m_eKeyboardType != value)
|
||||
{
|
||||
this.RenderNecessary = true;
|
||||
|
||||
Cleanup().Wait();
|
||||
|
||||
m_eKeyboardType = value;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private eKeyboardType m_eKeyboardType = eKeyboardType.ReplyKeyboard;
|
||||
|
||||
public ButtonGrid()
|
||||
{
|
||||
this.DataSource = new ButtonFormDataSource();
|
||||
|
||||
}
|
||||
|
||||
public ButtonGrid(eKeyboardType type) : this()
|
||||
{
|
||||
m_eKeyboardType = type;
|
||||
}
|
||||
|
||||
|
||||
public ButtonGrid(ButtonForm form)
|
||||
{
|
||||
this.DataSource = new ButtonFormDataSource(form);
|
||||
}
|
||||
|
||||
|
||||
public event AsyncEventHandler<ButtonClickedEventArgs> ButtonClicked
|
||||
{
|
||||
add
|
||||
{
|
||||
this.Events.AddHandler(__evButtonClicked, value);
|
||||
}
|
||||
remove
|
||||
{
|
||||
this.Events.RemoveHandler(__evButtonClicked, value);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task OnButtonClicked(ButtonClickedEventArgs e)
|
||||
{
|
||||
var handler = this.Events[__evButtonClicked]?.GetInvocationList().Cast<AsyncEventHandler<ButtonClickedEventArgs>>();
|
||||
if (handler == null)
|
||||
return;
|
||||
|
||||
foreach (var h in handler)
|
||||
{
|
||||
await Async.InvokeAllAsync<ButtonClickedEventArgs>(h, this, e);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Init()
|
||||
{
|
||||
this.Device.MessageDeleted += Device_MessageDeleted;
|
||||
}
|
||||
|
||||
private void Device_MessageDeleted(object sender, MessageDeletedEventArgs e)
|
||||
{
|
||||
if (this.MessageId == null)
|
||||
return;
|
||||
|
||||
if (e.MessageId != this.MessageId)
|
||||
return;
|
||||
|
||||
this.MessageId = null;
|
||||
}
|
||||
|
||||
public async override Task Load(MessageResult result)
|
||||
{
|
||||
if (this.KeyboardType != eKeyboardType.ReplyKeyboard)
|
||||
return;
|
||||
|
||||
if (!result.IsFirstHandler)
|
||||
return;
|
||||
|
||||
if (result.MessageText == null || result.MessageText == "")
|
||||
return;
|
||||
|
||||
var matches = new List<ButtonRow>();
|
||||
ButtonRow match = null;
|
||||
int index = -1;
|
||||
|
||||
if (HeadLayoutButtonRow?.Matches(result.MessageText) ?? false)
|
||||
{
|
||||
match = HeadLayoutButtonRow;
|
||||
goto check;
|
||||
}
|
||||
|
||||
if (SubHeadLayoutButtonRow?.Matches(result.MessageText) ?? false)
|
||||
{
|
||||
match = SubHeadLayoutButtonRow;
|
||||
goto check;
|
||||
}
|
||||
|
||||
var br = DataSource.FindRow(result.MessageText);
|
||||
if (br != null)
|
||||
{
|
||||
match = br.Item1;
|
||||
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:
|
||||
|
||||
//Remove button click message
|
||||
if (this.DeleteReplyMessage)
|
||||
await Device.DeleteMessage(result.MessageId);
|
||||
|
||||
if (match != null)
|
||||
{
|
||||
await OnButtonClicked(new ButtonClickedEventArgs(match.GetButtonMatch(result.MessageText), index, match));
|
||||
|
||||
result.Handled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (result.MessageText == PreviousPageLabel)
|
||||
{
|
||||
if (this.CurrentPageIndex > 0)
|
||||
this.CurrentPageIndex--;
|
||||
|
||||
this.Updated();
|
||||
}
|
||||
else if (result.MessageText == NextPageLabel)
|
||||
{
|
||||
if (this.CurrentPageIndex < this.PageCount - 1)
|
||||
this.CurrentPageIndex++;
|
||||
|
||||
this.Updated();
|
||||
}
|
||||
else if (this.EnableSearch)
|
||||
{
|
||||
if (result.MessageText.StartsWith("🔍"))
|
||||
{
|
||||
//Sent note about searching
|
||||
if (this.SearchQuery == null)
|
||||
{
|
||||
await this.Device.Send(this.SearchLabel);
|
||||
}
|
||||
|
||||
this.SearchQuery = null;
|
||||
this.Updated();
|
||||
return;
|
||||
}
|
||||
|
||||
this.SearchQuery = result.MessageText;
|
||||
|
||||
if (this.SearchQuery != null && this.SearchQuery != "")
|
||||
{
|
||||
this.CurrentPageIndex = 0;
|
||||
this.Updated();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
public async override Task Action(MessageResult result, string value = null)
|
||||
{
|
||||
if (result.Handled)
|
||||
return;
|
||||
|
||||
if (!result.IsFirstHandler)
|
||||
return;
|
||||
|
||||
//Find clicked button depending on Text or Value (depending on markup type)
|
||||
if (this.KeyboardType != eKeyboardType.InlineKeyBoard)
|
||||
return;
|
||||
|
||||
await result.ConfirmAction(this.ConfirmationText ?? "");
|
||||
|
||||
ButtonRow match = null;
|
||||
int index = -1;
|
||||
|
||||
if (HeadLayoutButtonRow?.Matches(result.RawData, false) ?? false)
|
||||
{
|
||||
match = HeadLayoutButtonRow;
|
||||
goto check;
|
||||
}
|
||||
|
||||
if (SubHeadLayoutButtonRow?.Matches(result.RawData, false) ?? false)
|
||||
{
|
||||
match = SubHeadLayoutButtonRow;
|
||||
goto check;
|
||||
}
|
||||
|
||||
var br = DataSource.FindRow(result.RawData, false);
|
||||
if (br != null)
|
||||
{
|
||||
match = br.Item1;
|
||||
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)
|
||||
{
|
||||
await OnButtonClicked(new ButtonClickedEventArgs(match.GetButtonMatch(result.RawData, false), index, match));
|
||||
|
||||
result.Handled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
switch (result.RawData)
|
||||
{
|
||||
case "$previous$":
|
||||
|
||||
if (this.CurrentPageIndex > 0)
|
||||
this.CurrentPageIndex--;
|
||||
|
||||
this.Updated();
|
||||
|
||||
break;
|
||||
case "$next$":
|
||||
|
||||
if (this.CurrentPageIndex < this.PageCount - 1)
|
||||
this.CurrentPageIndex++;
|
||||
|
||||
this.Updated();
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This method checks of the amount of buttons
|
||||
/// </summary>
|
||||
private void CheckGrid()
|
||||
{
|
||||
switch (m_eKeyboardType)
|
||||
{
|
||||
case eKeyboardType.InlineKeyBoard:
|
||||
|
||||
if (DataSource.RowCount > Constants.Telegram.MaxInlineKeyBoardRows && !this.EnablePaging)
|
||||
{
|
||||
throw new MaximumRowsReachedException() { Value = DataSource.RowCount, Maximum = Constants.Telegram.MaxInlineKeyBoardRows };
|
||||
}
|
||||
|
||||
if (DataSource.ColumnCount > Constants.Telegram.MaxInlineKeyBoardCols)
|
||||
{
|
||||
throw new MaximumColsException() { Value = DataSource.ColumnCount, Maximum = Constants.Telegram.MaxInlineKeyBoardCols };
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case eKeyboardType.ReplyKeyboard:
|
||||
|
||||
if (DataSource.RowCount > Constants.Telegram.MaxReplyKeyboardRows && !this.EnablePaging)
|
||||
{
|
||||
throw new MaximumRowsReachedException() { Value = DataSource.RowCount, Maximum = Constants.Telegram.MaxReplyKeyboardRows };
|
||||
}
|
||||
|
||||
if (DataSource.ColumnCount > Constants.Telegram.MaxReplyKeyboardCols)
|
||||
{
|
||||
throw new MaximumColsException() { Value = DataSource.ColumnCount, Maximum = Constants.Telegram.MaxReplyKeyboardCols };
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public async override Task Render(MessageResult result)
|
||||
{
|
||||
if (!this.RenderNecessary)
|
||||
return;
|
||||
|
||||
//Check for rows and column limits
|
||||
CheckGrid();
|
||||
|
||||
this.RenderNecessary = false;
|
||||
|
||||
ButtonForm form = this.DataSource.PickItems(CurrentPageIndex * ItemRowsPerPage, ItemRowsPerPage, (this.EnableSearch ? this.SearchQuery : null));
|
||||
|
||||
|
||||
//if (this.EnableSearch && this.SearchQuery != null && this.SearchQuery != "")
|
||||
//{
|
||||
// form = form.FilterDuplicate(this.SearchQuery, true);
|
||||
//}
|
||||
//else
|
||||
//{
|
||||
// form = form.Duplicate();
|
||||
//}
|
||||
|
||||
if (this.EnablePaging)
|
||||
{
|
||||
IntegratePagingView(form);
|
||||
}
|
||||
|
||||
if (this.HeadLayoutButtonRow != null && HeadLayoutButtonRow.Count > 0)
|
||||
{
|
||||
form.InsertButtonRow(0, this.HeadLayoutButtonRow);
|
||||
}
|
||||
|
||||
if (this.SubHeadLayoutButtonRow != null && SubHeadLayoutButtonRow.Count > 0)
|
||||
{
|
||||
if (this.IsNavigationBarVisible)
|
||||
{
|
||||
form.InsertButtonRow(2, this.SubHeadLayoutButtonRow);
|
||||
}
|
||||
else
|
||||
{
|
||||
form.InsertButtonRow(1, this.SubHeadLayoutButtonRow);
|
||||
}
|
||||
}
|
||||
|
||||
Message m = null;
|
||||
|
||||
switch (this.KeyboardType)
|
||||
{
|
||||
//Reply Keyboard could only be updated with a new keyboard.
|
||||
case eKeyboardType.ReplyKeyboard:
|
||||
|
||||
|
||||
if (form.Count == 0)
|
||||
{
|
||||
if (this.MessageId != null)
|
||||
{
|
||||
await this.Device.HideReplyKeyboard();
|
||||
this.MessageId = null;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
//if (this.MessageId != null)
|
||||
//{
|
||||
// if (form.Count == 0)
|
||||
// {
|
||||
// await this.Device.HideReplyKeyboard();
|
||||
// this.MessageId = null;
|
||||
// return;
|
||||
// }
|
||||
//}
|
||||
|
||||
//if (form.Count == 0)
|
||||
// return;
|
||||
|
||||
|
||||
var rkm = (ReplyKeyboardMarkup)form;
|
||||
rkm.ResizeKeyboard = this.ResizeKeyboard;
|
||||
rkm.OneTimeKeyboard = this.OneTimeKeyboard;
|
||||
m = await this.Device.Send(this.Title, rkm, disableNotification: true, parseMode: MessageParseMode, MarkdownV2AutoEscape: false);
|
||||
|
||||
//Prevent flicker of keyboard
|
||||
if (this.DeletePreviousMessage && this.MessageId != null)
|
||||
await this.Device.DeleteMessage(this.MessageId.Value);
|
||||
|
||||
break;
|
||||
|
||||
case eKeyboardType.InlineKeyBoard:
|
||||
|
||||
//Try to edit message if message id is available
|
||||
//When the returned message is null then the message has been already deleted, resend it
|
||||
if (this.MessageId != null)
|
||||
{
|
||||
m = await this.Device.Edit(this.MessageId.Value, this.Title, (InlineKeyboardMarkup)form);
|
||||
if (m != null)
|
||||
{
|
||||
this.MessageId = m.MessageId;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//When no message id is available or it has been deleted due the use of AutoCleanForm re-render automatically
|
||||
m = await this.Device.Send(this.Title, (InlineKeyboardMarkup)form, disableNotification: true, parseMode: MessageParseMode, MarkdownV2AutoEscape: false);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (m != null)
|
||||
{
|
||||
this.MessageId = m.MessageId;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
private void IntegratePagingView(ButtonForm dataForm)
|
||||
{
|
||||
//No Items
|
||||
if (dataForm.Rows == 0)
|
||||
{
|
||||
dataForm.AddButtonRow(new ButtonBase(NoItemsLabel, "$"));
|
||||
}
|
||||
|
||||
if (this.IsNavigationBarVisible)
|
||||
{
|
||||
//🔍
|
||||
ButtonRow row = new ButtonRow();
|
||||
row.Add(new ButtonBase(PreviousPageLabel, "$previous$"));
|
||||
row.Add(new ButtonBase(String.Format(Localizations.Default.Language["ButtonGrid_CurrentPage"], this.CurrentPageIndex + 1, this.PageCount), "$site$"));
|
||||
row.Add(new ButtonBase(NextPageLabel, "$next$"));
|
||||
|
||||
if (this.EnableSearch)
|
||||
{
|
||||
row.Insert(2, new ButtonBase("🔍 " + (this.SearchQuery ?? ""), "$search$"));
|
||||
}
|
||||
|
||||
dataForm.InsertButtonRow(0, row);
|
||||
|
||||
dataForm.AddButtonRow(row);
|
||||
}
|
||||
}
|
||||
|
||||
public bool PagingNecessary
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this.KeyboardType == eKeyboardType.InlineKeyBoard && TotalRows > Constants.Telegram.MaxInlineKeyBoardRows)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (this.KeyboardType == eKeyboardType.ReplyKeyboard && TotalRows > Constants.Telegram.MaxReplyKeyboardRows)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsNavigationBarVisible
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this.NavigationBarVisibility == eNavigationBarVisibility.always | (this.NavigationBarVisibility == eNavigationBarVisibility.auto && PagingNecessary))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the maximum number of rows
|
||||
/// </summary>
|
||||
public int MaximumRow
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (this.KeyboardType)
|
||||
{
|
||||
case eKeyboardType.InlineKeyBoard:
|
||||
return Constants.Telegram.MaxInlineKeyBoardRows;
|
||||
|
||||
case eKeyboardType.ReplyKeyboard:
|
||||
return Constants.Telegram.MaxReplyKeyboardRows;
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the number of all rows (layout + navigation + content);
|
||||
/// </summary>
|
||||
public int TotalRows
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.LayoutRows + DataSource.RowCount;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Contains the Number of Rows which are used by the layout.
|
||||
/// </summary>
|
||||
private int LayoutRows
|
||||
{
|
||||
get
|
||||
{
|
||||
int layoutRows = 0;
|
||||
|
||||
if (this.NavigationBarVisibility == eNavigationBarVisibility.always | this.NavigationBarVisibility == eNavigationBarVisibility.auto)
|
||||
layoutRows += 2;
|
||||
|
||||
if (this.HeadLayoutButtonRow != null && this.HeadLayoutButtonRow.Count > 0)
|
||||
layoutRows++;
|
||||
|
||||
if (this.SubHeadLayoutButtonRow != null && this.SubHeadLayoutButtonRow.Count > 0)
|
||||
layoutRows++;
|
||||
|
||||
return layoutRows;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the number of item rows per page.
|
||||
/// </summary>
|
||||
public int ItemRowsPerPage
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.MaximumRow - this.LayoutRows;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return the number of pages.
|
||||
/// </summary>
|
||||
public int PageCount
|
||||
{
|
||||
get
|
||||
{
|
||||
if (DataSource.RowCount == 0)
|
||||
return 1;
|
||||
|
||||
//var bf = this.DataSource.PickAllItems(this.EnableSearch ? this.SearchQuery : null);
|
||||
|
||||
var max = this.DataSource.CalculateMax(this.EnableSearch ? this.SearchQuery : null);
|
||||
|
||||
//if (this.EnableSearch && this.SearchQuery != null && this.SearchQuery != "")
|
||||
//{
|
||||
// bf = bf.FilterDuplicate(this.SearchQuery);
|
||||
//}
|
||||
|
||||
if (max == 0)
|
||||
return 1;
|
||||
|
||||
return (int)Math.Ceiling((decimal)((decimal)max / (decimal)ItemRowsPerPage));
|
||||
}
|
||||
}
|
||||
|
||||
public override async Task Hidden(bool FormClose)
|
||||
{
|
||||
//Prepare for opening Modal, and comming back
|
||||
if (!FormClose)
|
||||
{
|
||||
this.Updated();
|
||||
}
|
||||
else
|
||||
{
|
||||
//Remove event handler
|
||||
this.Device.MessageDeleted -= Device_MessageDeleted;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tells the control that it has been updated.
|
||||
/// </summary>
|
||||
public void Updated()
|
||||
{
|
||||
this.RenderNecessary = true;
|
||||
}
|
||||
|
||||
public async override Task Cleanup()
|
||||
{
|
||||
if (this.MessageId == null)
|
||||
return;
|
||||
|
||||
switch (this.KeyboardType)
|
||||
{
|
||||
case eKeyboardType.InlineKeyBoard:
|
||||
|
||||
await this.Device.DeleteMessage(this.MessageId.Value);
|
||||
|
||||
this.MessageId = null;
|
||||
|
||||
break;
|
||||
case eKeyboardType.ReplyKeyboard:
|
||||
|
||||
if (this.HideKeyboardOnCleanup)
|
||||
{
|
||||
await this.Device.HideReplyKeyboard();
|
||||
}
|
||||
|
||||
this.MessageId = null;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using TelegramBotBase.Form;
|
||||
|
||||
namespace TelegramBotBase.Controls.Hybrid
|
||||
{
|
||||
[DebuggerDisplay("{Count} columns")]
|
||||
public class ButtonRow
|
||||
{
|
||||
|
||||
List<ButtonBase> __buttons = new List<ButtonBase>();
|
||||
|
||||
public ButtonRow()
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
|
||||
public ButtonRow(params ButtonBase[] buttons)
|
||||
{
|
||||
__buttons = buttons.ToList();
|
||||
}
|
||||
|
||||
|
||||
public ButtonBase this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
return __buttons[index];
|
||||
}
|
||||
}
|
||||
|
||||
public int Count
|
||||
{
|
||||
get
|
||||
{
|
||||
return __buttons.Count;
|
||||
}
|
||||
}
|
||||
|
||||
public void Add(ButtonBase button)
|
||||
{
|
||||
__buttons.Add(button);
|
||||
}
|
||||
|
||||
public void AddRange(ButtonBase button)
|
||||
{
|
||||
__buttons.Add(button);
|
||||
}
|
||||
|
||||
public void Insert(int index, ButtonBase button)
|
||||
{
|
||||
__buttons.Insert(index, button);
|
||||
}
|
||||
|
||||
public IEnumerator<ButtonBase> GetEnumerator()
|
||||
{
|
||||
return __buttons.GetEnumerator();
|
||||
}
|
||||
|
||||
public ButtonBase[] ToArray()
|
||||
{
|
||||
return __buttons.ToArray();
|
||||
}
|
||||
|
||||
public List<ButtonBase> ToList()
|
||||
{
|
||||
return __buttons.ToList();
|
||||
}
|
||||
|
||||
public bool Matches(String text, bool useText = true)
|
||||
{
|
||||
foreach (var b in __buttons)
|
||||
{
|
||||
if (useText && b.Text.Trim().Equals(text, StringComparison.InvariantCultureIgnoreCase))
|
||||
return true;
|
||||
|
||||
if (!useText && b.Value.Equals(text, StringComparison.InvariantCultureIgnoreCase))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the button inside of the row which matches.
|
||||
/// </summary>
|
||||
/// <param name="text"></param>
|
||||
/// <param name="useText"></param>
|
||||
/// <returns></returns>
|
||||
public ButtonBase GetButtonMatch(String text, bool useText = true)
|
||||
{
|
||||
foreach (var b in __buttons)
|
||||
{
|
||||
if (useText && b.Text.Trim().Equals(text, StringComparison.InvariantCultureIgnoreCase))
|
||||
return b;
|
||||
if (!useText && b.Value.Equals(text, StringComparison.InvariantCultureIgnoreCase))
|
||||
return b;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static implicit operator ButtonRow(List<ButtonBase> list)
|
||||
{
|
||||
return new ButtonRow() { __buttons = list };
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,874 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics.SymbolStore;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Telegram.Bot.Types;
|
||||
using Telegram.Bot.Types.Enums;
|
||||
using Telegram.Bot.Types.ReplyMarkups;
|
||||
using TelegramBotBase.Args;
|
||||
using TelegramBotBase.Base;
|
||||
using TelegramBotBase.Datasources;
|
||||
using TelegramBotBase.Enums;
|
||||
using TelegramBotBase.Exceptions;
|
||||
using TelegramBotBase.Form;
|
||||
using static TelegramBotBase.Base.Async;
|
||||
|
||||
namespace TelegramBotBase.Controls.Hybrid
|
||||
{
|
||||
public class CheckedButtonList : Base.ControlBase
|
||||
{
|
||||
|
||||
public String Title { get; set; } = Localizations.Default.Language["ButtonGrid_Title"];
|
||||
|
||||
public String ConfirmationText { get; set; } = "";
|
||||
|
||||
private bool RenderNecessary = true;
|
||||
|
||||
private static readonly object __evButtonClicked = new object();
|
||||
|
||||
private static readonly object __evCheckedChanged = new object();
|
||||
|
||||
private readonly EventHandlerList Events = new EventHandlerList();
|
||||
|
||||
[Obsolete("This property is obsolete. Please use the DataSource property instead.")]
|
||||
public ButtonForm ButtonsForm
|
||||
{
|
||||
get
|
||||
{
|
||||
return DataSource.ButtonForm;
|
||||
}
|
||||
set
|
||||
{
|
||||
DataSource = new ButtonFormDataSource(value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Data source of the items.
|
||||
/// </summary>
|
||||
public ButtonFormDataSource DataSource { get; set; }
|
||||
|
||||
List<int> CheckedRows { get; set; } = new List<int>();
|
||||
|
||||
public int? MessageId { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Optional. Requests clients to resize the keyboard vertically for optimal fit (e.g., make the keyboard smaller if there are just two rows of buttons). Defaults to false, in which case the custom keyboard is always of the same height as the app's standard keyboard.
|
||||
/// Source: https://core.telegram.org/bots/api#replykeyboardmarkup
|
||||
/// </summary>
|
||||
public bool ResizeKeyboard { get; set; } = false;
|
||||
|
||||
public bool OneTimeKeyboard { get; set; } = false;
|
||||
|
||||
public bool HideKeyboardOnCleanup { get; set; } = true;
|
||||
|
||||
public bool DeletePreviousMessage { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Removes the reply message from a user.
|
||||
/// </summary>
|
||||
public bool DeleteReplyMessage { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Parsemode of the message.
|
||||
/// </summary>
|
||||
public ParseMode MessageParseMode { get; set; } = ParseMode.Default;
|
||||
|
||||
/// <summary>
|
||||
/// Enables automatic paging of buttons when the amount of rows is exceeding the limits.
|
||||
/// </summary>
|
||||
public bool EnablePaging { get; set; } = false;
|
||||
|
||||
|
||||
///// <summary>
|
||||
///// Enabled a search function.
|
||||
///// </summary>
|
||||
//public bool EnableSearch { get; set; } = false;
|
||||
|
||||
//public String SearchQuery { get; set; }
|
||||
|
||||
public eNavigationBarVisibility NavigationBarVisibility { get; set; } = eNavigationBarVisibility.always;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Index of the current page
|
||||
/// </summary>
|
||||
public int CurrentPageIndex { get; set; } = 0;
|
||||
|
||||
public String PreviousPageLabel = Localizations.Default.Language["ButtonGrid_PreviousPage"];
|
||||
|
||||
public String NextPageLabel = Localizations.Default.Language["ButtonGrid_NextPage"];
|
||||
|
||||
public String NoItemsLabel = Localizations.Default.Language["ButtonGrid_NoItems"];
|
||||
|
||||
//public String SearchLabel = Localizations.Default.Language["ButtonGrid_SearchFeature"];
|
||||
|
||||
public String CheckedIconLabel { get; set; } = "✅";
|
||||
|
||||
public String UncheckedIconLabel { get; set; } = "◻️";
|
||||
|
||||
/// <summary>
|
||||
/// Layout of the buttons which should be displayed always on top.
|
||||
/// </summary>
|
||||
public ButtonRow HeadLayoutButtonRow { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Layout of columns which should be displayed below the header
|
||||
/// </summary>
|
||||
public ButtonRow SubHeadLayoutButtonRow { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Defines which type of Button Keyboard should be rendered.
|
||||
/// </summary>
|
||||
public eKeyboardType KeyboardType
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_eKeyboardType;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (m_eKeyboardType != value)
|
||||
{
|
||||
this.RenderNecessary = true;
|
||||
|
||||
Cleanup().Wait();
|
||||
|
||||
m_eKeyboardType = value;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private eKeyboardType m_eKeyboardType = eKeyboardType.ReplyKeyboard;
|
||||
|
||||
public CheckedButtonList()
|
||||
{
|
||||
this.DataSource = new ButtonFormDataSource();
|
||||
|
||||
|
||||
}
|
||||
|
||||
public CheckedButtonList(eKeyboardType type) : this()
|
||||
{
|
||||
m_eKeyboardType = type;
|
||||
}
|
||||
|
||||
|
||||
public CheckedButtonList(ButtonForm form)
|
||||
{
|
||||
this.DataSource = new ButtonFormDataSource(form);
|
||||
}
|
||||
|
||||
public event AsyncEventHandler<ButtonClickedEventArgs> ButtonClicked
|
||||
{
|
||||
add
|
||||
{
|
||||
this.Events.AddHandler(__evButtonClicked, value);
|
||||
}
|
||||
remove
|
||||
{
|
||||
this.Events.RemoveHandler(__evButtonClicked, value);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task OnButtonClicked(ButtonClickedEventArgs e)
|
||||
{
|
||||
var handler = this.Events[__evButtonClicked]?.GetInvocationList().Cast<AsyncEventHandler<ButtonClickedEventArgs>>();
|
||||
if (handler == null)
|
||||
return;
|
||||
|
||||
foreach (var h in handler)
|
||||
{
|
||||
await Async.InvokeAllAsync<ButtonClickedEventArgs>(h, this, e);
|
||||
}
|
||||
}
|
||||
|
||||
public event AsyncEventHandler<CheckedChangedEventArgs> CheckedChanged
|
||||
{
|
||||
add
|
||||
{
|
||||
this.Events.AddHandler(__evCheckedChanged, value);
|
||||
}
|
||||
remove
|
||||
{
|
||||
this.Events.RemoveHandler(__evCheckedChanged, value);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task OnCheckedChanged(CheckedChangedEventArgs e)
|
||||
{
|
||||
var handler = this.Events[__evCheckedChanged]?.GetInvocationList().Cast<AsyncEventHandler<CheckedChangedEventArgs>>();
|
||||
if (handler == null)
|
||||
return;
|
||||
|
||||
foreach (var h in handler)
|
||||
{
|
||||
await Async.InvokeAllAsync<CheckedChangedEventArgs>(h, this, e);
|
||||
}
|
||||
}
|
||||
|
||||
public async override Task Load(MessageResult result)
|
||||
{
|
||||
if (this.KeyboardType != eKeyboardType.ReplyKeyboard)
|
||||
return;
|
||||
|
||||
if (!result.IsFirstHandler)
|
||||
return;
|
||||
|
||||
if (result.MessageText == null || result.MessageText == "")
|
||||
return;
|
||||
|
||||
var matches = new List<ButtonRow>();
|
||||
ButtonRow match = null;
|
||||
int index = -1;
|
||||
|
||||
if (HeadLayoutButtonRow?.Matches(result.MessageText) ?? false)
|
||||
{
|
||||
match = HeadLayoutButtonRow;
|
||||
goto check;
|
||||
}
|
||||
|
||||
if (SubHeadLayoutButtonRow?.Matches(result.MessageText) ?? false)
|
||||
{
|
||||
match = SubHeadLayoutButtonRow;
|
||||
goto check;
|
||||
}
|
||||
|
||||
var br = DataSource.FindRow(result.MessageText);
|
||||
if (br != null)
|
||||
{
|
||||
match = br.Item1;
|
||||
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:
|
||||
|
||||
|
||||
//Remove button click message
|
||||
if (this.DeleteReplyMessage)
|
||||
await Device.DeleteMessage(result.MessageId);
|
||||
|
||||
if (match == null)
|
||||
{
|
||||
if (result.MessageText == PreviousPageLabel)
|
||||
{
|
||||
if (this.CurrentPageIndex > 0)
|
||||
this.CurrentPageIndex--;
|
||||
|
||||
this.Updated();
|
||||
}
|
||||
else if (result.MessageText == NextPageLabel)
|
||||
{
|
||||
if (this.CurrentPageIndex < this.PageCount - 1)
|
||||
this.CurrentPageIndex++;
|
||||
|
||||
this.Updated();
|
||||
}
|
||||
else if (result.MessageText.EndsWith(CheckedIconLabel))
|
||||
{
|
||||
var s = result.MessageText.Split(' ', '.');
|
||||
index = int.Parse(s[0]) - 1;
|
||||
|
||||
if (!this.CheckedRows.Contains(index))
|
||||
return;
|
||||
|
||||
this.CheckedRows.Remove(index);
|
||||
|
||||
this.Updated();
|
||||
|
||||
await OnCheckedChanged(new CheckedChangedEventArgs(ButtonsForm[index], index, false));
|
||||
|
||||
}
|
||||
else if (result.MessageText.EndsWith(UncheckedIconLabel))
|
||||
{
|
||||
var s = result.MessageText.Split(' ', '.');
|
||||
index = int.Parse(s[0]) - 1;
|
||||
|
||||
if (this.CheckedRows.Contains(index))
|
||||
return;
|
||||
|
||||
|
||||
this.CheckedRows.Add(index);
|
||||
|
||||
this.Updated();
|
||||
|
||||
await OnCheckedChanged(new CheckedChangedEventArgs(ButtonsForm[index], index, true));
|
||||
|
||||
}
|
||||
//else if (this.EnableSearch)
|
||||
//{
|
||||
// if (result.MessageText.StartsWith("🔍"))
|
||||
// {
|
||||
// //Sent note about searching
|
||||
// if (this.SearchQuery == null)
|
||||
// {
|
||||
// await this.Device.Send(this.SearchLabel);
|
||||
// }
|
||||
|
||||
// this.SearchQuery = null;
|
||||
// this.Updated();
|
||||
// return;
|
||||
// }
|
||||
|
||||
// this.SearchQuery = result.MessageText;
|
||||
|
||||
// if (this.SearchQuery != null && this.SearchQuery != "")
|
||||
// {
|
||||
// this.CurrentPageIndex = 0;
|
||||
// this.Updated();
|
||||
// }
|
||||
|
||||
//}
|
||||
|
||||
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
await OnButtonClicked(new ButtonClickedEventArgs(match.GetButtonMatch(result.MessageText), index, match));
|
||||
|
||||
result.Handled = true;
|
||||
|
||||
//await OnButtonClicked(new ButtonClickedEventArgs(button, index));
|
||||
|
||||
////Remove button click message
|
||||
//if (this.DeletePreviousMessage)
|
||||
// await Device.DeleteMessage(result.MessageId);
|
||||
|
||||
//result.Handled = true;
|
||||
|
||||
}
|
||||
|
||||
public async override Task Action(MessageResult result, string value = null)
|
||||
{
|
||||
if (result.Handled)
|
||||
return;
|
||||
|
||||
if (!result.IsFirstHandler)
|
||||
return;
|
||||
|
||||
//Find clicked button depending on Text or Value (depending on markup type)
|
||||
if (this.KeyboardType != eKeyboardType.InlineKeyBoard)
|
||||
return;
|
||||
|
||||
await result.ConfirmAction(this.ConfirmationText ?? "");
|
||||
|
||||
ButtonRow match = null;
|
||||
int index = -1;
|
||||
|
||||
if (HeadLayoutButtonRow?.Matches(result.RawData, false) ?? false)
|
||||
{
|
||||
match = HeadLayoutButtonRow;
|
||||
goto check;
|
||||
}
|
||||
|
||||
if (SubHeadLayoutButtonRow?.Matches(result.RawData, false) ?? false)
|
||||
{
|
||||
match = SubHeadLayoutButtonRow;
|
||||
goto check;
|
||||
}
|
||||
|
||||
var br = DataSource.FindRow(result.RawData, false);
|
||||
if (br != null)
|
||||
{
|
||||
match = br.Item1;
|
||||
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)
|
||||
{
|
||||
await OnButtonClicked(new ButtonClickedEventArgs(match.GetButtonMatch(result.RawData, false), index, match));
|
||||
|
||||
result.Handled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
//if (button != null)
|
||||
//{
|
||||
// await OnButtonClicked(new ButtonClickedEventArgs(button, index));
|
||||
|
||||
// result.Handled = true;
|
||||
// return;
|
||||
//}
|
||||
|
||||
switch (result.RawData)
|
||||
{
|
||||
case "$previous$":
|
||||
|
||||
if (this.CurrentPageIndex > 0)
|
||||
this.CurrentPageIndex--;
|
||||
|
||||
this.Updated();
|
||||
|
||||
break;
|
||||
case "$next$":
|
||||
|
||||
if (this.CurrentPageIndex < this.PageCount - 1)
|
||||
this.CurrentPageIndex++;
|
||||
|
||||
this.Updated();
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
var s = result.RawData.Split('$');
|
||||
|
||||
|
||||
switch (s[0])
|
||||
{
|
||||
case "check":
|
||||
|
||||
index = int.Parse(s[1]);
|
||||
|
||||
if (!this.CheckedRows.Contains(index))
|
||||
{
|
||||
this.CheckedRows.Add(index);
|
||||
|
||||
this.Updated();
|
||||
|
||||
await OnCheckedChanged(new CheckedChangedEventArgs(ButtonsForm[index], index, true));
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case "uncheck":
|
||||
|
||||
index = int.Parse(s[1]);
|
||||
|
||||
if (this.CheckedRows.Contains(index))
|
||||
{
|
||||
this.CheckedRows.Remove(index);
|
||||
|
||||
this.Updated();
|
||||
|
||||
await OnCheckedChanged(new CheckedChangedEventArgs(ButtonsForm[index], index, false));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This method checks of the amount of buttons
|
||||
/// </summary>
|
||||
private void CheckGrid()
|
||||
{
|
||||
switch (m_eKeyboardType)
|
||||
{
|
||||
case eKeyboardType.InlineKeyBoard:
|
||||
|
||||
if (DataSource.RowCount > Constants.Telegram.MaxInlineKeyBoardRows && !this.EnablePaging)
|
||||
{
|
||||
throw new MaximumRowsReachedException() { Value = DataSource.RowCount, Maximum = Constants.Telegram.MaxInlineKeyBoardRows };
|
||||
}
|
||||
|
||||
if (DataSource.ColumnCount > Constants.Telegram.MaxInlineKeyBoardCols)
|
||||
{
|
||||
throw new MaximumColsException() { Value = DataSource.ColumnCount, Maximum = Constants.Telegram.MaxInlineKeyBoardCols };
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case eKeyboardType.ReplyKeyboard:
|
||||
|
||||
if (DataSource.RowCount > Constants.Telegram.MaxReplyKeyboardRows && !this.EnablePaging)
|
||||
{
|
||||
throw new MaximumRowsReachedException() { Value = DataSource.RowCount, Maximum = Constants.Telegram.MaxReplyKeyboardRows };
|
||||
}
|
||||
|
||||
if (DataSource.ColumnCount > Constants.Telegram.MaxReplyKeyboardCols)
|
||||
{
|
||||
throw new MaximumColsException() { Value = DataSource.ColumnCount, Maximum = Constants.Telegram.MaxReplyKeyboardCols };
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public async override Task Render(MessageResult result)
|
||||
{
|
||||
if (!this.RenderNecessary)
|
||||
return;
|
||||
|
||||
//Check for rows and column limits
|
||||
CheckGrid();
|
||||
|
||||
this.RenderNecessary = false;
|
||||
|
||||
Message m = null;
|
||||
|
||||
ButtonForm form = this.DataSource.PickItems(CurrentPageIndex * ItemRowsPerPage, ItemRowsPerPage, null);
|
||||
|
||||
//if (this.EnableSearch && this.SearchQuery != null && this.SearchQuery != "")
|
||||
//{
|
||||
// form = form.FilterDuplicate(this.SearchQuery, true);
|
||||
//}
|
||||
//else
|
||||
//{
|
||||
//form = form.Duplicate();
|
||||
//}
|
||||
|
||||
if (this.EnablePaging)
|
||||
{
|
||||
IntegratePagingView(form);
|
||||
}
|
||||
else
|
||||
{
|
||||
form = PrepareCheckableLayout(form);
|
||||
}
|
||||
|
||||
if (this.HeadLayoutButtonRow != null && HeadLayoutButtonRow.Count > 0)
|
||||
{
|
||||
form.InsertButtonRow(0, this.HeadLayoutButtonRow.ToArray());
|
||||
}
|
||||
|
||||
if (this.SubHeadLayoutButtonRow != null && SubHeadLayoutButtonRow.Count > 0)
|
||||
{
|
||||
if (this.IsNavigationBarVisible)
|
||||
{
|
||||
form.InsertButtonRow(2, this.SubHeadLayoutButtonRow.ToArray());
|
||||
}
|
||||
else
|
||||
{
|
||||
form.InsertButtonRow(1, this.SubHeadLayoutButtonRow.ToArray());
|
||||
}
|
||||
}
|
||||
|
||||
switch (this.KeyboardType)
|
||||
{
|
||||
//Reply Keyboard could only be updated with a new keyboard.
|
||||
case eKeyboardType.ReplyKeyboard:
|
||||
|
||||
if (form.Count == 0)
|
||||
{
|
||||
if (this.MessageId != null)
|
||||
{
|
||||
await this.Device.HideReplyKeyboard();
|
||||
this.MessageId = null;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
//if (form.Count == 0)
|
||||
// return;
|
||||
|
||||
|
||||
var rkm = (ReplyKeyboardMarkup)form;
|
||||
rkm.ResizeKeyboard = this.ResizeKeyboard;
|
||||
rkm.OneTimeKeyboard = this.OneTimeKeyboard;
|
||||
m = await this.Device.Send(this.Title, rkm, disableNotification: true, parseMode: MessageParseMode, MarkdownV2AutoEscape: false);
|
||||
|
||||
//Prevent flicker of keyboard
|
||||
if (this.DeletePreviousMessage && this.MessageId != null)
|
||||
await this.Device.DeleteMessage(this.MessageId.Value);
|
||||
|
||||
break;
|
||||
|
||||
case eKeyboardType.InlineKeyBoard:
|
||||
|
||||
if (this.MessageId != null)
|
||||
{
|
||||
m = await this.Device.Edit(this.MessageId.Value, this.Title, (InlineKeyboardMarkup)form);
|
||||
}
|
||||
else
|
||||
{
|
||||
m = await this.Device.Send(this.Title, (InlineKeyboardMarkup)form, disableNotification: true, parseMode: MessageParseMode, MarkdownV2AutoEscape: false);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (m != null)
|
||||
{
|
||||
this.MessageId = m.MessageId;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
private void IntegratePagingView(ButtonForm dataForm)
|
||||
{
|
||||
//No Items
|
||||
if (dataForm.Rows == 0)
|
||||
{
|
||||
dataForm.AddButtonRow(new ButtonBase(NoItemsLabel, "$"));
|
||||
}
|
||||
|
||||
ButtonForm bf = new ButtonForm();
|
||||
|
||||
bf = PrepareCheckableLayout(dataForm);
|
||||
|
||||
|
||||
if (this.IsNavigationBarVisible)
|
||||
{
|
||||
//🔍
|
||||
ButtonRow row = new ButtonRow();
|
||||
row.Add(new ButtonBase(PreviousPageLabel, "$previous$"));
|
||||
row.Add(new ButtonBase(String.Format(Localizations.Default.Language["ButtonGrid_CurrentPage"], this.CurrentPageIndex + 1, this.PageCount), "$site$"));
|
||||
row.Add(new ButtonBase(NextPageLabel, "$next$"));
|
||||
|
||||
|
||||
dataForm.InsertButtonRow(0, row);
|
||||
|
||||
dataForm.AddButtonRow(row);
|
||||
}
|
||||
}
|
||||
|
||||
private ButtonForm PrepareCheckableLayout(ButtonForm dataForm)
|
||||
{
|
||||
var bf = new ButtonForm();
|
||||
for (int i = 0; i < dataForm.Rows; i++)
|
||||
{
|
||||
int it = (this.CurrentPageIndex * (this.MaximumRow - LayoutRows)) + i;
|
||||
|
||||
//if (it > dataForm.Rows - 1)
|
||||
// break;
|
||||
|
||||
var r = dataForm[i];
|
||||
|
||||
String s = CheckedRows.Contains(it) ? this.CheckedIconLabel : this.UncheckedIconLabel;
|
||||
|
||||
//On reply keyboards we need a unique text.
|
||||
if (this.KeyboardType == eKeyboardType.ReplyKeyboard)
|
||||
{
|
||||
s = $"{it + 1}. " + s;
|
||||
}
|
||||
|
||||
if (CheckedRows.Contains(it))
|
||||
{
|
||||
r.Insert(0, new ButtonBase(s, "uncheck$" + it.ToString()));
|
||||
}
|
||||
else
|
||||
{
|
||||
r.Insert(0, new ButtonBase(s, "check$" + it.ToString()));
|
||||
}
|
||||
|
||||
bf.AddButtonRow(r);
|
||||
}
|
||||
|
||||
return bf;
|
||||
}
|
||||
|
||||
public bool PagingNecessary
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this.KeyboardType == eKeyboardType.InlineKeyBoard && TotalRows > Constants.Telegram.MaxInlineKeyBoardRows)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (this.KeyboardType == eKeyboardType.ReplyKeyboard && TotalRows > Constants.Telegram.MaxReplyKeyboardRows)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsNavigationBarVisible
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this.NavigationBarVisibility == eNavigationBarVisibility.always | (this.NavigationBarVisibility == eNavigationBarVisibility.auto && PagingNecessary))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the maximum number of rows
|
||||
/// </summary>
|
||||
public int MaximumRow
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (this.KeyboardType)
|
||||
{
|
||||
case eKeyboardType.InlineKeyBoard:
|
||||
return Constants.Telegram.MaxInlineKeyBoardRows;
|
||||
|
||||
case eKeyboardType.ReplyKeyboard:
|
||||
return Constants.Telegram.MaxReplyKeyboardRows;
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the number of all rows (layout + navigation + content);
|
||||
/// </summary>
|
||||
public int TotalRows
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.LayoutRows + DataSource.RowCount;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Contains the Number of Rows which are used by the layout.
|
||||
/// </summary>
|
||||
private int LayoutRows
|
||||
{
|
||||
get
|
||||
{
|
||||
int layoutRows = 0;
|
||||
|
||||
if (this.NavigationBarVisibility == eNavigationBarVisibility.always | this.NavigationBarVisibility == eNavigationBarVisibility.auto)
|
||||
layoutRows += 2;
|
||||
|
||||
if (this.HeadLayoutButtonRow != null && this.HeadLayoutButtonRow.Count > 0)
|
||||
layoutRows++;
|
||||
|
||||
if (this.SubHeadLayoutButtonRow != null && this.SubHeadLayoutButtonRow.Count > 0)
|
||||
layoutRows++;
|
||||
|
||||
return layoutRows;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the number of item rows per page.
|
||||
/// </summary>
|
||||
public int ItemRowsPerPage
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.MaximumRow - this.LayoutRows;
|
||||
}
|
||||
}
|
||||
|
||||
public int PageCount
|
||||
{
|
||||
get
|
||||
{
|
||||
if (DataSource.RowCount == 0)
|
||||
return 1;
|
||||
|
||||
//var bf = this.DataSource.PickAllItems(this.EnableSearch ? this.SearchQuery : null);
|
||||
|
||||
var max = this.DataSource.RowCount;
|
||||
|
||||
//if (this.EnableSearch && this.SearchQuery != null && this.SearchQuery != "")
|
||||
//{
|
||||
// bf = bf.FilterDuplicate(this.SearchQuery);
|
||||
//}
|
||||
|
||||
if (max == 0)
|
||||
return 1;
|
||||
|
||||
return (int)Math.Ceiling((decimal)((decimal)max / (decimal)ItemRowsPerPage));
|
||||
}
|
||||
}
|
||||
|
||||
public override async Task Hidden(bool FormClose)
|
||||
{
|
||||
//Prepare for opening Modal, and comming back
|
||||
if (!FormClose)
|
||||
{
|
||||
this.Updated();
|
||||
}
|
||||
}
|
||||
|
||||
public List<ButtonBase> CheckedItems
|
||||
{
|
||||
get
|
||||
{
|
||||
List<ButtonBase> lst = new List<ButtonBase>();
|
||||
|
||||
foreach (var c in CheckedRows)
|
||||
{
|
||||
lst.Add(this.ButtonsForm[c][0]);
|
||||
}
|
||||
|
||||
return lst;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Tells the control that it has been updated.
|
||||
/// </summary>
|
||||
public void Updated()
|
||||
{
|
||||
this.RenderNecessary = true;
|
||||
}
|
||||
|
||||
public async override Task Cleanup()
|
||||
{
|
||||
if (this.MessageId == null)
|
||||
return;
|
||||
|
||||
switch (this.KeyboardType)
|
||||
{
|
||||
case eKeyboardType.InlineKeyBoard:
|
||||
|
||||
await this.Device.DeleteMessage(this.MessageId.Value);
|
||||
|
||||
this.MessageId = null;
|
||||
|
||||
break;
|
||||
case eKeyboardType.ReplyKeyboard:
|
||||
|
||||
if (this.HideKeyboardOnCleanup)
|
||||
{
|
||||
await this.Device.HideReplyKeyboard();
|
||||
}
|
||||
|
||||
this.MessageId = null;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using TelegramBotBase.Args;
|
||||
using TelegramBotBase.Base;
|
||||
|
||||
namespace TelegramBotBase.Controls.Hybrid
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// This Control is for having a basic form content switching control.
|
||||
/// </summary>
|
||||
public abstract class MultiView : Base.ControlBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Index of the current View.
|
||||
/// </summary>
|
||||
public int SelectedViewIndex
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_iSelectedViewIndex;
|
||||
}
|
||||
set
|
||||
{
|
||||
m_iSelectedViewIndex = value;
|
||||
|
||||
//Already rendered? Re-Render
|
||||
if (_Rendered)
|
||||
ForceRender().Wait();
|
||||
}
|
||||
}
|
||||
|
||||
private int m_iSelectedViewIndex = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Hold if the View has been rendered already.
|
||||
/// </summary>
|
||||
private bool _Rendered = false;
|
||||
|
||||
private List<int> Messages { get; set; }
|
||||
|
||||
|
||||
public MultiView()
|
||||
{
|
||||
Messages = new List<int>();
|
||||
}
|
||||
|
||||
|
||||
private void Device_MessageSent(object sender, MessageSentEventArgs e)
|
||||
{
|
||||
if (e.Origin == null || !e.Origin.IsSubclassOf(typeof(MultiView)))
|
||||
return;
|
||||
|
||||
this.Messages.Add(e.MessageId);
|
||||
}
|
||||
|
||||
public override void Init()
|
||||
{
|
||||
Device.MessageSent += Device_MessageSent;
|
||||
}
|
||||
|
||||
public override async Task Load(MessageResult result)
|
||||
{
|
||||
_Rendered = false;
|
||||
}
|
||||
|
||||
|
||||
public override async Task Render(MessageResult result)
|
||||
{
|
||||
//When already rendered, skip rendering
|
||||
if (_Rendered)
|
||||
return;
|
||||
|
||||
await CleanUpView();
|
||||
|
||||
await RenderView(new RenderViewEventArgs(this.SelectedViewIndex));
|
||||
|
||||
_Rendered = true;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Will get invoked on rendering the current controls view.
|
||||
/// </summary>
|
||||
/// <param name="e"></param>
|
||||
public virtual async Task RenderView(RenderViewEventArgs e)
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
|
||||
async Task CleanUpView()
|
||||
{
|
||||
|
||||
var tasks = new List<Task>();
|
||||
|
||||
foreach (var msg in this.Messages)
|
||||
{
|
||||
tasks.Add(this.Device.DeleteMessage(msg));
|
||||
}
|
||||
|
||||
await Task.WhenAll(tasks);
|
||||
|
||||
this.Messages.Clear();
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Forces render of control contents.
|
||||
/// </summary>
|
||||
public async Task ForceRender()
|
||||
{
|
||||
await CleanUpView();
|
||||
|
||||
await RenderView(new RenderViewEventArgs(this.SelectedViewIndex));
|
||||
|
||||
_Rendered = true;
|
||||
}
|
||||
|
||||
public override async Task Cleanup()
|
||||
{
|
||||
Device.MessageSent -= Device_MessageSent;
|
||||
|
||||
await CleanUpView();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,279 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using TelegramBotBase.Base;
|
||||
using TelegramBotBase.Enums;
|
||||
using TelegramBotBase.Form;
|
||||
using TelegramBotBase.Tools;
|
||||
using static TelegramBotBase.Tools.Arrays;
|
||||
using static TelegramBotBase.Tools.Time;
|
||||
|
||||
namespace TelegramBotBase.Controls.Inline
|
||||
{
|
||||
public class CalendarPicker : Base.ControlBase
|
||||
{
|
||||
|
||||
public DateTime SelectedDate { get; set; }
|
||||
|
||||
public DateTime VisibleMonth { get; set; }
|
||||
|
||||
public DayOfWeek FirstDayOfWeek { get; set; }
|
||||
|
||||
public CultureInfo Culture { get; set; }
|
||||
|
||||
|
||||
private int? MessageId { get; set; }
|
||||
|
||||
public String Title { get; set; } = Localizations.Default.Language["CalendarPicker_Title"];
|
||||
|
||||
public eMonthPickerMode PickerMode { get; set; }
|
||||
|
||||
public bool EnableDayView { get; set; } = true;
|
||||
|
||||
public bool EnableMonthView { get; set; } = true;
|
||||
|
||||
public bool EnableYearView { get; set; } = true;
|
||||
|
||||
public CalendarPicker(CultureInfo culture)
|
||||
{
|
||||
this.SelectedDate = DateTime.Today;
|
||||
this.VisibleMonth = DateTime.Today;
|
||||
this.FirstDayOfWeek = DayOfWeek.Monday;
|
||||
this.Culture = culture;
|
||||
this.PickerMode = eMonthPickerMode.day;
|
||||
}
|
||||
|
||||
public CalendarPicker() : this(new CultureInfo("en-en")) { }
|
||||
|
||||
|
||||
|
||||
|
||||
public override async Task Action(MessageResult result, String value = null)
|
||||
{
|
||||
await result.ConfirmAction();
|
||||
|
||||
switch (result.RawData)
|
||||
{
|
||||
case "$next$":
|
||||
|
||||
switch (this.PickerMode)
|
||||
{
|
||||
case eMonthPickerMode.day:
|
||||
this.VisibleMonth = this.VisibleMonth.AddMonths(1);
|
||||
break;
|
||||
|
||||
case eMonthPickerMode.month:
|
||||
this.VisibleMonth = this.VisibleMonth.AddYears(1);
|
||||
break;
|
||||
|
||||
case eMonthPickerMode.year:
|
||||
this.VisibleMonth = this.VisibleMonth.AddYears(10);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
break;
|
||||
case "$prev$":
|
||||
|
||||
switch (this.PickerMode)
|
||||
{
|
||||
case eMonthPickerMode.day:
|
||||
this.VisibleMonth = this.VisibleMonth.AddMonths(-1);
|
||||
break;
|
||||
|
||||
case eMonthPickerMode.month:
|
||||
this.VisibleMonth = this.VisibleMonth.AddYears(-1);
|
||||
break;
|
||||
|
||||
case eMonthPickerMode.year:
|
||||
this.VisibleMonth = this.VisibleMonth.AddYears(-10);
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case "$monthtitle$":
|
||||
|
||||
if (this.EnableMonthView)
|
||||
{
|
||||
this.PickerMode = eMonthPickerMode.month;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case "$yeartitle$":
|
||||
|
||||
if (this.EnableYearView)
|
||||
{
|
||||
this.PickerMode = eMonthPickerMode.year;
|
||||
}
|
||||
|
||||
break;
|
||||
case "$yearstitle$":
|
||||
|
||||
if (this.EnableMonthView)
|
||||
{
|
||||
this.PickerMode = eMonthPickerMode.month;
|
||||
}
|
||||
|
||||
this.VisibleMonth = this.SelectedDate;
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
int day = 0;
|
||||
if (result.RawData.StartsWith("d-") && TryParseDay(result.RawData.Split('-')[1], this.SelectedDate, out day))
|
||||
{
|
||||
this.SelectedDate = new DateTime(this.VisibleMonth.Year, this.VisibleMonth.Month, day);
|
||||
}
|
||||
|
||||
int month = 0;
|
||||
if (result.RawData.StartsWith("m-") && TryParseMonth(result.RawData.Split('-')[1], out month))
|
||||
{
|
||||
this.SelectedDate = new DateTime(this.VisibleMonth.Year, month, 1);
|
||||
this.VisibleMonth = this.SelectedDate;
|
||||
|
||||
if (this.EnableDayView)
|
||||
{
|
||||
this.PickerMode = eMonthPickerMode.day;
|
||||
}
|
||||
}
|
||||
|
||||
int year = 0;
|
||||
if (result.RawData.StartsWith("y-") && TryParseYear(result.RawData.Split('-')[1], out year))
|
||||
{
|
||||
this.SelectedDate = new DateTime(year, SelectedDate.Month, SelectedDate.Day);
|
||||
this.VisibleMonth = this.SelectedDate;
|
||||
|
||||
if (this.EnableMonthView)
|
||||
{
|
||||
this.PickerMode = eMonthPickerMode.month;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
public override async Task Render(MessageResult result)
|
||||
{
|
||||
|
||||
|
||||
|
||||
ButtonForm bf = new ButtonForm();
|
||||
|
||||
switch (this.PickerMode)
|
||||
{
|
||||
case eMonthPickerMode.day:
|
||||
|
||||
var month = this.VisibleMonth;
|
||||
|
||||
string[] dayNamesNormal = this.Culture.DateTimeFormat.ShortestDayNames;
|
||||
string[] dayNamesShifted = Shift(dayNamesNormal, (int)this.FirstDayOfWeek);
|
||||
|
||||
bf.AddButtonRow(new ButtonBase(Localizations.Default.Language["CalendarPicker_PreviousPage"], "$prev$"), new ButtonBase(this.Culture.DateTimeFormat.MonthNames[month.Month - 1] + " " + month.Year.ToString(), "$monthtitle$"), new ButtonBase(Localizations.Default.Language["CalendarPicker_NextPage"], "$next$"));
|
||||
|
||||
bf.AddButtonRow(dayNamesShifted.Select(a => new ButtonBase(a, a)).ToList());
|
||||
|
||||
//First Day of month
|
||||
var firstDay = new DateTime(month.Year, month.Month, 1);
|
||||
|
||||
//Last Day of month
|
||||
var lastDay = firstDay.LastDayOfMonth();
|
||||
|
||||
//Start of Week where first day of month is (left border)
|
||||
var start = firstDay.StartOfWeek(this.FirstDayOfWeek);
|
||||
|
||||
//End of week where last day of month is (right border)
|
||||
var end = lastDay.EndOfWeek(this.FirstDayOfWeek);
|
||||
|
||||
for (int i = 0; i <= ((end - start).Days / 7); i++)
|
||||
{
|
||||
var lst = new List<ButtonBase>();
|
||||
for (int id = 0; id < 7; id++)
|
||||
{
|
||||
var d = start.AddDays((i * 7) + id);
|
||||
if (d < firstDay | d > lastDay)
|
||||
{
|
||||
lst.Add(new ButtonBase("-", "m-" + d.Day.ToString()));
|
||||
continue;
|
||||
}
|
||||
|
||||
var day = d.Day.ToString();
|
||||
|
||||
if (d == DateTime.Today)
|
||||
{
|
||||
day = "(" + day + ")";
|
||||
}
|
||||
|
||||
lst.Add(new ButtonBase((this.SelectedDate == d ? "[" + day + "]" : day), "d-" + d.Day.ToString()));
|
||||
}
|
||||
bf.AddButtonRow(lst);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case eMonthPickerMode.month:
|
||||
|
||||
bf.AddButtonRow(new ButtonBase(Localizations.Default.Language["CalendarPicker_PreviousPage"], "$prev$"), new ButtonBase(this.VisibleMonth.Year.ToString("0000"), "$yeartitle$"), new ButtonBase(Localizations.Default.Language["CalendarPicker_NextPage"], "$next$"));
|
||||
|
||||
var months = this.Culture.DateTimeFormat.MonthNames;
|
||||
|
||||
var buttons = months.Select((a, b) => new ButtonBase((b == this.SelectedDate.Month - 1 && this.SelectedDate.Year == this.VisibleMonth.Year ? "[ " + a + " ]" : a), "m-" + (b + 1).ToString()));
|
||||
|
||||
bf.AddSplitted(buttons, 2);
|
||||
|
||||
break;
|
||||
|
||||
case eMonthPickerMode.year:
|
||||
|
||||
bf.AddButtonRow(new ButtonBase(Localizations.Default.Language["CalendarPicker_PreviousPage"], "$prev$"), new ButtonBase("Year", "$yearstitle$"), new ButtonBase(Localizations.Default.Language["CalendarPicker_NextPage"], "$next$"));
|
||||
|
||||
var starti = Math.Floor(this.VisibleMonth.Year / 10f) * 10;
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
var m = starti + (i * 2);
|
||||
bf.AddButtonRow(new ButtonBase((this.SelectedDate.Year == m ? "[ " + m.ToString() + " ]" : m.ToString()), "y-" + m.ToString()), new ButtonBase((this.SelectedDate.Year == (m + 1) ? "[ " + (m + 1).ToString() + " ]" : (m + 1).ToString()), "y-" + (m + 1).ToString()));
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
|
||||
if (this.MessageId != null)
|
||||
{
|
||||
var m = await this.Device.Edit(this.MessageId.Value, this.Title, bf);
|
||||
}
|
||||
else
|
||||
{
|
||||
var m = await this.Device.Send(this.Title, bf);
|
||||
this.MessageId = m.MessageId;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public override async Task Cleanup()
|
||||
{
|
||||
|
||||
if (this.MessageId != null)
|
||||
{
|
||||
await this.Device.DeleteMessage(this.MessageId.Value);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using TelegramBotBase.Base;
|
||||
using TelegramBotBase.Enums;
|
||||
using TelegramBotBase.Form;
|
||||
|
||||
namespace TelegramBotBase.Controls.Inline
|
||||
{
|
||||
public class MonthPicker : CalendarPicker
|
||||
{
|
||||
|
||||
|
||||
|
||||
public MonthPicker()
|
||||
{
|
||||
this.PickerMode = eMonthPickerMode.month;
|
||||
this.EnableDayView = false;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using TelegramBotBase.Base;
|
||||
using TelegramBotBase.Form;
|
||||
|
||||
namespace TelegramBotBase.Controls.Inline
|
||||
{
|
||||
public class MultiToggleButton : ControlBase
|
||||
{
|
||||
/// <summary>
|
||||
/// This contains the selected icon.
|
||||
/// </summary>
|
||||
public String SelectedIcon { get; set; } = Localizations.Default.Language["MultiToggleButton_SelectedIcon"];
|
||||
|
||||
/// <summary>
|
||||
/// This will appear on the ConfirmAction message (if not empty)
|
||||
/// </summary>
|
||||
public String ChangedString { get; set; } = Localizations.Default.Language["MultiToggleButton_Changed"];
|
||||
|
||||
/// <summary>
|
||||
/// This holds the title of the control.
|
||||
/// </summary>
|
||||
public String Title { get; set; } = Localizations.Default.Language["MultiToggleButton_Title"];
|
||||
|
||||
public int? MessageId { get; set; }
|
||||
|
||||
private bool RenderNecessary = true;
|
||||
|
||||
private static readonly object __evToggled = new object();
|
||||
|
||||
private readonly EventHandlerList Events = new EventHandlerList();
|
||||
|
||||
/// <summary>
|
||||
/// This will hold all options available.
|
||||
/// </summary>
|
||||
public List<ButtonBase> Options { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This will set if an empty selection (null) is allowed.
|
||||
/// </summary>
|
||||
public bool AllowEmptySelection { get; set; } = true;
|
||||
|
||||
|
||||
public MultiToggleButton()
|
||||
{
|
||||
Options = new List<ButtonBase>();
|
||||
}
|
||||
|
||||
public event EventHandler Toggled
|
||||
{
|
||||
add
|
||||
{
|
||||
this.Events.AddHandler(__evToggled, value);
|
||||
}
|
||||
remove
|
||||
{
|
||||
this.Events.RemoveHandler(__evToggled, value);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnToggled(EventArgs e)
|
||||
{
|
||||
(this.Events[__evToggled] as EventHandler)?.Invoke(this, e);
|
||||
}
|
||||
|
||||
public override async Task Action(MessageResult result, String value = null)
|
||||
{
|
||||
if (result.Handled)
|
||||
return;
|
||||
|
||||
await result.ConfirmAction(this.ChangedString);
|
||||
|
||||
switch (value ?? "unknown")
|
||||
{
|
||||
default:
|
||||
|
||||
var s = value.Split('$');
|
||||
|
||||
if (s[0] == "check" && s.Length > 1)
|
||||
{
|
||||
int index = 0;
|
||||
if (!int.TryParse(s[1], out index))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if(SelectedOption== null || SelectedOption != this.Options[index])
|
||||
{
|
||||
this.SelectedOption = this.Options[index];
|
||||
OnToggled(new EventArgs());
|
||||
}
|
||||
else if(this.AllowEmptySelection)
|
||||
{
|
||||
this.SelectedOption = null;
|
||||
OnToggled(new EventArgs());
|
||||
}
|
||||
|
||||
RenderNecessary = true;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
RenderNecessary = false;
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
result.Handled = true;
|
||||
|
||||
}
|
||||
|
||||
public override async Task Render(MessageResult result)
|
||||
{
|
||||
if (!RenderNecessary)
|
||||
return;
|
||||
|
||||
var bf = new ButtonForm(this);
|
||||
|
||||
var lst = new List<ButtonBase>();
|
||||
foreach (var o in this.Options)
|
||||
{
|
||||
var index = this.Options.IndexOf(o);
|
||||
if (o == this.SelectedOption)
|
||||
{
|
||||
lst.Add(new ButtonBase(SelectedIcon + " " + o.Text, "check$" + index));
|
||||
continue;
|
||||
}
|
||||
|
||||
lst.Add(new ButtonBase(o.Text, "check$" + index));
|
||||
}
|
||||
|
||||
bf.AddButtonRow(lst);
|
||||
|
||||
if (this.MessageId != null)
|
||||
{
|
||||
var m = await this.Device.Edit(this.MessageId.Value, this.Title, bf);
|
||||
}
|
||||
else
|
||||
{
|
||||
var m = await this.Device.Send(this.Title, bf, disableNotification: true);
|
||||
if (m != null)
|
||||
{
|
||||
this.MessageId = m.MessageId;
|
||||
}
|
||||
}
|
||||
|
||||
this.RenderNecessary = false;
|
||||
|
||||
|
||||
}
|
||||
|
||||
public ButtonBase SelectedOption
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+3
-2
@@ -3,8 +3,9 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using TelegramBotBase.Base;
|
||||
|
||||
namespace TelegramBotBase.Controls
|
||||
namespace TelegramBotBase.Controls.Inline
|
||||
{
|
||||
/// <summary>
|
||||
/// A simple control for show and managing progress.
|
||||
@@ -220,7 +221,7 @@ namespace TelegramBotBase.Controls
|
||||
|
||||
}
|
||||
|
||||
public async override Task Render()
|
||||
public async override Task Render(MessageResult result)
|
||||
{
|
||||
if (!this.RenderNecessary)
|
||||
{
|
||||
@@ -0,0 +1,147 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using TelegramBotBase.Base;
|
||||
using TelegramBotBase.Form;
|
||||
|
||||
namespace TelegramBotBase.Controls.Inline
|
||||
{
|
||||
public class ToggleButton : ControlBase
|
||||
{
|
||||
|
||||
public String UncheckedIcon { get; set; } = Localizations.Default.Language["ToggleButton_OffIcon"];
|
||||
|
||||
public String CheckedIcon { get; set; } = Localizations.Default.Language["ToggleButton_OnIcon"];
|
||||
|
||||
public String CheckedString { get; set; } = Localizations.Default.Language["ToggleButton_On"];
|
||||
|
||||
public String UncheckedString { get; set; } = Localizations.Default.Language["ToggleButton_Off"];
|
||||
|
||||
public String ChangedString { get; set; } = Localizations.Default.Language["ToggleButton_Changed"];
|
||||
|
||||
public String Title { get; set; } = Localizations.Default.Language["ToggleButton_Title"];
|
||||
|
||||
public int? MessageId { get; set; }
|
||||
|
||||
public bool Checked { get; set; }
|
||||
|
||||
private bool RenderNecessary = true;
|
||||
|
||||
private static readonly object __evToggled = new object();
|
||||
|
||||
private readonly EventHandlerList Events = new EventHandlerList();
|
||||
|
||||
|
||||
public ToggleButton()
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
|
||||
public ToggleButton(String CheckedString, String UncheckedString)
|
||||
{
|
||||
this.CheckedString = CheckedString;
|
||||
this.UncheckedString = UncheckedString;
|
||||
}
|
||||
|
||||
public event EventHandler Toggled
|
||||
{
|
||||
add
|
||||
{
|
||||
this.Events.AddHandler(__evToggled, value);
|
||||
}
|
||||
remove
|
||||
{
|
||||
this.Events.RemoveHandler(__evToggled, value);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnToggled(EventArgs e)
|
||||
{
|
||||
(this.Events[__evToggled] as EventHandler)?.Invoke(this, e);
|
||||
}
|
||||
|
||||
public override async Task Action(MessageResult result, String value = null)
|
||||
{
|
||||
|
||||
if (result.Handled)
|
||||
return;
|
||||
|
||||
await result.ConfirmAction(this.ChangedString);
|
||||
|
||||
switch (value ?? "unknown")
|
||||
{
|
||||
case "on":
|
||||
|
||||
if (this.Checked)
|
||||
return;
|
||||
|
||||
RenderNecessary = true;
|
||||
|
||||
this.Checked = true;
|
||||
|
||||
OnToggled(new EventArgs());
|
||||
|
||||
break;
|
||||
|
||||
case "off":
|
||||
|
||||
if (!this.Checked)
|
||||
return;
|
||||
|
||||
RenderNecessary = true;
|
||||
|
||||
this.Checked = false;
|
||||
|
||||
OnToggled(new EventArgs());
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
RenderNecessary = false;
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
result.Handled = true;
|
||||
|
||||
}
|
||||
|
||||
public override async Task Render(MessageResult result)
|
||||
{
|
||||
if (!RenderNecessary)
|
||||
return;
|
||||
|
||||
var bf = new ButtonForm(this);
|
||||
|
||||
ButtonBase bOn = new ButtonBase((this.Checked ? CheckedIcon : UncheckedIcon) + " " + CheckedString, "on");
|
||||
|
||||
ButtonBase bOff = new ButtonBase((!this.Checked ? CheckedIcon : UncheckedIcon) + " " + UncheckedString, "off");
|
||||
|
||||
bf.AddButtonRow(bOn, bOff);
|
||||
|
||||
if (this.MessageId != null)
|
||||
{
|
||||
var m = await this.Device.Edit(this.MessageId.Value, this.Title, bf);
|
||||
}
|
||||
else
|
||||
{
|
||||
var m = await this.Device.Send(this.Title, bf, disableNotification: true);
|
||||
if (m != null)
|
||||
{
|
||||
this.MessageId = m.MessageId;
|
||||
}
|
||||
}
|
||||
|
||||
this.RenderNecessary = false;
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using TelegramBotBase.Base;
|
||||
using TelegramBotBase.Form;
|
||||
|
||||
namespace TelegramBotBase.Controls.Inline
|
||||
{
|
||||
public class TreeView : ControlBase
|
||||
{
|
||||
public List<TreeViewNode> Nodes { get; set; }
|
||||
|
||||
public TreeViewNode SelectedNode { get; set; }
|
||||
|
||||
public TreeViewNode VisibleNode { get; set; }
|
||||
|
||||
public String Title { get; set; }
|
||||
|
||||
private int? MessageId { get; set; }
|
||||
|
||||
public String MoveUpIcon { get; set; } = Localizations.Default.Language["TreeView_LevelUp"];
|
||||
|
||||
public TreeView()
|
||||
{
|
||||
this.Nodes = new List<TreeViewNode>();
|
||||
this.Title = Localizations.Default.Language["TreeView_Title"];
|
||||
}
|
||||
|
||||
|
||||
public override async Task Action(MessageResult result, String value = null)
|
||||
{
|
||||
await result.ConfirmAction();
|
||||
|
||||
if (result.Handled)
|
||||
return;
|
||||
|
||||
var val = result.RawData;
|
||||
|
||||
switch (val)
|
||||
{
|
||||
case "up":
|
||||
case "parent":
|
||||
|
||||
this.VisibleNode = (this.VisibleNode?.ParentNode);
|
||||
|
||||
result.Handled = true;
|
||||
|
||||
break;
|
||||
default:
|
||||
|
||||
var n = (this.VisibleNode != null ? this.VisibleNode.FindNodeByValue(val) : this.Nodes.FirstOrDefault(a => a.Value == val));
|
||||
|
||||
if (n == null)
|
||||
return;
|
||||
|
||||
|
||||
if (n.ChildNodes.Count > 0)
|
||||
{
|
||||
this.VisibleNode = n;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.SelectedNode = (this.SelectedNode != n ? n : null);
|
||||
}
|
||||
|
||||
result.Handled = true;
|
||||
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
public override async Task Render(MessageResult result)
|
||||
{
|
||||
var startnode = this.VisibleNode;
|
||||
|
||||
var nodes = (startnode?.ChildNodes ?? this.Nodes);
|
||||
|
||||
ButtonForm bf = new ButtonForm();
|
||||
|
||||
if (startnode != null)
|
||||
{
|
||||
bf.AddButtonRow(new ButtonBase(this.MoveUpIcon, "up"), new ButtonBase(startnode.Text, "parent"));
|
||||
}
|
||||
|
||||
foreach (var n in nodes)
|
||||
{
|
||||
var s = n.Text;
|
||||
if (this.SelectedNode == n)
|
||||
{
|
||||
s = "[ " + s + " ]";
|
||||
}
|
||||
|
||||
bf.AddButtonRow(new ButtonBase(s, n.Value, n.Url));
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (this.MessageId != null)
|
||||
{
|
||||
var m = await this.Device.Edit(this.MessageId.Value, this.Title, bf);
|
||||
}
|
||||
else
|
||||
{
|
||||
var m = await this.Device.Send(this.Title, bf);
|
||||
this.MessageId = m.MessageId;
|
||||
}
|
||||
}
|
||||
|
||||
public String GetPath()
|
||||
{
|
||||
return (this.VisibleNode?.GetPath() ?? "\\");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace TelegramBotBase.Controls.Inline
|
||||
{
|
||||
public class TreeViewNode
|
||||
{
|
||||
public String Text { get; set; }
|
||||
|
||||
public String Value { get; set; }
|
||||
|
||||
public String Url { get; set; }
|
||||
|
||||
public List<TreeViewNode> ChildNodes { get; set; } = new List<TreeViewNode>();
|
||||
|
||||
public TreeViewNode ParentNode { get; set; }
|
||||
|
||||
public TreeViewNode(String Text, String Value)
|
||||
{
|
||||
this.Text = Text;
|
||||
this.Value = Value;
|
||||
}
|
||||
|
||||
public TreeViewNode(String Text, String Value, String Url) : this(Text, Value)
|
||||
{
|
||||
this.Url = Url;
|
||||
}
|
||||
|
||||
public TreeViewNode(String Text, String Value, params TreeViewNode[] childnodes) : this(Text, Value)
|
||||
{
|
||||
foreach(var c in childnodes)
|
||||
{
|
||||
AddNode(c);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void AddNode(TreeViewNode node)
|
||||
{
|
||||
node.ParentNode = this;
|
||||
ChildNodes.Add(node);
|
||||
}
|
||||
|
||||
public TreeViewNode FindNodeByValue(String Value)
|
||||
{
|
||||
return this.ChildNodes.FirstOrDefault(a => a.Value == Value);
|
||||
}
|
||||
|
||||
public String GetPath()
|
||||
{
|
||||
String s = "\\" + this.Value;
|
||||
var p = this;
|
||||
while (p.ParentNode != null)
|
||||
{
|
||||
s = "\\" + p.ParentNode.Value + s;
|
||||
p = p.ParentNode;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using TelegramBotBase.Controls.Hybrid;
|
||||
using TelegramBotBase.Form;
|
||||
using TelegramBotBase.Interfaces;
|
||||
|
||||
namespace TelegramBotBase.Datasources
|
||||
{
|
||||
public class ButtonFormDataSource : Interfaces.IDataSource<ButtonRow>
|
||||
{
|
||||
public virtual ButtonForm ButtonForm
|
||||
{
|
||||
get
|
||||
{
|
||||
return __buttonform;
|
||||
}
|
||||
set
|
||||
{
|
||||
__buttonform = value;
|
||||
}
|
||||
}
|
||||
|
||||
private ButtonForm __buttonform = null;
|
||||
|
||||
public ButtonFormDataSource()
|
||||
{
|
||||
__buttonform = new ButtonForm();
|
||||
}
|
||||
|
||||
public ButtonFormDataSource(ButtonForm bf)
|
||||
{
|
||||
__buttonform = bf;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns the amount of rows exisiting.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public virtual int Count => ButtonForm.Count;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns the amount of rows.
|
||||
/// </summary>
|
||||
public virtual int RowCount => ButtonForm.Rows;
|
||||
|
||||
/// <summary>
|
||||
/// Returns the maximum amount of columns.
|
||||
/// </summary>
|
||||
public virtual int ColumnCount => ButtonForm.Cols;
|
||||
|
||||
/// <summary>
|
||||
/// Returns the row with the specific index.
|
||||
/// </summary>
|
||||
/// <param name="index"></param>
|
||||
/// <returns></returns>
|
||||
public virtual ButtonRow ItemAt(int index)
|
||||
{
|
||||
return ButtonForm[index];
|
||||
}
|
||||
|
||||
public virtual List<ButtonRow> ItemRange(int start, int count)
|
||||
{
|
||||
return ButtonForm.GetRange(start, count);
|
||||
}
|
||||
|
||||
public virtual List<ButtonRow> AllItems()
|
||||
{
|
||||
return ButtonForm.ToArray();
|
||||
}
|
||||
|
||||
public virtual ButtonForm PickItems(int start, int count, String filter = null)
|
||||
{
|
||||
ButtonForm bf = new ButtonForm();
|
||||
ButtonForm dataForm = null;
|
||||
|
||||
if (filter == null)
|
||||
{
|
||||
dataForm = ButtonForm.Duplicate();
|
||||
}
|
||||
else
|
||||
{
|
||||
dataForm = ButtonForm.FilterDuplicate(filter, true);
|
||||
}
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
int it = start + i;
|
||||
|
||||
if (it > dataForm.Rows - 1)
|
||||
break;
|
||||
|
||||
bf.AddButtonRow(dataForm[it]);
|
||||
}
|
||||
|
||||
return bf;
|
||||
}
|
||||
|
||||
public virtual ButtonForm PickAllItems(String filter = null)
|
||||
{
|
||||
if (filter == null)
|
||||
return ButtonForm.Duplicate();
|
||||
|
||||
|
||||
return ButtonForm.FilterDuplicate(filter, true);
|
||||
}
|
||||
|
||||
public virtual Tuple<ButtonRow, int> FindRow(String text, bool useText = true)
|
||||
{
|
||||
return ButtonForm.FindRow(text, useText);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the maximum items of this data source.
|
||||
/// </summary>
|
||||
/// <param name="filter"></param>
|
||||
/// <returns></returns>
|
||||
public virtual int CalculateMax(String filter = null)
|
||||
{
|
||||
return PickAllItems(filter).Rows;
|
||||
}
|
||||
|
||||
public virtual ButtonRow Render(object data)
|
||||
{
|
||||
return data as ButtonRow;
|
||||
}
|
||||
|
||||
|
||||
public static implicit operator ButtonFormDataSource(ButtonForm bf)
|
||||
{
|
||||
return new ButtonFormDataSource(bf);
|
||||
}
|
||||
|
||||
public static implicit operator ButtonForm(ButtonFormDataSource ds)
|
||||
{
|
||||
return ds.ButtonForm;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace TelegramBotBase.Datasources
|
||||
{
|
||||
public class StaticDataSource<T> : Interfaces.IDataSource<T>
|
||||
{
|
||||
List<T> Data { get; set; }
|
||||
|
||||
public StaticDataSource()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public StaticDataSource(List<T> data)
|
||||
{
|
||||
this.Data = data;
|
||||
}
|
||||
|
||||
|
||||
public int Count
|
||||
{
|
||||
get
|
||||
{
|
||||
return Data.Count;
|
||||
}
|
||||
}
|
||||
|
||||
public T ItemAt(int index)
|
||||
{
|
||||
return Data[index];
|
||||
}
|
||||
|
||||
public List<T> ItemRange(int start, int count)
|
||||
{
|
||||
return Data.Skip(start).Take(count).ToList();
|
||||
}
|
||||
|
||||
public List<T> AllItems()
|
||||
{
|
||||
return Data;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace TelegramBotBase.Enums
|
||||
{
|
||||
public enum eDeleteMode
|
||||
{
|
||||
/// <summary>
|
||||
/// Don't delete any message.
|
||||
/// </summary>
|
||||
None = 0,
|
||||
/// <summary>
|
||||
/// Delete messages on every callback/action.
|
||||
/// </summary>
|
||||
OnEveryCall = 1,
|
||||
/// <summary>
|
||||
/// Delete on leaving this form.
|
||||
/// </summary>
|
||||
OnLeavingForm = 2
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace TelegramBotBase.Enums
|
||||
{
|
||||
public enum eDeleteSide
|
||||
{
|
||||
/// <summary>
|
||||
/// Delete only messages from this bot.
|
||||
/// </summary>
|
||||
BotOnly = 0,
|
||||
/// <summary>
|
||||
/// Delete only user messages.
|
||||
/// </summary>
|
||||
UserOnly = 1,
|
||||
/// <summary>
|
||||
/// Delete all messages in this context.
|
||||
/// </summary>
|
||||
Both = 2
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace TelegramBotBase.Enums
|
||||
{
|
||||
public enum eKeyboardType
|
||||
{
|
||||
/// <summary>
|
||||
/// Uses a ReplyKeyboardMarkup
|
||||
/// </summary>
|
||||
ReplyKeyboard = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Uses a InlineKeyboardMakup
|
||||
/// </summary>
|
||||
InlineKeyBoard = 1
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace TelegramBotBase.Enums
|
||||
{
|
||||
public enum eMonthPickerMode
|
||||
{
|
||||
/// <summary>
|
||||
/// Shows the calendar with day picker mode
|
||||
/// </summary>
|
||||
day = 0,
|
||||
/// <summary>
|
||||
/// Shows the calendar with month overview
|
||||
/// </summary>
|
||||
month = 1,
|
||||
/// <summary>
|
||||
/// Shows the calendar with year overview
|
||||
/// </summary>
|
||||
year = 2
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace TelegramBotBase.Enums
|
||||
{
|
||||
public enum eNavigationBarVisibility
|
||||
{
|
||||
/// <summary>
|
||||
/// Shows it depending on the amount of items.
|
||||
/// </summary>
|
||||
auto = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Will not show it at any time.
|
||||
/// </summary>
|
||||
never = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Will show it at any time.
|
||||
/// </summary>
|
||||
always = 2
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace TelegramBotBase.Enums
|
||||
{
|
||||
public enum eSettings
|
||||
{
|
||||
/// <summary>
|
||||
/// How often could a form navigate to another (within one user action/call/message)
|
||||
/// </summary>
|
||||
NavigationMaximum = 1,
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Loggs all messages and sent them to the event handler
|
||||
/// </summary>
|
||||
LogAllMessages = 2,
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Skips all messages during running (good for big delay updates)
|
||||
/// </summary>
|
||||
SkipAllMessages = 3,
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Does stick to the console event handler and saves all sessions on exit.
|
||||
/// </summary>
|
||||
SaveSessionsOnConsoleExit = 4,
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Indicates the maximum number of times a request that received error
|
||||
/// 429 will be sent again after a timeout until it receives code 200 or an error code not equal to 429.
|
||||
/// </summary>
|
||||
MaxNumberOfRetries = 5,
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace TelegramBotBase.Exceptions
|
||||
{
|
||||
public class MaxLengthException : Exception
|
||||
{
|
||||
public MaxLengthException(int length) : base($"Your messages with a length of {length} is too long for telegram. Actually is {Constants.Telegram.MaxMessageLength} characters allowed. Please split it.")
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace TelegramBotBase.Exceptions
|
||||
{
|
||||
public class MaximumColsException : Exception
|
||||
{
|
||||
public int Value { get; set; }
|
||||
|
||||
public int Maximum { get; set; }
|
||||
|
||||
|
||||
public override string Message
|
||||
{
|
||||
get
|
||||
{
|
||||
return $"You have exceeded the maximum of columns by {Value.ToString()} / {Maximum.ToString()}";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace TelegramBotBase.Exceptions
|
||||
{
|
||||
public class MaximumRowsReachedException : Exception
|
||||
{
|
||||
public int Value { get; set; }
|
||||
|
||||
public int Maximum { get; set; }
|
||||
|
||||
|
||||
public override string Message
|
||||
{
|
||||
get
|
||||
{
|
||||
return $"You have exceeded the maximum of rows by {Value.ToString()} / {Maximum.ToString()}";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using TelegramBotBase.Attributes;
|
||||
using TelegramBotBase.Base;
|
||||
|
||||
namespace TelegramBotBase.Form
|
||||
@@ -11,17 +12,16 @@ namespace TelegramBotBase.Form
|
||||
/// <summary>
|
||||
/// A simple prompt dialog with one ok Button
|
||||
/// </summary>
|
||||
public class AlertDialog : PromptDialog
|
||||
[IgnoreState]
|
||||
public class AlertDialog : ConfirmDialog
|
||||
{
|
||||
public String ButtonText { get; set; }
|
||||
|
||||
public AlertDialog(String Message, String ButtonText, FormBase FormToOpen = null) : base(Message)
|
||||
public AlertDialog(String Message, String ButtonText) : base(Message)
|
||||
{
|
||||
this.Buttons.Add(new ButtonBase(ButtonText, "ok"));
|
||||
this.ButtonText = ButtonText;
|
||||
|
||||
if (FormToOpen != null)
|
||||
this.ButtonForms.Add("ok", FormToOpen);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using TelegramBotBase.Args;
|
||||
using TelegramBotBase.Attributes;
|
||||
using TelegramBotBase.Base;
|
||||
|
||||
namespace TelegramBotBase.Form
|
||||
@@ -11,13 +13,22 @@ namespace TelegramBotBase.Form
|
||||
/// <summary>
|
||||
/// A prompt with a lot of buttons
|
||||
/// </summary>
|
||||
[IgnoreState]
|
||||
public class ArrayPromptDialog : FormBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The message the users sees.
|
||||
/// </summary>
|
||||
public String Message { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// An additional optional value.
|
||||
/// </summary>
|
||||
public object Tag { get; set; }
|
||||
|
||||
public ButtonBase[][] Buttons { get; set; }
|
||||
|
||||
[Obsolete]
|
||||
public Dictionary<String, FormBase> ButtonForms { get; set; } = new Dictionary<string, FormBase>();
|
||||
|
||||
private EventHandlerList __Events { get; set; } = new EventHandlerList();
|
||||
@@ -67,7 +78,7 @@ namespace TelegramBotBase.Form
|
||||
return;
|
||||
}
|
||||
|
||||
OnButtonClicked(new ButtonClickedEventArgs(button));
|
||||
OnButtonClicked(new ButtonClickedEventArgs(button) { Tag = this.Tag });
|
||||
|
||||
FormBase fb = ButtonForms.ContainsKey(call.Value) ? ButtonForms[call.Value] : null;
|
||||
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Telegram.Bot.Exceptions;
|
||||
using Telegram.Bot.Types;
|
||||
using TelegramBotBase.Args;
|
||||
using TelegramBotBase.Attributes;
|
||||
using TelegramBotBase.Base;
|
||||
using TelegramBotBase.Enums;
|
||||
|
||||
namespace TelegramBotBase.Form
|
||||
{
|
||||
@@ -13,35 +18,61 @@ namespace TelegramBotBase.Form
|
||||
/// </summary>
|
||||
public class AutoCleanForm : FormBase
|
||||
{
|
||||
List<Message> OldMessages { get; set; }
|
||||
[SaveState]
|
||||
public List<int> OldMessages { get; set; }
|
||||
|
||||
[SaveState]
|
||||
public eDeleteMode DeleteMode { get; set; }
|
||||
|
||||
public enum eDeleteMode
|
||||
{
|
||||
None = 0,
|
||||
OnEveryCall = 1,
|
||||
OnLeavingForm = 2
|
||||
}
|
||||
[SaveState]
|
||||
public eDeleteSide DeleteSide { get; set; }
|
||||
|
||||
|
||||
|
||||
public AutoCleanForm()
|
||||
{
|
||||
this.OldMessages = new List<Message>();
|
||||
this.OldMessages = new List<int>();
|
||||
this.DeleteMode = eDeleteMode.OnEveryCall;
|
||||
this.DeleteSide = eDeleteSide.BotOnly;
|
||||
|
||||
this.Init += AutoCleanForm_Init;
|
||||
|
||||
this.Closed += AutoCleanForm_Closed;
|
||||
|
||||
}
|
||||
|
||||
public override async Task Init(params object[] args)
|
||||
private async Task AutoCleanForm_Init(object sender, InitEventArgs e)
|
||||
{
|
||||
if (this.Device == null)
|
||||
return;
|
||||
|
||||
this.Device.MessageSent += Device_MessageSent;
|
||||
|
||||
this.Device.MessageReceived += Device_MessageReceived;
|
||||
|
||||
this.Device.MessageDeleted += Device_MessageDeleted;
|
||||
}
|
||||
|
||||
private void Device_MessageDeleted(object sender, MessageDeletedEventArgs e)
|
||||
{
|
||||
if (OldMessages.Contains(e.MessageId))
|
||||
OldMessages.Remove(e.MessageId);
|
||||
}
|
||||
|
||||
private void Device_MessageReceived(object sender, MessageReceivedEventArgs e)
|
||||
{
|
||||
if (this.DeleteSide == eDeleteSide.BotOnly)
|
||||
return;
|
||||
|
||||
this.OldMessages.Add(e.Message.MessageId);
|
||||
}
|
||||
|
||||
private void Device_MessageSent(object sender, MessageSentEventArgs e)
|
||||
{
|
||||
this.OldMessages.Add(e.Message);
|
||||
if (this.DeleteSide == eDeleteSide.UserOnly)
|
||||
return;
|
||||
|
||||
this.OldMessages.Add(e.Message.MessageId);
|
||||
}
|
||||
|
||||
public override async Task PreLoad(MessageResult message)
|
||||
@@ -49,41 +80,39 @@ namespace TelegramBotBase.Form
|
||||
if (this.DeleteMode != eDeleteMode.OnEveryCall)
|
||||
return;
|
||||
|
||||
while (this.OldMessages.Count > 0)
|
||||
{
|
||||
if (!await this.Device.DeleteMessage(this.OldMessages[0].MessageId))
|
||||
{
|
||||
//Nachricht konnte nicht gelöscht werden, vermutlich existiert diese nicht mehr
|
||||
if (this.OldMessages.Count > 0)
|
||||
this.OldMessages.RemoveAt(0);
|
||||
}
|
||||
}
|
||||
await MessageCleanup();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fügt eine Nachricht zu der Liste der löschenden hinzu.
|
||||
/// Adds a message to this of removable ones
|
||||
/// </summary>
|
||||
/// <param name="Id"></param>
|
||||
public void AddMessage(Message m)
|
||||
{
|
||||
this.OldMessages.Add(m);
|
||||
this.OldMessages.Add(m.MessageId);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Adds a message to this of removable ones
|
||||
/// </summary>
|
||||
/// <param name="Id"></param>
|
||||
public void AddMessage(int messageId)
|
||||
{
|
||||
this.OldMessages.Add(messageId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Behält die Nachricht mit der angegebenen Id.
|
||||
/// Keeps the message by removing it from the list
|
||||
/// </summary>
|
||||
/// <param name="Id"></param>
|
||||
public void LeaveMessage(int Id)
|
||||
{
|
||||
var m = this.OldMessages.FirstOrDefault(a => a.MessageId == Id);
|
||||
if (m == null)
|
||||
return;
|
||||
|
||||
this.OldMessages.Remove(m);
|
||||
this.OldMessages.Remove(Id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Behält die zuletzt gesendete Nachricht.
|
||||
/// Keeps the last sent message
|
||||
/// </summary>
|
||||
public void LeaveLastMessage()
|
||||
{
|
||||
@@ -93,15 +122,107 @@ namespace TelegramBotBase.Form
|
||||
this.OldMessages.RemoveAt(this.OldMessages.Count - 1);
|
||||
}
|
||||
|
||||
public async override Task Closed()
|
||||
private async Task AutoCleanForm_Closed(object sender, EventArgs e)
|
||||
{
|
||||
if (this.DeleteMode != eDeleteMode.OnLeavingForm)
|
||||
return;
|
||||
|
||||
foreach (var m in this.OldMessages)
|
||||
MessageCleanup().Wait();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cleans up all remembered messages.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task MessageCleanup()
|
||||
{
|
||||
var oldMessages = OldMessages.AsEnumerable();
|
||||
|
||||
#if !NET472
|
||||
while (oldMessages.Any())
|
||||
{
|
||||
await this.Device.DeleteMessage(m.MessageId);
|
||||
using var cts = new CancellationTokenSource();
|
||||
var deletedMessages = new ConcurrentBag<int>();
|
||||
var parallelQuery = OldMessages.AsParallel()
|
||||
.WithCancellation(cts.Token);
|
||||
Task retryAfterTask = null;
|
||||
try
|
||||
{
|
||||
parallelQuery.ForAll(i =>
|
||||
{
|
||||
try
|
||||
{
|
||||
Device.DeleteMessage(i).GetAwaiter().GetResult();
|
||||
deletedMessages.Add(i);
|
||||
}
|
||||
catch (ApiRequestException req) when (req.ErrorCode == 400)
|
||||
{
|
||||
deletedMessages.Add(i);
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (AggregateException ex)
|
||||
{
|
||||
cts.Cancel();
|
||||
|
||||
var retryAfterSeconds = ex.InnerExceptions
|
||||
.Where(e => e is ApiRequestException apiEx && apiEx.ErrorCode == 429)
|
||||
.Max(e => (int?)((ApiRequestException)e).Parameters.RetryAfter) ?? 0;
|
||||
retryAfterTask = Task.Delay(retryAfterSeconds * 1000);
|
||||
}
|
||||
|
||||
//deletedMessages.AsParallel().ForAll(i => Device.OnMessageDeleted(new MessageDeletedEventArgs(i)));
|
||||
|
||||
oldMessages = oldMessages.Where(x => !deletedMessages.Contains(x));
|
||||
if (retryAfterTask != null)
|
||||
await retryAfterTask;
|
||||
}
|
||||
#else
|
||||
while (oldMessages.Any())
|
||||
{
|
||||
using (var cts = new CancellationTokenSource())
|
||||
{
|
||||
var deletedMessages = new ConcurrentBag<int>();
|
||||
var parallelQuery = OldMessages.AsParallel()
|
||||
.WithCancellation(cts.Token);
|
||||
Task retryAfterTask = null;
|
||||
try
|
||||
{
|
||||
parallelQuery.ForAll(i =>
|
||||
{
|
||||
try
|
||||
{
|
||||
Device.DeleteMessage(i).GetAwaiter().GetResult();
|
||||
deletedMessages.Add(i);
|
||||
}
|
||||
catch (ApiRequestException req) when (req.ErrorCode == 400)
|
||||
{
|
||||
deletedMessages.Add(i);
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (AggregateException ex)
|
||||
{
|
||||
cts.Cancel();
|
||||
|
||||
var retryAfterSeconds = ex.InnerExceptions
|
||||
.Where(e => e is ApiRequestException apiEx && apiEx.ErrorCode == 429)
|
||||
.Max(e => (int?)((ApiRequestException)e).Parameters.RetryAfter) ?? 0;
|
||||
retryAfterTask = Task.Delay(retryAfterSeconds * 1000);
|
||||
}
|
||||
|
||||
//deletedMessages.AsParallel().ForAll(i => Device.OnMessageDeleted(new MessageDeletedEventArgs(i)));
|
||||
|
||||
oldMessages = oldMessages.Where(x => !deletedMessages.Contains(x));
|
||||
if (retryAfterTask != null)
|
||||
await retryAfterTask;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
OldMessages.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,29 +1,69 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Telegram.Bot.Types.ReplyMarkups;
|
||||
|
||||
namespace TelegramBotBase.Form
|
||||
{
|
||||
[DebuggerDisplay("{Text}, {Value}")]
|
||||
/// <summary>
|
||||
/// Base class for button handling
|
||||
/// </summary>
|
||||
public class ButtonBase
|
||||
{
|
||||
public String Text { get; set; }
|
||||
public virtual String Text { get; set; }
|
||||
|
||||
public String Value { get; set; }
|
||||
|
||||
public String Url { get; set; }
|
||||
|
||||
public ButtonBase()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public ButtonBase(String Text, String Value)
|
||||
public ButtonBase(String Text, String Value, String Url = null)
|
||||
{
|
||||
this.Text = Text;
|
||||
this.Value = Value;
|
||||
this.Url = Url;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns an inline Button
|
||||
/// </summary>
|
||||
/// <param name="form"></param>
|
||||
/// <returns></returns>
|
||||
public virtual InlineKeyboardButton ToInlineButton(ButtonForm form)
|
||||
{
|
||||
String id = (form.DependencyControl != null ? form.DependencyControl.ControlID + "_" : "");
|
||||
if (this.Url == null)
|
||||
{
|
||||
return InlineKeyboardButton.WithCallbackData(this.Text, id + this.Value);
|
||||
}
|
||||
|
||||
var ikb = new InlineKeyboardButton();
|
||||
|
||||
ikb.Text = this.Text;
|
||||
ikb.Url = this.Url;
|
||||
|
||||
return ikb;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns a KeyBoardButton
|
||||
/// </summary>
|
||||
/// <param name="form"></param>
|
||||
/// <returns></returns>
|
||||
public virtual KeyboardButton ToKeyboardButton(ButtonForm form)
|
||||
{
|
||||
return new KeyboardButton(this.Text);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Telegram.Bot.Types.ReplyMarkups;
|
||||
using TelegramBotBase.Base;
|
||||
using TelegramBotBase.Controls.Hybrid;
|
||||
|
||||
namespace TelegramBotBase.Form
|
||||
{
|
||||
@@ -12,14 +14,65 @@ namespace TelegramBotBase.Form
|
||||
/// </summary>
|
||||
public class ButtonForm
|
||||
{
|
||||
List<List<ButtonBase>> Buttons = new List<List<ButtonBase>>();
|
||||
List<ButtonRow> Buttons = new List<ButtonRow>();
|
||||
|
||||
|
||||
public IReplyMarkup Markup { get; set; }
|
||||
|
||||
public ControlBase DependencyControl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Contains the number of rows.
|
||||
/// </summary>
|
||||
public int Rows
|
||||
{
|
||||
get
|
||||
{
|
||||
return Buttons.Count;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Contains the highest number of columns in an row.
|
||||
/// </summary>
|
||||
public int Cols
|
||||
{
|
||||
get
|
||||
{
|
||||
return Buttons.Select(a => a.Count).OrderByDescending(a => a).FirstOrDefault();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public ButtonRow this[int row]
|
||||
{
|
||||
get
|
||||
{
|
||||
return Buttons[row];
|
||||
}
|
||||
}
|
||||
|
||||
public ButtonForm()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void AddButtonRow(List<ButtonBase> row)
|
||||
public ButtonForm(ControlBase control)
|
||||
{
|
||||
this.DependencyControl = control;
|
||||
}
|
||||
|
||||
public void AddButtonRow(String Text, String Value, String Url = null)
|
||||
{
|
||||
Buttons.Add(new List<ButtonBase>() { new ButtonBase(Text, Value, Url) });
|
||||
}
|
||||
|
||||
//public void AddButtonRow(ButtonRow row)
|
||||
//{
|
||||
// Buttons.Add(row.ToList());
|
||||
//}
|
||||
|
||||
public void AddButtonRow(ButtonRow row)
|
||||
{
|
||||
Buttons.Add(row);
|
||||
}
|
||||
@@ -29,6 +82,26 @@ namespace TelegramBotBase.Form
|
||||
AddButtonRow(row.ToList());
|
||||
}
|
||||
|
||||
public void AddButtonRows(IEnumerable<ButtonRow> rows)
|
||||
{
|
||||
Buttons.AddRange(rows);
|
||||
}
|
||||
|
||||
public void InsertButtonRow(int index, IEnumerable<ButtonBase> row)
|
||||
{
|
||||
Buttons.Insert(index, row.ToList());
|
||||
}
|
||||
|
||||
public void InsertButtonRow(int index, ButtonRow row)
|
||||
{
|
||||
Buttons.Insert(index, row);
|
||||
}
|
||||
|
||||
//public void InsertButtonRow(int index, params ButtonBase[] row)
|
||||
//{
|
||||
// InsertButtonRow(index, row.ToList());
|
||||
//}
|
||||
|
||||
public static T[][] SplitTo<T>(IEnumerable<T> items, int itemsPerRow = 2)
|
||||
{
|
||||
T[][] splitted = default(T[][]);
|
||||
@@ -49,6 +122,17 @@ namespace TelegramBotBase.Form
|
||||
return splitted;
|
||||
}
|
||||
|
||||
public int Count
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this.Buttons.Count == 0)
|
||||
return 0;
|
||||
|
||||
return this.Buttons.Select(a => a.ToArray()).ToList().Aggregate((a, b) => a.Union(b).ToArray()).Length;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add buttons splitted in the amount of columns (i.e. 2 per row...)
|
||||
/// </summary>
|
||||
@@ -58,24 +142,202 @@ namespace TelegramBotBase.Form
|
||||
{
|
||||
var sp = SplitTo<ButtonBase>(buttons, buttonsPerRow);
|
||||
|
||||
foreach(var bl in sp)
|
||||
foreach (var bl in sp)
|
||||
{
|
||||
AddButtonRow(bl);
|
||||
}
|
||||
}
|
||||
|
||||
public InlineKeyboardButton[][] ToArray()
|
||||
/// <summary>
|
||||
/// Returns a range of rows from the buttons.
|
||||
/// </summary>
|
||||
/// <param name="start"></param>
|
||||
/// <param name="count"></param>
|
||||
/// <returns></returns>
|
||||
public List<ButtonRow> GetRange(int start, int count)
|
||||
{
|
||||
var ikb = this.Buttons.Select(a => a.Select(b => InlineKeyboardButton.WithCallbackData(b.Text, b.Value)).ToArray()).ToArray();
|
||||
return Buttons.Skip(start).Take(count).ToList();
|
||||
}
|
||||
|
||||
|
||||
public List<ButtonBase> ToList()
|
||||
{
|
||||
return this.Buttons.DefaultIfEmpty(new List<ButtonBase>()).Select(a => a.ToList()).Aggregate((a, b) => a.Union(b).ToList());
|
||||
}
|
||||
|
||||
public InlineKeyboardButton[][] ToInlineButtonArray()
|
||||
{
|
||||
var ikb = this.Buttons.Select(a => a.ToArray().Select(b => b.ToInlineButton(this)).ToArray()).ToArray();
|
||||
|
||||
return ikb;
|
||||
}
|
||||
|
||||
public KeyboardButton[][] ToReplyButtonArray()
|
||||
{
|
||||
var ikb = this.Buttons.Select(a => a.ToArray().Select(b => b.ToKeyboardButton(this)).ToArray()).ToArray();
|
||||
|
||||
return ikb;
|
||||
}
|
||||
|
||||
public List<ButtonRow> ToArray()
|
||||
{
|
||||
return Buttons;
|
||||
}
|
||||
|
||||
public int FindRowByButton(ButtonBase button)
|
||||
{
|
||||
var row = this.Buttons.FirstOrDefault(a => a.ToArray().Count(b => b == button) > 0);
|
||||
if (row == null)
|
||||
return -1;
|
||||
|
||||
return this.Buttons.IndexOf(row);
|
||||
}
|
||||
|
||||
public Tuple<ButtonRow, int> FindRow(String text, bool useText = true)
|
||||
{
|
||||
var r = this.Buttons.FirstOrDefault(a => a.Matches(text, useText));
|
||||
if (r == null)
|
||||
return null;
|
||||
|
||||
var i = this.Buttons.IndexOf(r);
|
||||
return new Tuple<ButtonRow, int>(r, i);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns the first Button with the given value.
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <returns></returns>
|
||||
public ButtonBase GetButtonByValue(String value)
|
||||
{
|
||||
return this.ToList().Where(a => a.Value == value).FirstOrDefault();
|
||||
}
|
||||
|
||||
public static implicit operator InlineKeyboardMarkup(ButtonForm form)
|
||||
{
|
||||
InlineKeyboardMarkup ikm = new InlineKeyboardMarkup(form.ToArray());
|
||||
if (form == null)
|
||||
return null;
|
||||
|
||||
InlineKeyboardMarkup ikm = new InlineKeyboardMarkup(form.ToInlineButtonArray());
|
||||
|
||||
return ikm;
|
||||
}
|
||||
|
||||
public static implicit operator ReplyKeyboardMarkup(ButtonForm form)
|
||||
{
|
||||
if (form == null)
|
||||
return null;
|
||||
|
||||
ReplyKeyboardMarkup ikm = new ReplyKeyboardMarkup(form.ToReplyButtonArray());
|
||||
|
||||
return ikm;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a copy of this form.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public ButtonForm Duplicate()
|
||||
{
|
||||
var bf = new ButtonForm()
|
||||
{
|
||||
Markup = this.Markup,
|
||||
DependencyControl = this.DependencyControl
|
||||
};
|
||||
|
||||
foreach (var b in Buttons)
|
||||
{
|
||||
var lst = new ButtonRow();
|
||||
foreach (var b2 in b)
|
||||
{
|
||||
lst.Add(b2);
|
||||
}
|
||||
bf.Buttons.Add(lst);
|
||||
}
|
||||
|
||||
return bf;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a copy of this form and filters by the parameter.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public ButtonForm FilterDuplicate(String filter, bool ByRow = false)
|
||||
{
|
||||
var bf = new ButtonForm()
|
||||
{
|
||||
Markup = this.Markup,
|
||||
DependencyControl = this.DependencyControl
|
||||
};
|
||||
|
||||
foreach (var b in Buttons)
|
||||
{
|
||||
var lst = new ButtonRow();
|
||||
foreach (var b2 in b)
|
||||
{
|
||||
if (b2.Text.IndexOf(filter, StringComparison.InvariantCultureIgnoreCase) == -1)
|
||||
continue;
|
||||
|
||||
//Copy full row, when at least one match has found.
|
||||
if (ByRow)
|
||||
{
|
||||
lst = b;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
lst.Add(b2);
|
||||
}
|
||||
}
|
||||
|
||||
if (lst.Count > 0)
|
||||
bf.Buttons.Add(lst);
|
||||
}
|
||||
|
||||
return bf;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a copy of this form and filters by the parameter.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public ButtonForm TagDuplicate(List<String> tags, bool ByRow = false)
|
||||
{
|
||||
var bf = new ButtonForm()
|
||||
{
|
||||
Markup = this.Markup,
|
||||
DependencyControl = this.DependencyControl
|
||||
};
|
||||
|
||||
foreach (var b in Buttons)
|
||||
{
|
||||
var lst = new ButtonRow();
|
||||
foreach (var b2 in b)
|
||||
{
|
||||
if (!(b2 is TagButtonBase tb))
|
||||
continue;
|
||||
|
||||
if (!tags.Contains(tb.Tag))
|
||||
continue;
|
||||
|
||||
//Copy full row, when at least one match has found.
|
||||
if (ByRow)
|
||||
{
|
||||
lst = b;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
lst.Add(b2);
|
||||
}
|
||||
}
|
||||
|
||||
if (lst.Count > 0)
|
||||
bf.Buttons.Add(lst);
|
||||
}
|
||||
|
||||
return bf;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using TelegramBotBase.Args;
|
||||
using TelegramBotBase.Attributes;
|
||||
using TelegramBotBase.Base;
|
||||
|
||||
namespace TelegramBotBase.Form
|
||||
{
|
||||
[IgnoreState]
|
||||
public class ConfirmDialog : ModalDialog
|
||||
{
|
||||
/// <summary>
|
||||
/// The message the users sees.
|
||||
/// </summary>
|
||||
public String Message { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// An additional optional value.
|
||||
/// </summary>
|
||||
public object Tag { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Automatically close form on button click
|
||||
/// </summary>
|
||||
public bool AutoCloseOnClick { get; set; } = true;
|
||||
|
||||
public List<ButtonBase> Buttons { get; set; }
|
||||
|
||||
private EventHandlerList __Events { get; set; } = new EventHandlerList();
|
||||
|
||||
private static object __evButtonClicked { get; } = new object();
|
||||
|
||||
public ConfirmDialog()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public ConfirmDialog(String Message)
|
||||
{
|
||||
this.Message = Message;
|
||||
this.Buttons = new List<Form.ButtonBase>();
|
||||
}
|
||||
|
||||
public ConfirmDialog(String Message, params ButtonBase[] Buttons)
|
||||
{
|
||||
this.Message = Message;
|
||||
this.Buttons = Buttons.ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds one Button
|
||||
/// </summary>
|
||||
/// <param name="button"></param>
|
||||
public void AddButton(ButtonBase button)
|
||||
{
|
||||
this.Buttons.Add(button);
|
||||
}
|
||||
|
||||
public override async Task Action(MessageResult message)
|
||||
{
|
||||
if (message.Handled)
|
||||
return;
|
||||
|
||||
if (!message.IsFirstHandler)
|
||||
return;
|
||||
|
||||
var call = message.GetData<CallbackData>();
|
||||
if (call == null)
|
||||
return;
|
||||
|
||||
message.Handled = true;
|
||||
|
||||
await message.ConfirmAction();
|
||||
|
||||
await message.DeleteMessage();
|
||||
|
||||
ButtonBase button = this.Buttons.FirstOrDefault(a => a.Value == call.Value);
|
||||
|
||||
if (button == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
OnButtonClicked(new ButtonClickedEventArgs(button) { Tag = this.Tag });
|
||||
|
||||
if (AutoCloseOnClick)
|
||||
await CloseForm();
|
||||
}
|
||||
|
||||
|
||||
public override async Task Render(MessageResult message)
|
||||
{
|
||||
ButtonForm btn = new ButtonForm();
|
||||
|
||||
var buttons = this.Buttons.Select(a => new ButtonBase(a.Text, CallbackData.Create("action", a.Value))).ToList();
|
||||
btn.AddButtonRow(buttons);
|
||||
|
||||
await this.Device.Send(this.Message, btn);
|
||||
}
|
||||
|
||||
|
||||
public event EventHandler<ButtonClickedEventArgs> ButtonClicked
|
||||
{
|
||||
add
|
||||
{
|
||||
this.__Events.AddHandler(__evButtonClicked, value);
|
||||
}
|
||||
remove
|
||||
{
|
||||
this.__Events.RemoveHandler(__evButtonClicked, value);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnButtonClicked(ButtonClickedEventArgs e)
|
||||
{
|
||||
(this.__Events[__evButtonClicked] as EventHandler<ButtonClickedEventArgs>)?.Invoke(this, e);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace TelegramBotBase.Form
|
||||
{
|
||||
public class DynamicButton : ButtonBase
|
||||
{
|
||||
public override string Text
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetText?.Invoke() ?? m_text;
|
||||
}
|
||||
set
|
||||
{
|
||||
m_text = value;
|
||||
}
|
||||
}
|
||||
|
||||
private String m_text = "";
|
||||
|
||||
private Func<String> GetText;
|
||||
|
||||
public DynamicButton(String Text, String Value, String Url = null)
|
||||
{
|
||||
this.Text = Text;
|
||||
this.Value = Value;
|
||||
this.Url = Url;
|
||||
}
|
||||
|
||||
public DynamicButton(Func<String> GetText, String Value, String Url = null)
|
||||
{
|
||||
this.GetText = GetText;
|
||||
this.Value = Value;
|
||||
this.Url = Url;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using TelegramBotBase.Args;
|
||||
using TelegramBotBase.Base;
|
||||
|
||||
namespace TelegramBotBase.Form
|
||||
{
|
||||
public class GroupForm : FormBase
|
||||
{
|
||||
|
||||
|
||||
public GroupForm()
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
|
||||
public override async Task Load(MessageResult message)
|
||||
{
|
||||
switch (message.MessageType)
|
||||
{
|
||||
case Telegram.Bot.Types.Enums.MessageType.ChatMembersAdded:
|
||||
|
||||
await OnMemberChanges(new MemberChangeEventArgs(Telegram.Bot.Types.Enums.MessageType.ChatMembersAdded, message, message.RawMessageData.Message.NewChatMembers));
|
||||
|
||||
break;
|
||||
case Telegram.Bot.Types.Enums.MessageType.ChatMemberLeft:
|
||||
|
||||
await OnMemberChanges(new MemberChangeEventArgs(Telegram.Bot.Types.Enums.MessageType.ChatMemberLeft, message, message.RawMessageData.Message.LeftChatMember));
|
||||
|
||||
break;
|
||||
|
||||
case Telegram.Bot.Types.Enums.MessageType.ChatPhotoChanged:
|
||||
case Telegram.Bot.Types.Enums.MessageType.ChatPhotoDeleted:
|
||||
case Telegram.Bot.Types.Enums.MessageType.ChatTitleChanged:
|
||||
case Telegram.Bot.Types.Enums.MessageType.MigratedFromGroup:
|
||||
case Telegram.Bot.Types.Enums.MessageType.MigratedToSupergroup:
|
||||
case Telegram.Bot.Types.Enums.MessageType.MessagePinned:
|
||||
case Telegram.Bot.Types.Enums.MessageType.GroupCreated:
|
||||
case Telegram.Bot.Types.Enums.MessageType.SupergroupCreated:
|
||||
case Telegram.Bot.Types.Enums.MessageType.ChannelCreated:
|
||||
|
||||
await OnGroupChanged(new GroupChangedEventArgs(message.MessageType, message));
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
await OnMessage(message);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public override async Task Edited(MessageResult message)
|
||||
{
|
||||
await OnMessageEdit(message);
|
||||
}
|
||||
|
||||
public virtual async Task OnMemberChanges(MemberChangeEventArgs e)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
public virtual async Task OnGroupChanged(GroupChangedEventArgs e)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
public virtual async Task OnMessage(MessageResult e)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public virtual async Task OnMessageEdit(MessageResult e)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace TelegramBotBase.Form
|
||||
{
|
||||
public class ModalDialog : FormBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains the parent from where the modal dialog has been opened.
|
||||
/// </summary>
|
||||
public FormBase ParentForm { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This is a modal only function and does everything to close this form.
|
||||
/// </summary>
|
||||
public async Task CloseForm()
|
||||
{
|
||||
await this.CloseControls();
|
||||
|
||||
await this.OnClosed(new EventArgs());
|
||||
|
||||
|
||||
await this.ParentForm?.ReturnFromModal(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,358 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using TelegramBotBase.Args;
|
||||
using TelegramBotBase.Attributes;
|
||||
using TelegramBotBase.Base;
|
||||
using TelegramBotBase.Interfaces;
|
||||
|
||||
namespace TelegramBotBase.Form.Navigation
|
||||
{
|
||||
[DebuggerDisplay("{Index+1} Forms")]
|
||||
public class NavigationController : FormBase, IStateForm
|
||||
{
|
||||
|
||||
[SaveState]
|
||||
private List<FormBase> History { get; set; }
|
||||
|
||||
[SaveState]
|
||||
public int Index { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Will replace the controller when poping a form to the root form.
|
||||
/// </summary>
|
||||
[SaveState]
|
||||
public bool ForceCleanupOnLastPop { get; set; }
|
||||
|
||||
public NavigationController()
|
||||
{
|
||||
History = new List<FormBase>();
|
||||
Index = -1;
|
||||
ForceCleanupOnLastPop = true;
|
||||
|
||||
this.Init += NavigationController_Init;
|
||||
this.Opened += NavigationController_Opened;
|
||||
this.Closed += NavigationController_Closed;
|
||||
}
|
||||
|
||||
public NavigationController(FormBase startForm, params FormBase[] forms) : this()
|
||||
{
|
||||
this.Client = startForm.Client;
|
||||
this.Device = startForm.Device;
|
||||
startForm.NavigationController = this;
|
||||
|
||||
History.Add(startForm);
|
||||
Index = 0;
|
||||
|
||||
if (forms.Length > 0)
|
||||
{
|
||||
History.AddRange(forms);
|
||||
Index = History.Count - 1;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task NavigationController_Init(object sender, Args.InitEventArgs e)
|
||||
{
|
||||
if (CurrentForm == null)
|
||||
return;
|
||||
|
||||
await CurrentForm.OnInit(e);
|
||||
}
|
||||
|
||||
private async Task NavigationController_Opened(object sender, EventArgs e)
|
||||
{
|
||||
if (CurrentForm == null)
|
||||
return;
|
||||
|
||||
await CurrentForm.OnOpened(e);
|
||||
}
|
||||
|
||||
private async Task NavigationController_Closed(object sender, EventArgs e)
|
||||
{
|
||||
if (CurrentForm == null)
|
||||
return;
|
||||
|
||||
await CurrentForm.OnClosed(e);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Remove the current active form on the stack.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public virtual async Task PopAsync()
|
||||
{
|
||||
if (History.Count == 0)
|
||||
return;
|
||||
|
||||
var form = History[Index];
|
||||
|
||||
form.NavigationController = null;
|
||||
History.Remove(form);
|
||||
Index--;
|
||||
|
||||
Device.FormSwitched = true;
|
||||
|
||||
await form.OnClosed(new EventArgs());
|
||||
|
||||
//Leave NavigationController and move to the last one
|
||||
if (ForceCleanupOnLastPop && History.Count == 1)
|
||||
{
|
||||
var last_form = History[0];
|
||||
last_form.NavigationController = null;
|
||||
await this.NavigateTo(last_form);
|
||||
return;
|
||||
}
|
||||
|
||||
if (History.Count > 0)
|
||||
{
|
||||
form = History[Index];
|
||||
await form.OnOpened(new EventArgs());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pop's through all forms back to the root form.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public virtual async Task PopToRootAsync()
|
||||
{
|
||||
while (Index > 0)
|
||||
{
|
||||
await PopAsync();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pushing the given form to the stack and renders it.
|
||||
/// </summary>
|
||||
/// <param name="form"></param>
|
||||
/// <returns></returns>
|
||||
public virtual async Task PushAsync(FormBase form, params object[] args)
|
||||
{
|
||||
form.Client = this.Client;
|
||||
form.Device = this.Device;
|
||||
form.NavigationController = this;
|
||||
|
||||
this.History.Add(form);
|
||||
Index++;
|
||||
|
||||
Device.FormSwitched = true;
|
||||
|
||||
if (Index < 2)
|
||||
return;
|
||||
|
||||
await form.OnInit(new InitEventArgs(args));
|
||||
|
||||
await form.OnOpened(new EventArgs());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pops the current form and pushes a new one.
|
||||
/// Will help to remove forms so you can not navigate back to them.
|
||||
/// </summary>
|
||||
/// <param name="form"></param>
|
||||
/// <param name="args"></param>
|
||||
/// <returns></returns>
|
||||
public virtual async Task PushAndReplaceAsync(FormBase form, params object[] args)
|
||||
{
|
||||
await PopAsync();
|
||||
|
||||
await PushAsync(form, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the current form from the stack.
|
||||
/// </summary>
|
||||
public FormBase CurrentForm
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this.History.Count == 0)
|
||||
return null;
|
||||
|
||||
return this.History[Index];
|
||||
}
|
||||
}
|
||||
|
||||
public List<FormBase> GetAllForms()
|
||||
{
|
||||
return History.ToList();
|
||||
}
|
||||
|
||||
|
||||
public void LoadState(LoadStateEventArgs e)
|
||||
{
|
||||
if (e.Get("$controller.history.count") == null)
|
||||
return;
|
||||
|
||||
|
||||
int historyCount = e.GetInt("$controller.history.count");
|
||||
|
||||
for (int i = 0; i < historyCount; i++)
|
||||
{
|
||||
|
||||
var c = e.GetObject($"$controller.history[{i}]") as Dictionary<String, object>;
|
||||
|
||||
|
||||
|
||||
var qname = e.Get($"$controller.history[{i}].type");
|
||||
|
||||
if (qname == null)
|
||||
continue;
|
||||
|
||||
Type t = Type.GetType(qname.ToString());
|
||||
if (t == null || !t.IsSubclassOf(typeof(FormBase)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var form = t.GetConstructor(new Type[] { })?.Invoke(new object[] { }) as FormBase;
|
||||
|
||||
//No default constructor, fallback
|
||||
if (form == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var properties = c.Where(a => a.Key.StartsWith("$"));
|
||||
|
||||
var fields = form.GetType().GetProperties(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic).Where(a => a.GetCustomAttributes(typeof(SaveState), true).Length != 0).ToList();
|
||||
|
||||
foreach (var p in properties)
|
||||
{
|
||||
var f = fields.FirstOrDefault(a => a.Name == p.Key.Substring(1));
|
||||
if (f == null)
|
||||
continue;
|
||||
|
||||
try
|
||||
{
|
||||
if (f.PropertyType.IsEnum)
|
||||
{
|
||||
var ent = Enum.Parse(f.PropertyType, p.Value.ToString());
|
||||
|
||||
f.SetValue(form, ent);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
f.SetValue(form, p.Value);
|
||||
}
|
||||
catch (ArgumentException ex)
|
||||
{
|
||||
|
||||
Tools.Conversion.CustomConversionChecks(form, p, f);
|
||||
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
form.Device = this.Device;
|
||||
form.Client = this.Client;
|
||||
form.NavigationController = this;
|
||||
|
||||
form.OnInit(new InitEventArgs());
|
||||
|
||||
this.History.Add(form);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void SaveState(SaveStateEventArgs e)
|
||||
{
|
||||
e.Set("$controller.history.count", History.Count.ToString());
|
||||
|
||||
int i = 0;
|
||||
foreach (var form in History)
|
||||
{
|
||||
var fields = form.GetType().GetProperties(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic).Where(a => a.GetCustomAttributes(typeof(SaveState), true).Length != 0).ToList();
|
||||
|
||||
var dt = new Dictionary<String, object>();
|
||||
foreach (var f in fields)
|
||||
{
|
||||
var val = f.GetValue(form);
|
||||
|
||||
dt.Add("$" + f.Name, val);
|
||||
}
|
||||
|
||||
e.Set($"$controller.history[{i}].type", form.GetType().AssemblyQualifiedName);
|
||||
|
||||
e.SetObject($"$controller.history[{i}]", dt);
|
||||
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#region "Methods passthrough"
|
||||
|
||||
public override async Task NavigateTo(FormBase newForm, params object[] args)
|
||||
{
|
||||
await CurrentForm.NavigateTo(newForm, args);
|
||||
}
|
||||
|
||||
public override async Task LoadControls(MessageResult message)
|
||||
{
|
||||
await CurrentForm.LoadControls(message);
|
||||
}
|
||||
|
||||
public override async Task Load(MessageResult message)
|
||||
{
|
||||
await CurrentForm.Load(message);
|
||||
}
|
||||
|
||||
public override async Task ActionControls(MessageResult message)
|
||||
{
|
||||
await CurrentForm.ActionControls(message);
|
||||
}
|
||||
|
||||
public override async Task Action(MessageResult message)
|
||||
{
|
||||
await CurrentForm.Action(message);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public override async Task Edited(MessageResult message)
|
||||
{
|
||||
await CurrentForm.Edited(message);
|
||||
}
|
||||
|
||||
public override async Task Render(MessageResult message)
|
||||
{
|
||||
await CurrentForm.Render(message);
|
||||
}
|
||||
|
||||
public override async Task RenderControls(MessageResult message)
|
||||
{
|
||||
await CurrentForm.RenderControls(message);
|
||||
}
|
||||
|
||||
public override async Task PreLoad(MessageResult message)
|
||||
{
|
||||
await CurrentForm.PreLoad(message);
|
||||
}
|
||||
|
||||
public override async Task ReturnFromModal(ModalDialog modal)
|
||||
{
|
||||
await CurrentForm.ReturnFromModal(modal);
|
||||
}
|
||||
|
||||
public override async Task SentData(DataResult message)
|
||||
{
|
||||
await CurrentForm.SentData(message);
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
@@ -4,22 +4,44 @@ using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Telegram.Bot.Types;
|
||||
using Telegram.Bot.Types.ReplyMarkups;
|
||||
using TelegramBotBase.Args;
|
||||
using TelegramBotBase.Attributes;
|
||||
using TelegramBotBase.Base;
|
||||
|
||||
namespace TelegramBotBase.Form
|
||||
{
|
||||
public class PromptDialog : FormBase
|
||||
[IgnoreState]
|
||||
public class PromptDialog : ModalDialog
|
||||
{
|
||||
/// <summary>
|
||||
/// The message the users sees.
|
||||
/// </summary>
|
||||
public String Message { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The returned text value by the user.
|
||||
/// </summary>
|
||||
public String Value { get; set; }
|
||||
|
||||
public List<ButtonBase> Buttons { get; set; }
|
||||
|
||||
public Dictionary<String, FormBase> ButtonForms { get; set; } = new Dictionary<string, FormBase>();
|
||||
/// <summary>
|
||||
/// An additional optional value.
|
||||
/// </summary>
|
||||
public object Tag { get; set; }
|
||||
|
||||
private EventHandlerList __Events { get; set; } = new EventHandlerList();
|
||||
|
||||
private static object __evButtonClicked { get; } = new object();
|
||||
private static object __evCompleted { get; } = new object();
|
||||
|
||||
public bool ShowBackButton { get; set; } = false;
|
||||
|
||||
public String BackLabel { get; set; } = Localizations.Default.Language["PromptDialog_Back"];
|
||||
|
||||
/// <summary>
|
||||
/// Contains the RAW received message.
|
||||
/// </summary>
|
||||
public Message ReceivedMessage { get; set; }
|
||||
|
||||
public PromptDialog()
|
||||
{
|
||||
@@ -29,80 +51,74 @@ namespace TelegramBotBase.Form
|
||||
public PromptDialog(String Message)
|
||||
{
|
||||
this.Message = Message;
|
||||
this.Buttons = new List<Form.ButtonBase>();
|
||||
}
|
||||
|
||||
public PromptDialog(String Message, params ButtonBase[] Buttons)
|
||||
public async override Task Load(MessageResult message)
|
||||
{
|
||||
this.Message = Message;
|
||||
this.Buttons = Buttons.ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds one Button
|
||||
/// </summary>
|
||||
/// <param name="button"></param>
|
||||
public void AddButton(ButtonBase button)
|
||||
{
|
||||
this.Buttons.Add(button);
|
||||
}
|
||||
|
||||
public override async Task Action(MessageResult message)
|
||||
{
|
||||
var call = message.GetData<CallbackData>();
|
||||
if (call == null)
|
||||
if (message.Handled)
|
||||
return;
|
||||
|
||||
message.Handled = true;
|
||||
if (!message.IsFirstHandler)
|
||||
return;
|
||||
|
||||
await message.ConfirmAction();
|
||||
|
||||
await message.DeleteMessage();
|
||||
|
||||
ButtonBase button = this.Buttons.FirstOrDefault(a => a.Value == call.Value);
|
||||
|
||||
if (button == null)
|
||||
if (this.ShowBackButton && message.MessageText == BackLabel)
|
||||
{
|
||||
await this.CloseForm();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
OnButtonClicked(new ButtonClickedEventArgs(button));
|
||||
|
||||
FormBase fb = ButtonForms.ContainsKey(call.Value) ? ButtonForms[call.Value] : null;
|
||||
|
||||
if (fb != null)
|
||||
if (this.Value == null)
|
||||
{
|
||||
await this.NavigateTo(fb);
|
||||
}
|
||||
}
|
||||
this.Value = message.MessageText;
|
||||
|
||||
ReceivedMessage = message.Message;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
public override async Task Render(MessageResult message)
|
||||
{
|
||||
ButtonForm btn = new ButtonForm();
|
||||
|
||||
var buttons = this.Buttons.Select(a => new ButtonBase(a.Text, CallbackData.Create("action", a.Value))).ToList();
|
||||
btn.AddButtonRow(buttons);
|
||||
if (this.Value == null)
|
||||
{
|
||||
if (this.ShowBackButton)
|
||||
{
|
||||
ButtonForm bf = new ButtonForm();
|
||||
bf.AddButtonRow(new ButtonBase(BackLabel, "back"));
|
||||
await this.Device.Send(this.Message, (ReplyMarkupBase)bf);
|
||||
return;
|
||||
}
|
||||
|
||||
await this.Device.Send(this.Message, btn);
|
||||
await this.Device.Send(this.Message);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
message.Handled = true;
|
||||
|
||||
OnCompleted(new PromptDialogCompletedEventArgs() { Tag = this.Tag, Value = this.Value });
|
||||
|
||||
await this.CloseForm();
|
||||
}
|
||||
|
||||
|
||||
public event EventHandler<ButtonClickedEventArgs> ButtonClicked
|
||||
public event EventHandler<PromptDialogCompletedEventArgs> Completed
|
||||
{
|
||||
add
|
||||
{
|
||||
this.__Events.AddHandler(__evButtonClicked, value);
|
||||
this.__Events.AddHandler(__evCompleted, value);
|
||||
}
|
||||
remove
|
||||
{
|
||||
this.__Events.RemoveHandler(__evButtonClicked, value);
|
||||
this.__Events.RemoveHandler(__evCompleted, value);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnButtonClicked(ButtonClickedEventArgs e)
|
||||
public void OnCompleted(PromptDialogCompletedEventArgs e)
|
||||
{
|
||||
(this.__Events[__evButtonClicked] as EventHandler<ButtonClickedEventArgs>)?.Invoke(this, e);
|
||||
(this.__Events[__evCompleted] as EventHandler<PromptDialogCompletedEventArgs>)?.Invoke(this, e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using TelegramBotBase.Base;
|
||||
|
||||
|
||||
namespace TelegramBotBase.Form
|
||||
{
|
||||
/// <summary>
|
||||
/// This is used to split incomming requests depending on the chat type.
|
||||
/// </summary>
|
||||
public class SplitterForm : FormBase
|
||||
{
|
||||
|
||||
private static object __evOpenSupergroup = new object();
|
||||
private static object __evOpenGroup = new object();
|
||||
private static object __evOpenChannel = new object();
|
||||
private static object __evOpen = new object();
|
||||
|
||||
|
||||
public override async Task Load(MessageResult message)
|
||||
{
|
||||
|
||||
if (message.Message.Chat.Type == Telegram.Bot.Types.Enums.ChatType.Channel)
|
||||
{
|
||||
if (await OpenChannel(message))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (message.Message.Chat.Type == Telegram.Bot.Types.Enums.ChatType.Supergroup)
|
||||
{
|
||||
if (await OpenSupergroup(message))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (await OpenGroup(message))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (message.Message.Chat.Type == Telegram.Bot.Types.Enums.ChatType.Group)
|
||||
{
|
||||
if (await OpenGroup(message))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
await Open(message);
|
||||
}
|
||||
|
||||
|
||||
public virtual async Task<bool> OpenSupergroup(MessageResult e)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public virtual async Task<bool> OpenChannel(MessageResult e)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public virtual async Task<bool> Open(MessageResult e)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public virtual async Task<bool> OpenGroup(MessageResult e)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public override Task Action(MessageResult message)
|
||||
{
|
||||
return base.Action(message);
|
||||
}
|
||||
|
||||
public override Task PreLoad(MessageResult message)
|
||||
{
|
||||
return base.PreLoad(message);
|
||||
}
|
||||
|
||||
public override Task Render(MessageResult message)
|
||||
{
|
||||
return base.Render(message);
|
||||
}
|
||||
|
||||
public override Task SentData(DataResult message)
|
||||
{
|
||||
return base.SentData(message);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Telegram.Bot.Types.ReplyMarkups;
|
||||
|
||||
namespace TelegramBotBase.Form
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class for button handling
|
||||
/// </summary>
|
||||
public class TagButtonBase : ButtonBase
|
||||
{
|
||||
public String Tag { get; set; }
|
||||
|
||||
public TagButtonBase()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public TagButtonBase(String Text, String Value, String Tag)
|
||||
{
|
||||
this.Text = Text;
|
||||
this.Value = Value;
|
||||
this.Tag = Tag;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns an inline Button
|
||||
/// </summary>
|
||||
/// <param name="form"></param>
|
||||
/// <returns></returns>
|
||||
public override InlineKeyboardButton ToInlineButton(ButtonForm form)
|
||||
{
|
||||
String id = (form.DependencyControl != null ? form.DependencyControl.ControlID + "_" : "");
|
||||
|
||||
return InlineKeyboardButton.WithCallbackData(this.Text, id + this.Value);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns a KeyBoardButton
|
||||
/// </summary>
|
||||
/// <param name="form"></param>
|
||||
/// <returns></returns>
|
||||
public override KeyboardButton ToKeyboardButton(ButtonForm form)
|
||||
{
|
||||
return new KeyboardButton(this.Text);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using TelegramBotBase.Controls.Hybrid;
|
||||
|
||||
namespace TelegramBotBase.Interfaces
|
||||
{
|
||||
public interface IDataSource<T>
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Returns the amount of items within this source.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
int Count { get; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns the item at the specific index.
|
||||
/// </summary>
|
||||
/// <param name="index"></param>
|
||||
/// <returns></returns>
|
||||
T ItemAt(int index);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Get all items from this source within this range.
|
||||
/// </summary>
|
||||
/// <param name="start"></param>
|
||||
/// <param name="count"></param>
|
||||
/// <returns></returns>
|
||||
List<T> ItemRange(int start, int count);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of all items of this datasource.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
List<T> AllItems();
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using TelegramBotBase.Form;
|
||||
|
||||
namespace TelegramBotBase.Interfaces
|
||||
{
|
||||
interface IDeviceSession
|
||||
{
|
||||
/// <summary>
|
||||
/// Device or chat id
|
||||
/// </summary>
|
||||
long DeviceId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Username of user or group
|
||||
/// </summary>
|
||||
String ChatTitle { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// When did any last action happend (message received or button clicked)
|
||||
/// </summary>
|
||||
DateTime LastAction { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns the form where the user/group is at the moment.
|
||||
/// </summary>
|
||||
FormBase ActiveForm { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns the previous shown form
|
||||
/// </summary>
|
||||
FormBase PreviousForm { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// contains if the form has been switched (navigated)
|
||||
/// </summary>
|
||||
bool FormSwitched { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using TelegramBotBase.Args;
|
||||
using TelegramBotBase.Base;
|
||||
|
||||
namespace TelegramBotBase.Interfaces
|
||||
{
|
||||
/// <summary>
|
||||
/// Is used to save specific fields into a session state to survive restarts or unhandled exceptions and crashes.
|
||||
/// </summary>
|
||||
public interface IStateForm
|
||||
{
|
||||
|
||||
void LoadState(LoadStateEventArgs e);
|
||||
|
||||
void SaveState(SaveStateEventArgs e);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using TelegramBotBase.Args;
|
||||
using TelegramBotBase.Base;
|
||||
using TelegramBotBase.Form;
|
||||
|
||||
namespace TelegramBotBase.Interfaces
|
||||
{
|
||||
public interface IStateMachine
|
||||
{
|
||||
Type FallbackStateForm { get; }
|
||||
|
||||
void SaveFormStates(SaveStatesEventArgs e);
|
||||
|
||||
StateContainer LoadFormStates();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace TelegramBotBase.Localizations
|
||||
{
|
||||
public static class Default
|
||||
{
|
||||
|
||||
public static Localization Language = new English();
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace TelegramBotBase.Localizations
|
||||
{
|
||||
public class English : Localization
|
||||
{
|
||||
public English() : base()
|
||||
{
|
||||
Values["Language"] = "English";
|
||||
Values["ButtonGrid_Title"] = "Menu";
|
||||
Values["ButtonGrid_NoItems"] = "There are no items.";
|
||||
Values["ButtonGrid_PreviousPage"] = "◀️";
|
||||
Values["ButtonGrid_NextPage"] = "▶️";
|
||||
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["CalendarPicker_Title"] = "Pick date";
|
||||
Values["CalendarPicker_PreviousPage"] = "◀️";
|
||||
Values["CalendarPicker_NextPage"] = "▶️";
|
||||
Values["TreeView_Title"] = "Select node";
|
||||
Values["TreeView_LevelUp"] = "🔼 level up";
|
||||
Values["ToggleButton_On"] = "On";
|
||||
Values["ToggleButton_Off"] = "Off";
|
||||
Values["ToggleButton_OnIcon"] = "⚫";
|
||||
Values["ToggleButton_OffIcon"] = "⚪";
|
||||
Values["ToggleButton_Title"] = "Toggle";
|
||||
Values["ToggleButton_Changed"] = "Choosen";
|
||||
Values["MultiToggleButton_SelectedIcon"] = "✅";
|
||||
Values["MultiToggleButton_Title"] = "Multi-Toggle";
|
||||
Values["MultiToggleButton_Changed"] = "Choosen";
|
||||
Values["PromptDialog_Back"] = "Back";
|
||||
Values["ToggleButton_Changed"] = "Setting changed";
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace TelegramBotBase.Localizations
|
||||
{
|
||||
public class German : Localization
|
||||
{
|
||||
public German() : base()
|
||||
{
|
||||
Values["Language"] = "Deutsch (German)";
|
||||
Values["ButtonGrid_Title"] = "Menü";
|
||||
Values["ButtonGrid_NoItems"] = "Es sind keine Einträge vorhanden.";
|
||||
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_Back"] = "Zurück";
|
||||
Values["ButtonGrid_CheckAll"] = "Alle auswählen";
|
||||
Values["ButtonGrid_UncheckAll"] = "Keine auswählen";
|
||||
Values["CalendarPicker_Title"] = "Datum auswählen";
|
||||
Values["CalendarPicker_PreviousPage"] = "◀️";
|
||||
Values["CalendarPicker_NextPage"] = "▶️";
|
||||
Values["TreeView_Title"] = "Knoten auswählen";
|
||||
Values["TreeView_LevelUp"] = "🔼 Stufe hoch";
|
||||
Values["ToggleButton_On"] = "An";
|
||||
Values["ToggleButton_Off"] = "Aus";
|
||||
Values["ToggleButton_OnIcon"] = "⚫";
|
||||
Values["ToggleButton_OffIcon"] = "⚪";
|
||||
Values["ToggleButton_Title"] = "Schalter";
|
||||
Values["ToggleButton_Changed"] = "Ausgewählt";
|
||||
Values["MultiToggleButton_SelectedIcon"] = "✅";
|
||||
Values["MultiToggleButton_Title"] = "Mehrfach-Schalter";
|
||||
Values["MultiToggleButton_Changed"] = "Ausgewählt";
|
||||
Values["PromptDialog_Back"] = "Zurück";
|
||||
Values["ToggleButton_Changed"] = "Einstellung geändert";
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user