Compare commits

...
28 Commits
Author SHA1 Message Date
FlorianDahn 07dec0dc9d Small fixes and clarifications 2020-05-04 02:20:16 +02:00
FlorianDahn 2fc16b18da DefaultStateForm
- adding DefaultStateForm to redirect Sessions after reloading Sessions to a different Form instead of "restart" the session (i.e. on an in Bot wizard you just want to let the user continue on the main menu)
- adding to SaveSessions to save the "main menu" instead of skipping the ignored form
2020-05-04 02:12:50 +02:00
FlorianDahn 11eeb0dcab Merge branch 'master' of https://github.com/MajMcCloud/TelegramBotFramework 2020-04-29 22:11:29 +02:00
FlorianDahn fbfcbe99fb Update SystemCommandsBot.csproj
adding NugetPackage instead of direct Project reference
2020-04-29 22:11:26 +02:00
Florian Dahn 948c1368d7 Update README.md 2020-04-28 20:18:24 +02:00
FlorianDahn d66df4978d Create Program.cs 2020-04-28 20:17:18 +02:00
FlorianDahn 7d8895850a Example for a Join/Leave Messages delete Bot 2020-04-28 20:10:18 +02:00
FlorianDahn 5e87d00c58 Localization updates
- small localization updates
- correction in enumration from safe to save
2020-04-21 21:50:59 +02:00
FlorianDahn efe5fef7d0 Adding setting for automated session serialization on shutdown
- adding automated session serialization on application ending (console or shutdown/logoff)
- adding console class for managing system events
2020-04-21 21:45:21 +02:00
FlorianDahn eebb5cb6f5 Create eSettings.cs
- adding eSettings enumeration
2020-04-21 21:44:01 +02:00
FlorianDahn 992065d289 Update BotBase.cs
- improving system settings for bot
2020-04-21 21:43:25 +02:00
FlorianDahn 1acfeb873f Buttongrid Paging support and Localization updates
- adding Paging functionality to ButtonGrid
- adding localization for ButtonGrid
2020-04-21 21:40:47 +02:00
Florian Dahn de35ebb66e Update README.md 2020-04-18 13:16:53 +02:00
FlorianDahn 2c3e707673 Session serialization update
- updating Session serialization for using new method GetChatTitle() to get the chat title for debugging purposes
- removing unncessary use of getting the chat title at every call (improving performance)
2020-04-15 20:30:42 +02:00
FlorianDahn 6d39d2fb73 Merge branch 'master' of https://github.com/MajMcCloud/TelegramBotFramework 2020-04-15 20:27:54 +02:00
FlorianDahn ef667d12d8 Update DeviceSession.cs
- adding EditReplyMarkup to Device methods
- adding GetChatTitle method to get easier the name of a chat/group/channel
- some cleanup
2020-04-15 20:27:46 +02:00
FlorianDahn d24f8e7ac5 Localizations
- adding localizations in english and german for default controls
- updating default controls
2020-04-15 20:26:39 +02:00
FlorianDahn e5dc3fd2bc Update ButtonForm.cs
- adding a Duplicate function
- improving Count property
- adding InsertButtonRow methods
- adding a dictionary property to get all buttons in a specific row
2020-04-15 20:23:39 +02:00
Florian Dahn 6ebb96bd93 Update README.md 2020-04-15 20:20:57 +02:00
FlorianDahn a1b3b9246b Update BotBase.cs
fixing new command handling
2020-04-11 19:42:24 +02:00
FlorianDahn 25a0ff2e3e Update BotCommandEventArgs.cs
updating documentation
2020-04-11 19:41:56 +02:00
FlorianDahn b74233d66d Update BotBase.cs
Small fix
2020-04-11 19:24:31 +02:00
FlorianDahn fb7b624ad8 BotCommands and refactoring
- refactoring SystemCalls to BotCommand (due new Telegram Bot update)
- Adding UploadBotCommands method to BotBase
- refactoring and simplifying BotBase constructors
- adding getBotCommands and SetBotCommands to MessageClient
2020-04-11 19:12:34 +02:00
Florian Dahn 965e6e18b9 Update README.md 2020-04-10 10:24:29 +02:00
Florian Dahn b1c518cbd3 Update README.md 2020-04-10 10:16:25 +02:00
Florian Dahn ab0ae5cda4 Update README.md 2020-04-10 10:11:20 +02:00
FlorianDahn b850330f65 update readme 2020-04-10 10:09:03 +02:00
FlorianDahn 317dad7d24 - adding state machine documentation 2020-04-10 10:08:02 +02:00
30 changed files with 1072 additions and 188 deletions
+12
View File
@@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="TelegramBotBase" Version="3.0.0" />
</ItemGroup>
</Project>
+19
View File
@@ -0,0 +1,19 @@
using System;
namespace JoinHiderBot
{
class Program
{
static void Main(string[] args)
{
String apiKey = "";
var bot = new TelegramBotBase.BotBase<forms.Start>(apiKey);
bot.Start();
Console.ReadLine();
}
}
}
@@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using TelegramBotBase.Args;
using TelegramBotBase.Form;
namespace JoinHiderBot.forms
{
public class GroupManageForm : GroupForm
{
public override async Task OnMemberChanges(MemberChangeEventArgs e)
{
if (e.Type != Telegram.Bot.Types.Enums.MessageType.ChatMembersAdded && e.Type != Telegram.Bot.Types.Enums.MessageType.ChatMemberLeft)
return;
var m = e.Result.Message;
await this.Device.DeleteMessage(m);
}
}
}
+29
View File
@@ -0,0 +1,29 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using TelegramBotBase.Base;
using TelegramBotBase.Form;
namespace JoinHiderBot.forms
{
public class Start : SplitterForm
{
public override async Task<bool> Open(MessageResult e)
{
await this.Device.Send("This bot works only in groups.");
return true;
}
public override async Task<bool> OpenGroup(MessageResult e)
{
var gmf = new GroupManageForm();
await this.NavigateTo(gmf);
return true;
}
}
}
+36
View File
@@ -0,0 +1,36 @@
using System;
namespace SystemCommandsBot
{
class Program
{
public static config.Config BotConfig { get; set; }
static void Main(string[] args)
{
BotConfig = config.Config.load();
if (BotConfig.ApiKey == null || BotConfig.ApiKey.Trim() == "")
{
Console.WriteLine("Config created...");
Console.ReadLine();
return;
}
var bot = new TelegramBotBase.BotBase<forms.StartForm>(BotConfig.ApiKey);
bot.Start();
Console.WriteLine("Bot started");
Console.ReadLine();
bot.Stop();
}
}
}
@@ -7,10 +7,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" /> <PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
</ItemGroup> <PackageReference Include="TelegramBotBase" Version="3.0.0" />
<ItemGroup>
<ProjectReference Include="..\..\TelegramBotBase\TelegramBotBase.csproj" />
</ItemGroup> </ItemGroup>
</Project> </Project>
+176 -3
View File
@@ -74,12 +74,31 @@ Thanks !
- [Groups](#groups) - [Groups](#groups)
* [SplitterForm](#splitter-form) * [SplitterForm](#splitter-form)
* [GroupForm](#group-form) * [GroupForm](#group-form)
- [State Machine and Session Serialization](#statemachine) - [State Machine and Session Serialization (v3.0.0)](#statemachine-and-sessions)
* In the making, is comming soon. * [StateMachines](#statemachines)
* [SimpleJSONStateMachine](#simplejsonstatemachine)
* [JSONStateMachine](#jsonstatemachine)
* [XMLStateMachine](#xmlstatemachine)
* [Interfaces](#interfaces)
* [IStateMachine](#istatemachine)
* [IStateForm](#istateform)
* [Attributes](#attributes)
* [SaveState](#savestate)
* [IgnoreState](#ignorestate)
- [Examples](#examples)
--- ---
@@ -152,6 +171,12 @@ public class StartForm : FormBase
{ {
await this.Device.Send("Hello world!"); await this.Device.Send("Hello world!");
} }
//Gets invoked on edited messages
public override async Task Edited(MessageResult message)
{
}
//Gets invoked on Button clicks //Gets invoked on Button clicks
public override async Task Action(MessageResult message) public override async Task Action(MessageResult message)
@@ -891,7 +916,155 @@ public class GroupForm : FormBase
``` ```
## Statemachine and Sessions
Depending on the usecases and the overall structure of a Telegram Bot it is essential to have some kind of session serialization or state machine to keep the user context after restarts of the bot (ie. due to updates) or crashes.
For this I have created some easy to implement structures which fits into the current environment.
Below you find all possiblities.
### Statemachines
There are actually 3 types of example state machines you could use. A state machine is a kind of serializer which saves the important session data in a reusable structure like JSON or XML.
You could use one of the following state machines:
#### SimpleJSONStateMachine
Is easy to use and useful for simple structures like basic datatypes. Did not work for complex ones like generics. Use the JSONStateMachine for them.
In general you didn't need to do more then, to keep the actual form:
```
//Prepare the System
BotBase<StartForm> bb = new BotBase<StartForm>("{YOUR API KEY}");
//Add Systemcommands if you like, you could catch them later
bb.SystemCalls.Add("/start");
//Set the statemachine and enable it
bb.StateMachine = new TelegramBotBase.States.SimpleJSONStateMachine(AppContext.BaseDirectory + "config\\states.json");
//Start your Bot
bb.Start();
```
#### JSONStateMachine
Is easy to use too, but works for complex datatypes cause it saves there namespaces and additional type informations into the JSON file too.
In general you didn't need to do more then, to keep the actual form:
```
//Prepare the System
BotBase<StartForm> bb = new BotBase<StartForm>("{YOUR API KEY}");
//Add Systemcommands if you like, you could catch them later
bb.SystemCalls.Add("/start");
//Set the statemachine and enable it
bb.StateMachine = new TelegramBotBase.States.JSONStateMachine(AppContext.BaseDirectory + "config\\states.json");
//Start your Bot
bb.Start();
```
#### XMLStateMachine
The last one, should work like the others.
In general you didn't need to do more then, to keep the actual form:
```
//Prepare the System
BotBase<StartForm> bb = new BotBase<StartForm>("{YOUR API KEY}");
//Add Systemcommands if you like, you could catch them later
bb.SystemCalls.Add("/start");
//Set the statemachine and enable it
bb.StateMachine = new TelegramBotBase.States.XMLStateMachine(AppContext.BaseDirectory + "config\\states.xml");
//Start your Bot
bb.Start();
```
### Interfaces
There are two interfaces, one for the StateMachine itself, which is useful to build a custom one for a different datatype and one for implementing into a form which should be invoked with events.
#### IStateMachine
Is the basic StateMachine interface, it has two methods SaveFormStates(SaveStatesEventArgs e) and StateContainer LoadFormStates(), nothing fancy, just simple calls. Implement into both methods your own serialization process.
```
public interface IStateMachine
{
void SaveFormStates(SaveStatesEventArgs e);
StateContainer LoadFormStates();
}
```
#### IStateForm
When implemented, this will invoke one of these two methods: LoadState(LoadStateEventArgs e) or SaveState(SaveStateEventArgs e).
They have methods to load or save data from the statemachine of the current form.
```
public interface IStateForm
{
void LoadState(LoadStateEventArgs e);
void SaveState(SaveStateEventArgs e);
}
```
### Attributes
If you don't want to implement the IStateForm interface, cause there are maybe "just" one or two properties you want to keep and restore, use the following attribute:
#### SaveState
This will let the engine know, that you want too keep and restore this field automatically. Unlike the IStateForm methods, you have no option to manipulate data.
```
[SaveState]
public long UserId { get; set; }
```
#### IgnoreState
Due to the fact that Attribute implementation and interaction is optional, you want to let the engine maybe know, that you don't want to keep a specific form. So it should get "lost". This attribute will help you here, add it to the form class and it will not get serialized, even if it implements IStateForm or the SaveState attributes.
```
[IgnoreState]
public class Registration : STForm
{
}
```
## Examples
You find all example projects in the "Examples" subfolder.
- [Examples/SystemCommandsBot/](Examples/SystemCommandsBot/)
Will allow you to run specific system commands or run/kill processes via Bot. Has also a simple authentication mechanism with one password.
- [Examples/JoinHiderBot/](Examples/JoinHiderBot/)
Will delete Join and Leave messages automatically in groups.
--- ---
@@ -9,9 +9,9 @@ using TelegramBotBase.Sessions;
namespace TelegramBotBase.Args namespace TelegramBotBase.Args
{ {
/// <summary> /// <summary>
/// Base class for given system call results /// Base class for given bot command results
/// </summary> /// </summary>
public class SystemCallEventArgs : EventArgs public class BotCommandEventArgs : EventArgs
{ {
public String Command { get; set; } public String Command { get; set; }
@@ -26,13 +26,13 @@ namespace TelegramBotBase.Args
public Message OriginalMessage { get; set; } public Message OriginalMessage { get; set; }
public SystemCallEventArgs() public BotCommandEventArgs()
{ {
} }
public SystemCallEventArgs(String Command, List<String> Parameters, Message Message, long DeviceId, DeviceSession Device) public BotCommandEventArgs(String Command, List<String> Parameters, Message Message, long DeviceId, DeviceSession Device)
{ {
this.Command = Command; this.Command = Command;
this.Parameters = Parameters; this.Parameters = Parameters;
+24 -1
View File
@@ -6,6 +6,7 @@ using System.Net;
using System.Net.Http; using System.Net.Http;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Telegram.Bot.Types;
namespace TelegramBotBase.Base namespace TelegramBotBase.Base
{ {
@@ -15,6 +16,7 @@ namespace TelegramBotBase.Base
public class MessageClient public class MessageClient
{ {
public String APIKey { get; set; } public String APIKey { get; set; }
public Telegram.Bot.TelegramBotClient TelegramClient { get; set; } public Telegram.Bot.TelegramBotClient TelegramClient { get; set; }
@@ -40,7 +42,7 @@ namespace TelegramBotBase.Base
{ {
this.APIKey = APIKey; this.APIKey = APIKey;
this.TelegramClient = new Telegram.Bot.TelegramBotClient(APIKey, proxy); this.TelegramClient = new Telegram.Bot.TelegramBotClient(APIKey, proxy);
Prepare(); Prepare();
} }
@@ -142,6 +144,27 @@ namespace TelegramBotBase.Base
} }
} }
/// <summary>
/// This will return the current list of bot commands.
/// </summary>
/// <returns></returns>
public async Task<BotCommand[]> GetBotCommands()
{
return await this.TelegramClient.GetMyCommandsAsync();
}
/// <summary>
/// This will set your bot commands to the given list.
/// </summary>
/// <param name="botcommands"></param>
/// <returns></returns>
public async Task SetBotCommands(List<BotCommand> botcommands)
{
await this.TelegramClient.SetMyCommandsAsync(botcommands);
}
#region "Events" #region "Events"
+7 -7
View File
@@ -74,9 +74,9 @@ namespace TelegramBotBase.Base
} }
/// <summary> /// <summary>
/// Is this a system call ? Starts with a slash '/' and a command /// Is this a command ? Starts with a slash '/' and a command
/// </summary> /// </summary>
public bool IsSystemCall public bool IsBotCommand
{ {
get get
{ {
@@ -87,11 +87,11 @@ namespace TelegramBotBase.Base
/// <summary> /// <summary>
/// Returns a List of all parameters which has been sent with the command itself (i.e. /start 123 456 789 => 123,456,789) /// Returns a List of all parameters which has been sent with the command itself (i.e. /start 123 456 789 => 123,456,789)
/// </summary> /// </summary>
public List<String> SystemCallParameters public List<String> BotCommandParameters
{ {
get get
{ {
if (!IsSystemCall) if (!IsBotCommand)
return new List<string>(); return new List<string>();
//Split by empty space and skip first entry (command itself), return as list //Split by empty space and skip first entry (command itself), return as list
@@ -100,13 +100,13 @@ namespace TelegramBotBase.Base
} }
/// <summary> /// <summary>
/// Returns just the System call command (i.e. /start 1 2 3 => /start) /// Returns just the command (i.e. /start 1 2 3 => /start)
/// </summary> /// </summary>
public String SystemCommand public String BotCommand
{ {
get get
{ {
if (!IsSystemCall) if (!IsBotCommand)
return null; return null;
return this.MessageText.Split(' ')[0]; return this.MessageText.Split(' ')[0];
+118 -62
View File
@@ -5,9 +5,11 @@ using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Telegram.Bot; using Telegram.Bot;
using Telegram.Bot.Types;
using TelegramBotBase.Args; using TelegramBotBase.Args;
using TelegramBotBase.Attributes; using TelegramBotBase.Attributes;
using TelegramBotBase.Base; using TelegramBotBase.Base;
using TelegramBotBase.Enums;
using TelegramBotBase.Form; using TelegramBotBase.Form;
using TelegramBotBase.Interfaces; using TelegramBotBase.Interfaces;
using TelegramBotBase.Sessions; using TelegramBotBase.Sessions;
@@ -36,7 +38,10 @@ namespace TelegramBotBase
/// <summary> /// <summary>
/// Contains System commands which will be available at everytime and didnt get passed to forms, i.e. /start /// Contains System commands which will be available at everytime and didnt get passed to forms, i.e. /start
/// </summary> /// </summary>
public List<String> SystemCalls { get; set; } public List<BotCommand> BotCommands { get; set; }
#region "Events"
private EventHandlerList __Events = new EventHandlerList(); private EventHandlerList __Events = new EventHandlerList();
@@ -46,47 +51,51 @@ namespace TelegramBotBase
private static object __evSystemCall = new object(); private static object __evSystemCall = new object();
public delegate Task SystemCallEventHandler(object sender, SystemCallEventArgs e); public delegate Task BotCommandEventHandler(object sender, BotCommandEventArgs e);
private static object __evException = new object(); private static object __evException = new object();
private static object __evUnhandledCall = new object(); private static object __evUnhandledCall = new object();
#endregion
/// <summary>
/// Skips all messages during running (good for big delay updates)
/// </summary>
public bool SkipAllMessages { get; set; } = false;
/// <summary>
/// Loggs all messages and sent them to the event handler
/// </summary>
public bool LogAllMessages { get; set; } = false;
/// <summary> /// <summary>
/// Enable the SessionState (you need to implement on call forms the IStateForm interface) /// Enable the SessionState (you need to implement on call forms the IStateForm interface)
/// </summary> /// </summary>
public IStateMachine StateMachine { get; set; } public IStateMachine StateMachine { get; set; }
/// <summary>
/// How often could a form navigate to another (within one user action/call/message) public Dictionary<eSettings, uint> SystemSettings { get; private set; }
/// </summary>
private const int NavigationMaximum = 10; private BotBase()
{
this.SystemSettings = new Dictionary<eSettings, uint>();
SetSetting(eSettings.NavigationMaximum, 10);
SetSetting(eSettings.LogAllMessages, false);
SetSetting(eSettings.SkipAllMessages, false);
SetSetting(eSettings.SaveSessionsOnConsoleExit, false);
this.BotCommands = new List<BotCommand>();
this.Sessions = new SessionBase();
}
/// <summary> /// <summary>
/// Simple start of your Bot with the APIKey /// Simple start of your Bot with the APIKey
/// </summary> /// </summary>
/// <param name="apiKey"></param> /// <param name="apiKey"></param>
public BotBase(String apiKey) public BotBase(String apiKey, bool initClient = true) : this()
{ {
this.APIKey = apiKey; this.APIKey = apiKey;
if (!initClient)
return;
this.Client = new Base.MessageClient(this.APIKey); this.Client = new Base.MessageClient(this.APIKey);
this.Client.TelegramClient.Timeout = new TimeSpan(0, 1, 0); this.Client.TelegramClient.Timeout = new TimeSpan(0, 1, 0);
this.SystemCalls = new List<string>();
this.Sessions = new SessionBase();
this.Sessions.Client = this.Client; this.Sessions.Client = this.Client;
} }
@@ -95,15 +104,10 @@ namespace TelegramBotBase
/// </summary> /// </summary>
/// <param name="apiKey"></param> /// <param name="apiKey"></param>
/// <param name="proxyBaseAddress">i.e. https://127.0.0.1:10000</param> /// <param name="proxyBaseAddress">i.e. https://127.0.0.1:10000</param>
public BotBase(String apiKey, System.Net.Http.HttpClient proxy) public BotBase(String apiKey, System.Net.Http.HttpClient proxy) : this(apiKey, false)
{ {
this.APIKey = apiKey;
this.Client = new Base.MessageClient(this.APIKey, proxy); this.Client = new Base.MessageClient(this.APIKey, proxy);
this.SystemCalls = new List<string>();
this.Sessions = new SessionBase();
this.Sessions.Client = this.Client; this.Sessions.Client = this.Client;
} }
@@ -112,15 +116,10 @@ namespace TelegramBotBase
/// </summary> /// </summary>
/// <param name="apiKey"></param> /// <param name="apiKey"></param>
/// <param name="client"></param> /// <param name="client"></param>
public BotBase(String apiKey, TelegramBotClient client) public BotBase(String apiKey, TelegramBotClient client) : this(apiKey, false)
{ {
this.APIKey = apiKey;
this.Client = new Base.MessageClient(this.APIKey, client); this.Client = new Base.MessageClient(this.APIKey, client);
this.SystemCalls = new List<string>();
this.Sessions = new SessionBase();
this.Sessions.Client = this.Client; this.Sessions.Client = this.Client;
} }
@@ -129,17 +128,12 @@ namespace TelegramBotBase
/// </summary> /// </summary>
/// <param name="apiKey"></param> /// <param name="apiKey"></param>
/// <param name="proxyBaseAddress">i.e. https://127.0.0.1:10000</param> /// <param name="proxyBaseAddress">i.e. https://127.0.0.1:10000</param>
public BotBase(String apiKey, String proxyBaseAddress) public BotBase(String apiKey, String proxyBaseAddress) : this(apiKey, false)
{ {
this.APIKey = apiKey;
var url = new Uri(proxyBaseAddress); var url = new Uri(proxyBaseAddress);
this.Client = new Base.MessageClient(this.APIKey, url); this.Client = new Base.MessageClient(this.APIKey, url);
this.SystemCalls = new List<string>();
this.Sessions = new SessionBase();
this.Sessions.Client = this.Client; this.Sessions.Client = this.Client;
} }
@@ -149,15 +143,10 @@ namespace TelegramBotBase
/// <param name="apiKey"></param> /// <param name="apiKey"></param>
/// <param name="proxyHost">i.e. 127.0.0.1</param> /// <param name="proxyHost">i.e. 127.0.0.1</param>
/// <param name="proxyPort">i.e. 10000</param> /// <param name="proxyPort">i.e. 10000</param>
public BotBase(String apiKey, String proxyHost, int proxyPort) public BotBase(String apiKey, String proxyHost, int proxyPort) : this(apiKey, false)
{ {
this.APIKey = apiKey;
this.Client = new Base.MessageClient(this.APIKey, proxyHost, proxyPort); this.Client = new Base.MessageClient(this.APIKey, proxyHost, proxyPort);
this.SystemCalls = new List<string>();
this.Sessions = new SessionBase();
this.Sessions.Client = this.Client; this.Sessions.Client = this.Client;
} }
@@ -178,6 +167,15 @@ namespace TelegramBotBase
this.Sessions.LoadSessionStates(this.StateMachine); this.Sessions.LoadSessionStates(this.StateMachine);
} }
//Enable auto session saving
if (this.GetSetting(eSettings.SaveSessionsOnConsoleExit, false))
{
TelegramBotBase.Tools.Console.SetHandler(() =>
{
this.Sessions.SaveSessionStates(this.StateMachine);
});
}
this.Client.TelegramClient.StartReceiving(); this.Client.TelegramClient.StartReceiving();
} }
@@ -219,7 +217,7 @@ namespace TelegramBotBase
private async void Client_Message(object sender, MessageResult e) private async void Client_Message(object sender, MessageResult e)
{ {
if (this.SkipAllMessages) if (this.GetSetting(eSettings.SkipAllMessages, false))
return; return;
try try
@@ -227,7 +225,7 @@ namespace TelegramBotBase
DeviceSession ds = this.Sessions.GetSession(e.DeviceId); DeviceSession ds = this.Sessions.GetSession(e.DeviceId);
e.Device = ds; e.Device = ds;
if (LogAllMessages) if (this.GetSetting(eSettings.LogAllMessages, false))
{ {
OnMessage(new MessageIncomeEventArgs(e.DeviceId, ds, e)); OnMessage(new MessageIncomeEventArgs(e.DeviceId, ds, e));
} }
@@ -260,15 +258,14 @@ namespace TelegramBotBase
OnSessionBegins(new SessionBeginEventArgs(e.DeviceId, ds)); OnSessionBegins(new SessionBeginEventArgs(e.DeviceId, ds));
} }
ds.ChatTitle = e.Message.Chat.Username ?? e.Message.Chat.Title;
ds.LastAction = DateTime.Now; ds.LastAction = DateTime.Now;
ds.LastMessage = e.Message; ds.LastMessage = e.Message;
//Is this a systemcall ? //Is this a bot command ?
if (e.IsSystemCall && this.SystemCalls.Contains(e.SystemCommand)) if (e.IsBotCommand && this.BotCommands.Count(a => "/" + a.Command == e.BotCommand) > 0)
{ {
var sce = new SystemCallEventArgs(e.SystemCommand, e.SystemCallParameters, e.Message, ds.DeviceId, ds); var sce = new BotCommandEventArgs(e.BotCommand, e.BotCommandParameters, e.Message, ds.DeviceId, ds);
await OnSystemCall(sce); await OnBotCommand(sce);
if (sce.Handled) if (sce.Handled)
return; return;
@@ -314,14 +311,14 @@ namespace TelegramBotBase
e.IsFirstHandler = false; e.IsFirstHandler = false;
} while (ds.FormSwitched && i < NavigationMaximum); } while (ds.FormSwitched && i < this.GetSetting(eSettings.NavigationMaximum, 10));
} }
private async void Client_MessageEdit(object sender, MessageResult e) private async void Client_MessageEdit(object sender, MessageResult e)
{ {
if (this.SkipAllMessages) if (this.GetSetting(eSettings.SkipAllMessages, false))
return; return;
try try
@@ -329,7 +326,7 @@ namespace TelegramBotBase
DeviceSession ds = this.Sessions.GetSession(e.DeviceId); DeviceSession ds = this.Sessions.GetSession(e.DeviceId);
e.Device = ds; e.Device = ds;
if (LogAllMessages) if (this.GetSetting(eSettings.LogAllMessages, false))
{ {
OnMessage(new MessageIncomeEventArgs(e.DeviceId, ds, e)); OnMessage(new MessageIncomeEventArgs(e.DeviceId, ds, e));
} }
@@ -359,7 +356,6 @@ namespace TelegramBotBase
e.Device = ds; e.Device = ds;
} }
ds.ChatTitle = e.Message.Chat.Username ?? e.Message.Chat.Title;
ds.LastAction = DateTime.Now; ds.LastAction = DateTime.Now;
ds.LastMessage = e.Message; ds.LastMessage = e.Message;
@@ -381,7 +377,7 @@ namespace TelegramBotBase
DeviceSession ds = this.Sessions.GetSession(e.DeviceId); DeviceSession ds = this.Sessions.GetSession(e.DeviceId);
e.Device = ds; e.Device = ds;
if (LogAllMessages) if (this.GetSetting(eSettings.LogAllMessages, false))
{ {
OnMessage(new MessageIncomeEventArgs(e.DeviceId, ds, e)); OnMessage(new MessageIncomeEventArgs(e.DeviceId, ds, e));
} }
@@ -404,7 +400,6 @@ namespace TelegramBotBase
e.Device = ds; e.Device = ds;
} }
ds.ChatTitle = e.Message.Chat.Username ?? e.Message.Chat.Title;
ds.LastAction = DateTime.Now; ds.LastAction = DateTime.Now;
ds.LastMessage = e.Message; ds.LastMessage = e.Message;
@@ -470,10 +465,68 @@ namespace TelegramBotBase
e.IsFirstHandler = false; e.IsFirstHandler = false;
} while (ds.FormSwitched && i < NavigationMaximum); } while (ds.FormSwitched && i < this.GetSetting(eSettings.NavigationMaximum, 10));
} }
/// <summary>
/// This method will update all local created bot commands to the botfather.
/// </summary>
public async Task UploadBotCommands()
{
await this.Client.SetBotCommands(this.BotCommands);
}
/// <summary>
/// Could set a variety of settings to improve the bot handling.
/// </summary>
/// <param name="set"></param>
/// <param name="Value"></param>
public void SetSetting(eSettings set, uint Value)
{
this.SystemSettings[set] = Value;
}
/// <summary>
/// Could set a variety of settings to improve the bot handling.
/// </summary>
/// <param name="set"></param>
/// <param name="Value"></param>
public void SetSetting(eSettings set, bool Value)
{
this.SystemSettings[set] = (Value ? 1u : 0u);
}
/// <summary>
/// Could get the current value of a setting
/// </summary>
/// <param name="set"></param>
/// <param name="defaultValue"></param>
/// <returns></returns>
public uint GetSetting(eSettings set, uint defaultValue)
{
if (!this.SystemSettings.ContainsKey(set))
return defaultValue;
return this.SystemSettings[set];
}
/// <summary>
/// Could get the current value of a setting
/// </summary>
/// <param name="set"></param>
/// <param name="defaultValue"></param>
/// <returns></returns>
public bool GetSetting(eSettings set, bool defaultValue)
{
if (!this.SystemSettings.ContainsKey(set))
return defaultValue;
return this.SystemSettings[set] == 0u ? false : true;
}
#region "Events"
/// <summary> /// <summary>
/// Will be called if a session/context gets started /// Will be called if a session/context gets started
/// </summary> /// </summary>
@@ -518,15 +571,15 @@ namespace TelegramBotBase
} }
/// <summary> /// <summary>
/// Will be called if a system call gets raised /// Will be called if a bot command gets raised
/// </summary> /// </summary>
public event SystemCallEventHandler SystemCall; public event BotCommandEventHandler BotCommand;
public async Task OnSystemCall(SystemCallEventArgs e) public async Task OnBotCommand(BotCommandEventArgs e)
{ {
if (this.SystemCall != null) if (this.BotCommand != null)
await SystemCall(this, e); await BotCommand(this, e);
} }
/// <summary> /// <summary>
@@ -570,5 +623,8 @@ namespace TelegramBotBase
(this.__Events[__evUnhandledCall] as EventHandler<UnhandledCallEventArgs>)?.Invoke(this, e); (this.__Events[__evUnhandledCall] as EventHandler<UnhandledCallEventArgs>)?.Invoke(this, e);
} }
#endregion
} }
} }
+240 -62
View File
@@ -18,7 +18,7 @@ namespace TelegramBotBase.Controls
public class ButtonGrid : Base.ControlBase public class ButtonGrid : Base.ControlBase
{ {
public String Title { get; set; } = "Toggle"; public String Title { get; set; } = Localizations.Default.Language["ButtonGrid_Title"];
public String ConfirmationText { get; set; } = ""; public String ConfirmationText { get; set; } = "";
@@ -45,8 +45,32 @@ namespace TelegramBotBase.Controls
public bool DeletePreviousMessage { get; set; } = true; public bool DeletePreviousMessage { get; set; } = true;
/// <summary>
/// Parsemode of the message.
/// </summary>
public ParseMode MessageParseMode { get; set; } = ParseMode.Default; public ParseMode MessageParseMode { get; set; } = ParseMode.Default;
/// <summary>
/// Enables automatic paging of buttons when the amount of rows is exceeding the limits.
/// </summary>
public bool EnablePaging { get; set; } = false;
/// <summary>
/// Index of the current page
/// </summary>
public int CurrentPageIndex { get; set; } = 0;
public String PreviousPageLabel = Localizations.Default.Language["ButtonGrid_PreviousPage"];
public String NextPageLabel = Localizations.Default.Language["ButtonGrid_NextPage"];
public String NoItemsLabel = Localizations.Default.Language["ButtonGrid_NoItems"];
/// <summary>
/// Layout of the buttons which should be displayed always on top.
/// </summary>
public List<ButtonBase> HeadLayoutButtonRow { get; set; }
/// <summary> /// <summary>
/// Defines which type of Button Keyboard should be rendered. /// Defines which type of Button Keyboard should be rendered.
/// </summary> /// </summary>
@@ -116,10 +140,31 @@ namespace TelegramBotBase.Controls
if (!result.IsFirstHandler) if (!result.IsFirstHandler)
return; return;
var button = ButtonsForm.ToList().FirstOrDefault(a => a.Text.Trim() == result.MessageText); var button = HeadLayoutButtonRow?.FirstOrDefault(a => a.Text.Trim() == result.MessageText) ?? ButtonsForm.ToList().FirstOrDefault(a => a.Text.Trim() == result.MessageText);
if (button == null) if (button == null)
{
if (result.MessageText != null)
{
if (result.MessageText == PreviousPageLabel)
{
if (this.CurrentPageIndex > 0)
this.CurrentPageIndex--;
this.Updated();
}
else if (result.MessageText == NextPageLabel)
{
if (this.CurrentPageIndex < this.PageCount - 1)
this.CurrentPageIndex++;
this.Updated();
}
}
return; return;
}
OnButtonClicked(new ButtonClickedEventArgs(button)); OnButtonClicked(new ButtonClickedEventArgs(button));
@@ -146,10 +191,33 @@ namespace TelegramBotBase.Controls
{ {
case eKeyboardType.InlineKeyBoard: case eKeyboardType.InlineKeyBoard:
var button = ButtonsForm.ToList().FirstOrDefault(a => a.Value == result.RawData); var button = HeadLayoutButtonRow?.FirstOrDefault(a => a.Value == result.RawData) ?? ButtonsForm.ToList().FirstOrDefault(a => a.Value == result.RawData);
if (button == null) if (button == null)
{
switch (result.RawData)
{
case "$previous$":
if (this.CurrentPageIndex > 0)
this.CurrentPageIndex--;
this.Updated();
break;
case "$next$":
if (this.CurrentPageIndex < this.PageCount - 1)
this.CurrentPageIndex++;
this.Updated();
break;
}
return; return;
}
OnButtonClicked(new ButtonClickedEventArgs(button)); OnButtonClicked(new ButtonClickedEventArgs(button));
@@ -160,17 +228,16 @@ namespace TelegramBotBase.Controls
} }
public async override Task Render(MessageResult result) /// <summary>
/// This method checks of the amount of buttons
/// </summary>
private void CheckGrid()
{ {
if (!this.RenderNecessary)
return;
switch (m_eKeyboardType) switch (m_eKeyboardType)
{ {
case eKeyboardType.InlineKeyBoard: case eKeyboardType.InlineKeyBoard:
if (ButtonsForm.Rows > Constants.Telegram.MaxInlineKeyBoardRows) if (ButtonsForm.Rows > Constants.Telegram.MaxInlineKeyBoardRows && !this.EnablePaging)
{ {
throw new MaximumRowsReachedException() { Value = ButtonsForm.Rows, Maximum = Constants.Telegram.MaxInlineKeyBoardRows }; throw new MaximumRowsReachedException() { Value = ButtonsForm.Rows, Maximum = Constants.Telegram.MaxInlineKeyBoardRows };
} }
@@ -181,9 +248,10 @@ namespace TelegramBotBase.Controls
} }
break; break;
case eKeyboardType.ReplyKeyboard: case eKeyboardType.ReplyKeyboard:
if (ButtonsForm.Rows > Constants.Telegram.MaxReplyKeyboardRows) if (ButtonsForm.Rows > Constants.Telegram.MaxReplyKeyboardRows && !this.EnablePaging)
{ {
throw new MaximumRowsReachedException() { Value = ButtonsForm.Rows, Maximum = Constants.Telegram.MaxReplyKeyboardRows }; throw new MaximumRowsReachedException() { Value = ButtonsForm.Rows, Maximum = Constants.Telegram.MaxReplyKeyboardRows };
} }
@@ -195,63 +263,173 @@ namespace TelegramBotBase.Controls
break; break;
} }
}
Message m = null; public async override Task Render(MessageResult result)
if (this.MessageId != null) {
{ if (!this.RenderNecessary)
switch (this.KeyboardType) return;
{
//Reply Keyboard could only be updated with a new keyboard.
case eKeyboardType.ReplyKeyboard:
if (this.ButtonsForm.Count == 0)
{
await this.Device.HideReplyKeyboard();
this.MessageId = null;
}
else
{
if (this.DeletePreviousMessage)
await this.Device.DeleteMessage(this.MessageId.Value);
var rkm = (ReplyKeyboardMarkup)this.ButtonsForm; //Check for rows and column limits
rkm.ResizeKeyboard = this.ResizeKeyboard; CheckGrid();
rkm.OneTimeKeyboard = this.OneTimeKeyboard;
m = await this.Device.Send(this.Title, rkm, disableNotification: true, parseMode: MessageParseMode);
}
break;
case eKeyboardType.InlineKeyBoard:
m = await this.Device.Edit(this.MessageId.Value, this.Title, (InlineKeyboardMarkup)this.ButtonsForm);
break;
}
}
else
{
switch (this.KeyboardType)
{
case eKeyboardType.ReplyKeyboard:
var rkm = (ReplyKeyboardMarkup)this.ButtonsForm;
rkm.ResizeKeyboard = this.ResizeKeyboard;
rkm.OneTimeKeyboard = this.OneTimeKeyboard;
m = await this.Device.Send(this.Title, rkm, disableNotification: true, parseMode: MessageParseMode);
break;
case eKeyboardType.InlineKeyBoard:
m = await this.Device.Send(this.Title, (InlineKeyboardMarkup)this.ButtonsForm, disableNotification: true, parseMode: MessageParseMode);
break;
}
if (m != null)
{
this.MessageId = m.MessageId;
}
}
this.RenderNecessary = false; this.RenderNecessary = false;
Message m = null;
ButtonForm form = this.ButtonsForm.Duplicate();
if (this.EnablePaging)
{
form = GeneratePagingView(form);
}
if (this.HeadLayoutButtonRow != null && HeadLayoutButtonRow.Count > 0)
{
form.InsertButtonRow(0, this.HeadLayoutButtonRow);
}
switch (this.KeyboardType)
{
//Reply Keyboard could only be updated with a new keyboard.
case eKeyboardType.ReplyKeyboard:
if (this.MessageId != null)
{
if (form.Count == 0)
{
await this.Device.HideReplyKeyboard();
this.MessageId = null;
return;
}
if (this.DeletePreviousMessage)
await this.Device.DeleteMessage(this.MessageId.Value);
}
if (form.Count == 0)
return;
var rkm = (ReplyKeyboardMarkup)form;
rkm.ResizeKeyboard = this.ResizeKeyboard;
rkm.OneTimeKeyboard = this.OneTimeKeyboard;
m = await this.Device.Send(this.Title, rkm, disableNotification: true, parseMode: MessageParseMode, MarkdownV2AutoEscape: false);
break;
case eKeyboardType.InlineKeyBoard:
if (this.MessageId != null)
{
m = await this.Device.Edit(this.MessageId.Value, this.Title, (InlineKeyboardMarkup)form);
}
else
{
m = await this.Device.Send(this.Title, (InlineKeyboardMarkup)form, disableNotification: true, parseMode: MessageParseMode, MarkdownV2AutoEscape: false);
}
break;
}
if (m != null)
{
this.MessageId = m.MessageId;
}
}
private ButtonForm GeneratePagingView(ButtonForm dataForm)
{
ButtonForm bf = new ButtonForm();
for (int i = 0; i < this.MaximumRow - LayoutRows; i++)
{
int it = (this.CurrentPageIndex * (this.MaximumRow - LayoutRows)) + i;
if (it > dataForm.Rows - 1)
break;
bf.AddButtonRow(dataForm[it]);
}
//No Items
if (this.ButtonsForm.Count == 0)
{
bf.AddButtonRow(new ButtonBase(NoItemsLabel, "$"));
}
bf.InsertButtonRow(0, new ButtonBase(PreviousPageLabel, "$previous$"), new ButtonBase(String.Format(Localizations.Default.Language["ButtonGrid_CurrentPage"], this.CurrentPageIndex + 1, this.PageCount), "$site$"), new ButtonBase(NextPageLabel, "$next$"));
bf.AddButtonRow(new ButtonBase(PreviousPageLabel, "$previous$"), new ButtonBase(String.Format(Localizations.Default.Language["ButtonGrid_CurrentPage"], this.CurrentPageIndex + 1, this.PageCount), "$site$"), new ButtonBase(NextPageLabel, "$next$"));
return bf;
}
public bool PagingNecessary
{
get
{
if ((this.KeyboardType == eKeyboardType.InlineKeyBoard && ButtonsForm.Rows > Constants.Telegram.MaxInlineKeyBoardRows) | (this.KeyboardType == eKeyboardType.ReplyKeyboard && ButtonsForm.Rows > Constants.Telegram.MaxReplyKeyboardRows))
{
return true;
}
return false;
}
}
/// <summary>
/// Returns the maximum number of rows
/// </summary>
public int MaximumRow
{
get
{
switch (this.KeyboardType)
{
case eKeyboardType.InlineKeyBoard:
return Constants.Telegram.MaxInlineKeyBoardRows;
case eKeyboardType.ReplyKeyboard:
return Constants.Telegram.MaxReplyKeyboardRows;
default:
return 0;
}
}
}
/// <summary>
/// Contains the Number of Rows which are used by the layout.
/// </summary>
private int LayoutRows
{
get
{
int layoutRows = 2;
if (this.HeadLayoutButtonRow != null && this.HeadLayoutButtonRow.Count > 0)
layoutRows++;
return layoutRows;
}
}
public int PageCount
{
get
{
if (this.ButtonsForm.Count == 0)
return 1;
return (int)Math.Ceiling((decimal)(this.ButtonsForm.Rows / (decimal)(MaximumRow - 3)));
}
} }
public override async Task Hidden(bool FormClose) public override async Task Hidden(bool FormClose)
+9 -9
View File
@@ -27,7 +27,7 @@ namespace TelegramBotBase.Controls
private int? MessageId { get; set; } private int? MessageId { get; set; }
public String Title { get; set; } = "Pick Date"; public String Title { get; set; } = Localizations.Default.Language["CalendarPicker_Title"];
public eMonthPickerMode PickerMode { get; set; } public eMonthPickerMode PickerMode { get; set; }
@@ -55,7 +55,7 @@ namespace TelegramBotBase.Controls
switch (result.RawData) switch (result.RawData)
{ {
case "next": case "$next$":
switch (this.PickerMode) switch (this.PickerMode)
{ {
@@ -74,7 +74,7 @@ namespace TelegramBotBase.Controls
break; break;
case "prev": case "$prev$":
switch (this.PickerMode) switch (this.PickerMode)
{ {
@@ -93,7 +93,7 @@ namespace TelegramBotBase.Controls
break; break;
case "monthtitle": case "$monthtitle$":
if (this.EnableMonthView) if (this.EnableMonthView)
{ {
@@ -102,7 +102,7 @@ namespace TelegramBotBase.Controls
break; break;
case "yeartitle": case "$yeartitle$":
if (this.EnableYearView) if (this.EnableYearView)
{ {
@@ -110,7 +110,7 @@ namespace TelegramBotBase.Controls
} }
break; break;
case "yearstitle": case "$yearstitle$":
if (this.EnableMonthView) if (this.EnableMonthView)
{ {
@@ -179,7 +179,7 @@ namespace TelegramBotBase.Controls
string[] dayNamesNormal = this.Culture.DateTimeFormat.ShortestDayNames; string[] dayNamesNormal = this.Culture.DateTimeFormat.ShortestDayNames;
string[] dayNamesShifted = Shift(dayNamesNormal, (int)this.FirstDayOfWeek); string[] dayNamesShifted = Shift(dayNamesNormal, (int)this.FirstDayOfWeek);
bf.AddButtonRow(new ButtonBase("<<", "prev"), new ButtonBase(this.Culture.DateTimeFormat.MonthNames[month.Month - 1] + " " + month.Year.ToString(), "monthtitle"), new ButtonBase(">>", "next")); bf.AddButtonRow(new ButtonBase(Localizations.Default.Language["CalendarPicker_PreviousPage"], "$prev$"), new ButtonBase(this.Culture.DateTimeFormat.MonthNames[month.Month - 1] + " " + month.Year.ToString(), "$monthtitle$"), new ButtonBase(Localizations.Default.Language["CalendarPicker_NextPage"], "$next$"));
bf.AddButtonRow(dayNamesShifted.Select(a => new ButtonBase(a, a)).ToList()); bf.AddButtonRow(dayNamesShifted.Select(a => new ButtonBase(a, a)).ToList());
@@ -223,7 +223,7 @@ namespace TelegramBotBase.Controls
case eMonthPickerMode.month: case eMonthPickerMode.month:
bf.AddButtonRow(new ButtonBase("<<", "prev"), new ButtonBase(this.VisibleMonth.Year.ToString("0000"), "yeartitle"), new ButtonBase(">>", "next")); bf.AddButtonRow(new ButtonBase(Localizations.Default.Language["CalendarPicker_PreviousPage"], "$prev$"), new ButtonBase(this.VisibleMonth.Year.ToString("0000"), "$yeartitle$"), new ButtonBase(Localizations.Default.Language["CalendarPicker_NextPage"], "$next$"));
var months = this.Culture.DateTimeFormat.MonthNames; var months = this.Culture.DateTimeFormat.MonthNames;
@@ -235,7 +235,7 @@ namespace TelegramBotBase.Controls
case eMonthPickerMode.year: case eMonthPickerMode.year:
bf.AddButtonRow(new ButtonBase("<<", "prev"), new ButtonBase("Year", "yearstitle"), new ButtonBase(">>", "next")); bf.AddButtonRow(new ButtonBase(Localizations.Default.Language["CalendarPicker_PreviousPage"], "$prev$"), new ButtonBase("Year", "$yearstitle$"), new ButtonBase(Localizations.Default.Language["CalendarPicker_NextPage"], "$next$"));
var starti = Math.Floor(this.VisibleMonth.Year / 10f) * 10; var starti = Math.Floor(this.VisibleMonth.Year / 10f) * 10;
+5 -5
View File
@@ -12,15 +12,15 @@ namespace TelegramBotBase.Controls
public class ToggleButton : ControlBase public class ToggleButton : ControlBase
{ {
public String UncheckedIcon { get; set; } = "⚪"; public String UncheckedIcon { get; set; } = Localizations.Default.Language["ToggleButton_OffIcon"];
public String CheckedIcon { get; set; } = "⚫"; public String CheckedIcon { get; set; } = Localizations.Default.Language["ToggleButton_OnIcon"];
public String CheckedString { get; set; } = "On"; public String CheckedString { get; set; } = Localizations.Default.Language["ToggleButton_On"];
public String UncheckedString { get; set; } = "Off"; public String UncheckedString { get; set; } = Localizations.Default.Language["ToggleButton_Off"];
public String Title { get; set; } = "Toggle"; public String Title { get; set; } = Localizations.Default.Language["ToggleButton_Title"];
public int? MessageId { get; set; } public int? MessageId { get; set; }
+15 -13
View File
@@ -20,12 +20,12 @@ namespace TelegramBotBase.Controls
private int? MessageId { get; set; } private int? MessageId { get; set; }
public String MoveUpIcon { get; set; } = "🔼 up"; public String MoveUpIcon { get; set; } = Localizations.Default.Language["TreeView_LevelUp"];
public TreeView() public TreeView()
{ {
this.Nodes = new List<TreeViewNode>(); this.Nodes = new List<TreeViewNode>();
this.Title = "Select node"; this.Title = Localizations.Default.Language["TreeView_Title"];
} }
@@ -52,19 +52,21 @@ namespace TelegramBotBase.Controls
var n = (this.VisibleNode != null ? this.VisibleNode.FindNodeByValue(val) : this.Nodes.FirstOrDefault(a => a.Value == val)); var n = (this.VisibleNode != null ? this.VisibleNode.FindNodeByValue(val) : this.Nodes.FirstOrDefault(a => a.Value == val));
if (n != null) if (n == null)
{ return;
if (n.ChildNodes.Count > 0)
{
this.VisibleNode = n;
}
else
{
this.SelectedNode = (this.SelectedNode != n ? n : null);
}
result.Handled = true;
if (n.ChildNodes.Count > 0)
{
this.VisibleNode = n;
} }
else
{
this.SelectedNode = (this.SelectedNode != n ? n : null);
}
result.Handled = true;
break; break;
+35
View File
@@ -0,0 +1,35 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace TelegramBotBase.Enums
{
public enum eSettings
{
/// <summary>
/// How often could a form navigate to another (within one user action/call/message)
/// </summary>
NavigationMaximum = 1,
/// <summary>
/// Loggs all messages and sent them to the event handler
/// </summary>
LogAllMessages = 2,
/// <summary>
/// Skips all messages during running (good for big delay updates)
/// </summary>
SkipAllMessages = 3,
/// <summary>
/// Does stick to the console event handler and saves all sessions on exit.
/// </summary>
SaveSessionsOnConsoleExit = 4
}
}
+48 -1
View File
@@ -42,6 +42,15 @@ namespace TelegramBotBase.Form
} }
} }
public List<ButtonBase> this[int row]
{
get
{
return Buttons[row];
}
}
public ButtonForm() public ButtonForm()
{ {
@@ -62,6 +71,16 @@ namespace TelegramBotBase.Form
AddButtonRow(row.ToList()); AddButtonRow(row.ToList());
} }
public void InsertButtonRow(int index, IEnumerable<ButtonBase> row)
{
Buttons.Insert(index, row.ToList());
}
public void InsertButtonRow(int index, params ButtonBase[] row)
{
InsertButtonRow(index, row.ToList());
}
public static T[][] SplitTo<T>(IEnumerable<T> items, int itemsPerRow = 2) public static T[][] SplitTo<T>(IEnumerable<T> items, int itemsPerRow = 2)
{ {
T[][] splitted = default(T[][]); T[][] splitted = default(T[][]);
@@ -86,7 +105,10 @@ namespace TelegramBotBase.Form
{ {
get get
{ {
return this.Buttons.Select(a => a.ToArray()).Aggregate((a, b) => a.Union(b).ToArray()).Length; if (this.Buttons.Count == 0)
return 0;
return this.Buttons.Select(a => a.ToArray()).ToList().Aggregate((a, b) => a.Union(b).ToArray()).Length;
} }
} }
@@ -153,5 +175,30 @@ namespace TelegramBotBase.Form
return ikm; return ikm;
} }
/// <summary>
/// Creates a copy of this form.
/// </summary>
/// <returns></returns>
public ButtonForm Duplicate()
{
var bf = new ButtonForm()
{
Markup = this.Markup,
DependencyControl = this.DependencyControl
};
foreach(var b in Buttons)
{
var lst = new List<ButtonBase>();
foreach(var b2 in b)
{
lst.Add(b2);
}
bf.Buttons.Add(lst);
}
return bf;
}
} }
} }
+1 -1
View File
@@ -21,7 +21,7 @@ namespace TelegramBotBase.Form
public bool ShowBackButton { get; set; } = false; public bool ShowBackButton { get; set; } = false;
public String BackLabel { get; set; } = "Zurück"; public String BackLabel { get; set; } = Localizations.Default.Language["PromptDialog_Back"];
public PromptDialog() public PromptDialog()
{ {
@@ -3,11 +3,14 @@ using System.Collections.Generic;
using System.Text; using System.Text;
using TelegramBotBase.Args; using TelegramBotBase.Args;
using TelegramBotBase.Base; using TelegramBotBase.Base;
using TelegramBotBase.Form;
namespace TelegramBotBase.Interfaces namespace TelegramBotBase.Interfaces
{ {
public interface IStateMachine public interface IStateMachine
{ {
Type FallbackStateForm { get; }
void SaveFormStates(SaveStatesEventArgs e); void SaveFormStates(SaveStatesEventArgs e);
StateContainer LoadFormStates(); StateContainer LoadFormStates();
+14
View File
@@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace TelegramBotBase.Localizations
{
public static class Default
{
public static Localization Language = new English();
}
}
+25
View File
@@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace TelegramBotBase.Localizations
{
public class English : Localization
{
public English() : base()
{
Values["Language"] = "English";
Values["ButtonGrid_Title"] = "Menu";
Values["ButtonGrid_NoItems"] = "There are no items.";
Values["ButtonGrid_CurrentPage"] = "Page {0} of {1}";
Values["CalendarPicker_Title"] = "Pick date";
Values["TreeView_Title"] = "Select node";
Values["TreeView_LevelUp"] = "🔼 level up";
Values["ToggleButton_On"] = "On";
Values["ToggleButton_Off"] = "Off";
Values["ToggleButton_Title"] = "Toggle";
}
}
}
+16
View File
@@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace TelegramBotBase.Localizations
{
public class German : Localization
{
public German() : base()
{
}
}
}
@@ -0,0 +1,43 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace TelegramBotBase.Localizations
{
public class Localization
{
public Dictionary<String, String> Values = new Dictionary<string, string>();
public String this[String key]
{
get
{
return Values[key];
}
}
public Localization()
{
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["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["PromptDialog_Back"] = "Zurück";
}
}
}
+11 -4
View File
@@ -88,7 +88,7 @@ namespace TelegramBotBase
if (d != null) if (d != null)
{ {
this.SessionList.Remove(deviceId); this.SessionList.Remove(deviceId);
} }
} }
@@ -211,19 +211,26 @@ namespace TelegramBotBase
var form = s.Value.ActiveForm; var form = s.Value.ActiveForm;
try try
{ {
var se = new StateEntry(); var se = new StateEntry();
se.DeviceId = s.Key; se.DeviceId = s.Key;
se.ChatTitle = s.Value.ChatTitle; se.ChatTitle = s.Value.GetChatTitle();
se.FormUri = form.GetType().FullName; se.FormUri = form.GetType().FullName;
se.QualifiedName = form.GetType().AssemblyQualifiedName; se.QualifiedName = form.GetType().AssemblyQualifiedName;
//Skip classes where IgnoreState attribute is existing //Skip classes where IgnoreState attribute is existing
if (form.GetType().GetCustomAttributes(typeof(IgnoreState), true).Length != 0) if (form.GetType().GetCustomAttributes(typeof(IgnoreState), true).Length != 0)
{ {
continue; //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 //Is Subclass of IStateForm
+33 -9
View File
@@ -35,6 +35,18 @@ namespace TelegramBotBase.Sessions
/// </summary> /// </summary>
public String ChatTitle { get; set; } public String ChatTitle { get; set; }
/// <summary>
/// Returns the ChatTitle depending on groups/channels or users
/// </summary>
/// <returns></returns>
public String GetChatTitle()
{
return LastMessage?.Chat.Title
?? LastMessage?.Chat.Username
?? LastMessage?.Chat.FirstName
?? ChatTitle;
}
/// <summary> /// <summary>
/// When did any last action happend (message received or button clicked) /// When did any last action happend (message received or button clicked)
/// </summary> /// </summary>
@@ -132,9 +144,6 @@ namespace TelegramBotBase.Sessions
/// <returns></returns> /// <returns></returns>
public async Task<Message> Edit(int messageId, String text, ButtonForm buttons = null) public async Task<Message> Edit(int messageId, String text, ButtonForm buttons = null)
{ {
if (this.ActiveForm == null)
return null;
InlineKeyboardMarkup markup = buttons; InlineKeyboardMarkup markup = buttons;
if (text.Length > Constants.Telegram.MaxMessageLength) if (text.Length > Constants.Telegram.MaxMessageLength)
@@ -164,9 +173,6 @@ namespace TelegramBotBase.Sessions
/// <returns></returns> /// <returns></returns>
public async Task<Message> Edit(int messageId, String text, InlineKeyboardMarkup markup) public async Task<Message> Edit(int messageId, String text, InlineKeyboardMarkup markup)
{ {
if (this.ActiveForm == null)
return null;
if (text.Length > Constants.Telegram.MaxMessageLength) if (text.Length > Constants.Telegram.MaxMessageLength)
{ {
throw new MaxLengthException(text.Length); throw new MaxLengthException(text.Length);
@@ -194,9 +200,6 @@ namespace TelegramBotBase.Sessions
/// <returns></returns> /// <returns></returns>
public async Task<Message> Edit(Message message, ButtonForm buttons = null) public async Task<Message> Edit(Message message, ButtonForm buttons = null)
{ {
if (this.ActiveForm == null)
return null;
InlineKeyboardMarkup markup = buttons; InlineKeyboardMarkup markup = buttons;
if (message.Text.Length > Constants.Telegram.MaxMessageLength) if (message.Text.Length > Constants.Telegram.MaxMessageLength)
@@ -217,6 +220,27 @@ namespace TelegramBotBase.Sessions
return null; return null;
} }
/// <summary>
/// Edits the reply keyboard markup (buttons)
/// </summary>
/// <param name="messageId"></param>
/// <param name="bf"></param>
/// <returns></returns>
public async Task<Message> EditReplyMarkup(int messageId, ButtonForm bf)
{
try
{
return await this.Client.TelegramClient.EditMessageReplyMarkupAsync(this.DeviceId, messageId, bf);
}
catch
{
}
return null;
}
/// <summary> /// <summary>
/// Sends a simple text message /// Sends a simple text message
/// </summary> /// </summary>
+17 -1
View File
@@ -5,6 +5,7 @@ using System.Runtime.Serialization.Formatters;
using System.Text; using System.Text;
using TelegramBotBase.Args; using TelegramBotBase.Args;
using TelegramBotBase.Base; using TelegramBotBase.Base;
using TelegramBotBase.Form;
using TelegramBotBase.Interfaces; using TelegramBotBase.Interfaces;
namespace TelegramBotBase.States namespace TelegramBotBase.States
@@ -18,13 +19,28 @@ namespace TelegramBotBase.States
public bool Overwrite { get; set; } public bool Overwrite { get; set; }
public JSONStateMachine(String file, bool overwrite = true) 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 (file is null) if (file is null)
{ {
throw new ArgumentNullException(nameof(file)); throw new ArgumentNullException(nameof(file));
} }
this.FallbackStateForm = fallbackStateForm;
if (!this.FallbackStateForm.IsSubclassOf(typeof(FormBase)))
{
throw new ArgumentException("FallbackStateForm is not a subclass of FormBase");
}
this.FilePath = file; this.FilePath = file;
this.Overwrite = overwrite; this.Overwrite = overwrite;
} }
@@ -5,6 +5,7 @@ using System.Runtime.Serialization.Formatters;
using System.Text; using System.Text;
using TelegramBotBase.Args; using TelegramBotBase.Args;
using TelegramBotBase.Base; using TelegramBotBase.Base;
using TelegramBotBase.Form;
using TelegramBotBase.Interfaces; using TelegramBotBase.Interfaces;
namespace TelegramBotBase.States namespace TelegramBotBase.States
@@ -18,13 +19,28 @@ namespace TelegramBotBase.States
public bool Overwrite { get; set; } public bool Overwrite { get; set; }
public SimpleJSONStateMachine(String file, bool overwrite = true) 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 SimpleJSONStateMachine(String file, Type fallbackStateForm = null, bool overwrite = true)
{ {
if (file is null) if (file is null)
{ {
throw new ArgumentNullException(nameof(file)); throw new ArgumentNullException(nameof(file));
} }
this.FallbackStateForm = fallbackStateForm;
if (!this.FallbackStateForm.IsSubclassOf(typeof(FormBase)))
{
throw new ArgumentException("FallbackStateForm is not a subclass of FormBase");
}
this.FilePath = file; this.FilePath = file;
this.Overwrite = overwrite; this.Overwrite = overwrite;
} }
+17 -1
View File
@@ -7,6 +7,7 @@ using System.Xml;
using System.Xml.Serialization; using System.Xml.Serialization;
using TelegramBotBase.Args; using TelegramBotBase.Args;
using TelegramBotBase.Base; using TelegramBotBase.Base;
using TelegramBotBase.Form;
using TelegramBotBase.Interfaces; using TelegramBotBase.Interfaces;
namespace TelegramBotBase.States namespace TelegramBotBase.States
@@ -17,13 +18,28 @@ namespace TelegramBotBase.States
public bool Overwrite { get; set; } public bool Overwrite { get; set; }
public XMLStateMachine(String file, bool overwrite = true) 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 XMLStateMachine(String file, Type fallbackStateForm = null, bool overwrite = true)
{ {
if (file is null) if (file is null)
{ {
throw new ArgumentNullException(nameof(file)); throw new ArgumentNullException(nameof(file));
} }
this.FallbackStateForm = fallbackStateForm;
if (!this.FallbackStateForm.IsSubclassOf(typeof(FormBase)))
{
throw new ArgumentException("FallbackStateForm is not a subclass of FormBase");
}
this.FilePath = file; this.FilePath = file;
this.Overwrite = overwrite; this.Overwrite = overwrite;
} }
+65
View File
@@ -0,0 +1,65 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
namespace TelegramBotBase.Tools
{
public static class Console
{
[DllImport("Kernel32")]
private static extern bool SetConsoleCtrlHandler(EventHandler handler, bool add);
private delegate bool EventHandler(CtrlType sig);
static EventHandler _handler;
static List<Action> Actions = new List<Action>();
enum CtrlType
{
CTRL_C_EVENT = 0,
CTRL_BREAK_EVENT = 1,
CTRL_CLOSE_EVENT = 2,
CTRL_LOGOFF_EVENT = 5,
CTRL_SHUTDOWN_EVENT = 6
}
static Console()
{
}
public static void SetHandler(Action action)
{
Actions.Add(action);
if (_handler != null)
return;
_handler += new EventHandler(Handler);
SetConsoleCtrlHandler(_handler, true);
}
private static bool Handler(CtrlType sig)
{
switch (sig)
{
case CtrlType.CTRL_C_EVENT:
case CtrlType.CTRL_LOGOFF_EVENT:
case CtrlType.CTRL_SHUTDOWN_EVENT:
case CtrlType.CTRL_CLOSE_EVENT:
foreach (var a in Actions)
{
a();
}
return false;
default:
return false;
}
}
}
}
+7
View File
@@ -16,6 +16,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Examples", "Examples", "{BF
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SystemCommandsBot", "Examples\SystemCommandsBot\SystemCommandsBot.csproj", "{FC484952-3060-4F87-9809-4CD66D6961C0}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SystemCommandsBot", "Examples\SystemCommandsBot\SystemCommandsBot.csproj", "{FC484952-3060-4F87-9809-4CD66D6961C0}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JoinHiderBot", "Examples\JoinHiderBot\JoinHiderBot.csproj", "{E804B9E5-7ACC-49D3-9253-806766C1D9A5}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@@ -34,12 +36,17 @@ Global
{FC484952-3060-4F87-9809-4CD66D6961C0}.Debug|Any CPU.Build.0 = Debug|Any CPU {FC484952-3060-4F87-9809-4CD66D6961C0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FC484952-3060-4F87-9809-4CD66D6961C0}.Release|Any CPU.ActiveCfg = Release|Any CPU {FC484952-3060-4F87-9809-4CD66D6961C0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FC484952-3060-4F87-9809-4CD66D6961C0}.Release|Any CPU.Build.0 = Release|Any CPU {FC484952-3060-4F87-9809-4CD66D6961C0}.Release|Any CPU.Build.0 = Release|Any CPU
{E804B9E5-7ACC-49D3-9253-806766C1D9A5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E804B9E5-7ACC-49D3-9253-806766C1D9A5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E804B9E5-7ACC-49D3-9253-806766C1D9A5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E804B9E5-7ACC-49D3-9253-806766C1D9A5}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
EndGlobalSection EndGlobalSection
GlobalSection(NestedProjects) = preSolution GlobalSection(NestedProjects) = preSolution
{FC484952-3060-4F87-9809-4CD66D6961C0} = {BFA71E3F-31C0-4FC1-A320-4DCF704768C5} {FC484952-3060-4F87-9809-4CD66D6961C0} = {BFA71E3F-31C0-4FC1-A320-4DCF704768C5}
{E804B9E5-7ACC-49D3-9253-806766C1D9A5} = {BFA71E3F-31C0-4FC1-A320-4DCF704768C5}
EndGlobalSection EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {59CB40E1-9FA7-4867-A56F-4F418286F057} SolutionGuid = {59CB40E1-9FA7-4867-A56F-4F418286F057}