- Update of FormBase for better async event management

- Update of ControlBase for partial rendering options
- Updating serveral base classes to better asyc event management
- separating Enums into different folder
- some small bug fixes
- removed "CustomEventManagement" cause it doesnt make sense now
This commit is contained in:
FlorianDahn 2019-07-14 22:22:04 +02:00
parent cb4a797b12
commit e4da70d37d
17 changed files with 404 additions and 103 deletions

View File

@ -11,7 +11,12 @@ namespace TelegramBaseTest.Tests
public class ButtonTestForm : AutoCleanForm public class ButtonTestForm : AutoCleanForm
{ {
public override async Task Opened() public ButtonTestForm()
{
this.Opened += ButtonTestForm_Opened;
}
private async Task ButtonTestForm_Opened(object sender, EventArgs e)
{ {
await this.Device.Send("Hello world! (Click 'back' to get back to Start)"); await this.Device.Send("Hello world! (Click 'back' to get back to Start)");
} }
@ -83,6 +88,8 @@ namespace TelegramBaseTest.Tests
btn.AddButtonRow(new ButtonBase("Button 3", new CallbackData("a", "button3").Serialize()), new ButtonBase("Button 4", new CallbackData("a", "button4").Serialize())); 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())); btn.AddButtonRow(new ButtonBase("Back", new CallbackData("a", "back").Serialize()));
await this.Device.Send("Click a button", btn); await this.Device.Send("Click a button", btn);

View File

@ -14,10 +14,13 @@ namespace TelegramBaseTest.Tests
public ProgressTest() public ProgressTest()
{ {
this.DeleteMode = eDeleteMode.OnLeavingForm; this.DeleteMode = TelegramBotBase.Enums.eDeleteMode.OnLeavingForm;
this.Opened += ProgressTest_Opened;
this.Closed += ProgressTest_Closed;
} }
public override async Task Opened()
private async Task ProgressTest_Opened(object sender, EventArgs e)
{ {
await this.Device.Send("Welcome to ProgressTest"); await this.Device.Send("Welcome to ProgressTest");
} }
@ -87,14 +90,14 @@ namespace TelegramBaseTest.Tests
//Render Progress bar and show some "example" progress //Render Progress bar and show some "example" progress
await Bar.Render(); await Bar.Render(message);
this.Controls.Add(Bar); this.Controls.Add(Bar);
for (int i = 0; i <= 100; i++) for (int i = 0; i <= 100; i++)
{ {
Bar.Value++; Bar.Value++;
await Bar.Render(); await Bar.Render(message);
Thread.Sleep(250); Thread.Sleep(250);
} }
@ -117,17 +120,10 @@ namespace TelegramBaseTest.Tests
await this.Device.Send("Choose your progress bar:", btn); await this.Device.Send("Choose your progress bar:", btn);
} }
public override async Task Closed() private async Task ProgressTest_Closed(object sender, EventArgs e)
{ {
foreach (var b in this.Controls)
{
await b.Cleanup();
}
await this.Device.Send("Ciao from ProgressTest"); await this.Device.Send("Ciao from ProgressTest");
} }
} }
} }

View File

@ -12,11 +12,17 @@ namespace TelegramBaseTest.Tests.Register.Steps
{ {
public Data UserData { get; set; } public Data UserData { get; set; }
public async override Task Init(params object[] args) public Step1()
{
this.Init += Step1_Init;
}
private async Task Step1_Init(object sender, InitEventArgs e)
{ {
this.UserData = new Data(); this.UserData = new Data();
} }
public async override Task Load(MessageResult message) public async override Task Load(MessageResult message)
{ {
if (message.Handled) if (message.Handled)

View File

@ -13,16 +13,17 @@ namespace TelegramBaseTest.Tests
public SimpleForm() public SimpleForm()
{ {
this.DeleteSide = eSide.Both; this.DeleteSide = TelegramBotBase.Enums.eSide.Both;
this.DeleteMode = eDeleteMode.OnLeavingForm; this.DeleteMode = TelegramBotBase.Enums.eDeleteMode.OnLeavingForm;
this.Opened += SimpleForm_Opened;
} }
public override async Task Opened() private async Task SimpleForm_Opened(object sender, EventArgs e)
{ {
await this.Device.Send("Hello world! (send 'back' to get back to Start)\r\nOr\r\nhi, hello, maybe, bye and ciao"); await 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 //message.MessageText will work also, cause it is a string you could manage a lot different scenerios here

View File

@ -8,8 +8,12 @@ using TelegramBotBase.Form;
namespace TelegramBaseTest.Tests namespace TelegramBaseTest.Tests
{ {
public class Start : FormBase public class Start : AutoCleanForm
{ {
public Start()
{
this.DeleteMode = TelegramBotBase.Enums.eDeleteMode.OnLeavingForm;
}
public override async Task Action(MessageResult message) public override async Task Action(MessageResult message)
{ {
@ -79,6 +83,36 @@ namespace TelegramBaseTest.Tests
await this.NavigateTo(data); await this.NavigateTo(data);
break;
case "calendar":
message.Handled = true;
var calendar = new Controls.CalendarPickerForm();
await this.NavigateTo(calendar);
break;
case "month":
message.Handled = true;
var month = new Controls.MonthPickerForm();
await this.NavigateTo(month);
break;
case "treeview":
message.Handled = true;
var tree = new Controls.TreeViewForms();
await this.NavigateTo(tree);
break; break;
} }
@ -99,6 +133,10 @@ namespace TelegramBaseTest.Tests
btn.AddButtonRow(new ButtonBase("#6 Form2 Command", new CallbackData("a", "form2").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())); btn.AddButtonRow(new ButtonBase("#7 Data Handling", new CallbackData("a", "data").Serialize()));
btn.AddButtonRow(new ButtonBase("#8 Calendar Picker", new CallbackData("a", "calendar").Serialize()));
btn.AddButtonRow(new ButtonBase("#9 Month Picker", new CallbackData("a", "month").Serialize()));
btn.AddButtonRow(new ButtonBase("#10 TreeView", new CallbackData("a", "treeview").Serialize()));
await this.Device.Send("Choose your test:", btn); await this.Device.Send("Choose your test:", btn);
} }

View File

@ -15,13 +15,18 @@ namespace TelegramBaseTest.Tests
String LastMessage { get; set; } String LastMessage { get; set; }
public TestForm()
{
this.Opened += TestForm_Opened;
this.Closed += TestForm_Closed;
}
public override async Task Opened() private async Task TestForm_Opened(object sender, EventArgs e)
{ {
await this.Device.Send("Welcome to Form 1"); await this.Device.Send("Welcome to Form 1");
} }
public override async Task Closed() private async Task TestForm_Closed(object sender, EventArgs e)
{ {
await this.Device.Send("Ciao from Form 1"); await this.Device.Send("Ciao from Form 1");
} }

View File

@ -16,17 +16,25 @@ namespace TelegramBaseTest.Tests
{ {
public override async Task Opened() public TestForm2()
{
this.Opened += TestForm2_Opened;
this.Closed += TestForm2_Closed;
}
private async Task TestForm2_Opened(object sender, EventArgs e)
{ {
await this.Device.Send("Welcome to Form 2"); await this.Device.Send("Welcome to Form 2");
} }
public override async Task Closed() private async Task TestForm2_Closed(object sender, EventArgs e)
{ {
await this.Device.Send("Ciao from Form 2"); await this.Device.Send("Ciao from Form 2");
} }
public override async Task Action(MessageResult message) public override async Task Action(MessageResult message)
{ {
var call = message.GetData<CallbackData>(); var call = message.GetData<CallbackData>();

View File

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

View File

@ -13,9 +13,26 @@ namespace TelegramBotBase.Base
{ {
public Sessions.DeviceSession Device { get; set; } public Sessions.DeviceSession Device { get; set; }
public virtual async Task Render() public int ID { get; set; }
/// <summary>
/// Defines if the control should be rendered and invoked with actions
/// </summary>
public bool Enabled { get; set; } = true;
public virtual async Task Action(MessageResult result)
{ {
}
public virtual async Task Render(MessageResult result)
{
} }
public virtual async Task Cleanup() public virtual async Task Cleanup()

View File

@ -1,10 +1,12 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using TelegramBotBase.Base; using TelegramBotBase.Base;
using TelegramBotBase.Sessions; using TelegramBotBase.Sessions;
using static TelegramBotBase.Base.Async;
namespace TelegramBotBase.Form namespace TelegramBotBase.Form
{ {
@ -13,12 +15,11 @@ namespace TelegramBotBase.Form
/// </summary> /// </summary>
public class FormBase : IDisposable public class FormBase : IDisposable
{ {
public DeviceSession Device { get; set; } public DeviceSession Device { get; set; }
public MessageClient Client { get; set; } public MessageClient Client { get; set; }
public bool CustomEventManagement { get; set; } = false;
/// <summary> /// <summary>
/// has this formular already been disposed ? /// has this formular already been disposed ?
/// </summary> /// </summary>
@ -26,34 +27,119 @@ namespace TelegramBotBase.Form
public List<ControlBase> Controls { get; set; } public List<ControlBase> Controls { get; set; }
private EventHandlerList Events = new EventHandlerList();
private static object __evInit = new object();
private static object __evOpened = new object();
private static object __evClosed = new object();
public FormBase() public FormBase()
{ {
this.Controls = new List<Base.ControlBase>(); this.Controls = new List<Base.ControlBase>();
} }
public FormBase(MessageClient Client): this() public FormBase(MessageClient Client) : this()
{ {
this.Client = Client; 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>>();
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>>();
foreach (var h in handler)
{
await Async.InvokeAllAsync<EventArgs>(h, this, e);
}
} }
/// <summary> /// <summary>
/// Gets invoked if gets navigated to this form /// Gets invoked if gets navigated to this form
/// </summary> /// </summary>
/// <returns></returns> /// <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 virtual async Task Closed() public async Task OnClosed(EventArgs e)
{
if (this.Events[__evClosed] == null)
return;
var handler = this.Events[__evClosed].GetInvocationList().Cast<AsyncEventHandler<EventArgs>>();
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>
/// Pre to form close, cleanup all controls
/// </summary>
/// <returns></returns>
public async Task CloseControls()
{ {
foreach (var b in this.Controls) foreach (var b in this.Controls)
{ {
@ -76,6 +162,22 @@ namespace TelegramBotBase.Form
} }
/// <summary>
/// Gets invoked if the user clicked a button.
/// </summary>
/// <param name="message"></param>
/// <returns></returns>
public async Task ActionControls(MessageResult message)
{
foreach (var b in this.Controls)
{
if (!b.Enabled)
continue;
await b.Action(message);
}
}
/// <summary> /// <summary>
/// Gets invoked if the user has clicked a button. /// Gets invoked if the user has clicked a button.
/// </summary> /// </summary>
@ -96,6 +198,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 async Task RenderControls(MessageResult message)
{
foreach (var b in this.Controls)
{
if (!b.Enabled)
continue;
await b.Render(message);
}
}
/// <summary> /// <summary>
/// Gets invoked at the end of the cycle to "Render" text, images, buttons, etc... /// Gets invoked at the end of the cycle to "Render" text, images, buttons, etc...
/// </summary> /// </summary>
@ -106,6 +224,8 @@ namespace TelegramBotBase.Form
} }
/// <summary> /// <summary>
/// Navigates to a new form /// Navigates to a new form
/// </summary> /// </summary>
@ -125,11 +245,20 @@ namespace TelegramBotBase.Form
newForm.Client = this.Client; newForm.Client = this.Client;
newForm.Device = ds; newForm.Device = ds;
await newForm.Init(args); await newForm.OnInit(new InitEventArgs(args));
await this.Closed(); await this.CloseControls();
await newForm.Opened(); await this.OnClosed(new EventArgs());
await newForm.OnOpened(new EventArgs());
}
public void AddControl(ControlBase control)
{
control.ID = this.Controls.Count + 1;
control.Device = this.Device;
this.Controls.Add(control);
} }
/// <summary> /// <summary>

View File

@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TelegramBotBase.Base
{
public class InitEventArgs : EventArgs
{
public object[] Args { get; set; }
public InitEventArgs(params object[] args)
{
this.Args = args;
}
}
}

View File

@ -264,10 +264,6 @@ namespace TelegramBotBase
activeForm = ds.ActiveForm; activeForm = ds.ActiveForm;
//If the form manages the events by itselfs, skip here
if (activeForm.CustomEventManagement)
return;
//Pre Loading Event //Pre Loading Event
await activeForm.PreLoad(e); await activeForm.PreLoad(e);
@ -337,10 +333,6 @@ namespace TelegramBotBase
activeForm = ds.ActiveForm; activeForm = ds.ActiveForm;
//If the form manages the events by itselfs, skip here
if (activeForm.CustomEventManagement)
return;
//Pre Loading Event //Pre Loading Event
await activeForm.PreLoad(e); await activeForm.PreLoad(e);
@ -350,6 +342,10 @@ namespace TelegramBotBase
//Action Event //Action Event
if (!ds.FormSwitched) if (!ds.FormSwitched)
{ {
//Send Action event to controls
await activeForm.ActionControls(e);
//Send Action event to form itself
await activeForm.Action(e); await activeForm.Action(e);
if (!e.Handled) if (!e.Handled)
@ -374,7 +370,11 @@ namespace TelegramBotBase
//Render Event //Render Event
if (!ds.FormSwitched) if (!ds.FormSwitched)
{
await activeForm.RenderControls(e);
await activeForm.Render(e); await activeForm.Render(e);
}
} while (ds.FormSwitched && i < NavigationMaximum); } while (ds.FormSwitched && i < NavigationMaximum);

View File

@ -0,0 +1,56 @@
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
}
public enum eSide
{
/// <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
}
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
}
}

View File

@ -5,6 +5,7 @@ using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Telegram.Bot.Types; using Telegram.Bot.Types;
using TelegramBotBase.Base; using TelegramBotBase.Base;
using TelegramBotBase.Enums;
namespace TelegramBotBase.Form namespace TelegramBotBase.Form
{ {
@ -19,37 +20,7 @@ namespace TelegramBotBase.Form
public eSide DeleteSide { get; set; } public eSide DeleteSide { get; set; }
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
}
public enum eSide
{
/// <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
}
public AutoCleanForm() public AutoCleanForm()
{ {
@ -57,9 +28,14 @@ namespace TelegramBotBase.Form
this.DeleteMode = eDeleteMode.OnEveryCall; this.DeleteMode = eDeleteMode.OnEveryCall;
this.DeleteSide = eSide.BotOnly; this.DeleteSide = eSide.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) if (this.Device == null)
return; return;
@ -127,7 +103,7 @@ namespace TelegramBotBase.Form
this.OldMessages.RemoveAt(this.OldMessages.Count - 1); 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) if (this.DeleteMode != eDeleteMode.OnLeavingForm)
return; return;
@ -135,7 +111,6 @@ namespace TelegramBotBase.Form
await MessageCleanup(); await MessageCleanup();
} }
/// <summary> /// <summary>
/// Cleans up all remembered messages. /// Cleans up all remembered messages.
/// </summary> /// </summary>

View File

@ -77,6 +77,9 @@ namespace TelegramBotBase.Form
public static implicit operator InlineKeyboardMarkup(ButtonForm form) public static implicit operator InlineKeyboardMarkup(ButtonForm form)
{ {
if (form == null)
return null;
InlineKeyboardMarkup ikm = new InlineKeyboardMarkup(form.ToArray()); InlineKeyboardMarkup ikm = new InlineKeyboardMarkup(form.ToArray());
return ikm; return ikm;

View File

@ -67,9 +67,9 @@ namespace TelegramBotBase
DeviceSession ds = new Sessions.DeviceSession(deviceId, start); DeviceSession ds = new Sessions.DeviceSession(deviceId, start);
start.Device = ds; start.Device = ds;
await start.Init(); await start.OnInit(new InitEventArgs());
await start.Opened(); await start.OnOpened(new EventArgs());
this[deviceId] = ds; this[deviceId] = ds;
return ds; return ds;

View File

@ -127,11 +127,7 @@ namespace TelegramBotBase.Sessions
if (this.ActiveForm == null) if (this.ActiveForm == null)
return null; return null;
InlineKeyboardMarkup markup = null; InlineKeyboardMarkup markup = buttons;
if (buttons != null)
{
markup = buttons;
}
try try
{ {
@ -160,11 +156,7 @@ namespace TelegramBotBase.Sessions
if (this.ActiveForm == null) if (this.ActiveForm == null)
return null; return null;
InlineKeyboardMarkup markup = null; InlineKeyboardMarkup markup = buttons;
if (buttons != null)
{
markup = buttons;
}
try try
{ {
@ -194,11 +186,7 @@ namespace TelegramBotBase.Sessions
if (this.ActiveForm == null) if (this.ActiveForm == null)
return null; return null;
InlineKeyboardMarkup markup = null; InlineKeyboardMarkup markup = buttons;
if (buttons != null)
{
markup = buttons;
}
Message m = null; Message m = null;
@ -253,6 +241,39 @@ namespace TelegramBotBase.Sessions
return m; return m;
} }
/// <summary>
/// Sends a simple text message
/// </summary>
/// <param name="text"></param>
/// <param name="markup"></param>
/// <param name="replyTo"></param>
/// <param name="disableNotification"></param>
/// <returns></returns>
public async Task<Message> Send(String text, ReplyKeyboardMarkup markup, int replyTo = 0, bool disableNotification = false)
{
if (this.ActiveForm == null)
return null;
Message m = null;
try
{
m = await (this.Client.TelegramClient.SendTextMessageAsync(this.DeviceId, text, replyToMessageId: replyTo, replyMarkup: markup, disableNotification: disableNotification));
OnMessageSent(new MessageSentEventArgs(m));
}
catch (ApiRequestException ex)
{
return null;
}
catch
{
return null;
}
return m;
}
/// <summary> /// <summary>
/// Sends an image /// Sends an image
/// </summary> /// </summary>
@ -266,11 +287,7 @@ namespace TelegramBotBase.Sessions
if (this.ActiveForm == null) if (this.ActiveForm == null)
return null; return null;
InlineKeyboardMarkup markup = null; InlineKeyboardMarkup markup = buttons;
if (buttons != null)
{
markup = buttons;
}
Message m = null; Message m = null;