Compare commits

...
152 Commits
Author SHA1 Message Date
FlorianDahn 2457773a12 Merge branch 'master' of https://github.com/MajMcCloud/TelegramBotFramework 2020-09-05 02:08:28 +02:00
FlorianDahn 46e65faf31 Adding Enum deserialization
Fixing issue in SessionBase where it does not correctly deserialize Enum's
2020-09-05 02:08:19 +02:00
FlorianDahn 193a4811bf Simplifiy AutoCleanForm
Improving serialization by replacing the message with just the ID
2020-09-05 02:07:48 +02:00
Florian Dahn 08a0de8087 Update README.md 2020-09-01 19:00:11 +02:00
FlorianDahn 82469cff70 - Adding not about search feature 2020-08-30 20:48:44 +02:00
Florian Dahn 81eafe1219 Merge pull request #1 from JTOne123/master
[PR] The proj files have been updated to enable SourceLink
2020-08-09 17:25:00 +02:00
FlorianDahn 183e89c36f Adding paging/search example 2020-08-09 17:22:19 +02:00
FlorianDahn ad6cf9539c Adding filter functionality to ButtonGrid
Adding filter functionality
2020-08-09 17:00:04 +02:00
FlorianDahn 46576eb819 Update ButtonForm.cs
adding method to create a ButtonForm duplicate and filter the single buttons, used for ButtonGrid
2020-08-09 16:59:25 +02:00
FlorianDahn cf9e2285f1 Adding DynamicButton for Button with dynamic Text updates
For use, call the ButtonGrid.Updated() method
2020-07-25 15:11:15 +02:00
FlorianDahn ecfacdadbb Update AssemblyInfo.cs 2020-07-25 15:09:01 +02:00
FlorianDahn c00c2e8d8f Update DeviceSession.cs
- adding Unban method
2020-07-25 15:08:55 +02:00
FlorianDahn bfd905d590 Update TelegramBotBase.csproj 2020-07-25 15:08:41 +02:00
FlorianDahn 7b1bf75dde Update ToggleButton
- adding ChangedString variable for custom message
2020-07-25 15:08:22 +02:00
FlorianDahn 7a7a65c3a7 Update FormBase.cs
- adding RemoveAllControls method
2020-07-25 15:07:36 +02:00
Pavlo Datsiuk 8a42a861cf [COMMIT] The proj files have been updated to enable SourceLink [TelegramBotBase.csproj] 2020-06-25 15:54:40 +03:00
FlorianDahn 47876681e0 IgnoreState and SaveState addings for better session serialization
- adding IgnoreState to all ModalDialogs to prevent them get saved.
- adding SaveState to AutoCleanForm to keep state
- change OldMessages property of AutoCleanForm to public
2020-05-07 23:49:08 +02:00
FlorianDahn 9314b4c0e5 Update ButtonGrid.cs
- Fix for async event timings, button click got sometimes fired after Render event
2020-05-06 20:56:38 +02:00
FlorianDahn 01c19b9900 Update README.md 2020-05-04 15:33:50 +02:00
FlorianDahn 0af587d27f Updating readme and test project 2020-05-04 15:33:21 +02:00
FlorianDahn 97b9176c3a Updating packages 2020-05-04 15:22:33 +02:00
FlorianDahn dfa479d409 Fix in StateMachines 2020-05-04 15:18:48 +02:00
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
FlorianDahn eb61d40a38 -Projectfile update 2020-04-09 15:32:48 +02:00
FlorianDahn 2ed030b2fb - adding MarkdownV2 escape function
- adding MarkdownV2 autoescape feature
2020-04-09 11:57:45 +02:00
FlorianDahn fd6f6fef34 - moving LoadSessionStates and SaveSessionStates from BotBase to SessionBase 2020-04-09 11:18:23 +02:00
FlorianDahn 54176f9136 Update SessionBase.cs
- adding method to get User Sessions and Group Sessions
2020-04-05 23:47:27 +07:00
FlorianDahn f80fda8ff9 More Attributes for Serialization
- adding IgnoreState Attribute to make it easier to skip forms on session serialization
2020-04-05 23:46:33 +07:00
FlorianDahn 8b66842d34 Update BotBase.cs
- prevent BotBase from crashing on  loading serialized sessions
2020-04-05 23:31:37 +07:00
FlorianDahn 0cdb8c3a1a Adding StateMachine for Session serialization
- adding multiple classes and interfaces for Session Serialization and recovery after restart
2020-04-05 13:13:11 +07:00
FlorianDahn 8b9929198a Modal and Form updates
- adding ReturnFromModal method to FormBase, will get invoked if context leaves a ModalDialog
- ModalDialog contains ParentForm for better navigation
- ModalDialog invokes ReturnFromModal on parentForm after closing
- adding ShowBackButton to PromptDialog for show a back button
2020-04-04 19:52:15 +07:00
FlorianDahn 7a3e4721bd Update DataResult.cs
- adding DownloadAudio
- adding an optional method for all available ones to save attachments directly to a file
2020-04-03 21:54:32 +07:00
FlorianDahn f914920919 Update LinkReplaceTest.cs
- updating LinkReplaceTest to use Message edits, so links will replace in original messages or edited ones
2020-04-02 12:41:26 +07:00
FlorianDahn 0435c68281 Update DeviceSession.cs
DeviceSession: Removing unused ex variable in Exceptions
2020-04-02 12:39:48 +07:00
FlorianDahn 5a1e050aec Update Generator.cs
- Markup Generator: Updating MentionUser
2020-04-02 12:39:26 +07:00
FlorianDahn 6a62d73fe0 Adding Edited Features
- FormBase: adding Edited method
- MessageClient: adding MessageEdit event
- GroupForm: adding Edited method
- DeviceSession: adding ChatTitle
2020-04-02 12:37:53 +07:00
FlorianDahn 519a6fe41c Update ButtonGrid.cs
- ButtonGrid: adding ConfirmationText
- ButtonGrid: adding ParseMode
2020-04-02 12:30:38 +07:00
FlorianDahn 201dfd10dd - updating Testproject to latest changes in Main project 2020-03-29 11:12:34 +07:00
FlorianDahn 78a412c630 - simplify function returns and improving memory use 2020-03-28 10:58:25 +07:00
FlorianDahn 7a7014cec3 - adding SendTextFile to simplify sending csv or textfiles by code without saving them to harddrive 2020-03-28 10:55:32 +07:00
FlorianDahn 64311d0cd9 - adding link to documentation 2020-03-28 10:29:34 +07:00
FlorianDahn 762fb27294 Markdown Fix
- Updating Markdown generator
- adding more options
2020-03-27 12:41:36 +07:00
FlorianDahn 164194ac66 Update
- adding more Constants to Telegram file
- Buttongrid: fixing check for rows and columns
- Buttongrid: replacingvalues with constants from Telegram file
2020-03-27 12:08:34 +07:00
FlorianDahn 321bd7c0ff - adding GetButtonByValue to ButtonForm 2020-03-27 11:03:15 +07:00
FlorianDahn 37ef30ebee - changing OpenModal and NavigateTo to virtual, so you could override it 2020-03-25 18:10:13 +07:00
FlorianDahn 9b1461ad78 Update README.md 2020-03-25 12:45:07 +07:00
FlorianDahn 38fa0a4e99 Update README.md 2020-03-25 12:44:29 +07:00
FlorianDahn fba922049f Updates and fixes
- adding IsFirstHandler to allow controls to skip Actions which has been invoked already (i.e. due to navigation)
- adding Hidden method to ButtonGrid to fix re-rendering Buttons after opening a Modal form and comming back
- improving and fixing the Cleanup method for ButtonGrid to prevent "Keyboard jumping" for ReplyKeyboard on mobile devices.
- renaming some old *Result objects to *EventArgs for more clear definitions
2020-03-25 12:37:16 +07:00
FlorianDahn d5a9a5f7a4 - adding Hidden method to ControlBase which will be called on leaving form or on opening a modal one 2020-03-25 12:27:01 +07:00
FlorianDahn 5126074419 Merge branch 'master' of https://github.com/MajMcCloud/TelegramBotFramework 2020-03-23 13:06:56 +07:00
FlorianDahn aa38492bde - more refactoring and renaming models *Result to *EventArgs 2020-03-23 13:06:52 +07:00
Florian Dahn 9c68914e9d Delete TelegramBotBase.2.1.0.zip 2020-03-23 13:03:59 +07:00
Florian Dahn 3fe2779b1e Delete TelegramBotBase.2.0.0.zip 2020-03-23 13:03:51 +07:00
FlorianDahn 8e6de4a0e1 - Refactoring and moving all EventArgs structures to folder Args 2020-03-23 13:02:31 +07:00
FlorianDahn 382ae2de2d - adding example project for Bot to start predefined system commands 2020-03-10 01:24:09 +01:00
FlorianDahn 77fd3393de - update spaces 2020-03-10 01:22:52 +01:00
FlorianDahn 0f1da77aca - update on AutoCleanForm for better message deletion on leaving 2020-03-10 01:22:28 +01:00
FlorianDahn 1561c1dd08 -adding HideKeyboardOnCleanup and DeletePreviousMessage on ButtonGrid Control 2020-03-10 01:21:42 +01:00
FlorianDahn 5b38f2e1e2 - adding sendvideo to device for videos or gifs 2019-12-04 01:29:22 +01:00
FlorianDahn 2989456fe5 - change Formbase for better navigation management 2019-12-04 01:28:15 +01:00
FlorianDahn 7e4174a3be Merge branch 'master' of https://github.com/MajMcCloud/TelegramBotFramework 2019-12-04 01:27:07 +01:00
FlorianDahn 73f81cc8a2 Revert "- adding new exceptions"
This reverts commit 2071ba2eeb.
2019-12-02 03:54:10 +01:00
FlorianDahn 2071ba2eeb - adding new exceptions 2019-12-02 03:48:43 +01:00
FlorianDahn a08ba8f512 -update on FormBase which should prevent issues on control management 2019-12-02 03:46:40 +01:00
Florian Dahn 649b94dafe Delete dotnetcore.yml 2019-11-28 17:17:19 +01:00
FlorianDahn 7be0f15ca4 - updated readme for groups
- adding readme to project solution
2019-11-28 15:41:44 +01:00
FlorianDahn e29be4f1f4 Merge branch 'master' of https://github.com/MajMcCloud/TelegramBotFramework 2019-11-23 02:44:59 +01:00
FlorianDahn 176107300b - adding group examples for:
link replacing
   group change notifications
   welcoming users
2019-11-23 02:44:50 +01:00
FlorianDahn 0ceab58059 - updating namespaces in test project
- adding chat splitter to testproject to allow single chat and group chat examples at the same time
2019-11-23 02:44:05 +01:00
FlorianDahn 6ebdd9f208 - additional stuff for group management 2019-11-23 02:41:17 +01:00
FlorianDahn 595efd2276 - adding base class for group management 2019-11-23 02:40:51 +01:00
FlorianDahn 85a479d826 - adding useful tasks for groups 2019-11-23 02:40:25 +01:00
FlorianDahn 150e9f731d - addes SplitterForm to switch between "normal" chats and group chats 2019-11-23 02:39:56 +01:00
FlorianDahn 0eab45bdbb - making Events property of FormBase public, for use in sub classes
- making ResultBase derive from Eventargs
- adding messagetype to messageresult
2019-11-21 21:38:20 +01:00
Florian Dahn a33cf5b841 Create dotnetcore.yml 2019-11-14 22:58:50 +01:00
FlorianDahn 1f9539a23d - updating readme 2019-10-11 22:38:50 +02:00
FlorianDahn 9cbde4830e - adding OpenModal/CloseModal features to leave a form open and "just" show a prompt and get back
- added new "ModalDialog" class to present modal forms
- updating examples for prompt dialog to use the modal feature
2019-10-11 22:37:54 +02:00
FlorianDahn 3bea2783b1 - updated Readme 2019-10-11 21:41:02 +02:00
FlorianDahn 038a941d33 - PromptDialog has been changed to ConfirmDialog
- PromptDialog is now (like in JavaScript) for text inputs
- examples corrected
2019-10-11 21:33:33 +02:00
FlorianDahn 48c62e57f8 - moving TelegramBaseTest to TelegramBotBaseTest 2019-09-29 00:47:18 +02:00
FlorianDahn b0e6a8b981 -migrate from .net 4.7.2 to .Net standard 2.0 for the library and .Net core 3.0 for the example project 2019-09-28 14:06:19 +02:00
FlorianDahn 2e0656dc06 - update readme 2019-09-28 01:19:55 +02:00
FlorianDahn d1456d5bd1 - update readme and formbase 2019-09-28 01:17:38 +02:00
FlorianDahn 058539777f - removing obsolete Property and methods
- updating test project
2019-09-27 23:24:31 +02:00
FlorianDahn 5c69289198 - small additions 2019-09-27 23:18:18 +02:00
FlorianDahn 4c8b085655 - small cleanup 2019-09-27 23:14:28 +02:00
FlorianDahn 94d22318f8 - change SetKeyboardType method to Property 2019-09-27 23:08:55 +02:00
FlorianDahn 2028e2f9e6 - adding ButtonGrid control to Readme 2019-09-27 21:26:43 +02:00
FlorianDahn 394209b303 updating both projects to .Net 4.7.2 2019-09-27 00:01:20 +02:00
FlorianDahn f0fa953819 Updating csproj Files 2019-09-26 23:27:38 +02:00
FlorianDahn 1d44b50883 - small change 2019-09-26 20:57:51 +02:00
FlorianDahn 3c21af8f68 - small change and adding comments 2019-09-26 20:57:16 +02:00
FlorianDahn 1b811e1786 - added ToKeyboardButton to ButtonBase
- added eKeyboardType to switch between Inline and KeykboardButtons
- addes some small functions to ButtonForm
- added new control "ButtonGrid" which manages Button handling with Event Handler for each Button and can switch between InlineMode and KeyboardMode during runtime
- added also Examples to it
2019-09-26 20:54:35 +02:00
FlorianDahn 94195ec5bf - added another Edit Method for InlineKeyboardMarkup
- change Send method parameter ReplyKeyboardMarkup to ReplyMarkupBase to allow also RemoveKeyboard
2019-09-26 20:51:30 +02:00
FlorianDahn 256cac825f - adding Load method for controls 2019-09-26 20:50:33 +02:00
FlorianDahn 69e5a6717b - Splitting enums into single files for more structure
- remove old Enum file
2019-09-26 17:14:12 +02:00
FlorianDahn a6d22cdd82 Update version 2019-09-17 16:44:14 +02:00
FlorianDahn ee4a3d765c Merge branch 'master' of https://github.com/MajMcCloud/TelegramBotFramework 2019-09-17 16:43:10 +02:00
FlorianDahn 703c99eb8d - added a custom exception for maximum length messages, saves to time to sent to API
- added a constants file, for have constants to work with
- catching maximum message length
2019-09-17 16:43:02 +02:00
Florian Dahn 138fee9772 Update README.md
adding paypal
2019-09-17 16:33:51 +02:00
Florian Dahn d60399514f Update README.md 2019-09-04 15:13:42 +02:00
FlorianDahn ae0fa3b0e3 Added simple Markdown/HTML Generator
Use with:

"Bold Text".Bold();

"Other text".Italic();

"www.google.de".Link("Google")
2019-08-28 17:33:18 +02:00
FlorianDahn 6a2ac7a309 Added simple Markdown/HTML Generator
Use with:

"Bold Text".Bold();

"Other text".Italic();

"www.google.de".AsLink("Google")
2019-08-28 17:32:08 +02:00
FlorianDahn 1a76b2b3f8 added some forms to obsolete, cause their use is bad for ram management 2019-08-26 01:34:50 +02:00
FlorianDahn 5893108bd3 Update readme 2019-08-23 14:55:54 +02:00
FlorianDahn d668f57711 Update readme 2019-08-23 14:53:24 +02:00
FlorianDahn 56c7754408 - Added ToggleButton as an easy switch control (i.e. for settings page)
- added control id, for easier separating of controls in one form
- added automatic event selection for specific controls, to not raise an event for other controls who has invoked it
- changed Action method
- added example
2019-08-23 14:02:24 +02:00
FlorianDahn 805ffb9ec7 Update version 2019-07-15 13:33:01 +02:00
FlorianDahn aeddd82ea9 Merge branch 'master' of https://github.com/MajMcCloud/TelegramBotFramework 2019-07-15 13:31:07 +02:00
FlorianDahn 1ec14ce680 Updating nuget packages 2019-07-15 13:31:02 +02:00
Florian Dahn 3a309baeae Update README.md 2019-07-14 23:14:37 +02:00
FlorianDahn e02af0fc76 Merge branch 'master' of https://github.com/MajMcCloud/TelegramBotFramework 2019-07-14 23:13:19 +02:00
FlorianDahn 530e01f971 new images 2019-07-14 23:13:16 +02:00
Florian Dahn dcf3cf804f Update README.md 2019-07-14 23:00:06 +02:00
Florian Dahn a42d3934b5 Update README.md 2019-07-14 22:52:59 +02:00
Florian Dahn 2126b45fed Update README.md 2019-07-14 22:47:04 +02:00
FlorianDahn b6b9d5b6b1 Update images 2019-07-14 22:46:29 +02:00
FlorianDahn eed5f012dc adding images for example 2019-07-14 22:38:10 +02:00
FlorianDahn de4cc138ae - New control: CalendarPicker
- New control: MonthPicker
- New control TreeView
- adding examples for all 3
- small change on Progress Bar Control
- due latest changes on the base it is now easier to create controls which will be rendered (or not)in several forms depending on user context
2019-07-14 22:27:16 +02:00
FlorianDahn e4da70d37d - Update of FormBase for better async event management
- Update of ControlBase for partial rendering options
- Updating serveral base classes to better asyc event management
- separating Enums into different folder
- some small bug fixes
- removed "CustomEventManagement" cause it doesnt make sense now
2019-07-14 22:22:04 +02:00
FlorianDahn cb4a797b12 Updating ButtonBase to enable Url Buttons 2019-07-14 22:13:23 +02:00
FlorianDahn 94d153e141 Merge branch 'master' of https://github.com/MajMcCloud/TelegramBotFramework 2019-06-26 12:48:37 +02:00
FlorianDahn e13cb6d4ef - Updating NavigateTo to set PreviousForm now correctly 2019-06-26 12:48:25 +02:00
Florian Dahn c0e285d39e Update README.md 2019-06-24 13:50:55 +02:00
Florian Dahn 8338c5c066 Update README.md 2019-06-24 13:49:37 +02:00
137 changed files with 6578 additions and 697 deletions
+12
View File
@@ -240,3 +240,15 @@ ModelManifest.xml
# FAKE - F# Make # FAKE - F# Make
.fake/ .fake/
MigrationBackup
TelegramBotBase/nuget.exe
Material
TelegramBotBase/cp.ps1
TelegramBotBase/TelegramBotBase.nuspec
Program.cs
TelegramBotBase/Archive
/TelegramBotBase/TelegramBotBase.1.5.0.zip
/TelegramBotBase/TelegramBotBase.1.5.1.zip
/TelegramBotBase/TelegramBotBase.1.5.2.zip
/TelegramBotBase/cpush.ps1
/TelegramBotBase/cpack.ps1
+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.1.1" />
</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();
}
}
}
@@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="TelegramBotBase" Version="3.1.1" />
</ItemGroup>
</Project>
@@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace SystemCommandsBot.commands
{
public class Commando
{
public int ID { get; set; }
public String Title { get; set; }
public String ShellCmd { get; set; }
public bool Enabled { get; set; } = true;
public String Action { get; set; }
public bool UseShell { get; set; } = true;
public int? MaxInstances { get; set; }
public String ProcName
{
get;set;
}
}
}
@@ -0,0 +1,92 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace SystemCommandsBot.config
{
public class Config
{
public String Password { get; set; }
public String ApiKey { get; set; }
public List<commands.Commando> Commandos { get; set; }
public Config()
{
this.Commandos = new List<commands.Commando>();
}
public void loadDefaultValues()
{
this.ApiKey = "";
this.Commandos.Add(new commands.Commando() { ID = 0, Enabled = true, Title = "Test Befehl", ShellCmd = "explorer.exe", Action = "start", MaxInstances = 2 });
}
public static Config load()
{
try
{
return load(AppContext.BaseDirectory + "config\\default.cfg");
}
catch(Exception ex)
{
Console.WriteLine(ex.Message);
}
return null;
}
public static Config load(String path)
{
try
{
var cfg = Newtonsoft.Json.JsonConvert.DeserializeObject<Config>(File.ReadAllText(path)) as Config;
return cfg;
}
catch (DirectoryNotFoundException ex)
{
DirectoryInfo di = new DirectoryInfo(path);
if (!Directory.Exists(di.Parent.FullName))
{
Directory.CreateDirectory(di.Parent.FullName);
}
var cfg = new Config();
cfg.loadDefaultValues();
cfg.save(path);
return cfg;
}
catch (FileNotFoundException ex)
{
var cfg = new Config();
cfg.loadDefaultValues();
cfg.save(path);
return cfg;
}
catch(Exception ex)
{
Console.WriteLine(ex.Message);
}
return null;
}
public void save(String path)
{
try
{
File.WriteAllText(path, Newtonsoft.Json.JsonConvert.SerializeObject(this));
}
catch
{
}
}
}
}
+172
View File
@@ -0,0 +1,172 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TelegramBotBase.Base;
using TelegramBotBase.Form;
namespace SystemCommandsBot.forms
{
public class CmdForm : TelegramBotBase.Form.AutoCleanForm
{
public DateTime ExpiresAt { get; set; }
public int? MessageId { get; set; }
public override async Task Load(MessageResult message)
{
}
public override async Task Action(MessageResult message)
{
var btn = message.RawData;
int id = -1;
if (!int.TryParse(btn, out id))
{
return;
}
var cmd = Program.BotConfig.Commandos.Where(a => a.Enabled && a.ID == id).FirstOrDefault();
if (cmd == null)
{
await this.Device.Send("Cmd nicht verfügbar.");
return;
}
message.Handled = true;
switch (cmd.Action)
{
case "start":
FileInfo fi = new FileInfo(cmd.ShellCmd);
if (cmd.MaxInstances != null && cmd.MaxInstances >= 0)
{
if (Process.GetProcessesByName(cmd.ProcName).Count() >= cmd.MaxInstances)
{
await this.Device.Send("Anwendung läuft bereits.");
await message.ConfirmAction("Anwendung läuft bereits.");
return;
}
}
ProcessStartInfo psi = new ProcessStartInfo();
psi.FileName = cmd.ShellCmd;
psi.WorkingDirectory = fi.DirectoryName;
psi.UseShellExecute = cmd.UseShell;
Process.Start(psi);
await this.Device.Send(fi.Name + " wurde gestarted.");
await message.ConfirmAction(fi.Name + " wurde gestarted.");
break;
case "kill":
FileInfo fi2 = new FileInfo(cmd.ShellCmd);
String pros = fi2.Name.Replace(fi2.Extension, "");
var proc = Process.GetProcessesByName(pros).ToList();
foreach (var p in proc)
{
try
{
p.Kill();
}
catch
{
}
}
await this.Device.Send(fi2.Name + " wurde beendet.");
await message.ConfirmAction(fi2.Name + " wurde beendet.");
break;
case "restart":
FileInfo fi3 = new FileInfo(cmd.ShellCmd);
String pros2 = fi3.Name.Replace(fi3.Extension, "");
var proc2 = Process.GetProcessesByName(pros2).ToList();
foreach (var p in proc2)
{
try
{
p.Kill();
}
catch
{
}
}
FileInfo fi4 = new FileInfo(cmd.ShellCmd);
ProcessStartInfo psi2 = new ProcessStartInfo();
psi2.FileName = cmd.ShellCmd;
psi2.WorkingDirectory = fi4.DirectoryName;
psi2.FileName = cmd.ShellCmd;
psi2.UseShellExecute = cmd.UseShell;
Process.Start(psi2);
await this.Device.Send(fi3.Name + " wurde neugestarted.");
await message.ConfirmAction(fi3.Name + " wurde neugestarted.");
break;
default:
await message.ConfirmAction();
break;
}
}
public override async Task Render(MessageResult message)
{
if (this.MessageId == null)
{
var buttons = Program.BotConfig.Commandos.Where(a => a.Enabled).Select(a => new ButtonBase(a.Title, a.ID.ToString()));
ButtonForm bf = new ButtonForm();
bf.AddSplitted(buttons, 1);
await this.Device.Send("Deine Optionen", bf);
return;
}
}
}
}
@@ -0,0 +1,42 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using TelegramBotBase.Base;
namespace SystemCommandsBot.forms
{
public class StartForm : TelegramBotBase.Form.FormBase
{
public String Password { get; set; }
public override async Task Load(MessageResult message)
{
var inp = message.MessageText;
if (Program.BotConfig.Password == inp)
{
this.Password = inp;
}
}
public override async Task Render(MessageResult message)
{
if (this.Password == null || this.Password.Trim() == "")
{
await this.Device.Send("Bitte gib dein Passwort an.");
return;
}
var cmd = new forms.CmdForm();
cmd.ExpiresAt = DateTime.Now.AddDays(14);
await this.NavigateTo(cmd);
}
}
}
+429 -29
View File
@@ -1,4 +1,5 @@
# .Net Telegram Bot Framework - Context based addon # .Net Telegram Bot Framework - Context based addon
[![NuGet version (TelegramBotBase)](https://img.shields.io/nuget/v/TelegramBotBase.svg?style=flat-square)](https://www.nuget.org/packages/TelegramBotBase/) [![NuGet version (TelegramBotBase)](https://img.shields.io/nuget/v/TelegramBotBase.svg?style=flat-square)](https://www.nuget.org/packages/TelegramBotBase/)
@@ -12,6 +13,25 @@ Test the Testproject: [@TGBaseBot](https://www.t.me/TGBaseBot)
Join the Telegram Group: [https://www.t.me/tgbotbase](https://www.t.me/tgbotbase) Join the Telegram Group: [https://www.t.me/tgbotbase](https://www.t.me/tgbotbase)
Download a release: [Releases](https://github.com/MajMcCloud/TelegramBotFramework/releases)
---
Donations
Bitcoin: 1NqyGWmZg8HLp9qQELbf6bChEoba3mxaFc
ETH: 0x795B70CFC27C69603ce115F2b450cbAC5a5460D0
Litecoin: LM5iCN6Nz22wAi8LSFnKgdGGWEtxWfJZXv
DASH: Xb2qyVefvbKTXusHoxZ4Soja2S6R4vLsSr
Paypal: https://paypal.me/majmccloud
Thanks !
--- ---
## Index ## Index
@@ -36,6 +56,51 @@ Join the Telegram Group: [https://www.t.me/tgbotbase](https://www.t.me/tgbotbase
* [PromptDialog](#prompt-dialog) * [PromptDialog](#prompt-dialog)
* [ConfirmDialog](#confirm-dialog)
- [Controls](#controls)
* [ProgressBar](#progress-bar)
* [CalendarPicker](#calendar-picker)
* [MonthPicker](#month-picker)
* [TreeView](#tree-view)
* [ToggleButton](#toggle-button)
* [ButtonGrid](#button-grid)
* [Paging and Searching](#paging--searching)
- [Groups](#groups)
* [SplitterForm](#splitter-form)
* [GroupForm](#group-form)
- [State Machine and Session Serialization (v3.0.0)](#statemachine-and-sessions)
* [StateMachines](#statemachines)
* [SimpleJSONStateMachine](#simplejsonstatemachine)
* [JSONStateMachine](#jsonstatemachine)
* [XMLStateMachine](#xmlstatemachine)
* [Interfaces](#interfaces)
* [IStateMachine](#istatemachine)
* [IStateForm](#istateform)
* [Attributes](#attributes)
* [SaveState](#savestate)
* [IgnoreState](#ignorestate)
- [Examples](#examples)
--- ---
@@ -55,7 +120,7 @@ It gives you features which will look/feel like WinForms or have a good way to c
## How to start: ## How to start:
Within your empty App your need to put some initial lines including your APIKey to get things started. Within your empty App your need to put some initial lines including your APIKey to get things started.
The "BotBase" Class will manage a lot of things for you, like system calls, action events and so on. The "BotBase" Class will manage a lot of things for you, like bot commands, action events and so on.
"StartForm" is your first Formular which every user will get internally redirected to, like a start page, you could redirect from there later in code, so users won't recognize it. "StartForm" is your first Formular which every user will get internally redirected to, like a start page, you could redirect from there later in code, so users won't recognize it.
It needs to be a subclass of "FormBase" you will find in Namespace TelegramBotBase.Base It needs to be a subclass of "FormBase" you will find in Namespace TelegramBotBase.Base
@@ -66,7 +131,10 @@ It needs to be a subclass of "FormBase" you will find in Namespace TelegramBotBa
BotBase<StartForm> bb = new BotBase<StartForm>("{YOUR API KEY}"); BotBase<StartForm> bb = new BotBase<StartForm>("{YOUR API KEY}");
//Add Systemcommands if you like, you could catch them later //Add Systemcommands if you like, you could catch them later
bb.SystemCalls.Add("/start"); bb.BotCommands.Add(new BotCommand() { Command = "start", Description = "Starts the bot" });
//Update bot commands to botfather
bb.UploadBotCommands().Wait();
//Start your Bot //Start your Bot
bb.Start(); bb.Start();
@@ -109,6 +177,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)
{ {
@@ -152,10 +226,10 @@ await this.NavigateTo(tf);
All examples are within the test project, so just try it out on your own. All examples are within the test project, so just try it out on your own.
### Add some system calls (Example #0 - System Calls) ### Add some system calls (Example #0 - Bot Commands)
Inside of the BotFather you are able to add "Commands" to your TelegramBot. The user will see them, depending on the application as options he could choose. Inside of the BotFather you are able to add "Commands" to your TelegramBot. The user will see them, depending on the application as options he could choose.
I'm calling them Systemcalls. Before start (and later for sure) you could add them to your BotBase. Every time a message comes in they will get checked if they are one of them. Before start (and later for sure) you could add them to your BotBase. Every time a message comes in they will get checked if they are one of them.
If so, a special event Handler will get raised where you are easier able to manage the action behind. If so, a special event Handler will get raised where you are easier able to manage the action behind.
Below we have 4 options. Below we have 4 options.
@@ -173,13 +247,12 @@ Below we have 4 options.
``` ```
BotBase<Start> bb = new BotBase<Start>("{YOUR API KEY}"); BotBase<Start> bb = new BotBase<Start>("{YOUR API KEY}");
bb.SystemCalls.Add("/start"); bb.BotCommands.Add(new BotCommand() { Command = "start", Description = "Starts the bot" });
bb.SystemCalls.Add("/form1"); bb.BotCommands.Add(new BotCommand() { Command = "form1", Description = "Opens test form 1" });
bb.SystemCalls.Add("/form2"); bb.BotCommands.Add(new BotCommand() { Command = "form2", Description = "Opens test form 2" });
bb.BotCommands.Add(new BotCommand() { Command = "params", Description = "Returns all send parameters as a message." });
bb.SystemCalls.Add("/params"); bb.BotCommand += async (s, en) =>
bb.SystemCall += async (s, en) =>
{ {
switch (en.Command) switch (en.Command)
{ {
@@ -210,6 +283,9 @@ bb.SystemCall += async (s, en) =>
}; };
//Update Bot commands to botfather
bb.UploadBotCommands().Wait();
bb.Start(); bb.Start();
``` ```
@@ -219,7 +295,7 @@ On every input the user is sending back to the bot the Action event gets raised.
### Lets start with text messages (Example #1 - Simple Test) ### Lets start with text messages (Example #1 - Simple Test)
<img src="images/example1.PNG" />
``` ```
public class SimpleForm : AutoCleanForm public class SimpleForm : AutoCleanForm
@@ -274,6 +350,7 @@ public class SimpleForm : AutoCleanForm
I using a different base class (AutoCleanForm) I created for a better "feeling" inside the bot which will delete "old" messages from this form. You have some settings within this class to manage when messages should be getting deleted. I using a different base class (AutoCleanForm) I created for a better "feeling" inside the bot which will delete "old" messages from this form. You have some settings within this class to manage when messages should be getting deleted.
<img src="images/example2.PNG" />
``` ```
public class ButtonTestForm : AutoCleanForm public class ButtonTestForm : AutoCleanForm
@@ -351,6 +428,8 @@ public class ButtonTestForm : AutoCleanForm
btn.AddButtonRow(new ButtonBase("Button 3", new CallbackData("a", "button3").Serialize()), new ButtonBase("Button 4", new CallbackData("a", "button4").Serialize())); btn.AddButtonRow(new ButtonBase("Button 3", new CallbackData("a", "button3").Serialize()), new ButtonBase("Button 4", new CallbackData("a", "button4").Serialize()));
btn.AddButtonRow(new ButtonBase("Google.com", "google", "https://www.google.com"), new ButtonBase("Telegram", "telegram", "https://telegram.org/"));
btn.AddButtonRow(new ButtonBase("Back", new CallbackData("a", "back").Serialize())); btn.AddButtonRow(new ButtonBase("Back", new CallbackData("a", "back").Serialize()));
await this.Device.Send("Click a button", btn); await this.Device.Send("Click a button", btn);
@@ -366,6 +445,8 @@ public class ButtonTestForm : AutoCleanForm
Sometimes it makes sense to show the end user a type of progressbar or status. For this i tried to make a simple control, which is useful for some situations. Sometimes it makes sense to show the end user a type of progressbar or status. For this i tried to make a simple control, which is useful for some situations.
Maybe, if i got more ideas, i will add other "controls" in the future. Maybe, if i got more ideas, i will add other "controls" in the future.
<img src="images/example3.PNG" />
``` ```
public class ProgressTest : AutoCleanForm public class ProgressTest : AutoCleanForm
@@ -500,6 +581,14 @@ public override async Task Closed()
I read it a lot in different Telegram groups that some developers are searching for easy solutions to create context based apps. For this is my project an ideal solution here. I read it a lot in different Telegram groups that some developers are searching for easy solutions to create context based apps. For this is my project an ideal solution here.
To give you an example about the possiblities, i added into the Test project an example for a registration form. To give you an example about the possiblities, i added into the Test project an example for a registration form.
<img src="images/example4.1.PNG" />
<img src="images/example4.2.PNG" />
<img src="images/example4.3.PNG" />
<img src="images/example4.4.PNG" />
``` ```
public class PerForm : AutoCleanForm public class PerForm : AutoCleanForm
@@ -618,15 +707,24 @@ For now we have the following:
Makes sense to create a "feeling" of a clean environment for the user. For instance if you have a multilevel menu. This will remove the previously shown menu, and renders the new sub/top level. Makes sense to create a "feeling" of a clean environment for the user. For instance if you have a multilevel menu. This will remove the previously shown menu, and renders the new sub/top level.
- [PromptDialog](#prompt-dialog) - [PromptDialog](#prompt-dialog)
A simple dialog which will show a message and then waits for a text input (response).
- [ConfirmDialog](#confirm-dialog)
A simple dialog which is able to show multiple buttons and a Text message. The user could select one option and will get redirected to a different form, depending on the click. A simple dialog which is able to show multiple buttons and a Text message. The user could select one option and will get redirected to a different form, depending on the click.
### Alert Dialog ### Alert Dialog
<img src="images/alertdialog.PNG" />
``` ```
var fto = new TestForm2(); AlertDialog ad = new AlertDialog("This is a message", "Ok");
AlertDialog ad = new AlertDialog("This is a message", "Ok", fto); ad.ButtonClicked += async (s, en) =>
{
var fto = new TestForm2();
await this.NavigateTo(fto);
};
await this.NavigateTo(ad); await this.NavigateTo(ad);
@@ -635,47 +733,349 @@ await this.NavigateTo(ad);
### AutoCleanForm ### AutoCleanForm
No example yet
### Prompt Dialog ### Prompt Dialog
#### Without Eventhandler (pre-init of form necessary) <img src="images/promptdialog.PNG" />
``` ```
PromptDialog pd = new PromptDialog("Please confirm"); PromptDialog pd = new PromptDialog("Please tell me your name ?");
pd.AddButton(new ButtonBase("Ok", "ok")); pd.Completed += async (s, en) =>
pd.AddButton(new ButtonBase("Cancel", "cancel")); {
await this.Device.Send("Hello " + pd.Value);
};
var tf = new TestForm2(); await this.OpenModal(pd);
pd.ButtonForms.Add("ok", tf);
pd.ButtonForms.Add("cancel", tf);
await this.NavigateTo(pd);
``` ```
### Confirm Dialog
<img src="images/confirmdialog.PNG" />
#### With Eventhandler (no pre-init of form necessary)
``` ```
PromptDialog pd = new PromptDialog("Please confirm", new ButtonBase("Ok", "ok"), new ButtonBase("Cancel", "cancel")); ConfirmDialog cd = new ConfirmDialog("Please confirm", new ButtonBase("Ok", "ok"), new ButtonBase("Cancel", "cancel"));
pd.ButtonClicked += async (s, en) => cd.ButtonClicked += async (s, en) =>
{ {
var tf = new TestForm2(); var tf = new TestForm2();
//Remember only to navigate from the current running form. (here it is the prompt dialog, cause we have left the above already) //Remember only to navigate from the current running form. (here it is the prompt dialog, cause we have left the above already)
await pd.NavigateTo(tf); await cd.NavigateTo(tf);
}; };
await this.NavigateTo(pd); await this.NavigateTo(cd);
``` ```
## Controls
### Progress Bar
<img src="images/progressbar.PNG?raw=true" />
### Calendar Picker
<img src="images/calendarpicker.PNG?raw=true" />
<img src="images/calendarpicker.gif?raw=true" />
### Month Picker
<img src="images/monthpicker1.PNG?raw=true" />
<img src="images/monthpicker2.PNG?raw=true" />
### Tree View
<img src="images/treeview1.PNG?raw=true" />
<img src="images/treeview2.PNG?raw=true" />
<img src="images/treeview3.PNG?raw=true" />
<img src="images/treeview4.PNG?raw=true" />
### Toggle Button
<img src="images/togglebutton.gif?raw=true" />
### Button Grid
<img src="images/buttongrid.gif?raw=true" />
#### Paging & Searching
<img src="images/buttongrid_pagingfilter.gif?raw=true" />
## Groups
For working with groups, there are multiple different tools which helps to work with and allows bot also to manage "Single-User" chats and group chats.
### Splitter Form
An easy way to switch between a "Single-User" form and one for managing a group is the SplitterForm base class.
It calls special methods which you can override and then move from there to the form you need.
The OpenGroup method is the "backup" if OpenChannel or OpenSupergroup is not overridden. Same for Open, it is "backup" if none of the previous methods has been overridden.
```
public class Start : SplitterForm
{
public override async Task<bool> Open(MessageResult e)
{
var st = new Menu();
await this.NavigateTo(st);
return true;
}
public override async Task<bool> OpenGroup(MessageResult e)
{
var st = new Groups.LinkReplaceTest();
await this.NavigateTo(st);
return true;
}
public override Task<bool> OpenChannel(MessageResult e)
{
return base.OpenChannel(e);
}
public override Task<bool> OpenSupergroup(MessageResult e)
{
return base.OpenSupergroup(e);
}
}
```
### Group Form
For managing groups im introducing a new base class called "GroupForm". This one has special events which should make it easier to work with groups and channels.
In the Example project is a simple example for deleting a url written by a user and incrementing an internal counter. At every url he writes he got blocked for a small amount of time and the messages gots deleted. At 3 "failes" the user gets kicked of the group and blocked.
```
public class GroupForm : FormBase
{
public override async Task Load(MessageResult message)
{
switch (message.MessageType)
{
case Telegram.Bot.Types.Enums.MessageType.ChatMembersAdded:
await OnMemberChanges(new MemberChangeEventArgs(Telegram.Bot.Types.Enums.MessageType.ChatMembersAdded, message, message.RawMessageData.Message.NewChatMembers));
break;
case Telegram.Bot.Types.Enums.MessageType.ChatMemberLeft:
await OnMemberChanges(new MemberChangeEventArgs(Telegram.Bot.Types.Enums.MessageType.ChatMemberLeft, message, message.RawMessageData.Message.LeftChatMember));
break;
case Telegram.Bot.Types.Enums.MessageType.ChatPhotoChanged:
case Telegram.Bot.Types.Enums.MessageType.ChatPhotoDeleted:
case Telegram.Bot.Types.Enums.MessageType.ChatTitleChanged:
case Telegram.Bot.Types.Enums.MessageType.MigratedFromGroup:
case Telegram.Bot.Types.Enums.MessageType.MigratedToSupergroup:
case Telegram.Bot.Types.Enums.MessageType.MessagePinned:
case Telegram.Bot.Types.Enums.MessageType.GroupCreated:
case Telegram.Bot.Types.Enums.MessageType.SupergroupCreated:
await OnGroupChanged(new GroupChangedEventArgs(message.MessageType, message));
break;
default:
OnMessage(message);
break;
}
}
public virtual async Task OnMemberChanges(MemberChangeEventArgs e)
{
}
public virtual async Task OnGroupChanged(GroupChangedEventArgs e)
{
}
public virtual async Task OnMessage(MessageResult e)
{
}
}
```
## 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.
--- ---
I will add more notes to it soon, so stay tuned. I will add more notes to it soon, so stay tuned.
-93
View File
@@ -1,93 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{5817184C-0D59-4924-AC6C-6B943967811C}</ProjectGuid>
<OutputType>Exe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>TelegramBaseTest</RootNamespace>
<AssemblyName>TelegramBaseTest</AssemblyName>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="Newtonsoft.Json, Version=12.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.12.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Drawing" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
<Reference Include="Telegram.Bot, Version=14.10.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Telegram.Bot.14.10.0\lib\net45\Telegram.Bot.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Tests\ButtonTestForm.cs" />
<Compile Include="Tests\DataForm.cs" />
<Compile Include="Tests\ProgressTest.cs" />
<Compile Include="Tests\Register\PerForm.cs" />
<Compile Include="Tests\Register\PerStep.cs" />
<Compile Include="Tests\Register\Start.cs" />
<Compile Include="Tests\Register\Steps\Data.cs" />
<Compile Include="Tests\Register\Steps\Step3.cs" />
<Compile Include="Tests\Register\Steps\Step2.cs" />
<Compile Include="Tests\Register\Steps\Step1.cs" />
<Compile Include="Tests\SimpleForm.cs" />
<Compile Include="Tests\Start.cs" />
<Compile Include="Tests\TestForm.cs" />
<Compile Include="Tests\TestForm2.cs" />
</ItemGroup>
<ItemGroup>
<None Include="App.config">
<SubType>Designer</SubType>
</None>
<None Include="packages.config">
<SubType>Designer</SubType>
</None>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\TelegramBotBase\TelegramBotBase.csproj">
<Project>{a61eda27-cfdf-4bcb-b2d6-184f94904f2d}</Project>
<Name>TelegramBotBase</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>
-108
View File
@@ -1,108 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TelegramBotBase.Base;
using TelegramBotBase.Form;
namespace TelegramBaseTest.Tests
{
public class Start : FormBase
{
public override async Task Action(MessageResult message)
{
var call = message.GetData<CallbackData>();
await message.ConfirmAction();
if (call == null)
return;
switch(call.Value)
{
case "text":
var sf = new SimpleForm();
await this.NavigateTo(sf);
break;
case "buttons":
var bf = new ButtonTestForm();
await this.NavigateTo(bf);
break;
case "progress":
var pf = new ProgressTest();
await this.NavigateTo(pf);
break;
case "registration":
var reg = new Register.Start();
await this.NavigateTo(reg);
break;
case "form1":
var form1 = new TestForm();
await this.NavigateTo(form1);
break;
case "form2":
var form2 = new TestForm2();
await this.NavigateTo(form2);
break;
case "data":
message.Handled = true;
var data = new DataForm();
await this.NavigateTo(data);
break;
}
}
public override async Task Render(MessageResult message)
{
ButtonForm btn = new ButtonForm();
btn.AddButtonRow(new ButtonBase("#1 Simple Text", new CallbackData("a", "text").Serialize()), new ButtonBase("#2 Button Test", new CallbackData("a", "buttons").Serialize()));
btn.AddButtonRow(new ButtonBase("#3 Progress Bar", new CallbackData("a", "progress").Serialize()));
btn.AddButtonRow(new ButtonBase("#4 Registration Example", new CallbackData("a", "registration").Serialize()));
btn.AddButtonRow(new ButtonBase("#5 Form1 Command", new CallbackData("a", "form1").Serialize()));
btn.AddButtonRow(new ButtonBase("#6 Form2 Command", new CallbackData("a", "form2").Serialize()));
btn.AddButtonRow(new ButtonBase("#7 Data Handling", new CallbackData("a", "data").Serialize()));
await this.Device.Send("Choose your test:", btn);
}
}
}
@@ -6,12 +6,12 @@ using System.Threading.Tasks;
using Telegram.Bot.Types; using Telegram.Bot.Types;
using TelegramBotBase.Sessions; using TelegramBotBase.Sessions;
namespace TelegramBotBase.Base 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.Base
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;
@@ -5,7 +5,7 @@ using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using TelegramBotBase.Form; using TelegramBotBase.Form;
namespace TelegramBotBase.Base namespace TelegramBotBase.Args
{ {
/// <summary> /// <summary>
/// Button get clicked event /// Button get clicked event
@@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.Text;
using Telegram.Bot.Types.Enums;
using TelegramBotBase.Base;
namespace TelegramBotBase.Args
{
public class GroupChangedEventArgs : EventArgs
{
public MessageType Type { get; set; }
public MessageResult OriginalMessage { get; set; }
public GroupChangedEventArgs(MessageType type, MessageResult message)
{
this.Type = type;
this.OriginalMessage = message;
}
}
}
+18
View File
@@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TelegramBotBase.Args
{
public class InitEventArgs : EventArgs
{
public object[] Args { get; set; }
public InitEventArgs(params object[] args)
{
this.Args = args;
}
}
}
@@ -0,0 +1,58 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace TelegramBotBase.Args
{
public class LoadStateEventArgs
{
public Dictionary<String,object> Values { get; set; }
public LoadStateEventArgs()
{
Values = new Dictionary<string, object>();
}
public List<String> Keys
{
get
{
return Values.Keys.ToList();
}
}
public String Get(String key)
{
return Values[key].ToString();
}
public int GetInt(String key)
{
int 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)
{
bool b = false;
if (bool.TryParse(Values[key].ToString(), out b))
return b;
return false;
}
}
}
@@ -0,0 +1,37 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Telegram.Bot.Types;
using Telegram.Bot.Types.Enums;
using TelegramBotBase.Base;
namespace TelegramBotBase.Args
{
public class MemberChangeEventArgs : EventArgs
{
public List<User> Members { get; set; }
public MessageType Type { get; set; }
public MessageResult Result { get; set; }
public MemberChangeEventArgs()
{
this.Members = new List<User>();
}
public MemberChangeEventArgs(MessageType type, MessageResult result, params User[] members)
{
this.Type = type;
this.Result = result;
this.Members = members.ToList();
}
}
}
@@ -7,7 +7,7 @@ using TelegramBotBase.Sessions;
namespace TelegramBotBase.Base namespace TelegramBotBase.Base
{ {
public class MessageIncomeResult : EventArgs public class MessageIncomeEventArgs : EventArgs
{ {
public long DeviceId { get; set; } public long DeviceId { get; set; }
@@ -16,7 +16,7 @@ namespace TelegramBotBase.Base
public MessageResult Message { get; set; } public MessageResult Message { get; set; }
public MessageIncomeResult(long DeviceId, DeviceSession Device, MessageResult message) public MessageIncomeEventArgs(long DeviceId, DeviceSession Device, MessageResult message)
{ {
this.DeviceId = DeviceId; this.DeviceId = DeviceId;
this.Device = Device; this.Device = Device;
@@ -5,7 +5,7 @@ using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Telegram.Bot.Types; using Telegram.Bot.Types;
namespace TelegramBotBase.Base namespace TelegramBotBase.Args
{ {
public class MessageReceivedEventArgs public class MessageReceivedEventArgs
{ {
@@ -5,7 +5,7 @@ using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Telegram.Bot.Types; using Telegram.Bot.Types;
namespace TelegramBotBase.Base namespace TelegramBotBase.Args
{ {
public class MessageSentEventArgs public class MessageSentEventArgs
{ {
@@ -0,0 +1,37 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace TelegramBotBase.Args
{
public class 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;
}
}
}
@@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using TelegramBotBase.Base;
using TelegramBotBase.Sessions;
namespace TelegramBotBase.Args
{
public class SaveStatesEventArgs
{
public StateContainer States { get; set; }
public SaveStatesEventArgs(StateContainer states)
{
this.States = states;
}
}
}
@@ -7,13 +7,13 @@ using TelegramBotBase.Sessions;
namespace TelegramBotBase.Base namespace TelegramBotBase.Base
{ {
public class SessionBeginResult : EventArgs public class SessionBeginEventArgs : EventArgs
{ {
public long DeviceId { get; set; } public long DeviceId { get; set; }
public DeviceSession Device { get; set; } public DeviceSession Device { get; set; }
public SessionBeginResult(long DeviceId, DeviceSession Device) public SessionBeginEventArgs(long DeviceId, DeviceSession Device)
{ {
this.DeviceId = DeviceId; this.DeviceId = DeviceId;
this.Device = Device; this.Device = Device;
@@ -5,7 +5,7 @@ using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using TelegramBotBase.Sessions; using TelegramBotBase.Sessions;
namespace TelegramBotBase.Base namespace TelegramBotBase.Args
{ {
public class SystemExceptionEventArgs : EventArgs public class SystemExceptionEventArgs : EventArgs
{ {
@@ -6,7 +6,7 @@ using System.Threading.Tasks;
using Telegram.Bot.Types; using Telegram.Bot.Types;
using TelegramBotBase.Sessions; using TelegramBotBase.Sessions;
namespace TelegramBotBase.Base namespace TelegramBotBase.Args
{ {
public class UnhandledCallEventArgs : EventArgs public class UnhandledCallEventArgs : EventArgs
{ {
+17
View File
@@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace TelegramBotBase.Attributes
{
/// <summary>
/// Declares that this class should not be getting serialized
/// </summary>
[AttributeUsage(AttributeTargets.Class)]
public class IgnoreState : Attribute
{
}
}
+15
View File
@@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Text;
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; }
}
}
+25
View File
@@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TelegramBotBase.Base
{
public static class Async
{
public delegate Task AsyncEventHandler<TEventArgs>(object sender, TEventArgs e) where TEventArgs : EventArgs;
public static IEnumerable<AsyncEventHandler<TEventArgs>> GetHandlers<TEventArgs>(
this AsyncEventHandler<TEventArgs> handler)
where TEventArgs : EventArgs
=> handler.GetInvocationList().Cast<AsyncEventHandler<TEventArgs>>();
public static Task InvokeAllAsync<TEventArgs>(this AsyncEventHandler<TEventArgs> handler, object sender, TEventArgs e)
where TEventArgs : EventArgs
=> Task.WhenAll(
handler.GetHandlers()
.Select(handleAsync => handleAsync(sender, e)));
}
}
+42 -1
View File
@@ -13,8 +13,49 @@ namespace TelegramBotBase.Base
{ {
public Sessions.DeviceSession Device { get; set; } public Sessions.DeviceSession Device { get; set; }
public virtual async Task Render() public int ID { get; set; }
public String ControlID
{ {
get
{
return "#c" + this.ID.ToString();
}
}
/// <summary>
/// Defines if the control should be rendered and invoked with actions
/// </summary>
public bool Enabled { get; set; } = true;
public virtual async Task Load(MessageResult result)
{
}
public virtual async Task Action(MessageResult result, String value = null)
{
}
public virtual async Task Render(MessageResult result)
{
}
public virtual async Task Hidden(bool FormClose)
{
} }
+47 -1
View File
@@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
@@ -97,7 +98,7 @@ namespace TelegramBotBase.Base
this.RawMessageData = message.RawMessageData; this.RawMessageData = message.RawMessageData;
this.Message = message.Message; this.Message = message.Message;
this.Client = message.Client;
} }
public async Task<InputOnlineFile> DownloadDocument() public async Task<InputOnlineFile> DownloadDocument()
@@ -108,6 +109,15 @@ namespace TelegramBotBase.Base
return new InputOnlineFile(encryptedContent, this.Document.FileName); return new InputOnlineFile(encryptedContent, this.Document.FileName);
} }
public async Task DownloadDocument(String path)
{
var file = await this.Client.TelegramClient.GetFileAsync(this.Document.FileId);
FileStream fs = new FileStream(path, FileMode.Create);
await this.Client.TelegramClient.DownloadFileAsync(file.FilePath, fs);
fs.Close();
fs.Dispose();
}
public async Task<InputOnlineFile> DownloadVideo() public async Task<InputOnlineFile> DownloadVideo()
{ {
var encryptedContent = new System.IO.MemoryStream(this.Video.FileSize); var encryptedContent = new System.IO.MemoryStream(this.Video.FileSize);
@@ -116,6 +126,32 @@ namespace TelegramBotBase.Base
return new InputOnlineFile(encryptedContent, ""); return new InputOnlineFile(encryptedContent, "");
} }
public async Task DownloadVideo(String path)
{
var file = await this.Client.TelegramClient.GetFileAsync(this.Video.FileId);
FileStream fs = new FileStream(path, FileMode.Create);
await this.Client.TelegramClient.DownloadFileAsync(file.FilePath, fs);
fs.Close();
fs.Dispose();
}
public async Task<InputOnlineFile> DownloadAudio()
{
var encryptedContent = new System.IO.MemoryStream(this.Audio.FileSize);
var file = await this.Client.TelegramClient.GetInfoAndDownloadFileAsync(this.Audio.FileId, encryptedContent);
return new InputOnlineFile(encryptedContent, "");
}
public async Task DownloadAudio(String path)
{
var file = await this.Client.TelegramClient.GetFileAsync(this.Audio.FileId);
FileStream fs = new FileStream(path, FileMode.Create);
await this.Client.TelegramClient.DownloadFileAsync(file.FilePath, fs);
fs.Close();
fs.Dispose();
}
public async Task<InputOnlineFile> DownloadPhoto(int index) public async Task<InputOnlineFile> DownloadPhoto(int index)
{ {
var photo = this.Photos[index]; var photo = this.Photos[index];
@@ -125,5 +161,15 @@ namespace TelegramBotBase.Base
return new InputOnlineFile(encryptedContent, ""); return new InputOnlineFile(encryptedContent, "");
} }
public async Task DownloadPhoto(int index, String path)
{
var photo = this.Photos[index];
var file = await this.Client.TelegramClient.GetFileAsync(photo.FileId);
FileStream fs = new FileStream(path, FileMode.Create);
await this.Client.TelegramClient.DownloadFileAsync(file.FilePath, fs);
fs.Close();
fs.Dispose();
}
} }
} }
+306 -15
View File
@@ -1,10 +1,13 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using TelegramBotBase.Args;
using TelegramBotBase.Base; using TelegramBotBase.Base;
using TelegramBotBase.Sessions; using TelegramBotBase.Sessions;
using static TelegramBotBase.Base.Async;
namespace TelegramBotBase.Form namespace TelegramBotBase.Form
{ {
@@ -13,12 +16,11 @@ namespace TelegramBotBase.Form
/// </summary> /// </summary>
public class FormBase : IDisposable public class FormBase : IDisposable
{ {
public DeviceSession Device { get; set; } public DeviceSession Device { get; set; }
public MessageClient Client { get; set; } public MessageClient Client { get; set; }
public bool CustomEventManagement { get; set; } = false;
/// <summary> /// <summary>
/// has this formular already been disposed ? /// has this formular already been disposed ?
/// </summary> /// </summary>
@@ -26,6 +28,17 @@ namespace TelegramBotBase.Form
public List<ControlBase> Controls { get; set; } public List<ControlBase> Controls { get; set; }
public EventHandlerList Events = new EventHandlerList();
private static object __evInit = new object();
private static object __evOpened = new object();
private static object __evClosed = new object();
public FormBase() public FormBase()
{ {
this.Controls = new List<Base.ControlBase>(); this.Controls = new List<Base.ControlBase>();
@@ -36,28 +49,115 @@ namespace TelegramBotBase.Form
this.Client = Client; this.Client = Client;
} }
/// <summary>
/// Will get called at the initialization (once per context)
/// </summary>
public virtual async Task Init(params object[] args)
{
public async Task OnInit(InitEventArgs e)
{
if (this.Events[__evInit] == null)
return;
var handler = this.Events[__evInit].GetInvocationList().Cast<AsyncEventHandler<InitEventArgs>>();
foreach (var h in handler)
{
await Async.InvokeAllAsync<InitEventArgs>(h, this, e);
}
}
///// <summary>
///// Will get called at the initialization (once per context)
///// </summary>
public event AsyncEventHandler<InitEventArgs> Init
{
add
{
this.Events.AddHandler(__evInit, value);
}
remove
{
this.Events.RemoveHandler(__evInit, value);
}
}
public async Task OnOpened(EventArgs e)
{
if (this.Events[__evOpened] == null)
return;
var handler = this.Events[__evOpened].GetInvocationList().Cast<AsyncEventHandler<EventArgs>>();
foreach (var h in handler)
{
await Async.InvokeAllAsync<EventArgs>(h, this, e);
}
} }
/// <summary> /// <summary>
/// Gets invoked if gets navigated to this form /// Gets invoked if gets navigated to this form
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
public virtual async Task Opened() public event AsyncEventHandler<EventArgs> Opened
{
add
{
this.Events.AddHandler(__evOpened, value);
}
remove
{
this.Events.RemoveHandler(__evOpened, value);
}
}
public async Task OnClosed(EventArgs e)
{
if (this.Events[__evClosed] == null)
return;
var handler = this.Events[__evClosed].GetInvocationList().Cast<AsyncEventHandler<EventArgs>>();
foreach (var h in handler)
{
await Async.InvokeAllAsync<EventArgs>(h, this, e);
}
}
/// <summary>
/// Form has been closed (left)
/// </summary>
/// <returns></returns>
public event AsyncEventHandler<EventArgs> Closed
{
add
{
this.Events.AddHandler(__evClosed, value);
}
remove
{
this.Events.RemoveHandler(__evClosed, value);
}
}
/// <summary>
/// Get invoked when a modal child from has been closed.
/// </summary>
/// <param name="message"></param>
/// <returns></returns>
public virtual async Task ReturnFromModal(ModalDialog modal)
{ {
} }
public virtual async Task Closed()
/// <summary>
/// Pre to form close, cleanup all controls
/// </summary>
/// <returns></returns>
public async Task CloseControls()
{ {
foreach (var b in this.Controls) foreach (var b in this.Controls)
{ {
await b.Cleanup(); b.Cleanup().Wait();
} }
} }
@@ -66,6 +166,33 @@ namespace TelegramBotBase.Form
} }
/// <summary>
/// Gets invoked if a message was sent or an action triggered
/// </summary>
/// <param name="message"></param>
/// <returns></returns>
public 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 = this.Controls.FirstOrDefault(a => a.ControlID == message.RawData.Split('_')[0]);
if (c != null)
{
await c.Load(message);
return;
}
}
foreach (var b in this.Controls)
{
if (!b.Enabled)
continue;
await b.Load(message);
}
}
/// <summary> /// <summary>
/// Gets invoked if the form gets loaded and on every message belongs to this context /// Gets invoked if the form gets loaded and on every message belongs to this context
/// </summary> /// </summary>
@@ -76,6 +203,47 @@ namespace TelegramBotBase.Form
} }
/// <summary>
/// Gets invoked, when a messages has been edited.
/// </summary>
/// <param name="message"></param>
/// <returns></returns>
public virtual async Task Edited(MessageResult message)
{
}
/// <summary>
/// Gets invoked if the user clicked a button.
/// </summary>
/// <param name="message"></param>
/// <returns></returns>
public 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 = this.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 this.Controls)
{
if (!b.Enabled)
continue;
await b.Action(message);
if (message.Handled)
return;
}
}
/// <summary> /// <summary>
/// Gets invoked if the user has clicked a button. /// Gets invoked if the user has clicked a button.
/// </summary> /// </summary>
@@ -96,6 +264,22 @@ namespace TelegramBotBase.Form
} }
/// <summary>
/// Gets invoked at the end of the cycle to "Render" text, images, buttons, etc...
/// </summary>
/// <param name="message"></param>
/// <returns></returns>
public async Task RenderControls(MessageResult message)
{
foreach (var b in this.Controls)
{
if (!b.Enabled)
continue;
await b.Render(message);
}
}
/// <summary> /// <summary>
/// Gets invoked at the end of the cycle to "Render" text, images, buttons, etc... /// Gets invoked at the end of the cycle to "Render" text, images, buttons, etc...
/// </summary> /// </summary>
@@ -106,12 +290,14 @@ namespace TelegramBotBase.Form
} }
/// <summary> /// <summary>
/// Navigates to a new form /// Navigates to a new form
/// </summary> /// </summary>
/// <param name="newForm"></param> /// <param name="newForm"></param>
/// <returns></returns> /// <returns></returns>
public async Task NavigateTo(FormBase newForm, params object[] args) public virtual async Task NavigateTo(FormBase newForm, params object[] args)
{ {
DeviceSession ds = this.Device; DeviceSession ds = this.Device;
if (ds == null) if (ds == null)
@@ -119,17 +305,122 @@ namespace TelegramBotBase.Form
ds.FormSwitched = true; ds.FormSwitched = true;
ds.PreviousForm = ds.ActiveForm;
ds.ActiveForm = newForm; ds.ActiveForm = newForm;
newForm.Client = this.Client; newForm.Client = this.Client;
newForm.Device = ds; newForm.Device = ds;
ds.PreviousForm = this; //Notify prior to close
foreach (var b in this.Controls)
{
if (!b.Enabled)
continue;
await newForm.Init(args); await b.Hidden(true);
}
await this.Closed(); this.CloseControls().Wait();
await newForm.Opened(); await this.OnClosed(new EventArgs());
await newForm.OnInit(new InitEventArgs(args));
await newForm.OnOpened(new EventArgs());
}
/// <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)
{
DeviceSession ds = this.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 this.Controls)
{
if (!b.Enabled)
continue;
await b.Hidden(false);
}
await newForm.OnInit(new InitEventArgs(args));
await newForm.OnOpened(new EventArgs());
}
public async Task CloseModal(ModalDialog modalForm, FormBase oldForm)
{
DeviceSession ds = this.Device;
if (ds == null)
return;
if (modalForm == null)
throw new Exception("No modal form");
ds.FormSwitched = true;
ds.PreviousForm = ds.ActiveForm;
ds.ActiveForm = oldForm;
}
/// <summary>
/// Adds a control to the formular and sets its ID and Device.
/// </summary>
/// <param name="control"></param>
public void AddControl(ControlBase control)
{
control.ID = this.Controls.Count + 1;
control.Device = this.Device;
this.Controls.Add(control);
}
/// <summary>
/// Removes control from the formular and runs a cleanup on it.
/// </summary>
/// <param name="control"></param>
public void RemoveControl(ControlBase control)
{
if (!this.Controls.Contains(control))
return;
control.Cleanup().Wait();
this.Controls.Remove(control);
}
/// <summary>
/// Removes all controls.
/// </summary>
public void RemoveAllControls()
{
foreach(var c in this.Controls)
{
c.Cleanup().Wait();
this.Controls.Remove(c);
}
} }
/// <summary> /// <summary>
+62 -3
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; }
@@ -23,6 +25,8 @@ namespace TelegramBotBase.Base
private static object __evOnMessage = new object(); private static object __evOnMessage = new object();
private static object __evOnMessageEdit = new object();
private static object __evCallbackQuery = new object(); private static object __evCallbackQuery = new object();
@@ -84,13 +88,11 @@ namespace TelegramBotBase.Base
{ {
this.TelegramClient.Timeout = new TimeSpan(0, 0, 30); this.TelegramClient.Timeout = new TimeSpan(0, 0, 30);
this.TelegramClient.OnMessage += TelegramClient_OnMessage; this.TelegramClient.OnMessage += TelegramClient_OnMessage;
this.TelegramClient.OnMessageEdited += TelegramClient_OnMessageEdited;
this.TelegramClient.OnCallbackQuery += TelegramClient_OnCallbackQuery; this.TelegramClient.OnCallbackQuery += TelegramClient_OnCallbackQuery;
} }
private async void TelegramClient_OnMessage(object sender, Telegram.Bot.Args.MessageEventArgs e) private async void TelegramClient_OnMessage(object sender, Telegram.Bot.Args.MessageEventArgs e)
{ {
//Skip empty messages by default //Skip empty messages by default
@@ -109,6 +111,25 @@ namespace TelegramBotBase.Base
} }
} }
private async void TelegramClient_OnMessageEdited(object sender, Telegram.Bot.Args.MessageEventArgs e)
{
//Skip empty messages by default
if (e.Message == null)
return;
try
{
var mr = new MessageResult(e);
mr.Client = this;
OnMessageEdit(mr);
}
catch
{
}
}
private async void TelegramClient_OnCallbackQuery(object sender, Telegram.Bot.Args.CallbackQueryEventArgs e) private async void TelegramClient_OnCallbackQuery(object sender, Telegram.Bot.Args.CallbackQueryEventArgs e)
{ {
try try
@@ -123,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"
@@ -143,6 +185,23 @@ namespace TelegramBotBase.Base
(this.__Events[__evOnMessage] as EventHandler<MessageResult>)?.Invoke(this, result); (this.__Events[__evOnMessage] as EventHandler<MessageResult>)?.Invoke(this, result);
} }
public event EventHandler<MessageResult> MessageEdit
{
add
{
this.__Events.AddHandler(__evOnMessageEdit, value);
}
remove
{
this.__Events.RemoveHandler(__evOnMessageEdit, value);
}
}
public void OnMessageEdit(MessageResult result)
{
(this.__Events[__evOnMessageEdit] as EventHandler<MessageResult>)?.Invoke(this, result);
}
public event EventHandler<MessageResult> Action public event EventHandler<MessageResult> Action
{ {
add add
+20 -7
View File
@@ -54,6 +54,14 @@ namespace TelegramBotBase.Base
} }
} }
public Telegram.Bot.Types.Enums.MessageType MessageType
{
get
{
return this.RawMessageData?.Message?.Type ?? Telegram.Bot.Types.Enums.MessageType.Unknown;
}
}
/// <summary> /// <summary>
/// Is this an action ? (i.e. button click) /// Is this an action ? (i.e. button click)
/// </summary> /// </summary>
@@ -66,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
{ {
@@ -79,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
@@ -92,19 +100,24 @@ 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];
} }
} }
/// <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 bool Handled { get; set; } = false;
public String RawData public String RawData
+1 -1
View File
@@ -6,7 +6,7 @@ using System.Threading.Tasks;
namespace TelegramBotBase.Base namespace TelegramBotBase.Base
{ {
public class ResultBase public class ResultBase : EventArgs
{ {
public MessageClient Client { get; set; } public MessageClient Client { get; set; }
+34
View File
@@ -0,0 +1,34 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace TelegramBotBase.Base
{
public partial class 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()
{
this.States = new List<StateEntry>();
}
}
}
+44
View File
@@ -0,0 +1,44 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.Serialization;
using System.Text;
namespace TelegramBotBase.Base
{
[DebuggerDisplay("Device: {DeviceId}, {FormUri}")]
public class 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()
{
this.Values = new Dictionary<string, object>();
}
}
}
+227 -77
View File
@@ -5,8 +5,13 @@ 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.Attributes;
using TelegramBotBase.Base; using TelegramBotBase.Base;
using TelegramBotBase.Enums;
using TelegramBotBase.Form; using TelegramBotBase.Form;
using TelegramBotBase.Interfaces;
using TelegramBotBase.Sessions; using TelegramBotBase.Sessions;
namespace TelegramBotBase namespace TelegramBotBase
@@ -33,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();
@@ -43,41 +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> /// <summary>
/// Skips all messages during running (good for big delay updates) /// Enable the SessionState (you need to implement on call forms the IStateForm interface)
/// </summary> /// </summary>
public bool SkipAllMessages { get; set; } = false; public IStateMachine StateMachine { get; set; }
/// <summary>
/// Loggs all messages and sent them to the event handler
/// </summary>
public bool LogAllMessages { get; set; } = false;
/// <summary> public Dictionary<eSettings, uint> SystemSettings { get; private set; }
/// How often could a form navigate to another (within one user action/call/message)
/// </summary> private BotBase()
private const int NavigationMaximum = 10; {
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.SystemCalls = new List<string>();
this.Sessions = new SessionBase();
this.Sessions.Client = this.Client; this.Sessions.Client = this.Client;
} }
@@ -86,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;
} }
@@ -103,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;
} }
@@ -120,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;
} }
@@ -140,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;
} }
@@ -161,12 +159,27 @@ namespace TelegramBotBase
return; return;
this.Client.Message += Client_Message; this.Client.Message += Client_Message;
this.Client.MessageEdit += Client_MessageEdit;
this.Client.Action += Client_Action; this.Client.Action += Client_Action;
if (this.StateMachine != null)
{
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();
} }
/// <summary> /// <summary>
/// Stop your Bot /// Stop your Bot
/// </summary> /// </summary>
@@ -175,7 +188,15 @@ namespace TelegramBotBase
if (this.Client == null) if (this.Client == null)
return; return;
this.Client.Message -= Client_Message;
this.Client.Action -= Client_Action;
this.Client.TelegramClient.StopReceiving(); this.Client.TelegramClient.StopReceiving();
if (this.StateMachine != null)
{
this.Sessions.SaveSessionStates(this.StateMachine);
}
} }
/// <summary> /// <summary>
@@ -194,9 +215,9 @@ namespace TelegramBotBase
} }
} }
private 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
@@ -204,14 +225,14 @@ 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 MessageIncomeResult(e.DeviceId, ds, e)); OnMessage(new MessageIncomeEventArgs(e.DeviceId, ds, e));
} }
ds?.OnMessageReceived(new MessageReceivedEventArgs(e.Message)); ds?.OnMessageReceived(new MessageReceivedEventArgs(e.Message));
Client_TryMessage(sender, e); await Client_TryMessage(sender, e);
} }
catch (Telegram.Bot.Exceptions.ApiRequestException ex) catch (Telegram.Bot.Exceptions.ApiRequestException ex)
{ {
@@ -224,7 +245,7 @@ namespace TelegramBotBase
} }
} }
private async void Client_TryMessage(object sender, MessageResult e) private async Task Client_TryMessage(object sender, MessageResult e)
{ {
DeviceSession ds = e.Device; DeviceSession ds = e.Device;
if (ds == null) if (ds == null)
@@ -234,17 +255,17 @@ namespace TelegramBotBase
ds.LastMessage = e.Message; ds.LastMessage = e.Message;
OnSessionBegins(new SessionBeginResult(e.DeviceId, ds)); OnSessionBegins(new SessionBeginEventArgs(e.DeviceId, ds));
} }
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;
@@ -264,13 +285,12 @@ namespace TelegramBotBase
activeForm = ds.ActiveForm; activeForm = ds.ActiveForm;
//If the form manages the events by itselfs, skip here
if (activeForm.CustomEventManagement)
return;
//Pre Loading Event //Pre Loading Event
await activeForm.PreLoad(e); await activeForm.PreLoad(e);
//Send Load event to controls
await activeForm.LoadControls(e);
//Loading Event //Loading Event
await activeForm.Load(e); await activeForm.Load(e);
@@ -283,9 +303,70 @@ namespace TelegramBotBase
//Render Event //Render Event
if (!ds.FormSwitched) if (!ds.FormSwitched)
await activeForm.Render(e); {
await activeForm.RenderControls(e);
} while (ds.FormSwitched && i < NavigationMaximum); await activeForm.Render(e);
}
e.IsFirstHandler = false;
} while (ds.FormSwitched && i < this.GetSetting(eSettings.NavigationMaximum, 10));
}
private async void Client_MessageEdit(object sender, MessageResult e)
{
if (this.GetSetting(eSettings.SkipAllMessages, false))
return;
try
{
DeviceSession ds = this.Sessions.GetSession(e.DeviceId);
e.Device = ds;
if (this.GetSetting(eSettings.LogAllMessages, false))
{
OnMessage(new MessageIncomeEventArgs(e.DeviceId, ds, e));
}
//Call same, to handle received liked edited
ds?.OnMessageReceived(new MessageReceivedEventArgs(e.Message));
await Client_TryMessageEdit(sender, e);
}
catch (Telegram.Bot.Exceptions.ApiRequestException ex)
{
}
catch (Exception ex)
{
DeviceSession ds = this.Sessions.GetSession(e.DeviceId);
OnException(new SystemExceptionEventArgs(e.Message.Text, ds?.DeviceId ?? -1, ds, ex));
}
}
private async Task Client_TryMessageEdit(object sender, MessageResult e)
{
DeviceSession ds = e.Device;
if (ds == null)
{
ds = await this.Sessions.StartSession<T>(e.DeviceId);
e.Device = ds;
}
ds.LastAction = DateTime.Now;
ds.LastMessage = e.Message;
//Pre Loading Event
await ds.ActiveForm.Edited(e);
//When form has been switched due navigation within the edit method, reopen Client_Message
if (ds.FormSwitched)
{
await Client_TryMessage(sender, e);
}
} }
@@ -296,9 +377,9 @@ 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 MessageIncomeResult(e.DeviceId, ds, e)); OnMessage(new MessageIncomeEventArgs(e.DeviceId, ds, e));
} }
Client_TryAction(sender, e); Client_TryAction(sender, e);
@@ -319,7 +400,6 @@ namespace TelegramBotBase
e.Device = ds; e.Device = ds;
} }
ds.LastAction = DateTime.Now; ds.LastAction = DateTime.Now;
ds.LastMessage = e.Message; ds.LastMessage = e.Message;
@@ -337,19 +417,22 @@ namespace TelegramBotBase
activeForm = ds.ActiveForm; activeForm = ds.ActiveForm;
//If the form manages the events by itselfs, skip here
if (activeForm.CustomEventManagement)
return;
//Pre Loading Event //Pre Loading Event
await activeForm.PreLoad(e); await activeForm.PreLoad(e);
//Send Load event to controls
await activeForm.LoadControls(e);
//Loading Event //Loading Event
await activeForm.Load(e); await activeForm.Load(e);
//Action Event //Action Event
if (!ds.FormSwitched) if (!ds.FormSwitched)
{ {
//Send Action event to controls
await activeForm.ActionControls(e);
//Send Action event to form itself
await activeForm.Action(e); await activeForm.Action(e);
if (!e.Handled) if (!e.Handled)
@@ -374,17 +457,81 @@ namespace TelegramBotBase
//Render Event //Render Event
if (!ds.FormSwitched) if (!ds.FormSwitched)
await activeForm.Render(e); {
await activeForm.RenderControls(e);
} while (ds.FormSwitched && i < NavigationMaximum); await activeForm.Render(e);
}
e.IsFirstHandler = false;
} 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>
public event EventHandler<SessionBeginResult> SessionBegins public event EventHandler<SessionBeginEventArgs> SessionBegins
{ {
add add
{ {
@@ -396,16 +543,16 @@ namespace TelegramBotBase
} }
} }
public void OnSessionBegins(SessionBeginResult e) public void OnSessionBegins(SessionBeginEventArgs e)
{ {
(this.__Events[__evSessionBegins] as EventHandler<SessionBeginResult>)?.Invoke(this, e); (this.__Events[__evSessionBegins] as EventHandler<SessionBeginEventArgs>)?.Invoke(this, e);
} }
/// <summary> /// <summary>
/// Will be called on incomming message /// Will be called on incomming message
/// </summary> /// </summary>
public event EventHandler<MessageIncomeResult> Message public event EventHandler<MessageIncomeEventArgs> Message
{ {
add add
{ {
@@ -417,22 +564,22 @@ namespace TelegramBotBase
} }
} }
public void OnMessage(MessageIncomeResult e) public void OnMessage(MessageIncomeEventArgs e)
{ {
(this.__Events[__evMessage] as EventHandler<MessageIncomeResult>)?.Invoke(this, e); (this.__Events[__evMessage] as EventHandler<MessageIncomeEventArgs>)?.Invoke(this, e);
} }
/// <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>
@@ -476,5 +623,8 @@ namespace TelegramBotBase
(this.__Events[__evUnhandledCall] as EventHandler<UnhandledCallEventArgs>)?.Invoke(this, e); (this.__Events[__evUnhandledCall] as EventHandler<UnhandledCallEventArgs>)?.Invoke(this, e);
} }
#endregion
} }
} }
+25
View File
@@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TelegramBotBase.Constants
{
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;
public const int MaxInlineKeyBoardRows = 13;
public const int MaxInlineKeyBoardCols = 8;
public const int MaxReplyKeyboardRows = 25;
public const int MaxReplyKeyboardCols = 12;
}
}
+554
View File
@@ -0,0 +1,554 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics.SymbolStore;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Telegram.Bot.Types;
using Telegram.Bot.Types.Enums;
using Telegram.Bot.Types.ReplyMarkups;
using TelegramBotBase.Args;
using TelegramBotBase.Base;
using TelegramBotBase.Enums;
using TelegramBotBase.Exceptions;
using TelegramBotBase.Form;
using static TelegramBotBase.Base.Async;
namespace TelegramBotBase.Controls
{
public class ButtonGrid : Base.ControlBase
{
public String Title { get; set; } = Localizations.Default.Language["ButtonGrid_Title"];
public String ConfirmationText { get; set; } = "";
private bool RenderNecessary = true;
private static readonly object __evButtonClicked = new object();
private readonly EventHandlerList Events = new EventHandlerList();
public ButtonForm ButtonsForm { get; set; }
public int? MessageId { get; set; }
/// <summary>
/// Optional. Requests clients to resize the keyboard vertically for optimal fit (e.g., make the keyboard smaller if there are just two rows of buttons). Defaults to false, in which case the custom keyboard is always of the same height as the app's standard keyboard.
/// Source: https://core.telegram.org/bots/api#replykeyboardmarkup
/// </summary>
public bool ResizeKeyboard { get; set; } = false;
public bool OneTimeKeyboard { get; set; } = false;
public bool HideKeyboardOnCleanup { get; set; } = true;
public bool DeletePreviousMessage { get; set; } = true;
/// <summary>
/// Parsemode of the message.
/// </summary>
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>
/// Enabled a search function.
/// </summary>
public bool EnableSearch { get; set; } = false;
public String SearchQuery { get; set; }
/// <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"];
public String SearchLabel = Localizations.Default.Language["ButtonGrid_SearchFeature"];
/// <summary>
/// Layout of the buttons which should be displayed always on top.
/// </summary>
public List<ButtonBase> HeadLayoutButtonRow { get; set; }
/// <summary>
/// Defines which type of Button Keyboard should be rendered.
/// </summary>
public eKeyboardType KeyboardType
{
get
{
return m_eKeyboardType;
}
set
{
if (m_eKeyboardType != value)
{
this.RenderNecessary = true;
Cleanup().Wait();
m_eKeyboardType = value;
}
}
}
private eKeyboardType m_eKeyboardType = eKeyboardType.ReplyKeyboard;
public ButtonGrid()
{
this.ButtonsForm = new ButtonForm();
}
public ButtonGrid(eKeyboardType type) : this()
{
m_eKeyboardType = type;
}
public ButtonGrid(ButtonForm form)
{
this.ButtonsForm = form;
}
public event AsyncEventHandler<ButtonClickedEventArgs> ButtonClicked
{
add
{
this.Events.AddHandler(__evButtonClicked, value);
}
remove
{
this.Events.RemoveHandler(__evButtonClicked, value);
}
}
public async Task OnButtonClicked(ButtonClickedEventArgs e)
{
var handler = this.Events[__evButtonClicked].GetInvocationList().Cast<AsyncEventHandler<ButtonClickedEventArgs>>();
foreach (var h in handler)
{
await Async.InvokeAllAsync<ButtonClickedEventArgs>(h, this, e);
}
}
public async override Task Load(MessageResult result)
{
if (this.KeyboardType != eKeyboardType.ReplyKeyboard)
return;
if (!result.IsFirstHandler)
return;
var button = HeadLayoutButtonRow?.FirstOrDefault(a => a.Text.Trim() == result.MessageText) ?? ButtonsForm.ToList().FirstOrDefault(a => a.Text.Trim() == result.MessageText);
if (button == null)
{
if (result.MessageText == null)
return;
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();
}
else if (this.EnableSearch)
{
if (result.MessageText.StartsWith("🔍"))
{
//Sent note about searching
if (this.SearchQuery == null)
{
await this.Device.Send(this.SearchLabel);
}
this.SearchQuery = null;
this.Updated();
return;
}
this.SearchQuery = result.MessageText;
if (this.SearchQuery != null && this.SearchQuery != "")
{
this.CurrentPageIndex = 0;
this.Updated();
}
}
return;
}
await OnButtonClicked(new ButtonClickedEventArgs(button));
//Remove button click message
if (this.DeletePreviousMessage)
await Device.DeleteMessage(result.MessageId);
result.Handled = true;
}
public async override Task Action(MessageResult result, string value = null)
{
if (result.Handled)
return;
if (!result.IsFirstHandler)
return;
await result.ConfirmAction(this.ConfirmationText ?? "");
//Find clicked button depending on Text or Value (depending on markup type)
switch (this.KeyboardType)
{
case eKeyboardType.InlineKeyBoard:
var button = HeadLayoutButtonRow?.FirstOrDefault(a => a.Value == result.RawData) ?? ButtonsForm.ToList().FirstOrDefault(a => a.Value == result.RawData);
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;
}
await OnButtonClicked(new ButtonClickedEventArgs(button));
result.Handled = true;
break;
}
}
/// <summary>
/// This method checks of the amount of buttons
/// </summary>
private void CheckGrid()
{
switch (m_eKeyboardType)
{
case eKeyboardType.InlineKeyBoard:
if (ButtonsForm.Rows > Constants.Telegram.MaxInlineKeyBoardRows && !this.EnablePaging)
{
throw new MaximumRowsReachedException() { Value = ButtonsForm.Rows, Maximum = Constants.Telegram.MaxInlineKeyBoardRows };
}
if (ButtonsForm.Cols > Constants.Telegram.MaxInlineKeyBoardCols)
{
throw new MaximumColsException() { Value = ButtonsForm.Rows, Maximum = Constants.Telegram.MaxInlineKeyBoardCols };
}
break;
case eKeyboardType.ReplyKeyboard:
if (ButtonsForm.Rows > Constants.Telegram.MaxReplyKeyboardRows && !this.EnablePaging)
{
throw new MaximumRowsReachedException() { Value = ButtonsForm.Rows, Maximum = Constants.Telegram.MaxReplyKeyboardRows };
}
if (ButtonsForm.Cols > Constants.Telegram.MaxReplyKeyboardCols)
{
throw new MaximumColsException() { Value = ButtonsForm.Rows, Maximum = Constants.Telegram.MaxReplyKeyboardCols };
}
break;
}
}
public async override Task Render(MessageResult result)
{
if (!this.RenderNecessary)
return;
//Check for rows and column limits
CheckGrid();
this.RenderNecessary = false;
Message m = null;
ButtonForm form = this.ButtonsForm;
if (this.EnableSearch && this.SearchQuery != null && this.SearchQuery != "")
{
form = form.FilterDuplicate(this.SearchQuery);
}
else
{
form = form.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, "$"));
}
//🔍
List<ButtonBase> lst = new List<ButtonBase>();
lst.Add(new ButtonBase(PreviousPageLabel, "$previous$"));
lst.Add(new ButtonBase(String.Format(Localizations.Default.Language["ButtonGrid_CurrentPage"], this.CurrentPageIndex + 1, this.PageCount), "$site$"));
lst.Add(new ButtonBase(NextPageLabel, "$next$"));
if (this.EnableSearch)
{
lst.Insert(2, new ButtonBase("🔍 " + (this.SearchQuery ?? ""), "$search$"));
}
bf.InsertButtonRow(0, lst);
bf.AddButtonRow(lst);
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;
var bf = this.ButtonsForm;
if (this.EnableSearch && this.SearchQuery != null && this.SearchQuery != "")
{
bf = bf.FilterDuplicate(this.SearchQuery);
}
return (int)Math.Ceiling((decimal)(bf.Rows / (decimal)(MaximumRow - 3)));
}
}
public override async Task Hidden(bool FormClose)
{
//Prepare for opening Modal, and comming back
if (!FormClose)
{
this.Updated();
}
}
/// <summary>
/// Tells the control that it has been updated.
/// </summary>
public void Updated()
{
this.RenderNecessary = true;
}
public async override Task Cleanup()
{
if (this.MessageId == null)
return;
switch (this.KeyboardType)
{
case eKeyboardType.InlineKeyBoard:
await this.Device.DeleteMessage(this.MessageId.Value);
this.MessageId = null;
break;
case eKeyboardType.ReplyKeyboard:
if (this.HideKeyboardOnCleanup)
{
await this.Device.HideReplyKeyboard();
}
this.MessageId = null;
break;
}
}
}
}
+277
View File
@@ -0,0 +1,277 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TelegramBotBase.Base;
using TelegramBotBase.Enums;
using TelegramBotBase.Form;
using static TelegramBotBase.Tools.Arrays;
using static TelegramBotBase.Tools.Time;
namespace TelegramBotBase.Controls
{
public class CalendarPicker : Base.ControlBase
{
public DateTime SelectedDate { get; set; }
public DateTime VisibleMonth { get; set; }
public DayOfWeek FirstDayOfWeek { get; set; }
public CultureInfo Culture { get; set; }
private int? MessageId { get; set; }
public String Title { get; set; } = Localizations.Default.Language["CalendarPicker_Title"];
public eMonthPickerMode PickerMode { get; set; }
public bool EnableDayView { get; set; } = true;
public bool EnableMonthView { get; set; } = true;
public bool EnableYearView { get; set; } = true;
public CalendarPicker()
{
this.SelectedDate = DateTime.Today;
this.VisibleMonth = DateTime.Today;
this.FirstDayOfWeek = DayOfWeek.Monday;
this.Culture = new CultureInfo("de-de");
this.PickerMode = eMonthPickerMode.day;
}
public override async Task Action(MessageResult result, String value = null)
{
await result.ConfirmAction();
switch (result.RawData)
{
case "$next$":
switch (this.PickerMode)
{
case eMonthPickerMode.day:
this.VisibleMonth = this.VisibleMonth.AddMonths(1);
break;
case eMonthPickerMode.month:
this.VisibleMonth = this.VisibleMonth.AddYears(1);
break;
case eMonthPickerMode.year:
this.VisibleMonth = this.VisibleMonth.AddYears(10);
break;
}
break;
case "$prev$":
switch (this.PickerMode)
{
case eMonthPickerMode.day:
this.VisibleMonth = this.VisibleMonth.AddMonths(-1);
break;
case eMonthPickerMode.month:
this.VisibleMonth = this.VisibleMonth.AddYears(-1);
break;
case eMonthPickerMode.year:
this.VisibleMonth = this.VisibleMonth.AddYears(-10);
break;
}
break;
case "$monthtitle$":
if (this.EnableMonthView)
{
this.PickerMode = eMonthPickerMode.month;
}
break;
case "$yeartitle$":
if (this.EnableYearView)
{
this.PickerMode = eMonthPickerMode.year;
}
break;
case "$yearstitle$":
if (this.EnableMonthView)
{
this.PickerMode = eMonthPickerMode.month;
}
this.VisibleMonth = this.SelectedDate;
break;
default:
int day = 0;
if (result.RawData.StartsWith("d-") && int.TryParse(result.RawData.Split('-')[1], out day))
{
this.SelectedDate = new DateTime(this.VisibleMonth.Year, this.VisibleMonth.Month, day);
}
int month = 0;
if (result.RawData.StartsWith("m-") && int.TryParse(result.RawData.Split('-')[1], out month))
{
this.SelectedDate = new DateTime(this.VisibleMonth.Year, month, 1);
this.VisibleMonth = this.SelectedDate;
if (this.EnableDayView)
{
this.PickerMode = eMonthPickerMode.day;
}
}
int year = 0;
if (result.RawData.StartsWith("y-") && int.TryParse(result.RawData.Split('-')[1], out year))
{
this.SelectedDate = new DateTime(year, SelectedDate.Month, SelectedDate.Day);
this.VisibleMonth = this.SelectedDate;
if (this.EnableMonthView)
{
this.PickerMode = eMonthPickerMode.month;
}
}
break;
}
}
public override async Task Render(MessageResult result)
{
ButtonForm bf = new ButtonForm();
switch (this.PickerMode)
{
case eMonthPickerMode.day:
var month = this.VisibleMonth;
string[] dayNamesNormal = this.Culture.DateTimeFormat.ShortestDayNames;
string[] dayNamesShifted = Shift(dayNamesNormal, (int)this.FirstDayOfWeek);
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());
//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(this.FirstDayOfWeek);
//End of week where last day of month is (right border)
var end = lastDay.EndOfWeek(this.FirstDayOfWeek);
for (int i = 0; i <= ((end - start).Days / 7); i++)
{
var lst = new List<ButtonBase>();
for (int id = 0; id < 7; id++)
{
var d = start.AddDays((i * 7) + id);
if (d < firstDay | d > lastDay)
{
lst.Add(new ButtonBase("-", "m-" + d.Day.ToString()));
continue;
}
var day = d.Day.ToString();
if (d == DateTime.Today)
{
day = "(" + day + ")";
}
lst.Add(new ButtonBase((this.SelectedDate == d ? "[" + day + "]" : day), "d-" + d.Day.ToString()));
}
bf.AddButtonRow(lst);
}
break;
case eMonthPickerMode.month:
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 buttons = months.Select((a, b) => new ButtonBase((b == this.SelectedDate.Month - 1 && this.SelectedDate.Year == this.VisibleMonth.Year ? "[ " + a + " ]" : a), "m-" + (b + 1).ToString()));
bf.AddSplitted(buttons, 2);
break;
case eMonthPickerMode.year:
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;
for (int i = 0; i < 10; i++)
{
var m = starti + (i * 2);
bf.AddButtonRow(new ButtonBase((this.SelectedDate.Year == m ? "[ " + m.ToString() + " ]" : m.ToString()), "y-" + m.ToString()), new ButtonBase((this.SelectedDate.Year == (m + 1) ? "[ " + (m + 1).ToString() + " ]" : (m + 1).ToString()), "y-" + (m + 1).ToString()));
}
break;
}
if (this.MessageId != null)
{
var m = await this.Device.Edit(this.MessageId.Value, this.Title, bf);
}
else
{
var m = await this.Device.Send(this.Title, bf);
this.MessageId = m.MessageId;
}
}
public override async Task Cleanup()
{
if (this.MessageId != null)
{
await this.Device.DeleteMessage(this.MessageId.Value);
}
}
}
}
+26
View File
@@ -0,0 +1,26 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TelegramBotBase.Base;
using TelegramBotBase.Enums;
using TelegramBotBase.Form;
namespace TelegramBotBase.Controls
{
public class MonthPicker : CalendarPicker
{
public MonthPicker()
{
this.PickerMode = eMonthPickerMode.month;
this.EnableDayView = false;
}
}
}
+2 -1
View File
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using TelegramBotBase.Base;
namespace TelegramBotBase.Controls namespace TelegramBotBase.Controls
{ {
@@ -220,7 +221,7 @@ namespace TelegramBotBase.Controls
} }
public async override Task Render() public async override Task Render(MessageResult result)
{ {
if (!this.RenderNecessary) if (!this.RenderNecessary)
{ {
+147
View File
@@ -0,0 +1,147 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TelegramBotBase.Base;
using TelegramBotBase.Form;
namespace TelegramBotBase.Controls
{
public class ToggleButton : ControlBase
{
public String UncheckedIcon { get; set; } = Localizations.Default.Language["ToggleButton_OffIcon"];
public String CheckedIcon { get; set; } = Localizations.Default.Language["ToggleButton_OnIcon"];
public String CheckedString { get; set; } = Localizations.Default.Language["ToggleButton_On"];
public String UncheckedString { get; set; } = Localizations.Default.Language["ToggleButton_Off"];
public String ChangedString { get; set; } = Localizations.Default.Language["ToggleButton_Changed"];
public String Title { get; set; } = Localizations.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
{
this.Events.AddHandler(__evToggled, value);
}
remove
{
this.Events.RemoveHandler(__evToggled, value);
}
}
public void OnToggled(EventArgs e)
{
(this.Events[__evToggled] as EventHandler)?.Invoke(this, e);
}
public override async Task Action(MessageResult result, String value = null)
{
if (result.Handled)
return;
await result.ConfirmAction(this.ChangedString);
switch (value ?? "unknown")
{
case "on":
if (this.Checked)
return;
RenderNecessary = true;
this.Checked = true;
OnToggled(new EventArgs());
break;
case "off":
if (!this.Checked)
return;
RenderNecessary = true;
this.Checked = false;
OnToggled(new EventArgs());
break;
default:
RenderNecessary = false;
break;
}
result.Handled = true;
}
public override async Task Render(MessageResult result)
{
if (!RenderNecessary)
return;
var bf = new ButtonForm(this);
ButtonBase bOn = new ButtonBase((this.Checked ? CheckedIcon : UncheckedIcon) + " " + CheckedString, "on");
ButtonBase bOff = new ButtonBase((!this.Checked ? CheckedIcon : UncheckedIcon) + " " + UncheckedString, "off");
bf.AddButtonRow(bOn, bOff);
if (this.MessageId != null)
{
var m = await this.Device.Edit(this.MessageId.Value, this.Title, bf);
}
else
{
var m = await this.Device.Send(this.Title, bf, disableNotification: true);
if (m != null)
{
this.MessageId = m.MessageId;
}
}
this.RenderNecessary = false;
}
}
}
+122
View File
@@ -0,0 +1,122 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TelegramBotBase.Base;
using TelegramBotBase.Form;
namespace TelegramBotBase.Controls
{
public class TreeView : ControlBase
{
public List<TreeViewNode> Nodes { get; set; }
public TreeViewNode SelectedNode { get; set; }
public TreeViewNode VisibleNode { get; set; }
public String Title { get; set; }
private int? MessageId { get; set; }
public String MoveUpIcon { get; set; } = Localizations.Default.Language["TreeView_LevelUp"];
public TreeView()
{
this.Nodes = new List<TreeViewNode>();
this.Title = Localizations.Default.Language["TreeView_Title"];
}
public override async Task Action(MessageResult result, String value = null)
{
await result.ConfirmAction();
if (result.Handled)
return;
var val = result.RawData;
switch (val)
{
case "up":
case "parent":
this.VisibleNode = (this.VisibleNode?.ParentNode);
result.Handled = true;
break;
default:
var n = (this.VisibleNode != null ? this.VisibleNode.FindNodeByValue(val) : this.Nodes.FirstOrDefault(a => a.Value == val));
if (n == null)
return;
if (n.ChildNodes.Count > 0)
{
this.VisibleNode = n;
}
else
{
this.SelectedNode = (this.SelectedNode != n ? n : null);
}
result.Handled = true;
break;
}
}
public override async Task Render(MessageResult result)
{
var startnode = this.VisibleNode;
var nodes = (startnode?.ChildNodes ?? this.Nodes);
ButtonForm bf = new ButtonForm();
if (startnode != null)
{
bf.AddButtonRow(new ButtonBase(this.MoveUpIcon, "up"), new ButtonBase(startnode.Text, "parent"));
}
foreach (var n in nodes)
{
var s = n.Text;
if (this.SelectedNode == n)
{
s = "[ " + s + " ]";
}
bf.AddButtonRow(new ButtonBase(s, n.Value, n.Url));
}
if (this.MessageId != null)
{
var m = await this.Device.Edit(this.MessageId.Value, this.Title, bf);
}
else
{
var m = await this.Device.Send(this.Title, bf);
this.MessageId = m.MessageId;
}
}
public String GetPath()
{
return (this.VisibleNode?.GetPath() ?? "\\");
}
}
}
+65
View File
@@ -0,0 +1,65 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TelegramBotBase.Controls
{
public class TreeViewNode
{
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 this.ChildNodes.FirstOrDefault(a => a.Value == Value);
}
public String GetPath()
{
String s = "\\" + this.Value;
var p = this;
while (p.ParentNode != null)
{
s = "\\" + p.ParentNode.Value + s;
p = p.ParentNode;
}
return s;
}
}
}
+24
View File
@@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TelegramBotBase.Enums
{
public enum eDeleteMode
{
/// <summary>
/// Don't delete any message.
/// </summary>
None = 0,
/// <summary>
/// Delete messages on every callback/action.
/// </summary>
OnEveryCall = 1,
/// <summary>
/// Delete on leaving this form.
/// </summary>
OnLeavingForm = 2
}
}
+24
View File
@@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TelegramBotBase.Enums
{
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
}
}
+22
View File
@@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TelegramBotBase.Enums
{
public enum eKeyboardType
{
/// <summary>
/// Uses a ReplyKeyboardMarkup
/// </summary>
ReplyKeyboard = 0,
/// <summary>
/// Uses a InlineKeyboardMakup
/// </summary>
InlineKeyBoard = 1
}
}
+24
View File
@@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TelegramBotBase.Enums
{
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
}
}
+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
}
}
@@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
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.")
{
}
}
}
@@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace TelegramBotBase.Exceptions
{
public class MaximumColsException : Exception
{
public int Value { get; set; }
public int Maximum { get; set; }
public override string Message
{
get
{
return $"You have exceeded the maximum of columns by {Value.ToString()} / {Maximum.ToString()}";
}
}
}
}
@@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace TelegramBotBase.Exceptions
{
public class MaximumRowsReachedException : Exception
{
public int Value { get; set; }
public int Maximum { get; set; }
public override string Message
{
get
{
return $"You have exceeded the maximum of rows by {Value.ToString()} / {Maximum.ToString()}";
}
}
}
}
+4 -4
View File
@@ -4,6 +4,7 @@ using System.ComponentModel;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using TelegramBotBase.Attributes;
using TelegramBotBase.Base; using TelegramBotBase.Base;
namespace TelegramBotBase.Form namespace TelegramBotBase.Form
@@ -11,17 +12,16 @@ namespace TelegramBotBase.Form
/// <summary> /// <summary>
/// A simple prompt dialog with one ok Button /// A simple prompt dialog with one ok Button
/// </summary> /// </summary>
public class AlertDialog : PromptDialog [IgnoreState]
public class AlertDialog : ConfirmDialog
{ {
public String ButtonText { get; set; } public String ButtonText { get; set; }
public AlertDialog(String Message, String ButtonText, FormBase FormToOpen = null) : base(Message) public AlertDialog(String Message, String ButtonText) : base(Message)
{ {
this.Buttons.Add(new ButtonBase(ButtonText, "ok")); this.Buttons.Add(new ButtonBase(ButtonText, "ok"));
this.ButtonText = ButtonText; this.ButtonText = ButtonText;
if (FormToOpen != null)
this.ButtonForms.Add("ok", FormToOpen);
} }
} }
+4 -1
View File
@@ -4,6 +4,8 @@ using System.ComponentModel;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using TelegramBotBase.Args;
using TelegramBotBase.Attributes;
using TelegramBotBase.Base; using TelegramBotBase.Base;
namespace TelegramBotBase.Form namespace TelegramBotBase.Form
@@ -11,13 +13,14 @@ namespace TelegramBotBase.Form
/// <summary> /// <summary>
/// A prompt with a lot of buttons /// A prompt with a lot of buttons
/// </summary> /// </summary>
[IgnoreState]
public class ArrayPromptDialog : FormBase public class ArrayPromptDialog : FormBase
{ {
public String Message { get; set; } public String Message { get; set; }
public ButtonBase[][] Buttons { get; set; } public ButtonBase[][] Buttons { get; set; }
[Obsolete]
public Dictionary<String, FormBase> ButtonForms { get; set; } = new Dictionary<string, FormBase>(); public Dictionary<String, FormBase> ButtonForms { get; set; } = new Dictionary<string, FormBase>();
private EventHandlerList __Events { get; set; } = new EventHandlerList(); private EventHandlerList __Events { get; set; } = new EventHandlerList();
+35 -49
View File
@@ -4,7 +4,10 @@ using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Telegram.Bot.Types; using Telegram.Bot.Types;
using TelegramBotBase.Args;
using TelegramBotBase.Attributes;
using TelegramBotBase.Base; using TelegramBotBase.Base;
using TelegramBotBase.Enums;
namespace TelegramBotBase.Form namespace TelegramBotBase.Form
{ {
@@ -13,53 +16,31 @@ namespace TelegramBotBase.Form
/// </summary> /// </summary>
public class AutoCleanForm : FormBase public class AutoCleanForm : FormBase
{ {
List<Message> OldMessages { get; set; } [SaveState]
public List<int> OldMessages { get; set; }
[SaveState]
public eDeleteMode DeleteMode { get; set; } public eDeleteMode DeleteMode { get; set; }
public eSide DeleteSide { get; set; } [SaveState]
public eDeleteSide DeleteSide { get; set; }
public enum eDeleteMode
{
/// <summary>
/// Don't delete any message.
/// </summary>
None = 0,
/// <summary>
/// Delete messages on every callback/action.
/// </summary>
OnEveryCall = 1,
/// <summary>
/// Delete on leaving this form.
/// </summary>
OnLeavingForm = 2
}
public enum eSide
{
/// <summary>
/// Delete only messages from this bot.
/// </summary>
BotOnly = 0,
/// <summary>
/// Delete only user messages.
/// </summary>
UserOnly = 1,
/// <summary>
/// Delete all messages in this context.
/// </summary>
Both = 2
}
public AutoCleanForm() public AutoCleanForm()
{ {
this.OldMessages = new List<Message>(); this.OldMessages = new List<int>();
this.DeleteMode = eDeleteMode.OnEveryCall; this.DeleteMode = eDeleteMode.OnEveryCall;
this.DeleteSide = eSide.BotOnly; this.DeleteSide = eDeleteSide.BotOnly;
this.Init += AutoCleanForm_Init;
this.Closed += AutoCleanForm_Closed;
} }
public override async Task Init(params object[] args)
private async Task AutoCleanForm_Init(object sender, InitEventArgs e)
{ {
if (this.Device == null) if (this.Device == null)
return; return;
@@ -72,18 +53,18 @@ namespace TelegramBotBase.Form
private void Device_MessageReceived(object sender, MessageReceivedEventArgs e) private void Device_MessageReceived(object sender, MessageReceivedEventArgs e)
{ {
if (this.DeleteSide == eSide.BotOnly) if (this.DeleteSide == eDeleteSide.BotOnly)
return; return;
this.OldMessages.Add(e.Message); this.OldMessages.Add(e.Message.MessageId);
} }
private void Device_MessageSent(object sender, MessageSentEventArgs e) private void Device_MessageSent(object sender, MessageSentEventArgs e)
{ {
if (this.DeleteSide == eSide.UserOnly) if (this.DeleteSide == eDeleteSide.UserOnly)
return; return;
this.OldMessages.Add(e.Message); this.OldMessages.Add(e.Message.MessageId);
} }
public override async Task PreLoad(MessageResult message) public override async Task PreLoad(MessageResult message)
@@ -100,7 +81,17 @@ namespace TelegramBotBase.Form
/// <param name="Id"></param> /// <param name="Id"></param>
public void AddMessage(Message m) public void AddMessage(Message m)
{ {
this.OldMessages.Add(m); this.OldMessages.Add(m.MessageId);
}
/// <summary>
/// Adds a message to this of removable ones
/// </summary>
/// <param name="Id"></param>
public void AddMessage(int messageId)
{
this.OldMessages.Add(messageId);
} }
/// <summary> /// <summary>
@@ -109,11 +100,7 @@ namespace TelegramBotBase.Form
/// <param name="Id"></param> /// <param name="Id"></param>
public void LeaveMessage(int Id) public void LeaveMessage(int Id)
{ {
var m = this.OldMessages.FirstOrDefault(a => a.MessageId == Id); this.OldMessages.Remove(Id);
if (m == null)
return;
this.OldMessages.Remove(m);
} }
/// <summary> /// <summary>
@@ -127,15 +114,14 @@ namespace TelegramBotBase.Form
this.OldMessages.RemoveAt(this.OldMessages.Count - 1); this.OldMessages.RemoveAt(this.OldMessages.Count - 1);
} }
public async override Task Closed() private async Task AutoCleanForm_Closed(object sender, EventArgs e)
{ {
if (this.DeleteMode != eDeleteMode.OnLeavingForm) if (this.DeleteMode != eDeleteMode.OnLeavingForm)
return; return;
await MessageCleanup(); MessageCleanup().Wait();
} }
/// <summary> /// <summary>
/// Cleans up all remembered messages. /// Cleans up all remembered messages.
/// </summary> /// </summary>
@@ -144,7 +130,7 @@ namespace TelegramBotBase.Form
{ {
while (this.OldMessages.Count > 0) while (this.OldMessages.Count > 0)
{ {
if (!await this.Device.DeleteMessage(this.OldMessages[0].MessageId)) if (!await this.Device.DeleteMessage(this.OldMessages[0]))
{ {
//Message can't be deleted cause it seems not to exist anymore //Message can't be deleted cause it seems not to exist anymore
if (this.OldMessages.Count > 0) if (this.OldMessages.Count > 0)
+40 -2
View File
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Telegram.Bot.Types.ReplyMarkups;
namespace TelegramBotBase.Form namespace TelegramBotBase.Form
{ {
@@ -11,19 +12,56 @@ namespace TelegramBotBase.Form
/// </summary> /// </summary>
public class ButtonBase public class ButtonBase
{ {
public String Text { get; set; } public virtual String Text { get; set; }
public String Value { get; set; } public String Value { get; set; }
public String Url { get; set; }
public ButtonBase() public ButtonBase()
{ {
} }
public ButtonBase(String Text, String Value) public ButtonBase(String Text, String Value, String Url = null)
{ {
this.Text = Text; this.Text = Text;
this.Value = Value; this.Value = Value;
this.Url = Url;
}
/// <summary>
/// Returns an inline Button
/// </summary>
/// <param name="form"></param>
/// <returns></returns>
public InlineKeyboardButton ToInlineButton(ButtonForm form)
{
String id = (form.DependencyControl != null ? form.DependencyControl.ControlID + "_" : "");
if (this.Url == null)
{
return InlineKeyboardButton.WithCallbackData(this.Text, id + this.Value);
}
var ikb = new InlineKeyboardButton();
ikb.Text = this.Text;
ikb.Url = this.Url;
return ikb;
}
/// <summary>
/// Returns a KeyBoardButton
/// </summary>
/// <param name="form"></param>
/// <returns></returns>
public KeyboardButton ToKeyboardButton(ButtonForm form)
{
return new KeyboardButton(this.Text);
} }
} }
+158 -5
View File
@@ -4,6 +4,7 @@ using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Telegram.Bot.Types.ReplyMarkups; using Telegram.Bot.Types.ReplyMarkups;
using TelegramBotBase.Base;
namespace TelegramBotBase.Form namespace TelegramBotBase.Form
{ {
@@ -14,14 +15,55 @@ namespace TelegramBotBase.Form
{ {
List<List<ButtonBase>> Buttons = new List<List<ButtonBase>>(); List<List<ButtonBase>> Buttons = new List<List<ButtonBase>>();
public IReplyMarkup Markup { get; set; }
public ControlBase DependencyControl { get; set; }
/// <summary>
/// Contains the number of rows.
/// </summary>
public int Rows
{
get
{
return Buttons.Count;
}
}
/// <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 List<ButtonBase> this[int row]
{
get
{
return Buttons[row];
}
}
public ButtonForm() public ButtonForm()
{ {
} }
public void AddButtonRow(List<ButtonBase> row) public ButtonForm(ControlBase control)
{ {
Buttons.Add(row); this.DependencyControl = control;
}
public void AddButtonRow(IEnumerable<ButtonBase> row)
{
Buttons.Add(row.ToList());
} }
public void AddButtonRow(params ButtonBase[] row) public void AddButtonRow(params ButtonBase[] row)
@@ -29,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[][]);
@@ -49,6 +101,17 @@ namespace TelegramBotBase.Form
return splitted; return splitted;
} }
public int Count
{
get
{
if (this.Buttons.Count == 0)
return 0;
return this.Buttons.Select(a => a.ToArray()).ToList().Aggregate((a, b) => a.Union(b).ToArray()).Length;
}
}
/// <summary> /// <summary>
/// Add buttons splitted in the amount of columns (i.e. 2 per row...) /// Add buttons splitted in the amount of columns (i.e. 2 per row...)
/// </summary> /// </summary>
@@ -64,18 +127,108 @@ namespace TelegramBotBase.Form
} }
} }
public InlineKeyboardButton[][] ToArray() public List<ButtonBase> ToList()
{ {
var ikb = this.Buttons.Select(a => a.Select(b => InlineKeyboardButton.WithCallbackData(b.Text, b.Value)).ToArray()).ToArray(); return this.Buttons.Aggregate((a, b) => a.Union(b).ToList());
}
public InlineKeyboardButton[][] ToInlineButtonArray()
{
var ikb = this.Buttons.Select(a => a.Select(b => b.ToInlineButton(this)).ToArray()).ToArray();
return ikb; return ikb;
} }
public KeyboardButton[][] ToReplyButtonArray()
{
var ikb = this.Buttons.Select(a => a.Select(b => b.ToKeyboardButton(this)).ToArray()).ToArray();
return ikb;
}
/// <summary>
/// Returns the first Button with the given value.
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public ButtonBase GetButtonByValue(String value)
{
return this.ToList().Where(a => a.Value == value).FirstOrDefault();
}
public static implicit operator InlineKeyboardMarkup(ButtonForm form) public static implicit operator InlineKeyboardMarkup(ButtonForm form)
{ {
InlineKeyboardMarkup ikm = new InlineKeyboardMarkup(form.ToArray()); if (form == null)
return null;
InlineKeyboardMarkup ikm = new InlineKeyboardMarkup(form.ToInlineButtonArray());
return ikm; return ikm;
} }
public static implicit operator ReplyKeyboardMarkup(ButtonForm form)
{
if (form == null)
return null;
ReplyKeyboardMarkup 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 = 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;
}
/// <summary>
/// Creates a copy of this form and filters by the parameter.
/// </summary>
/// <returns></returns>
public ButtonForm FilterDuplicate(String filter)
{
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)
{
if (b2.Text.IndexOf(filter, StringComparison.InvariantCultureIgnoreCase) == -1)
continue;
lst.Add(b2);
}
if (lst.Count > 0)
bf.Buttons.Add(lst);
}
return bf;
}
} }
} }
+102
View File
@@ -0,0 +1,102 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TelegramBotBase.Args;
using TelegramBotBase.Attributes;
using TelegramBotBase.Base;
namespace TelegramBotBase.Form
{
[IgnoreState]
public class ConfirmDialog : ModalDialog
{
public String Message { get; set; }
public List<ButtonBase> Buttons { get; set; }
private EventHandlerList __Events { get; set; } = new EventHandlerList();
private static object __evButtonClicked { get; } = new object();
public ConfirmDialog()
{
}
public ConfirmDialog(String Message)
{
this.Message = Message;
this.Buttons = new List<Form.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)
{
this.Buttons.Add(button);
}
public override async Task Action(MessageResult message)
{
var call = message.GetData<CallbackData>();
if (call == null)
return;
message.Handled = true;
await message.ConfirmAction();
await message.DeleteMessage();
ButtonBase button = this.Buttons.FirstOrDefault(a => a.Value == call.Value);
if (button == null)
{
return;
}
OnButtonClicked(new ButtonClickedEventArgs(button));
}
public override async Task Render(MessageResult message)
{
ButtonForm btn = new ButtonForm();
var buttons = this.Buttons.Select(a => new ButtonBase(a.Text, CallbackData.Create("action", a.Value))).ToList();
btn.AddButtonRow(buttons);
await this.Device.Send(this.Message, btn);
}
public event EventHandler<ButtonClickedEventArgs> ButtonClicked
{
add
{
this.__Events.AddHandler(__evButtonClicked, value);
}
remove
{
this.__Events.RemoveHandler(__evButtonClicked, value);
}
}
public void OnButtonClicked(ButtonClickedEventArgs e)
{
(this.__Events[__evButtonClicked] as EventHandler<ButtonClickedEventArgs>)?.Invoke(this, e);
}
}
}
+41
View File
@@ -0,0 +1,41 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace TelegramBotBase.Form
{
public class DynamicButton : ButtonBase
{
public override string Text
{
get
{
return GetText?.Invoke() ?? m_text;
}
set
{
m_text = value;
}
}
private String m_text = "";
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;
}
}
}
+86
View File
@@ -0,0 +1,86 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TelegramBotBase.Args;
using TelegramBotBase.Base;
using static TelegramBotBase.Base.Async;
namespace TelegramBotBase.Form
{
public class GroupForm : FormBase
{
public GroupForm()
{
}
public override async Task Load(MessageResult message)
{
switch (message.MessageType)
{
case Telegram.Bot.Types.Enums.MessageType.ChatMembersAdded:
await OnMemberChanges(new MemberChangeEventArgs(Telegram.Bot.Types.Enums.MessageType.ChatMembersAdded, message, message.RawMessageData.Message.NewChatMembers));
break;
case Telegram.Bot.Types.Enums.MessageType.ChatMemberLeft:
await OnMemberChanges(new MemberChangeEventArgs(Telegram.Bot.Types.Enums.MessageType.ChatMemberLeft, message, message.RawMessageData.Message.LeftChatMember));
break;
case Telegram.Bot.Types.Enums.MessageType.ChatPhotoChanged:
case Telegram.Bot.Types.Enums.MessageType.ChatPhotoDeleted:
case Telegram.Bot.Types.Enums.MessageType.ChatTitleChanged:
case Telegram.Bot.Types.Enums.MessageType.MigratedFromGroup:
case Telegram.Bot.Types.Enums.MessageType.MigratedToSupergroup:
case Telegram.Bot.Types.Enums.MessageType.MessagePinned:
case Telegram.Bot.Types.Enums.MessageType.GroupCreated:
case Telegram.Bot.Types.Enums.MessageType.SupergroupCreated:
await OnGroupChanged(new GroupChangedEventArgs(message.MessageType, message));
break;
default:
await OnMessage(message);
break;
}
}
public override async Task Edited(MessageResult message)
{
await OnMessageEdit(message);
}
public virtual async Task OnMemberChanges(MemberChangeEventArgs e)
{
}
public virtual async Task OnGroupChanged(GroupChangedEventArgs e)
{
}
public virtual async Task OnMessage(MessageResult e)
{
}
public virtual async Task OnMessageEdit(MessageResult e)
{
}
}
}
+28
View File
@@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
namespace TelegramBotBase.Form
{
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()
{
await this.CloseControls();
await this.OnClosed(new EventArgs());
this.ParentForm?.ReturnFromModal(this);
}
}
}
+41 -50
View File
@@ -4,22 +4,26 @@ using System.ComponentModel;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Telegram.Bot.Types.ReplyMarkups;
using TelegramBotBase.Attributes;
using TelegramBotBase.Base; using TelegramBotBase.Base;
namespace TelegramBotBase.Form namespace TelegramBotBase.Form
{ {
public class PromptDialog : FormBase [IgnoreState]
public class PromptDialog : ModalDialog
{ {
public String Message { get; set; } public String Message { get; set; }
public String Value { get; set; }
public List<ButtonBase> Buttons { get; set; }
public Dictionary<String, FormBase> ButtonForms { get; set; } = new Dictionary<string, FormBase>();
private EventHandlerList __Events { get; set; } = new EventHandlerList(); private EventHandlerList __Events { get; set; } = new EventHandlerList();
private static object __evButtonClicked { get; } = new object(); private static object __evCompleted { get; } = new object();
public bool ShowBackButton { get; set; } = false;
public String BackLabel { get; set; } = Localizations.Default.Language["PromptDialog_Back"];
public PromptDialog() public PromptDialog()
{ {
@@ -29,80 +33,67 @@ namespace TelegramBotBase.Form
public PromptDialog(String Message) public PromptDialog(String Message)
{ {
this.Message = Message; this.Message = Message;
this.Buttons = new List<Form.ButtonBase>();
} }
public PromptDialog(String Message, params ButtonBase[] Buttons) public async override Task Load(MessageResult message)
{ {
this.Message = Message; if (message.Handled)
this.Buttons = Buttons.ToList();
}
/// <summary>
/// Adds one Button
/// </summary>
/// <param name="button"></param>
public void AddButton(ButtonBase button)
{
this.Buttons.Add(button);
}
public override async Task Action(MessageResult message)
{
var call = message.GetData<CallbackData>();
if (call == null)
return; return;
message.Handled = true; if (this.ShowBackButton && message.MessageText == BackLabel)
await message.ConfirmAction();
await message.DeleteMessage();
ButtonBase button = this.Buttons.FirstOrDefault(a => a.Value == call.Value);
if (button == null)
{ {
await this.CloseForm();
return; return;
} }
OnButtonClicked(new ButtonClickedEventArgs(button)); if (this.Value == null)
FormBase fb = ButtonForms.ContainsKey(call.Value) ? ButtonForms[call.Value] : null;
if (fb != null)
{ {
await this.NavigateTo(fb); this.Value = message.MessageText;
}
} }
}
public override async Task Render(MessageResult message) public override async Task Render(MessageResult message)
{ {
ButtonForm btn = new ButtonForm();
var buttons = this.Buttons.Select(a => new ButtonBase(a.Text, CallbackData.Create("action", a.Value))).ToList(); if (this.Value == null)
btn.AddButtonRow(buttons); {
if (this.ShowBackButton)
{
ButtonForm bf = new ButtonForm();
bf.AddButtonRow(new ButtonBase(BackLabel, "back"));
await this.Device.Send(this.Message, (ReplyMarkupBase)bf);
return;
}
await this.Device.Send(this.Message, btn); await this.Device.Send(this.Message);
return;
} }
public event EventHandler<ButtonClickedEventArgs> ButtonClicked OnCompleted(new EventArgs());
await this.CloseForm();
}
public event EventHandler<EventArgs> Completed
{ {
add add
{ {
this.__Events.AddHandler(__evButtonClicked, value); this.__Events.AddHandler(__evCompleted, value);
} }
remove remove
{ {
this.__Events.RemoveHandler(__evButtonClicked, value); this.__Events.RemoveHandler(__evCompleted, value);
} }
} }
public void OnButtonClicked(ButtonClickedEventArgs e) public void OnCompleted(EventArgs e)
{ {
(this.__Events[__evButtonClicked] as EventHandler<ButtonClickedEventArgs>)?.Invoke(this, e); (this.__Events[__evCompleted] as EventHandler<EventArgs>)?.Invoke(this, e);
} }
} }
+100
View File
@@ -0,0 +1,100 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TelegramBotBase.Base;
using static TelegramBotBase.Base.Async;
namespace TelegramBotBase.Form
{
/// <summary>
/// This is used to split incomming requests depending on the chat type.
/// </summary>
public class SplitterForm : FormBase
{
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 == Telegram.Bot.Types.Enums.ChatType.Channel)
{
if (await OpenChannel(message))
{
return;
}
}
if (message.Message.Chat.Type == Telegram.Bot.Types.Enums.ChatType.Supergroup)
{
if (await OpenSupergroup(message))
{
return;
}
if (await OpenGroup(message))
{
return;
}
}
if (message.Message.Chat.Type == Telegram.Bot.Types.Enums.ChatType.Group)
{
if (await OpenGroup(message))
{
return;
}
}
await Open(message);
}
public virtual async Task<bool> OpenSupergroup(MessageResult e)
{
return false;
}
public virtual async Task<bool> OpenChannel(MessageResult e)
{
return false;
}
public virtual async Task<bool> Open(MessageResult e)
{
return false;
}
public virtual async Task<bool> OpenGroup(MessageResult e)
{
return 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);
}
}
}
+20
View File
@@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Text;
using TelegramBotBase.Args;
using TelegramBotBase.Base;
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
{
void LoadState(LoadStateEventArgs e);
void SaveState(SaveStateEventArgs e);
}
}
@@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Text;
using TelegramBotBase.Args;
using TelegramBotBase.Base;
using TelegramBotBase.Form;
namespace TelegramBotBase.Interfaces
{
public interface IStateMachine
{
Type FallbackStateForm { get; }
void SaveFormStates(SaveStatesEventArgs e);
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();
}
}
+34
View File
@@ -0,0 +1,34 @@
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_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["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["PromptDialog_Back"] = "Back";
Values["ToggleButton_Changed"] = "Setting changed";
}
}
}
+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,45 @@
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["ButtonGrid_SearchFeature"] = "💡 Sende eine Nachricht um die Liste zu filtern. Klicke die 🔍 um den Filter zurückzusetzen.";
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";
Values["ToggleButton_Changed"] = "Einstellung geändert";
}
}
}
+178
View File
@@ -0,0 +1,178 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Telegram.Bot.Types.Enums;
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>
/// 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)
{
switch (OutputMode)
{
case ParseMode.Markdown:
return "[" + (title ?? url) + "](" + url + " " + (tooltip ?? "") + ")";
case ParseMode.Html:
return $"<a href=\"{url}\" title=\"{tooltip ?? ""}\">{title ?? ""}</b>";
}
return 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.ToString(), 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)
{
switch (OutputMode)
{
case ParseMode.Markdown:
return "*" + text + "*";
case ParseMode.Html:
return "<b>" + text + "</b>";
}
return text;
}
/// <summary>
/// Returns a strike through in Markdown or HTML
/// </summary>
/// <param name="text"></param>
/// <returns></returns>
public static String Strikesthrough(this String text)
{
switch (OutputMode)
{
case ParseMode.Markdown:
return "~" + text + "~";
case ParseMode.Html:
return "<s>" + text + "</s>";
}
return text;
}
/// <summary>
/// Returns a italic text in Markdown or HTML
/// </summary>
/// <param name="text"></param>
/// <returns></returns>
public static String Italic(this String text)
{
switch (OutputMode)
{
case ParseMode.Markdown:
return "_" + text + "_";
case ParseMode.Html:
return "<i>" + text + "</i>";
}
return text;
}
/// <summary>
/// Returns a underline text in Markdown or HTML
/// </summary>
/// <param name="text"></param>
/// <returns></returns>
public static String Underline(this String text)
{
switch (OutputMode)
{
case ParseMode.Markdown:
return "__" + text + "__";
case ParseMode.Html:
return "<u>" + text + "</u>";
}
return text;
}
/// <summary>
/// Returns a monospace text in Markdown or HTML
/// </summary>
/// <param name="text"></param>
/// <returns></returns>
public static String Monospace(this String text)
{
switch (OutputMode)
{
case ParseMode.Markdown:
return "`" + text + "`";
case ParseMode.Html:
return "<code>" + text + "</code>";
}
return 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)
{
switch (OutputMode)
{
case ParseMode.Markdown:
return "```" + text + "```";
case ParseMode.Html:
return "<pre>" + text + "</pre>";
}
return 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)
{
char[] toEscape = new char[] { '_', '*', '[', ']', '(', ')', '~', '`', '>', '#', '+', '-', '=', '|', '{', '}', '.', '!' };
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));
}
}
}
+4 -4
View File
@@ -6,11 +6,11 @@ using System.Runtime.InteropServices;
// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern, // Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern,
// die einer Assembly zugeordnet sind. // die einer Assembly zugeordnet sind.
[assembly: AssemblyTitle("TelegramBotBase Framework")] [assembly: AssemblyTitle("TelegramBotBase Framework")]
[assembly: AssemblyDescription("A simple addon for the original C# Telegram Bot library.")] [assembly: AssemblyDescription("This is a context based application framework for the C# TelegramBot library.")]
[assembly: AssemblyConfiguration("")] [assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Florian Dahn")] [assembly: AssemblyCompany("Florian Dahn")]
[assembly: AssemblyProduct("TelegramBotBase Framework")] [assembly: AssemblyProduct("TelegramBotBase Framework")]
[assembly: AssemblyCopyright("Copyright © Florian Dahn 2019")] [assembly: AssemblyCopyright("Copyright © Florian Dahn 2020")]
[assembly: AssemblyTrademark("")] [assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")] [assembly: AssemblyCulture("")]
@@ -32,5 +32,5 @@ using System.Runtime.InteropServices;
// Sie können alle Werte angeben oder die standardmäßigen Build- und Revisionsnummern // Sie können alle Werte angeben oder die standardmäßigen Build- und Revisionsnummern
// übernehmen, indem Sie "*" eingeben: // übernehmen, indem Sie "*" eingeben:
// [assembly: AssemblyVersion("1.0.*")] // [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.4.2.0")] [assembly: AssemblyVersion("3.1.0.0")]
[assembly: AssemblyFileVersion("1.4.2.0")] [assembly: AssemblyFileVersion("3.1.0.0")]
+191 -2
View File
@@ -3,8 +3,11 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using TelegramBotBase.Args;
using TelegramBotBase.Attributes;
using TelegramBotBase.Base; using TelegramBotBase.Base;
using TelegramBotBase.Form; using TelegramBotBase.Form;
using TelegramBotBase.Interfaces;
using TelegramBotBase.Sessions; using TelegramBotBase.Sessions;
namespace TelegramBotBase namespace TelegramBotBase
{ {
@@ -67,9 +70,9 @@ namespace TelegramBotBase
DeviceSession ds = new Sessions.DeviceSession(deviceId, start); DeviceSession ds = new Sessions.DeviceSession(deviceId, start);
start.Device = ds; start.Device = ds;
await start.Init(); await start.OnInit(new InitEventArgs());
await start.Opened(); await start.OnOpened(new EventArgs());
this[deviceId] = ds; this[deviceId] = ds;
return ds; return ds;
@@ -89,5 +92,191 @@ namespace TelegramBotBase
} }
} }
/// <summary>
/// Returns all active User Sessions.
/// </summary>
/// <returns></returns>
public List<DeviceSession> GetUserSessions()
{
return this.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 this.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(IStateMachine statemachine)
{
if (statemachine == null)
{
throw new ArgumentNullException("StateMachine", "No StateMachine defined. Please set one to property BotBase.StateMachine");
}
var container = statemachine.LoadFormStates();
foreach (var s in container.States)
{
Type t = Type.GetType(s.QualifiedName);
if (t == null || !t.IsSubclassOf(typeof(FormBase)))
{
continue;
}
var form = t.GetConstructor(new Type[] { }).Invoke(new object[] { }) as FormBase;
if (s.Values != null && s.Values.Count > 0)
{
var properties = s.Values.Where(a => a.Key.StartsWith("$"));
var fields = form.GetType().GetProperties(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public | System.Reflection.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 ex)
{
}
catch
{
}
}
}
//Is Subclass of IStateForm
var iform = form as IStateForm;
if (iform != null)
{
var ls = new LoadStateEventArgs();
ls.Values = s.Values;
iform.LoadState(ls);
}
form.Client = Client;
var device = new DeviceSession(s.DeviceId, form);
device.ChatTitle = s.ChatTitle;
this.SessionList.Add(s.DeviceId, device);
try
{
await form.OnInit(new InitEventArgs());
await form.OnOpened(new EventArgs());
}
catch
{
//Skip on exception
this.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 this.SessionList)
{
if (s.Value == null)
{
continue;
}
var form = s.Value.ActiveForm;
try
{
var se = new StateEntry();
se.DeviceId = s.Key;
se.ChatTitle = s.Value.GetChatTitle();
se.FormUri = form.GetType().FullName;
se.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
var iform = form as IStateForm;
if (iform != null)
{
//Loading Session states
SaveStateEventArgs ssea = new SaveStateEventArgs();
iform.SaveState(ssea);
se.Values = ssea.Values;
}
//Search for public properties with SaveState attribute
var fields = form.GetType().GetProperties(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public | System.Reflection.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)
continue;
}
}
var sc = new StateContainer();
sc.States = states;
statemachine.SaveFormStates(new SaveStatesEventArgs(sc));
}
} }
} }
+331 -41
View File
@@ -12,8 +12,11 @@ using Telegram.Bot.Types;
using Telegram.Bot.Types.Enums; using Telegram.Bot.Types.Enums;
using Telegram.Bot.Types.InputFiles; using Telegram.Bot.Types.InputFiles;
using Telegram.Bot.Types.ReplyMarkups; using Telegram.Bot.Types.ReplyMarkups;
using TelegramBotBase.Args;
using TelegramBotBase.Base; using TelegramBotBase.Base;
using TelegramBotBase.Exceptions;
using TelegramBotBase.Form; using TelegramBotBase.Form;
using TelegramBotBase.Markdown;
namespace TelegramBotBase.Sessions namespace TelegramBotBase.Sessions
{ {
@@ -27,6 +30,23 @@ namespace TelegramBotBase.Sessions
/// </summary> /// </summary>
public long DeviceId { get; set; } public long DeviceId { get; set; }
/// <summary>
/// Username of user or group
/// </summary>
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>
@@ -115,6 +135,7 @@ namespace TelegramBotBase.Sessions
this.ActiveForm.Device = this; this.ActiveForm.Device = this;
} }
/// <summary> /// <summary>
/// Edits the text message /// Edits the text message
/// </summary> /// </summary>
@@ -124,20 +145,43 @@ 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) InlineKeyboardMarkup markup = buttons;
return null;
InlineKeyboardMarkup markup = null; if (text.Length > Constants.Telegram.MaxMessageLength)
if (buttons != null)
{ {
markup = buttons; throw new MaxLengthException(text.Length);
} }
try try
{ {
var m = await this.Client.TelegramClient.EditMessageTextAsync(this.DeviceId, messageId, text, replyMarkup: markup); return await this.Client.TelegramClient.EditMessageTextAsync(this.DeviceId, messageId, text, replyMarkup: markup);
}
catch
{
return m; }
return null;
}
/// <summary>
/// Edits the text message
/// </summary>
/// <param name="messageId"></param>
/// <param name="text"></param>
/// <param name="buttons"></param>
/// <returns></returns>
public async Task<Message> Edit(int messageId, String text, InlineKeyboardMarkup markup)
{
if (text.Length > Constants.Telegram.MaxMessageLength)
{
throw new MaxLengthException(text.Length);
}
try
{
return await this.Client.TelegramClient.EditMessageTextAsync(this.DeviceId, messageId, text, replyMarkup: markup);
} }
catch catch
{ {
@@ -157,20 +201,16 @@ 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) InlineKeyboardMarkup markup = buttons;
return null;
InlineKeyboardMarkup markup = null; if (message.Text.Length > Constants.Telegram.MaxMessageLength)
if (buttons != null)
{ {
markup = buttons; throw new MaxLengthException(message.Text.Length);
} }
try try
{ {
var m = await this.Client.TelegramClient.EditMessageTextAsync(this.DeviceId, message.MessageId, message.Text, replyMarkup: markup); return await this.Client.TelegramClient.EditMessageTextAsync(this.DeviceId, message.MessageId, message.Text, replyMarkup: markup);
return m;
} }
catch catch
{ {
@@ -181,6 +221,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>
@@ -189,26 +250,32 @@ namespace TelegramBotBase.Sessions
/// <param name="replyTo"></param> /// <param name="replyTo"></param>
/// <param name="disableNotification"></param> /// <param name="disableNotification"></param>
/// <returns></returns> /// <returns></returns>
public async Task<Message> Send(String text, ButtonForm buttons = null, int replyTo = 0, bool disableNotification = false) public async Task<Message> Send(String text, ButtonForm buttons = null, int replyTo = 0, bool disableNotification = false, ParseMode parseMode = ParseMode.Default, bool MarkdownV2AutoEscape = true)
{ {
if (this.ActiveForm == null) if (this.ActiveForm == null)
return null; return null;
InlineKeyboardMarkup markup = null; InlineKeyboardMarkup markup = buttons;
if (buttons != null)
{
markup = buttons;
}
Message m = null; Message m = null;
if (text.Length > Constants.Telegram.MaxMessageLength)
{
throw new MaxLengthException(text.Length);
}
if (parseMode == ParseMode.MarkdownV2 && MarkdownV2AutoEscape)
{
text = text.MarkdownV2Escape();
}
try try
{ {
m = await (this.Client.TelegramClient.SendTextMessageAsync(this.DeviceId, text, replyToMessageId: replyTo, replyMarkup: markup, disableNotification: disableNotification)); m = await (this.Client.TelegramClient.SendTextMessageAsync(this.DeviceId, text, parseMode, replyToMessageId: replyTo, replyMarkup: markup, disableNotification: disableNotification));
OnMessageSent(new MessageSentEventArgs(m)); OnMessageSent(new MessageSentEventArgs(m));
} }
catch (ApiRequestException ex) catch (ApiRequestException)
{ {
return null; return null;
} }
@@ -228,20 +295,73 @@ namespace TelegramBotBase.Sessions
/// <param name="replyTo"></param> /// <param name="replyTo"></param>
/// <param name="disableNotification"></param> /// <param name="disableNotification"></param>
/// <returns></returns> /// <returns></returns>
public async Task<Message> Send(String text, InlineKeyboardMarkup markup, int replyTo = 0, bool disableNotification = false) public async Task<Message> Send(String text, InlineKeyboardMarkup markup, int replyTo = 0, bool disableNotification = false, ParseMode parseMode = ParseMode.Default, bool MarkdownV2AutoEscape = true)
{ {
if (this.ActiveForm == null) if (this.ActiveForm == null)
return null; return null;
Message m = null; Message m = null;
if (text.Length > Constants.Telegram.MaxMessageLength)
{
throw new MaxLengthException(text.Length);
}
if (parseMode == ParseMode.MarkdownV2 && MarkdownV2AutoEscape)
{
text = text.MarkdownV2Escape();
}
try try
{ {
m = await (this.Client.TelegramClient.SendTextMessageAsync(this.DeviceId, text, replyToMessageId: replyTo, replyMarkup: markup, disableNotification: disableNotification)); m = await (this.Client.TelegramClient.SendTextMessageAsync(this.DeviceId, text, parseMode, replyToMessageId: replyTo, replyMarkup: markup, disableNotification: disableNotification));
OnMessageSent(new MessageSentEventArgs(m)); OnMessageSent(new MessageSentEventArgs(m));
} }
catch (ApiRequestException ex) catch (ApiRequestException)
{
return null;
}
catch
{
return null;
}
return m;
}
/// <summary>
/// Sends a simple text message
/// </summary>
/// <param name="text"></param>
/// <param name="markup"></param>
/// <param name="replyTo"></param>
/// <param name="disableNotification"></param>
/// <returns></returns>
public async Task<Message> Send(String text, ReplyMarkupBase markup, int replyTo = 0, bool disableNotification = false, ParseMode parseMode = ParseMode.Default, bool MarkdownV2AutoEscape = true)
{
if (this.ActiveForm == null)
return null;
Message m = null;
if (text.Length > Constants.Telegram.MaxMessageLength)
{
throw new MaxLengthException(text.Length);
}
if (parseMode == ParseMode.MarkdownV2 && MarkdownV2AutoEscape)
{
text = text.MarkdownV2Escape();
}
try
{
m = await (this.Client.TelegramClient.SendTextMessageAsync(this.DeviceId, text, parseMode, replyToMessageId: replyTo, replyMarkup: markup, disableNotification: disableNotification));
OnMessageSent(new MessageSentEventArgs(m));
}
catch (ApiRequestException)
{ {
return null; return null;
} }
@@ -261,26 +381,22 @@ namespace TelegramBotBase.Sessions
/// <param name="replyTo"></param> /// <param name="replyTo"></param>
/// <param name="disableNotification"></param> /// <param name="disableNotification"></param>
/// <returns></returns> /// <returns></returns>
public async Task<Message> SendPhoto(InputOnlineFile file, ButtonForm buttons = null, int replyTo = 0, bool disableNotification = false) public async Task<Message> SendPhoto(InputOnlineFile file, ButtonForm buttons = null, int replyTo = 0, bool disableNotification = false, ParseMode parseMode = ParseMode.Default)
{ {
if (this.ActiveForm == null) if (this.ActiveForm == null)
return null; return null;
InlineKeyboardMarkup markup = null; InlineKeyboardMarkup markup = buttons;
if (buttons != null)
{
markup = buttons;
}
Message m = null; Message m = null;
try try
{ {
m = await this.Client.TelegramClient.SendPhotoAsync(this.DeviceId, file, replyToMessageId: replyTo, replyMarkup: markup, disableNotification: disableNotification); m = await this.Client.TelegramClient.SendPhotoAsync(this.DeviceId, file, parseMode: parseMode, replyToMessageId: replyTo, replyMarkup: markup, disableNotification: disableNotification);
OnMessageSent(new MessageSentEventArgs(m)); OnMessageSent(new MessageSentEventArgs(m));
} }
catch (ApiRequestException ex) catch (ApiRequestException)
{ {
return null; return null;
} }
@@ -307,9 +423,7 @@ namespace TelegramBotBase.Sessions
{ {
InputOnlineFile fts = new InputOnlineFile(fileStream, name); InputOnlineFile fts = new InputOnlineFile(fileStream, name);
var m = await SendPhoto(fts, buttons, replyTo, disableNotification); return await SendPhoto(fts, buttons, replyTo, disableNotification);
return m;
} }
} }
@@ -328,10 +442,78 @@ namespace TelegramBotBase.Sessions
{ {
InputOnlineFile fts = new InputOnlineFile(fileStream, name); InputOnlineFile fts = new InputOnlineFile(fileStream, name);
var m = await SendPhoto(fts, buttons, replyTo, disableNotification); return await SendPhoto(fts, buttons, replyTo, disableNotification);
}
}
/// <summary>
/// Sends an video
/// </summary>
/// <param name="file"></param>
/// <param name="buttons"></param>
/// <param name="replyTo"></param>
/// <param name="disableNotification"></param>
/// <returns></returns>
public async Task<Message> SendVideo(InputOnlineFile file, ButtonForm buttons = null, int replyTo = 0, bool disableNotification = false, ParseMode parseMode = ParseMode.Default)
{
if (this.ActiveForm == null)
return null;
InlineKeyboardMarkup markup = buttons;
Message m = null;
try
{
m = await this.Client.TelegramClient.SendVideoAsync(this.DeviceId, file, parseMode: parseMode, replyToMessageId: replyTo, replyMarkup: markup, disableNotification: disableNotification);
OnMessageSent(new MessageSentEventArgs(m));
}
catch (ApiRequestException)
{
return null;
}
catch
{
return null;
}
return m; return m;
} }
/// <summary>
/// Sends an video
/// </summary>
/// <param name="file"></param>
/// <param name="buttons"></param>
/// <param name="replyTo"></param>
/// <param name="disableNotification"></param>
/// <returns></returns>
public async Task<Message> SendVideo(String url, ButtonForm buttons = null, int replyTo = 0, bool disableNotification = false, ParseMode parseMode = ParseMode.Default)
{
if (this.ActiveForm == null)
return null;
InlineKeyboardMarkup markup = buttons;
Message m = null;
try
{
m = await this.Client.TelegramClient.SendVideoAsync(this.DeviceId, new InputOnlineFile(url), parseMode: parseMode, replyToMessageId: replyTo, replyMarkup: markup, disableNotification: disableNotification);
OnMessageSent(new MessageSentEventArgs(m));
}
catch (ApiRequestException)
{
return null;
}
catch
{
return null;
}
return m;
} }
/// <summary> /// <summary>
@@ -350,9 +532,33 @@ namespace TelegramBotBase.Sessions
InputOnlineFile fts = new InputOnlineFile(ms, filename); InputOnlineFile fts = new InputOnlineFile(ms, filename);
var m = await SendDocument(fts, caption, buttons, replyTo, disableNotification); return await SendDocument(fts, caption, buttons, replyTo, disableNotification);
}
return m; /// <summary>
/// Generates a Textfile from scratch with the specified encoding. (Default is UTF8)
/// </summary>
/// <param name="filename"></param>
/// <param name="textcontent"></param>
/// <param name="encoding">Default is UTF8</param>
/// <param name="caption"></param>
/// <param name="buttons"></param>
/// <param name="replyTo"></param>
/// <param name="disableNotification"></param>
/// <returns></returns>
public async Task<Message> SendTextFile(String filename, String textcontent, Encoding encoding = null, String caption = "", ButtonForm buttons = null, int replyTo = 0, bool disableNotification = false)
{
encoding = encoding ?? Encoding.UTF8;
var ms = new MemoryStream();
var sw = new StreamWriter(ms, encoding);
sw.Write(textcontent);
sw.Flush();
var content = ms.ToArray();
return await SendDocument(filename, content, caption, buttons, replyTo, disableNotification);
} }
/// <summary> /// <summary>
@@ -417,6 +623,26 @@ namespace TelegramBotBase.Sessions
return await this.Client.TelegramClient.SendTextMessageAsync(this.DeviceId, requestMessage, replyMarkup: rcl); return await this.Client.TelegramClient.SendTextMessageAsync(this.DeviceId, requestMessage, replyMarkup: rcl);
} }
public async Task<Message> HideReplyKeyboard(String closedMsg = "Closed", bool autoDeleteResponse = true)
{
try
{
var m = await this.Send(closedMsg, new ReplyKeyboardRemove());
if (autoDeleteResponse && m != null)
{
await this.DeleteMessage(m);
}
return m;
}
catch
{
}
return null;
}
/// <summary> /// <summary>
/// Deletes a message /// Deletes a message
/// </summary> /// </summary>
@@ -430,7 +656,7 @@ namespace TelegramBotBase.Sessions
return true; return true;
} }
catch (ApiRequestException ex) catch (ApiRequestException)
{ {
} }
@@ -448,6 +674,70 @@ namespace TelegramBotBase.Sessions
return await DeleteMessage(message.MessageId); return await DeleteMessage(message.MessageId);
} }
public virtual async Task ChangeChatPermissions(ChatPermissions permissions)
{
try
{
await this.Client.TelegramClient.SetChatPermissionsAsync(this.DeviceId, permissions);
}
catch
{
}
}
public virtual async Task RestrictUser(int userId, ChatPermissions permissions, DateTime until = default(DateTime))
{
try
{
await this.Client.TelegramClient.RestrictChatMemberAsync(this.DeviceId, userId, permissions, until);
}
catch
{
}
}
public virtual async Task<ChatMember> GetChatUser(int userId)
{
try
{
return await this.Client.TelegramClient.GetChatMemberAsync(this.DeviceId, userId);
}
catch
{
}
return null;
}
public virtual async Task KickUser(int userId, DateTime until = default(DateTime))
{
try
{
await this.Client.TelegramClient.KickChatMemberAsync(this.DeviceId, userId, until);
}
catch
{
}
}
public virtual async Task UnbanUser(int userId)
{
try
{
await this.Client.TelegramClient.UnbanChatMemberAsync(this.DeviceId, userId);
}
catch
{
}
}
/// <summary> /// <summary>
/// Eventhandler for sent messages /// Eventhandler for sent messages
/// </summary> /// </summary>
@@ -0,0 +1,99 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Runtime.Serialization.Formatters;
using System.Text;
using TelegramBotBase.Args;
using TelegramBotBase.Base;
using TelegramBotBase.Form;
using TelegramBotBase.Interfaces;
namespace TelegramBotBase.States
{
/// <summary>
/// Is used for all complex data types. Use if other default machines are not working.
/// </summary>
public class JSONStateMachine : IStateMachine
{
public String FilePath { get; set; }
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 (file is null)
{
throw new ArgumentNullException(nameof(file));
}
this.FallbackStateForm = fallbackStateForm;
if (this.FallbackStateForm != null && !this.FallbackStateForm.IsSubclassOf(typeof(FormBase)))
{
throw new ArgumentException("FallbackStateForm is not a subclass of FormBase");
}
this.FilePath = file;
this.Overwrite = overwrite;
}
public StateContainer LoadFormStates()
{
try
{
var content = System.IO.File.ReadAllText(FilePath);
var sc = Newtonsoft.Json.JsonConvert.DeserializeObject<StateContainer>(content, new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.All,
TypeNameAssemblyFormatHandling = TypeNameAssemblyFormatHandling.Simple
}) as StateContainer;
return sc;
}
catch
{
}
return new StateContainer();
}
public void SaveFormStates(SaveStatesEventArgs e)
{
if (System.IO.File.Exists(FilePath))
{
if (!this.Overwrite)
{
throw new Exception("File exists already.");
}
System.IO.File.Delete(FilePath);
}
try
{
var content = Newtonsoft.Json.JsonConvert.SerializeObject(e.States, Formatting.Indented, new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.All,
TypeNameAssemblyFormatHandling = TypeNameAssemblyFormatHandling.Simple
});
System.IO.File.WriteAllText(FilePath, content);
}
catch
{
}
}
}
}
@@ -0,0 +1,91 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Runtime.Serialization.Formatters;
using System.Text;
using TelegramBotBase.Args;
using TelegramBotBase.Base;
using TelegramBotBase.Form;
using TelegramBotBase.Interfaces;
namespace TelegramBotBase.States
{
/// <summary>
/// Is used for simple object structures like classes, lists or basic datatypes without generics and other compiler based data types.
/// </summary>
public class SimpleJSONStateMachine : IStateMachine
{
public String FilePath { get; set; }
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 SimpleJSONStateMachine(String file, Type fallbackStateForm = null, bool overwrite = true)
{
if (file is null)
{
throw new ArgumentNullException(nameof(file));
}
this.FallbackStateForm = fallbackStateForm;
if (this.FallbackStateForm != null && !this.FallbackStateForm.IsSubclassOf(typeof(FormBase)))
{
throw new ArgumentException("FallbackStateForm is not a subclass of FormBase");
}
this.FilePath = file;
this.Overwrite = overwrite;
}
public StateContainer LoadFormStates()
{
try
{
var content = System.IO.File.ReadAllText(FilePath);
var sc = Newtonsoft.Json.JsonConvert.DeserializeObject<StateContainer>(content) as StateContainer;
return sc;
}
catch
{
}
return new StateContainer();
}
public void SaveFormStates(SaveStatesEventArgs e)
{
if (System.IO.File.Exists(FilePath))
{
if (!this.Overwrite)
{
throw new Exception("File exists already.");
}
System.IO.File.Delete(FilePath);
}
try
{
var content = Newtonsoft.Json.JsonConvert.SerializeObject(e.States, Formatting.Indented);
System.IO.File.WriteAllText(FilePath, content);
}
catch
{
}
}
}
}
+104
View File
@@ -0,0 +1,104 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using TelegramBotBase.Args;
using TelegramBotBase.Base;
using TelegramBotBase.Form;
using TelegramBotBase.Interfaces;
namespace TelegramBotBase.States
{
public class XMLStateMachine : IStateMachine
{
public String FilePath { get; set; }
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 XMLStateMachine(String file, Type fallbackStateForm = null, bool overwrite = true)
{
if (file is null)
{
throw new ArgumentNullException(nameof(file));
}
this.FallbackStateForm = fallbackStateForm;
if (this.FallbackStateForm != null && !this.FallbackStateForm.IsSubclassOf(typeof(FormBase)))
{
throw new ArgumentException("FallbackStateForm is not a subclass of FormBase");
}
this.FilePath = file;
this.Overwrite = overwrite;
}
public StateContainer LoadFormStates()
{
try
{
DataContractSerializer serializer = new DataContractSerializer(typeof(StateContainer));
using (var reader = new StreamReader(FilePath))
{
using (var xml = new XmlTextReader(reader))
{
StateContainer sc = serializer.ReadObject(xml) as StateContainer;
return sc;
}
}
}
catch
{
}
return new StateContainer();
}
public void SaveFormStates(SaveStatesEventArgs e)
{
if (System.IO.File.Exists(FilePath))
{
if (!this.Overwrite)
{
throw new Exception("File exists already.");
}
System.IO.File.Delete(FilePath);
}
try
{
DataContractSerializer serializer = new DataContractSerializer(typeof(StateContainer));
using (var sw = new StreamWriter(this.FilePath))
{
using (var writer = new XmlTextWriter(sw))
{
writer.Formatting = Formatting.Indented; // indent the Xml so its human readable
serializer.WriteObject(writer, e.States);
writer.Flush();
}
}
}
catch
{
}
}
}
}
+62 -70
View File
@@ -1,18 +1,28 @@
<?xml version="1.0" encoding="utf-8"?> <Project Sdk="Microsoft.NET.Sdk">
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup> <PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <TargetFrameworks>netstandard2.1;net461;netcoreapp3.1</TargetFrameworks>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<ProjectGuid>{A61EDA27-CFDF-4BCB-B2D6-184F94904F2D}</ProjectGuid> <GeneratePackageOnBuild>false</GeneratePackageOnBuild>
<OutputType>Library</OutputType> <PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
<AppDesignerFolder>Properties</AppDesignerFolder> <PackageProjectUrl>https://github.com/MajMcCloud/TelegramBotFramework</PackageProjectUrl>
<RootNamespace>TelegramBotBase</RootNamespace> <RepositoryUrl>https://github.com/MajMcCloud/TelegramBotFramework</RepositoryUrl>
<AssemblyName>TelegramBotBase</AssemblyName> <PackageReleaseNotes>- moving from .Net Framework 4.7.2 to .Net Standard 2.1 for the Library and .Net Core 3.1 for the test project!</PackageReleaseNotes>
<TargetFrameworkVersion>v4.5.1</TargetFrameworkVersion> <Configurations>Debug;Release;</Configurations>
<FileAlignment>512</FileAlignment> <PackageLicenseExpression>MIT</PackageLicenseExpression>
<TargetFrameworkProfile /> <RunAnalyzersDuringBuild>false</RunAnalyzersDuringBuild>
<RunAnalyzersDuringLiveAnalysis>false</RunAnalyzersDuringLiveAnalysis>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
</ItemGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols> <DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType> <DebugType>full</DebugType>
@@ -22,6 +32,7 @@
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType> <DebugType>pdbonly</DebugType>
<Optimize>true</Optimize> <Optimize>true</Optimize>
@@ -31,68 +42,49 @@
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
<DocumentationFile>bin\Release\TelegramBotBase.xml</DocumentationFile> <DocumentationFile>bin\Release\TelegramBotBase.xml</DocumentationFile>
</PropertyGroup> </PropertyGroup>
<PropertyGroup>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<DocumentationFile>bin\Release\TelegramBotBase.xml</DocumentationFile>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="System" /> <Compile Remove="Archive\**" />
<Reference Include="System.ComponentModel.Composition" /> <EmbeddedResource Remove="Archive\**" />
<Reference Include="System.Core" /> <None Remove="Archive\**" />
<Reference Include="System.Drawing" />
<Reference Include="System.IO.Compression" />
<Reference Include="System.Numerics" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Base\ButtonClickedEventArgs.cs" /> <None Remove="cpack.ps1" />
<Compile Include="Base\ControlBase.cs" /> <None Remove="cpack_bak.ps1" />
<Compile Include="Base\DataResult.cs" /> <None Remove="cpush.ps1" />
<Compile Include="Base\MessageClient.cs" /> <None Remove="nuget.exe" />
<Compile Include="Base\MessageIncomeResult.cs" /> <None Remove="TelegramBotBase.1.5.0.nupkg" />
<Compile Include="Base\MessageResult.cs" /> <None Remove="TelegramBotBase.1.5.0.zip" />
<Compile Include="Base\MessageSentEventArgs.cs" /> <None Remove="TelegramBotBase.1.5.1.nupkg" />
<Compile Include="Base\ResultBase.cs" /> <None Remove="TelegramBotBase.1.5.1.zip" />
<Compile Include="Base\SessionBeginResult.cs" /> <None Remove="TelegramBotBase.1.5.2.nupkg" />
<Compile Include="Base\SystemExceptionEventArgs.cs" /> <None Remove="TelegramBotBase.1.5.2.zip" />
<Compile Include="Base\UnhandledCallEventArgs.cs" /> <None Remove="TelegramBotBase.2.0.0.nupkg" />
<Compile Include="Base\SystemCallEventArgs.cs" /> <None Remove="TelegramBotBase.2.1.0.nupkg" />
<Compile Include="BotBase.cs" /> <None Remove="TelegramBotBase.2.1.0.zip" />
<Compile Include="Controls\ProgressBar.cs" /> <None Remove="TelegramBotBase.3.0.0.nupkg" />
<Compile Include="Form\AlertDialog.cs" /> <None Remove="TelegramBotBase.3.0.0.zip" />
<Compile Include="Form\ArrayPromptDialog.cs" /> <None Remove="TelegramBotBase.nuspec" />
<Compile Include="Form\AutoCleanForm.cs" />
<Compile Include="Form\ButtonBase.cs" />
<Compile Include="Form\ButtonForm.cs" />
<Compile Include="Base\FormBase.cs" />
<Compile Include="Form\CallbackData.cs" />
<Compile Include="Form\PromptDialog.cs" />
<Compile Include="SessionBase.cs" />
<Compile Include="Sessions\DeviceSession.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Tools\Images.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Newtonsoft.Json"> <PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<Version>11.0.2</Version> <PackageReference Include="System.Drawing.Common" Version="4.7.0" />
</PackageReference> <PackageReference Include="Telegram.Bot" Version="15.5.1" />
<PackageReference Include="System.Net.Requests">
<Version>4.3.0</Version>
</PackageReference>
<PackageReference Include="Telegram.Bot">
<Version>14.10.0</Version>
</PackageReference>
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project> </Project>
+20 -14
View File
@@ -1,21 +1,27 @@
<?xml version="1.0"?> <?xml version="1.0" encoding="utf-8"?>
<package > <package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd">
<metadata> <metadata>
<id>$id$</id> <id>TelegramBotBase</id>
<version>$version$</version> <version>2.1.0</version>
<title>$title$</title> <authors>TelegramBotBase</authors>
<authors>$author$</authors> <owners>TelegramBotBase</owners>
<owners>$author$</owners> <requireLicenseAcceptance>false</requireLicenseAcceptance>
<license type="expression">MIT</license> <license type="expression">MIT</license>
<projectUrl>https://github.com/MajMcCloud/TelegramBotFramework</projectUrl> <projectUrl>https://github.com/MajMcCloud/TelegramBotFramework</projectUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance> <description>Package Description</description>
<description>$description$</description> <releaseNotes>- moving from .Net Framework 4.7.2 to .Net Standard 2.1 for the Library and .Net Core 3.0 for the test project!</releaseNotes>
<releaseNotes>First release on nuget for easier use.</releaseNotes> <repository url="https://github.com/MajMcCloud/TelegramBotFramework" />
<copyright>Copyright 2019</copyright>
<tags>Telegram Bot Framework C# Addon Context Modules</tags>
<dependencies> <dependencies>
<dependency id="Telegram.Bot" version="14.3.0" /> <group targetFramework=".NETFramework4.6.1">
<dependency id="Newtonsoft.Json" version="11.0.2" /> <dependency id="Newtonsoft.Json" version="12.0.2" exclude="Build,Analyzers" />
<dependency id="System.Drawing.Common" version="4.6.0" exclude="Build,Analyzers" />
<dependency id="Telegram.Bot" version="15.0.0" exclude="Build,Analyzers" />
</group>
<group targetFramework=".NETStandard2.0">
<dependency id="Newtonsoft.Json" version="12.0.2" exclude="Build,Analyzers" />
<dependency id="System.Drawing.Common" version="4.6.0" exclude="Build,Analyzers" />
<dependency id="Telegram.Bot" version="15.0.0" exclude="Build,Analyzers" />
</group>
</dependencies> </dependencies>
</metadata> </metadata>
</package> </package>
+22
View File
@@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TelegramBotBase.Base;
using TelegramBotBase.Form;
namespace TelegramBotBase.Tools
{
public static class Arrays
{
public static T[] Shift<T>(T[] array, int positions)
{
T[] copy = new T[array.Length];
Array.Copy(array, 0, copy, array.Length - positions, positions);
Array.Copy(array, positions, copy, 0, array.Length - positions);
return copy;
}
}
}
+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;
}
}
}
}
+36
View File
@@ -0,0 +1,36 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TelegramBotBase.Tools
{
public static class Time
{
public static DateTime StartOfWeek(this DateTime dt, DayOfWeek startOfWeek)
{
int diff = dt.DayOfWeek - startOfWeek;
if (diff < 0)
{
diff += 7;
}
return dt.AddDays(-1 * diff).Date;
}
public static DateTime EndOfWeek(this DateTime dt, DayOfWeek startOfWeek)
{
return StartOfWeek(dt, startOfWeek).AddDays(6);
}
public static DateTime FirstDayOfMonth(this DateTime date)
{
return new DateTime(date.Year, date.Month, 1);
}
public static DateTime LastDayOfMonth(this DateTime date)
{
return FirstDayOfMonth(date).AddMonths(1).AddDays(-1);
}
}
}
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<configuration> <configuration>
<startup> <startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" /> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2"/>
</startup> </startup>
<runtime> <runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
@@ -3,33 +3,32 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Telegram.Bot.Types;
using TelegramBotBase; using TelegramBotBase;
using TelegramBotBase.Form; using TelegramBotBase.Form;
using TelegramBaseTest.Tests; using TelegramBotBaseTest.Tests;
namespace TelegramBaseTest namespace TelegramBotBaseTest
{ {
class Program class Program
{ {
static void Main(string[] args) static void Main(string[] args)
{ {
String APIKey = "{YOUR API KEY}";
BotBase<Start> bb = new BotBase<Start>(APIKey); BotBase<Start> bb = new BotBase<Start>(APIKey);
bb.SystemCalls.Add("/start"); bb.BotCommands.Add(new BotCommand() { Command = "start", Description = "Starts the bot" });
bb.SystemCalls.Add("/form1"); bb.BotCommands.Add(new BotCommand() { Command = "form1", Description = "Opens test form 1" });
bb.SystemCalls.Add("/form2"); bb.BotCommands.Add(new BotCommand() { Command = "form2", Description = "Opens test form 2" });
bb.SystemCalls.Add("/params"); bb.BotCommands.Add(new BotCommand() { Command = "params", Description = "Returns all send parameters as a message." });
bb.SystemCall += async (s, en) => bb.BotCommand += async (s, en) =>
{ {
switch (en.Command) switch (en.Command)
{ {
case "/start": case "/start":
var start = new Start(); var start = new Menu();
await en.Device.ActiveForm.NavigateTo(start); await en.Device.ActiveForm.NavigateTo(start);
@@ -63,6 +62,16 @@ namespace TelegramBaseTest
}; };
//Update Bot commands to botfather
bb.UploadBotCommands().Wait();
bb.SetSetting(TelegramBotBase.Enums.eSettings.LogAllMessages, true);
bb.Message += (s,en) =>
{
Console.WriteLine(en.DeviceId + " " + en.Message.MessageText + " " + (en.Message.RawData ?? ""));
};
bb.Start(); bb.Start();
@@ -77,5 +86,7 @@ namespace TelegramBaseTest
bb.Stop(); bb.Stop();
} }
} }
} }
@@ -5,11 +5,11 @@ using System.Runtime.InteropServices;
// Allgemeine Informationen über eine Assembly werden über die folgenden // Allgemeine Informationen über eine Assembly werden über die folgenden
// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern, // Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern,
// die einer Assembly zugeordnet sind. // die einer Assembly zugeordnet sind.
[assembly: AssemblyTitle("TelegramBaseTest")] [assembly: AssemblyTitle("TelegramBotBaseTest")]
[assembly: AssemblyDescription("")] [assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")] [assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")] [assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("TelegramBaseTest")] [assembly: AssemblyProduct("TelegramBotBaseTest")]
[assembly: AssemblyCopyright("Copyright © 2017")] [assembly: AssemblyCopyright("Copyright © 2017")]
[assembly: AssemblyTrademark("")] [assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")] [assembly: AssemblyCulture("")]
@@ -0,0 +1,33 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFrameworks>netcoreapp3.0;net461</TargetFrameworks>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<Configurations>Debug;Release</Configurations>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\TelegramBotBase\TelegramBotBase.csproj" />
</ItemGroup>
</Project>
@@ -6,12 +6,17 @@ using System.Threading.Tasks;
using TelegramBotBase.Base; using TelegramBotBase.Base;
using TelegramBotBase.Form; using TelegramBotBase.Form;
namespace TelegramBaseTest.Tests namespace TelegramBotBaseTest.Tests
{ {
public class ButtonTestForm : AutoCleanForm public class ButtonTestForm : AutoCleanForm
{ {
public override async Task Opened() public ButtonTestForm()
{
this.Opened += ButtonTestForm_Opened;
}
private async Task ButtonTestForm_Opened(object sender, EventArgs e)
{ {
await this.Device.Send("Hello world! (Click 'back' to get back to Start)"); await this.Device.Send("Hello world! (Click 'back' to get back to Start)");
} }
@@ -57,7 +62,7 @@ namespace TelegramBaseTest.Tests
case "back": case "back":
var st = new Start(); var st = new Menu();
await this.NavigateTo(st); await this.NavigateTo(st);
@@ -83,6 +88,8 @@ namespace TelegramBaseTest.Tests
btn.AddButtonRow(new ButtonBase("Button 3", new CallbackData("a", "button3").Serialize()), new ButtonBase("Button 4", new CallbackData("a", "button4").Serialize())); btn.AddButtonRow(new ButtonBase("Button 3", new CallbackData("a", "button3").Serialize()), new ButtonBase("Button 4", new CallbackData("a", "button4").Serialize()));
btn.AddButtonRow(new ButtonBase("Google.com", "google", "https://www.google.com"), new ButtonBase("Telegram", "telegram", "https://telegram.org/"));
btn.AddButtonRow(new ButtonBase("Back", new CallbackData("a", "back").Serialize())); btn.AddButtonRow(new ButtonBase("Back", new CallbackData("a", "back").Serialize()));
await this.Device.Send("Click a button", btn); await this.Device.Send("Click a button", btn);
@@ -0,0 +1,80 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TelegramBotBase.Args;
using TelegramBotBase.Controls;
using TelegramBotBase.Form;
namespace TelegramBotBaseTest.Tests.Controls
{
public class ButtonGridForm : AutoCleanForm
{
ButtonGrid m_Buttons = null;
public ButtonGridForm()
{
this.DeleteMode = TelegramBotBase.Enums.eDeleteMode.OnLeavingForm;
this.Init += ButtonGridForm_Init;
}
private async Task ButtonGridForm_Init(object sender, InitEventArgs e)
{
m_Buttons = new ButtonGrid();
m_Buttons.KeyboardType = TelegramBotBase.Enums.eKeyboardType.InlineKeyBoard;
ButtonForm bf = new ButtonForm();
bf.AddButtonRow(new ButtonBase("Back", "back"), new ButtonBase("Switch Keyboard", "switch"));
bf.AddButtonRow(new ButtonBase("Button1", "b1"), new ButtonBase("Button2", "b2"));
bf.AddButtonRow(new ButtonBase("Button3", "b3"), new ButtonBase("Button4", "b4"));
m_Buttons.ButtonsForm = bf;
m_Buttons.ButtonClicked += Bg_ButtonClicked;
this.AddControl(m_Buttons);
}
private async Task Bg_ButtonClicked(object sender, ButtonClickedEventArgs e)
{
if (e.Button == null)
return;
if (e.Button.Value == "back")
{
var start = new Menu();
await this.NavigateTo(start);
}
else if (e.Button.Value == "switch")
{
switch (m_Buttons.KeyboardType)
{
case TelegramBotBase.Enums.eKeyboardType.ReplyKeyboard:
m_Buttons.KeyboardType = TelegramBotBase.Enums.eKeyboardType.InlineKeyBoard;
break;
case TelegramBotBase.Enums.eKeyboardType.InlineKeyBoard:
m_Buttons.KeyboardType = TelegramBotBase.Enums.eKeyboardType.ReplyKeyboard;
break;
}
}
else
{
await this.Device.Send($"Button clicked with Text: {e.Button.Text} and Value {e.Button.Value}");
}
}
}
}
@@ -0,0 +1,73 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TelegramBotBase.Args;
using TelegramBotBase.Controls;
using TelegramBotBase.Form;
namespace TelegramBotBaseTest.Tests.Controls
{
public class ButtonGridPagingForm : AutoCleanForm
{
ButtonGrid m_Buttons = null;
public ButtonGridPagingForm()
{
this.DeleteMode = TelegramBotBase.Enums.eDeleteMode.OnLeavingForm;
this.Init += ButtonGridForm_Init;
}
private async Task ButtonGridForm_Init(object sender, InitEventArgs e)
{
m_Buttons = new ButtonGrid();
m_Buttons.KeyboardType = TelegramBotBase.Enums.eKeyboardType.ReplyKeyboard;
m_Buttons.EnablePaging = true;
m_Buttons.EnableSearch = true;
m_Buttons.HeadLayoutButtonRow = new List<ButtonBase>() { new ButtonBase("Back", "back") };
var countries = CultureInfo.GetCultures(CultureTypes.SpecificCultures);
ButtonForm bf = new ButtonForm();
foreach (var c in countries)
{
bf.AddButtonRow(new ButtonBase(c.EnglishName, c.EnglishName));
}
m_Buttons.ButtonsForm = bf;
m_Buttons.ButtonClicked += Bg_ButtonClicked;
this.AddControl(m_Buttons);
}
private async Task Bg_ButtonClicked(object sender, ButtonClickedEventArgs e)
{
if (e.Button == null)
return;
if (e.Button.Value == "back")
{
var start = new Menu();
await this.NavigateTo(start);
}
else
{
await this.Device.Send($"Button clicked with Text: {e.Button.Text} and Value {e.Button.Value}");
}
}
}
}
@@ -0,0 +1,80 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TelegramBotBase.Base;
using TelegramBotBase.Form;
using TelegramBotBase.Controls;
using TelegramBotBase.Args;
namespace TelegramBotBaseTest.Tests.Controls
{
public class CalendarPickerForm : AutoCleanForm
{
public CalendarPicker Picker { get; set; }
int? selectedDateMessage { get; set; }
public CalendarPickerForm()
{
this.DeleteMode = TelegramBotBase.Enums.eDeleteMode.OnLeavingForm;
this.Init += CalendarPickerForm_Init;
}
private async Task CalendarPickerForm_Init(object sender, InitEventArgs e)
{
this.Picker = new CalendarPicker();
this.Picker.Title = "Datum auswählen / Pick date";
this.AddControl(Picker);
}
public override async Task Action(MessageResult message)
{
switch(message.RawData)
{
case "back":
var s = new Menu();
await this.NavigateTo(s);
break;
}
}
public override async Task Render(MessageResult message)
{
String s = "";
s = "Selected date is " + this.Picker.SelectedDate.ToShortDateString() + "\r\n";
s += "Selected month is " + this.Picker.Culture.DateTimeFormat.MonthNames[this.Picker.VisibleMonth.Month - 1] + "\r\n";
s += "Selected year is " + this.Picker.VisibleMonth.Year.ToString();
ButtonForm bf = new ButtonForm();
bf.AddButtonRow(new ButtonBase("Back","back"));
if (selectedDateMessage != null)
{
await this.Device.Edit(this.selectedDateMessage.Value, s, bf);
}
else
{
var m = await this.Device.Send(s, bf);
this.selectedDateMessage = m.MessageId;
}
}
}
}
@@ -0,0 +1,78 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TelegramBotBase.Base;
using TelegramBotBase.Form;
using TelegramBotBase.Controls;
using TelegramBotBase.Args;
namespace TelegramBotBaseTest.Tests.Controls
{
public class MonthPickerForm : AutoCleanForm
{
public MonthPicker Picker { get; set; }
int? selectedDateMessage { get; set; }
public MonthPickerForm()
{
this.DeleteMode = TelegramBotBase.Enums.eDeleteMode.OnLeavingForm;
this.Init += MonthPickerForm_Init;
}
private async Task MonthPickerForm_Init(object sender, InitEventArgs e)
{
this.Picker = new MonthPicker();
this.Picker.Title = "Monat auswählen / Pick month";
this.AddControl(Picker);
}
public override async Task Action(MessageResult message)
{
switch(message.RawData)
{
case "back":
var s = new Menu();
await this.NavigateTo(s);
break;
}
}
public override async Task Render(MessageResult message)
{
String s = "";
s += "Selected month is " + this.Picker.Culture.DateTimeFormat.MonthNames[this.Picker.SelectedDate.Month - 1] + "\r\n";
s += "Selected year is " + this.Picker.VisibleMonth.Year.ToString();
ButtonForm bf = new ButtonForm();
bf.AddButtonRow(new ButtonBase("Back","back"));
if (selectedDateMessage != null)
{
await this.Device.Edit(this.selectedDateMessage.Value, s, bf);
}
else
{
var m = await this.Device.Send(s, bf);
this.selectedDateMessage = m.MessageId;
}
}
}
}
@@ -0,0 +1,50 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TelegramBotBase.Args;
using TelegramBotBase.Controls;
using TelegramBotBase.Form;
namespace TelegramBotBaseTest.Tests.Controls
{
public class ToggleButtons : AutoCleanForm
{
public ToggleButtons()
{
this.DeleteMode = TelegramBotBase.Enums.eDeleteMode.OnLeavingForm;
this.Init += ToggleButtons_Init;
}
private async Task ToggleButtons_Init(object sender, InitEventArgs e)
{
var tb = new ToggleButton();
tb.Checked = true;
tb.Toggled += Tb_Toggled;
this.AddControl(tb);
tb = new ToggleButton();
tb.Checked = false;
tb.Toggled += Tb_Toggled;
this.AddControl(tb);
tb = new ToggleButton();
tb.Checked = true;
tb.Toggled += Tb_Toggled;
this.AddControl(tb);
}
private void Tb_Toggled(object sender, EventArgs e)
{
var tb = sender as ToggleButton;
Console.WriteLine(tb.ID.ToString() + " was pressed, and toggled to " + (tb.Checked ? "Checked" : "Unchecked"));
}
}
}
@@ -0,0 +1,100 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TelegramBotBase.Form;
using TelegramBotBase.Controls;
using TelegramBotBase.Base;
using TelegramBotBase.Args;
namespace TelegramBotBaseTest.Tests.Controls
{
public class TreeViewForms : AutoCleanForm
{
public TreeView view { get; set; }
private int? MessageId { get; set; }
public TreeViewForms()
{
this.DeleteMode = TelegramBotBase.Enums.eDeleteMode.OnLeavingForm;
this.Init += TreeViewForms_Init;
}
private async Task TreeViewForms_Init(object sender, InitEventArgs e)
{
view = new TreeView();
var tvn = new TreeViewNode("Cars", "cars");
tvn.AddNode(new TreeViewNode("Porsche", "porsche", new TreeViewNode("Website", "web", "https://www.porsche.com/germany/"), new TreeViewNode("911", "911"), new TreeViewNode("918 Spyder", "918")));
tvn.AddNode(new TreeViewNode("BMW", "bmw"));
tvn.AddNode(new TreeViewNode("Audi", "audi"));
tvn.AddNode(new TreeViewNode("VW", "vw"));
tvn.AddNode(new TreeViewNode("Lamborghini", "lamborghini"));
view.Nodes.Add(tvn);
tvn = new TreeViewNode("Fruits", "fruits");
tvn.AddNode(new TreeViewNode("Apple", "apple"));
tvn.AddNode(new TreeViewNode("Orange", "orange"));
tvn.AddNode(new TreeViewNode("Lemon", "lemon"));
view.Nodes.Add(tvn);
this.AddControl(view);
}
public override async Task Action(MessageResult message)
{
await message.ConfirmAction();
if (message.Handled)
return;
switch (message.RawData)
{
case "back":
message.Handled = true;
var start = new Menu();
await this.NavigateTo(start);
break;
}
}
public override async Task Render(MessageResult message)
{
String s = "";
s += "Selected Node: " + (this.view.SelectedNode?.Text ?? "(null)") + "\r\n";
s += "Visible Node: " + (this.view.VisibleNode?.Text ?? "(top)") + "\r\n";
s += "Visible Path: " + this.view.GetPath() + "\r\n";
s += "Selected Path: " + (this.view.SelectedNode?.GetPath() ?? "(null)") + "\r\n";
ButtonForm bf = new ButtonForm();
bf.AddButtonRow(new ButtonBase("Back", "back"));
if (MessageId != null)
{
await this.Device.Edit(this.MessageId.Value, s, bf);
}
else
{
var m = await this.Device.Send(s, bf);
this.MessageId = m.MessageId;
}
}
}
}
@@ -9,7 +9,7 @@ using Telegram.Bot.Types.ReplyMarkups;
using TelegramBotBase.Base; using TelegramBotBase.Base;
using TelegramBotBase.Form; using TelegramBotBase.Form;
namespace TelegramBaseTest.Tests namespace TelegramBotBaseTest.Tests
{ {
public class DataForm : AutoCleanForm public class DataForm : AutoCleanForm
{ {
@@ -110,7 +110,7 @@ namespace TelegramBaseTest.Tests
message.Handled = true; message.Handled = true;
var start = new Start(); var start = new Menu();
await this.NavigateTo(start); await this.NavigateTo(start);
@@ -0,0 +1,76 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using TelegramBotBase.Args;
using TelegramBotBase.Base;
using TelegramBotBase.Form;
namespace TelegramBotBaseTest.Tests.Groups
{
public class GroupChange : TelegramBotBase.Form.GroupForm
{
public GroupChange()
{
this.Opened += GroupChange_Opened;
}
private async Task GroupChange_Opened(object sender, EventArgs e)
{
ButtonForm bf = new ButtonForm();
bf.AddButtonRow(new ButtonBase("Open GroupChange Test", "groupchange"));
bf.AddButtonRow(new ButtonBase("Open WelcomeUser Test", "welcomeuser"));
bf.AddButtonRow(new ButtonBase("Open LinkReplace Test", "linkreplace"));
await this.Device.Send("GroupChange started, click to switch", bf);
}
public override async Task Action(MessageResult message)
{
if (message.Handled)
return;
var bn = message.RawData;
await message.ConfirmAction();
message.Handled = true;
switch (bn)
{
case "groupchange":
var gc = new GroupChange();
await this.NavigateTo(gc);
break;
case "welcomeuser":
var wu = new WelcomeUser();
await this.NavigateTo(wu);
break;
case "linkreplace":
var lr = new LinkReplaceTest();
await this.NavigateTo(lr);
break;
}
}
public override async Task OnGroupChanged(GroupChangedEventArgs e)
{
await this.Device.Send("Group has been changed by " + e.OriginalMessage.Message.From.FirstName + " " + e.OriginalMessage.Message.From.LastName);
}
}
}
@@ -0,0 +1,164 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Telegram.Bot.Types;
using TelegramBotBase.Base;
using TelegramBotBase.Form;
namespace TelegramBotBaseTest.Tests.Groups
{
public class LinkReplaceTest : TelegramBotBase.Form.GroupForm
{
Dictionary<int, int> Counter { get; set; } = new Dictionary<int, int>();
private const int Maximum = 3;
public LinkReplaceTest()
{
this.Opened += LinkReplaceTest_Opened;
}
private async Task LinkReplaceTest_Opened(object sender, EventArgs e)
{
ButtonForm bf = new ButtonForm();
bf.AddButtonRow(new ButtonBase("Open GroupChange Test", "groupchange"));
bf.AddButtonRow(new ButtonBase("Open WelcomeUser Test", "welcomeuser"));
bf.AddButtonRow(new ButtonBase("Open LinkReplace Test", "linkreplace"));
await this.Device.Send("LinkReplaceTest started, click to switch", bf);
}
public override async Task Action(MessageResult message)
{
if (message.Handled)
return;
var bn = message.RawData;
await message.ConfirmAction();
message.Handled = true;
switch (bn)
{
case "groupchange":
var gc = new GroupChange();
await this.NavigateTo(gc);
break;
case "welcomeuser":
var wu = new WelcomeUser();
await this.NavigateTo(wu);
break;
case "linkreplace":
var lr = new LinkReplaceTest();
await this.NavigateTo(lr);
break;
}
}
public override async Task OnMessage(MessageResult e)
{
var from = e.Message.From.Id;
if (e.Message.From.IsBot)
return;
//Are urls inside his message ?
if (!HasLinks(e.MessageText))
return;
var u = await Device.GetChatUser(from);
//Don't kick Admins or Creators
if (u.Status == Telegram.Bot.Types.Enums.ChatMemberStatus.Administrator | u.Status == Telegram.Bot.Types.Enums.ChatMemberStatus.Creator)
{
await this.Device.Send("You won't get kicked,...not this time.");
return;
}
await e.Device.DeleteMessage(e.MessageId);
var cp = new ChatPermissions();
cp.CanAddWebPagePreviews = false;
cp.CanChangeInfo = false;
cp.CanInviteUsers = false;
cp.CanPinMessages = false;
cp.CanSendMediaMessages = false;
cp.CanSendMessages = false;
cp.CanSendOtherMessages = false;
cp.CanSendPolls = false;
//Collect user "mistakes" with sending url, after 3 he gets kicked out.
if (Counter.ContainsKey(from))
{
Counter[from]++;
}
else
{
Counter[from] = 1;
}
if (Counter[from] >= 3)
{
await e.Device.KickUser(from);
await e.Device.Send(e.Message.From.FirstName + " " + e.Message.From.LastName + " has been removed from the group");
}
else
{
await e.Device.RestrictUser(from, cp, DateTime.UtcNow.AddSeconds(30));
await e.Device.Send(e.Message.From.FirstName + " " + e.Message.From.LastName + " has been blocked for 30 seconds");
}
}
/// <summary>
/// Call onmessage also on edited message, if someone want to spoof a failed message and insert a link.
/// </summary>
/// <param name="message"></param>
/// <returns></returns>
public override async Task OnMessageEdit(MessageResult message)
{
await OnMessage(message);
}
/// <summary>
/// https://stackoverflow.com/a/20651284
/// </summary>
/// <param name="str"></param>
/// <returns></returns>
private bool HasLinks(String str)
{
var tmp = str;
var pattern = @"^(http|https|ftp|)\://|[a-zA-Z0-9\-\.]+\.[a-zA-Z](:[a-zA-Z0-9]*)?/?([a-zA-Z0-9\-\._\?\,\'/\\\+&amp;%\$#\=~])*[^\.\,\)\(\s]$";
Regex r = new Regex(pattern);
var matches = r.Matches(tmp);
return (matches.Count > 0);
}
}
}
@@ -0,0 +1,92 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using System.Threading.Tasks;
using TelegramBotBase.Base;
using TelegramBotBase.Form;
using TelegramBotBase.Args;
namespace TelegramBotBaseTest.Tests.Groups
{
public class WelcomeUser : TelegramBotBase.Form.GroupForm
{
public WelcomeUser()
{
this.Opened += WelcomeUser_Opened;
}
private async Task WelcomeUser_Opened(object sender, EventArgs e)
{
ButtonForm bf = new ButtonForm();
bf.AddButtonRow(new ButtonBase("Open GroupChange Test", "groupchange"));
bf.AddButtonRow(new ButtonBase("Open WelcomeUser Test", "welcomeuser"));
bf.AddButtonRow(new ButtonBase("Open LinkReplace Test", "linkreplace"));
await this.Device.Send("WelcomeUser started, click to switch", bf);
}
public override async Task Action(MessageResult message)
{
if (message.Handled)
return;
var bn = message.RawData;
await message.ConfirmAction();
message.Handled = true;
switch (bn)
{
case "groupchange":
var gc = new GroupChange();
await this.NavigateTo(gc);
break;
case "welcomeuser":
var wu = new WelcomeUser();
await this.NavigateTo(wu);
break;
case "linkreplace":
var lr = new LinkReplaceTest();
await this.NavigateTo(lr);
break;
}
}
public override async Task OnMemberChanges(MemberChangeEventArgs e)
{
if (e.Type == Telegram.Bot.Types.Enums.MessageType.ChatMembersAdded)
{
await this.Device.Send("Welcome you new members!\r\n\r\n" + e.Members.Select(a => a.FirstName + " " + a.LastName).Aggregate((a, b) => a + "\r\n" + b));
}
else if (e.Type == Telegram.Bot.Types.Enums.MessageType.ChatMemberLeft)
{
await this.Device.Send(e.Members.Select(a => a.FirstName + " " + a.LastName).Aggregate((a, b) => a + " and " + b) + " has left the group");
}
}
}
}

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