fix: reformat using C# rules

This commit is contained in:
ZavaruKitsu
2022-10-08 19:26:34 +03:00
parent a731e2a8d0
commit 5ab15621a0
180 changed files with 13136 additions and 13081 deletions
+13 -16
View File
@@ -1,21 +1,18 @@
using TelegramBotBase.Attributes;
namespace TelegramBotBase.Form
namespace TelegramBotBase.Form;
/// <summary>
/// A simple prompt dialog with one ok Button
/// </summary>
[IgnoreState]
public class AlertDialog : ConfirmDialog
{
/// <summary>
/// A simple prompt dialog with one ok Button
/// </summary>
[IgnoreState]
public class AlertDialog : ConfirmDialog
public AlertDialog(string message, string buttonText) : base(message)
{
public string ButtonText { get; set; }
public AlertDialog(string message, string buttonText) : base(message)
{
Buttons.Add(new ButtonBase(buttonText, "ok"));
this.ButtonText = buttonText;
}
Buttons.Add(new ButtonBase(buttonText, "ok"));
ButtonText = buttonText;
}
}
public string ButtonText { get; set; }
}
+84 -85
View File
@@ -1,113 +1,112 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Threading.Tasks;
using TelegramBotBase.Args;
using TelegramBotBase.Attributes;
using TelegramBotBase.Base;
namespace TelegramBotBase.Form
namespace TelegramBotBase.Form;
/// <summary>
/// A prompt with a lot of buttons
/// </summary>
[IgnoreState]
public class ArrayPromptDialog : FormBase
{
/// <summary>
/// A prompt with a lot of buttons
/// </summary>
[IgnoreState]
public class ArrayPromptDialog : FormBase
public ArrayPromptDialog()
{
/// <summary>
/// The message the users sees.
/// </summary>
public string Message { get; set; }
}
/// <summary>
/// An additional optional value.
/// </summary>
public object Tag { get; set; }
public ArrayPromptDialog(string message)
{
Message = message;
}
public ButtonBase[][] Buttons { get; set; }
public ArrayPromptDialog(string message, params ButtonBase[][] buttons)
{
Message = message;
Buttons = buttons;
}
[Obsolete]
public Dictionary<string, FormBase> ButtonForms { get; set; } = new Dictionary<string, FormBase>();
/// <summary>
/// The message the users sees.
/// </summary>
public string Message { get; set; }
private static object EvButtonClicked { get; } = new object();
/// <summary>
/// An additional optional value.
/// </summary>
public object Tag { get; set; }
public ArrayPromptDialog()
public ButtonBase[][] Buttons { get; set; }
[Obsolete] public Dictionary<string, FormBase> ButtonForms { get; set; } = new();
private static object EvButtonClicked { get; } = new();
public override async Task Action(MessageResult message)
{
var call = message.GetData<CallbackData>();
message.Handled = true;
if (!message.IsAction)
{
return;
}
public ArrayPromptDialog(string message)
await message.ConfirmAction();
await message.DeleteMessage();
var buttons = Buttons.Aggregate((a, b) => a.Union(b).ToArray()).ToList();
if (call == null)
{
this.Message = message;
return;
}
public ArrayPromptDialog(string message, params ButtonBase[][] buttons)
var button = buttons.FirstOrDefault(a => a.Value == call.Value);
if (button == null)
{
this.Message = message;
this.Buttons = buttons;
return;
}
public override async Task Action(MessageResult message)
OnButtonClicked(new ButtonClickedEventArgs(button) { Tag = Tag });
var fb = ButtonForms.ContainsKey(call.Value) ? ButtonForms[call.Value] : null;
if (fb != null)
{
var call = message.GetData<CallbackData>();
message.Handled = true;
if (!message.IsAction)
return;
await message.ConfirmAction();
await message.DeleteMessage();
var buttons = Buttons.Aggregate((a, b) => a.Union(b).ToArray()).ToList();
if(call==null)
{
return;
}
var button = buttons.FirstOrDefault(a => a.Value == call.Value);
if (button == null)
{
return;
}
OnButtonClicked(new ButtonClickedEventArgs(button) { Tag = Tag });
var fb = ButtonForms.ContainsKey(call.Value) ? ButtonForms[call.Value] : null;
if (fb != null)
{
await NavigateTo(fb);
}
}
public override async Task Render(MessageResult message)
{
var btn = new ButtonForm();
foreach(var bl in Buttons)
{
btn.AddButtonRow(bl.Select(a => new ButtonBase(a.Text, CallbackData.Create("action", a.Value))).ToList());
}
await Device.Send(Message, btn);
}
public event EventHandler<ButtonClickedEventArgs> ButtonClicked
{
add => Events.AddHandler(EvButtonClicked, value);
remove => Events.RemoveHandler(EvButtonClicked, value);
}
public void OnButtonClicked(ButtonClickedEventArgs e)
{
(Events[EvButtonClicked] as EventHandler<ButtonClickedEventArgs>)?.Invoke(this, e);
await NavigateTo(fb);
}
}
public override async Task Render(MessageResult message)
{
var btn = new ButtonForm();
foreach (var bl in Buttons)
{
btn.AddButtonRow(
bl.Select(a => new ButtonBase(a.Text, CallbackData.Create("action", a.Value))).ToList());
}
await Device.Send(Message, btn);
}
public event EventHandler<ButtonClickedEventArgs> ButtonClicked
{
add => Events.AddHandler(EvButtonClicked, value);
remove => Events.RemoveHandler(EvButtonClicked, value);
}
public void OnButtonClicked(ButtonClickedEventArgs e)
{
(Events[EvButtonClicked] as EventHandler<ButtonClickedEventArgs>)?.Invoke(this, e);
}
}
+143 -135
View File
@@ -11,135 +11,143 @@ using TelegramBotBase.Attributes;
using TelegramBotBase.Base;
using TelegramBotBase.Enums;
namespace TelegramBotBase.Form
namespace TelegramBotBase.Form;
/// <summary>
/// A form which cleans up old messages sent within
/// </summary>
public class AutoCleanForm : FormBase
{
/// <summary>
/// A form which cleans up old messages sent within
/// </summary>
public class AutoCleanForm : FormBase
public AutoCleanForm()
{
[SaveState]
public List<int> OldMessages { get; set; }
OldMessages = new List<int>();
DeleteMode = EDeleteMode.OnEveryCall;
DeleteSide = EDeleteSide.BotOnly;
[SaveState]
public EDeleteMode DeleteMode { get; set; }
Init += AutoCleanForm_Init;
[SaveState]
public EDeleteSide DeleteSide { get; set; }
Closed += AutoCleanForm_Closed;
}
[SaveState] public List<int> OldMessages { get; set; }
[SaveState] public EDeleteMode DeleteMode { get; set; }
public AutoCleanForm()
[SaveState] public EDeleteSide DeleteSide { get; set; }
private Task AutoCleanForm_Init(object sender, InitEventArgs e)
{
if (Device == null)
{
OldMessages = new List<int>();
DeleteMode = EDeleteMode.OnEveryCall;
DeleteSide = EDeleteSide.BotOnly;
Init += AutoCleanForm_Init;
Closed += AutoCleanForm_Closed;
}
private Task AutoCleanForm_Init(object sender, InitEventArgs e)
{
if (Device == null)
return Task.CompletedTask;
Device.MessageSent += Device_MessageSent;
Device.MessageReceived += Device_MessageReceived;
Device.MessageDeleted += Device_MessageDeleted;
return Task.CompletedTask;
}
private void Device_MessageDeleted(object sender, MessageDeletedEventArgs e)
Device.MessageSent += Device_MessageSent;
Device.MessageReceived += Device_MessageReceived;
Device.MessageDeleted += Device_MessageDeleted;
return Task.CompletedTask;
}
private void Device_MessageDeleted(object sender, MessageDeletedEventArgs e)
{
if (OldMessages.Contains(e.MessageId))
{
if (OldMessages.Contains(e.MessageId))
OldMessages.Remove(e.MessageId);
OldMessages.Remove(e.MessageId);
}
}
private void Device_MessageReceived(object sender, MessageReceivedEventArgs e)
{
if (DeleteSide == EDeleteSide.BotOnly)
{
return;
}
private void Device_MessageReceived(object sender, MessageReceivedEventArgs e)
OldMessages.Add(e.Message.MessageId);
}
private Task Device_MessageSent(object sender, MessageSentEventArgs e)
{
if (DeleteSide == EDeleteSide.UserOnly)
{
if (DeleteSide == EDeleteSide.BotOnly)
return;
OldMessages.Add(e.Message.MessageId);
}
private Task Device_MessageSent(object sender, MessageSentEventArgs e)
{
if (DeleteSide == EDeleteSide.UserOnly)
return Task.CompletedTask;
OldMessages.Add(e.Message.MessageId);
return Task.CompletedTask;
}
public override async Task PreLoad(MessageResult message)
{
if (DeleteMode != EDeleteMode.OnEveryCall)
return;
OldMessages.Add(e.Message.MessageId);
return Task.CompletedTask;
}
await MessageCleanup();
public override async Task PreLoad(MessageResult message)
{
if (DeleteMode != EDeleteMode.OnEveryCall)
{
return;
}
/// <summary>
/// Adds a message to this of removable ones
/// </summary>
/// <param name="Id"></param>
public void AddMessage(Message m)
await MessageCleanup();
}
/// <summary>
/// Adds a message to this of removable ones
/// </summary>
/// <param name="Id"></param>
public void AddMessage(Message m)
{
OldMessages.Add(m.MessageId);
}
/// <summary>
/// Adds a message to this of removable ones
/// </summary>
/// <param name="Id"></param>
public void AddMessage(int messageId)
{
OldMessages.Add(messageId);
}
/// <summary>
/// Keeps the message by removing it from the list
/// </summary>
/// <param name="id"></param>
public void LeaveMessage(int id)
{
OldMessages.Remove(id);
}
/// <summary>
/// Keeps the last sent message
/// </summary>
public void LeaveLastMessage()
{
if (OldMessages.Count == 0)
{
OldMessages.Add(m.MessageId);
return;
}
OldMessages.RemoveAt(OldMessages.Count - 1);
}
/// <summary>
/// Adds a message to this of removable ones
/// </summary>
/// <param name="Id"></param>
public void AddMessage(int messageId)
private Task AutoCleanForm_Closed(object sender, EventArgs e)
{
if (DeleteMode != EDeleteMode.OnLeavingForm)
{
OldMessages.Add(messageId);
}
/// <summary>
/// Keeps the message by removing it from the list
/// </summary>
/// <param name="id"></param>
public void LeaveMessage(int id)
{
OldMessages.Remove(id);
}
/// <summary>
/// Keeps the last sent message
/// </summary>
public void LeaveLastMessage()
{
if (OldMessages.Count == 0)
return;
OldMessages.RemoveAt(OldMessages.Count - 1);
}
private Task AutoCleanForm_Closed(object sender, EventArgs e)
{
if (DeleteMode != EDeleteMode.OnLeavingForm)
return Task.CompletedTask;
MessageCleanup().Wait();
return Task.CompletedTask;
}
/// <summary>
/// Cleans up all remembered messages.
/// </summary>
/// <returns></returns>
public async Task MessageCleanup()
{
var oldMessages = OldMessages.AsEnumerable();
MessageCleanup().Wait();
return Task.CompletedTask;
}
/// <summary>
/// Cleans up all remembered messages.
/// </summary>
/// <returns></returns>
public async Task MessageCleanup()
{
var oldMessages = OldMessages.AsEnumerable();
#if !NETSTANDARD2_0
while (oldMessages.Any())
@@ -181,51 +189,51 @@ namespace TelegramBotBase.Form
await retryAfterTask;
}
#else
while (oldMessages.Any())
while (oldMessages.Any())
{
using (var cts = new CancellationTokenSource())
{
using (var cts = new CancellationTokenSource())
var deletedMessages = new ConcurrentBag<int>();
var parallelQuery = OldMessages.AsParallel()
.WithCancellation(cts.Token);
Task retryAfterTask = null;
try
{
var deletedMessages = new ConcurrentBag<int>();
var parallelQuery = OldMessages.AsParallel()
.WithCancellation(cts.Token);
Task retryAfterTask = null;
try
parallelQuery.ForAll(i =>
{
parallelQuery.ForAll(i =>
try
{
try
{
Device.DeleteMessage(i).GetAwaiter().GetResult();
deletedMessages.Add(i);
}
catch (ApiRequestException req) when (req.ErrorCode == 400)
{
deletedMessages.Add(i);
}
});
}
catch (AggregateException ex)
{
cts.Cancel();
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 => ((ApiRequestException)e).Parameters.RetryAfter) ?? 0;
retryAfterTask = Task.Delay(retryAfterSeconds * 1000);
}
var retryAfterSeconds = ex.InnerExceptions
.Where(e => e is ApiRequestException apiEx && apiEx.ErrorCode == 429)
.Max(e => ((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;
//deletedMessages.AsParallel().ForAll(i => Device.OnMessageDeleted(new MessageDeletedEventArgs(i)));
oldMessages = oldMessages.Where(x => !deletedMessages.Contains(x));
if (retryAfterTask != null)
{
await retryAfterTask;
}
}
}
#endif
OldMessages.Clear();
}
OldMessages.Clear();
}
}
}
+56 -60
View File
@@ -1,66 +1,62 @@
using System.Diagnostics;
using Telegram.Bot.Types.ReplyMarkups;
namespace TelegramBotBase.Form
namespace TelegramBotBase.Form;
[DebuggerDisplay("{Text}, {Value}")]
/// <summary>
/// Base class for button handling
/// </summary>
public class ButtonBase
{
[DebuggerDisplay("{Text}, {Value}")]
/// <summary>
/// Base class for button handling
/// </summary>
public class ButtonBase
public ButtonBase()
{
public virtual string Text { get; set; }
public string Value { get; set; }
public string Url { get; set; }
public ButtonBase()
{
}
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)
{
var id = (form.DependencyControl != null ? form.DependencyControl.ControlId + "_" : "");
if (Url == null)
{
return InlineKeyboardButton.WithCallbackData(Text, id + Value);
}
var ikb = new InlineKeyboardButton(Text)
{
//ikb.Text = this.Text;
Url = Url
};
return ikb;
}
/// <summary>
/// Returns a KeyBoardButton
/// </summary>
/// <param name="form"></param>
/// <returns></returns>
public virtual KeyboardButton ToKeyboardButton(ButtonForm form)
{
return new KeyboardButton(Text);
}
}
}
public ButtonBase(string text, string value, string url = null)
{
Text = text;
Value = value;
Url = url;
}
public virtual string Text { get; set; }
public string Value { get; set; }
public string Url { get; set; }
/// <summary>
/// Returns an inline Button
/// </summary>
/// <param name="form"></param>
/// <returns></returns>
public virtual InlineKeyboardButton ToInlineButton(ButtonForm form)
{
var id = form.DependencyControl != null ? form.DependencyControl.ControlId + "_" : "";
if (Url == null)
{
return InlineKeyboardButton.WithCallbackData(Text, id + Value);
}
var ikb = new InlineKeyboardButton(Text)
{
//ikb.Text = this.Text;
Url = Url
};
return ikb;
}
/// <summary>
/// Returns a KeyBoardButton
/// </summary>
/// <param name="form"></param>
/// <returns></returns>
public virtual KeyboardButton ToKeyboardButton(ButtonForm form)
{
return new KeyboardButton(Text);
}
}
+321 -305
View File
@@ -5,321 +5,337 @@ using Telegram.Bot.Types.ReplyMarkups;
using TelegramBotBase.Base;
using TelegramBotBase.Controls.Hybrid;
namespace TelegramBotBase.Form
namespace TelegramBotBase.Form;
/// <summary>
/// Base class for an buttons array
/// </summary>
public class ButtonForm
{
/// <summary>
/// Base class for an buttons array
/// </summary>
public class ButtonForm
private readonly List<ButtonRow> _buttons = new();
public ButtonForm()
{
private List<ButtonRow> _buttons = new List<ButtonRow>();
}
public ButtonForm(ControlBase control)
{
DependencyControl = control;
}
public IReplyMarkup Markup { get; set; }
public IReplyMarkup Markup { get; set; }
public ControlBase DependencyControl { get; set; }
public ControlBase DependencyControl { get; set; }
/// <summary>
/// Contains the number of rows.
/// </summary>
public int Rows => _buttons.Count;
/// <summary>
/// Contains the number of rows.
/// </summary>
public int Rows => _buttons.Count;
/// <summary>
/// Contains the highest number of columns in an row.
/// </summary>
public int Cols
/// <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] => _buttons[row];
public int Count
{
get
{
get
if (_buttons.Count == 0)
{
return _buttons.Select(a => a.Count).OrderByDescending(a => a).FirstOrDefault();
}
}
public ButtonRow this[int row] => _buttons[row];
public ButtonForm()
{
}
public ButtonForm(ControlBase control)
{
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);
}
public void AddButtonRow(params ButtonBase[] row)
{
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)
{
var splitted = default(T[][]);
try
{
var t = items.Select((a, index) => new { a, index })
.GroupBy(a => a.index / itemsPerRow)
.Select(a => a.Select(b => b.a).ToArray()).ToArray();
splitted = t;
}
catch
{
return 0;
}
return splitted;
}
public int Count
{
get
{
if (_buttons.Count == 0)
return 0;
return _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>
/// <param name="buttons"></param>
/// <param name="buttonsPerRow"></param>
public void AddSplitted(IEnumerable<ButtonBase> buttons, int buttonsPerRow = 2)
{
var sp = SplitTo(buttons, buttonsPerRow);
foreach (var bl in sp)
{
AddButtonRow(bl);
}
}
/// <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)
{
return _buttons.Skip(start).Take(count).ToList();
}
public List<ButtonBase> ToList()
{
return _buttons.DefaultIfEmpty(new List<ButtonBase>()).Select(a => a.ToList()).Aggregate((a, b) => a.Union(b).ToList());
}
public InlineKeyboardButton[][] ToInlineButtonArray()
{
var ikb = _buttons.Select(a => a.ToArray().Select(b => b.ToInlineButton(this)).ToArray()).ToArray();
return ikb;
}
public KeyboardButton[][] ToReplyButtonArray()
{
var ikb = _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 = _buttons.FirstOrDefault(a => a.ToArray().Count(b => b == button) > 0);
if (row == null)
return -1;
return _buttons.IndexOf(row);
}
public Tuple<ButtonRow, int> FindRow(string text, bool useText = true)
{
var r = _buttons.FirstOrDefault(a => a.Matches(text, useText));
if (r == null)
return null;
var i = _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 ToList().Where(a => a.Value == value).FirstOrDefault();
}
public static implicit operator InlineKeyboardMarkup(ButtonForm form)
{
if (form == null)
return null;
var ikm = new InlineKeyboardMarkup(form.ToInlineButtonArray());
return ikm;
}
public static implicit operator ReplyKeyboardMarkup(ButtonForm form)
{
if (form == null)
return null;
var 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 = Markup,
DependencyControl = 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 = Markup,
DependencyControl = 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;
}
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 = Markup,
DependencyControl = 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;
}
lst.Add(b2);
}
if (lst.Count > 0)
bf._buttons.Add(lst);
}
return bf;
return _buttons.Select(a => a.ToArray()).ToList().Aggregate((a, b) => a.Union(b).ToArray()).Length;
}
}
}
public void AddButtonRow(string text, string value, string url = null)
{
_buttons.Add(new List<ButtonBase> { new(text, value, url) });
}
//public void AddButtonRow(ButtonRow row)
//{
// Buttons.Add(row.ToList());
//}
public void AddButtonRow(ButtonRow row)
{
_buttons.Add(row);
}
public void AddButtonRow(params ButtonBase[] row)
{
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)
{
var splitted = default(T[][]);
try
{
var t = items.Select((a, index) => new { a, index })
.GroupBy(a => a.index / itemsPerRow)
.Select(a => a.Select(b => b.a).ToArray()).ToArray();
splitted = t;
}
catch
{
}
return splitted;
}
/// <summary>
/// Add buttons splitted in the amount of columns (i.e. 2 per row...)
/// </summary>
/// <param name="buttons"></param>
/// <param name="buttonsPerRow"></param>
public void AddSplitted(IEnumerable<ButtonBase> buttons, int buttonsPerRow = 2)
{
var sp = SplitTo(buttons, buttonsPerRow);
foreach (var bl in sp)
{
AddButtonRow(bl);
}
}
/// <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)
{
return _buttons.Skip(start).Take(count).ToList();
}
public List<ButtonBase> ToList()
{
return _buttons.DefaultIfEmpty(new List<ButtonBase>()).Select(a => a.ToList())
.Aggregate((a, b) => a.Union(b).ToList());
}
public InlineKeyboardButton[][] ToInlineButtonArray()
{
var ikb = _buttons.Select(a => a.ToArray().Select(b => b.ToInlineButton(this)).ToArray()).ToArray();
return ikb;
}
public KeyboardButton[][] ToReplyButtonArray()
{
var ikb = _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 = _buttons.FirstOrDefault(a => a.ToArray().Count(b => b == button) > 0);
if (row == null)
{
return -1;
}
return _buttons.IndexOf(row);
}
public Tuple<ButtonRow, int> FindRow(string text, bool useText = true)
{
var r = _buttons.FirstOrDefault(a => a.Matches(text, useText));
if (r == null)
{
return null;
}
var i = _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 ToList().Where(a => a.Value == value).FirstOrDefault();
}
public static implicit operator InlineKeyboardMarkup(ButtonForm form)
{
if (form == null)
{
return null;
}
var ikm = new InlineKeyboardMarkup(form.ToInlineButtonArray());
return ikm;
}
public static implicit operator ReplyKeyboardMarkup(ButtonForm form)
{
if (form == null)
{
return null;
}
var 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 = Markup,
DependencyControl = 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 = Markup,
DependencyControl = 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;
}
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 = Markup,
DependencyControl = 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;
}
lst.Add(b2);
}
if (lst.Count > 0)
{
bf._buttons.Add(lst);
}
}
return bf;
}
}
+62 -71
View File
@@ -1,76 +1,67 @@
using Newtonsoft.Json;
namespace TelegramBotBase.Form
namespace TelegramBotBase.Form;
/// <summary>
/// Base class for serializing buttons and data
/// </summary>
public class CallbackData
{
/// <summary>
/// Base class for serializing buttons and data
/// </summary>
public class CallbackData
public CallbackData()
{
[JsonProperty("m")]
public string Method { get; set; }
[JsonProperty("v")]
public string Value { get; set; }
public CallbackData()
{
}
public CallbackData(string method, string value)
{
Method = method;
Value = value;
}
public static string Create(string method, string value)
{
return new CallbackData(method, value).Serialize();
}
/// <summary>
/// Serializes data to json string
/// </summary>
/// <returns></returns>
public string Serialize()
{
var s = "";
try
{
s = JsonConvert.SerializeObject(this);
}
catch
{
}
return s;
}
/// <summary>
/// Deserializes data from json string
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
public static CallbackData Deserialize(string data)
{
CallbackData cd = null;
try
{
cd = JsonConvert.DeserializeObject<CallbackData>(data);
return cd;
}
catch
{
}
return null;
}
}
}
public CallbackData(string method, string value)
{
Method = method;
Value = value;
}
[JsonProperty("m")] public string Method { get; set; }
[JsonProperty("v")] public string Value { get; set; }
public static string Create(string method, string value)
{
return new CallbackData(method, value).Serialize();
}
/// <summary>
/// Serializes data to json string
/// </summary>
/// <returns></returns>
public string Serialize()
{
var s = "";
try
{
s = JsonConvert.SerializeObject(this);
}
catch
{
}
return s;
}
/// <summary>
/// Deserializes data from json string
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
public static CallbackData Deserialize(string data)
{
CallbackData cd = null;
try
{
cd = JsonConvert.DeserializeObject<CallbackData>(data);
return cd;
}
catch
{
}
return null;
}
}
+108 -104
View File
@@ -1,115 +1,119 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Threading.Tasks;
using TelegramBotBase.Args;
using TelegramBotBase.Attributes;
using TelegramBotBase.Base;
namespace TelegramBotBase.Form
namespace TelegramBotBase.Form;
[IgnoreState]
public class ConfirmDialog : ModalDialog
{
[IgnoreState]
public class ConfirmDialog : ModalDialog
public ConfirmDialog()
{
/// <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 static object EvButtonClicked { get; } = new object();
public ConfirmDialog()
{
}
public ConfirmDialog(string message)
{
this.Message = message;
Buttons = new List<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)
{
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();
var button = Buttons.FirstOrDefault(a => a.Value == call.Value);
if (button == null)
{
return;
}
OnButtonClicked(new ButtonClickedEventArgs(button) { Tag = Tag });
if (AutoCloseOnClick)
await CloseForm();
}
public override async Task Render(MessageResult message)
{
var btn = new ButtonForm();
var buttons = Buttons.Select(a => new ButtonBase(a.Text, CallbackData.Create("action", a.Value))).ToList();
btn.AddButtonRow(buttons);
await Device.Send(Message, btn);
}
public event EventHandler<ButtonClickedEventArgs> ButtonClicked
{
add => Events.AddHandler(EvButtonClicked, value);
remove => Events.RemoveHandler(EvButtonClicked, value);
}
public void OnButtonClicked(ButtonClickedEventArgs e)
{
(Events[EvButtonClicked] as EventHandler<ButtonClickedEventArgs>)?.Invoke(this, e);
}
}
}
public ConfirmDialog(string message)
{
Message = message;
Buttons = new List<ButtonBase>();
}
public ConfirmDialog(string message, params ButtonBase[] buttons)
{
Message = message;
Buttons = buttons.ToList();
}
/// <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 static object EvButtonClicked { get; } = new();
/// <summary>
/// Adds one Button
/// </summary>
/// <param name="button"></param>
public void AddButton(ButtonBase button)
{
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();
var button = Buttons.FirstOrDefault(a => a.Value == call.Value);
if (button == null)
{
return;
}
OnButtonClicked(new ButtonClickedEventArgs(button) { Tag = Tag });
if (AutoCloseOnClick)
{
await CloseForm();
}
}
public override async Task Render(MessageResult message)
{
var btn = new ButtonForm();
var buttons = Buttons.Select(a => new ButtonBase(a.Text, CallbackData.Create("action", a.Value))).ToList();
btn.AddButtonRow(buttons);
await Device.Send(Message, btn);
}
public event EventHandler<ButtonClickedEventArgs> ButtonClicked
{
add => Events.AddHandler(EvButtonClicked, value);
remove => Events.RemoveHandler(EvButtonClicked, value);
}
public void OnButtonClicked(ButtonClickedEventArgs e)
{
(Events[EvButtonClicked] as EventHandler<ButtonClickedEventArgs>)?.Invoke(this, e);
}
}
+25 -28
View File
@@ -1,33 +1,30 @@
using System;
namespace TelegramBotBase.Form
namespace TelegramBotBase.Form;
public class DynamicButton : ButtonBase
{
public class DynamicButton : ButtonBase
private readonly Func<string> _getText;
private string _mText = "";
public DynamicButton(string text, string value, string url = null)
{
public override string Text
{
get => _getText?.Invoke() ?? _mText;
set => _mText = value;
}
private string _mText = "";
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;
}
Text = text;
Value = value;
Url = url;
}
}
public DynamicButton(Func<string> getText, string value, string url = null)
{
_getText = getText;
Value = value;
Url = url;
}
public override string Text
{
get => _getText?.Invoke() ?? _mText;
set => _mText = value;
}
}
+55 -55
View File
@@ -3,73 +3,73 @@ using Telegram.Bot.Types.Enums;
using TelegramBotBase.Args;
using TelegramBotBase.Base;
namespace TelegramBotBase.Form
namespace TelegramBotBase.Form;
public class GroupForm : FormBase
{
public class GroupForm : FormBase
public override async Task Load(MessageResult message)
{
public override async Task Load(MessageResult message)
switch (message.MessageType)
{
switch (message.MessageType)
{
case MessageType.ChatMembersAdded:
case MessageType.ChatMembersAdded:
await OnMemberChanges(new MemberChangeEventArgs(MessageType.ChatMembersAdded, message, message.Message.NewChatMembers));
await OnMemberChanges(new MemberChangeEventArgs(MessageType.ChatMembersAdded, message,
message.Message.NewChatMembers));
break;
case MessageType.ChatMemberLeft:
break;
case MessageType.ChatMemberLeft:
await OnMemberChanges(new MemberChangeEventArgs(MessageType.ChatMemberLeft, message, message.Message.LeftChatMember));
await OnMemberChanges(new MemberChangeEventArgs(MessageType.ChatMemberLeft, message,
message.Message.LeftChatMember));
break;
break;
case MessageType.ChatPhotoChanged:
case MessageType.ChatPhotoDeleted:
case MessageType.ChatTitleChanged:
case MessageType.MigratedFromGroup:
case MessageType.MigratedToSupergroup:
case MessageType.MessagePinned:
case MessageType.GroupCreated:
case MessageType.SupergroupCreated:
case MessageType.ChannelCreated:
case MessageType.ChatPhotoChanged:
case MessageType.ChatPhotoDeleted:
case MessageType.ChatTitleChanged:
case MessageType.MigratedFromGroup:
case MessageType.MigratedToSupergroup:
case MessageType.MessagePinned:
case MessageType.GroupCreated:
case MessageType.SupergroupCreated:
case MessageType.ChannelCreated:
await OnGroupChanged(new GroupChangedEventArgs(message.MessageType, message));
await OnGroupChanged(new GroupChangedEventArgs(message.MessageType, message));
break;
break;
default:
default:
await OnMessage(message);
await OnMessage(message);
break;
}
}
public override async Task Edited(MessageResult message)
{
await OnMessageEdit(message);
}
public virtual Task OnMemberChanges(MemberChangeEventArgs e)
{
return Task.CompletedTask;
}
public virtual Task OnGroupChanged(GroupChangedEventArgs e)
{
return Task.CompletedTask;
}
public virtual Task OnMessage(MessageResult e)
{
return Task.CompletedTask;
}
public virtual Task OnMessageEdit(MessageResult e)
{
return Task.CompletedTask;
break;
}
}
}
public override async Task Edited(MessageResult message)
{
await OnMessageEdit(message);
}
public virtual Task OnMemberChanges(MemberChangeEventArgs e)
{
return Task.CompletedTask;
}
public virtual Task OnGroupChanged(GroupChangedEventArgs e)
{
return Task.CompletedTask;
}
public virtual Task OnMessage(MessageResult e)
{
return Task.CompletedTask;
}
public virtual Task OnMessageEdit(MessageResult e)
{
return Task.CompletedTask;
}
}
+16 -17
View File
@@ -1,26 +1,25 @@
using System;
using System.Threading.Tasks;
namespace TelegramBotBase.Form
namespace TelegramBotBase.Form;
public class ModalDialog : FormBase
{
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()
{
/// <summary>
/// Contains the parent from where the modal dialog has been opened.
/// </summary>
public FormBase ParentForm { get; set; }
await CloseControls();
/// <summary>
/// This is a modal only function and does everything to close this form.
/// </summary>
public async Task CloseForm()
{
await CloseControls();
await OnClosed(EventArgs.Empty);
await OnClosed(EventArgs.Empty);
await ParentForm?.ReturnFromModal(this);
}
await ParentForm?.ReturnFromModal(this);
}
}
}
@@ -10,348 +10,356 @@ using TelegramBotBase.Base;
using TelegramBotBase.Interfaces;
using TelegramBotBase.Tools;
namespace TelegramBotBase.Form.Navigation
namespace TelegramBotBase.Form.Navigation;
[DebuggerDisplay("{Index+1} Forms")]
public class NavigationController : FormBase, IStateForm
{
[DebuggerDisplay("{Index+1} Forms")]
public class NavigationController : FormBase, IStateForm
public NavigationController()
{
History = new List<FormBase>();
Index = -1;
ForceCleanupOnLastPop = true;
[SaveState]
private List<FormBase> History { get; set; }
Init += NavigationController_Init;
Opened += NavigationController_Opened;
Closed += NavigationController_Closed;
}
[SaveState]
public int Index { get; set; }
public NavigationController(FormBase startForm, params FormBase[] forms) : this()
{
Client = startForm.Client;
Device = startForm.Device;
startForm.NavigationController = this;
/// <summary>
/// Will replace the controller when poping a form to the root form.
/// </summary>
[SaveState]
public bool ForceCleanupOnLastPop { get; set; }
History.Add(startForm);
Index = 0;
public NavigationController()
if (forms.Length > 0)
{
History = new List<FormBase>();
Index = -1;
ForceCleanupOnLastPop = true;
Init += NavigationController_Init;
Opened += NavigationController_Opened;
Closed += NavigationController_Closed;
History.AddRange(forms);
Index = History.Count - 1;
}
}
public NavigationController(FormBase startForm, params FormBase[] forms) : this()
{
Client = startForm.Client;
Device = startForm.Device;
startForm.NavigationController = this;
[SaveState] private List<FormBase> History { get; }
History.Add(startForm);
Index = 0;
[SaveState] public int Index { get; set; }
if (forms.Length > 0)
{
History.AddRange(forms);
Index = History.Count - 1;
}
}
/// <summary>
/// Will replace the controller when poping a form to the root form.
/// </summary>
[SaveState]
public bool ForceCleanupOnLastPop { get; set; }
private async Task NavigationController_Init(object sender, 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()
/// <summary>
/// Returns the current form from the stack.
/// </summary>
public FormBase CurrentForm
{
get
{
if (History.Count == 0)
return;
var form = History[Index];
form.NavigationController = null;
History.Remove(form);
Index--;
Device.FormSwitched = true;
await form.OnClosed(EventArgs.Empty);
//Leave NavigationController and move to the last one
if (ForceCleanupOnLastPop && History.Count == 1)
{
var lastForm = History[0];
lastForm.NavigationController = null;
await NavigateTo(lastForm);
return;
return null;
}
if (History.Count > 0)
{
form = History[Index];
await form.OnOpened(EventArgs.Empty);
}
return History[Index];
}
}
public void LoadState(LoadStateEventArgs e)
{
if (e.Get("$controller.history.count") == null)
{
return;
}
/// <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)
var historyCount = e.GetInt("$controller.history.count");
for (var i = 0; i < historyCount; i++)
{
form.Client = Client;
var c = e.GetObject($"$controller.history[{i}]") as Dictionary<string, object>;
var qname = e.Get($"$controller.history[{i}].type");
if (qname == null)
{
continue;
}
var t = Type.GetType(qname);
if (t == null || !t.IsSubclassOf(typeof(FormBase)))
{
continue;
}
//No default constructor, fallback
if (t.GetConstructor(new Type[] { })?.Invoke(new object[] { }) is not FormBase form)
{
continue;
}
var properties = c.Where(a => a.Key.StartsWith("$"));
var fields = form.GetType()
.GetProperties(BindingFlags.Instance | BindingFlags.Public | 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)
{
Conversion.CustomConversionChecks(form, p, f);
}
catch
{
}
}
form.Device = Device;
form.Client = Client;
form.NavigationController = this;
form.OnInit(new InitEventArgs());
History.Add(form);
Index++;
}
}
Device.FormSwitched = true;
public void SaveState(SaveStateEventArgs e)
{
e.Set("$controller.history.count", History.Count.ToString());
if (Index < 2)
return;
var i = 0;
foreach (var form in History)
{
var fields = form.GetType()
.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
.Where(a => a.GetCustomAttributes(typeof(SaveState), true).Length != 0).ToList();
await form.OnInit(new InitEventArgs(args));
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++;
}
}
private async Task NavigationController_Init(object sender, 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(EventArgs.Empty);
//Leave NavigationController and move to the last one
if (ForceCleanupOnLastPop && History.Count == 1)
{
var lastForm = History[0];
lastForm.NavigationController = null;
await NavigateTo(lastForm);
return;
}
if (History.Count > 0)
{
form = History[Index];
await form.OnOpened(EventArgs.Empty);
}
}
/// <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)
/// <summary>
/// Pop's through all forms back to the root form.
/// </summary>
/// <returns></returns>
public virtual async Task PopToRootAsync()
{
while (Index > 0)
{
await PopAsync();
await PushAsync(form, args);
}
/// <summary>
/// Returns the current form from the stack.
/// </summary>
public FormBase CurrentForm
{
get
{
if (History.Count == 0)
return null;
return History[Index];
}
}
public List<FormBase> GetAllForms()
{
return History.ToList();
}
public void LoadState(LoadStateEventArgs e)
{
if (e.Get("$controller.history.count") == null)
return;
var historyCount = e.GetInt("$controller.history.count");
for (var 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;
var t = Type.GetType(qname);
if (t == null || !t.IsSubclassOf(typeof(FormBase)))
{
continue;
}
//No default constructor, fallback
if (t.GetConstructor(new Type[] { })?.Invoke(new object[] { }) is not FormBase form)
{
continue;
}
var properties = c.Where(a => a.Key.StartsWith("$"));
var fields = form.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public | 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)
{
Conversion.CustomConversionChecks(form, p, f);
}
catch
{
}
}
form.Device = Device;
form.Client = Client;
form.NavigationController = this;
form.OnInit(new InitEventArgs());
History.Add(form);
}
}
public void SaveState(SaveStateEventArgs e)
{
e.Set("$controller.history.count", History.Count.ToString());
var i = 0;
foreach (var form in History)
{
var fields = form.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public | 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
}
/// <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 = Client;
form.Device = Device;
form.NavigationController = this;
History.Add(form);
Index++;
Device.FormSwitched = true;
if (Index < 2)
{
return;
}
await form.OnInit(new InitEventArgs(args));
await form.OnOpened(EventArgs.Empty);
}
/// <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);
}
public List<FormBase> GetAllForms()
{
return History.ToList();
}
#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
}
+100 -103
View File
@@ -1,5 +1,4 @@
using System;
using System.ComponentModel;
using System.Threading.Tasks;
using Telegram.Bot.Types;
using Telegram.Bot.Types.ReplyMarkups;
@@ -8,108 +7,106 @@ using TelegramBotBase.Attributes;
using TelegramBotBase.Base;
using TelegramBotBase.Localizations;
namespace TelegramBotBase.Form
namespace TelegramBotBase.Form;
[IgnoreState]
public class PromptDialog : ModalDialog
{
[IgnoreState]
public class PromptDialog : ModalDialog
public PromptDialog()
{
/// <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 static object EvCompleted { get; } = new object();
public bool ShowBackButton { get; set; } = false;
public string BackLabel { get; set; } = Default.Language["PromptDialog_Back"];
/// <summary>
/// Contains the RAW received message.
/// </summary>
public Message ReceivedMessage { get; set; }
public PromptDialog()
{
}
public PromptDialog(string message)
{
this.Message = message;
}
public override async Task Load(MessageResult message)
{
if (message.Handled)
return;
if (!message.IsFirstHandler)
return;
if (ShowBackButton && message.MessageText == BackLabel)
{
await CloseForm();
return;
}
if (Value == null)
{
Value = message.MessageText;
ReceivedMessage = message.Message;
}
}
public override async Task Render(MessageResult message)
{
if (Value == null)
{
if (ShowBackButton)
{
var bf = new ButtonForm();
bf.AddButtonRow(new ButtonBase(BackLabel, "back"));
await Device.Send(Message, (ReplyMarkupBase)bf);
return;
}
await Device.Send(Message);
return;
}
message.Handled = true;
OnCompleted(new PromptDialogCompletedEventArgs { Tag = Tag, Value = Value });
await CloseForm();
}
public event EventHandler<PromptDialogCompletedEventArgs> Completed
{
add => Events.AddHandler(EvCompleted, value);
remove => Events.RemoveHandler(EvCompleted, value);
}
public void OnCompleted(PromptDialogCompletedEventArgs e)
{
(Events[EvCompleted] as EventHandler<PromptDialogCompletedEventArgs>)?.Invoke(this, e);
}
}
}
public PromptDialog(string message)
{
Message = message;
}
/// <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 static object EvCompleted { get; } = new();
public bool ShowBackButton { get; set; } = false;
public string BackLabel { get; set; } = Default.Language["PromptDialog_Back"];
/// <summary>
/// Contains the RAW received message.
/// </summary>
public Message ReceivedMessage { get; set; }
public override async Task Load(MessageResult message)
{
if (message.Handled)
{
return;
}
if (!message.IsFirstHandler)
{
return;
}
if (ShowBackButton && message.MessageText == BackLabel)
{
await CloseForm();
return;
}
if (Value == null)
{
Value = message.MessageText;
ReceivedMessage = message.Message;
}
}
public override async Task Render(MessageResult message)
{
if (Value == null)
{
if (ShowBackButton)
{
var bf = new ButtonForm();
bf.AddButtonRow(new ButtonBase(BackLabel, "back"));
await Device.Send(Message, (ReplyMarkupBase)bf);
return;
}
await Device.Send(Message);
return;
}
message.Handled = true;
OnCompleted(new PromptDialogCompletedEventArgs { Tag = Tag, Value = Value });
await CloseForm();
}
public event EventHandler<PromptDialogCompletedEventArgs> Completed
{
add => Events.AddHandler(EvCompleted, value);
remove => Events.RemoveHandler(EvCompleted, value);
}
public void OnCompleted(PromptDialogCompletedEventArgs e)
{
(Events[EvCompleted] as EventHandler<PromptDialogCompletedEventArgs>)?.Invoke(this, e);
}
}
+76 -79
View File
@@ -2,95 +2,92 @@
using Telegram.Bot.Types.Enums;
using TelegramBotBase.Base;
namespace TelegramBotBase.Form
namespace TelegramBotBase.Form;
/// <summary>
/// This is used to split incomming requests depending on the chat type.
/// </summary>
public class SplitterForm : FormBase
{
/// <summary>
/// This is used to split incomming requests depending on the chat type.
/// </summary>
public class SplitterForm : FormBase
private static object __evOpenSupergroup = new();
private static object __evOpenGroup = new();
private static object __evOpenChannel = new();
private static object __evOpen = new();
public override async Task Load(MessageResult message)
{
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 == ChatType.Channel)
{
if (await OpenChannel(message))
{
return;
}
}
if (message.Message.Chat.Type == ChatType.Channel)
if (message.Message.Chat.Type == ChatType.Supergroup)
{
if (await OpenSupergroup(message))
{
if (await OpenChannel(message))
{
return;
}
}
if (message.Message.Chat.Type == ChatType.Supergroup)
{
if (await OpenSupergroup(message))
{
return;
}
if (await OpenGroup(message))
{
return;
}
}
if (message.Message.Chat.Type == ChatType.Group)
{
if (await OpenGroup(message))
{
return;
}
return;
}
await Open(message);
if (await OpenGroup(message))
{
return;
}
}
public virtual Task<bool> OpenSupergroup(MessageResult e)
if (message.Message.Chat.Type == ChatType.Group)
{
return Task.FromResult(false);
}
public virtual Task<bool> OpenChannel(MessageResult e)
{
return Task.FromResult(false);
}
public virtual Task<bool> Open(MessageResult e)
{
return Task.FromResult(false);
}
public virtual Task<bool> OpenGroup(MessageResult e)
{
return Task.FromResult(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);
if (await OpenGroup(message))
{
return;
}
}
await Open(message);
}
}
public virtual Task<bool> OpenSupergroup(MessageResult e)
{
return Task.FromResult(false);
}
public virtual Task<bool> OpenChannel(MessageResult e)
{
return Task.FromResult(false);
}
public virtual Task<bool> Open(MessageResult e)
{
return Task.FromResult(false);
}
public virtual Task<bool> OpenGroup(MessageResult e)
{
return Task.FromResult(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);
}
}
+41 -45
View File
@@ -1,50 +1,46 @@
using Telegram.Bot.Types.ReplyMarkups;
namespace TelegramBotBase.Form
namespace TelegramBotBase.Form;
/// <summary>
/// Base class for button handling
/// </summary>
public class TagButtonBase : ButtonBase
{
/// <summary>
/// Base class for button handling
/// </summary>
public class TagButtonBase : ButtonBase
public TagButtonBase()
{
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)
{
var id = (form.DependencyControl != null ? form.DependencyControl.ControlId + "_" : "");
return InlineKeyboardButton.WithCallbackData(Text, id + Value);
}
/// <summary>
/// Returns a KeyBoardButton
/// </summary>
/// <param name="form"></param>
/// <returns></returns>
public override KeyboardButton ToKeyboardButton(ButtonForm form)
{
return new KeyboardButton(Text);
}
}
}
public TagButtonBase(string text, string value, string tag)
{
Text = text;
Value = value;
Tag = tag;
}
public string Tag { get; set; }
/// <summary>
/// Returns an inline Button
/// </summary>
/// <param name="form"></param>
/// <returns></returns>
public override InlineKeyboardButton ToInlineButton(ButtonForm form)
{
var id = form.DependencyControl != null ? form.DependencyControl.ControlId + "_" : "";
return InlineKeyboardButton.WithCallbackData(Text, id + Value);
}
/// <summary>
/// Returns a KeyBoardButton
/// </summary>
/// <param name="form"></param>
/// <returns></returns>
public override KeyboardButton ToKeyboardButton(ButtonForm form)
{
return new KeyboardButton(Text);
}
}