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
+30 -34
View File
@@ -3,40 +3,36 @@ using System.Collections.Generic;
using Telegram.Bot.Types;
using TelegramBotBase.Sessions;
namespace TelegramBotBase.Args
namespace TelegramBotBase.Args;
/// <summary>
/// Base class for given bot command results
/// </summary>
public class BotCommandEventArgs : EventArgs
{
/// <summary>
/// Base class for given bot command results
/// </summary>
public class BotCommandEventArgs : EventArgs
public BotCommandEventArgs()
{
public string Command { get; set; }
public List<string> Parameters { get; set; }
public long DeviceId { get; set; }
public DeviceSession Device { get; set; }
public bool Handled { get; set; } = false;
public Message OriginalMessage { get; set; }
public BotCommandEventArgs()
{
}
public BotCommandEventArgs(string command, List<string> parameters, Message message, long deviceId, DeviceSession device)
{
this.Command = command;
this.Parameters = parameters;
OriginalMessage = message;
this.DeviceId = deviceId;
this.Device = device;
}
}
}
public BotCommandEventArgs(string command, List<string> parameters, Message message, long deviceId,
DeviceSession device)
{
Command = command;
Parameters = parameters;
OriginalMessage = message;
DeviceId = deviceId;
Device = device;
}
public string Command { get; set; }
public List<string> Parameters { get; set; }
public long DeviceId { get; set; }
public DeviceSession Device { get; set; }
public bool Handled { get; set; } = false;
public Message OriginalMessage { get; set; }
}
+35 -38
View File
@@ -2,44 +2,41 @@
using TelegramBotBase.Controls.Hybrid;
using TelegramBotBase.Form;
namespace TelegramBotBase.Args
namespace TelegramBotBase.Args;
/// <summary>
/// Button get clicked event
/// </summary>
public class ButtonClickedEventArgs : EventArgs
{
/// <summary>
/// Button get clicked event
/// </summary>
public class ButtonClickedEventArgs : EventArgs
public ButtonClickedEventArgs()
{
public ButtonBase Button { get; set; }
public int Index { get; set; }
public object Tag { get; set; }
public ButtonRow Row { get; set; }
public ButtonClickedEventArgs()
{
}
public ButtonClickedEventArgs(ButtonBase button)
{
Button = button;
Index = -1;
}
public ButtonClickedEventArgs(ButtonBase button, int index)
{
Button = button;
this.Index = index;
}
public ButtonClickedEventArgs(ButtonBase button, int index, ButtonRow row)
{
Button = button;
this.Index = index;
Row = row;
}
}
}
public ButtonClickedEventArgs(ButtonBase button)
{
Button = button;
Index = -1;
}
public ButtonClickedEventArgs(ButtonBase button, int index)
{
Button = button;
Index = index;
}
public ButtonClickedEventArgs(ButtonBase button, int index, ButtonRow row)
{
Button = button;
Index = index;
Row = row;
}
public ButtonBase Button { get; set; }
public int Index { get; set; }
public object Tag { get; set; }
public ButtonRow Row { get; set; }
}
+30 -35
View File
@@ -1,41 +1,36 @@
using System;
using TelegramBotBase.Controls.Hybrid;
namespace TelegramBotBase.Args
namespace TelegramBotBase.Args;
public class CheckedChangedEventArgs : EventArgs
{
public class CheckedChangedEventArgs : EventArgs
public CheckedChangedEventArgs()
{
/// <summary>
/// Contains the index of the row where the button is inside.
/// Contains -1 when it is a layout button or not found.
/// </summary>
public int Index { get; set; }
/// <summary>
/// Contains all buttons within this row, excluding the checkbox.
/// </summary>
public ButtonRow Row { get; set; }
/// <summary>
/// Contains the new checked status of the row.
/// </summary>
public bool Checked { get; set; }
public CheckedChangedEventArgs()
{
}
public CheckedChangedEventArgs(ButtonRow row, int index, bool @checked)
{
Row = row;
this.Index = index;
this.Checked = @checked;
}
}
}
public CheckedChangedEventArgs(ButtonRow row, int index, bool @checked)
{
Row = row;
Index = index;
Checked = @checked;
}
/// <summary>
/// Contains the index of the row where the button is inside.
/// Contains -1 when it is a layout button or not found.
/// </summary>
public int Index { get; set; }
/// <summary>
/// Contains all buttons within this row, excluding the checkbox.
/// </summary>
public ButtonRow Row { get; set; }
/// <summary>
/// Contains the new checked status of the row.
/// </summary>
public bool Checked { get; set; }
}
+11 -15
View File
@@ -2,21 +2,17 @@
using Telegram.Bot.Types.Enums;
using TelegramBotBase.Base;
namespace TelegramBotBase.Args
namespace TelegramBotBase.Args;
public class GroupChangedEventArgs : EventArgs
{
public class GroupChangedEventArgs : EventArgs
public GroupChangedEventArgs(MessageType type, MessageResult message)
{
public MessageType Type { get; set; }
public MessageResult OriginalMessage { get; set; }
public GroupChangedEventArgs(MessageType type, MessageResult message)
{
Type = type;
OriginalMessage = message;
}
Type = type;
OriginalMessage = message;
}
}
public MessageType Type { get; set; }
public MessageResult OriginalMessage { get; set; }
}
+9 -10
View File
@@ -1,14 +1,13 @@
using System;
namespace TelegramBotBase.Args
{
public class InitEventArgs : EventArgs
{
public object[] Args { get; set; }
namespace TelegramBotBase.Args;
public InitEventArgs(params object[] args)
{
Args = args;
}
public class InitEventArgs : EventArgs
{
public InitEventArgs(params object[] args)
{
Args = args;
}
}
public object[] Args { get; set; }
}
+53 -49
View File
@@ -1,55 +1,59 @@
using System.Collections.Generic;
using System.Linq;
namespace TelegramBotBase.Args
namespace TelegramBotBase.Args;
public class LoadStateEventArgs
{
public class LoadStateEventArgs
public LoadStateEventArgs()
{
public Dictionary<string,object> Values { get; set; }
public LoadStateEventArgs()
{
Values = new Dictionary<string, object>();
}
public List<string> Keys => Values.Keys.ToList();
public string Get(string key)
{
return Values[key].ToString();
}
public int GetInt(string key)
{
var i = 0;
if (int.TryParse(Values[key].ToString(), out i))
return i;
return 0;
}
public double GetDouble(string key)
{
double d = 0;
if (double.TryParse(Values[key].ToString(), out d))
return d;
return 0;
}
public bool GetBool(string key)
{
var b = false;
if (bool.TryParse(Values[key].ToString(), out b))
return b;
return false;
}
public object GetObject(string key)
{
return Values[key];
}
Values = new Dictionary<string, object>();
}
}
public Dictionary<string, object> Values { get; set; }
public List<string> Keys => Values.Keys.ToList();
public string Get(string key)
{
return Values[key].ToString();
}
public int GetInt(string key)
{
var i = 0;
if (int.TryParse(Values[key].ToString(), out i))
{
return i;
}
return 0;
}
public double GetDouble(string key)
{
double d = 0;
if (double.TryParse(Values[key].ToString(), out d))
{
return d;
}
return 0;
}
public bool GetBool(string key)
{
var b = false;
if (bool.TryParse(Values[key].ToString(), out b))
{
return b;
}
return false;
}
public object GetObject(string key)
{
return Values[key];
}
}
+19 -26
View File
@@ -5,32 +5,25 @@ using Telegram.Bot.Types;
using Telegram.Bot.Types.Enums;
using TelegramBotBase.Base;
namespace TelegramBotBase.Args
namespace TelegramBotBase.Args;
public class MemberChangeEventArgs : EventArgs
{
public class MemberChangeEventArgs : EventArgs
public MemberChangeEventArgs()
{
public List<User> Members { get; set; }
public MessageType Type { get; set; }
public MessageResult Result { get; set; }
public MemberChangeEventArgs()
{
Members = new List<User>();
}
public MemberChangeEventArgs(MessageType type, MessageResult result, params User[] members)
{
Type = type;
Result = result;
Members = members.ToList();
}
Members = new List<User>();
}
}
public MemberChangeEventArgs(MessageType type, MessageResult result, params User[] members)
{
Type = type;
Result = result;
Members = members.ToList();
}
public List<User> Members { get; set; }
public MessageType Type { get; set; }
public MessageResult Result { get; set; }
}
@@ -1,16 +1,11 @@
namespace TelegramBotBase.Args
namespace TelegramBotBase.Args;
public class MessageDeletedEventArgs
{
public class MessageDeletedEventArgs
public MessageDeletedEventArgs(int messageId)
{
public int MessageId
{
get;set;
}
public MessageDeletedEventArgs(int messageId)
{
MessageId = messageId;
}
MessageId = messageId;
}
}
public int MessageId { get; set; }
}
+14 -20
View File
@@ -1,26 +1,20 @@
using System;
using TelegramBotBase.Sessions;
namespace TelegramBotBase.Base
namespace TelegramBotBase.Base;
public class MessageIncomeEventArgs : EventArgs
{
public class MessageIncomeEventArgs : EventArgs
public MessageIncomeEventArgs(long deviceId, DeviceSession device, MessageResult message)
{
public long DeviceId { get; set; }
public DeviceSession Device { get; set; }
public MessageResult Message { get; set; }
public MessageIncomeEventArgs(long deviceId, DeviceSession device, MessageResult message)
{
this.DeviceId = deviceId;
this.Device = device;
Message = message;
}
DeviceId = deviceId;
Device = device;
Message = message;
}
}
public long DeviceId { get; set; }
public DeviceSession Device { get; set; }
public MessageResult Message { get; set; }
}
@@ -1,17 +1,15 @@
using Telegram.Bot.Types;
namespace TelegramBotBase.Args
namespace TelegramBotBase.Args;
public class MessageReceivedEventArgs
{
public class MessageReceivedEventArgs
public MessageReceivedEventArgs(Message m)
{
public int MessageId => Message.MessageId;
public Message Message { get; set; }
public MessageReceivedEventArgs(Message m)
{
Message = m;
}
Message = m;
}
}
public int MessageId => Message.MessageId;
public Message Message { get; set; }
}
+16 -20
View File
@@ -1,26 +1,22 @@
using System;
using Telegram.Bot.Types;
namespace TelegramBotBase.Args
namespace TelegramBotBase.Args;
public class MessageSentEventArgs : EventArgs
{
public class MessageSentEventArgs : EventArgs
public MessageSentEventArgs(Message message, Type origin)
{
public int MessageId => Message.MessageId;
public Message Message { get; set; }
/// <summary>
/// Contains the element, which has called the method.
/// </summary>
public Type Origin { get; set; }
public MessageSentEventArgs(Message message, Type origin)
{
Message = message;
this.Origin = origin;
}
Message = message;
Origin = origin;
}
}
public int MessageId => Message.MessageId;
public Message Message { get; set; }
/// <summary>
/// Contains the element, which has called the method.
/// </summary>
public Type Origin { get; set; }
}
@@ -1,10 +1,8 @@
namespace TelegramBotBase.Args
namespace TelegramBotBase.Args;
public class PromptDialogCompletedEventArgs
{
public class PromptDialogCompletedEventArgs
{
public object Tag { get; set; }
public object Tag { get; set; }
public string Value { get; set; }
}
}
public string Value { get; set; }
}
+8 -13
View File
@@ -1,18 +1,13 @@
using System;
namespace TelegramBotBase.Args
namespace TelegramBotBase.Args;
public class RenderViewEventArgs : EventArgs
{
public class RenderViewEventArgs : EventArgs
public RenderViewEventArgs(int viewIndex)
{
public int CurrentView { get; set; }
public RenderViewEventArgs(int viewIndex)
{
CurrentView = viewIndex;
}
CurrentView = viewIndex;
}
}
public int CurrentView { get; set; }
}
+33 -34
View File
@@ -1,39 +1,38 @@
using System.Collections.Generic;
namespace TelegramBotBase.Args
namespace TelegramBotBase.Args;
public class SaveStateEventArgs
{
public class SaveStateEventArgs
public SaveStateEventArgs()
{
public Dictionary<string, object> Values { get; set; }
public SaveStateEventArgs()
{
Values = new Dictionary<string, object>();
}
public void Set(string key, string value)
{
Values[key] = value;
}
public void SetInt(string key, int value)
{
Values[key] = value;
}
public void SetBool(string key, bool value)
{
Values[key] = value;
}
public void SetDouble(string key, double value)
{
Values[key] = value;
}
public void SetObject(string key, object value)
{
Values[key] = value;
}
Values = new Dictionary<string, object>();
}
}
public Dictionary<string, object> Values { get; set; }
public void Set(string key, string value)
{
Values[key] = value;
}
public void SetInt(string key, int value)
{
Values[key] = value;
}
public void SetBool(string key, bool value)
{
Values[key] = value;
}
public void SetDouble(string key, double value)
{
Values[key] = value;
}
public void SetObject(string key, object value)
{
Values[key] = value;
}
}
+8 -10
View File
@@ -1,15 +1,13 @@
using TelegramBotBase.Base;
namespace TelegramBotBase.Args
namespace TelegramBotBase.Args;
public class SaveStatesEventArgs
{
public class SaveStatesEventArgs
public SaveStatesEventArgs(StateContainer states)
{
public StateContainer States { get; set; }
public SaveStatesEventArgs(StateContainer states)
{
States = states;
}
States = states;
}
}
public StateContainer States { get; set; }
}
+11 -12
View File
@@ -1,18 +1,17 @@
using System;
using TelegramBotBase.Sessions;
namespace TelegramBotBase.Base
namespace TelegramBotBase.Base;
public class SessionBeginEventArgs : EventArgs
{
public class SessionBeginEventArgs : EventArgs
public SessionBeginEventArgs(long deviceId, DeviceSession device)
{
public long DeviceId { get; set; }
public DeviceSession Device { get; set; }
public SessionBeginEventArgs(long deviceId, DeviceSession device)
{
this.DeviceId = deviceId;
this.Device = device;
}
DeviceId = deviceId;
Device = device;
}
}
public long DeviceId { get; set; }
public DeviceSession Device { get; set; }
}
@@ -1,32 +1,27 @@
using System;
using TelegramBotBase.Sessions;
namespace TelegramBotBase.Args
namespace TelegramBotBase.Args;
public class SystemExceptionEventArgs : EventArgs
{
public class SystemExceptionEventArgs : EventArgs
public SystemExceptionEventArgs()
{
public string Command { get; set; }
public long DeviceId { get; set; }
public DeviceSession Device { get; set; }
public Exception Error { get; set; }
public SystemExceptionEventArgs()
{
}
public SystemExceptionEventArgs(string command, long deviceId, DeviceSession device, Exception error)
{
this.Command = command;
this.DeviceId = deviceId;
this.Device = device;
Error = error;
}
}
}
public SystemExceptionEventArgs(string command, long deviceId, DeviceSession device, Exception error)
{
Command = command;
DeviceId = deviceId;
Device = device;
Error = error;
}
public string Command { get; set; }
public long DeviceId { get; set; }
public DeviceSession Device { get; set; }
public Exception Error { get; set; }
}
+31 -34
View File
@@ -2,40 +2,37 @@
using Telegram.Bot.Types;
using TelegramBotBase.Sessions;
namespace TelegramBotBase.Args
namespace TelegramBotBase.Args;
public class UnhandledCallEventArgs : EventArgs
{
public class UnhandledCallEventArgs : EventArgs
public UnhandledCallEventArgs()
{
public string Command { get; set; }
public long DeviceId { get; set; }
public DeviceSession Device {get;set;}
public string RawData { get; set; }
public int MessageId { get; set; }
public Message Message { get; set; }
public bool Handled { get; set; }
public UnhandledCallEventArgs()
{
Handled = false;
}
public UnhandledCallEventArgs(string command,string rawData, long deviceId, int messageId, Message message, DeviceSession device) : this()
{
this.Command = command;
this.RawData = rawData;
this.DeviceId = deviceId;
this.MessageId = messageId;
Message = message;
this.Device = device;
}
Handled = false;
}
}
public UnhandledCallEventArgs(string command, string rawData, long deviceId, int messageId, Message message,
DeviceSession device) : this()
{
Command = command;
RawData = rawData;
DeviceId = deviceId;
MessageId = messageId;
Message = message;
Device = device;
}
public string Command { get; set; }
public long DeviceId { get; set; }
public DeviceSession Device { get; set; }
public string RawData { get; set; }
public int MessageId { get; set; }
public Message Message { get; set; }
public bool Handled { get; set; }
}
+8 -12
View File
@@ -1,15 +1,11 @@
using System;
namespace TelegramBotBase.Attributes
namespace TelegramBotBase.Attributes;
/// <summary>
/// Declares that this class should not be getting serialized
/// </summary>
[AttributeUsage(AttributeTargets.Class)]
public class IgnoreState : Attribute
{
/// <summary>
/// Declares that this class should not be getting serialized
/// </summary>
[AttributeUsage(AttributeTargets.Class)]
public class IgnoreState : Attribute
{
}
}
}
+8 -10
View File
@@ -1,13 +1,11 @@
using System;
namespace TelegramBotBase.Attributes
{
/// <summary>
/// Declares that the field or property should be save and recovered an restart.
/// </summary>
public class SaveState : Attribute
{
public string Key { get; set; }
namespace TelegramBotBase.Attributes;
}
}
/// <summary>
/// Declares that the field or property should be save and recovered an restart.
/// </summary>
public class SaveState : Attribute
{
public string Key { get; set; }
}
+18 -15
View File
@@ -3,22 +3,25 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace TelegramBotBase.Base
{
public static class Async
{
public delegate Task AsyncEventHandler<TEventArgs>(object sender, TEventArgs e) where TEventArgs : EventArgs;
namespace TelegramBotBase.Base;
public static IEnumerable<AsyncEventHandler<TEventArgs>> GetHandlers<TEventArgs>(
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)));
{
return handler.GetInvocationList().Cast<AsyncEventHandler<TEventArgs>>();
}
}
public static Task InvokeAllAsync<TEventArgs>(this AsyncEventHandler<TEventArgs> handler, object sender,
TEventArgs e)
where TEventArgs : EventArgs
{
return Task.WhenAll(
handler.GetHandlers()
.Select(handleAsync => handleAsync(sender, e)));
}
}
+51 -55
View File
@@ -1,63 +1,59 @@
using System.Threading.Tasks;
using TelegramBotBase.Sessions;
namespace TelegramBotBase.Base
namespace TelegramBotBase.Base;
/// <summary>
/// Base class for controls
/// </summary>
public class ControlBase
{
public DeviceSession Device { get; set; }
public int Id { get; set; }
public string ControlId => "#c" + Id;
/// <summary>
/// Base class for controls
/// Defines if the control should be rendered and invoked with actions
/// </summary>
public class ControlBase
public bool Enabled { get; set; } = true;
/// <summary>
/// Get invoked when control will be added to a form and invoked.
/// </summary>
/// <returns></returns>
public virtual void Init()
{
public DeviceSession Device { get; set; }
public int Id { get; set; }
public string ControlId => "#c" + Id;
/// <summary>
/// Defines if the control should be rendered and invoked with actions
/// </summary>
public bool Enabled { get; set; } = true;
/// <summary>
/// Get invoked when control will be added to a form and invoked.
/// </summary>
/// <returns></returns>
public virtual void Init()
{
}
public virtual Task Load(MessageResult result)
{
return Task.CompletedTask;
}
public virtual Task Action(MessageResult result, string value = null)
{
return Task.CompletedTask;
}
public virtual Task Render(MessageResult result)
{
return Task.CompletedTask;
}
public virtual Task Hidden(bool formClose)
{
return Task.CompletedTask;
}
/// <summary>
/// Will be called on a cleanup.
/// </summary>
/// <returns></returns>
public virtual Task Cleanup()
{
return Task.CompletedTask;
}
}
}
public virtual Task Load(MessageResult result)
{
return Task.CompletedTask;
}
public virtual Task Action(MessageResult result, string value = null)
{
return Task.CompletedTask;
}
public virtual Task Render(MessageResult result)
{
return Task.CompletedTask;
}
public virtual Task Hidden(bool formClose)
{
return Task.CompletedTask;
}
/// <summary>
/// Will be called on a cleanup.
/// </summary>
/// <returns></returns>
public virtual Task Cleanup()
{
return Task.CompletedTask;
}
}
+158 -160
View File
@@ -7,166 +7,164 @@ using Telegram.Bot.Types;
using Telegram.Bot.Types.Enums;
using Telegram.Bot.Types.InputFiles;
namespace TelegramBotBase.Base
namespace TelegramBotBase.Base;
/// <summary>
/// Returns a class to manage attachments within messages.
/// </summary>
public class DataResult : ResultBase
{
/// <summary>
/// Returns a class to manage attachments within messages.
/// </summary>
public class DataResult : ResultBase
public DataResult(UpdateResult update)
{
//public Telegram.Bot.Args.MessageEventArgs RawMessageData { get; set; }
public UpdateResult UpdateData { get; set; }
public Contact Contact => Message.Contact;
public Location Location => Message.Location;
public Document Document => Message.Document;
public Audio Audio => Message.Audio;
public Video Video => Message.Video;
public PhotoSize[] Photos => Message.Photo;
public MessageType Type => Message?.Type ?? MessageType.Unknown;
public override Message Message => UpdateData?.Message;
/// <summary>
/// Returns the FileId of the first reachable element.
/// </summary>
public string FileId =>
(Document?.FileId ??
Audio?.FileId ??
Video?.FileId ??
Photos.FirstOrDefault()?.FileId);
public DataResult(UpdateResult update)
{
UpdateData = update;
}
public async Task<InputOnlineFile> DownloadDocument()
{
var encryptedContent = new MemoryStream();
encryptedContent.SetLength(Document.FileSize.Value);
var file = await Device.Client.TelegramClient.GetInfoAndDownloadFileAsync(Document.FileId, encryptedContent);
return new InputOnlineFile(encryptedContent, Document.FileName);
}
/// <summary>
/// Downloads a file and saves it to the given path.
/// </summary>
/// <param name="path"></param>
/// <returns></returns>
public async Task DownloadDocument(string path)
{
var file = await Device.Client.TelegramClient.GetFileAsync(Document.FileId);
var fs = new FileStream(path, FileMode.Create);
await Device.Client.TelegramClient.DownloadFileAsync(file.FilePath, fs);
fs.Close();
fs.Dispose();
}
/// <summary>
/// Downloads the document and returns an byte array.
/// </summary>
/// <returns></returns>
public async Task<byte[]> DownloadRawDocument()
{
var ms = new MemoryStream();
await Device.Client.TelegramClient.GetInfoAndDownloadFileAsync(Document.FileId, ms);
return ms.ToArray();
}
/// <summary>
/// Downloads a document and returns it as string. (txt,csv,etc) Default encoding ist UTF8.
/// </summary>
/// <returns></returns>
public async Task<string> DownloadRawTextDocument()
{
return await DownloadRawTextDocument(Encoding.UTF8);
}
/// <summary>
/// Downloads a document and returns it as string. (txt,csv,etc)
/// </summary>
/// <returns></returns>
public async Task<string> DownloadRawTextDocument(Encoding encoding)
{
var ms = new MemoryStream();
await Device.Client.TelegramClient.GetInfoAndDownloadFileAsync(Document.FileId, ms);
ms.Position = 0;
var sr = new StreamReader(ms, encoding);
return sr.ReadToEnd();
}
public async Task<InputOnlineFile> DownloadVideo()
{
var encryptedContent = new MemoryStream();
encryptedContent.SetLength(Video.FileSize.Value);
var file = await Device.Client.TelegramClient.GetInfoAndDownloadFileAsync(Video.FileId, encryptedContent);
return new InputOnlineFile(encryptedContent, "");
}
public async Task DownloadVideo(string path)
{
var file = await Device.Client.TelegramClient.GetFileAsync(Video.FileId);
var fs = new FileStream(path, FileMode.Create);
await Device.Client.TelegramClient.DownloadFileAsync(file.FilePath, fs);
fs.Close();
fs.Dispose();
}
public async Task<InputOnlineFile> DownloadAudio()
{
var encryptedContent = new MemoryStream();
encryptedContent.SetLength(Audio.FileSize.Value);
var file = await Device.Client.TelegramClient.GetInfoAndDownloadFileAsync(Audio.FileId, encryptedContent);
return new InputOnlineFile(encryptedContent, "");
}
public async Task DownloadAudio(string path)
{
var file = await Device.Client.TelegramClient.GetFileAsync(Audio.FileId);
var fs = new FileStream(path, FileMode.Create);
await Device.Client.TelegramClient.DownloadFileAsync(file.FilePath, fs);
fs.Close();
fs.Dispose();
}
public async Task<InputOnlineFile> DownloadPhoto(int index)
{
var photo = Photos[index];
var encryptedContent = new MemoryStream();
encryptedContent.SetLength(photo.FileSize.Value);
var file = await Device.Client.TelegramClient.GetInfoAndDownloadFileAsync(photo.FileId, encryptedContent);
return new InputOnlineFile(encryptedContent, "");
}
public async Task DownloadPhoto(int index, string path)
{
var photo = Photos[index];
var file = await Device.Client.TelegramClient.GetFileAsync(photo.FileId);
var fs = new FileStream(path, FileMode.Create);
await Device.Client.TelegramClient.DownloadFileAsync(file.FilePath, fs);
fs.Close();
fs.Dispose();
}
UpdateData = update;
}
}
//public Telegram.Bot.Args.MessageEventArgs RawMessageData { get; set; }
public UpdateResult UpdateData { get; set; }
public Contact Contact => Message.Contact;
public Location Location => Message.Location;
public Document Document => Message.Document;
public Audio Audio => Message.Audio;
public Video Video => Message.Video;
public PhotoSize[] Photos => Message.Photo;
public MessageType Type => Message?.Type ?? MessageType.Unknown;
public override Message Message => UpdateData?.Message;
/// <summary>
/// Returns the FileId of the first reachable element.
/// </summary>
public string FileId =>
Document?.FileId ??
Audio?.FileId ??
Video?.FileId ??
Photos.FirstOrDefault()?.FileId;
public async Task<InputOnlineFile> DownloadDocument()
{
var encryptedContent = new MemoryStream();
encryptedContent.SetLength(Document.FileSize.Value);
var file = await Device.Client.TelegramClient.GetInfoAndDownloadFileAsync(Document.FileId,
encryptedContent);
return new InputOnlineFile(encryptedContent, Document.FileName);
}
/// <summary>
/// Downloads a file and saves it to the given path.
/// </summary>
/// <param name="path"></param>
/// <returns></returns>
public async Task DownloadDocument(string path)
{
var file = await Device.Client.TelegramClient.GetFileAsync(Document.FileId);
var fs = new FileStream(path, FileMode.Create);
await Device.Client.TelegramClient.DownloadFileAsync(file.FilePath, fs);
fs.Close();
fs.Dispose();
}
/// <summary>
/// Downloads the document and returns an byte array.
/// </summary>
/// <returns></returns>
public async Task<byte[]> DownloadRawDocument()
{
var ms = new MemoryStream();
await Device.Client.TelegramClient.GetInfoAndDownloadFileAsync(Document.FileId, ms);
return ms.ToArray();
}
/// <summary>
/// Downloads a document and returns it as string. (txt,csv,etc) Default encoding ist UTF8.
/// </summary>
/// <returns></returns>
public async Task<string> DownloadRawTextDocument()
{
return await DownloadRawTextDocument(Encoding.UTF8);
}
/// <summary>
/// Downloads a document and returns it as string. (txt,csv,etc)
/// </summary>
/// <returns></returns>
public async Task<string> DownloadRawTextDocument(Encoding encoding)
{
var ms = new MemoryStream();
await Device.Client.TelegramClient.GetInfoAndDownloadFileAsync(Document.FileId, ms);
ms.Position = 0;
var sr = new StreamReader(ms, encoding);
return sr.ReadToEnd();
}
public async Task<InputOnlineFile> DownloadVideo()
{
var encryptedContent = new MemoryStream();
encryptedContent.SetLength(Video.FileSize.Value);
var file = await Device.Client.TelegramClient.GetInfoAndDownloadFileAsync(Video.FileId, encryptedContent);
return new InputOnlineFile(encryptedContent, "");
}
public async Task DownloadVideo(string path)
{
var file = await Device.Client.TelegramClient.GetFileAsync(Video.FileId);
var fs = new FileStream(path, FileMode.Create);
await Device.Client.TelegramClient.DownloadFileAsync(file.FilePath, fs);
fs.Close();
fs.Dispose();
}
public async Task<InputOnlineFile> DownloadAudio()
{
var encryptedContent = new MemoryStream();
encryptedContent.SetLength(Audio.FileSize.Value);
var file = await Device.Client.TelegramClient.GetInfoAndDownloadFileAsync(Audio.FileId, encryptedContent);
return new InputOnlineFile(encryptedContent, "");
}
public async Task DownloadAudio(string path)
{
var file = await Device.Client.TelegramClient.GetFileAsync(Audio.FileId);
var fs = new FileStream(path, FileMode.Create);
await Device.Client.TelegramClient.DownloadFileAsync(file.FilePath, fs);
fs.Close();
fs.Dispose();
}
public async Task<InputOnlineFile> DownloadPhoto(int index)
{
var photo = Photos[index];
var encryptedContent = new MemoryStream();
encryptedContent.SetLength(photo.FileSize.Value);
var file = await Device.Client.TelegramClient.GetInfoAndDownloadFileAsync(photo.FileId, encryptedContent);
return new InputOnlineFile(encryptedContent, "");
}
public async Task DownloadPhoto(int index, string path)
{
var photo = Photos[index];
var file = await Device.Client.TelegramClient.GetFileAsync(photo.FileId);
var fs = new FileStream(path, FileMode.Create);
await Device.Client.TelegramClient.DownloadFileAsync(file.FilePath, fs);
fs.Close();
fs.Dispose();
}
}
+424 -403
View File
@@ -9,421 +9,442 @@ using TelegramBotBase.Form.Navigation;
using TelegramBotBase.Sessions;
using static TelegramBotBase.Base.Async;
namespace TelegramBotBase.Form
namespace TelegramBotBase.Form;
/// <summary>
/// Base class for forms
/// </summary>
public class FormBase : IDisposable
{
/// <summary>
/// Base class for forms
/// </summary>
public class FormBase : IDisposable
private static readonly object EvInit = new();
private static readonly object EvOpened = new();
private static readonly object EvClosed = new();
public EventHandlerList Events = new();
public FormBase()
{
Controls = new List<ControlBase>();
}
public NavigationController NavigationController { get; set; }
public FormBase(MessageClient client) : this()
{
Client = client;
}
public DeviceSession Device { get; set; }
public NavigationController NavigationController { get; set; }
public MessageClient Client { get; set; }
public DeviceSession Device { get; set; }
/// <summary>
/// has this formular already been disposed ?
/// </summary>
public bool IsDisposed { get; set; }
public MessageClient Client { get; set; }
public List<ControlBase> Controls { get; set; }
/// <summary>
/// has this formular already been disposed ?
/// </summary>
public bool IsDisposed { get; set; }
public List<ControlBase> Controls { get; set; }
/// <summary>
/// Cleanup
/// </summary>
public void Dispose()
{
Client = null;
Device = null;
IsDisposed = true;
}
public EventHandlerList Events = new EventHandlerList();
private static readonly object EvInit = new object();
private static readonly object EvOpened = new object();
private static readonly object EvClosed = new object();
public FormBase()
public async Task OnInit(InitEventArgs e)
{
var handler = Events[EvInit]?.GetInvocationList().Cast<AsyncEventHandler<InitEventArgs>>();
if (handler == null)
{
Controls = new List<ControlBase>();
return;
}
public FormBase(MessageClient client) : this()
foreach (var h in handler)
{
this.Client = client;
}
public async Task OnInit(InitEventArgs e)
{
var handler = Events[EvInit]?.GetInvocationList().Cast<AsyncEventHandler<InitEventArgs>>();
if (handler == null)
return;
foreach (var h in handler)
{
await h.InvokeAllAsync(this, e);
}
}
///// <summary>
///// Will get called at the initialization (once per context)
///// </summary>
public event AsyncEventHandler<InitEventArgs> Init
{
add => Events.AddHandler(EvInit, value);
remove => Events.RemoveHandler(EvInit, value);
}
public async Task OnOpened(EventArgs e)
{
var handler = Events[EvOpened]?.GetInvocationList().Cast<AsyncEventHandler<EventArgs>>();
if (handler == null)
return;
foreach (var h in handler)
{
await h.InvokeAllAsync(this, e);
}
}
/// <summary>
/// Gets invoked if gets navigated to this form
/// </summary>
/// <returns></returns>
public event AsyncEventHandler<EventArgs> Opened
{
add => Events.AddHandler(EvOpened, value);
remove => Events.RemoveHandler(EvOpened, value);
}
public async Task OnClosed(EventArgs e)
{
var handler = Events[EvClosed]?.GetInvocationList().Cast<AsyncEventHandler<EventArgs>>();
if (handler == null)
return;
foreach (var h in handler)
{
await h.InvokeAllAsync(this, e);
}
}
/// <summary>
/// Form has been closed (left)
/// </summary>
/// <returns></returns>
public event AsyncEventHandler<EventArgs> Closed
{
add => Events.AddHandler(EvClosed, value);
remove => Events.RemoveHandler(EvClosed, value);
}
/// <summary>
/// Get invoked when a modal child from has been closed.
/// </summary>
/// <param name="message"></param>
/// <returns></returns>
public virtual Task ReturnFromModal(ModalDialog modal)
{
return Task.CompletedTask;
}
/// <summary>
/// Pre to form close, cleanup all controls
/// </summary>
/// <returns></returns>
public Task CloseControls()
{
foreach (var b in Controls)
{
b.Cleanup().Wait();
}
return Task.CompletedTask;
}
public virtual Task PreLoad(MessageResult message)
{
return Task.CompletedTask;
}
/// <summary>
/// Gets invoked if a message was sent or an action triggered
/// </summary>
/// <param name="message"></param>
/// <returns></returns>
public virtual async Task LoadControls(MessageResult message)
{
//Looking for the control by id, if not listened, raise event for all
if (message.RawData?.StartsWith("#c") ?? false)
{
var c = Controls.FirstOrDefault(a => a.ControlId == message.RawData.Split('_')[0]);
if (c != null)
{
await c.Load(message);
return;
}
}
foreach (var b in Controls)
{
if (!b.Enabled)
continue;
await b.Load(message);
}
}
/// <summary>
/// Gets invoked if the form gets loaded and on every message belongs to this context
/// </summary>
/// <param name="message"></param>
/// <returns></returns>
public virtual Task Load(MessageResult message)
{
return Task.CompletedTask;
}
/// <summary>
/// Gets invoked, when a messages has been edited.
/// </summary>
/// <param name="message"></param>
/// <returns></returns>
public virtual Task Edited(MessageResult message)
{
return Task.CompletedTask;
}
/// <summary>
/// Gets invoked if the user clicked a button.
/// </summary>
/// <param name="message"></param>
/// <returns></returns>
public virtual async Task ActionControls(MessageResult message)
{
//Looking for the control by id, if not listened, raise event for all
if (message.RawData.StartsWith("#c"))
{
var c = Controls.FirstOrDefault(a => a.ControlId == message.RawData.Split('_')[0]);
if (c != null)
{
await c.Action(message, message.RawData.Split('_')[1]);
return;
}
}
foreach (var b in Controls)
{
if (!b.Enabled)
continue;
await b.Action(message);
if (message.Handled)
return;
}
}
/// <summary>
/// Gets invoked if the user has clicked a button.
/// </summary>
/// <param name="message"></param>
/// <returns></returns>
public virtual Task Action(MessageResult message)
{
return Task.CompletedTask;
}
/// <summary>
/// Gets invoked if the user has sent some media (Photo, Audio, Video, Contact, Location, Document)
/// </summary>
/// <param name="message"></param>
/// <returns></returns>
public virtual Task SentData(DataResult message)
{
return Task.CompletedTask;
}
/// <summary>
/// Gets invoked at the end of the cycle to "Render" text, images, buttons, etc...
/// </summary>
/// <param name="message"></param>
/// <returns></returns>
public virtual async Task RenderControls(MessageResult message)
{
foreach (var b in Controls)
{
if (!b.Enabled)
continue;
await b.Render(message);
}
}
/// <summary>
/// Gets invoked at the end of the cycle to "Render" text, images, buttons, etc...
/// </summary>
/// <param name="message"></param>
/// <returns></returns>
public virtual Task Render(MessageResult message)
{
return Task.CompletedTask;
}
/// <summary>
/// Navigates to a new form
/// </summary>
/// <param name="newForm"></param>
/// <returns></returns>
public virtual async Task NavigateTo(FormBase newForm, params object[] args)
{
var ds = Device;
if (ds == null)
return;
ds.FormSwitched = true;
ds.PreviousForm = ds.ActiveForm;
ds.ActiveForm = newForm;
newForm.Client = Client;
newForm.Device = ds;
//Notify prior to close
foreach (var b in Controls)
{
if (!b.Enabled)
continue;
await b.Hidden(true);
}
CloseControls().Wait();
await OnClosed(EventArgs.Empty);
await newForm.OnInit(new InitEventArgs(args));
await newForm.OnOpened(EventArgs.Empty);
}
/// <summary>
/// Opens this form modal, but don't closes the original ones
/// </summary>
/// <param name="newForm"></param>
/// <returns></returns>
public virtual async Task OpenModal(ModalDialog newForm, params object[] args)
{
var ds = Device;
if (ds == null)
return;
var parentForm = this;
ds.FormSwitched = true;
ds.PreviousForm = ds.ActiveForm;
ds.ActiveForm = newForm;
newForm.Client = parentForm.Client;
newForm.Device = ds;
newForm.ParentForm = parentForm;
newForm.Closed += async (s, en) =>
{
await CloseModal(newForm, parentForm);
};
foreach (var b in Controls)
{
if (!b.Enabled)
continue;
await b.Hidden(false);
}
await newForm.OnInit(new InitEventArgs(args));
await newForm.OnOpened(EventArgs.Empty);
}
public Task CloseModal(ModalDialog modalForm, FormBase oldForm)
{
var ds = Device;
if (ds == null)
return Task.CompletedTask;
if (modalForm == null)
throw new Exception("No modal form");
ds.FormSwitched = true;
ds.PreviousForm = ds.ActiveForm;
ds.ActiveForm = oldForm;
return Task.CompletedTask;
}
/// <summary>
/// Adds a control to the formular and sets its ID and Device.
/// </summary>
/// <param name="control"></param>
public void AddControl(ControlBase control)
{
//Duplicate check
if (Controls.Contains(control))
throw new ArgumentException("Control has been already added.");
control.Id = Controls.Count + 1;
control.Device = Device;
Controls.Add(control);
control.Init();
}
/// <summary>
/// Removes control from the formular and runs a cleanup on it.
/// </summary>
/// <param name="control"></param>
public void RemoveControl(ControlBase control)
{
if (!Controls.Contains(control))
return;
control.Cleanup().Wait();
Controls.Remove(control);
}
/// <summary>
/// Removes all controls.
/// </summary>
public void RemoveAllControls()
{
foreach(var c in Controls)
{
c.Cleanup().Wait();
Controls.Remove(c);
}
}
/// <summary>
/// Cleanup
/// </summary>
public void Dispose()
{
Client = null;
Device = null;
IsDisposed = true;
await h.InvokeAllAsync(this, e);
}
}
}
///// <summary>
///// Will get called at the initialization (once per context)
///// </summary>
public event AsyncEventHandler<InitEventArgs> Init
{
add => Events.AddHandler(EvInit, value);
remove => Events.RemoveHandler(EvInit, value);
}
public async Task OnOpened(EventArgs e)
{
var handler = Events[EvOpened]?.GetInvocationList().Cast<AsyncEventHandler<EventArgs>>();
if (handler == null)
{
return;
}
foreach (var h in handler)
{
await h.InvokeAllAsync(this, e);
}
}
/// <summary>
/// Gets invoked if gets navigated to this form
/// </summary>
/// <returns></returns>
public event AsyncEventHandler<EventArgs> Opened
{
add => Events.AddHandler(EvOpened, value);
remove => Events.RemoveHandler(EvOpened, value);
}
public async Task OnClosed(EventArgs e)
{
var handler = Events[EvClosed]?.GetInvocationList().Cast<AsyncEventHandler<EventArgs>>();
if (handler == null)
{
return;
}
foreach (var h in handler)
{
await h.InvokeAllAsync(this, e);
}
}
/// <summary>
/// Form has been closed (left)
/// </summary>
/// <returns></returns>
public event AsyncEventHandler<EventArgs> Closed
{
add => Events.AddHandler(EvClosed, value);
remove => Events.RemoveHandler(EvClosed, value);
}
/// <summary>
/// Get invoked when a modal child from has been closed.
/// </summary>
/// <param name="message"></param>
/// <returns></returns>
public virtual Task ReturnFromModal(ModalDialog modal)
{
return Task.CompletedTask;
}
/// <summary>
/// Pre to form close, cleanup all controls
/// </summary>
/// <returns></returns>
public Task CloseControls()
{
foreach (var b in Controls)
{
b.Cleanup().Wait();
}
return Task.CompletedTask;
}
public virtual Task PreLoad(MessageResult message)
{
return Task.CompletedTask;
}
/// <summary>
/// Gets invoked if a message was sent or an action triggered
/// </summary>
/// <param name="message"></param>
/// <returns></returns>
public virtual async Task LoadControls(MessageResult message)
{
//Looking for the control by id, if not listened, raise event for all
if (message.RawData?.StartsWith("#c") ?? false)
{
var c = Controls.FirstOrDefault(a => a.ControlId == message.RawData.Split('_')[0]);
if (c != null)
{
await c.Load(message);
return;
}
}
foreach (var b in Controls)
{
if (!b.Enabled)
{
continue;
}
await b.Load(message);
}
}
/// <summary>
/// Gets invoked if the form gets loaded and on every message belongs to this context
/// </summary>
/// <param name="message"></param>
/// <returns></returns>
public virtual Task Load(MessageResult message)
{
return Task.CompletedTask;
}
/// <summary>
/// Gets invoked, when a messages has been edited.
/// </summary>
/// <param name="message"></param>
/// <returns></returns>
public virtual Task Edited(MessageResult message)
{
return Task.CompletedTask;
}
/// <summary>
/// Gets invoked if the user clicked a button.
/// </summary>
/// <param name="message"></param>
/// <returns></returns>
public virtual async Task ActionControls(MessageResult message)
{
//Looking for the control by id, if not listened, raise event for all
if (message.RawData.StartsWith("#c"))
{
var c = Controls.FirstOrDefault(a => a.ControlId == message.RawData.Split('_')[0]);
if (c != null)
{
await c.Action(message, message.RawData.Split('_')[1]);
return;
}
}
foreach (var b in Controls)
{
if (!b.Enabled)
{
continue;
}
await b.Action(message);
if (message.Handled)
{
return;
}
}
}
/// <summary>
/// Gets invoked if the user has clicked a button.
/// </summary>
/// <param name="message"></param>
/// <returns></returns>
public virtual Task Action(MessageResult message)
{
return Task.CompletedTask;
}
/// <summary>
/// Gets invoked if the user has sent some media (Photo, Audio, Video, Contact, Location, Document)
/// </summary>
/// <param name="message"></param>
/// <returns></returns>
public virtual Task SentData(DataResult message)
{
return Task.CompletedTask;
}
/// <summary>
/// Gets invoked at the end of the cycle to "Render" text, images, buttons, etc...
/// </summary>
/// <param name="message"></param>
/// <returns></returns>
public virtual async Task RenderControls(MessageResult message)
{
foreach (var b in Controls)
{
if (!b.Enabled)
{
continue;
}
await b.Render(message);
}
}
/// <summary>
/// Gets invoked at the end of the cycle to "Render" text, images, buttons, etc...
/// </summary>
/// <param name="message"></param>
/// <returns></returns>
public virtual Task Render(MessageResult message)
{
return Task.CompletedTask;
}
/// <summary>
/// Navigates to a new form
/// </summary>
/// <param name="newForm"></param>
/// <returns></returns>
public virtual async Task NavigateTo(FormBase newForm, params object[] args)
{
var ds = Device;
if (ds == null)
{
return;
}
ds.FormSwitched = true;
ds.PreviousForm = ds.ActiveForm;
ds.ActiveForm = newForm;
newForm.Client = Client;
newForm.Device = ds;
//Notify prior to close
foreach (var b in Controls)
{
if (!b.Enabled)
{
continue;
}
await b.Hidden(true);
}
CloseControls().Wait();
await OnClosed(EventArgs.Empty);
await newForm.OnInit(new InitEventArgs(args));
await newForm.OnOpened(EventArgs.Empty);
}
/// <summary>
/// Opens this form modal, but don't closes the original ones
/// </summary>
/// <param name="newForm"></param>
/// <returns></returns>
public virtual async Task OpenModal(ModalDialog newForm, params object[] args)
{
var ds = Device;
if (ds == null)
{
return;
}
var parentForm = this;
ds.FormSwitched = true;
ds.PreviousForm = ds.ActiveForm;
ds.ActiveForm = newForm;
newForm.Client = parentForm.Client;
newForm.Device = ds;
newForm.ParentForm = parentForm;
newForm.Closed += async (s, en) => { await CloseModal(newForm, parentForm); };
foreach (var b in Controls)
{
if (!b.Enabled)
{
continue;
}
await b.Hidden(false);
}
await newForm.OnInit(new InitEventArgs(args));
await newForm.OnOpened(EventArgs.Empty);
}
public Task CloseModal(ModalDialog modalForm, FormBase oldForm)
{
var ds = Device;
if (ds == null)
{
return Task.CompletedTask;
}
if (modalForm == null)
{
throw new Exception("No modal form");
}
ds.FormSwitched = true;
ds.PreviousForm = ds.ActiveForm;
ds.ActiveForm = oldForm;
return Task.CompletedTask;
}
/// <summary>
/// Adds a control to the formular and sets its ID and Device.
/// </summary>
/// <param name="control"></param>
public void AddControl(ControlBase control)
{
//Duplicate check
if (Controls.Contains(control))
{
throw new ArgumentException("Control has been already added.");
}
control.Id = Controls.Count + 1;
control.Device = Device;
Controls.Add(control);
control.Init();
}
/// <summary>
/// Removes control from the formular and runs a cleanup on it.
/// </summary>
/// <param name="control"></param>
public void RemoveControl(ControlBase control)
{
if (!Controls.Contains(control))
{
return;
}
control.Cleanup().Wait();
Controls.Remove(control);
}
/// <summary>
/// Removes all controls.
/// </summary>
public void RemoveAllControls()
{
foreach (var c in Controls)
{
c.Cleanup().Wait();
Controls.Remove(c);
}
}
}
+179 -186
View File
@@ -10,193 +10,186 @@ using Telegram.Bot.Exceptions;
using Telegram.Bot.Extensions.Polling;
using Telegram.Bot.Types;
namespace TelegramBotBase.Base
namespace TelegramBotBase.Base;
/// <summary>
/// Base class for message handling
/// </summary>
public class MessageClient
{
/// <summary>
/// Base class for message handling
/// </summary>
public class MessageClient
private static readonly object EvOnMessageLoop = new();
private static object __evOnMessage = new();
private static object __evOnMessageEdit = new();
private static object __evCallbackQuery = new();
private CancellationTokenSource _cancellationTokenSource;
public MessageClient(string apiKey)
{
ApiKey = apiKey;
TelegramClient = new TelegramBotClient(apiKey);
public string ApiKey { get; set; }
public ITelegramBotClient TelegramClient { get; set; }
private EventHandlerList Events { get; set; } = new EventHandlerList();
private static readonly object EvOnMessageLoop = new object();
private static object __evOnMessage = new object();
private static object __evOnMessageEdit = new object();
private static object __evCallbackQuery = new object();
private CancellationTokenSource _cancellationTokenSource;
public MessageClient(string apiKey)
{
this.ApiKey = apiKey;
TelegramClient = new TelegramBotClient(apiKey);
Prepare();
}
public MessageClient(string apiKey, HttpClient proxy)
{
this.ApiKey = apiKey;
TelegramClient = new TelegramBotClient(apiKey, proxy);
Prepare();
}
public MessageClient(string apiKey, Uri proxyUrl, NetworkCredential credential = null)
{
this.ApiKey = apiKey;
var proxy = new WebProxy(proxyUrl)
{
Credentials = credential
};
var httpClient = new HttpClient(
new HttpClientHandler { Proxy = proxy, UseProxy = true }
);
TelegramClient = new TelegramBotClient(apiKey, httpClient);
Prepare();
}
/// <summary>
/// Initializes the client with a proxy
/// </summary>
/// <param name="apiKey"></param>
/// <param name="proxyHost">i.e. 127.0.0.1</param>
/// <param name="proxyPort">i.e. 10000</param>
public MessageClient(string apiKey, string proxyHost, int proxyPort)
{
this.ApiKey = apiKey;
var proxy = new WebProxy(proxyHost, proxyPort);
var httpClient = new HttpClient(
new HttpClientHandler { Proxy = proxy, UseProxy = true }
);
TelegramClient = new TelegramBotClient(apiKey, httpClient);
Prepare();
}
public MessageClient(string apiKey, TelegramBotClient client)
{
this.ApiKey = apiKey;
TelegramClient = client;
Prepare();
}
public void Prepare()
{
TelegramClient.Timeout = new TimeSpan(0, 0, 30);
}
public void StartReceiving()
{
_cancellationTokenSource = new CancellationTokenSource();
var receiverOptions = new ReceiverOptions();
TelegramClient.StartReceiving(HandleUpdateAsync, HandleErrorAsync, receiverOptions, _cancellationTokenSource.Token);
}
public void StopReceiving()
{
_cancellationTokenSource.Cancel();
}
public Task HandleUpdateAsync(ITelegramBotClient botClient, Update update, CancellationToken cancellationToken)
{
OnMessageLoop(new UpdateResult(update, null));
return Task.CompletedTask;
}
public Task HandleErrorAsync(ITelegramBotClient botClient, Exception exception, CancellationToken cancellationToken)
{
if (exception is ApiRequestException exApi)
{
Console.WriteLine($"Telegram API Error:\n[{exApi.ErrorCode}]\n{exApi.Message}");
}
else
{
Console.WriteLine(exception.ToString());
}
return Task.CompletedTask;
}
/// <summary>
/// This will return the current list of bot commands.
/// </summary>
/// <returns></returns>
public async Task<BotCommand[]> GetBotCommands(BotCommandScope scope = null, string languageCode = null)
{
return await TelegramClient.GetMyCommandsAsync(scope, languageCode);
}
/// <summary>
/// This will set your bot commands to the given list.
/// </summary>
/// <param name="botcommands"></param>
/// <returns></returns>
public async Task SetBotCommands(List<BotCommand> botcommands, BotCommandScope scope = null, string languageCode = null)
{
await TelegramClient.SetMyCommandsAsync(botcommands, scope, languageCode);
}
/// <summary>
/// This will delete the current list of bot commands.
/// </summary>
/// <returns></returns>
public async Task DeleteBotCommands(BotCommandScope scope = null, string languageCode = null)
{
await TelegramClient.DeleteMyCommandsAsync(scope, languageCode);
}
#region "Events"
public event Async.AsyncEventHandler<UpdateResult> MessageLoop
{
add => Events.AddHandler(EvOnMessageLoop, value);
remove => Events.RemoveHandler(EvOnMessageLoop, value);
}
public void OnMessageLoop(UpdateResult update)
{
(Events[EvOnMessageLoop] as Async.AsyncEventHandler<UpdateResult>)?.Invoke(this, update);
}
#endregion
Prepare();
}
}
public MessageClient(string apiKey, HttpClient proxy)
{
ApiKey = apiKey;
TelegramClient = new TelegramBotClient(apiKey, proxy);
Prepare();
}
public MessageClient(string apiKey, Uri proxyUrl, NetworkCredential credential = null)
{
ApiKey = apiKey;
var proxy = new WebProxy(proxyUrl)
{
Credentials = credential
};
var httpClient = new HttpClient(
new HttpClientHandler { Proxy = proxy, UseProxy = true }
);
TelegramClient = new TelegramBotClient(apiKey, httpClient);
Prepare();
}
/// <summary>
/// Initializes the client with a proxy
/// </summary>
/// <param name="apiKey"></param>
/// <param name="proxyHost">i.e. 127.0.0.1</param>
/// <param name="proxyPort">i.e. 10000</param>
public MessageClient(string apiKey, string proxyHost, int proxyPort)
{
ApiKey = apiKey;
var proxy = new WebProxy(proxyHost, proxyPort);
var httpClient = new HttpClient(
new HttpClientHandler { Proxy = proxy, UseProxy = true }
);
TelegramClient = new TelegramBotClient(apiKey, httpClient);
Prepare();
}
public MessageClient(string apiKey, TelegramBotClient client)
{
ApiKey = apiKey;
TelegramClient = client;
Prepare();
}
public string ApiKey { get; set; }
public ITelegramBotClient TelegramClient { get; set; }
private EventHandlerList Events { get; } = new();
public void Prepare()
{
TelegramClient.Timeout = new TimeSpan(0, 0, 30);
}
public void StartReceiving()
{
_cancellationTokenSource = new CancellationTokenSource();
var receiverOptions = new ReceiverOptions();
TelegramClient.StartReceiving(HandleUpdateAsync, HandleErrorAsync, receiverOptions,
_cancellationTokenSource.Token);
}
public void StopReceiving()
{
_cancellationTokenSource.Cancel();
}
public Task HandleUpdateAsync(ITelegramBotClient botClient, Update update, CancellationToken cancellationToken)
{
OnMessageLoop(new UpdateResult(update, null));
return Task.CompletedTask;
}
public Task HandleErrorAsync(ITelegramBotClient botClient, Exception exception,
CancellationToken cancellationToken)
{
if (exception is ApiRequestException exApi)
{
Console.WriteLine($"Telegram API Error:\n[{exApi.ErrorCode}]\n{exApi.Message}");
}
else
{
Console.WriteLine(exception.ToString());
}
return Task.CompletedTask;
}
/// <summary>
/// This will return the current list of bot commands.
/// </summary>
/// <returns></returns>
public async Task<BotCommand[]> GetBotCommands(BotCommandScope scope = null, string languageCode = null)
{
return await TelegramClient.GetMyCommandsAsync(scope, languageCode);
}
/// <summary>
/// This will set your bot commands to the given list.
/// </summary>
/// <param name="botcommands"></param>
/// <returns></returns>
public async Task SetBotCommands(List<BotCommand> botcommands, BotCommandScope scope = null,
string languageCode = null)
{
await TelegramClient.SetMyCommandsAsync(botcommands, scope, languageCode);
}
/// <summary>
/// This will delete the current list of bot commands.
/// </summary>
/// <returns></returns>
public async Task DeleteBotCommands(BotCommandScope scope = null, string languageCode = null)
{
await TelegramClient.DeleteMyCommandsAsync(scope, languageCode);
}
#region "Events"
public event Async.AsyncEventHandler<UpdateResult> MessageLoop
{
add => Events.AddHandler(EvOnMessageLoop, value);
remove => Events.RemoveHandler(EvOnMessageLoop, value);
}
public void OnMessageLoop(UpdateResult update)
{
(Events[EvOnMessageLoop] as Async.AsyncEventHandler<UpdateResult>)?.Invoke(this, update);
}
#endregion
}
+100 -102
View File
@@ -5,136 +5,134 @@ using Newtonsoft.Json;
using Telegram.Bot.Types;
using Telegram.Bot.Types.Enums;
namespace TelegramBotBase.Base
namespace TelegramBotBase.Base;
public class MessageResult : ResultBase
{
public class MessageResult : ResultBase
internal MessageResult()
{
}
public Update UpdateData { get; set; }
public MessageResult(Update update)
{
UpdateData = update;
}
/// <summary>
/// Returns the Device/ChatId
/// </summary>
public override long DeviceId =>
UpdateData?.Message?.Chat?.Id
?? UpdateData?.EditedMessage?.Chat.Id
?? UpdateData?.CallbackQuery.Message?.Chat.Id
?? Device?.DeviceId
?? 0;
public Update UpdateData { get; set; }
/// <summary>
/// The message id
/// </summary>
public new int MessageId =>
UpdateData?.Message?.MessageId
?? Message?.MessageId
?? UpdateData?.CallbackQuery?.Message?.MessageId
?? 0;
/// <summary>
/// Returns the Device/ChatId
/// </summary>
public override long DeviceId =>
UpdateData?.Message?.Chat?.Id
?? UpdateData?.EditedMessage?.Chat.Id
?? UpdateData?.CallbackQuery.Message?.Chat.Id
?? Device?.DeviceId
?? 0;
public string Command => UpdateData?.Message?.Text ?? "";
/// <summary>
/// The message id
/// </summary>
public new int MessageId =>
UpdateData?.Message?.MessageId
?? Message?.MessageId
?? UpdateData?.CallbackQuery?.Message?.MessageId
?? 0;
public string MessageText => UpdateData?.Message?.Text ?? "";
public string Command => UpdateData?.Message?.Text ?? "";
public MessageType MessageType => Message?.Type ?? MessageType.Unknown;
public string MessageText => UpdateData?.Message?.Text ?? "";
/// <summary>
/// Is this an action ? (i.e. button click)
/// </summary>
public bool IsAction => (UpdateData.CallbackQuery != null);
public MessageType MessageType => Message?.Type ?? MessageType.Unknown;
/// <summary>
/// Is this a command ? Starts with a slash '/' and a command
/// </summary>
public bool IsBotCommand => (MessageText.StartsWith("/"));
/// <summary>
/// Is this an action ? (i.e. button click)
/// </summary>
public bool IsAction => UpdateData.CallbackQuery != null;
/// <summary>
/// Returns a List of all parameters which has been sent with the command itself (i.e. /start 123 456 789 => 123,456,789)
/// </summary>
public List<string> BotCommandParameters
/// <summary>
/// Is this a command ? Starts with a slash '/' and a command
/// </summary>
public bool IsBotCommand => MessageText.StartsWith("/");
/// <summary>
/// Returns a List of all parameters which has been sent with the command itself (i.e. /start 123 456 789 =>
/// 123,456,789)
/// </summary>
public List<string> BotCommandParameters
{
get
{
get
if (!IsBotCommand)
{
if (!IsBotCommand)
return new List<string>();
//Split by empty space and skip first entry (command itself), return as list
return MessageText.Split(' ').Skip(1).ToList();
}
}
/// <summary>
/// Returns just the command (i.e. /start 1 2 3 => /start)
/// </summary>
public string BotCommand
{
get
{
if (!IsBotCommand)
return null;
return MessageText.Split(' ')[0];
}
}
/// <summary>
/// Returns if this message will be used on the first form or not.
/// </summary>
public bool IsFirstHandler { get; set; } = true;
public bool Handled { get; set; } = false;
public string RawData => UpdateData?.CallbackQuery?.Data;
public T GetData<T>()
where T : class
{
T cd = null;
try
{
cd = JsonConvert.DeserializeObject<T>(RawData);
return cd;
}
catch
{
return new List<string>();
}
return null;
//Split by empty space and skip first entry (command itself), return as list
return MessageText.Split(' ').Skip(1).ToList();
}
}
/// <summary>
/// Confirm incomming action (i.e. Button click)
/// </summary>
/// <param name="message"></param>
/// <returns></returns>
public async Task ConfirmAction(string message = "", bool showAlert = false, string urlToOpen = null)
/// <summary>
/// Returns just the command (i.e. /start 1 2 3 => /start)
/// </summary>
public string BotCommand
{
get
{
await Device.ConfirmAction(UpdateData.CallbackQuery.Id, message, showAlert, urlToOpen);
}
public override async Task DeleteMessage()
{
try
if (!IsBotCommand)
{
await base.DeleteMessage(MessageId);
return null;
}
catch
{
}
return MessageText.Split(' ')[0];
}
}
internal MessageResult()
/// <summary>
/// Returns if this message will be used on the first form or not.
/// </summary>
public bool IsFirstHandler { get; set; } = true;
public bool Handled { get; set; } = false;
public string RawData => UpdateData?.CallbackQuery?.Data;
public T GetData<T>()
where T : class
{
T cd = null;
try
{
cd = JsonConvert.DeserializeObject<T>(RawData);
return cd;
}
public MessageResult(Update update)
catch
{
UpdateData = update;
}
return null;
}
/// <summary>
/// Confirm incomming action (i.e. Button click)
/// </summary>
/// <param name="message"></param>
/// <returns></returns>
public async Task ConfirmAction(string message = "", bool showAlert = false, string urlToOpen = null)
{
await Device.ConfirmAction(UpdateData.CallbackQuery.Id, message, showAlert, urlToOpen);
}
public override async Task DeleteMessage()
{
try
{
await base.DeleteMessage(MessageId);
}
catch
{
}
}
}
+36 -42
View File
@@ -4,48 +4,42 @@ using Telegram.Bot;
using Telegram.Bot.Types;
using TelegramBotBase.Sessions;
namespace TelegramBotBase.Base
namespace TelegramBotBase.Base;
public class ResultBase : EventArgs
{
public class ResultBase : EventArgs
public DeviceSession Device { get; set; }
public virtual long DeviceId { get; set; }
public int MessageId => Message.MessageId;
public virtual Message Message { get; set; }
/// <summary>
/// Deletes the current message
/// </summary>
/// <param name="messageId"></param>
/// <returns></returns>
public virtual async Task DeleteMessage()
{
public DeviceSession Device
{
get;
set;
}
public virtual long DeviceId { get; set; }
public int MessageId => Message.MessageId;
public virtual Message Message { get; set; }
/// <summary>
/// Deletes the current message
/// </summary>
/// <param name="messageId"></param>
/// <returns></returns>
public virtual async Task DeleteMessage()
{
await DeleteMessage(MessageId);
}
/// <summary>
///Deletes the current message or the given one.
/// </summary>
/// <param name="messageId"></param>
/// <returns></returns>
public virtual async Task DeleteMessage(int messageId = -1)
{
try
{
await Device.Client.TelegramClient.DeleteMessageAsync(DeviceId, (messageId == -1 ? MessageId : messageId));
}
catch
{
}
}
await DeleteMessage(MessageId);
}
}
/// <summary>
/// Deletes the current message or the given one.
/// </summary>
/// <param name="messageId"></param>
/// <returns></returns>
public virtual async Task DeleteMessage(int messageId = -1)
{
try
{
await Device.Client.TelegramClient.DeleteMessageAsync(DeviceId,
messageId == -1 ? MessageId : messageId);
}
catch
{
}
}
}
+18 -26
View File
@@ -1,32 +1,24 @@
using System.Collections.Generic;
using System.Linq;
namespace TelegramBotBase.Base
namespace TelegramBotBase.Base;
public class StateContainer
{
public class StateContainer
public StateContainer()
{
public List<StateEntry> States { get; set; }
public List<long> ChatIds
{
get
{
return States.Where(a => a.DeviceId > 0).Select(a => a.DeviceId).ToList();
}
}
public List<long> GroupIds
{
get
{
return States.Where(a => a.DeviceId < 0).Select(a => a.DeviceId).ToList();
}
}
public StateContainer()
{
States = new List<StateEntry>();
}
States = new List<StateEntry>();
}
}
public List<StateEntry> States { get; set; }
public List<long> ChatIds
{
get { return States.Where(a => a.DeviceId > 0).Select(a => a.DeviceId).ToList(); }
}
public List<long> GroupIds
{
get { return States.Where(a => a.DeviceId < 0).Select(a => a.DeviceId).ToList(); }
}
}
+32 -35
View File
@@ -1,41 +1,38 @@
using System.Collections.Generic;
using System.Diagnostics;
namespace TelegramBotBase.Base
namespace TelegramBotBase.Base;
[DebuggerDisplay("Device: {DeviceId}, {FormUri}")]
public class StateEntry
{
[DebuggerDisplay("Device: {DeviceId}, {FormUri}")]
public class StateEntry
public StateEntry()
{
/// <summary>
/// Contains the DeviceId of the entry.
/// </summary>
public long DeviceId { get; set; }
/// <summary>
/// Contains the Username (on privat chats) or Group title on groups/channels.
/// </summary>
public string ChatTitle { get; set; }
/// <summary>
/// Contains additional values to save.
/// </summary>
public Dictionary<string, object> Values { get; set; }
/// <summary>
/// Contains the full qualified namespace of the form to used for reload it via reflection.
/// </summary>
public string FormUri {get;set;}
/// <summary>
/// Contains the assembly, where to find that form.
/// </summary>
public string QualifiedName { get; set; }
public StateEntry()
{
Values = new Dictionary<string, object>();
}
Values = new Dictionary<string, object>();
}
}
/// <summary>
/// Contains the DeviceId of the entry.
/// </summary>
public long DeviceId { get; set; }
/// <summary>
/// Contains the Username (on privat chats) or Group title on groups/channels.
/// </summary>
public string ChatTitle { get; set; }
/// <summary>
/// Contains additional values to save.
/// </summary>
public Dictionary<string, object> Values { get; set; }
/// <summary>
/// Contains the full qualified namespace of the form to used for reload it via reflection.
/// </summary>
public string FormUri { get; set; }
/// <summary>
/// Contains the assembly, where to find that form.
/// </summary>
public string QualifiedName { get; set; }
}
+24 -28
View File
@@ -1,35 +1,31 @@
using Telegram.Bot.Types;
using TelegramBotBase.Sessions;
namespace TelegramBotBase.Base
namespace TelegramBotBase.Base;
public class UpdateResult : ResultBase
{
public class UpdateResult : ResultBase
public UpdateResult(Update rawData, DeviceSession device)
{
public UpdateResult(Update rawData, DeviceSession device)
{
RawData = rawData;
Device = device;
}
/// <summary>
/// Returns the Device/ChatId
/// </summary>
public override long DeviceId =>
RawData?.Message?.Chat?.Id
?? RawData?.CallbackQuery?.Message?.Chat?.Id
?? Device?.DeviceId
?? 0;
public Update RawData { get; set; }
public override Message Message =>
RawData?.Message
?? RawData?.EditedMessage
?? RawData?.ChannelPost
?? RawData?.EditedChannelPost
?? RawData?.CallbackQuery?.Message;
RawData = rawData;
Device = device;
}
}
/// <summary>
/// Returns the Device/ChatId
/// </summary>
public override long DeviceId =>
RawData?.Message?.Chat?.Id
?? RawData?.CallbackQuery?.Message?.Chat?.Id
?? Device?.DeviceId
?? 0;
public Update RawData { get; set; }
public override Message Message =>
RawData?.Message
?? RawData?.EditedMessage
?? RawData?.ChannelPost
?? RawData?.EditedChannelPost
?? RawData?.CallbackQuery?.Message;
}
+390 -394
View File
@@ -2,413 +2,409 @@
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Telegram.Bot;
using Telegram.Bot.Exceptions;
using Telegram.Bot.Types;
using Telegram.Bot.Types.Enums;
using TelegramBotBase.Args;
using TelegramBotBase.Attributes;
using TelegramBotBase.Base;
using TelegramBotBase.Enums;
using TelegramBotBase.MessageLoops;
using TelegramBotBase.Form;
using TelegramBotBase.Interfaces;
using TelegramBotBase.Sessions;
using Console = TelegramBotBase.Tools.Console;
namespace TelegramBotBase
namespace TelegramBotBase;
/// <summary>
/// Bot base class for full Device/Context and Messagehandling
/// </summary>
/// <typeparam name="T"></typeparam>
public class BotBase
{
/// <summary>
/// Bot base class for full Device/Context and Messagehandling
/// </summary>
/// <typeparam name="T"></typeparam>
public class BotBase
public BotBase()
{
public MessageClient Client { get; set; }
SystemSettings = new Dictionary<ESettings, uint>();
/// <summary>
/// Your TelegramBot APIKey
/// </summary>
public string ApiKey { get; set; } = "";
SetSetting(ESettings.MaxNumberOfRetries, 5);
SetSetting(ESettings.NavigationMaximum, 10);
SetSetting(ESettings.LogAllMessages, false);
SetSetting(ESettings.SkipAllMessages, false);
SetSetting(ESettings.SaveSessionsOnConsoleExit, false);
/// <summary>
/// List of all running/active sessions
/// </summary>
public SessionBase Sessions { get; set; }
BotCommandScopes = new Dictionary<BotCommandScope, List<BotCommand>>();
/// <summary>
/// Contains System commands which will be available at everytime and didnt get passed to forms, i.e. /start
/// </summary>
public Dictionary<BotCommandScope, List<BotCommand>> BotCommandScopes { get; set; } = new Dictionary<BotCommandScope, List<BotCommand>>();
#region "Events"
private readonly EventHandlerList _events = new EventHandlerList();
private static readonly object EvSessionBegins = new object();
private static readonly object EvMessage = new object();
private static object __evSystemCall = new object();
public delegate Task BotCommandEventHandler(object sender, BotCommandEventArgs e);
private static readonly object EvException = new object();
private static readonly object EvUnhandledCall = new object();
#endregion
/// <summary>
/// Enable the SessionState (you need to implement on call forms the IStateForm interface)
/// </summary>
public IStateMachine StateMachine { get; set; }
/// <summary>
/// Offers functionality to manage the creation process of the start form.
/// </summary>
public IStartFormFactory StartFormFactory { get; set; }
/// <summary>
/// Contains the message loop factory, which cares about "message-management."
/// </summary>
public IMessageLoopFactory MessageLoopFactory { get; set; }
/// <summary>
/// All internal used settings.
/// </summary>
public Dictionary<ESettings, uint> SystemSettings { get; private set; }
public BotBase()
Sessions = new SessionBase
{
SystemSettings = new Dictionary<ESettings, uint>();
SetSetting(ESettings.MaxNumberOfRetries, 5);
SetSetting(ESettings.NavigationMaximum, 10);
SetSetting(ESettings.LogAllMessages, false);
SetSetting(ESettings.SkipAllMessages, false);
SetSetting(ESettings.SaveSessionsOnConsoleExit, false);
BotCommandScopes = new Dictionary<BotCommandScope, List<BotCommand>>();
Sessions = new SessionBase
{
BotBase = this
};
}
/// <summary>
/// Start your Bot
/// </summary>
public void Start()
{
if (Client == null)
return;
Client.MessageLoop += Client_MessageLoop;
if (StateMachine != null)
{
Sessions.LoadSessionStates(StateMachine);
}
//Enable auto session saving
if (GetSetting(ESettings.SaveSessionsOnConsoleExit, false))
{
Tools.Console.SetHandler(() =>
{
Sessions.SaveSessionStates();
});
}
DeviceSession.MaxNumberOfRetries = GetSetting(ESettings.MaxNumberOfRetries, 5);
Client.StartReceiving();
}
private async Task Client_MessageLoop(object sender, UpdateResult e)
{
var ds = Sessions.GetSession(e.DeviceId);
if (ds == null)
{
ds = Sessions.StartSession(e.DeviceId).GetAwaiter().GetResult();
e.Device = ds;
ds.LastMessage = e.RawData.Message;
OnSessionBegins(new SessionBeginEventArgs(e.DeviceId, ds));
}
var mr = new MessageResult(e.RawData);
var i = 0;
//Should formulars get navigated (allow maximum of 10, to dont get loops)
do
{
i++;
//Reset navigation
ds.FormSwitched = false;
await MessageLoopFactory.MessageLoop(this, ds, e, mr);
mr.IsFirstHandler = false;
} while (ds.FormSwitched && i < GetSetting(ESettings.NavigationMaximum, 10));
}
/// <summary>
/// Stop your Bot
/// </summary>
public void Stop()
{
if (Client == null)
return;
Client.MessageLoop -= Client_MessageLoop;
Client.StopReceiving();
Sessions.SaveSessionStates();
}
/// <summary>
/// Send a message to all active Sessions.
/// </summary>
/// <param name="message"></param>
/// <returns></returns>
public async Task SentToAll(string message)
{
if (Client == null)
return;
foreach (var s in Sessions.SessionList)
{
await Client.TelegramClient.SendTextMessageAsync(s.Key, message);
}
}
/// <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
{
UpdateData = new Update()
{
Message = new Message()
}
};
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
{
var ds = Sessions.GetSession(deviceId);
e.Device = ds;
await MessageLoopFactory.MessageLoop(this, ds, new UpdateResult(e.UpdateData, ds), e);
//await Client_Loop(this, e);
}
catch (Exception ex)
{
var ds = Sessions.GetSession(deviceId);
OnException(new SystemExceptionEventArgs(e.Message.Text, deviceId, ds, ex));
}
}
/// <summary>
/// Will get invoke on an unhandled call.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public void MessageLoopFactory_UnhandledCall(object sender, UnhandledCallEventArgs e)
{
OnUnhandledCall(e);
}
/// <summary>
/// This method will update all local created bot commands to the botfather.
/// </summary>
public async Task UploadBotCommands()
{
foreach (var bs in BotCommandScopes)
{
if(bs.Value !=null)
{
await Client.SetBotCommands(bs.Value, bs.Key);
}
else
{
await Client.DeleteBotCommands(bs.Key);
}
}
}
/// <summary>
/// Searching if parameter is a known command in all configured BotCommandScopes.
/// </summary>
/// <param name="command"></param>
/// <returns></returns>
public bool IsKnownBotCommand(string command)
{
foreach (var scope in BotCommandScopes)
{
if (scope.Value.Any(a => "/" + a.Command == command))
return true;
}
return false;
}
/// <summary>
/// Could set a variety of settings to improve the bot handling.
/// </summary>
/// <param name="set"></param>
/// <param name="value"></param>
public void SetSetting(ESettings set, uint value)
{
SystemSettings[set] = value;
}
/// <summary>
/// Could set a variety of settings to improve the bot handling.
/// </summary>
/// <param name="set"></param>
/// <param name="value"></param>
public void SetSetting(ESettings set, bool value)
{
SystemSettings[set] = (value ? 1u : 0u);
}
/// <summary>
/// Could get the current value of a setting
/// </summary>
/// <param name="set"></param>
/// <param name="defaultValue"></param>
/// <returns></returns>
public uint GetSetting(ESettings set, uint defaultValue)
{
if (!SystemSettings.ContainsKey(set))
return defaultValue;
return SystemSettings[set];
}
/// <summary>
/// Could get the current value of a setting
/// </summary>
/// <param name="set"></param>
/// <param name="defaultValue"></param>
/// <returns></returns>
public bool GetSetting(ESettings set, bool defaultValue)
{
if (!SystemSettings.ContainsKey(set))
return defaultValue;
return SystemSettings[set] == 0u ? false : true;
}
#region "Events"
/// <summary>
/// Will be called if a session/context gets started
/// </summary>
public event EventHandler<SessionBeginEventArgs> SessionBegins
{
add => _events.AddHandler(EvSessionBegins, value);
remove => _events.RemoveHandler(EvSessionBegins, value);
}
public void OnSessionBegins(SessionBeginEventArgs e)
{
(_events[EvSessionBegins] as EventHandler<SessionBeginEventArgs>)?.Invoke(this, e);
}
/// <summary>
/// Will be called on incomming message
/// </summary>
public event EventHandler<MessageIncomeEventArgs> Message
{
add => _events.AddHandler(EvMessage, value);
remove => _events.RemoveHandler(EvMessage, value);
}
public void OnMessage(MessageIncomeEventArgs e)
{
(_events[EvMessage] as EventHandler<MessageIncomeEventArgs>)?.Invoke(this, e);
}
/// <summary>
/// Will be called if a bot command gets raised
/// </summary>
public event BotCommandEventHandler BotCommand;
public async Task OnBotCommand(BotCommandEventArgs e)
{
if (BotCommand != null)
await BotCommand(this, e);
}
/// <summary>
/// Will be called on an inner exception
/// </summary>
public event EventHandler<SystemExceptionEventArgs> Exception
{
add => _events.AddHandler(EvException, value);
remove => _events.RemoveHandler(EvException, value);
}
public void OnException(SystemExceptionEventArgs e)
{
(_events[EvException] as EventHandler<SystemExceptionEventArgs>)?.Invoke(this, e);
}
/// <summary>
/// Will be called if no form handeled this call
/// </summary>
public event EventHandler<UnhandledCallEventArgs> UnhandledCall
{
add => _events.AddHandler(EvUnhandledCall, value);
remove => _events.RemoveHandler(EvUnhandledCall, value);
}
public void OnUnhandledCall(UnhandledCallEventArgs e)
{
(_events[EvUnhandledCall] as EventHandler<UnhandledCallEventArgs>)?.Invoke(this, e);
}
#endregion
BotBase = this
};
}
}
public MessageClient Client { get; set; }
/// <summary>
/// Your TelegramBot APIKey
/// </summary>
public string ApiKey { get; set; } = "";
/// <summary>
/// List of all running/active sessions
/// </summary>
public SessionBase Sessions { get; set; }
/// <summary>
/// Contains System commands which will be available at everytime and didnt get passed to forms, i.e. /start
/// </summary>
public Dictionary<BotCommandScope, List<BotCommand>> BotCommandScopes { get; set; } = new();
/// <summary>
/// Enable the SessionState (you need to implement on call forms the IStateForm interface)
/// </summary>
public IStateMachine StateMachine { get; set; }
/// <summary>
/// Offers functionality to manage the creation process of the start form.
/// </summary>
public IStartFormFactory StartFormFactory { get; set; }
/// <summary>
/// Contains the message loop factory, which cares about "message-management."
/// </summary>
public IMessageLoopFactory MessageLoopFactory { get; set; }
/// <summary>
/// All internal used settings.
/// </summary>
public Dictionary<ESettings, uint> SystemSettings { get; }
/// <summary>
/// Start your Bot
/// </summary>
public void Start()
{
if (Client == null)
{
return;
}
Client.MessageLoop += Client_MessageLoop;
if (StateMachine != null)
{
Sessions.LoadSessionStates(StateMachine);
}
//Enable auto session saving
if (GetSetting(ESettings.SaveSessionsOnConsoleExit, false))
{
Console.SetHandler(() => { Sessions.SaveSessionStates(); });
}
DeviceSession.MaxNumberOfRetries = GetSetting(ESettings.MaxNumberOfRetries, 5);
Client.StartReceiving();
}
private async Task Client_MessageLoop(object sender, UpdateResult e)
{
var ds = Sessions.GetSession(e.DeviceId);
if (ds == null)
{
ds = Sessions.StartSession(e.DeviceId).GetAwaiter().GetResult();
e.Device = ds;
ds.LastMessage = e.RawData.Message;
OnSessionBegins(new SessionBeginEventArgs(e.DeviceId, ds));
}
var mr = new MessageResult(e.RawData);
var i = 0;
//Should formulars get navigated (allow maximum of 10, to dont get loops)
do
{
i++;
//Reset navigation
ds.FormSwitched = false;
await MessageLoopFactory.MessageLoop(this, ds, e, mr);
mr.IsFirstHandler = false;
} while (ds.FormSwitched && i < GetSetting(ESettings.NavigationMaximum, 10));
}
/// <summary>
/// Stop your Bot
/// </summary>
public void Stop()
{
if (Client == null)
{
return;
}
Client.MessageLoop -= Client_MessageLoop;
Client.StopReceiving();
Sessions.SaveSessionStates();
}
/// <summary>
/// Send a message to all active Sessions.
/// </summary>
/// <param name="message"></param>
/// <returns></returns>
public async Task SentToAll(string message)
{
if (Client == null)
{
return;
}
foreach (var s in Sessions.SessionList)
{
await Client.TelegramClient.SendTextMessageAsync(s.Key, message);
}
}
/// <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
{
UpdateData = new Update
{
Message = new Message()
}
};
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
{
var ds = Sessions.GetSession(deviceId);
e.Device = ds;
await MessageLoopFactory.MessageLoop(this, ds, new UpdateResult(e.UpdateData, ds), e);
//await Client_Loop(this, e);
}
catch (Exception ex)
{
var ds = Sessions.GetSession(deviceId);
OnException(new SystemExceptionEventArgs(e.Message.Text, deviceId, ds, ex));
}
}
/// <summary>
/// Will get invoke on an unhandled call.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public void MessageLoopFactory_UnhandledCall(object sender, UnhandledCallEventArgs e)
{
OnUnhandledCall(e);
}
/// <summary>
/// This method will update all local created bot commands to the botfather.
/// </summary>
public async Task UploadBotCommands()
{
foreach (var bs in BotCommandScopes)
{
if (bs.Value != null)
{
await Client.SetBotCommands(bs.Value, bs.Key);
}
else
{
await Client.DeleteBotCommands(bs.Key);
}
}
}
/// <summary>
/// Searching if parameter is a known command in all configured BotCommandScopes.
/// </summary>
/// <param name="command"></param>
/// <returns></returns>
public bool IsKnownBotCommand(string command)
{
foreach (var scope in BotCommandScopes)
{
if (scope.Value.Any(a => "/" + a.Command == command))
{
return true;
}
}
return false;
}
/// <summary>
/// Could set a variety of settings to improve the bot handling.
/// </summary>
/// <param name="set"></param>
/// <param name="value"></param>
public void SetSetting(ESettings set, uint value)
{
SystemSettings[set] = value;
}
/// <summary>
/// Could set a variety of settings to improve the bot handling.
/// </summary>
/// <param name="set"></param>
/// <param name="value"></param>
public void SetSetting(ESettings set, bool value)
{
SystemSettings[set] = value ? 1u : 0u;
}
/// <summary>
/// Could get the current value of a setting
/// </summary>
/// <param name="set"></param>
/// <param name="defaultValue"></param>
/// <returns></returns>
public uint GetSetting(ESettings set, uint defaultValue)
{
if (!SystemSettings.ContainsKey(set))
{
return defaultValue;
}
return SystemSettings[set];
}
/// <summary>
/// Could get the current value of a setting
/// </summary>
/// <param name="set"></param>
/// <param name="defaultValue"></param>
/// <returns></returns>
public bool GetSetting(ESettings set, bool defaultValue)
{
if (!SystemSettings.ContainsKey(set))
{
return defaultValue;
}
return SystemSettings[set] == 0u ? false : true;
}
#region "Events"
private readonly EventHandlerList _events = new();
private static readonly object EvSessionBegins = new();
private static readonly object EvMessage = new();
private static object __evSystemCall = new();
public delegate Task BotCommandEventHandler(object sender, BotCommandEventArgs e);
private static readonly object EvException = new();
private static readonly object EvUnhandledCall = new();
#endregion
#region "Events"
/// <summary>
/// Will be called if a session/context gets started
/// </summary>
public event EventHandler<SessionBeginEventArgs> SessionBegins
{
add => _events.AddHandler(EvSessionBegins, value);
remove => _events.RemoveHandler(EvSessionBegins, value);
}
public void OnSessionBegins(SessionBeginEventArgs e)
{
(_events[EvSessionBegins] as EventHandler<SessionBeginEventArgs>)?.Invoke(this, e);
}
/// <summary>
/// Will be called on incomming message
/// </summary>
public event EventHandler<MessageIncomeEventArgs> Message
{
add => _events.AddHandler(EvMessage, value);
remove => _events.RemoveHandler(EvMessage, value);
}
public void OnMessage(MessageIncomeEventArgs e)
{
(_events[EvMessage] as EventHandler<MessageIncomeEventArgs>)?.Invoke(this, e);
}
/// <summary>
/// Will be called if a bot command gets raised
/// </summary>
public event BotCommandEventHandler BotCommand;
public async Task OnBotCommand(BotCommandEventArgs e)
{
if (BotCommand != null)
{
await BotCommand(this, e);
}
}
/// <summary>
/// Will be called on an inner exception
/// </summary>
public event EventHandler<SystemExceptionEventArgs> Exception
{
add => _events.AddHandler(EvException, value);
remove => _events.RemoveHandler(EvException, value);
}
public void OnException(SystemExceptionEventArgs e)
{
(_events[EvException] as EventHandler<SystemExceptionEventArgs>)?.Invoke(this, e);
}
/// <summary>
/// Will be called if no form handeled this call
/// </summary>
public event EventHandler<UnhandledCallEventArgs> UnhandledCall
{
add => _events.AddHandler(EvUnhandledCall, value);
remove => _events.RemoveHandler(EvUnhandledCall, value);
}
public void OnUnhandledCall(UnhandledCallEventArgs e)
{
(_events[EvUnhandledCall] as EventHandler<UnhandledCallEventArgs>)?.Invoke(this, e);
}
#endregion
}
+354 -356
View File
@@ -13,362 +13,360 @@ using TelegramBotBase.Localizations;
using TelegramBotBase.MessageLoops;
using TelegramBotBase.States;
namespace TelegramBotBase.Builder
namespace TelegramBotBase.Builder;
public class BotBaseBuilder : IAPIKeySelectionStage, IMessageLoopSelectionStage, IStartFormSelectionStage,
IBuildingStage, INetworkingSelectionStage, IBotCommandsStage, ISessionSerializationStage,
ILanguageSelectionStage
{
public class BotBaseBuilder : IAPIKeySelectionStage, IMessageLoopSelectionStage, IStartFormSelectionStage, IBuildingStage, INetworkingSelectionStage, IBotCommandsStage, ISessionSerializationStage, ILanguageSelectionStage
private string _apiKey;
private MessageClient _client;
private IStartFormFactory _factory;
private IMessageLoopFactory _messageLoopFactory;
private IStateMachine _statemachine;
private BotBaseBuilder()
{
private string _apiKey;
private IStartFormFactory _factory;
private MessageClient _client;
/// <summary>
/// Contains different Botcommands for different areas.
/// </summary>
private Dictionary<BotCommandScope, List<BotCommand>> BotCommandScopes { get; set; } = new Dictionary<BotCommandScope, List<BotCommand>>();
private IStateMachine _statemachine;
private IMessageLoopFactory _messageLoopFactory;
private BotBaseBuilder()
{
}
public static IAPIKeySelectionStage Create()
{
return new BotBaseBuilder();
}
#region "Step 1 (Basic Stuff)"
public IMessageLoopSelectionStage WithAPIKey(string apiKey)
{
_apiKey = apiKey;
return this;
}
public IBuildingStage QuickStart(string apiKey, Type StartForm)
{
_apiKey = apiKey;
_factory = new DefaultStartFormFactory(StartForm);
DefaultMessageLoop();
NoProxy();
OnlyStart();
NoSerialization();
DefaultLanguage();
return this;
}
public IBuildingStage QuickStart<T>(string apiKey)
where T : FormBase
{
_apiKey = apiKey;
_factory = new DefaultStartFormFactory(typeof(T));
DefaultMessageLoop();
NoProxy();
OnlyStart();
NoSerialization();
DefaultLanguage();
return this;
}
public IBuildingStage QuickStart(string apiKey, IStartFormFactory StartFormFactory)
{
_apiKey = apiKey;
_factory = StartFormFactory;
DefaultMessageLoop();
NoProxy();
OnlyStart();
NoSerialization();
DefaultLanguage();
return this;
}
#endregion
#region "Step 2 (Message Loop)"
public IStartFormSelectionStage DefaultMessageLoop()
{
_messageLoopFactory = new FormBaseMessageLoop();
return this;
}
public IStartFormSelectionStage MinimalMessageLoop()
{
_messageLoopFactory = new MinimalMessageLoop();
return this;
}
public IStartFormSelectionStage CustomMessageLoop(IMessageLoopFactory messageLoopClass)
{
_messageLoopFactory = messageLoopClass;
return this;
}
public IStartFormSelectionStage CustomMessageLoop<T>()
where T : class, new()
{
_messageLoopFactory = typeof(T).GetConstructor(new Type[] { })?.Invoke(new object[] { }) as IMessageLoopFactory;
return this;
}
#endregion
#region "Step 3 (Start Form/Factory)"
public INetworkingSelectionStage WithStartForm(Type startFormClass)
{
_factory = new DefaultStartFormFactory(startFormClass);
return this;
}
public INetworkingSelectionStage WithStartForm<T>()
where T : FormBase, new()
{
_factory = new DefaultStartFormFactory(typeof(T));
return this;
}
public INetworkingSelectionStage WithServiceProvider(Type startFormClass, IServiceProvider serviceProvider)
{
_factory = new ServiceProviderStartFormFactory(startFormClass, serviceProvider);
return this;
}
public INetworkingSelectionStage WithServiceProvider<T>(IServiceProvider serviceProvider)
where T : FormBase
{
_factory = new ServiceProviderStartFormFactory<T>(serviceProvider);
return this;
}
public INetworkingSelectionStage WithStartFormFactory(IStartFormFactory factory)
{
_factory = factory;
return this;
}
#endregion
#region "Step 4 (Network Settings)"
public IBotCommandsStage WithProxy(string proxyAddress)
{
var url = new Uri(proxyAddress);
_client = new MessageClient(_apiKey, url)
{
TelegramClient =
{
Timeout = new TimeSpan(0, 1, 0)
}
};
return this;
}
public IBotCommandsStage NoProxy()
{
_client = new MessageClient(_apiKey)
{
TelegramClient =
{
Timeout = new TimeSpan(0, 1, 0)
}
};
return this;
}
public IBotCommandsStage WithBotClient(TelegramBotClient tgclient)
{
_client = new MessageClient(_apiKey, tgclient)
{
TelegramClient =
{
Timeout = new TimeSpan(0, 1, 0)
}
};
return this;
}
public IBotCommandsStage WithHostAndPort(string proxyHost, int proxyPort)
{
_client = new MessageClient(_apiKey, proxyHost, proxyPort)
{
TelegramClient =
{
Timeout = new TimeSpan(0, 1, 0)
}
};
return this;
}
public IBotCommandsStage WithHttpClient(HttpClient tgclient)
{
_client = new MessageClient(_apiKey, tgclient)
{
TelegramClient =
{
Timeout = new TimeSpan(0, 1, 0)
}
};
return this;
}
#endregion
#region "Step 5 (Bot Commands)"
public ISessionSerializationStage NoCommands()
{
return this;
}
public ISessionSerializationStage OnlyStart()
{
BotCommandScopes.Start("Starts the bot");
return this;
}
public ISessionSerializationStage DefaultCommands()
{
BotCommandScopes.Start("Starts the bot");
BotCommandScopes.Help("Should show you some help");
BotCommandScopes.Settings("Should show you some settings");
return this;
}
public ISessionSerializationStage CustomCommands(Action<Dictionary<BotCommandScope, List<BotCommand>>> action)
{
action?.Invoke(BotCommandScopes);
return this;
}
#endregion
#region "Step 6 (Serialization)"
public ILanguageSelectionStage NoSerialization()
{
return this;
}
public ILanguageSelectionStage UseSerialization(IStateMachine machine)
{
_statemachine = machine;
return this;
}
public ILanguageSelectionStage UseJSON(string path)
{
_statemachine = new JsonStateMachine(path);
return this;
}
public ILanguageSelectionStage UseSimpleJSON(string path)
{
_statemachine = new SimpleJsonStateMachine(path);
return this;
}
public ILanguageSelectionStage UseXML(string path)
{
_statemachine = new XmlStateMachine(path);
return this;
}
#endregion
#region "Step 7 (Language)"
public IBuildingStage DefaultLanguage()
{
return this;
}
public IBuildingStage UseEnglish()
{
Default.Language = new English();
return this;
}
public IBuildingStage UseGerman()
{
Default.Language = new German();
return this;
}
public IBuildingStage Custom(Localization language)
{
Default.Language = language;
return this;
}
#endregion
public BotBase Build()
{
var bb = new BotBase
{
ApiKey = _apiKey,
StartFormFactory = _factory,
Client = _client
};
bb.Sessions.Client = bb.Client;
bb.BotCommandScopes = BotCommandScopes;
bb.StateMachine = _statemachine;
bb.MessageLoopFactory = _messageLoopFactory;
bb.MessageLoopFactory.UnhandledCall += bb.MessageLoopFactory_UnhandledCall;
return bb;
}
}
}
/// <summary>
/// Contains different Botcommands for different areas.
/// </summary>
private Dictionary<BotCommandScope, List<BotCommand>> BotCommandScopes { get; } = new();
public BotBase Build()
{
var bb = new BotBase
{
ApiKey = _apiKey,
StartFormFactory = _factory,
Client = _client
};
bb.Sessions.Client = bb.Client;
bb.BotCommandScopes = BotCommandScopes;
bb.StateMachine = _statemachine;
bb.MessageLoopFactory = _messageLoopFactory;
bb.MessageLoopFactory.UnhandledCall += bb.MessageLoopFactory_UnhandledCall;
return bb;
}
public static IAPIKeySelectionStage Create()
{
return new BotBaseBuilder();
}
#region "Step 1 (Basic Stuff)"
public IMessageLoopSelectionStage WithAPIKey(string apiKey)
{
_apiKey = apiKey;
return this;
}
public IBuildingStage QuickStart(string apiKey, Type StartForm)
{
_apiKey = apiKey;
_factory = new DefaultStartFormFactory(StartForm);
DefaultMessageLoop();
NoProxy();
OnlyStart();
NoSerialization();
DefaultLanguage();
return this;
}
public IBuildingStage QuickStart<T>(string apiKey)
where T : FormBase
{
_apiKey = apiKey;
_factory = new DefaultStartFormFactory(typeof(T));
DefaultMessageLoop();
NoProxy();
OnlyStart();
NoSerialization();
DefaultLanguage();
return this;
}
public IBuildingStage QuickStart(string apiKey, IStartFormFactory StartFormFactory)
{
_apiKey = apiKey;
_factory = StartFormFactory;
DefaultMessageLoop();
NoProxy();
OnlyStart();
NoSerialization();
DefaultLanguage();
return this;
}
#endregion
#region "Step 2 (Message Loop)"
public IStartFormSelectionStage DefaultMessageLoop()
{
_messageLoopFactory = new FormBaseMessageLoop();
return this;
}
public IStartFormSelectionStage MinimalMessageLoop()
{
_messageLoopFactory = new MinimalMessageLoop();
return this;
}
public IStartFormSelectionStage CustomMessageLoop(IMessageLoopFactory messageLoopClass)
{
_messageLoopFactory = messageLoopClass;
return this;
}
public IStartFormSelectionStage CustomMessageLoop<T>()
where T : class, new()
{
_messageLoopFactory =
typeof(T).GetConstructor(new Type[] { })?.Invoke(new object[] { }) as IMessageLoopFactory;
return this;
}
#endregion
#region "Step 3 (Start Form/Factory)"
public INetworkingSelectionStage WithStartForm(Type startFormClass)
{
_factory = new DefaultStartFormFactory(startFormClass);
return this;
}
public INetworkingSelectionStage WithStartForm<T>()
where T : FormBase, new()
{
_factory = new DefaultStartFormFactory(typeof(T));
return this;
}
public INetworkingSelectionStage WithServiceProvider(Type startFormClass, IServiceProvider serviceProvider)
{
_factory = new ServiceProviderStartFormFactory(startFormClass, serviceProvider);
return this;
}
public INetworkingSelectionStage WithServiceProvider<T>(IServiceProvider serviceProvider)
where T : FormBase
{
_factory = new ServiceProviderStartFormFactory<T>(serviceProvider);
return this;
}
public INetworkingSelectionStage WithStartFormFactory(IStartFormFactory factory)
{
_factory = factory;
return this;
}
#endregion
#region "Step 4 (Network Settings)"
public IBotCommandsStage WithProxy(string proxyAddress)
{
var url = new Uri(proxyAddress);
_client = new MessageClient(_apiKey, url)
{
TelegramClient =
{
Timeout = new TimeSpan(0, 1, 0)
}
};
return this;
}
public IBotCommandsStage NoProxy()
{
_client = new MessageClient(_apiKey)
{
TelegramClient =
{
Timeout = new TimeSpan(0, 1, 0)
}
};
return this;
}
public IBotCommandsStage WithBotClient(TelegramBotClient tgclient)
{
_client = new MessageClient(_apiKey, tgclient)
{
TelegramClient =
{
Timeout = new TimeSpan(0, 1, 0)
}
};
return this;
}
public IBotCommandsStage WithHostAndPort(string proxyHost, int proxyPort)
{
_client = new MessageClient(_apiKey, proxyHost, proxyPort)
{
TelegramClient =
{
Timeout = new TimeSpan(0, 1, 0)
}
};
return this;
}
public IBotCommandsStage WithHttpClient(HttpClient tgclient)
{
_client = new MessageClient(_apiKey, tgclient)
{
TelegramClient =
{
Timeout = new TimeSpan(0, 1, 0)
}
};
return this;
}
#endregion
#region "Step 5 (Bot Commands)"
public ISessionSerializationStage NoCommands()
{
return this;
}
public ISessionSerializationStage OnlyStart()
{
BotCommandScopes.Start("Starts the bot");
return this;
}
public ISessionSerializationStage DefaultCommands()
{
BotCommandScopes.Start("Starts the bot");
BotCommandScopes.Help("Should show you some help");
BotCommandScopes.Settings("Should show you some settings");
return this;
}
public ISessionSerializationStage CustomCommands(Action<Dictionary<BotCommandScope, List<BotCommand>>> action)
{
action?.Invoke(BotCommandScopes);
return this;
}
#endregion
#region "Step 6 (Serialization)"
public ILanguageSelectionStage NoSerialization()
{
return this;
}
public ILanguageSelectionStage UseSerialization(IStateMachine machine)
{
_statemachine = machine;
return this;
}
public ILanguageSelectionStage UseJSON(string path)
{
_statemachine = new JsonStateMachine(path);
return this;
}
public ILanguageSelectionStage UseSimpleJSON(string path)
{
_statemachine = new SimpleJsonStateMachine(path);
return this;
}
public ILanguageSelectionStage UseXML(string path)
{
_statemachine = new XmlStateMachine(path);
return this;
}
#endregion
#region "Step 7 (Language)"
public IBuildingStage DefaultLanguage()
{
return this;
}
public IBuildingStage UseEnglish()
{
Default.Language = new English();
return this;
}
public IBuildingStage UseGerman()
{
Default.Language = new German();
return this;
}
public IBuildingStage Custom(Localization language)
{
Default.Language = language;
return this;
}
#endregion
}
@@ -2,42 +2,41 @@
using TelegramBotBase.Form;
using TelegramBotBase.Interfaces;
namespace TelegramBotBase.Builder.Interfaces
namespace TelegramBotBase.Builder.Interfaces;
public interface IAPIKeySelectionStage
{
public interface IAPIKeySelectionStage
{
/// <summary>
/// Sets the API Key which will be used by the telegram bot client.
/// </summary>
/// <param name="apiKey"></param>
/// <returns></returns>
IMessageLoopSelectionStage WithAPIKey(string apiKey);
/// <summary>
/// Sets the API Key which will be used by the telegram bot client.
/// </summary>
/// <param name="apiKey"></param>
/// <returns></returns>
IMessageLoopSelectionStage WithAPIKey(string apiKey);
/// <summary>
/// Quick and easy way to create a BotBase instance.
/// Uses: DefaultMessageLoop, NoProxy, OnlyStart, NoSerialization, DefaultLanguage
/// </summary>
/// <param name="apiKey"></param>
/// <param name="StartForm"></param>
/// <returns></returns>
IBuildingStage QuickStart(string apiKey, Type StartForm);
/// <summary>
/// Quick and easy way to create a BotBase instance.
/// Uses: DefaultMessageLoop, NoProxy, OnlyStart, NoSerialization, DefaultLanguage
/// </summary>
/// <param name="apiKey"></param>
/// <param name="StartForm"></param>
/// <returns></returns>
IBuildingStage QuickStart(string apiKey, Type StartForm);
/// <summary>
/// Quick and easy way to create a BotBase instance.
/// Uses: DefaultMessageLoop, NoProxy, OnlyStart, NoSerialization, DefaultLanguage
/// </summary>
/// <param name="apiKey"></param>
/// <returns></returns>
IBuildingStage QuickStart<T>(string apiKey) where T : FormBase;
/// <summary>
/// Quick and easy way to create a BotBase instance.
/// Uses: DefaultMessageLoop, NoProxy, OnlyStart, NoSerialization, DefaultLanguage
/// </summary>
/// <param name="apiKey"></param>
/// <returns></returns>
IBuildingStage QuickStart<T>(string apiKey) where T : FormBase;
/// <summary>
/// Quick and easy way to create a BotBase instance.
/// Uses: DefaultMessageLoop, NoProxy, OnlyStart, NoSerialization, DefaultLanguage
/// </summary>
/// <param name="apiKey"></param>
/// <param name="StartFormFactory"></param>
/// <returns></returns>
IBuildingStage QuickStart(string apiKey, IStartFormFactory StartFormFactory);
}
}
/// <summary>
/// Quick and easy way to create a BotBase instance.
/// Uses: DefaultMessageLoop, NoProxy, OnlyStart, NoSerialization, DefaultLanguage
/// </summary>
/// <param name="apiKey"></param>
/// <param name="StartFormFactory"></param>
/// <returns></returns>
IBuildingStage QuickStart(string apiKey, IStartFormFactory StartFormFactory);
}
@@ -2,39 +2,35 @@
using System.Collections.Generic;
using Telegram.Bot.Types;
namespace TelegramBotBase.Builder.Interfaces
namespace TelegramBotBase.Builder.Interfaces;
public interface IBotCommandsStage
{
public interface IBotCommandsStage
{
/// <summary>
/// Does not create any commands.
/// </summary>
/// <returns></returns>
ISessionSerializationStage NoCommands();
/// <summary>
/// Does not create any commands.
/// </summary>
/// <returns></returns>
ISessionSerializationStage NoCommands();
/// <summary>
/// Creates default commands for start, help and settings.
/// </summary>
/// <returns></returns>
ISessionSerializationStage DefaultCommands();
/// <summary>
/// Creates default commands for start, help and settings.
/// </summary>
/// <returns></returns>
ISessionSerializationStage DefaultCommands();
/// <summary>
/// Only adds the start command.
/// </summary>
/// <returns></returns>
ISessionSerializationStage OnlyStart();
/// <summary>
/// Only adds the start command.
/// </summary>
/// <returns></returns>
ISessionSerializationStage OnlyStart();
/// <summary>
/// Gives you the ability to add custom commands.
/// </summary>
/// <param name="action"></param>
/// <returns></returns>
ISessionSerializationStage CustomCommands(Action<Dictionary<BotCommandScope, List<BotCommand>>> action);
}
}
/// <summary>
/// Gives you the ability to add custom commands.
/// </summary>
/// <param name="action"></param>
/// <returns></returns>
ISessionSerializationStage CustomCommands(Action<Dictionary<BotCommandScope, List<BotCommand>>> action);
}
@@ -1,7 +1,6 @@
namespace TelegramBotBase.Builder.Interfaces
namespace TelegramBotBase.Builder.Interfaces;
public interface IBuildingStage
{
public interface IBuildingStage
{
BotBase Build();
}
}
BotBase Build();
}
@@ -1,33 +1,30 @@
using TelegramBotBase.Localizations;
namespace TelegramBotBase.Builder.Interfaces
namespace TelegramBotBase.Builder.Interfaces;
public interface ILanguageSelectionStage
{
public interface ILanguageSelectionStage
{
/// <summary>
/// Selects the default language for control usage. (English)
/// </summary>
/// <returns></returns>
IBuildingStage DefaultLanguage();
/// <summary>
/// Selects the default language for control usage. (English)
/// </summary>
/// <returns></returns>
IBuildingStage DefaultLanguage();
/// <summary>
/// Selects english as the default language for control labels.
/// </summary>
/// <returns></returns>
IBuildingStage UseEnglish();
/// <summary>
/// Selects english as the default language for control labels.
/// </summary>
/// <returns></returns>
IBuildingStage UseEnglish();
/// <summary>
/// Selects german as the default language for control labels.
/// </summary>
/// <returns></returns>
IBuildingStage UseGerman();
/// <summary>
/// Selects german as the default language for control labels.
/// </summary>
/// <returns></returns>
IBuildingStage UseGerman();
/// <summary>
/// Selects a custom language as the default language for control labels.
/// </summary>
/// <returns></returns>
IBuildingStage Custom(Localization language);
}
}
/// <summary>
/// Selects a custom language as the default language for control labels.
/// </summary>
/// <returns></returns>
IBuildingStage Custom(Localization language);
}
@@ -1,40 +1,36 @@
using TelegramBotBase.Interfaces;
namespace TelegramBotBase.Builder.Interfaces
namespace TelegramBotBase.Builder.Interfaces;
public interface IMessageLoopSelectionStage
{
public interface IMessageLoopSelectionStage
{
/// <summary>
/// Chooses a default message loop.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
IStartFormSelectionStage DefaultMessageLoop();
/// <summary>
/// Chooses a default message loop.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
IStartFormSelectionStage DefaultMessageLoop();
/// <summary>
/// Chooses a minimalistic message loop, which catches all update types and only calls the Load function.
/// </summary>
/// <returns></returns>
IStartFormSelectionStage MinimalMessageLoop();
/// <summary>
/// Chooses a minimalistic message loop, which catches all update types and only calls the Load function.
/// </summary>
/// <returns></returns>
IStartFormSelectionStage MinimalMessageLoop();
/// <summary>
/// Chooses a custom message loop.
/// </summary>
/// <param name="startFormClass"></param>
/// <returns></returns>
IStartFormSelectionStage CustomMessageLoop(IMessageLoopFactory startFormClass);
/// <summary>
/// Chooses a custom message loop.
/// </summary>
/// <param name="startFormClass"></param>
/// <returns></returns>
IStartFormSelectionStage CustomMessageLoop(IMessageLoopFactory startFormClass);
/// <summary>
/// Chooses a custom message loop.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
IStartFormSelectionStage CustomMessageLoop<T>() where T : class, new();
}
}
/// <summary>
/// Chooses a custom message loop.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
IStartFormSelectionStage CustomMessageLoop<T>() where T : class, new();
}
@@ -1,48 +1,44 @@
using System.Net.Http;
using Telegram.Bot;
namespace TelegramBotBase.Builder.Interfaces
namespace TelegramBotBase.Builder.Interfaces;
public interface INetworkingSelectionStage
{
public interface INetworkingSelectionStage
{
/// <summary>
/// Chooses a proxy as network configuration.
/// </summary>
/// <param name="proxyAddress"></param>
/// <returns></returns>
IBotCommandsStage WithProxy(string proxyAddress);
/// <summary>
/// Chooses a proxy as network configuration.
/// </summary>
/// <param name="proxyAddress"></param>
/// <returns></returns>
IBotCommandsStage WithProxy(string proxyAddress);
/// <summary>
/// Do not choose a proxy as network configuration.
/// </summary>
/// <returns></returns>
IBotCommandsStage NoProxy();
/// <summary>
/// Do not choose a proxy as network configuration.
/// </summary>
/// <returns></returns>
IBotCommandsStage NoProxy();
/// <summary>
/// Chooses a custom instance of TelegramBotClient.
/// </summary>
/// <param name="client"></param>
/// <returns></returns>
IBotCommandsStage WithBotClient(TelegramBotClient client);
/// <summary>
/// Chooses a custom instance of TelegramBotClient.
/// </summary>
/// <param name="client"></param>
/// <returns></returns>
IBotCommandsStage WithBotClient(TelegramBotClient client);
/// <summary>
/// Sets the custom proxy host and port.
/// </summary>
/// <param name="proxyHost"></param>
/// <param name="Port"></param>
/// <returns></returns>
IBotCommandsStage WithHostAndPort(string proxyHost, int Port);
/// <summary>
/// Sets the custom proxy host and port.
/// </summary>
/// <param name="proxyHost"></param>
/// <param name="Port"></param>
/// <returns></returns>
IBotCommandsStage WithHostAndPort(string proxyHost, int Port);
/// <summary>
/// Uses a custom http client.
/// </summary>
/// <param name="client"></param>
/// <returns></returns>
IBotCommandsStage WithHttpClient(HttpClient client);
}
}
/// <summary>
/// Uses a custom http client.
/// </summary>
/// <param name="client"></param>
/// <returns></returns>
IBotCommandsStage WithHttpClient(HttpClient client);
}
@@ -1,46 +1,44 @@
using TelegramBotBase.Interfaces;
namespace TelegramBotBase.Builder.Interfaces
namespace TelegramBotBase.Builder.Interfaces;
public interface ISessionSerializationStage
{
public interface ISessionSerializationStage
{
/// <summary>
/// Do not uses serialization.
/// </summary>
/// <returns></returns>
ILanguageSelectionStage NoSerialization();
/// <summary>
/// Do not uses serialization.
/// </summary>
/// <returns></returns>
ILanguageSelectionStage NoSerialization();
/// <summary>
/// Sets the state machine for serialization.
/// </summary>
/// <param name="machine"></param>
/// <returns></returns>
ILanguageSelectionStage UseSerialization(IStateMachine machine);
/// <summary>
/// Sets the state machine for serialization.
/// </summary>
/// <param name="machine"></param>
/// <returns></returns>
ILanguageSelectionStage UseSerialization(IStateMachine machine);
/// <summary>
/// Using the complex version of .Net JSON, which can serialize all objects.
/// </summary>
/// <param name="path"></param>
/// <returns></returns>
ILanguageSelectionStage UseJSON(string path);
/// <summary>
/// Using the complex version of .Net JSON, which can serialize all objects.
/// </summary>
/// <param name="path"></param>
/// <returns></returns>
ILanguageSelectionStage UseJSON(string path);
/// <summary>
/// Use the easy version of .Net JSON, which can serialize basic types, but not generics and others.
/// </summary>
/// <param name="path"></param>
/// <returns></returns>
ILanguageSelectionStage UseSimpleJSON(string path);
/// <summary>
/// Use the easy version of .Net JSON, which can serialize basic types, but not generics and others.
/// </summary>
/// <param name="path"></param>
/// <returns></returns>
ILanguageSelectionStage UseSimpleJSON(string path);
/// <summary>
/// Uses the XML serializer for session serialization.
/// </summary>
/// <param name="path"></param>
/// <returns></returns>
ILanguageSelectionStage UseXML(string path);
}
}
/// <summary>
/// Uses the XML serializer for session serialization.
/// </summary>
/// <param name="path"></param>
/// <returns></returns>
ILanguageSelectionStage UseXML(string path);
}
@@ -2,47 +2,44 @@
using TelegramBotBase.Form;
using TelegramBotBase.Interfaces;
namespace TelegramBotBase.Builder.Interfaces
namespace TelegramBotBase.Builder.Interfaces;
public interface IStartFormSelectionStage
{
public interface IStartFormSelectionStage
{
/// <summary>
/// Chooses a start form type which will be used for new sessions.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
INetworkingSelectionStage WithStartForm(Type startFormClass);
/// <summary>
/// Chooses a start form type which will be used for new sessions.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
INetworkingSelectionStage WithStartForm(Type startFormClass);
/// <summary>
/// Chooses a generic start form which will be used for new sessions.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
INetworkingSelectionStage WithStartForm<T>() where T : FormBase, new();
/// <summary>
/// Chooses a generic start form which will be used for new sessions.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
INetworkingSelectionStage WithStartForm<T>() where T : FormBase, new();
/// <summary>
/// Chooses a StartFormFactory which will be use for new sessions.
/// </summary>
/// <param name="startFormClass"></param>
/// <param name="serviceProvider"></param>
/// <returns></returns>
INetworkingSelectionStage WithServiceProvider(Type startFormClass, IServiceProvider serviceProvider);
/// <summary>
/// Chooses a StartFormFactory which will be use for new sessions.
/// </summary>
/// <param name="startFormClass"></param>
/// <param name="serviceProvider"></param>
/// <returns></returns>
INetworkingSelectionStage WithServiceProvider(Type startFormClass, IServiceProvider serviceProvider);
/// <summary>
/// Chooses a StartFormFactory which will be use for new sessions.
/// </summary>
/// <param name="serviceProvider"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
INetworkingSelectionStage WithServiceProvider<T>(IServiceProvider serviceProvider) where T : FormBase;
/// <summary>
/// Chooses a StartFormFactory which will be use for new sessions.
/// </summary>
/// <param name="serviceProvider"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
INetworkingSelectionStage WithServiceProvider<T>(IServiceProvider serviceProvider) where T : FormBase;
/// <summary>
/// Chooses a StartFormFactory which will be use for new sessions.
/// </summary>
/// <param name="factory"></param>
/// <returns></returns>
INetworkingSelectionStage WithStartFormFactory(IStartFormFactory factory);
}
}
/// <summary>
/// Chooses a StartFormFactory which will be use for new sessions.
/// </summary>
/// <param name="factory"></param>
/// <returns></returns>
INetworkingSelectionStage WithStartFormFactory(IStartFormFactory factory);
}
+170 -130
View File
@@ -2,141 +2,181 @@
using System.Linq;
using Telegram.Bot.Types;
namespace TelegramBotBase.Commands
namespace TelegramBotBase.Commands;
public static class Extensions
{
public static class Extensions
/// <summary>
/// Adding the command with a description.
/// </summary>
/// <param name="cmds"></param>
/// <param name="command"></param>
/// <param name="description"></param>
public static void Add(this Dictionary<BotCommandScope, List<BotCommand>> cmds, string command,
string description, BotCommandScope scope = null)
{
/// <summary>
/// Adding the command with a description.
/// </summary>
/// <param name="cmds"></param>
/// <param name="command"></param>
/// <param name="description"></param>
public static void Add(this Dictionary<BotCommandScope, List<BotCommand>> cmds, string command, string description, BotCommandScope scope = null)
if (scope == null)
{
if (scope == null)
{
scope = BotCommandScope.Default();
}
var item = cmds.FirstOrDefault(a => a.Key.Type == scope.Type);
if (item.Value != null)
{
item.Value.Add(new BotCommand { Command = command, Description = description });
}
else
{
cmds.Add(scope, new List<BotCommand> { new BotCommand { Command = command, Description = description } });
}
scope = BotCommandScope.Default();
}
/// <summary>
/// Adding the command with a description.
/// </summary>
/// <param name="cmds"></param>
/// <param name="command"></param>
/// <param name="description"></param>
public static void Clear(this Dictionary<BotCommandScope, List<BotCommand>> cmds, BotCommandScope scope = null)
var item = cmds.FirstOrDefault(a => a.Key.Type == scope.Type);
if (item.Value != null)
{
if (scope == null)
{
scope = BotCommandScope.Default();
}
var item = cmds.FirstOrDefault(a => a.Key.Type == scope.Type);
if (item.Key != null)
{
cmds[item.Key] = null;
}
else
{
cmds[scope] = null;
}
item.Value.Add(new BotCommand { Command = command, Description = description });
}
else
{
cmds.Add(scope, new List<BotCommand> { new() { Command = command, Description = description } });
}
/// <summary>
/// Adding the default /start command with a description.
/// </summary>
/// <param name="cmds"></param>
/// <param name="description"></param>
public static void Start(this Dictionary<BotCommandScope, List<BotCommand>> cmds, string description) => Add(cmds, "start", description);
/// <summary>
/// Adding the default /help command with a description.
/// </summary>
/// <param name="cmds"></param>
/// <param name="description"></param>
public static void Help(this Dictionary<BotCommandScope, List<BotCommand>> cmds, string description) => Add(cmds, "help", description);
/// <summary>
/// Adding the default /settings command with a description.
/// </summary>
/// <param name="cmds"></param>
/// <param name="description"></param>
public static void Settings(this Dictionary<BotCommandScope, List<BotCommand>> cmds, string description) => Add(cmds, "settings", description);
/// <summary>
/// Clears all default commands.
/// </summary>
/// <param name="cmds"></param>
public static void ClearDefaultCommands(this Dictionary<BotCommandScope, List<BotCommand>> cmds) => Clear(cmds);
/// <summary>
/// Clears all commands of a specific device.
/// </summary>
/// <param name="cmds"></param>
public static void ClearChatCommands(this Dictionary<BotCommandScope, List<BotCommand>> cmds, long deviceId) => Clear(cmds, new BotCommandScopeChat { ChatId = deviceId });
/// <summary>
/// Adding a chat command with a description.
/// </summary>
/// <param name="cmds"></param>
/// <param name="command"></param>
/// <param name="description"></param>
public static void AddChatCommand(this Dictionary<BotCommandScope, List<BotCommand>> cmds, long deviceId, string command, string description) => Add(cmds, command, description, new BotCommandScopeChat { ChatId = deviceId });
/// <summary>
/// Adding a group command with a description.
/// </summary>
/// <param name="cmds"></param>
/// <param name="command"></param>
/// <param name="description"></param>
public static void AddGroupCommand(this Dictionary<BotCommandScope, List<BotCommand>> cmds, string command, string description) => Add(cmds, command, description, new BotCommandScopeAllGroupChats());
/// <summary>
/// Clears all group commands.
/// </summary>
/// <param name="cmds"></param>
public static void ClearGroupCommands(this Dictionary<BotCommandScope, List<BotCommand>> cmds) => Clear(cmds, new BotCommandScopeAllGroupChats());
/// <summary>
/// Adding group admin command with a description.
/// </summary>
/// <param name="cmds"></param>
/// <param name="command"></param>
/// <param name="description"></param>
public static void AddGroupAdminCommand(this Dictionary<BotCommandScope, List<BotCommand>> cmds, string command, string description) => Add(cmds, command, description, new BotCommandScopeAllChatAdministrators());
/// <summary>
/// Clears all group admin commands.
/// </summary>
/// <param name="cmds"></param>
public static void ClearGroupAdminCommand(this Dictionary<BotCommandScope, List<BotCommand>> cmds) => Clear(cmds, new BotCommandScopeAllChatAdministrators());
/// <summary>
/// Adding a privat command with a description.
/// </summary>
/// <param name="cmds"></param>
/// <param name="command"></param>
/// <param name="description"></param>
public static void AddPrivateChatCommand(this Dictionary<BotCommandScope, List<BotCommand>> cmds, string command, string description) => Add(cmds, command, description, new BotCommandScopeAllPrivateChats());
/// <summary>
/// Clears all private commands.
/// </summary>
/// <param name="cmds"></param>
public static void ClearPrivateChatCommand(this Dictionary<BotCommandScope, List<BotCommand>> cmds) => Clear(cmds, new BotCommandScopeAllPrivateChats());
}
}
/// <summary>
/// Adding the command with a description.
/// </summary>
/// <param name="cmds"></param>
/// <param name="command"></param>
/// <param name="description"></param>
public static void Clear(this Dictionary<BotCommandScope, List<BotCommand>> cmds, BotCommandScope scope = null)
{
if (scope == null)
{
scope = BotCommandScope.Default();
}
var item = cmds.FirstOrDefault(a => a.Key.Type == scope.Type);
if (item.Key != null)
{
cmds[item.Key] = null;
}
else
{
cmds[scope] = null;
}
}
/// <summary>
/// Adding the default /start command with a description.
/// </summary>
/// <param name="cmds"></param>
/// <param name="description"></param>
public static void Start(this Dictionary<BotCommandScope, List<BotCommand>> cmds, string description)
{
Add(cmds, "start", description);
}
/// <summary>
/// Adding the default /help command with a description.
/// </summary>
/// <param name="cmds"></param>
/// <param name="description"></param>
public static void Help(this Dictionary<BotCommandScope, List<BotCommand>> cmds, string description)
{
Add(cmds, "help", description);
}
/// <summary>
/// Adding the default /settings command with a description.
/// </summary>
/// <param name="cmds"></param>
/// <param name="description"></param>
public static void Settings(this Dictionary<BotCommandScope, List<BotCommand>> cmds, string description)
{
Add(cmds, "settings", description);
}
/// <summary>
/// Clears all default commands.
/// </summary>
/// <param name="cmds"></param>
public static void ClearDefaultCommands(this Dictionary<BotCommandScope, List<BotCommand>> cmds)
{
Clear(cmds);
}
/// <summary>
/// Clears all commands of a specific device.
/// </summary>
/// <param name="cmds"></param>
public static void ClearChatCommands(this Dictionary<BotCommandScope, List<BotCommand>> cmds, long deviceId)
{
Clear(cmds, new BotCommandScopeChat { ChatId = deviceId });
}
/// <summary>
/// Adding a chat command with a description.
/// </summary>
/// <param name="cmds"></param>
/// <param name="command"></param>
/// <param name="description"></param>
public static void AddChatCommand(this Dictionary<BotCommandScope, List<BotCommand>> cmds, long deviceId,
string command, string description)
{
Add(cmds, command, description, new BotCommandScopeChat { ChatId = deviceId });
}
/// <summary>
/// Adding a group command with a description.
/// </summary>
/// <param name="cmds"></param>
/// <param name="command"></param>
/// <param name="description"></param>
public static void AddGroupCommand(this Dictionary<BotCommandScope, List<BotCommand>> cmds, string command,
string description)
{
Add(cmds, command, description, new BotCommandScopeAllGroupChats());
}
/// <summary>
/// Clears all group commands.
/// </summary>
/// <param name="cmds"></param>
public static void ClearGroupCommands(this Dictionary<BotCommandScope, List<BotCommand>> cmds)
{
Clear(cmds, new BotCommandScopeAllGroupChats());
}
/// <summary>
/// Adding group admin command with a description.
/// </summary>
/// <param name="cmds"></param>
/// <param name="command"></param>
/// <param name="description"></param>
public static void AddGroupAdminCommand(this Dictionary<BotCommandScope, List<BotCommand>> cmds, string command,
string description)
{
Add(cmds, command, description, new BotCommandScopeAllChatAdministrators());
}
/// <summary>
/// Clears all group admin commands.
/// </summary>
/// <param name="cmds"></param>
public static void ClearGroupAdminCommand(this Dictionary<BotCommandScope, List<BotCommand>> cmds)
{
Clear(cmds, new BotCommandScopeAllChatAdministrators());
}
/// <summary>
/// Adding a privat command with a description.
/// </summary>
/// <param name="cmds"></param>
/// <param name="command"></param>
/// <param name="description"></param>
public static void AddPrivateChatCommand(this Dictionary<BotCommandScope, List<BotCommand>> cmds,
string command, string description)
{
Add(cmds, command, description, new BotCommandScopeAllPrivateChats());
}
/// <summary>
/// Clears all private commands.
/// </summary>
/// <param name="cmds"></param>
public static void ClearPrivateChatCommand(this Dictionary<BotCommandScope, List<BotCommand>> cmds)
{
Clear(cmds, new BotCommandScopeAllPrivateChats());
}
}
+13 -15
View File
@@ -1,21 +1,19 @@
namespace TelegramBotBase.Constants
namespace TelegramBotBase.Constants;
public static class Telegram
{
public static class Telegram
{
/// <summary>
/// The maximum length of message text before the API throws an exception. (We will catch it before)
/// </summary>
public const int MaxMessageLength = 4096;
/// <summary>
/// The maximum length of message text before the API throws an exception. (We will catch it before)
/// </summary>
public const int MaxMessageLength = 4096;
public const int MaxInlineKeyBoardRows = 13;
public const int MaxInlineKeyBoardRows = 13;
public const int MaxInlineKeyBoardCols = 8;
public const int MaxInlineKeyBoardCols = 8;
public const int MaxReplyKeyboardRows = 25;
public const int MaxReplyKeyboardRows = 25;
public const int MaxReplyKeyboardCols = 12;
public const int MaxReplyKeyboardCols = 12;
public const int MessageDeletionsPerSecond = 30;
}
}
public const int MessageDeletionsPerSecond = 30;
}
File diff suppressed because it is too large Load Diff
+95 -87
View File
@@ -4,93 +4,101 @@ using System.Diagnostics;
using System.Linq;
using TelegramBotBase.Form;
namespace TelegramBotBase.Controls.Hybrid
namespace TelegramBotBase.Controls.Hybrid;
[DebuggerDisplay("{Count} columns")]
public class ButtonRow
{
[DebuggerDisplay("{Count} columns")]
public class ButtonRow
private List<ButtonBase> _buttons = new();
public ButtonRow()
{
private List<ButtonBase> _buttons = new List<ButtonBase>();
public ButtonRow()
{
}
public ButtonRow(params ButtonBase[] buttons)
{
_buttons = buttons.ToList();
}
public ButtonBase this[int index] => _buttons[index];
public int Count => _buttons.Count;
public void Add(ButtonBase button)
{
_buttons.Add(button);
}
public void AddRange(ButtonBase button)
{
_buttons.Add(button);
}
public void Insert(int index, ButtonBase button)
{
_buttons.Insert(index, button);
}
public IEnumerator<ButtonBase> GetEnumerator()
{
return _buttons.GetEnumerator();
}
public ButtonBase[] ToArray()
{
return _buttons.ToArray();
}
public List<ButtonBase> ToList()
{
return _buttons.ToList();
}
public bool Matches(string text, bool useText = true)
{
foreach (var b in _buttons)
{
if (useText && b.Text.Trim().Equals(text, StringComparison.InvariantCultureIgnoreCase))
return true;
if (!useText && b.Value.Equals(text, StringComparison.InvariantCultureIgnoreCase))
return true;
}
return false;
}
/// <summary>
/// Returns the button inside of the row which matches.
/// </summary>
/// <param name="text"></param>
/// <param name="useText"></param>
/// <returns></returns>
public ButtonBase GetButtonMatch(string text, bool useText = true)
{
foreach (var b in _buttons)
{
if (useText && b.Text.Trim().Equals(text, StringComparison.InvariantCultureIgnoreCase))
return b;
if (!useText && b.Value.Equals(text, StringComparison.InvariantCultureIgnoreCase))
return b;
}
return null;
}
public static implicit operator ButtonRow(List<ButtonBase> list)
{
return new ButtonRow { _buttons = list };
}
}
}
public ButtonRow(params ButtonBase[] buttons)
{
_buttons = buttons.ToList();
}
public ButtonBase this[int index] => _buttons[index];
public int Count => _buttons.Count;
public void Add(ButtonBase button)
{
_buttons.Add(button);
}
public void AddRange(ButtonBase button)
{
_buttons.Add(button);
}
public void Insert(int index, ButtonBase button)
{
_buttons.Insert(index, button);
}
public IEnumerator<ButtonBase> GetEnumerator()
{
return _buttons.GetEnumerator();
}
public ButtonBase[] ToArray()
{
return _buttons.ToArray();
}
public List<ButtonBase> ToList()
{
return _buttons.ToList();
}
public bool Matches(string text, bool useText = true)
{
foreach (var b in _buttons)
{
if (useText && b.Text.Trim().Equals(text, StringComparison.InvariantCultureIgnoreCase))
{
return true;
}
if (!useText && b.Value.Equals(text, StringComparison.InvariantCultureIgnoreCase))
{
return true;
}
}
return false;
}
/// <summary>
/// Returns the button inside of the row which matches.
/// </summary>
/// <param name="text"></param>
/// <param name="useText"></param>
/// <returns></returns>
public ButtonBase GetButtonMatch(string text, bool useText = true)
{
foreach (var b in _buttons)
{
if (useText && b.Text.Trim().Equals(text, StringComparison.InvariantCultureIgnoreCase))
{
return b;
}
if (!useText && b.Value.Equals(text, StringComparison.InvariantCultureIgnoreCase))
{
return b;
}
}
return null;
}
public static implicit operator ButtonRow(List<ButtonBase> list)
{
return new ButtonRow { _buttons = list };
}
}
File diff suppressed because it is too large Load Diff
+116 -115
View File
@@ -3,124 +3,125 @@ using System.Threading.Tasks;
using TelegramBotBase.Args;
using TelegramBotBase.Base;
namespace TelegramBotBase.Controls.Hybrid
namespace TelegramBotBase.Controls.Hybrid;
/// <summary>
/// This Control is for having a basic form content switching control.
/// </summary>
public abstract class MultiView : ControlBase
{
private int _mISelectedViewIndex;
/// <summary>
/// This Control is for having a basic form content switching control.
/// Hold if the View has been rendered already.
/// </summary>
public abstract class MultiView : ControlBase
private bool _rendered;
public MultiView()
{
/// <summary>
/// Index of the current View.
/// </summary>
public int SelectedViewIndex
{
get => _mISelectedViewIndex;
set
{
_mISelectedViewIndex = value;
//Already rendered? Re-Render
if (_rendered)
ForceRender().Wait();
}
}
private int _mISelectedViewIndex;
/// <summary>
/// Hold if the View has been rendered already.
/// </summary>
private bool _rendered;
private List<int> Messages { get; set; }
public MultiView()
{
Messages = new List<int>();
}
private Task Device_MessageSent(object sender, MessageSentEventArgs e)
{
if (e.Origin == null || !e.Origin.IsSubclassOf(typeof(MultiView)))
return Task.CompletedTask;
Messages.Add(e.MessageId);
return Task.CompletedTask;
}
public override void Init()
{
Device.MessageSent += Device_MessageSent;
}
public override Task Load(MessageResult result)
{
_rendered = false;
return Task.CompletedTask;
}
public override async Task Render(MessageResult result)
{
//When already rendered, skip rendering
if (_rendered)
return;
await CleanUpView();
await RenderView(new RenderViewEventArgs(SelectedViewIndex));
_rendered = true;
}
/// <summary>
/// Will get invoked on rendering the current controls view.
/// </summary>
/// <param name="e"></param>
public virtual Task RenderView(RenderViewEventArgs e)
{
return Task.CompletedTask;
}
private async Task CleanUpView()
{
var tasks = new List<Task>();
foreach (var msg in Messages)
{
tasks.Add(Device.DeleteMessage(msg));
}
await Task.WhenAll(tasks);
Messages.Clear();
}
/// <summary>
/// Forces render of control contents.
/// </summary>
public async Task ForceRender()
{
await CleanUpView();
await RenderView(new RenderViewEventArgs(SelectedViewIndex));
_rendered = true;
}
public override async Task Cleanup()
{
Device.MessageSent -= Device_MessageSent;
await CleanUpView();
}
Messages = new List<int>();
}
}
/// <summary>
/// Index of the current View.
/// </summary>
public int SelectedViewIndex
{
get => _mISelectedViewIndex;
set
{
_mISelectedViewIndex = value;
//Already rendered? Re-Render
if (_rendered)
{
ForceRender().Wait();
}
}
}
private List<int> Messages { get; }
private Task Device_MessageSent(object sender, MessageSentEventArgs e)
{
if (e.Origin == null || !e.Origin.IsSubclassOf(typeof(MultiView)))
{
return Task.CompletedTask;
}
Messages.Add(e.MessageId);
return Task.CompletedTask;
}
public override void Init()
{
Device.MessageSent += Device_MessageSent;
}
public override Task Load(MessageResult result)
{
_rendered = false;
return Task.CompletedTask;
}
public override async Task Render(MessageResult result)
{
//When already rendered, skip rendering
if (_rendered)
{
return;
}
await CleanUpView();
await RenderView(new RenderViewEventArgs(SelectedViewIndex));
_rendered = true;
}
/// <summary>
/// Will get invoked on rendering the current controls view.
/// </summary>
/// <param name="e"></param>
public virtual Task RenderView(RenderViewEventArgs e)
{
return Task.CompletedTask;
}
private async Task CleanUpView()
{
var tasks = new List<Task>();
foreach (var msg in Messages)
{
tasks.Add(Device.DeleteMessage(msg));
}
await Task.WhenAll(tasks);
Messages.Clear();
}
/// <summary>
/// Forces render of control contents.
/// </summary>
public async Task ForceRender()
{
await CleanUpView();
await RenderView(new RenderViewEventArgs(SelectedViewIndex));
_rendered = true;
}
public override async Task Cleanup()
{
Device.MessageSent -= Device_MessageSent;
await CleanUpView();
}
}
File diff suppressed because it is too large Load Diff
+213 -210
View File
@@ -10,254 +10,257 @@ using TelegramBotBase.Localizations;
using static TelegramBotBase.Tools.Arrays;
using static TelegramBotBase.Tools.Time;
namespace TelegramBotBase.Controls.Inline
namespace TelegramBotBase.Controls.Inline;
public class CalendarPicker : ControlBase
{
public class CalendarPicker : ControlBase
public CalendarPicker(CultureInfo culture)
{
SelectedDate = DateTime.Today;
VisibleMonth = DateTime.Today;
FirstDayOfWeek = DayOfWeek.Monday;
Culture = culture;
PickerMode = EMonthPickerMode.day;
}
public DateTime SelectedDate { get; set; }
public CalendarPicker() : this(new CultureInfo("en-en"))
{
}
public DateTime VisibleMonth { get; set; }
public DateTime SelectedDate { get; set; }
public DayOfWeek FirstDayOfWeek { get; set; }
public DateTime VisibleMonth { get; set; }
public CultureInfo Culture { get; set; }
public DayOfWeek FirstDayOfWeek { get; set; }
public CultureInfo Culture { get; set; }
private int? MessageId { get; set; }
private int? MessageId { get; set; }
public string Title { get; set; } = Default.Language["CalendarPicker_Title"];
public string Title { get; set; } = Default.Language["CalendarPicker_Title"];
public EMonthPickerMode PickerMode { get; set; }
public EMonthPickerMode PickerMode { get; set; }
public bool EnableDayView { get; set; } = true;
public bool EnableDayView { get; set; } = true;
public bool EnableMonthView { get; set; } = true;
public bool EnableMonthView { get; set; } = true;
public bool EnableYearView { get; set; } = true;
public bool EnableYearView { get; set; } = true;
public CalendarPicker(CultureInfo culture)
public override async Task Action(MessageResult result, string value = null)
{
await result.ConfirmAction();
switch (result.RawData)
{
SelectedDate = DateTime.Today;
VisibleMonth = DateTime.Today;
FirstDayOfWeek = DayOfWeek.Monday;
Culture = culture;
PickerMode = EMonthPickerMode.day;
}
case "$next$":
public CalendarPicker() : this(new CultureInfo("en-en")) { }
VisibleMonth = PickerMode switch
{
EMonthPickerMode.day => VisibleMonth.AddMonths(1),
EMonthPickerMode.month => VisibleMonth.AddYears(1),
EMonthPickerMode.year => VisibleMonth.AddYears(10),
_ => VisibleMonth
};
break;
case "$prev$":
VisibleMonth = PickerMode switch
{
EMonthPickerMode.day => VisibleMonth.AddMonths(-1),
EMonthPickerMode.month => VisibleMonth.AddYears(-1),
EMonthPickerMode.year => VisibleMonth.AddYears(-10),
_ => VisibleMonth
};
break;
public override async Task Action(MessageResult result, string value = null)
{
await result.ConfirmAction();
case "$monthtitle$":
switch (result.RawData)
{
case "$next$":
if (EnableMonthView)
{
PickerMode = EMonthPickerMode.month;
}
VisibleMonth = PickerMode switch
{
EMonthPickerMode.day => VisibleMonth.AddMonths(1),
EMonthPickerMode.month => VisibleMonth.AddYears(1),
EMonthPickerMode.year => VisibleMonth.AddYears(10),
_ => VisibleMonth
};
break;
break;
case "$prev$":
case "$yeartitle$":
VisibleMonth = PickerMode switch
{
EMonthPickerMode.day => VisibleMonth.AddMonths(-1),
EMonthPickerMode.month => VisibleMonth.AddYears(-1),
EMonthPickerMode.year => VisibleMonth.AddYears(-10),
_ => VisibleMonth
};
if (EnableYearView)
{
PickerMode = EMonthPickerMode.year;
}
break;
break;
case "$yearstitle$":
case "$monthtitle$":
if (EnableMonthView)
{
PickerMode = EMonthPickerMode.month;
}
if (EnableMonthView)
{
PickerMode = EMonthPickerMode.month;
}
VisibleMonth = SelectedDate;
break;
break;
case "$yeartitle$":
default:
if (EnableYearView)
{
PickerMode = EMonthPickerMode.year;
}
break;
case "$yearstitle$":
if (EnableMonthView)
{
PickerMode = EMonthPickerMode.month;
}
var day = 0;
if (result.RawData.StartsWith("d-") &&
TryParseDay(result.RawData.Split('-')[1], SelectedDate, out day))
{
SelectedDate = new DateTime(VisibleMonth.Year, VisibleMonth.Month, day);
}
var month = 0;
if (result.RawData.StartsWith("m-") && TryParseMonth(result.RawData.Split('-')[1], out month))
{
SelectedDate = new DateTime(VisibleMonth.Year, month, 1);
VisibleMonth = SelectedDate;
break;
default:
var day = 0;
if (result.RawData.StartsWith("d-") && TryParseDay(result.RawData.Split('-')[1], SelectedDate, out day))
if (EnableDayView)
{
SelectedDate = new DateTime(VisibleMonth.Year, VisibleMonth.Month, day);
PickerMode = EMonthPickerMode.day;
}
}
var month = 0;
if (result.RawData.StartsWith("m-") && TryParseMonth(result.RawData.Split('-')[1], out month))
var year = 0;
if (result.RawData.StartsWith("y-") && TryParseYear(result.RawData.Split('-')[1], out year))
{
SelectedDate = new DateTime(year, SelectedDate.Month, SelectedDate.Day);
VisibleMonth = SelectedDate;
if (EnableMonthView)
{
SelectedDate = new DateTime(VisibleMonth.Year, month, 1);
VisibleMonth = SelectedDate;
if (EnableDayView)
{
PickerMode = EMonthPickerMode.day;
}
PickerMode = EMonthPickerMode.month;
}
}
var year = 0;
if (result.RawData.StartsWith("y-") && TryParseYear(result.RawData.Split('-')[1], out year))
{
SelectedDate = new DateTime(year, SelectedDate.Month, SelectedDate.Day);
VisibleMonth = SelectedDate;
if (EnableMonthView)
{
PickerMode = EMonthPickerMode.month;
}
}
break;
}
break;
}
public override async Task Render(MessageResult result)
{
var bf = new ButtonForm();
switch (PickerMode)
{
case EMonthPickerMode.day:
var month = VisibleMonth;
var dayNamesNormal = Culture.DateTimeFormat.ShortestDayNames;
var dayNamesShifted = Shift(dayNamesNormal, (int)FirstDayOfWeek);
bf.AddButtonRow(new ButtonBase(Default.Language["CalendarPicker_PreviousPage"], "$prev$"), new ButtonBase(Culture.DateTimeFormat.MonthNames[month.Month - 1] + " " + month.Year, "$monthtitle$"), new ButtonBase(Default.Language["CalendarPicker_NextPage"], "$next$"));
bf.AddButtonRow(dayNamesShifted.Select(a => new ButtonBase(a, a)).ToList());
//First Day of month
var firstDay = new DateTime(month.Year, month.Month, 1);
//Last Day of month
var lastDay = firstDay.LastDayOfMonth();
//Start of Week where first day of month is (left border)
var start = firstDay.StartOfWeek(FirstDayOfWeek);
//End of week where last day of month is (right border)
var end = lastDay.EndOfWeek(FirstDayOfWeek);
for (var i = 0; i <= ((end - start).Days / 7); i++)
{
var lst = new List<ButtonBase>();
for (var id = 0; id < 7; id++)
{
var d = start.AddDays((i * 7) + id);
if (d < firstDay | d > lastDay)
{
lst.Add(new ButtonBase("-", "m-" + d.Day));
continue;
}
var day = d.Day.ToString();
if (d == DateTime.Today)
{
day = "(" + day + ")";
}
lst.Add(new ButtonBase((SelectedDate == d ? "[" + day + "]" : day), "d-" + d.Day));
}
bf.AddButtonRow(lst);
}
break;
case EMonthPickerMode.month:
bf.AddButtonRow(new ButtonBase(Default.Language["CalendarPicker_PreviousPage"], "$prev$"), new ButtonBase(VisibleMonth.Year.ToString("0000"), "$yeartitle$"), new ButtonBase(Default.Language["CalendarPicker_NextPage"], "$next$"));
var months = Culture.DateTimeFormat.MonthNames;
var buttons = months.Select((a, b) => new ButtonBase((b == SelectedDate.Month - 1 && SelectedDate.Year == VisibleMonth.Year ? "[ " + a + " ]" : a), "m-" + (b + 1)));
bf.AddSplitted(buttons);
break;
case EMonthPickerMode.year:
bf.AddButtonRow(new ButtonBase(Default.Language["CalendarPicker_PreviousPage"], "$prev$"), new ButtonBase("Year", "$yearstitle$"), new ButtonBase(Default.Language["CalendarPicker_NextPage"], "$next$"));
var starti = Math.Floor(VisibleMonth.Year / 10f) * 10;
for (var i = 0; i < 10; i++)
{
var m = starti + (i * 2);
bf.AddButtonRow(new ButtonBase((SelectedDate.Year == m ? "[ " + m + " ]" : m.ToString()), "y-" + m), new ButtonBase((SelectedDate.Year == (m + 1) ? "[ " + (m + 1) + " ]" : (m + 1).ToString()), "y-" + (m + 1)));
}
break;
}
if (MessageId != null)
{
var m = await Device.Edit(MessageId.Value, Title, bf);
}
else
{
var m = await Device.Send(Title, bf);
MessageId = m.MessageId;
}
}
public override async Task Cleanup()
{
if (MessageId != null)
{
await Device.DeleteMessage(MessageId.Value);
}
}
}
}
public override async Task Render(MessageResult result)
{
var bf = new ButtonForm();
switch (PickerMode)
{
case EMonthPickerMode.day:
var month = VisibleMonth;
var dayNamesNormal = Culture.DateTimeFormat.ShortestDayNames;
var dayNamesShifted = Shift(dayNamesNormal, (int)FirstDayOfWeek);
bf.AddButtonRow(new ButtonBase(Default.Language["CalendarPicker_PreviousPage"], "$prev$"),
new ButtonBase(Culture.DateTimeFormat.MonthNames[month.Month - 1] + " " + month.Year,
"$monthtitle$"),
new ButtonBase(Default.Language["CalendarPicker_NextPage"], "$next$"));
bf.AddButtonRow(dayNamesShifted.Select(a => new ButtonBase(a, a)).ToList());
//First Day of month
var firstDay = new DateTime(month.Year, month.Month, 1);
//Last Day of month
var lastDay = firstDay.LastDayOfMonth();
//Start of Week where first day of month is (left border)
var start = firstDay.StartOfWeek(FirstDayOfWeek);
//End of week where last day of month is (right border)
var end = lastDay.EndOfWeek(FirstDayOfWeek);
for (var i = 0; i <= (end - start).Days / 7; i++)
{
var lst = new List<ButtonBase>();
for (var id = 0; id < 7; id++)
{
var d = start.AddDays(i * 7 + id);
if ((d < firstDay) | (d > lastDay))
{
lst.Add(new ButtonBase("-", "m-" + d.Day));
continue;
}
var day = d.Day.ToString();
if (d == DateTime.Today)
{
day = "(" + day + ")";
}
lst.Add(new ButtonBase(SelectedDate == d ? "[" + day + "]" : day, "d-" + d.Day));
}
bf.AddButtonRow(lst);
}
break;
case EMonthPickerMode.month:
bf.AddButtonRow(new ButtonBase(Default.Language["CalendarPicker_PreviousPage"], "$prev$"),
new ButtonBase(VisibleMonth.Year.ToString("0000"), "$yeartitle$"),
new ButtonBase(Default.Language["CalendarPicker_NextPage"], "$next$"));
var months = Culture.DateTimeFormat.MonthNames;
var buttons = months.Select((a, b) =>
new ButtonBase(
b == SelectedDate.Month - 1 &&
SelectedDate.Year == VisibleMonth.Year
? "[ " + a + " ]"
: a,
"m-" + (b + 1)));
bf.AddSplitted(buttons);
break;
case EMonthPickerMode.year:
bf.AddButtonRow(new ButtonBase(Default.Language["CalendarPicker_PreviousPage"], "$prev$"),
new ButtonBase("Year", "$yearstitle$"),
new ButtonBase(Default.Language["CalendarPicker_NextPage"], "$next$"));
var starti = Math.Floor(VisibleMonth.Year / 10f) * 10;
for (var i = 0; i < 10; i++)
{
var m = starti + i * 2;
bf.AddButtonRow(
new ButtonBase(SelectedDate.Year == m ? "[ " + m + " ]" : m.ToString(), "y-" + m),
new ButtonBase(SelectedDate.Year == m + 1 ? "[ " + (m + 1) + " ]" : (m + 1).ToString(),
"y-" + (m + 1)));
}
break;
}
if (MessageId != null)
{
var m = await Device.Edit(MessageId.Value, Title, bf);
}
else
{
var m = await Device.Send(Title, bf);
MessageId = m.MessageId;
}
}
public override async Task Cleanup()
{
if (MessageId != null)
{
await Device.DeleteMessage(MessageId.Value);
}
}
}
+7 -13
View File
@@ -1,18 +1,12 @@
using TelegramBotBase.Enums;
namespace TelegramBotBase.Controls.Inline
namespace TelegramBotBase.Controls.Inline;
public class MonthPicker : CalendarPicker
{
public class MonthPicker : CalendarPicker
public MonthPicker()
{
public MonthPicker()
{
PickerMode = EMonthPickerMode.month;
EnableDayView = false;
}
PickerMode = EMonthPickerMode.month;
EnableDayView = false;
}
}
}
@@ -6,152 +6,147 @@ using TelegramBotBase.Base;
using TelegramBotBase.Form;
using TelegramBotBase.Localizations;
namespace TelegramBotBase.Controls.Inline
namespace TelegramBotBase.Controls.Inline;
public class MultiToggleButton : ControlBase
{
public class MultiToggleButton : ControlBase
private static readonly object EvToggled = new();
private readonly EventHandlerList _events = new();
private bool _renderNecessary = true;
public MultiToggleButton()
{
/// <summary>
/// This contains the selected icon.
/// </summary>
public string SelectedIcon { get; set; } = Default.Language["MultiToggleButton_SelectedIcon"];
Options = new List<ButtonBase>();
}
/// <summary>
/// This will appear on the ConfirmAction message (if not empty)
/// </summary>
public string ChangedString { get; set; } = Default.Language["MultiToggleButton_Changed"];
/// <summary>
/// This contains the selected icon.
/// </summary>
public string SelectedIcon { get; set; } = Default.Language["MultiToggleButton_SelectedIcon"];
/// <summary>
/// This holds the title of the control.
/// </summary>
public string Title { get; set; } = Default.Language["MultiToggleButton_Title"];
/// <summary>
/// This will appear on the ConfirmAction message (if not empty)
/// </summary>
public string ChangedString { get; set; } = Default.Language["MultiToggleButton_Changed"];
public int? MessageId { get; set; }
/// <summary>
/// This holds the title of the control.
/// </summary>
public string Title { get; set; } = Default.Language["MultiToggleButton_Title"];
private bool _renderNecessary = true;
public int? MessageId { get; set; }
private static readonly object EvToggled = new object();
/// <summary>
/// This will hold all options available.
/// </summary>
public List<ButtonBase> Options { get; set; }
private readonly EventHandlerList _events = new EventHandlerList();
/// <summary>
/// This will set if an empty selection (null) is allowed.
/// </summary>
public bool AllowEmptySelection { get; set; } = true;
/// <summary>
/// This will hold all options available.
/// </summary>
public List<ButtonBase> Options { get; set; }
public ButtonBase SelectedOption { get; set; }
/// <summary>
/// This will set if an empty selection (null) is allowed.
/// </summary>
public bool AllowEmptySelection { get; set; } = true;
public event EventHandler Toggled
{
add => _events.AddHandler(EvToggled, value);
remove => _events.RemoveHandler(EvToggled, value);
}
public void OnToggled(EventArgs e)
{
(_events[EvToggled] as EventHandler)?.Invoke(this, e);
}
public MultiToggleButton()
public override async Task Action(MessageResult result, string value = null)
{
if (result.Handled)
{
Options = new List<ButtonBase>();
return;
}
public event EventHandler Toggled
await result.ConfirmAction(ChangedString);
switch (value ?? "unknown")
{
add => _events.AddHandler(EvToggled, value);
remove => _events.RemoveHandler(EvToggled, value);
}
default:
public void OnToggled(EventArgs e)
{
(_events[EvToggled] as EventHandler)?.Invoke(this, e);
}
var s = value.Split('$');
public override async Task Action(MessageResult result, string value = null)
{
if (result.Handled)
return;
await result.ConfirmAction(ChangedString);
switch (value ?? "unknown")
{
default:
var s = value.Split('$');
if (s[0] == "check" && s.Length > 1)
if (s[0] == "check" && s.Length > 1)
{
var index = 0;
if (!int.TryParse(s[1], out index))
{
var index = 0;
if (!int.TryParse(s[1], out index))
{
return;
}
if(SelectedOption== null || SelectedOption != Options[index])
{
SelectedOption = Options[index];
OnToggled(EventArgs.Empty);
}
else if(AllowEmptySelection)
{
SelectedOption = null;
OnToggled(EventArgs.Empty);
}
_renderNecessary = true;
return;
}
if (SelectedOption == null || SelectedOption != Options[index])
{
SelectedOption = Options[index];
OnToggled(EventArgs.Empty);
}
else if (AllowEmptySelection)
{
SelectedOption = null;
OnToggled(EventArgs.Empty);
}
_renderNecessary = false;
_renderNecessary = true;
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 Options)
{
var index = Options.IndexOf(o);
if (o == SelectedOption)
{
lst.Add(new ButtonBase(SelectedIcon + " " + o.Text, "check$" + index));
continue;
return;
}
lst.Add(new ButtonBase(o.Text, "check$" + index));
}
bf.AddButtonRow(lst);
if (MessageId != null)
{
var m = await Device.Edit(MessageId.Value, Title, bf);
}
else
{
var m = await Device.Send(Title, bf, disableNotification: true);
if (m != null)
{
MessageId = m.MessageId;
}
}
_renderNecessary = false;
_renderNecessary = false;
break;
}
public ButtonBase SelectedOption
{
get; set;
}
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 Options)
{
var index = Options.IndexOf(o);
if (o == SelectedOption)
{
lst.Add(new ButtonBase(SelectedIcon + " " + o.Text, "check$" + index));
continue;
}
lst.Add(new ButtonBase(o.Text, "check$" + index));
}
bf.AddButtonRow(lst);
if (MessageId != null)
{
var m = await Device.Edit(MessageId.Value, Title, bf);
}
else
{
var m = await Device.Send(Title, bf, disableNotification: true);
if (m != null)
{
MessageId = m.MessageId;
}
}
_renderNecessary = false;
}
}
+249 -260
View File
@@ -2,266 +2,255 @@
using System.Threading.Tasks;
using TelegramBotBase.Base;
namespace TelegramBotBase.Controls.Inline
namespace TelegramBotBase.Controls.Inline;
/// <summary>
/// A simple control for show and managing progress.
/// </summary>
public class ProgressBar : ControlBase
{
/// <summary>
/// A simple control for show and managing progress.
/// </summary>
public class ProgressBar : ControlBase
public enum EProgressStyle
{
public enum EProgressStyle
{
standard = 0,
squares = 1,
circles = 2,
lines = 3,
squaredLines = 4,
custom = 10
}
public EProgressStyle ProgressStyle
{
get => _mEStyle;
set
{
_mEStyle = value;
LoadStyle();
}
}
private EProgressStyle _mEStyle = EProgressStyle.standard;
public int Value
{
get => _mIValue;
set
{
if (value > Max)
{
return;
}
if (_mIValue != value)
{
RenderNecessary = true;
}
_mIValue = value;
}
}
private int _mIValue;
public int Max
{
get => _mIMax;
set
{
if (_mIMax != value)
{
RenderNecessary = true;
}
_mIMax = value;
}
}
private int _mIMax = 100;
public int? MessageId { get; set; }
private bool RenderNecessary { get; set; }
public int Steps
{
get
{
return ProgressStyle switch
{
EProgressStyle.standard => 1,
EProgressStyle.squares => 10,
EProgressStyle.circles => 10,
EProgressStyle.lines => 5,
EProgressStyle.squaredLines => 5,
_ => 1
};
}
}
/// <summary>
/// Filled block (reached percentage)
/// </summary>
public string BlockChar
{
get; set;
}
/// <summary>
/// Unfilled block (not reached yet)
/// </summary>
public string EmptyBlockChar
{
get; set;
}
/// <summary>
/// String at the beginning of the progress bar
/// </summary>
public string StartChar
{
get; set;
}
/// <summary>
/// String at the end of the progress bar
/// </summary>
public string EndChar
{
get; set;
}
public ProgressBar()
{
ProgressStyle = EProgressStyle.standard;
Value = 0;
Max = 100;
RenderNecessary = true;
}
public ProgressBar(int value, int max, EProgressStyle style)
{
this.Value = value;
this.Max = max;
ProgressStyle = style;
RenderNecessary = true;
}
public override async Task Cleanup()
{
if (MessageId == null || MessageId == -1)
return;
await Device.DeleteMessage(MessageId.Value);
}
public void LoadStyle()
{
StartChar = "";
EndChar = "";
switch (ProgressStyle)
{
case EProgressStyle.circles:
BlockChar = "⚫️ ";
EmptyBlockChar = "⚪️ ";
break;
case EProgressStyle.squares:
BlockChar = "⬛️ ";
EmptyBlockChar = "⬜️ ";
break;
case EProgressStyle.lines:
BlockChar = "█";
EmptyBlockChar = "▁";
break;
case EProgressStyle.squaredLines:
BlockChar = "▇";
EmptyBlockChar = "—";
StartChar = "[";
EndChar = "]";
break;
case EProgressStyle.standard:
case EProgressStyle.custom:
BlockChar = "";
EmptyBlockChar = "";
break;
}
}
public override async Task Render(MessageResult result)
{
if (!RenderNecessary)
{
return;
}
if (Device == null)
{
return;
}
var message = "";
var blocks = 0;
var maxBlocks = 0;
switch (ProgressStyle)
{
case EProgressStyle.standard:
message = Value.ToString("0") + "%";
break;
case EProgressStyle.squares:
case EProgressStyle.circles:
case EProgressStyle.lines:
case EProgressStyle.squaredLines:
case EProgressStyle.custom:
blocks = (int)Math.Floor((decimal)Value / Steps);
maxBlocks = (Max / Steps);
message += StartChar;
for (var i = 0; i < blocks; i++)
{
message += BlockChar;
}
for (var i = 0; i < (maxBlocks - blocks); i++)
{
message += EmptyBlockChar;
}
message += EndChar;
message += " " + Value.ToString("0") + "%";
break;
default:
return;
}
if (MessageId == null)
{
var m = await Device.Send(message);
MessageId = m.MessageId;
}
else
{
await Device.Edit(MessageId.Value, message);
}
RenderNecessary = false;
}
standard = 0,
squares = 1,
circles = 2,
lines = 3,
squaredLines = 4,
custom = 10
}
}
private EProgressStyle _mEStyle = EProgressStyle.standard;
private int _mIMax = 100;
private int _mIValue;
public ProgressBar()
{
ProgressStyle = EProgressStyle.standard;
Value = 0;
Max = 100;
RenderNecessary = true;
}
public ProgressBar(int value, int max, EProgressStyle style)
{
Value = value;
Max = max;
ProgressStyle = style;
RenderNecessary = true;
}
public EProgressStyle ProgressStyle
{
get => _mEStyle;
set
{
_mEStyle = value;
LoadStyle();
}
}
public int Value
{
get => _mIValue;
set
{
if (value > Max)
{
return;
}
if (_mIValue != value)
{
RenderNecessary = true;
}
_mIValue = value;
}
}
public int Max
{
get => _mIMax;
set
{
if (_mIMax != value)
{
RenderNecessary = true;
}
_mIMax = value;
}
}
public int? MessageId { get; set; }
private bool RenderNecessary { get; set; }
public int Steps
{
get
{
return ProgressStyle switch
{
EProgressStyle.standard => 1,
EProgressStyle.squares => 10,
EProgressStyle.circles => 10,
EProgressStyle.lines => 5,
EProgressStyle.squaredLines => 5,
_ => 1
};
}
}
/// <summary>
/// Filled block (reached percentage)
/// </summary>
public string BlockChar { get; set; }
/// <summary>
/// Unfilled block (not reached yet)
/// </summary>
public string EmptyBlockChar { get; set; }
/// <summary>
/// String at the beginning of the progress bar
/// </summary>
public string StartChar { get; set; }
/// <summary>
/// String at the end of the progress bar
/// </summary>
public string EndChar { get; set; }
public override async Task Cleanup()
{
if (MessageId == null || MessageId == -1)
{
return;
}
await Device.DeleteMessage(MessageId.Value);
}
public void LoadStyle()
{
StartChar = "";
EndChar = "";
switch (ProgressStyle)
{
case EProgressStyle.circles:
BlockChar = "⚫️ ";
EmptyBlockChar = "⚪️ ";
break;
case EProgressStyle.squares:
BlockChar = "⬛️ ";
EmptyBlockChar = "⬜️ ";
break;
case EProgressStyle.lines:
BlockChar = "█";
EmptyBlockChar = "▁";
break;
case EProgressStyle.squaredLines:
BlockChar = "▇";
EmptyBlockChar = "—";
StartChar = "[";
EndChar = "]";
break;
case EProgressStyle.standard:
case EProgressStyle.custom:
BlockChar = "";
EmptyBlockChar = "";
break;
}
}
public override async Task Render(MessageResult result)
{
if (!RenderNecessary)
{
return;
}
if (Device == null)
{
return;
}
var message = "";
var blocks = 0;
var maxBlocks = 0;
switch (ProgressStyle)
{
case EProgressStyle.standard:
message = Value.ToString("0") + "%";
break;
case EProgressStyle.squares:
case EProgressStyle.circles:
case EProgressStyle.lines:
case EProgressStyle.squaredLines:
case EProgressStyle.custom:
blocks = (int)Math.Floor((decimal)Value / Steps);
maxBlocks = Max / Steps;
message += StartChar;
for (var i = 0; i < blocks; i++)
{
message += BlockChar;
}
for (var i = 0; i < maxBlocks - blocks; i++)
{
message += EmptyBlockChar;
}
message += EndChar;
message += " " + Value.ToString("0") + "%";
break;
default:
return;
}
if (MessageId == null)
{
var m = await Device.Send(message);
MessageId = m.MessageId;
}
else
{
await Device.Edit(MessageId.Value, message);
}
RenderNecessary = false;
}
}
+127 -129
View File
@@ -5,135 +5,133 @@ using TelegramBotBase.Base;
using TelegramBotBase.Form;
using TelegramBotBase.Localizations;
namespace TelegramBotBase.Controls.Inline
namespace TelegramBotBase.Controls.Inline;
public class ToggleButton : ControlBase
{
public class ToggleButton : ControlBase
private static readonly object EvToggled = new();
private readonly EventHandlerList _events = new();
private bool _renderNecessary = true;
public ToggleButton()
{
public string UncheckedIcon { get; set; } = Default.Language["ToggleButton_OffIcon"];
public string CheckedIcon { get; set; } = Default.Language["ToggleButton_OnIcon"];
public string CheckedString { get; set; } = Default.Language["ToggleButton_On"];
public string UncheckedString { get; set; } = Default.Language["ToggleButton_Off"];
public string ChangedString { get; set; } = Default.Language["ToggleButton_Changed"];
public string Title { get; set; } = Default.Language["ToggleButton_Title"];
public int? MessageId { get; set; }
public bool Checked { get; set; }
private bool _renderNecessary = true;
private static readonly object EvToggled = new object();
private readonly EventHandlerList _events = new EventHandlerList();
public ToggleButton()
{
}
public ToggleButton(string checkedString, string uncheckedString)
{
this.CheckedString = checkedString;
this.UncheckedString = uncheckedString;
}
public event EventHandler Toggled
{
add => _events.AddHandler(EvToggled, value);
remove => _events.RemoveHandler(EvToggled, value);
}
public void OnToggled(EventArgs e)
{
(_events[EvToggled] as EventHandler)?.Invoke(this, e);
}
public override async Task Action(MessageResult result, string value = null)
{
if (result.Handled)
return;
await result.ConfirmAction(ChangedString);
switch (value ?? "unknown")
{
case "on":
if (Checked)
return;
_renderNecessary = true;
Checked = true;
OnToggled(EventArgs.Empty);
break;
case "off":
if (!Checked)
return;
_renderNecessary = true;
Checked = false;
OnToggled(EventArgs.Empty);
break;
default:
_renderNecessary = false;
break;
}
result.Handled = true;
}
public override async Task Render(MessageResult result)
{
if (!_renderNecessary)
return;
var bf = new ButtonForm(this);
var bOn = new ButtonBase((Checked ? CheckedIcon : UncheckedIcon) + " " + CheckedString, "on");
var bOff = new ButtonBase((!Checked ? CheckedIcon : UncheckedIcon) + " " + UncheckedString, "off");
bf.AddButtonRow(bOn, bOff);
if (MessageId != null)
{
var m = await Device.Edit(MessageId.Value, Title, bf);
}
else
{
var m = await Device.Send(Title, bf, disableNotification: true);
if (m != null)
{
MessageId = m.MessageId;
}
}
_renderNecessary = false;
}
}
}
public ToggleButton(string checkedString, string uncheckedString)
{
CheckedString = checkedString;
UncheckedString = uncheckedString;
}
public string UncheckedIcon { get; set; } = Default.Language["ToggleButton_OffIcon"];
public string CheckedIcon { get; set; } = Default.Language["ToggleButton_OnIcon"];
public string CheckedString { get; set; } = Default.Language["ToggleButton_On"];
public string UncheckedString { get; set; } = Default.Language["ToggleButton_Off"];
public string ChangedString { get; set; } = Default.Language["ToggleButton_Changed"];
public string Title { get; set; } = Default.Language["ToggleButton_Title"];
public int? MessageId { get; set; }
public bool Checked { get; set; }
public event EventHandler Toggled
{
add => _events.AddHandler(EvToggled, value);
remove => _events.RemoveHandler(EvToggled, value);
}
public void OnToggled(EventArgs e)
{
(_events[EvToggled] as EventHandler)?.Invoke(this, e);
}
public override async Task Action(MessageResult result, string value = null)
{
if (result.Handled)
{
return;
}
await result.ConfirmAction(ChangedString);
switch (value ?? "unknown")
{
case "on":
if (Checked)
{
return;
}
_renderNecessary = true;
Checked = true;
OnToggled(EventArgs.Empty);
break;
case "off":
if (!Checked)
{
return;
}
_renderNecessary = true;
Checked = false;
OnToggled(EventArgs.Empty);
break;
default:
_renderNecessary = false;
break;
}
result.Handled = true;
}
public override async Task Render(MessageResult result)
{
if (!_renderNecessary)
{
return;
}
var bf = new ButtonForm(this);
var bOn = new ButtonBase((Checked ? CheckedIcon : UncheckedIcon) + " " + CheckedString, "on");
var bOff = new ButtonBase((!Checked ? CheckedIcon : UncheckedIcon) + " " + UncheckedString, "off");
bf.AddButtonRow(bOn, bOff);
if (MessageId != null)
{
var m = await Device.Edit(MessageId.Value, Title, bf);
}
else
{
var m = await Device.Send(Title, bf, disableNotification: true);
if (m != null)
{
MessageId = m.MessageId;
}
}
_renderNecessary = false;
}
}
+89 -89
View File
@@ -5,117 +5,117 @@ using TelegramBotBase.Base;
using TelegramBotBase.Form;
using TelegramBotBase.Localizations;
namespace TelegramBotBase.Controls.Inline
namespace TelegramBotBase.Controls.Inline;
public class TreeView : ControlBase
{
public class TreeView : ControlBase
public TreeView()
{
public List<TreeViewNode> Nodes { get; set; }
Nodes = new List<TreeViewNode>();
Title = Default.Language["TreeView_Title"];
}
public TreeViewNode SelectedNode { get; set; }
public List<TreeViewNode> Nodes { get; set; }
public TreeViewNode VisibleNode { get; set; }
public TreeViewNode SelectedNode { get; set; }
public string Title { get; set; }
public TreeViewNode VisibleNode { get; set; }
private int? MessageId { get; set; }
public string Title { get; set; }
public string MoveUpIcon { get; set; } = Default.Language["TreeView_LevelUp"];
private int? MessageId { get; set; }
public TreeView()
public string MoveUpIcon { get; set; } = Default.Language["TreeView_LevelUp"];
public override async Task Action(MessageResult result, string value = null)
{
await result.ConfirmAction();
if (result.Handled)
{
Nodes = new List<TreeViewNode>();
Title = Default.Language["TreeView_Title"];
return;
}
var val = result.RawData;
public override async Task Action(MessageResult result, string value = null)
switch (val)
{
await result.ConfirmAction();
case "up":
case "parent":
if (result.Handled)
return;
VisibleNode = VisibleNode?.ParentNode;
var val = result.RawData;
result.Handled = true;
switch (val)
{
case "up":
case "parent":
break;
default:
VisibleNode = (VisibleNode?.ParentNode);
var n = VisibleNode != null
? VisibleNode.FindNodeByValue(val)
: Nodes.FirstOrDefault(a => a.Value == val);
result.Handled = true;
break;
default:
var n = (VisibleNode != null ? VisibleNode.FindNodeByValue(val) : Nodes.FirstOrDefault(a => a.Value == val));
if (n == null)
return;
if (n.ChildNodes.Count > 0)
{
VisibleNode = n;
}
else
{
SelectedNode = (SelectedNode != n ? n : null);
}
result.Handled = true;
break;
}
}
public override async Task Render(MessageResult result)
{
var startnode = VisibleNode;
var nodes = (startnode?.ChildNodes ?? Nodes);
var bf = new ButtonForm();
if (startnode != null)
{
bf.AddButtonRow(new ButtonBase(MoveUpIcon, "up"), new ButtonBase(startnode.Text, "parent"));
}
foreach (var n in nodes)
{
var s = n.Text;
if (SelectedNode == n)
if (n == null)
{
s = "[ " + s + " ]";
return;
}
bf.AddButtonRow(new ButtonBase(s, n.Value, n.Url));
}
if (n.ChildNodes.Count > 0)
{
VisibleNode = n;
}
else
{
SelectedNode = SelectedNode != n ? n : null;
}
result.Handled = true;
if (MessageId != null)
{
var m = await Device.Edit(MessageId.Value, Title, bf);
}
else
{
var m = await Device.Send(Title, bf);
MessageId = m.MessageId;
}
break;
}
public string GetPath()
{
return (VisibleNode?.GetPath() ?? "\\");
}
}
}
public override async Task Render(MessageResult result)
{
var startnode = VisibleNode;
var nodes = startnode?.ChildNodes ?? Nodes;
var bf = new ButtonForm();
if (startnode != null)
{
bf.AddButtonRow(new ButtonBase(MoveUpIcon, "up"), new ButtonBase(startnode.Text, "parent"));
}
foreach (var n in nodes)
{
var s = n.Text;
if (SelectedNode == n)
{
s = "[ " + s + " ]";
}
bf.AddButtonRow(new ButtonBase(s, n.Value, n.Url));
}
if (MessageId != null)
{
var m = await Device.Edit(MessageId.Value, Title, bf);
}
else
{
var m = await Device.Send(Title, bf);
MessageId = m.MessageId;
}
}
public string GetPath()
{
return VisibleNode?.GetPath() ?? "\\";
}
}
+55 -56
View File
@@ -1,62 +1,61 @@
using System.Collections.Generic;
using System.Linq;
namespace TelegramBotBase.Controls.Inline
namespace TelegramBotBase.Controls.Inline;
public class TreeViewNode
{
public class TreeViewNode
public TreeViewNode(string text, string value)
{
public string Text { get; set; }
public string Value { get; set; }
public string Url { get; set; }
public List<TreeViewNode> ChildNodes { get; set; } = new List<TreeViewNode>();
public TreeViewNode ParentNode { get; set; }
public TreeViewNode(string text, string value)
{
this.Text = text;
this.Value = value;
}
public TreeViewNode(string text, string value, string url) : this(text, value)
{
this.Url = url;
}
public TreeViewNode(string text, string value, params TreeViewNode[] childnodes) : this(text, value)
{
foreach(var c in childnodes)
{
AddNode(c);
}
}
public void AddNode(TreeViewNode node)
{
node.ParentNode = this;
ChildNodes.Add(node);
}
public TreeViewNode FindNodeByValue(string value)
{
return ChildNodes.FirstOrDefault(a => a.Value == value);
}
public string GetPath()
{
var s = "\\" + Value;
var p = this;
while (p.ParentNode != null)
{
s = "\\" + p.ParentNode.Value + s;
p = p.ParentNode;
}
return s;
}
Text = text;
Value = value;
}
}
public TreeViewNode(string text, string value, string url) : this(text, value)
{
Url = url;
}
public TreeViewNode(string text, string value, params TreeViewNode[] childnodes) : this(text, value)
{
foreach (var c in childnodes)
{
AddNode(c);
}
}
public string Text { get; set; }
public string Value { get; set; }
public string Url { get; set; }
public List<TreeViewNode> ChildNodes { get; set; } = new();
public TreeViewNode ParentNode { get; set; }
public void AddNode(TreeViewNode node)
{
node.ParentNode = this;
ChildNodes.Add(node);
}
public TreeViewNode FindNodeByValue(string value)
{
return ChildNodes.FirstOrDefault(a => a.Value == value);
}
public string GetPath()
{
var s = "\\" + Value;
var p = this;
while (p.ParentNode != null)
{
s = "\\" + p.ParentNode.Value + s;
p = p.ParentNode;
}
return s;
}
}
@@ -4,132 +4,134 @@ using TelegramBotBase.Controls.Hybrid;
using TelegramBotBase.Form;
using TelegramBotBase.Interfaces;
namespace TelegramBotBase.DataSources
namespace TelegramBotBase.DataSources;
public class ButtonFormDataSource : IDataSource<ButtonRow>
{
public class ButtonFormDataSource : IDataSource<ButtonRow>
private ButtonForm _buttonform;
public ButtonFormDataSource()
{
public virtual ButtonForm ButtonForm
{
get => _buttonform;
set => _buttonform = value;
}
private ButtonForm _buttonform;
public ButtonFormDataSource()
{
_buttonform = new ButtonForm();
}
public ButtonFormDataSource(ButtonForm bf)
{
_buttonform = bf;
}
/// <summary>
/// Returns the amount of rows exisiting.
/// </summary>
/// <returns></returns>
public virtual int Count => ButtonForm.Count;
/// <summary>
/// Returns the amount of rows.
/// </summary>
public virtual int RowCount => ButtonForm.Rows;
/// <summary>
/// Returns the maximum amount of columns.
/// </summary>
public virtual int ColumnCount => ButtonForm.Cols;
/// <summary>
/// Returns the row with the specific index.
/// </summary>
/// <param name="index"></param>
/// <returns></returns>
public virtual ButtonRow ItemAt(int index)
{
return ButtonForm[index];
}
public virtual List<ButtonRow> ItemRange(int start, int count)
{
return ButtonForm.GetRange(start, count);
}
public virtual List<ButtonRow> AllItems()
{
return ButtonForm.ToArray();
}
public virtual ButtonForm PickItems(int start, int count, string filter = null)
{
var bf = new ButtonForm();
ButtonForm dataForm = null;
if (filter == null)
{
dataForm = ButtonForm.Duplicate();
}
else
{
dataForm = ButtonForm.FilterDuplicate(filter, true);
}
for (var i = 0; i < count; i++)
{
var it = start + i;
if (it > dataForm.Rows - 1)
break;
bf.AddButtonRow(dataForm[it]);
}
return bf;
}
public virtual ButtonForm PickAllItems(string filter = null)
{
if (filter == null)
return ButtonForm.Duplicate();
return ButtonForm.FilterDuplicate(filter, true);
}
public virtual Tuple<ButtonRow, int> FindRow(string text, bool useText = true)
{
return ButtonForm.FindRow(text, useText);
}
/// <summary>
/// Returns the maximum items of this data source.
/// </summary>
/// <param name="filter"></param>
/// <returns></returns>
public virtual int CalculateMax(string filter = null)
{
return PickAllItems(filter).Rows;
}
public virtual ButtonRow Render(object data)
{
return data as ButtonRow;
}
public static implicit operator ButtonFormDataSource(ButtonForm bf)
{
return new ButtonFormDataSource(bf);
}
public static implicit operator ButtonForm(ButtonFormDataSource ds)
{
return ds.ButtonForm;
}
_buttonform = new ButtonForm();
}
}
public ButtonFormDataSource(ButtonForm bf)
{
_buttonform = bf;
}
public virtual ButtonForm ButtonForm
{
get => _buttonform;
set => _buttonform = value;
}
/// <summary>
/// Returns the amount of rows.
/// </summary>
public virtual int RowCount => ButtonForm.Rows;
/// <summary>
/// Returns the maximum amount of columns.
/// </summary>
public virtual int ColumnCount => ButtonForm.Cols;
/// <summary>
/// Returns the amount of rows exisiting.
/// </summary>
/// <returns></returns>
public virtual int Count => ButtonForm.Count;
/// <summary>
/// Returns the row with the specific index.
/// </summary>
/// <param name="index"></param>
/// <returns></returns>
public virtual ButtonRow ItemAt(int index)
{
return ButtonForm[index];
}
public virtual List<ButtonRow> ItemRange(int start, int count)
{
return ButtonForm.GetRange(start, count);
}
public virtual List<ButtonRow> AllItems()
{
return ButtonForm.ToArray();
}
public virtual ButtonForm PickItems(int start, int count, string filter = null)
{
var bf = new ButtonForm();
ButtonForm dataForm = null;
if (filter == null)
{
dataForm = ButtonForm.Duplicate();
}
else
{
dataForm = ButtonForm.FilterDuplicate(filter, true);
}
for (var i = 0; i < count; i++)
{
var it = start + i;
if (it > dataForm.Rows - 1)
{
break;
}
bf.AddButtonRow(dataForm[it]);
}
return bf;
}
public virtual ButtonForm PickAllItems(string filter = null)
{
if (filter == null)
{
return ButtonForm.Duplicate();
}
return ButtonForm.FilterDuplicate(filter, true);
}
public virtual Tuple<ButtonRow, int> FindRow(string text, bool useText = true)
{
return ButtonForm.FindRow(text, useText);
}
/// <summary>
/// Returns the maximum items of this data source.
/// </summary>
/// <param name="filter"></param>
/// <returns></returns>
public virtual int CalculateMax(string filter = null)
{
return PickAllItems(filter).Rows;
}
public virtual ButtonRow Render(object data)
{
return data as ButtonRow;
}
public static implicit operator ButtonFormDataSource(ButtonForm bf)
{
return new ButtonFormDataSource(bf);
}
public static implicit operator ButtonForm(ButtonFormDataSource ds)
{
return ds.ButtonForm;
}
}
+30 -32
View File
@@ -2,38 +2,36 @@
using System.Linq;
using TelegramBotBase.Interfaces;
namespace TelegramBotBase.DataSources
namespace TelegramBotBase.DataSources;
public class StaticDataSource<T> : IDataSource<T>
{
public class StaticDataSource<T> : IDataSource<T>
public StaticDataSource()
{
private List<T> Data { get; set; }
public StaticDataSource()
{
}
public StaticDataSource(List<T> data)
{
Data = data;
}
public int Count => Data.Count;
public T ItemAt(int index)
{
return Data[index];
}
public List<T> ItemRange(int start, int count)
{
return Data.Skip(start).Take(count).ToList();
}
public List<T> AllItems()
{
return Data;
}
}
}
public StaticDataSource(List<T> data)
{
Data = data;
}
private List<T> Data { get; }
public int Count => Data.Count;
public T ItemAt(int index)
{
return Data[index];
}
public List<T> ItemRange(int start, int count)
{
return Data.Skip(start).Take(count).ToList();
}
public List<T> AllItems()
{
return Data;
}
}
+18 -17
View File
@@ -1,18 +1,19 @@
namespace TelegramBotBase.Enums
namespace TelegramBotBase.Enums;
public enum EDeleteMode
{
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
}
}
/// <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
}
+18 -17
View File
@@ -1,18 +1,19 @@
namespace TelegramBotBase.Enums
namespace TelegramBotBase.Enums;
public enum EDeleteSide
{
public enum EDeleteSide
{
/// <summary>
/// Delete only messages from this bot.
/// </summary>
BotOnly = 0,
/// <summary>
/// Delete only user messages.
/// </summary>
UserOnly = 1,
/// <summary>
/// Delete all messages in this context.
/// </summary>
Both = 2
}
}
/// <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
}
+12 -14
View File
@@ -1,16 +1,14 @@
namespace TelegramBotBase.Enums
namespace TelegramBotBase.Enums;
public enum EKeyboardType
{
public enum EKeyboardType
{
/// <summary>
/// Uses a ReplyKeyboardMarkup
/// </summary>
ReplyKeyboard = 0,
/// <summary>
/// Uses a ReplyKeyboardMarkup
/// </summary>
ReplyKeyboard = 0,
/// <summary>
/// Uses a InlineKeyboardMakup
/// </summary>
InlineKeyBoard = 1
}
}
/// <summary>
/// Uses a InlineKeyboardMakup
/// </summary>
InlineKeyBoard = 1
}
+18 -17
View File
@@ -1,18 +1,19 @@
namespace TelegramBotBase.Enums
namespace TelegramBotBase.Enums;
public enum EMonthPickerMode
{
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
}
}
/// <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
}
@@ -1,21 +1,19 @@
namespace TelegramBotBase.Enums
namespace TelegramBotBase.Enums;
public enum ENavigationBarVisibility
{
public enum ENavigationBarVisibility
{
/// <summary>
/// Shows it depending on the amount of items.
/// </summary>
auto = 0,
/// <summary>
/// Shows it depending on the amount of items.
/// </summary>
auto = 0,
/// <summary>
/// Will not show it at any time.
/// </summary>
never = 1,
/// <summary>
/// Will not show it at any time.
/// </summary>
never = 1,
/// <summary>
/// Will show it at any time.
/// </summary>
always = 2
}
}
/// <summary>
/// Will show it at any time.
/// </summary>
always = 2
}
+25 -27
View File
@@ -1,36 +1,34 @@
namespace TelegramBotBase.Enums
namespace TelegramBotBase.Enums;
public enum ESettings
{
public enum ESettings
{
/// <summary>
/// How often could a form navigate to another (within one user action/call/message)
/// </summary>
NavigationMaximum = 1,
/// <summary>
/// How often could a form navigate to another (within one user action/call/message)
/// </summary>
NavigationMaximum = 1,
/// <summary>
/// Loggs all messages and sent them to the event handler
/// </summary>
LogAllMessages = 2,
/// <summary>
/// Loggs all messages and sent them to the event handler
/// </summary>
LogAllMessages = 2,
/// <summary>
/// Skips all messages during running (good for big delay updates)
/// </summary>
SkipAllMessages = 3,
/// <summary>
/// Skips all messages during running (good for big delay updates)
/// </summary>
SkipAllMessages = 3,
/// <summary>
/// Does stick to the console event handler and saves all sessions on exit.
/// </summary>
SaveSessionsOnConsoleExit = 4,
/// <summary>
/// Does stick to the console event handler and saves all sessions on exit.
/// </summary>
SaveSessionsOnConsoleExit = 4,
/// <summary>
/// Indicates the maximum number of times a request that received error
/// 429 will be sent again after a timeout until it receives code 200 or an error code not equal to 429.
/// </summary>
MaxNumberOfRetries = 5,
}
}
/// <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
}
@@ -1,13 +1,11 @@
using System;
namespace TelegramBotBase.Exceptions
{
public class MaxLengthException : Exception
{
public MaxLengthException(int length) : base($"Your messages with a length of {length} is too long for telegram. Actually is {Constants.Telegram.MaxMessageLength} characters allowed. Please split it.")
{
}
namespace TelegramBotBase.Exceptions;
public class MaxLengthException : Exception
{
public MaxLengthException(int length) : base(
$"Your messages with a length of {length} is too long for telegram. Actually is {Constants.Telegram.MaxMessageLength} characters allowed. Please split it.")
{
}
}
}
@@ -1,14 +1,14 @@
using System;
namespace TelegramBotBase.Exceptions
namespace TelegramBotBase.Exceptions;
public class MaximumColsException : Exception
{
public class MaximumColsException : Exception
{
public int Value { get; set; }
public int Value { get; set; }
public int Maximum { get; set; }
public int Maximum { get; set; }
public override string Message => $"You have exceeded the maximum of columns by {Value.ToString()} / {Maximum.ToString()}";
}
}
public override string Message =>
$"You have exceeded the maximum of columns by {Value.ToString()} / {Maximum.ToString()}";
}
@@ -1,14 +1,14 @@
using System;
namespace TelegramBotBase.Exceptions
namespace TelegramBotBase.Exceptions;
public class MaximumRowsReachedException : Exception
{
public class MaximumRowsReachedException : Exception
{
public int Value { get; set; }
public int Value { get; set; }
public int Maximum { get; set; }
public int Maximum { get; set; }
public override string Message => $"You have exceeded the maximum of rows by {Value.ToString()} / {Maximum.ToString()}";
}
}
public override string Message =>
$"You have exceeded the maximum of rows by {Value.ToString()} / {Maximum.ToString()}";
}
@@ -2,24 +2,25 @@
using TelegramBotBase.Form;
using TelegramBotBase.Interfaces;
namespace TelegramBotBase.Factories
namespace TelegramBotBase.Factories;
public class DefaultStartFormFactory : IStartFormFactory
{
public class DefaultStartFormFactory : IStartFormFactory
private readonly Type _startFormClass;
public DefaultStartFormFactory(Type startFormClass)
{
private readonly Type _startFormClass;
public DefaultStartFormFactory(Type startFormClass)
if (!typeof(FormBase).IsAssignableFrom(startFormClass))
{
if (!typeof(FormBase).IsAssignableFrom(startFormClass))
throw new ArgumentException("startFormClass argument must be a FormBase type");
_startFormClass = startFormClass;
throw new ArgumentException("startFormClass argument must be a FormBase type");
}
public FormBase CreateForm()
{
return _startFormClass.GetConstructor(new Type[] { })?.Invoke(new object[] { }) as FormBase;
}
_startFormClass = startFormClass;
}
}
public FormBase CreateForm()
{
return _startFormClass.GetConstructor(new Type[] { })?.Invoke(new object[] { }) as FormBase;
}
}
@@ -1,22 +1,21 @@
using TelegramBotBase.Form;
using TelegramBotBase.Interfaces;
namespace TelegramBotBase.Factories
namespace TelegramBotBase.Factories;
public class LambdaStartFormFactory : IStartFormFactory
{
public class LambdaStartFormFactory : IStartFormFactory
public delegate FormBase CreateFormDelegate();
private readonly CreateFormDelegate _lambda;
public LambdaStartFormFactory(CreateFormDelegate lambda)
{
public delegate FormBase CreateFormDelegate();
private readonly CreateFormDelegate _lambda;
public LambdaStartFormFactory(CreateFormDelegate lambda)
{
_lambda = lambda;
}
public FormBase CreateForm()
{
return _lambda();
}
_lambda = lambda;
}
}
public FormBase CreateForm()
{
return _lambda();
}
}
@@ -3,33 +3,34 @@ using Microsoft.Extensions.DependencyInjection;
using TelegramBotBase.Form;
using TelegramBotBase.Interfaces;
namespace TelegramBotBase.Factories
namespace TelegramBotBase.Factories;
public class ServiceProviderStartFormFactory : IStartFormFactory
{
public class ServiceProviderStartFormFactory : IStartFormFactory
private readonly IServiceProvider _serviceProvider;
private readonly Type _startFormClass;
public ServiceProviderStartFormFactory(Type startFormClass, IServiceProvider serviceProvider)
{
private readonly Type _startFormClass;
private readonly IServiceProvider _serviceProvider;
public ServiceProviderStartFormFactory(Type startFormClass, IServiceProvider serviceProvider)
if (!typeof(FormBase).IsAssignableFrom(startFormClass))
{
if (!typeof(FormBase).IsAssignableFrom(startFormClass))
throw new ArgumentException("startFormClass argument must be a FormBase type");
_startFormClass = startFormClass;
_serviceProvider = serviceProvider;
throw new ArgumentException("startFormClass argument must be a FormBase type");
}
public FormBase CreateForm()
{
return (FormBase)ActivatorUtilities.CreateInstance(_serviceProvider, _startFormClass);
}
_startFormClass = startFormClass;
_serviceProvider = serviceProvider;
}
public class ServiceProviderStartFormFactory<T> : ServiceProviderStartFormFactory
where T : FormBase
public FormBase CreateForm()
{
public ServiceProviderStartFormFactory(IServiceProvider serviceProvider) : base(typeof(T), serviceProvider)
{
}
return (FormBase)ActivatorUtilities.CreateInstance(_serviceProvider, _startFormClass);
}
}
public class ServiceProviderStartFormFactory<T> : ServiceProviderStartFormFactory
where T : FormBase
{
public ServiceProviderStartFormFactory(IServiceProvider serviceProvider) : base(typeof(T), serviceProvider)
{
}
}
+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);
}
}
+27 -31
View File
@@ -1,40 +1,36 @@
using System.Collections.Generic;
namespace TelegramBotBase.Interfaces
namespace TelegramBotBase.Interfaces;
public interface IDataSource<T>
{
public interface IDataSource<T>
{
/// <summary>
/// Returns the amount of items within this source.
/// </summary>
/// <returns></returns>
int Count { get; }
/// <summary>
/// Returns the amount of items within this source.
/// </summary>
/// <returns></returns>
int Count { get; }
/// <summary>
/// Returns the item at the specific index.
/// </summary>
/// <param name="index"></param>
/// <returns></returns>
T ItemAt(int index);
/// <summary>
/// Returns the item at the specific index.
/// </summary>
/// <param name="index"></param>
/// <returns></returns>
T ItemAt(int index);
/// <summary>
/// Get all items from this source within this range.
/// </summary>
/// <param name="start"></param>
/// <param name="count"></param>
/// <returns></returns>
List<T> ItemRange(int start, int count);
/// <summary>
/// Get all items from this source within this range.
/// </summary>
/// <param name="start"></param>
/// <param name="count"></param>
/// <returns></returns>
List<T> ItemRange(int start, int count);
/// <summary>
/// Gets a list of all items of this datasource.
/// </summary>
/// <returns></returns>
List<T> AllItems();
}
}
/// <summary>
/// Gets a list of all items of this datasource.
/// </summary>
/// <returns></returns>
List<T> AllItems();
}
+28 -30
View File
@@ -1,40 +1,38 @@
using System;
using TelegramBotBase.Form;
namespace TelegramBotBase.Interfaces
namespace TelegramBotBase.Interfaces;
internal interface IDeviceSession
{
internal interface IDeviceSession
{
/// <summary>
/// Device or chat id
/// </summary>
long DeviceId { get; set; }
/// <summary>
/// Device or chat id
/// </summary>
long DeviceId { get; set; }
/// <summary>
/// Username of user or group
/// </summary>
string ChatTitle { get; set; }
/// <summary>
/// Username of user or group
/// </summary>
string ChatTitle { get; set; }
/// <summary>
/// When did any last action happend (message received or button clicked)
/// </summary>
DateTime LastAction { get; set; }
/// <summary>
/// When did any last action happend (message received or button clicked)
/// </summary>
DateTime LastAction { get; set; }
/// <summary>
/// Returns the form where the user/group is at the moment.
/// </summary>
FormBase ActiveForm { get; set; }
/// <summary>
/// Returns the form where the user/group is at the moment.
/// </summary>
FormBase ActiveForm { get; set; }
/// <summary>
/// Returns the previous shown form
/// </summary>
FormBase PreviousForm { get; set; }
/// <summary>
/// Returns the previous shown form
/// </summary>
FormBase PreviousForm { get; set; }
/// <summary>
/// contains if the form has been switched (navigated)
/// </summary>
bool FormSwitched { get; set; }
}
}
/// <summary>
/// contains if the form has been switched (navigated)
/// </summary>
bool FormSwitched { get; set; }
}
@@ -4,15 +4,11 @@ using TelegramBotBase.Args;
using TelegramBotBase.Base;
using TelegramBotBase.Sessions;
namespace TelegramBotBase.Interfaces
namespace TelegramBotBase.Interfaces;
public interface IMessageLoopFactory
{
public interface IMessageLoopFactory
{
Task MessageLoop(BotBase bot, DeviceSession session, UpdateResult ur, MessageResult e);
Task MessageLoop(BotBase bot, DeviceSession session, UpdateResult ur, MessageResult e);
event EventHandler<UnhandledCallEventArgs> UnhandledCall;
}
}
event EventHandler<UnhandledCallEventArgs> UnhandledCall;
}
@@ -1,9 +1,8 @@
using TelegramBotBase.Form;
namespace TelegramBotBase.Interfaces
namespace TelegramBotBase.Interfaces;
public interface IStartFormFactory
{
public interface IStartFormFactory
{
FormBase CreateForm();
}
}
FormBase CreateForm();
}
+9 -12
View File
@@ -1,16 +1,13 @@
using TelegramBotBase.Args;
namespace TelegramBotBase.Interfaces
namespace TelegramBotBase.Interfaces;
/// <summary>
/// Is used to save specific fields into a session state to survive restarts or unhandled exceptions and crashes.
/// </summary>
public interface IStateForm
{
/// <summary>
/// Is used to save specific fields into a session state to survive restarts or unhandled exceptions and crashes.
/// </summary>
public interface IStateForm
{
void LoadState(LoadStateEventArgs e);
void LoadState(LoadStateEventArgs e);
void SaveState(SaveStateEventArgs e);
}
}
void SaveState(SaveStateEventArgs e);
}
+7 -8
View File
@@ -2,14 +2,13 @@
using TelegramBotBase.Args;
using TelegramBotBase.Base;
namespace TelegramBotBase.Interfaces
namespace TelegramBotBase.Interfaces;
public interface IStateMachine
{
public interface IStateMachine
{
Type FallbackStateForm { get; }
Type FallbackStateForm { get; }
void SaveFormStates(SaveStatesEventArgs e);
void SaveFormStates(SaveStatesEventArgs e);
StateContainer LoadFormStates();
}
}
StateContainer LoadFormStates();
}
+5 -9
View File
@@ -1,10 +1,6 @@
namespace TelegramBotBase.Localizations
namespace TelegramBotBase.Localizations;
public static class Default
{
public static class Default
{
public static Localization Language = new English();
}
}
public static Localization Language = new English();
}
+32 -34
View File
@@ -1,37 +1,35 @@
namespace TelegramBotBase.Localizations
namespace TelegramBotBase.Localizations;
public class English : Localization
{
public class English : Localization
public English()
{
public English()
{
Values["Language"] = "English";
Values["ButtonGrid_Title"] = "Menu";
Values["ButtonGrid_NoItems"] = "There are no items.";
Values["ButtonGrid_PreviousPage"] = "◀️";
Values["ButtonGrid_NextPage"] = "▶️";
Values["ButtonGrid_CurrentPage"] = "Page {0} of {1}";
Values["ButtonGrid_SearchFeature"] = "💡 Send a message to filter the list. Click the 🔍 to reset the filter.";
Values["ButtonGrid_Back"] = "Back";
Values["ButtonGrid_CheckAll"] = "Check all";
Values["ButtonGrid_UncheckAll"] = "Uncheck all";
Values["CalendarPicker_Title"] = "Pick date";
Values["CalendarPicker_PreviousPage"] = "";
Values["CalendarPicker_NextPage"] = "▶️";
Values["TreeView_Title"] = "Select node";
Values["TreeView_LevelUp"] = "🔼 level up";
Values["ToggleButton_On"] = "On";
Values["ToggleButton_Off"] = "Off";
Values["ToggleButton_OnIcon"] = "";
Values["ToggleButton_OffIcon"] = "";
Values["ToggleButton_Title"] = "Toggle";
Values["ToggleButton_Changed"] = "Choosen";
Values["MultiToggleButton_SelectedIcon"] = "✅";
Values["MultiToggleButton_Title"] = "Multi-Toggle";
Values["MultiToggleButton_Changed"] = "Choosen";
Values["PromptDialog_Back"] = "Back";
Values["ToggleButton_Changed"] = "Setting changed";
}
Values["Language"] = "English";
Values["ButtonGrid_Title"] = "Menu";
Values["ButtonGrid_NoItems"] = "There are no items.";
Values["ButtonGrid_PreviousPage"] = "◀️";
Values["ButtonGrid_NextPage"] = "▶️";
Values["ButtonGrid_CurrentPage"] = "Page {0} of {1}";
Values["ButtonGrid_SearchFeature"] =
"💡 Send a message to filter the list. Click the 🔍 to reset the filter.";
Values["ButtonGrid_Back"] = "Back";
Values["ButtonGrid_CheckAll"] = "Check all";
Values["ButtonGrid_UncheckAll"] = "Uncheck all";
Values["CalendarPicker_Title"] = "Pick date";
Values["CalendarPicker_PreviousPage"] = "◀️";
Values["CalendarPicker_NextPage"] = "";
Values["TreeView_Title"] = "Select node";
Values["TreeView_LevelUp"] = "🔼 level up";
Values["ToggleButton_On"] = "On";
Values["ToggleButton_Off"] = "Off";
Values["ToggleButton_OnIcon"] = "";
Values["ToggleButton_OffIcon"] = "";
Values["ToggleButton_Title"] = "Toggle";
Values["ToggleButton_Changed"] = "Choosen";
Values["MultiToggleButton_SelectedIcon"] = "";
Values["MultiToggleButton_Title"] = "Multi-Toggle";
Values["MultiToggleButton_Changed"] = "Choosen";
Values["PromptDialog_Back"] = "Back";
Values["ToggleButton_Changed"] = "Setting changed";
}
}
}
+32 -34
View File
@@ -1,37 +1,35 @@
namespace TelegramBotBase.Localizations
namespace TelegramBotBase.Localizations;
public class German : Localization
{
public class German : Localization
public German()
{
public German()
{
Values["Language"] = "Deutsch (German)";
Values["ButtonGrid_Title"] = "Menü";
Values["ButtonGrid_NoItems"] = "Es sind keine Einträge vorhanden.";
Values["ButtonGrid_PreviousPage"] = "◀️";
Values["ButtonGrid_NextPage"] = "▶️";
Values["ButtonGrid_CurrentPage"] = "Seite {0} von {1}";
Values["ButtonGrid_SearchFeature"] = "💡 Sende eine Nachricht um die Liste zu filtern. Klicke die 🔍 um den Filter zurückzusetzen.";
Values["ButtonGrid_Back"] = "Zurück";
Values["ButtonGrid_CheckAll"] = "Alle auswählen";
Values["ButtonGrid_UncheckAll"] = "Keine auswählen";
Values["CalendarPicker_Title"] = "Datum auswählen";
Values["CalendarPicker_PreviousPage"] = "";
Values["CalendarPicker_NextPage"] = "▶️";
Values["TreeView_Title"] = "Knoten auswählen";
Values["TreeView_LevelUp"] = "🔼 Stufe hoch";
Values["ToggleButton_On"] = "An";
Values["ToggleButton_Off"] = "Aus";
Values["ToggleButton_OnIcon"] = "";
Values["ToggleButton_OffIcon"] = "";
Values["ToggleButton_Title"] = "Schalter";
Values["ToggleButton_Changed"] = "Ausgewählt";
Values["MultiToggleButton_SelectedIcon"] = "✅";
Values["MultiToggleButton_Title"] = "Mehrfach-Schalter";
Values["MultiToggleButton_Changed"] = "Ausgewählt";
Values["PromptDialog_Back"] = "Zurück";
Values["ToggleButton_Changed"] = "Einstellung geändert";
}
Values["Language"] = "Deutsch (German)";
Values["ButtonGrid_Title"] = "Menü";
Values["ButtonGrid_NoItems"] = "Es sind keine Einträge vorhanden.";
Values["ButtonGrid_PreviousPage"] = "◀️";
Values["ButtonGrid_NextPage"] = "▶️";
Values["ButtonGrid_CurrentPage"] = "Seite {0} von {1}";
Values["ButtonGrid_SearchFeature"] =
"💡 Sende eine Nachricht um die Liste zu filtern. Klicke die 🔍 um den Filter zurückzusetzen.";
Values["ButtonGrid_Back"] = "Zurück";
Values["ButtonGrid_CheckAll"] = "Alle auswählen";
Values["ButtonGrid_UncheckAll"] = "Keine auswählen";
Values["CalendarPicker_Title"] = "Datum auswählen";
Values["CalendarPicker_PreviousPage"] = "◀️";
Values["CalendarPicker_NextPage"] = "";
Values["TreeView_Title"] = "Knoten auswählen";
Values["TreeView_LevelUp"] = "🔼 Stufe hoch";
Values["ToggleButton_On"] = "An";
Values["ToggleButton_Off"] = "Aus";
Values["ToggleButton_OnIcon"] = "";
Values["ToggleButton_OffIcon"] = "";
Values["ToggleButton_Title"] = "Schalter";
Values["ToggleButton_Changed"] = "Ausgewählt";
Values["MultiToggleButton_SelectedIcon"] = "";
Values["MultiToggleButton_Title"] = "Mehrfach-Schalter";
Values["MultiToggleButton_Changed"] = "Ausgewählt";
Values["PromptDialog_Back"] = "Zurück";
Values["ToggleButton_Changed"] = "Einstellung geändert";
}
}
}
@@ -1,12 +1,10 @@
using System.Collections.Generic;
namespace TelegramBotBase.Localizations
namespace TelegramBotBase.Localizations;
public abstract class Localization
{
public abstract class Localization
{
public Dictionary<string, string> Values = new Dictionary<string, string>();
public string this[string key] => Values[key];
}
}
public Dictionary<string, string> Values = new();
public string this[string key] => Values[key];
}
+150 -150
View File
@@ -1,159 +1,159 @@
using System.Linq;
using Telegram.Bot.Types.Enums;
namespace TelegramBotBase.Markdown
namespace TelegramBotBase.Markdown;
/// <summary>
/// https://core.telegram.org/bots/api#markdownv2-style
/// </summary>
public static class Generator
{
public static ParseMode OutputMode { get; set; } = ParseMode.Markdown;
/// <summary>
/// https://core.telegram.org/bots/api#markdownv2-style
/// Generates a link with title in Markdown or HTML
/// </summary>
public static class Generator
/// <param name="url"></param>
/// <param name="title"></param>
/// <param name="tooltip"></param>
/// <returns></returns>
public static string Link(this string url, string title = null, string tooltip = null)
{
public static ParseMode OutputMode { get; set; } = ParseMode.Markdown;
/// <summary>
/// Generates a link with title in Markdown or HTML
/// </summary>
/// <param name="url"></param>
/// <param name="title"></param>
/// <param name="tooltip"></param>
/// <returns></returns>
public static string Link(this string url, string title = null, string tooltip = null)
return OutputMode switch
{
return OutputMode switch
{
ParseMode.Markdown => "[" + (title ?? url) + "](" + url + " " + (tooltip ?? "") + ")",
ParseMode.Html => $"<a href=\"{url}\" title=\"{tooltip ?? ""}\">{title ?? ""}</b>",
_ => url
};
}
/// <summary>
/// Returns a Link to the User, title is optional.
/// </summary>
/// <param name="userId"></param>
/// <param name="title"></param>
/// <returns></returns>
public static string MentionUser(this int userId, string title = null)
{
return Link("tg://user?id=" + userId, title);
}
/// <summary>
/// Returns a Link to the User, title is optional.
/// </summary>
/// <param name="userId"></param>
/// <param name="title"></param>
/// <returns></returns>
public static string MentionUser(this string username, string title = null)
{
return Link("tg://user?id=" + username, title);
}
/// <summary>
/// Returns a bold text in Markdown or HTML
/// </summary>
/// <param name="text"></param>
/// <returns></returns>
public static string Bold(this string text)
{
return OutputMode switch
{
ParseMode.Markdown => "*" + text + "*",
ParseMode.Html => "<b>" + text + "</b>",
_ => text
};
}
/// <summary>
/// Returns a strike through in Markdown or HTML
/// </summary>
/// <param name="text"></param>
/// <returns></returns>
public static string Strikesthrough(this string text)
{
return OutputMode switch
{
ParseMode.Markdown => "~" + text + "~",
ParseMode.Html => "<s>" + text + "</s>",
_ => text
};
}
/// <summary>
/// Returns a italic text in Markdown or HTML
/// </summary>
/// <param name="text"></param>
/// <returns></returns>
public static string Italic(this string text)
{
return OutputMode switch
{
ParseMode.Markdown => "_" + text + "_",
ParseMode.Html => "<i>" + text + "</i>",
_ => text
};
}
/// <summary>
/// Returns a underline text in Markdown or HTML
/// </summary>
/// <param name="text"></param>
/// <returns></returns>
public static string Underline(this string text)
{
return OutputMode switch
{
ParseMode.Markdown => "__" + text + "__",
ParseMode.Html => "<u>" + text + "</u>",
_ => text
};
}
/// <summary>
/// Returns a monospace text in Markdown or HTML
/// </summary>
/// <param name="text"></param>
/// <returns></returns>
public static string Monospace(this string text)
{
return OutputMode switch
{
ParseMode.Markdown => "`" + text + "`",
ParseMode.Html => "<code>" + text + "</code>",
_ => text
};
}
/// <summary>
/// Returns a multi monospace text in Markdown or HTML
/// </summary>
/// <param name="text"></param>
/// <returns></returns>
public static string MultiMonospace(this string text)
{
return OutputMode switch
{
ParseMode.Markdown => "```" + text + "```",
ParseMode.Html => "<pre>" + text + "</pre>",
_ => text
};
}
/// <summary>
/// Escapes all characters as stated in the documentation: https://core.telegram.org/bots/api#markdownv2-style
/// </summary>
/// <param name="text"></param>
/// <returns></returns>
public static string MarkdownV2Escape(this string text, params char[] toKeep)
{
var toEscape = new[] { '_', '*', '[', ']', '(', ')', '~', '`', '>', '#', '+', '-', '=', '|', '{', '}', '.', '!' };
return text.EscapeAll(toEscape.Where(a => !toKeep.Contains(a)).Select(a => a.ToString()).ToArray());
}
public static string EscapeAll(this string seed, string[] chars, char escapeCharacter = '\\')
{
return chars.Aggregate(seed, (str, cItem) => str.Replace(cItem, escapeCharacter + cItem));
}
ParseMode.Markdown => "[" + (title ?? url) + "](" + url + " " + (tooltip ?? "") + ")",
ParseMode.Html => $"<a href=\"{url}\" title=\"{tooltip ?? ""}\">{title ?? ""}</b>",
_ => url
};
}
}
/// <summary>
/// Returns a Link to the User, title is optional.
/// </summary>
/// <param name="userId"></param>
/// <param name="title"></param>
/// <returns></returns>
public static string MentionUser(this int userId, string title = null)
{
return Link("tg://user?id=" + userId, title);
}
/// <summary>
/// Returns a Link to the User, title is optional.
/// </summary>
/// <param name="userId"></param>
/// <param name="title"></param>
/// <returns></returns>
public static string MentionUser(this string username, string title = null)
{
return Link("tg://user?id=" + username, title);
}
/// <summary>
/// Returns a bold text in Markdown or HTML
/// </summary>
/// <param name="text"></param>
/// <returns></returns>
public static string Bold(this string text)
{
return OutputMode switch
{
ParseMode.Markdown => "*" + text + "*",
ParseMode.Html => "<b>" + text + "</b>",
_ => text
};
}
/// <summary>
/// Returns a strike through in Markdown or HTML
/// </summary>
/// <param name="text"></param>
/// <returns></returns>
public static string Strikesthrough(this string text)
{
return OutputMode switch
{
ParseMode.Markdown => "~" + text + "~",
ParseMode.Html => "<s>" + text + "</s>",
_ => text
};
}
/// <summary>
/// Returns a italic text in Markdown or HTML
/// </summary>
/// <param name="text"></param>
/// <returns></returns>
public static string Italic(this string text)
{
return OutputMode switch
{
ParseMode.Markdown => "_" + text + "_",
ParseMode.Html => "<i>" + text + "</i>",
_ => text
};
}
/// <summary>
/// Returns a underline text in Markdown or HTML
/// </summary>
/// <param name="text"></param>
/// <returns></returns>
public static string Underline(this string text)
{
return OutputMode switch
{
ParseMode.Markdown => "__" + text + "__",
ParseMode.Html => "<u>" + text + "</u>",
_ => text
};
}
/// <summary>
/// Returns a monospace text in Markdown or HTML
/// </summary>
/// <param name="text"></param>
/// <returns></returns>
public static string Monospace(this string text)
{
return OutputMode switch
{
ParseMode.Markdown => "`" + text + "`",
ParseMode.Html => "<code>" + text + "</code>",
_ => text
};
}
/// <summary>
/// Returns a multi monospace text in Markdown or HTML
/// </summary>
/// <param name="text"></param>
/// <returns></returns>
public static string MultiMonospace(this string text)
{
return OutputMode switch
{
ParseMode.Markdown => "```" + text + "```",
ParseMode.Html => "<pre>" + text + "</pre>",
_ => text
};
}
/// <summary>
/// Escapes all characters as stated in the documentation: https://core.telegram.org/bots/api#markdownv2-style
/// </summary>
/// <param name="text"></param>
/// <returns></returns>
public static string MarkdownV2Escape(this string text, params char[] toKeep)
{
var toEscape = new[]
{ '_', '*', '[', ']', '(', ')', '~', '`', '>', '#', '+', '-', '=', '|', '{', '}', '.', '!' };
return text.EscapeAll(toEscape.Where(a => !toKeep.Contains(a)).Select(a => a.ToString()).ToArray());
}
public static string EscapeAll(this string seed, string[] chars, char escapeCharacter = '\\')
{
return chars.Aggregate(seed, (str, cItem) => str.Replace(cItem, escapeCharacter + cItem));
}
}
@@ -7,116 +7,116 @@ using TelegramBotBase.Base;
using TelegramBotBase.Interfaces;
using TelegramBotBase.Sessions;
namespace TelegramBotBase.MessageLoops
namespace TelegramBotBase.MessageLoops;
/// <summary>
/// Thats the default message loop which reacts to Message, EditMessage and CallbackQuery.
/// </summary>
public class FormBaseMessageLoop : IMessageLoopFactory
{
/// <summary>
/// Thats the default message loop which reacts to Message, EditMessage and CallbackQuery.
/// </summary>
public class FormBaseMessageLoop : IMessageLoopFactory
private static readonly object EvUnhandledCall = new();
private readonly EventHandlerList _events = new();
public async Task MessageLoop(BotBase bot, DeviceSession session, UpdateResult ur, MessageResult mr)
{
private static readonly object EvUnhandledCall = new object();
var update = ur.RawData;
private readonly EventHandlerList _events = new EventHandlerList();
public async Task MessageLoop(BotBase bot, DeviceSession session, UpdateResult ur, MessageResult mr)
if (update.Type != UpdateType.Message
&& update.Type != UpdateType.EditedMessage
&& update.Type != UpdateType.CallbackQuery)
{
var update = ur.RawData;
return;
}
//Is this a bot command ?
if (mr.IsFirstHandler && mr.IsBotCommand && bot.IsKnownBotCommand(mr.BotCommand))
{
var sce = new BotCommandEventArgs(mr.BotCommand, mr.BotCommandParameters, mr.Message, session.DeviceId,
session);
await bot.OnBotCommand(sce);
if (update.Type != UpdateType.Message
&& update.Type != UpdateType.EditedMessage
&& update.Type != UpdateType.CallbackQuery)
if (sce.Handled)
{
return;
}
}
//Is this a bot command ?
if (mr.IsFirstHandler && mr.IsBotCommand && bot.IsKnownBotCommand(mr.BotCommand))
mr.Device = session;
var activeForm = session.ActiveForm;
//Pre Loading Event
await activeForm.PreLoad(mr);
//Send Load event to controls
await activeForm.LoadControls(mr);
//Loading Event
await activeForm.Load(mr);
//Is Attachment ? (Photo, Audio, Video, Contact, Location, Document) (Ignore Callback Queries)
if (update.Type == UpdateType.Message)
{
if ((mr.MessageType == MessageType.Contact)
| (mr.MessageType == MessageType.Document)
| (mr.MessageType == MessageType.Location)
| (mr.MessageType == MessageType.Photo)
| (mr.MessageType == MessageType.Video)
| (mr.MessageType == MessageType.Audio))
{
var sce = new BotCommandEventArgs(mr.BotCommand, mr.BotCommandParameters, mr.Message, session.DeviceId, session);
await bot.OnBotCommand(sce);
if (sce.Handled)
return;
await activeForm.SentData(new DataResult(ur));
}
}
mr.Device = session;
//Action Event
if (!session.FormSwitched && mr.IsAction)
{
//Send Action event to controls
await activeForm.ActionControls(mr);
var activeForm = session.ActiveForm;
//Send Action event to form itself
await activeForm.Action(mr);
//Pre Loading Event
await activeForm.PreLoad(mr);
//Send Load event to controls
await activeForm.LoadControls(mr);
//Loading Event
await activeForm.Load(mr);
//Is Attachment ? (Photo, Audio, Video, Contact, Location, Document) (Ignore Callback Queries)
if (update.Type == UpdateType.Message)
if (!mr.Handled)
{
if (mr.MessageType == MessageType.Contact
| mr.MessageType == MessageType.Document
| mr.MessageType == MessageType.Location
| mr.MessageType == MessageType.Photo
| mr.MessageType == MessageType.Video
| mr.MessageType == MessageType.Audio)
var uhc = new UnhandledCallEventArgs(ur.Message.Text, mr.RawData, session.DeviceId, mr.MessageId,
ur.Message, session);
OnUnhandledCall(uhc);
if (uhc.Handled)
{
await activeForm.SentData(new DataResult(ur));
}
}
//Action Event
if (!session.FormSwitched && mr.IsAction)
{
//Send Action event to controls
await activeForm.ActionControls(mr);
//Send Action event to form itself
await activeForm.Action(mr);
if (!mr.Handled)
{
var uhc = new UnhandledCallEventArgs(ur.Message.Text, mr.RawData, session.DeviceId, mr.MessageId, ur.Message, session);
OnUnhandledCall(uhc);
if (uhc.Handled)
mr.Handled = true;
if (!session.FormSwitched)
{
mr.Handled = true;
if (!session.FormSwitched)
{
return;
}
return;
}
}
}
if (!session.FormSwitched)
{
//Render Event
await activeForm.RenderControls(mr);
await activeForm.Render(mr);
}
}
/// <summary>
/// Will be called if no form handeled this call
/// </summary>
public event EventHandler<UnhandledCallEventArgs> UnhandledCall
if (!session.FormSwitched)
{
add => _events.AddHandler(EvUnhandledCall, value);
remove => _events.RemoveHandler(EvUnhandledCall, value);
}
public void OnUnhandledCall(UnhandledCallEventArgs e)
{
(_events[EvUnhandledCall] as EventHandler<UnhandledCallEventArgs>)?.Invoke(this, e);
//Render Event
await activeForm.RenderControls(mr);
await activeForm.Render(mr);
}
}
}
/// <summary>
/// Will be called if no form handeled this call
/// </summary>
public event EventHandler<UnhandledCallEventArgs> UnhandledCall
{
add => _events.AddHandler(EvUnhandledCall, value);
remove => _events.RemoveHandler(EvUnhandledCall, value);
}
public void OnUnhandledCall(UnhandledCallEventArgs e)
{
(_events[EvUnhandledCall] as EventHandler<UnhandledCallEventArgs>)?.Invoke(this, e);
}
}
+75 -75
View File
@@ -7,109 +7,109 @@ using TelegramBotBase.Base;
using TelegramBotBase.Interfaces;
using TelegramBotBase.Sessions;
namespace TelegramBotBase.MessageLoops
namespace TelegramBotBase.MessageLoops;
/// <summary>
/// This message loop reacts to all update types.
/// </summary>
public class FullMessageLoop : IMessageLoopFactory
{
/// <summary>
/// This message loop reacts to all update types.
/// </summary>
public class FullMessageLoop : IMessageLoopFactory
private static readonly object EvUnhandledCall = new();
private readonly EventHandlerList _events = new();
public async Task MessageLoop(BotBase bot, DeviceSession session, UpdateResult ur, MessageResult mr)
{
private static readonly object EvUnhandledCall = new object();
var update = ur.RawData;
private readonly EventHandlerList _events = new EventHandlerList();
public async Task MessageLoop(BotBase bot, DeviceSession session, UpdateResult ur, MessageResult mr)
//Is this a bot command ?
if (mr.IsFirstHandler && mr.IsBotCommand && bot.IsKnownBotCommand(mr.BotCommand))
{
var update = ur.RawData;
var sce = new BotCommandEventArgs(mr.BotCommand, mr.BotCommandParameters, mr.Message, session.DeviceId,
session);
await bot.OnBotCommand(sce);
//Is this a bot command ?
if (mr.IsFirstHandler && mr.IsBotCommand && bot.IsKnownBotCommand(mr.BotCommand))
if (sce.Handled)
{
var sce = new BotCommandEventArgs(mr.BotCommand, mr.BotCommandParameters, mr.Message, session.DeviceId, session);
await bot.OnBotCommand(sce);
if (sce.Handled)
return;
return;
}
}
mr.Device = session;
mr.Device = session;
var activeForm = session.ActiveForm;
var activeForm = session.ActiveForm;
//Pre Loading Event
await activeForm.PreLoad(mr);
//Pre Loading Event
await activeForm.PreLoad(mr);
//Send Load event to controls
await activeForm.LoadControls(mr);
//Send Load event to controls
await activeForm.LoadControls(mr);
//Loading Event
await activeForm.Load(mr);
//Loading Event
await activeForm.Load(mr);
//Is Attachment ? (Photo, Audio, Video, Contact, Location, Document) (Ignore Callback Queries)
if (update.Type == UpdateType.Message)
//Is Attachment ? (Photo, Audio, Video, Contact, Location, Document) (Ignore Callback Queries)
if (update.Type == UpdateType.Message)
{
if ((mr.MessageType == MessageType.Contact)
| (mr.MessageType == MessageType.Document)
| (mr.MessageType == MessageType.Location)
| (mr.MessageType == MessageType.Photo)
| (mr.MessageType == MessageType.Video)
| (mr.MessageType == MessageType.Audio))
{
if (mr.MessageType == MessageType.Contact
| mr.MessageType == MessageType.Document
| mr.MessageType == MessageType.Location
| mr.MessageType == MessageType.Photo
| mr.MessageType == MessageType.Video
| mr.MessageType == MessageType.Audio)
{
await activeForm.SentData(new DataResult(ur));
}
await activeForm.SentData(new DataResult(ur));
}
}
//Action Event
if (!session.FormSwitched && mr.IsAction)
//Action Event
if (!session.FormSwitched && mr.IsAction)
{
//Send Action event to controls
await activeForm.ActionControls(mr);
//Send Action event to form itself
await activeForm.Action(mr);
if (!mr.Handled)
{
//Send Action event to controls
await activeForm.ActionControls(mr);
var uhc = new UnhandledCallEventArgs(ur.Message.Text, mr.RawData, session.DeviceId, mr.MessageId,
ur.Message, session);
OnUnhandledCall(uhc);
//Send Action event to form itself
await activeForm.Action(mr);
if (!mr.Handled)
if (uhc.Handled)
{
var uhc = new UnhandledCallEventArgs(ur.Message.Text, mr.RawData, session.DeviceId, mr.MessageId, ur.Message, session);
OnUnhandledCall(uhc);
if (uhc.Handled)
mr.Handled = true;
if (!session.FormSwitched)
{
mr.Handled = true;
if (!session.FormSwitched)
{
return;
}
return;
}
}
}
if (!session.FormSwitched)
{
//Render Event
await activeForm.RenderControls(mr);
await activeForm.Render(mr);
}
}
/// <summary>
/// Will be called if no form handeled this call
/// </summary>
public event EventHandler<UnhandledCallEventArgs> UnhandledCall
if (!session.FormSwitched)
{
add => _events.AddHandler(EvUnhandledCall, value);
remove => _events.RemoveHandler(EvUnhandledCall, value);
}
public void OnUnhandledCall(UnhandledCallEventArgs e)
{
(_events[EvUnhandledCall] as EventHandler<UnhandledCallEventArgs>)?.Invoke(this, e);
//Render Event
await activeForm.RenderControls(mr);
await activeForm.Render(mr);
}
}
}
/// <summary>
/// Will be called if no form handeled this call
/// </summary>
public event EventHandler<UnhandledCallEventArgs> UnhandledCall
{
add => _events.AddHandler(EvUnhandledCall, value);
remove => _events.RemoveHandler(EvUnhandledCall, value);
}
public void OnUnhandledCall(UnhandledCallEventArgs e)
{
(_events[EvUnhandledCall] as EventHandler<UnhandledCallEventArgs>)?.Invoke(this, e);
}
}
@@ -6,44 +6,41 @@ using TelegramBotBase.Base;
using TelegramBotBase.Interfaces;
using TelegramBotBase.Sessions;
namespace TelegramBotBase.MessageLoops
namespace TelegramBotBase.MessageLoops;
/// <summary>
/// This is a minimal message loop which will react to all update types and just calling the Load method.
/// </summary>
public class MinimalMessageLoop : IMessageLoopFactory
{
/// <summary>
/// This is a minimal message loop which will react to all update types and just calling the Load method.
/// </summary>
public class MinimalMessageLoop : IMessageLoopFactory
private static readonly object EvUnhandledCall = new();
private readonly EventHandlerList _events = new();
public async Task MessageLoop(BotBase bot, DeviceSession session, UpdateResult ur, MessageResult mr)
{
private static readonly object EvUnhandledCall = new object();
private readonly EventHandlerList _events = new EventHandlerList();
public async Task MessageLoop(BotBase bot, DeviceSession session, UpdateResult ur, MessageResult mr)
{
var update = ur.RawData;
var update = ur.RawData;
mr.Device = session;
mr.Device = session;
var activeForm = session.ActiveForm;
var activeForm = session.ActiveForm;
//Loading Event
await activeForm.Load(mr);
}
/// <summary>
/// Will be called if no form handeled this call
/// </summary>
public event EventHandler<UnhandledCallEventArgs> UnhandledCall
{
add => _events.AddHandler(EvUnhandledCall, value);
remove => _events.RemoveHandler(EvUnhandledCall, value);
}
public void OnUnhandledCall(UnhandledCallEventArgs e)
{
(_events[EvUnhandledCall] as EventHandler<UnhandledCallEventArgs>)?.Invoke(this, e);
}
//Loading Event
await activeForm.Load(mr);
}
}
/// <summary>
/// Will be called if no form handeled this call
/// </summary>
public event EventHandler<UnhandledCallEventArgs> UnhandledCall
{
add => _events.AddHandler(EvUnhandledCall, value);
remove => _events.RemoveHandler(EvUnhandledCall, value);
}
public void OnUnhandledCall(UnhandledCallEventArgs e)
{
(_events[EvUnhandledCall] as EventHandler<UnhandledCallEventArgs>)?.Invoke(this, e);
}
}
+1 -1
View File
@@ -32,4 +32,4 @@ using System.Runtime.InteropServices;
// übernehmen, indem Sie "*" eingeben:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("3.1.0.0")]
[assembly: AssemblyFileVersion("3.1.0.0")]
[assembly: AssemblyFileVersion("3.1.0.0")]
+275 -267
View File
@@ -11,330 +11,338 @@ using TelegramBotBase.Interfaces;
using TelegramBotBase.Sessions;
using TelegramBotBase.Tools;
namespace TelegramBotBase
namespace TelegramBotBase;
/// <summary>
/// Base class for managing all active sessions
/// </summary>
public class SessionBase
{
/// <summary>
/// Base class for managing all active sessions
/// </summary>
public class SessionBase
public SessionBase()
{
/// <summary>
/// The Basic message client.
/// </summary>
public MessageClient Client { get; set; }
SessionList = new Dictionary<long, DeviceSession>();
}
/// <summary>
/// A list of all active sessions.
/// </summary>
public Dictionary<long, DeviceSession> SessionList { get; set; }
/// <summary>
/// The Basic message client.
/// </summary>
public MessageClient Client { get; set; }
/// <summary>
/// A list of all active sessions.
/// </summary>
public Dictionary<long, DeviceSession> SessionList { get; set; }
/// <summary>
/// Reference to the Main BotBase instance for later use.
/// </summary>
public BotBase BotBase { get; set; }
/// <summary>
/// Reference to the Main BotBase instance for later use.
/// </summary>
public BotBase BotBase { get; set; }
/// <summary>
/// Get device session from Device/ChatId
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public DeviceSession this[long key]
{
get => SessionList[key];
set => SessionList[key] = value;
}
public SessionBase()
/// <summary>
/// Get device session from Device/ChatId
/// </summary>
/// <param name="deviceId"></param>
/// <returns></returns>
public DeviceSession GetSession(long deviceId)
{
var ds = SessionList.FirstOrDefault(a => a.Key == deviceId).Value ?? null;
return ds;
}
/// <summary>
/// Start a new session
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="deviceId"></param>
/// <returns></returns>
public async Task<DeviceSession> StartSession(long deviceId)
{
var start = BotBase.StartFormFactory.CreateForm();
start.Client = Client;
var ds = new DeviceSession(deviceId, start);
start.Device = ds;
await start.OnInit(new InitEventArgs());
await start.OnOpened(EventArgs.Empty);
this[deviceId] = ds;
return ds;
}
/// <summary>
/// End session
/// </summary>
/// <param name="deviceId"></param>
public void EndSession(long deviceId)
{
var d = this[deviceId];
if (d != null)
{
SessionList = new Dictionary<long, DeviceSession>();
SessionList.Remove(deviceId);
}
}
/// <summary>
/// Returns all active User Sessions.
/// </summary>
/// <returns></returns>
public List<DeviceSession> GetUserSessions()
{
return SessionList.Where(a => a.Key > 0).Select(a => a.Value).ToList();
}
/// <summary>
/// Returns all active Group Sessions.
/// </summary>
/// <returns></returns>
public List<DeviceSession> GetGroupSessions()
{
return SessionList.Where(a => a.Key < 0).Select(a => a.Value).ToList();
}
/// <summary>
/// Loads the previously saved states from the machine.
/// </summary>
public async void LoadSessionStates()
{
if (BotBase.StateMachine == null)
{
return;
}
/// <summary>
/// Get device session from Device/ChatId
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public DeviceSession this[long key]
LoadSessionStates(BotBase.StateMachine);
}
/// <summary>
/// Loads the previously saved states from the machine.
/// </summary>
public async void LoadSessionStates(IStateMachine statemachine)
{
if (statemachine == null)
{
get => SessionList[key];
set => SessionList[key] = value;
throw new ArgumentNullException("StateMachine",
"No StateMachine defined. Please set one to property BotBase.StateMachine");
}
/// <summary>
/// Get device session from Device/ChatId
/// </summary>
/// <param name="deviceId"></param>
/// <returns></returns>
public DeviceSession GetSession(long deviceId)
var container = statemachine.LoadFormStates();
foreach (var s in container.States)
{
var ds = SessionList.FirstOrDefault(a => a.Key == deviceId).Value ?? null;
return ds;
}
/// <summary>
/// Start a new session
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="deviceId"></param>
/// <returns></returns>
public async Task<DeviceSession> StartSession(long deviceId)
{
var start = BotBase.StartFormFactory.CreateForm();
start.Client = Client;
var ds = new DeviceSession(deviceId, start);
start.Device = ds;
await start.OnInit(new InitEventArgs());
await start.OnOpened(EventArgs.Empty);
this[deviceId] = ds;
return ds;
}
/// <summary>
/// End session
/// </summary>
/// <param name="deviceId"></param>
public void EndSession(long deviceId)
{
var d = this[deviceId];
if (d != null)
var t = Type.GetType(s.QualifiedName);
if (t == null || !t.IsSubclassOf(typeof(FormBase)))
{
SessionList.Remove(deviceId);
}
}
/// <summary>
/// Returns all active User Sessions.
/// </summary>
/// <returns></returns>
public List<DeviceSession> GetUserSessions()
{
return SessionList.Where(a => a.Key > 0).Select(a => a.Value).ToList();
}
/// <summary>
/// Returns all active Group Sessions.
/// </summary>
/// <returns></returns>
public List<DeviceSession> GetGroupSessions()
{
return SessionList.Where(a => a.Key < 0).Select(a => a.Value).ToList();
}
/// <summary>
/// Loads the previously saved states from the machine.
/// </summary>
public async void LoadSessionStates()
{
if (BotBase.StateMachine == null)
{
return;
continue;
}
LoadSessionStates(BotBase.StateMachine);
}
/// <summary>
/// Loads the previously saved states from the machine.
/// </summary>
public async void LoadSessionStates(IStateMachine statemachine)
{
if (statemachine == null)
//Key already existing
if (SessionList.ContainsKey(s.DeviceId))
{
throw new ArgumentNullException("StateMachine", "No StateMachine defined. Please set one to property BotBase.StateMachine");
continue;
}
var container = statemachine.LoadFormStates();
foreach (var s in container.States)
//No default constructor, fallback
if (!(t.GetConstructor(new Type[] { })?.Invoke(new object[] { }) is FormBase form))
{
var t = Type.GetType(s.QualifiedName);
if (t == null || !t.IsSubclassOf(typeof(FormBase)))
if (!statemachine.FallbackStateForm.IsSubclassOf(typeof(FormBase)))
{
continue;
}
//Key already existing
if (SessionList.ContainsKey(s.DeviceId))
form =
statemachine.FallbackStateForm.GetConstructor(new Type[] { })
?.Invoke(new object[] { }) as FormBase;
//Fallback failed, due missing default constructor
if (form == null)
{
continue;
//No default constructor, fallback
if (!(t.GetConstructor(new Type[] { })?.Invoke(new object[] { }) is FormBase form))
{
if (!statemachine.FallbackStateForm.IsSubclassOf(typeof(FormBase)))
continue;
form = statemachine.FallbackStateForm.GetConstructor(new Type[] { })?.Invoke(new object[] { }) as FormBase;
//Fallback failed, due missing default constructor
if (form == null)
continue;
}
}
if (s.Values != null && s.Values.Count > 0)
if (s.Values != null && s.Values.Count > 0)
{
var properties = s.Values.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 properties = s.Values.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)
{
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;
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
{
}
f.SetValue(form, p.Value);
}
catch (ArgumentException)
{
Conversion.CustomConversionChecks(form, p, f);
}
catch
{
}
}
}
form.Client = Client;
var device = new DeviceSession(s.DeviceId, form)
form.Client = Client;
var device = new DeviceSession(s.DeviceId, form)
{
ChatTitle = s.ChatTitle
};
SessionList.Add(s.DeviceId, device);
//Is Subclass of IStateForm
if (form is IStateForm iform)
{
var ls = new LoadStateEventArgs
{
ChatTitle = s.ChatTitle
Values = s.Values
};
iform.LoadState(ls);
}
try
{
await form.OnInit(new InitEventArgs());
await form.OnOpened(EventArgs.Empty);
}
catch
{
//Skip on exception
SessionList.Remove(s.DeviceId);
}
}
}
/// <summary>
/// Saves all open states into the machine.
/// </summary>
public void SaveSessionStates(IStateMachine statemachine)
{
if (statemachine == null)
{
throw new ArgumentNullException("StateMachine",
"No StateMachine defined. Please set one to property BotBase.StateMachine");
}
var states = new List<StateEntry>();
foreach (var s in SessionList)
{
if (s.Value == null)
{
continue;
}
var form = s.Value.ActiveForm;
try
{
var se = new StateEntry
{
DeviceId = s.Key,
ChatTitle = s.Value.GetChatTitle(),
FormUri = form.GetType().FullName,
QualifiedName = form.GetType().AssemblyQualifiedName
};
SessionList.Add(s.DeviceId, device);
//Skip classes where IgnoreState attribute is existing
if (form.GetType().GetCustomAttributes(typeof(IgnoreState), true).Length != 0)
{
//Skip this form, when there is no fallback state form
if (statemachine.FallbackStateForm == null)
{
continue;
}
//Replace form by default State one.
se.FormUri = statemachine.FallbackStateForm.FullName;
se.QualifiedName = statemachine.FallbackStateForm.AssemblyQualifiedName;
}
//Is Subclass of IStateForm
if (form is IStateForm iform)
{
var ls = new LoadStateEventArgs
{
Values = s.Values
};
iform.LoadState(ls);
//Loading Session states
var ssea = new SaveStateEventArgs();
iform.SaveState(ssea);
se.Values = ssea.Values;
}
try
{
await form.OnInit(new InitEventArgs());
//Search for public properties with SaveState attribute
var fields = form.GetType()
.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
.Where(a => a.GetCustomAttributes(typeof(SaveState), true).Length != 0).ToList();
await form.OnOpened(EventArgs.Empty);
}
catch
foreach (var f in fields)
{
//Skip on exception
SessionList.Remove(s.DeviceId);
var val = f.GetValue(form);
se.Values.Add("$" + f.Name, val);
}
states.Add(se);
}
catch
{
//Continue on error (skip this form)
}
}
/// <summary>
/// Saves all open states into the machine.
/// </summary>
public void SaveSessionStates(IStateMachine statemachine)
var sc = new StateContainer
{
if (statemachine == null)
{
throw new ArgumentNullException("StateMachine", "No StateMachine defined. Please set one to property BotBase.StateMachine");
}
States = states
};
var states = new List<StateEntry>();
statemachine.SaveFormStates(new SaveStatesEventArgs(sc));
}
foreach (var s in SessionList)
{
if (s.Value == null)
{
continue;
}
var form = s.Value.ActiveForm;
try
{
var se = new StateEntry
{
DeviceId = s.Key,
ChatTitle = s.Value.GetChatTitle(),
FormUri = form.GetType().FullName,
QualifiedName = form.GetType().AssemblyQualifiedName
};
//Skip classes where IgnoreState attribute is existing
if (form.GetType().GetCustomAttributes(typeof(IgnoreState), true).Length != 0)
{
//Skip this form, when there is no fallback state form
if (statemachine.FallbackStateForm == null)
{
continue;
}
//Replace form by default State one.
se.FormUri = statemachine.FallbackStateForm.FullName;
se.QualifiedName = statemachine.FallbackStateForm.AssemblyQualifiedName;
}
//Is Subclass of IStateForm
if (form is IStateForm iform)
{
//Loading Session states
var ssea = new SaveStateEventArgs();
iform.SaveState(ssea);
se.Values = ssea.Values;
}
//Search for public properties with SaveState attribute
var fields = form.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).Where(a => a.GetCustomAttributes(typeof(SaveState), true).Length != 0).ToList();
foreach (var f in fields)
{
var val = f.GetValue(form);
se.Values.Add("$" + f.Name, val);
}
states.Add(se);
}
catch
{
//Continue on error (skip this form)
}
}
var sc = new StateContainer
{
States = states
};
statemachine.SaveFormStates(new SaveStatesEventArgs(sc));
}
/// <summary>
/// Saves all open states into the machine.
/// </summary>
public void SaveSessionStates()
/// <summary>
/// Saves all open states into the machine.
/// </summary>
public void SaveSessionStates()
{
if (BotBase.StateMachine == null)
{
if (BotBase.StateMachine == null)
return;
SaveSessionStates(BotBase.StateMachine);
return;
}
SaveSessionStates(BotBase.StateMachine);
}
}
File diff suppressed because it is too large Load Diff
+63 -63
View File
@@ -6,87 +6,87 @@ using TelegramBotBase.Base;
using TelegramBotBase.Form;
using TelegramBotBase.Interfaces;
namespace TelegramBotBase.States
namespace TelegramBotBase.States;
/// <summary>
/// Is used for all complex data types. Use if other default machines are not working.
/// </summary>
public class JsonStateMachine : IStateMachine
{
/// <summary>
/// Is used for all complex data types. Use if other default machines are not working.
/// Will initialize the state machine.
/// </summary>
public class JsonStateMachine : IStateMachine
/// <param name="file">Path of the file and name where to save the session details.</param>
/// <param name="fallbackStateForm">
/// Type of Form which will be saved instead of Form which has
/// <seealso cref="Attributes.IgnoreState" /> attribute declared. Needs to be subclass of
/// <seealso cref="Form.FormBase" />.
/// </param>
/// <param name="overwrite">Declares of the file could be overwritten.</param>
public JsonStateMachine(string file, Type fallbackStateForm = null, bool overwrite = true)
{
public string FilePath { get; set; }
FallbackStateForm = fallbackStateForm;
public bool Overwrite { get; set; }
public Type FallbackStateForm { get; private set; }
/// <summary>
/// Will initialize the state machine.
/// </summary>
/// <param name="file">Path of the file and name where to save the session details.</param>
/// <param name="fallbackStateForm">Type of Form which will be saved instead of Form which has <seealso cref="Attributes.IgnoreState"/> attribute declared. Needs to be subclass of <seealso cref="Form.FormBase"/>.</param>
/// <param name="overwrite">Declares of the file could be overwritten.</param>
public JsonStateMachine(string file, Type fallbackStateForm = null, bool overwrite = true)
if (FallbackStateForm != null && !FallbackStateForm.IsSubclassOf(typeof(FormBase)))
{
FallbackStateForm = fallbackStateForm;
if (FallbackStateForm != null && !FallbackStateForm.IsSubclassOf(typeof(FormBase)))
{
throw new ArgumentException("FallbackStateForm is not a subclass of FormBase");
}
FilePath = file ?? throw new ArgumentNullException(nameof(file));
Overwrite = overwrite;
throw new ArgumentException("FallbackStateForm is not a subclass of FormBase");
}
public StateContainer LoadFormStates()
FilePath = file ?? throw new ArgumentNullException(nameof(file));
Overwrite = overwrite;
}
public string FilePath { get; set; }
public bool Overwrite { get; set; }
public Type FallbackStateForm { get; }
public StateContainer LoadFormStates()
{
try
{
try
var content = File.ReadAllText(FilePath);
var sc = JsonConvert.DeserializeObject<StateContainer>(content, new JsonSerializerSettings
{
var content = File.ReadAllText(FilePath);
TypeNameHandling = TypeNameHandling.All,
TypeNameAssemblyFormatHandling = TypeNameAssemblyFormatHandling.Simple
});
var sc = JsonConvert.DeserializeObject<StateContainer>(content, new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.All,
TypeNameAssemblyFormatHandling = TypeNameAssemblyFormatHandling.Simple
});
return sc;
}
catch
{
}
return new StateContainer();
return sc;
}
catch
{
}
public void SaveFormStates(SaveStatesEventArgs e)
return new StateContainer();
}
public void SaveFormStates(SaveStatesEventArgs e)
{
if (File.Exists(FilePath))
{
if (File.Exists(FilePath))
if (!Overwrite)
{
if (!Overwrite)
{
throw new Exception("File exists already.");
}
File.Delete(FilePath);
throw new Exception("File exists already.");
}
try
File.Delete(FilePath);
}
try
{
var content = JsonConvert.SerializeObject(e.States, Formatting.Indented, new JsonSerializerSettings
{
var content = JsonConvert.SerializeObject(e.States, Formatting.Indented, new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.All,
TypeNameAssemblyFormatHandling = TypeNameAssemblyFormatHandling.Simple
});
File.WriteAllText(FilePath, content);
}
catch
{
}
TypeNameHandling = TypeNameHandling.All,
TypeNameAssemblyFormatHandling = TypeNameAssemblyFormatHandling.Simple
});
File.WriteAllText(FilePath, content);
}
catch
{
}
}
}
}

Some files were not shown because too many files have changed in this diff Show More