Merge pull request #4 from MajMcCloud/development

A lot of updates and fixes
This commit is contained in:
Florian Dahn 2021-05-16 15:09:09 +02:00 committed by GitHub
commit bd908db867
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 1075 additions and 115 deletions

View File

@ -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>

View File

@ -0,0 +1,69 @@
<?xml version="1.0" encoding="utf-8"?>
<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>{673A56F5-6110-4AED-A68D-562FD6ED3EA6}</ProjectGuid>
<OutputType>Exe</OutputType>
<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>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<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.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=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" />
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\TelegramBotBase\TelegramBotBase.csproj">
<Project>{0bd16fb9-7ed4-4ccb-83eb-5cee538e1b6c}</Project>
<Name>TelegramBotBase</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View File

@ -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);
}
}
}
}

View File

@ -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")]

View File

@ -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;
}
}
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}

View File

@ -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>

View File

@ -77,6 +77,8 @@ Thanks !
* [CheckedButtonList](#checked-button-list)
* [MultiToggleButton](#multi-toggle-button)
- [Groups](#groups)
* [SplitterForm](#splitter-form)
@ -814,6 +816,8 @@ await this.NavigateTo(cd);
### Checked Button List
<img src="images/checkedbuttonlist.gif?raw=true" />
### Multi Toggle Button
<img src="images/multitogglebutton.gif?raw=true" />
## Groups
@ -896,6 +900,7 @@ public class GroupForm : FormBase
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));
@ -1081,6 +1086,9 @@ Will allow you to run specific system commands or run/kill processes via Bot. Ha
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.
---

View File

@ -16,6 +16,8 @@ namespace TelegramBotBase.Args
public int Index { get; set; }
public object Tag { get; set; }
public ButtonClickedEventArgs()
{

View File

@ -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;
}
}
}

View File

@ -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; }
}
}

View File

@ -20,12 +20,18 @@ 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; }
{
get;
set;
}
/// <summary>
/// The message id
@ -34,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;
}
}
@ -58,7 +66,8 @@ namespace TelegramBotBase.Base
{
get
{
return this.RawMessageData?.Message?.Type ?? Telegram.Bot.Types.Enums.MessageType.Unknown;
return this.RawMessageData?.Message?.Type
?? Telegram.Bot.Types.Enums.MessageType.Unknown;
}
}
@ -175,6 +184,10 @@ namespace TelegramBotBase.Base
}
}
internal MessageResult()
{
}
public MessageResult(Telegram.Bot.Args.MessageEventArgs rawdata)
{

View File

@ -72,6 +72,7 @@ namespace TelegramBotBase
{
this.SystemSettings = new Dictionary<eSettings, uint>();
SetSetting(eSettings.MaxNumberOfRetries, 5);
SetSetting(eSettings.NavigationMaximum, 10);
SetSetting(eSettings.LogAllMessages, false);
SetSetting(eSettings.SkipAllMessages, false);
@ -177,6 +178,8 @@ namespace TelegramBotBase
});
}
DeviceSession.MaxNumberOfRetries = this.GetSetting(eSettings.MaxNumberOfRetries, 5);
this.Client.TelegramClient.StartReceiving();
}
@ -230,7 +233,7 @@ namespace TelegramBotBase
ds?.OnMessageReceived(new MessageReceivedEventArgs(e.Message));
await Client_TryMessage(sender, e);
await Client_Loop(sender, e);
}
catch (Telegram.Bot.Exceptions.ApiRequestException ex)
{
@ -243,14 +246,86 @@ namespace TelegramBotBase
}
}
private async Task 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 = 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));
@ -293,15 +368,41 @@ namespace TelegramBotBase
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
//Action Event
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)
{
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;
}
}
}
}
if (!ds.FormSwitched)
{
//Render Event
await activeForm.RenderControls(e);
await activeForm.Render(e);
@ -314,6 +415,38 @@ namespace TelegramBotBase
}
/// <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))
@ -363,12 +496,12 @@ namespace TelegramBotBase
//When form has been switched due navigation within the edit method, reopen Client_Message
if (ds.FormSwitched)
{
await Client_TryMessage(sender, e);
await Client_Loop(sender, e);
}
}
private void Client_Action(object sender, MessageResult e)
private async void Client_Action(object sender, MessageResult e)
{
try
{
@ -380,7 +513,7 @@ namespace TelegramBotBase
OnMessage(new MessageIncomeEventArgs(e.DeviceId, ds, e));
}
Client_TryAction(sender, e);
await Client_Loop(sender, e);
}
catch (Exception ex)
{
@ -389,80 +522,80 @@ namespace TelegramBotBase
}
}
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;
}
//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;
// ds.LastAction = DateTime.Now;
// ds.LastMessage = e.Message;
FormBase activeForm = null;
// FormBase activeForm = null;
int i = 0;
// int i = 0;
//Should formulars get navigated (allow maximum of 10, to dont get loops)
do
{
i++;
// //Should formulars get navigated (allow maximum of 10, to dont get loops)
// do
// {
// i++;
//Reset navigation
ds.FormSwitched = false;
// //Reset navigation
// ds.FormSwitched = false;
activeForm = ds.ActiveForm;
// activeForm = ds.ActiveForm;
//Pre Loading Event
await activeForm.PreLoad(e);
// //Pre Loading Event
// await activeForm.PreLoad(e);
//Send Load event to controls
await activeForm.LoadControls(e);
// //Send Load event to controls
// await activeForm.LoadControls(e);
//Loading Event
await activeForm.Load(e);
// //Loading Event
// await activeForm.Load(e);
//Action Event
if (!ds.FormSwitched)
{
//Send Action event to controls
await activeForm.ActionControls(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);
// //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 (!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;
}
}
}
// if (uhc.Handled)
// {
// e.Handled = true;
// if (!ds.FormSwitched)
// {
// break;
// }
// }
// }
}
// }
//Render Event
if (!ds.FormSwitched)
{
await activeForm.RenderControls(e);
// //Render Event
// if (!ds.FormSwitched)
// {
// await activeForm.RenderControls(e);
await activeForm.Render(e);
}
// await activeForm.Render(e);
// }
e.IsFirstHandler = false;
// e.IsFirstHandler = false;
} while (ds.FormSwitched && i < this.GetSetting(eSettings.NavigationMaximum, 10));
// } while (ds.FormSwitched && i < this.GetSetting(eSettings.NavigationMaximum, 10));
}
//}
/// <summary>
/// This method will update all local created bot commands to the botfather.

View File

@ -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 });
}
}
}

View File

@ -21,5 +21,7 @@ namespace TelegramBotBase.Constants
public const int MaxReplyKeyboardCols = 12;
public const int MessageDeletionsPerSecond = 30;
}
}

View File

@ -159,6 +159,22 @@ namespace TelegramBotBase.Controls.Hybrid
}
}
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)
@ -396,14 +412,20 @@ namespace TelegramBotBase.Controls.Hybrid
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);
}
else
if (m != null)
{
m = await this.Device.Send(this.Title, (InlineKeyboardMarkup)form, disableNotification: true, parseMode: MessageParseMode, MarkdownV2AutoEscape: false);
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;
}
@ -573,6 +595,11 @@ namespace TelegramBotBase.Controls.Hybrid
{
this.Updated();
}
else
{
//Remove event handler
this.Device.MessageDeleted -= Device_MessageDeleted;
}
}
/// <summary>

View File

@ -170,6 +170,22 @@ namespace TelegramBotBase.Controls.Hybrid
}
}
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)
@ -185,10 +201,7 @@ namespace TelegramBotBase.Controls.Hybrid
?? SubHeadLayoutButtonRow?.FirstOrDefault(a => a.Text.Trim() == result.MessageText)
?? ButtonsForm.ToList().FirstOrDefault(a => a.Text.Trim() == result.MessageText);
var index = HeadLayoutButtonRow?.IndexOf(button)
?? SubHeadLayoutButtonRow?.IndexOf(button)
?? ButtonsForm.ToList().IndexOf(button);
var index = ButtonsForm.FindRowByButton(button);
switch (this.SelectedViewIndex)
@ -326,9 +339,7 @@ namespace TelegramBotBase.Controls.Hybrid
?? SubHeadLayoutButtonRow?.FirstOrDefault(a => a.Value == result.RawData)
?? ButtonsForm.ToList().FirstOrDefault(a => a.Value == result.RawData);
var index = HeadLayoutButtonRow?.IndexOf(button)
?? SubHeadLayoutButtonRow?.IndexOf(button)
?? ButtonsForm.ToList().IndexOf(button);
var index = ButtonsForm.FindRowByButton(button);
if (button != null)
{
@ -521,15 +532,21 @@ namespace TelegramBotBase.Controls.Hybrid
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);
}
else
if (m != null)
{
m = await this.Device.Send(this.Title, (InlineKeyboardMarkup)form, disableNotification: true, parseMode: MessageParseMode, MarkdownV2AutoEscape: false);
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;
}
@ -782,6 +799,11 @@ namespace TelegramBotBase.Controls.Hybrid
{
this.Updated();
}
else
{
//Remove event handler
this.Device.MessageDeleted -= Device_MessageDeleted;
}
}
/// <summary>

View File

@ -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;
}
}
}

View File

@ -27,9 +27,14 @@ namespace TelegramBotBase.Enums
/// <summary>
/// Does stick to the console event handler and saves all sessions on exit.
/// </summary>
SaveSessionsOnConsoleExit = 4
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,
}
}

View File

@ -16,8 +16,16 @@ namespace TelegramBotBase.Form
[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]
@ -70,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;

View File

@ -39,7 +39,6 @@ namespace TelegramBotBase.Form
}
private async Task AutoCleanForm_Init(object sender, InitEventArgs e)
{
if (this.Device == null)
@ -130,12 +129,22 @@ namespace TelegramBotBase.Form
{
while (this.OldMessages.Count > 0)
{
if (!await this.Device.DeleteMessage(this.OldMessages[0]))
var tasks = new List<Task>();
var msgs = this.OldMessages.Take(Constants.Telegram.MessageDeletionsPerSecond);
foreach (var msg in msgs)
{
//Message can't be deleted cause it seems not to exist anymore
if (this.OldMessages.Count > 0)
this.OldMessages.RemoveAt(0);
}
tasks.Add(this.Device.DeleteMessage(msg));
}
await Task.WhenAll(tasks);
foreach(var m in msgs)
{
Device.OnMessageDeleted(new MessageDeletedEventArgs(m));
}
this.OldMessages.RemoveRange(0, msgs.Count());
}
}
}

View File

@ -13,8 +13,16 @@ 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>
@ -77,7 +85,7 @@ namespace TelegramBotBase.Form
return;
}
OnButtonClicked(new ButtonClickedEventArgs(button));
OnButtonClicked(new ButtonClickedEventArgs(button) { Tag = this.Tag });
if (AutoCloseOnClick)
await CloseForm();

View File

@ -41,6 +41,7 @@ namespace TelegramBotBase.Form
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));

View File

@ -6,6 +6,7 @@ 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;
@ -14,10 +15,21 @@ namespace TelegramBotBase.Form
[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; }
/// <summary>
/// An additional optional value.
/// </summary>
public object Tag { get; set; }
private EventHandlerList __Events { get; set; } = new EventHandlerList();
private static object __evCompleted { get; } = new object();
@ -86,13 +98,13 @@ namespace TelegramBotBase.Form
message.Handled = true;
OnCompleted(new EventArgs());
OnCompleted(new PromptDialogCompletedEventArgs() { Tag = this.Tag, Value = this.Value });
await this.CloseForm();
}
public event EventHandler<EventArgs> Completed
public event EventHandler<PromptDialogCompletedEventArgs> Completed
{
add
{
@ -104,9 +116,9 @@ namespace TelegramBotBase.Form
}
}
public void OnCompleted(EventArgs e)
public void OnCompleted(PromptDialogCompletedEventArgs e)
{
(this.__Events[__evCompleted] as EventHandler<EventArgs>)?.Invoke(this, e);
(this.__Events[__evCompleted] as EventHandler<PromptDialogCompletedEventArgs>)?.Invoke(this, e);
}
}

View File

@ -26,6 +26,10 @@ namespace TelegramBotBase.Localizations
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";
}

View File

@ -36,6 +36,10 @@ namespace TelegramBotBase.Localizations
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";

View File

@ -198,6 +198,8 @@ namespace TelegramBotBase
catch (ArgumentException ex)
{
CustomConversionChecks(form, p, f);
}
catch
{
@ -238,6 +240,35 @@ namespace TelegramBotBase
}
private static void CustomConversionChecks(FormBase form, KeyValuePair<string, object> p, System.Reflection.PropertyInfo f)
{
//Newtonsoft Int64/Int32 converter issue
if (f.PropertyType == typeof(Int32))
{
int i = 0;
if(int.TryParse(p.Value.ToString(), out i))
{
f.SetValue(form, i);
}
return;
}
//Newtonsoft Double/Decimal converter issue
if(f.PropertyType == typeof(Decimal) | f.PropertyType == typeof(Nullable<Decimal>))
{
decimal d = 0;
if(decimal.TryParse(p.Value.ToString(), out d))
{
f.SetValue(form, d);
}
return;
}
}
/// <summary>
/// Saves all open states into the machine.
/// </summary>

View File

@ -119,6 +119,7 @@ namespace TelegramBotBase.Sessions
private static object __evMessageSent = new object();
private static object __evMessageReceived = new object();
private static object __evMessageDeleted = new object();
public DeviceSession()
{
@ -768,21 +769,25 @@ namespace TelegramBotBase.Sessions
/// <param name="call"></param>
/// <returns></returns>
public async Task<T> API<T>(Func<Telegram.Bot.TelegramBotClient, Task<T>> call)
{
var numberOfTries = 0;
while (numberOfTries < DeviceSession.MaxNumberOfRetries)
{
try
{
return await call(this.Client.TelegramClient);
return await call(Client.TelegramClient);
}
catch (ApiRequestException ex)
{
if (ex.ErrorCode != 429)
throw;
if (ex.Parameters != null)
{
await Task.Delay(ex.Parameters.RetryAfter);
await Task.Delay(ex.Parameters.RetryAfter * 1000);
return await call(this.Client.TelegramClient);
numberOfTries++;
}
}
return default(T);
}
@ -792,18 +797,24 @@ namespace TelegramBotBase.Sessions
/// <param name="call"></param>
/// <returns></returns>
public async Task API(Func<Telegram.Bot.TelegramBotClient, Task> call)
{
var numberOfTries = 0;
while (numberOfTries < DeviceSession.MaxNumberOfRetries)
{
try
{
await call(this.Client.TelegramClient);
await call(Client.TelegramClient);
return;
}
catch (ApiRequestException ex)
{
if (ex.Parameters != null)
{
await Task.Delay(ex.Parameters.RetryAfter);
if (ex.ErrorCode != 429)
throw;
await call(this.Client.TelegramClient);
if (ex.Parameters != null)
await Task.Delay(ex.Parameters.RetryAfter * 1000);
numberOfTries++;
}
}
}
@ -853,6 +864,37 @@ namespace TelegramBotBase.Sessions
(this.__Events[__evMessageReceived] as EventHandler<MessageReceivedEventArgs>)?.Invoke(this, e);
}
/// <summary>
/// Eventhandler for deleting messages
/// </summary>
public event EventHandler<MessageDeletedEventArgs> MessageDeleted
{
add
{
this.__Events.AddHandler(__evMessageDeleted, value);
}
remove
{
this.__Events.RemoveHandler(__evMessageDeleted, value);
}
}
public void OnMessageDeleted(MessageDeletedEventArgs e)
{
(this.__Events[__evMessageDeleted] as EventHandler<MessageDeletedEventArgs>)?.Invoke(this, e);
}
#endregion
#region "Static"
/// <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>
public static uint MaxNumberOfRetries { get; set; }
#endregion "Static"
}
}

View File

@ -0,0 +1,53 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TelegramBotBase.Args;
using TelegramBotBase.Controls;
using TelegramBotBase.Controls.Inline;
using TelegramBotBase.Form;
namespace TelegramBotBaseTest.Tests.Controls
{
public class MultiToggleButtons : AutoCleanForm
{
public MultiToggleButtons()
{
this.DeleteMode = TelegramBotBase.Enums.eDeleteMode.OnLeavingForm;
this.Init += ToggleButtons_Init;
}
private async Task ToggleButtons_Init(object sender, InitEventArgs e)
{
var mtb = new MultiToggleButton();
mtb.Options = new List<ButtonBase>() { new ButtonBase("Option 1", "1"), new ButtonBase("Option 2", "2"), new ButtonBase("Option 3", "3") };
mtb.SelectedOption = mtb.Options.FirstOrDefault();
mtb.Toggled += Tb_Toggled;
this.AddControl(mtb);
mtb = new MultiToggleButton();
mtb.Options = new List<ButtonBase>() { new ButtonBase("Option 4", "4"), new ButtonBase("Option 5", "5"), new ButtonBase("Option 6", "6") };
mtb.SelectedOption = mtb.Options.FirstOrDefault();
mtb.AllowEmptySelection = false;
mtb.Toggled += Tb_Toggled;
this.AddControl(mtb);
}
private void Tb_Toggled(object sender, EventArgs e)
{
var tb = sender as MultiToggleButton;
if (tb.SelectedOption != null)
{
Console.WriteLine(tb.ID.ToString() + " was pressed, and toggled to " + tb.SelectedOption.Value);
return;
}
Console.WriteLine("Selection for " + tb.ID.ToString() + " has been removed.");
}
}
}

View File

@ -140,6 +140,17 @@ namespace TelegramBotBaseTest.Tests
await this.NavigateTo(tb);
break;
case "multitogglebuttons":
message.Handled = true;
var mtb = new Controls.MultiToggleButtons();
await this.NavigateTo(mtb);
break;
case "buttongrid":
message.Handled = true;
@ -217,6 +228,8 @@ namespace TelegramBotBaseTest.Tests
btn.AddButtonRow(new ButtonBase("#11 ToggleButtons", new CallbackData("a", "togglebuttons").Serialize()));
btn.AddButtonRow(new ButtonBase("#11.2 MultiToggleButtons", new CallbackData("a", "multitogglebuttons").Serialize()));
btn.AddButtonRow(new ButtonBase("#12 ButtonGrid", new CallbackData("a", "buttongrid").Serialize()));
btn.AddButtonRow(new ButtonBase("#13 ButtonGrid Paging & Filter", new CallbackData("a", "buttongridfilter").Serialize()));

Binary file not shown.

After

Width:  |  Height:  |  Size: 328 KiB