diff --git a/images/alertdialog.PNG b/.github/images/alertdialog.PNG similarity index 100% rename from images/alertdialog.PNG rename to .github/images/alertdialog.PNG diff --git a/images/buttongrid.gif b/.github/images/buttongrid.gif similarity index 100% rename from images/buttongrid.gif rename to .github/images/buttongrid.gif diff --git a/images/buttongrid_pagingfilter.gif b/.github/images/buttongrid_pagingfilter.gif similarity index 100% rename from images/buttongrid_pagingfilter.gif rename to .github/images/buttongrid_pagingfilter.gif diff --git a/images/calendarpicker.PNG b/.github/images/calendarpicker.PNG similarity index 100% rename from images/calendarpicker.PNG rename to .github/images/calendarpicker.PNG diff --git a/images/calendarpicker.gif b/.github/images/calendarpicker.gif similarity index 100% rename from images/calendarpicker.gif rename to .github/images/calendarpicker.gif diff --git a/images/checkedbuttonlist.gif b/.github/images/checkedbuttonlist.gif similarity index 100% rename from images/checkedbuttonlist.gif rename to .github/images/checkedbuttonlist.gif diff --git a/images/confirmdialog.PNG b/.github/images/confirmdialog.PNG similarity index 100% rename from images/confirmdialog.PNG rename to .github/images/confirmdialog.PNG diff --git a/images/example1.PNG b/.github/images/example1.PNG similarity index 100% rename from images/example1.PNG rename to .github/images/example1.PNG diff --git a/images/example2.PNG b/.github/images/example2.PNG similarity index 100% rename from images/example2.PNG rename to .github/images/example2.PNG diff --git a/images/example3.PNG b/.github/images/example3.PNG similarity index 100% rename from images/example3.PNG rename to .github/images/example3.PNG diff --git a/images/example4.1.PNG b/.github/images/example4.1.PNG similarity index 100% rename from images/example4.1.PNG rename to .github/images/example4.1.PNG diff --git a/images/example4.2.PNG b/.github/images/example4.2.PNG similarity index 100% rename from images/example4.2.PNG rename to .github/images/example4.2.PNG diff --git a/images/example4.3.PNG b/.github/images/example4.3.PNG similarity index 100% rename from images/example4.3.PNG rename to .github/images/example4.3.PNG diff --git a/images/example4.4.PNG b/.github/images/example4.4.PNG similarity index 100% rename from images/example4.4.PNG rename to .github/images/example4.4.PNG diff --git a/images/monthpicker1.PNG b/.github/images/monthpicker1.PNG similarity index 100% rename from images/monthpicker1.PNG rename to .github/images/monthpicker1.PNG diff --git a/images/monthpicker2.PNG b/.github/images/monthpicker2.PNG similarity index 100% rename from images/monthpicker2.PNG rename to .github/images/monthpicker2.PNG diff --git a/images/multitogglebutton.gif b/.github/images/multitogglebutton.gif similarity index 100% rename from images/multitogglebutton.gif rename to .github/images/multitogglebutton.gif diff --git a/images/progressbar.PNG b/.github/images/progressbar.PNG similarity index 100% rename from images/progressbar.PNG rename to .github/images/progressbar.PNG diff --git a/images/promptdialog.PNG b/.github/images/promptdialog.PNG similarity index 100% rename from images/promptdialog.PNG rename to .github/images/promptdialog.PNG diff --git a/images/taggedbuttongrid.gif b/.github/images/taggedbuttongrid.gif similarity index 100% rename from images/taggedbuttongrid.gif rename to .github/images/taggedbuttongrid.gif diff --git a/images/togglebutton.gif b/.github/images/togglebutton.gif similarity index 100% rename from images/togglebutton.gif rename to .github/images/togglebutton.gif diff --git a/images/treeview1.PNG b/.github/images/treeview1.PNG similarity index 100% rename from images/treeview1.PNG rename to .github/images/treeview1.PNG diff --git a/images/treeview2.PNG b/.github/images/treeview2.PNG similarity index 100% rename from images/treeview2.PNG rename to .github/images/treeview2.PNG diff --git a/images/treeview3.PNG b/.github/images/treeview3.PNG similarity index 100% rename from images/treeview3.PNG rename to .github/images/treeview3.PNG diff --git a/images/treeview4.PNG b/.github/images/treeview4.PNG similarity index 100% rename from images/treeview4.PNG rename to .github/images/treeview4.PNG diff --git a/README.md b/README.md index dbe65ea..3795bf4 100644 --- a/README.md +++ b/README.md @@ -1,155 +1,91 @@ - - -# .Net Telegram Bot Framework - Context based addon +# .NET Telegram Bot Framework - Context based addon [![NuGet version (TelegramBotBase)](https://img.shields.io/nuget/vpre/TelegramBotBase.svg?style=flat-square)](https://www.nuget.org/packages/TelegramBotBase/) -[![telegram chat](https://img.shields.io/badge/Support_Chat-Telegram-blue.svg?style=flat-square)](https://www.t.me/tgbotbase) +[![Telegram chat](https://img.shields.io/badge/Support_Chat-Telegram-blue.svg?style=flat-square)](https://www.t.me/tgbotbase) +[![License](https://img.shields.io/github/license/MajMcCloud/telegrambotframework.svg?style=flat-square&maxAge=2592000&label=License)](https://raw.githubusercontent.com/MajMcCloud/TelegramBotFramework/master/LICENCE.md) +[![Package Downloads](https://img.shields.io/nuget/dt/TelegramBotBase.svg?style=flat-square&label=Package%20Downloads)](https://www.nuget.org/packages/TelegramBotBase) -[![license](https://img.shields.io/github/license/MajMcCloud/telegrambotframework.svg?style=flat-square&maxAge=2592000&label=License)](https://raw.githubusercontent.com/MajMcCloud/TelegramBotFramework/master/LICENCE.md) -[![downloads](https://img.shields.io/nuget/dt/TelegramBotBase.svg?style=flat-square&label=Package%20Downloads)](https://www.nuget.org/packages/TelegramBotBase) +**Showcase: [@TGBaseBot](https://t.me/TGBaseBot)** -Test the Testproject: [@TGBaseBot](https://www.t.me/TGBaseBot) +**Support group: [@tgbotbase](https://t.me/tgbotbase)** -Join the Telegram Group: [https://www.t.me/tgbotbase](https://www.t.me/tgbotbase) +**Releases: [GitHub](https://github.com/MajMcCloud/TelegramBotFramework/releases)** -Download a release: [Releases](https://github.com/MajMcCloud/TelegramBotFramework/releases) +## Donate ---- +Bitcoin: `1GoUJYMwAvBipQTfw2FKydAz12J8RDyeJs` / `bc1qqwlp0p5ley29lsu6jhe0qv7s7963kfc7d0m53d` -Donations +Etherium: `0xAf3835104c2C3E5b3e721FA2c7365955e87DB931` -Bitcoin: 1GoUJYMwAvBipQTfw2FKydAz12J8RDyeJs / bc1qqwlp0p5ley29lsu6jhe0qv7s7963kfc7d0m53d +Litecoin: `LRhF1eB7kneFontcDRDU8YjJhEm2GoYHch` -ETH: 0xAf3835104c2C3E5b3e721FA2c7365955e87DB931 +Dashcoin: `XudiUwWtSmAJj1QDdVW7jocQumJFLsyoGZ` -Litecoin: LRhF1eB7kneFontcDRDU8YjJhEm2GoYHch +Tron: `TYVZSykaVT1nKZnz9hjDgBRNB9VavU1bpW` -DASH: XudiUwWtSmAJj1QDdVW7jocQumJFLsyoGZ - -TRON: TYVZSykaVT1nKZnz9hjDgBRNB9VavU1bpW - -BITTORRENT: TYVZSykaVT1nKZnz9hjDgBRNB9VavU1bpW - - -Thanks ! +BitTorrent: `TYVZSykaVT1nKZnz9hjDgBRNB9VavU1bpW` --- ## Index -- [Introduction](#introduction) -- [How to Start](#how-to-start) -- [Quick Start](#quick-start) -- [Message Handling](#message-handling) - * [Example #0 - System Calls](#add-some-system-calls-example-0---system-calls) - - * [Example #1 - Simple text messages](#lets-start-with-text-messages-example-1---simple-test) - - * [Example #2 - Button test](#now-some-buttons-example-2---button-test) - - * [Example #3 - Progress Bar control](#now-some-controls-example-3---progress-bar-test) - - * [Example #4 - Registration Formular](#registration-example-example-4---registration-form-test) +- [Quick start](#quick-start) +- [Simplified builder](#simplified-builder) +- [Features](#features) + * [System calls & bot commands](#system-calls--bot-commands) + * [Text messages handling](#text-messages) + * [Buttons](#buttons) + * [Custom controls](#custom-controls) + * [Forms advanced](#forms-advanced) - [Special Forms](#forms) - - * [AlertDialog](#alert-dialog) - - * [AutoCleanForm](#autocleanform) - - * [PromptDialog](#prompt-dialog) - - * [ConfirmDialog](#confirm-dialog) - + * [AlertDialog](#alert-dialog) + * [AutoCleanForm](#autocleanform) + * [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) - + * [ProgressBar](#progress-bar) + * [CalendarPicker](#calendar-picker) + * [MonthPicker](#month-picker) + * [TreeView](#tree-view) + * [ToggleButton](#toggle-button) + * [ButtonGrid](#button-grid) + * [Paging and Searching](#paging--searching) * [TaggedButtonGrid](#tagged-button-grid) - * [CheckedButtonList](#checked-button-list) - * [MultiToggleButton](#multi-toggle-button) - - [Groups](#groups) - * [SplitterForm](#splitter-form) - + * [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) - - - [Navigation and NavigationController (v4.0.0)](#navigation-and-navigationcontroller) - * [As of Now](#as-of-now) - - * [How to use](#how-to-use-) - - - - + * [Usage](#usage) +- [Extensions](#extensions) - [Examples](#examples) --- -## Introduction +## Quick start -Hey guys, +First of all, create a new empty dotnet console project and paste some code: -here we are. After some time and thoughts i give my TelegramBot framework to public. -It is based on C#. +```csharp +// public async Task Main(string[] args) -It is a module which is based on the original [TelegramBotLibrary](https://github.com/TelegramBots/Telegram.Bot) you will find in nuget. - -It gives you features which will look/feel like WinForms or have a good way to create apps with actions and forms. - ---- - -## How to start: - -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 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. -It needs to be a subclass of "FormBase" you will find in Namespace TelegramBotBase.Base - - -``` - -//Prepare the System (New in V5) -var bb = BotBaseBuilder +var bot = BotBaseBuilder .Create() - .WithAPIKey("{YOUR API KEY}") + .WithAPIKey("{YOUR API KEY}") // do not store your API key as plain text in project sources .DefaultMessageLoop() .WithStartForm() .NoProxy() @@ -162,132 +98,106 @@ var bb = BotBaseBuilder .UseEnglish() .Build(); -//Update bot commands to botfather -bb.UploadBotCommands().Wait(); - -//Start your Bot -bb.Start(); +// Upload bot commands to BotFather +await bot.UploadBotCommands(); +// Start your Bot +bot.Start(); ``` -Every Form has some events which will get raised at specific times. On every form you are able to get notes about the "Remote Device" like ChatId and other stuff your carrying. From there you build up you apps: +The `BotBase` class will manage a lot of things for you, like bot commands, action events and so on. +`StartForm` is your first form which every user will get internally redirected to, *just like a start page*. +It needs to be a subclass of `FormBase` you will find in namespace `TelegramBotBase.Base` -``` +Every `Form` has some events which will get raised at specific times. On every form you are able to get notes about +the *Remote Device*, +like ChatId and other stuff your carrying. From there you build up your bots: + +```csharp public class StartForm : FormBase { + // Gets invoked during Navigation to this form + public override async Task PreLoad(MessageResult message) + { + } + // Gets invoked on every Message/Action/Data in this context + public override async Task Load(MessageResult message) + { + // `Device` is a wrapper for current chat - you can easily respond to the user + await this.Device.Send("Hello world!"); + } - public override async Task PreLoad(MessageResult message) - { + // Gets invoked on edited messages + public override async Task Edited(MessageResult message) + { + } - } - - //Gets invoked during Navigation to this form + // Gets invoked on Button clicks + public override async Task Action(MessageResult message) + { + } - //Init() got replaced with event handler - - //Opened() got replaced with event handler - - //Closed() got replaced with event handler - - - //Gets invoked on every Message/Action/Data in this context - public override async Task Load(MessageResult message) - { - await this.Device.Send("Hello world!"); - } - - //Gets invoked on edited messages - public override async Task Edited(MessageResult message) - { - - } - - //Gets invoked on Button clicks - public override async Task Action(MessageResult message) - { - - - } - - //Gets invoked on Data uploades by the user (of type Photo, Audio, Video, Contact, Location, Document) - public override async Task SentData(DataResult data) - { - - - } - - //Gets invoked on every Message/Action/Data to render Design or Response - public override async Task Render(MessageResult message) - { - - } + // Gets invoked on Data uploades by the user (of type Photo, Audio, Video, Contact, Location, Document) + public override async Task SentData(DataResult data) + { + } + //Gets invoked on every Message/Action/Data to render Design or Response + public override async Task Render(MessageResult message) + { + } } - ``` -For instance send a message after loading a specific form: +Send a message after loading a specific form: -``` +```csharp await this.Device.Send("Hello world!"); ``` -Or you want to goto a different form? +Want to go to a different form? Go ahead, create it, initialize it and navigate to it: -``` -var tf = new TestForm(); - -await this.NavigateTo(tf); +```csharp +var form = new TestForm(); +await this.NavigateTo(form); ``` -## Quick Start: - +## Simplified builder When migrating from a previous version or starting completely new, all these options can be a bit overwhelming. -For this I added a QuickStart option, directly after the Create call. It just need basic parameters like in earlier versions. +There's a function called `QuickStart` that simplifies building a bit. - -``` - -//Prepare the System (New in V5) -var bb = BotBaseBuilder +```csharp +var bot = BotBaseBuilder .Create() .QuickStart("{YOUR API KEY}") .Build(); -//Start your Bot -bb.Start(); - +bot.Start(); ``` +## Features +### System calls & bot commands -## Message Handling +Using BotFather you can add *Commands* to your bot. The user will see them as popups in a dialog. +Before start (and later for sure) you could add them to your BotBase. +If the message contains a command, a special *event handler* will get raised. -All examples are within the test project, so just try it out on your own. +Below we have 4 commands. -### Add some system calls (Example #0 - Bot Commands) +`/start` - opens the start form -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. -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. +`/form1` - navigates in this context to form1 -Below we have 4 options. +`/form2` - navigates in this context to form2 -/start - opens the Startformular +`/params` - demonstrates the use of parameters per command (i.e. /params 1 2 3 test ...) -/form1 - navigates in this context to form1 - -/form2 - navigates in this context to form2 - -/params - demonstrates the use of parameters per command (i.e. /params 1 2 3 test ...) - - - -``` -var bb = BotBaseBuilder +```csharp +var bot = BotBaseBuilder .Create() .WithAPIKey("{YOUR API KEY}") .DefaultMessageLoop() @@ -299,59 +209,45 @@ var bb = BotBaseBuilder a.Add("form1","Opens test form 1" ); a.Add("form2", "Opens test form 2" ); a.Add("params", "Returns all send parameters as a message." ); - - }) .NoSerialization() .UseEnglish() .Build(); -bb.BotCommand += async (s, en) => +bot.BotCommand += async (s, en) => { - switch (en.Command) - { - case "/form1": + switch (en.Command) + { + case "/form1": + var form1 = new TestForm(); + await en.Device.ActiveForm.NavigateTo(form1); + break; - var form1 = new TestForm(); - - await en.Device.ActiveForm.NavigateTo(form1); - - break; - - case "/form2": - - var form2 = new TestForm2(); - - await en.Device.ActiveForm.NavigateTo(form2); - - break; - - case "/params": - - String m = en.Parameters.DefaultIfEmpty("").Aggregate((a, b) => a + " and " + b); - - await en.Device.Send("Your parameters are " + m, replyTo: en.Device.LastMessage); - - break; - } + case "/form2": + var form2 = new TestForm2(); + await en.Device.ActiveForm.NavigateTo(form2); + break; + case "/params": + string m = en.Parameters.DefaultIfEmpty("").Aggregate((a, b) => a + " and " + b); + await en.Device.Send("Your parameters are " + m, replyTo: en.Device.LastMessage); + break; + } }; -//Update Bot commands to botfather -bb.UploadBotCommands().Wait(); - -bb.Start(); +await bot.UploadBotCommands() +bot.Start(); ``` +On every input the user is sending back to the bot, the `Action` event gets raised. So here we could manage to send +something back to him. -On every input the user is sending back to the bot the Action event gets raised. So here we could manage to send something back to him. For sure we could also manage different button inputs: +### Text messages -### Lets start with text messages (Example #1 - Simple Test) + - - -``` +```csharp public class SimpleForm : AutoCleanForm { public SimpleForm() @@ -361,419 +257,331 @@ public class SimpleForm : AutoCleanForm this.Opened += SimpleForm_Opened; } - + private async Task SimpleForm_Opened(object sender, EventArgs e) { await this.Device.Send("Hello world! (send 'back' to get back to Start)\r\nOr\r\nhi, hello, maybe, bye and ciao"); } - public override async Task Load(MessageResult message) - { - //message.MessageText will work also, cause it is a string you could manage a lot different scenerios here + { + // message.MessageText will work also, cause it is a string you could manage a lot different scenerios here + var messageId = message.MessageId; - var messageId = message.MessageId; - - switch (message.Command) - { - case "hello": - case "hi": - - //Send him a simple message - await this.Device.Send("Hello you there !"); - break; - - case "maybe": - - //Send him a simple message and reply to the one of himself - await this.Device.Send("Maybe what?", replyTo: messageId); - - break; - - case "bye": - case "ciao": - - //Send him a simple message - await this.Device.Send("Ok, take care !"); - break; - } - } + switch (message.Command) + { + case "hello": + case "hi": + // Send a simple message + await this.Device.Send("Hello you there !"); + break; + + case "maybe": + // Send a simple message and reply to the one of himself + await this.Device.Send("Maybe what?", replyTo: messageId); + break; + + case "bye": + case "ciao": + // Send a simple message + await this.Device.Send("Ok, take care !"); + break; + } + } } - ``` -### Now some buttons (Example #2 - Button Test) +### Buttons -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. + - - -``` +```csharp public class ButtonTestForm : AutoCleanForm { + public override async Task Opened() + { + await this.Device.Send("Hello world! (Click 'back' to get back to Start)"); + } - public override async Task Opened() + public override async Task Action(MessageResult message) + { + var call = message.GetData(); + await message.ConfirmAction(); + + if (call == null) + return; + + message.Handled = true; + + switch (call.Value) { - await this.Device.Send("Hello world! (Click 'back' to get back to Start)"); + case "button1": + await this.Device.Send("Button 1 pressed"); + break; + + case "button2": + await this.Device.Send("Button 2 pressed"); + break; + + case "button3": + await this.Device.Send("Button 3 pressed"); + break; + + case "button4": + await this.Device.Send("Button 4 pressed"); + break; + + case "back": + var st = new Start(); + await this.NavigateTo(st); + break; + + default: + message.Handled = false; + break; } + } - public override async Task Action(MessageResult message) - { + public override async Task Render(MessageResult message) + { + ButtonForm btn = new ButtonForm(); - var call = message.GetData(); + btn.AddButtonRow(new ButtonBase("Button 1", new CallbackData("a", "button1").Serialize()), new ButtonBase("Button 2", new CallbackData("a", "button2").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())); - await message.ConfirmAction(); - - - if (call == null) - return; - - message.Handled = true; - - switch (call.Value) - { - case "button1": - - await this.Device.Send("Button 1 pressed"); - - break; - - case "button2": - - await this.Device.Send("Button 2 pressed"); - - break; - - case "button3": - - await this.Device.Send("Button 3 pressed"); - - break; - - case "button4": - - await this.Device.Send("Button 4 pressed"); - - break; - - case "back": - - var st = new Start(); - - await this.NavigateTo(st); - - break; - - default: - - message.Handled = false; - - break; - } - - - } - - - public override async Task Render(MessageResult message) - { - - ButtonForm btn = new ButtonForm(); - - btn.AddButtonRow(new ButtonBase("Button 1", new CallbackData("a", "button1").Serialize()), new ButtonBase("Button 2", new CallbackData("a", "button2").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())); - - await this.Device.Send("Click a button", btn); - - - } + await this.Device.Send("Click a button", btn); + } } - ``` -### Now some controls (Example #3 - Progress Bar Test) +### Custom controls -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. +There are a bunch of ready to use controls. For example, progress bar. - - -``` + +```csharp public class ProgressTest : AutoCleanForm { - -public ProgressTest() -{ - this.DeleteMode = eDeleteMode.OnLeavingForm; -} - -public override async Task Opened() -{ - await this.Device.Send("Welcome to ProgressTest"); -} - -public override async Task Action(MessageResult message) -{ - var call = message.GetData(); - - await message.ConfirmAction(); - - - if (call == null) - return; - - TelegramBotBase.Controls.ProgressBar Bar = null; - - switch (call.Value) + public ProgressTest() { - case "standard": - - Bar = new TelegramBotBase.Controls.ProgressBar(0, 100, TelegramBotBase.Controls.ProgressBar.eProgressStyle.standard); - Bar.Device = this.Device; - - break; - - case "squares": - - Bar = new TelegramBotBase.Controls.ProgressBar(0, 100, TelegramBotBase.Controls.ProgressBar.eProgressStyle.squares); - Bar.Device = this.Device; - - break; - - case "circles": - - Bar = new TelegramBotBase.Controls.ProgressBar(0, 100, TelegramBotBase.Controls.ProgressBar.eProgressStyle.circles); - Bar.Device = this.Device; - - break; - - case "lines": - - Bar = new TelegramBotBase.Controls.ProgressBar(0, 100, TelegramBotBase.Controls.ProgressBar.eProgressStyle.lines); - Bar.Device = this.Device; - - break; - - case "squaredlines": - - Bar = new TelegramBotBase.Controls.ProgressBar(0, 100, TelegramBotBase.Controls.ProgressBar.eProgressStyle.squaredLines); - Bar.Device = this.Device; - - break; - - case "start": - - var sf = new Start(); - - await sf.Init(); - - await this.NavigateTo(sf); - - return; - - default: - - return; - + this.DeleteMode = eDeleteMode.OnLeavingForm; } - - //Render Progress bar and show some "example" progress - await Bar.Render(); - - this.Controls.Add(Bar); - - for (int i = 0; i <= 100; i++) + public override async Task Opened() { - Bar.Value++; - await Bar.Render(); - - Thread.Sleep(250); + await this.Device.Send("Welcome to ProgressTest"); } - -} - - -public override async Task Render(MessageResult message) -{ - ButtonForm btn = new ButtonForm(); - btn.AddButtonRow(new ButtonBase("Standard", new CallbackData("a", "standard").Serialize()), new ButtonBase("Squares", new CallbackData("a", "squares").Serialize())); - - btn.AddButtonRow(new ButtonBase("Circles", new CallbackData("a", "circles").Serialize()), new ButtonBase("Lines", new CallbackData("a", "lines").Serialize())); - - btn.AddButtonRow(new ButtonBase("Squared Line", new CallbackData("a", "squaredlines").Serialize())); - - btn.AddButtonRow(new ButtonBase("Back to start", new CallbackData("a", "start").Serialize())); - - await this.Device.Send("Choose your progress bar:", btn); -} - -public override async Task Closed() -{ - foreach (var b in this.Controls) + public override async Task Action(MessageResult message) { - await b.Cleanup(); + var call = message.GetData(); + await message.ConfirmAction(); + + if (call == null) return; + + TelegramBotBase.Controls.ProgressBar Bar = null; + + switch (call.Value) + { + case "standard": + Bar = new TelegramBotBase.Controls.ProgressBar(0, 100, TelegramBotBase.Controls.ProgressBar.eProgressStyle.standard); + Bar.Device = this.Device; + break; + + case "squares": + Bar = new TelegramBotBase.Controls.ProgressBar(0, 100, TelegramBotBase.Controls.ProgressBar.eProgressStyle.squares); + Bar.Device = this.Device; + break; + + case "circles": + Bar = new TelegramBotBase.Controls.ProgressBar(0, 100, TelegramBotBase.Controls.ProgressBar.eProgressStyle.circles); + Bar.Device = this.Device; + break; + + case "lines": + Bar = new TelegramBotBase.Controls.ProgressBar(0, 100, TelegramBotBase.Controls.ProgressBar.eProgressStyle.lines); + Bar.Device = this.Device; + break; + + case "squaredlines": + Bar = new TelegramBotBase.Controls.ProgressBar(0, 100, TelegramBotBase.Controls.ProgressBar.eProgressStyle.squaredLines); + Bar.Device = this.Device; + break; + + case "start": + var sf = new Start(); + await sf.Init(); + await this.NavigateTo(sf); + return; + + default: + return; + } + + // Render Progress bar and show some "example" progress + await Bar.Render(); + + this.Controls.Add(Bar); + + for (int i = 0; i <= 100; i++) + { + Bar.Value++; + await Bar.Render(); + + Thread.Sleep(250); + } } - await this.Device.Send("Ciao from ProgressTest"); + public override async Task Render(MessageResult message) + { + ButtonForm btn = new ButtonForm(); + + btn.AddButtonRow(new ButtonBase("Standard", new CallbackData("a", "standard").Serialize()), new ButtonBase("Squares", new CallbackData("a", "squares").Serialize())); + btn.AddButtonRow(new ButtonBase("Circles", new CallbackData("a", "circles").Serialize()), new ButtonBase("Lines", new CallbackData("a", "lines").Serialize())); + btn.AddButtonRow(new ButtonBase("Squared Line", new CallbackData("a", "squaredlines").Serialize())); + btn.AddButtonRow(new ButtonBase("Back to start", new CallbackData("a", "start").Serialize())); + + await this.Device.Send("Choose your progress bar:", btn); + } + + public override async Task Closed() + { + foreach (var b in this.Controls) + { + await b.Cleanup(); + } + + await this.Device.Send("Ciao from ProgressTest"); + } } - - - -} - - ``` -### Registration Example (Example #4 - Registration Form Test) +### Forms advanced -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. +Registration forms have never been so easy. - + - + - + - - -``` + +```csharp public class PerForm : AutoCleanForm { -public String EMail { get; set; } + public string EMail { get; set; } -public String Firstname { get; set; } + public string Firstname { get; set; } -public String Lastname { get; set; } + public string Lastname { get; set; } -public async override Task Load(MessageResult message) -{ - if (message.MessageText.Trim() == "") - return; - - if (this.Firstname == null) + public async override Task Load(MessageResult message) { - this.Firstname = message.MessageText; - return; + if (string.IsNullOrWhiteSpace(message.MessageText)) return; + + if (this.Firstname == null) + { + this.Firstname = message.MessageText; + return; + } + + if (this.Lastname == null) + { + this.Lastname = message.MessageText; + return; + } + + if (this.EMail == null) + { + this.EMail = message.MessageText; + return; + } } - if (this.Lastname == null) + public async override Task Action(MessageResult message) { - this.Lastname = message.MessageText; - return; + var call = message.GetData(); + await message.ConfirmAction(); + + if (call == null) return; + + switch (call.Value) + { + case "back": + var start = new Start(); + await this.NavigateTo(start); + break; + } } - if (this.EMail == null) + public async override Task Render(MessageResult message) { - this.EMail = message.MessageText; - return; - } + if (this.Firstname == null) + { + await this.Device.Send("Please sent your firstname:"); + return; + } + if (this.Lastname == null) + { + await this.Device.Send("Please sent your lastname:"); + return; + } + + if (this.EMail == null) + { + await this.Device.Send("Please sent your email address:"); + return; + } + + string s = ""; + + s += "Firstname: " + this.Firstname + "\r\n"; + s += "Lastname: " + this.Lastname + "\r\n"; + s += "E-Mail: " + this.EMail + "\r\n"; + + ButtonForm bf = new ButtonForm(); + bf.AddButtonRow(new ButtonBase("Back", new CallbackData("a", "back").Serialize())); + + await this.Device.Send("Your details:\r\n" + s, bf); + } } - -public async override Task Action(MessageResult message) -{ - var call = message.GetData(); - - await message.ConfirmAction(); - - if (call == null) - return; - - switch (call.Value) - { - case "back": - - var start = new Start(); - - await this.NavigateTo(start); - - break; - - } - - -} - -public async override Task Render(MessageResult message) -{ - if (this.Firstname == null) - { - await this.Device.Send("Please sent your firstname:"); - return; - } - - if (this.Lastname == null) - { - await this.Device.Send("Please sent your lastname:"); - return; - } - - if (this.EMail == null) - { - await this.Device.Send("Please sent your email address:"); - return; - } - - - String s = ""; - - s += "Firstname: " + this.Firstname + "\r\n"; - s += "Lastname: " + this.Lastname + "\r\n"; - s += "E-Mail: " + this.EMail + "\r\n"; - - ButtonForm bf = new ButtonForm(); - bf.AddButtonRow(new ButtonBase("Back", new CallbackData("a", "back").Serialize())); - - await this.Device.Send("Your details:\r\n" + s, bf); -} - - -} - ``` -There is also a second example, where every of these 3 inputs gets requested by a different formular (class). Just for imagination of the possiblites. -Cause its to much Text, i didnt have added it here. You will find it under [TelegramBotBaseTest/Tests/Register/PerStep.cs](TelegramBotBaseTest/Tests/Register/PerStep.cs) -Beginn there and navigate your way through these Steps in the subfolder. - - ---- - +[Another case](TelegramBotBase.Test/Tests/Register/PerStep.cs), where every of these 3 inputs gets requested by a +different forms. Just for +imagination of the possibilities. ## Forms -There are some default types of forms to make the interaction with users easier. -For now we have the following: +There are some default forms to make the interaction with users easier. - [AlertDialog](#alert-dialog) - Just a simple dialog with one Button. + Just a simple dialog with one Button. - [AutoCleanForm](#autocleanform) - A form which needs to be derived from. It will be delete all in the context sent messages to the user after every new message or on leaving the formular and navigates somewhere else. - 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. + A form which needs to be derived from. It will be delete all in the context sent messages to the user after every new + message or on leaving the form and navigates somewhere else. + 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) - A simple dialog which will show a message and then waits for a text input (response). + A simple dialog which will show a message and then wait 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 - - -``` + +```csharp AlertDialog ad = new AlertDialog("This is a message", "Ok"); ad.ButtonClicked += async (s, en) => @@ -783,234 +591,211 @@ ad.ButtonClicked += async (s, en) => }; await this.NavigateTo(ad); - ``` - ### AutoCleanForm -No example yet +Just try it by youself. ### Prompt Dialog - - - - -``` + +```csharp PromptDialog pd = new PromptDialog("Please tell me your name ?"); pd.Completed += async (s, en) => { - await this.Device.Send("Hello " + pd.Value); + await this.Device.Send("Hello " + pd.Value); }; await this.OpenModal(pd); - ``` ### Confirm Dialog - - - - -``` + +```csharp ConfirmDialog cd = new ConfirmDialog("Please confirm", new ButtonBase("Ok", "ok"), new ButtonBase("Cancel", "cancel")); cd.ButtonClicked += async (s, en) => { 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 cd.NavigateTo(tf); }; await this.NavigateTo(cd); - ``` - ## Controls ### Progress Bar - + ### Calendar Picker - - + + + ### Month Picker - - - - + + + + ### Tree View - - - - - + + + + + ### Toggle Button - + + ### Button Grid - + + #### Paging & Searching - + + ### Tagged Button Grid - + + ### Checked Button List - + + ### Multi Toggle Button - + + ## 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. +For 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. +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. - - -``` +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. +```csharp public class Start : SplitterForm { - public override async Task Open(MessageResult e) - { - var st = new Menu(); - await this.NavigateTo(st); + public override async Task Open(MessageResult e) + { + var st = new Menu(); + await this.NavigateTo(st); - return true; - } + return true; + } + public override async Task OpenGroup(MessageResult e) + { + var st = new Groups.LinkReplaceTest(); + await this.NavigateTo(st); - public override async Task OpenGroup(MessageResult e) - { - var st = new Groups.LinkReplaceTest(); - await this.NavigateTo(st); + return true; + } - return true; - } - - public override Task OpenChannel(MessageResult e) - { - return base.OpenChannel(e); - } + public override Task OpenChannel(MessageResult e) + { + return base.OpenChannel(e); + } - public override Task OpenSupergroup(MessageResult e) - { - return base.OpenSupergroup(e); - } + public override Task 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. +For managing groups there's a base class called `GroupForm`. This one has special events which should make it +easier to work with groups and channels. +In the Example project there's a simple example for deleting an url written by a user and incrementing an internal +counter. At +every url he writes he gets blocked for a small amount of time and the message gets deleted. At 3 failures, the user +gets +kicked of the group and blocked. -``` +```csharp public class GroupForm : FormBase { - - - public override async Task Load(MessageResult message) + public override async Task Load(MessageResult message) + { + switch (message.MessageType) { - switch (message.MessageType) - { - case Telegram.Bot.Types.Enums.MessageType.ChatMembersAdded: + case Telegram.Bot.Types.Enums.MessageType.ChatMembersAdded: + await OnMemberChanges(new MemberChangeEventArgs(Telegram.Bot.Types.Enums.MessageType.ChatMembersAdded, message, message.RawMessageData.Message.NewChatMembers)); + break; - 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: - case Telegram.Bot.Types.Enums.MessageType.ChannelCreated: - - await OnGroupChanged(new GroupChangedEventArgs(message.MessageType, message)); - - break; - - default: - - OnMessage(message); - - 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: + case Telegram.Bot.Types.Enums.MessageType.ChannelCreated: + await OnGroupChanged(new GroupChangedEventArgs(message.MessageType, message)); + break; + default: + OnMessage(message); + break; } + } - public virtual async Task OnMemberChanges(MemberChangeEventArgs e) - { + public virtual async Task OnMemberChanges(MemberChangeEventArgs e) + { + } - } + public virtual async Task OnGroupChanged(GroupChangedEventArgs e) + { + } - - public virtual async Task OnGroupChanged(GroupChangedEventArgs e) - { - - } - - - public virtual async Task OnMessage(MessageResult 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. +Depending on the use-cases 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 bot restarts (i.e. due to updates) or crashes. +For this we have some structures which fits into the current environment. ### 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: +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. #### 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 -var bb = BotBaseBuilder +Is easy to use and useful for simple structures like basic datatypes. Won't work for complex ones like generics. + +```csharp +var bot = BotBaseBuilder .Create() .WithAPIKey("{YOUR API KEY}") .DefaultMessageLoop() @@ -1024,18 +809,16 @@ var bb = BotBaseBuilder .UseEnglish() .Build(); -//Start your Bot -bb.Start(); - +bot.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 -var bb = BotBaseBuilder +Is easy to use too, but works for complex datatypes, because it saves there namespaces and additional type info +into the JSON file too. + +```csharp +var bot = BotBaseBuilder .Create() .WithAPIKey("{YOUR API KEY}") .DefaultMessageLoop() @@ -1049,19 +832,15 @@ var bb = BotBaseBuilder .UseEnglish() .Build(); -//Start your Bot -bb.Start(); - +bot.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: +The last one, should work like the others. -``` -//Prepare the System -var bb = BotBaseBuilder +```csharp +var bot = BotBaseBuilder .Create() .WithAPIKey("{YOUR API KEY}") .DefaultMessageLoop() @@ -1075,75 +854,70 @@ var bb = BotBaseBuilder .UseEnglish() .Build(); -//Start your Bot -bb.Start(); - +bot.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. +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. - -``` +Is the basic StateMachine interface, it has two methods `SaveFormStates(SaveStatesEventArgs e)` +and `StateContainerLoadFormStates()`, nothing fancy, just simple calls. Implement both methods with your own +serialization process. +```csharp 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). +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. -``` - +```csharp 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: +If you don't want to implement the `IStateForm` interface, because there are maybe *just* one or two properties you want +to +keep and restore, use the following attributes. #### 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. - -``` +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. +```csharp [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. - -``` +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. +```csharp [IgnoreState] public class Registration : STForm { - } - ``` ## Navigation and NavigationController @@ -1153,96 +927,104 @@ public class Registration : STForm As from earlier topics on this readme you already know the default way for (cross) navigation between Forms. It will look something like this: -``` - +```csharp var f = new FormBase(); await this.NavigateTo(f); - ``` -Depending on the model and structure of your bot it can make sense, to have more linear navigation instead of "cross" navigation. +Depending on the model and structure of your bot it can make sense, to have more linear navigation instead of *cross* +navigation. -In example you have a bot which shows a list of football teams. And when clicking on it you want to open the team details and latest matches. +For example, you have a bot which shows a list of football teams. And when clicking on it you want to open the team +details and latest matches. -After the matches you want to maybe switch to a different teams and take a look at their statistics and matches. +After the matches, you want to maybe switch to different teams and take a look at their statistics and matches. -At some point, you "just" want to get back to the first team so like on Android your clicking the "back" button multiple times. +At some point, you *just* want to get back to the first team so like on Android you're clicking the "back" button +multiple +times. This can become really complicated, when not having some controller below which handle these "Push/Pop" calls. -Thats why I introduced a NavigationController class which manages these situations and the stack. +Thats why we hace a NavigationController class which manages these situations and the stack. +### Usage -### How to use ? +First, you need to create a NavigationController instance at the same position in code, where you want to start the +navigation. -First you need to create a NavigationController instance at the same position in code, where you want to start the navigation. +You will use the current FormBase instance as a root class within the constructor, so you can later come back to this +one. -You will use the current FormBase instance as a root class within the constructor. So you can later come back to this one. +**Tip**: *You can add also a completely new instance of i.e. a main menu form here to get back to it then. So you are +free to choose.* -**Tip**: *You can add also a completely new instance of i.e. a main menu form here to get back to it then. So you are free to choose.* - -We are using the same FormBase instance as above. - - -``` +We are using the same `FormBase` instance as above. +```csharp var nc = new NavigationController(this); - var f = new FormBase(); -//Replace the current form in the context with the controller. +// Replace the current form in the context with the controller. await this.NavigateTo(nc); -//Push the new from onto the stack to render it +// Push the new from onto the stack to render it nc.PushAsync(f); - ``` +Later to open a new form use `PushAsync` again: -Later to open a new form use PushAsync again: - -``` - +```csharp await this.NavigationController.PushAsync(newForm); - ``` -When you want to go back one Form on the stack use PopAsync: - - -``` +When you want to go back one Form on the stack use `PopAsync`: +```csharp await this.NavigationController.PopAsync(); - ``` -**Notice**: *By default the NavigationController has ForceCleanupOnLastPop enabled, which means that when the stack is again at 1 (due to PopAsync or PopToRootAsync calls) it will replace the controller automatically with the root form you have given to the constructor at the beginning.* +**Notice**: *By default the `NavigationController` has `ForceCleanupOnLastPop` enabled, which means that when the stack +is +again at 1 (due to `PopAsync` or `PopToRootAsync` calls) it will replace the controller automatically with the root form +you +have given to the constructor at the beginning.* +## Extensions +### TelegramBotBase.Extensions.Images +Extends the base package with some additional image methods like SendPhoto (using Bitmap) +[![NuGet version (TelegramBotBase)](https://img.shields.io/nuget/v/TelegramBotBase.Extensions.Images.svg?style=flat-square)](https://www.nuget.org/packages/TelegramBotBase.Extensions.Images/) +[![Downloads](https://img.shields.io/nuget/dt/TelegramBotBase.Extensions.Images.svg?style=flat-square&label=Package%20Downloads)](https://www.nuget.org/packages/TelegramBotBase.Extensions.Images) + +[https://www.nuget.org/packages/TelegramBotBase.Extensions.Images/](https://www.nuget.org/packages/TelegramBotBase.Extensions.Images/) + +### TelegramBotBase.Extensions.Serializer.Database.MSSQL + +A session serializer for Microsoft SQL Server. + +[![NuGet version (TelegramBotBase)](https://img.shields.io/nuget/v/TelegramBotBase.Extensions.Serializer.Database.MSSQL.svg?style=flat-square)](https://www.nuget.org/packages/TelegramBotBase.Extensions.Serializer.Database.MSSQL/) +[![Downloads](https://img.shields.io/nuget/dt/TelegramBotBase.Extensions.Serializer.Database.MSSQL.svg?style=flat-square&label=Package%20Downloads)](https://www.nuget.org/packages/TelegramBotBase.Extensions.Serializer.Database.MSSQL) + +[https://www.nuget.org/packages/TelegramBotBase.Extensions.Serializer.Database.MSSQL/](https://www.nuget.org/packages/TelegramBotBase.Extensions.Serializer.Database.MSSQL/) ## Examples -You find all example projects in the "Examples" subfolder. +You can find all example projects in the "Examples" subfolder. -- [Examples/SystemCommandsBot/](Examples/SystemCommandsBot/) +- [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. +Example using minimal dotnet console template with EntityFramework and Dependency Injection. -- [Examples/JoinHiderBot/](Examples/JoinHiderBot/) +- [Examples/EFCoreBot](Examples/EFCoreBot) + +Will allow you to run specific system commands or run/kill processes via Bot. Also has a simple authentication mechanism +with one password. + +- [Examples/JoinHiderBot](Examples/JoinHiderBot) Will delete Join and Leave messages automatically in groups. -- [Examples/AsyncFormUpdates/](Examples/AsyncFormUpdates/) - -When you want to update forms async without any user interaction (message/action) before. Use the new InvokeMessageLoop method of BotBase. - ---- - -I will add more notes to it soon, so stay tuned. - -Warm regards - -Florian Dahn - +- [Examples/AsyncFormUpdates](Examples/AsyncFormUpdates) diff --git a/TelegramBotBase.Extensions.Images/README.md b/TelegramBotBase.Extensions.Images/README.md index cdef1e3..2611a85 100644 --- a/TelegramBotBase.Extensions.Images/README.md +++ b/TelegramBotBase.Extensions.Images/README.md @@ -1,8 +1,8 @@ # TelegramBotBase.Extensions.Images [![NuGet version (TelegramBotBase)](https://img.shields.io/nuget/v/TelegramBotBase.Extensions.Images.svg?style=flat-square)](https://www.nuget.org/packages/TelegramBotBase.Extensions.Images/) -[![telegram chat](https://img.shields.io/badge/Support_Chat-Telegram-blue.svg?style=flat-square)](https://www.t.me/tgbotbase) +[![Telegram chat](https://img.shields.io/badge/Support_Chat-Telegram-blue.svg?style=flat-square)](https://www.t.me/tgbotbase) -[![license](https://img.shields.io/github/license/MajMcCloud/telegrambotframework.svg?style=flat-square&maxAge=2592000&label=License)](https://raw.githubusercontent.com/MajMcCloud/TelegramBotFramework/master/LICENCE.md) -[![downloads](https://img.shields.io/nuget/dt/TelegramBotBase.Extensions.Images.svg?style=flat-square&label=Package%20Downloads)](https://www.nuget.org/packages/TelegramBotBase.Extensions.Images) +[![License](https://img.shields.io/github/license/MajMcCloud/telegrambotframework.svg?style=flat-square&maxAge=2592000&label=License)](https://raw.githubusercontent.com/MajMcCloud/TelegramBotFramework/master/LICENCE.md) +[![Package Downloads](https://img.shields.io/nuget/dt/TelegramBotBase.Extensions.Images.svg?style=flat-square&label=Package%20Downloads)](https://www.nuget.org/packages/TelegramBotBase.Extensions.Images) diff --git a/TelegramBotBase.Extensions.Serializer.Database.MSSQL/README.md b/TelegramBotBase.Extensions.Serializer.Database.MSSQL/README.md index d35b330..1a7f027 100644 --- a/TelegramBotBase.Extensions.Serializer.Database.MSSQL/README.md +++ b/TelegramBotBase.Extensions.Serializer.Database.MSSQL/README.md @@ -1,19 +1,19 @@ # TelegramBotBase.Extensions.Serializer.Database.MSSQL [![NuGet version (TelegramBotBase)](https://img.shields.io/nuget/v/TelegramBotBase.Extensions.Serializer.Database.MSSQL.svg?style=flat-square)](https://www.nuget.org/packages/TelegramBotBase.Extensions.Serializer.Database.MSSQL/) -[![telegram chat](https://img.shields.io/badge/Support_Chat-Telegram-blue.svg?style=flat-square)](https://www.t.me/tgbotbase) +[![Telegram chat](https://img.shields.io/badge/Support_Chat-Telegram-blue.svg?style=flat-square)](https://www.t.me/tgbotbase) -[![license](https://img.shields.io/github/license/MajMcCloud/telegrambotframework.svg?style=flat-square&maxAge=2592000&label=License)](https://raw.githubusercontent.com/MajMcCloud/TelegramBotFramework/master/LICENCE.md) -[![downloads](https://img.shields.io/nuget/dt/TelegramBotBase.Extensions.Serializer.Database.MSSQL.svg?style=flat-square&label=Package%20Downloads)](https://www.nuget.org/packages/TelegramBotBase.Extensions.Serializer.Database.MSSQL) +[![License](https://img.shields.io/github/license/MajMcCloud/telegrambotframework.svg?style=flat-square&maxAge=2592000&label=License)](https://raw.githubusercontent.com/MajMcCloud/TelegramBotFramework/master/LICENCE.md) +[![Package Downloads](https://img.shields.io/nuget/dt/TelegramBotBase.Extensions.Serializer.Database.MSSQL.svg?style=flat-square&label=Package%20Downloads)](https://www.nuget.org/packages/TelegramBotBase.Extensions.Serializer.Database.MSSQL) ## How to use -``` +```csharp using TelegramBotBase.Extensions.Serializer.Database.MSSQL; -var bb = BotBaseBuilder +var bot = BotBaseBuilder .Create() .WithAPIKey(APIKey) .DefaultMessageLoop() @@ -24,6 +24,5 @@ var bb = BotBaseBuilder .UseEnglish() .Build(); -bb.Start(); - -``` \ No newline at end of file +bot.Start(); +```