{"id":25004365,"url":"https://github.com/majmccloud/telegrambotframework","last_synced_at":"2025-04-04T19:12:21.092Z","repository":{"id":38107188,"uuid":"134098890","full_name":"MajMcCloud/TelegramBotFramework","owner":"MajMcCloud","description":"This is a context based application framework for the C# TelegramBot library.","archived":false,"fork":false,"pushed_at":"2024-05-19T12:16:22.000Z","size":6287,"stargazers_count":133,"open_issues_count":3,"forks_count":39,"subscribers_count":7,"default_branch":"master","last_synced_at":"2024-05-20T12:17:11.031Z","etag":null,"topics":["bot","bot-commands","botbase","context","controls","dialog","forms","groups","navigates","nuget","session","session-serialization","state-machine","telegram","telegram-bot","telegram-bot-api","telegram-bots","telegram-group","telegrambot"],"latest_commit_sha":null,"homepage":"https://www.t.me/tgbotbase","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/MajMcCloud.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":null,"custom":["https://paypal.me/majmccloud"]}},"created_at":"2018-05-19T21:22:30.000Z","updated_at":"2024-05-27T22:55:58.356Z","dependencies_parsed_at":"2023-02-15T12:01:43.692Z","dependency_job_id":"2a0f0d22-44bc-4a90-ad15-33677a94020c","html_url":"https://github.com/MajMcCloud/TelegramBotFramework","commit_stats":{"total_commits":410,"total_committers":9,"mean_commits":45.55555555555556,"dds":"0.17804878048780493","last_synced_commit":"284634d7d1ae0f6dd08f8e4ea9a91a196c37492d"},"previous_names":[],"tags_count":31,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MajMcCloud%2FTelegramBotFramework","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MajMcCloud%2FTelegramBotFramework/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MajMcCloud%2FTelegramBotFramework/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MajMcCloud%2FTelegramBotFramework/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/MajMcCloud","download_url":"https://codeload.github.com/MajMcCloud/TelegramBotFramework/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247234923,"owners_count":20905854,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bot","bot-commands","botbase","context","controls","dialog","forms","groups","navigates","nuget","session","session-serialization","state-machine","telegram","telegram-bot","telegram-bot-api","telegram-bots","telegram-group","telegrambot"],"created_at":"2025-02-04T23:52:18.139Z","updated_at":"2025-04-04T19:12:21.074Z","avatar_url":"https://github.com/MajMcCloud.png","language":"C#","readme":"# .NET Telegram Bot Framework - Context based addon\n\n[![NuGet version (TelegramBotBase)](https://img.shields.io/nuget/v/TelegramBotBase.svg?style=flat-square)](https://www.nuget.org/packages/TelegramBotBase/)\n[![Telegram chat](https://img.shields.io/badge/Support_Chat-Telegram-blue.svg?style=flat-square)](https://www.t.me/tgbotbase)\n\n[![License](https://img.shields.io/github/license/MajMcCloud/telegrambotframework.svg?style=flat-square\u0026maxAge=2592000\u0026label=License)](https://raw.githubusercontent.com/MajMcCloud/TelegramBotFramework/master/LICENCE.md)\n[![Package Downloads](https://img.shields.io/nuget/dt/TelegramBotBase.svg?style=flat-square\u0026label=Package%20Downloads)](https://www.nuget.org/packages/TelegramBotBase)\n\n**Showcase: [@TGBaseBot](https://t.me/TGBaseBot)**\n\n**Support group: [@tgbotbase](https://t.me/tgbotbase)**\n\n**Discord Server: [https://discord.gg/V3PxreDYfE](https://discord.gg/V3PxreDYfE)**\n\n**Releases: [GitHub](https://github.com/MajMcCloud/TelegramBotFramework/releases)**\n\n**Need your own bot? Get in touch https://t.me/botbasebuilder**\n\n**on X: @florian_zevedei**\n\n## Donate\n\nPaypal: [https://paypal.me/majmccloud](https://paypal.me/majmccloud)\n\nBitcoin: `1GoUJYMwAvBipQTfw2FKydAz12J8RDyeJs` / `bc1qqwlp0p5ley29lsu6jhe0qv7s7963kfc7d0m53d`\n\nEtherium: `0xAf3835104c2C3E5b3e721FA2c7365955e87DB931`\n\nLitecoin: `LRhF1eB7kneFontcDRDU8YjJhEm2GoYHch`\n\nDashcoin: `XudiUwWtSmAJj1QDdVW7jocQumJFLsyoGZ`\n\nTron: `TYVZSykaVT1nKZnz9hjDgBRNB9VavU1bpW`\n\nBitTorrent: `TYVZSykaVT1nKZnz9hjDgBRNB9VavU1bpW`\n\n---\n\n## Index\n\n- [Quick start](#quick-start)\n- [Simplified builder](#simplified-builder)\n- [Features](#features)\n    * [System calls \u0026 bot commands](#system-calls--bot-commands)\n    * [Text messages handling](#text-messages)\n    * [Buttons](#buttons)\n    * [Custom controls](#custom-controls)\n    * [Forms advanced](#forms-advanced)\n- [Special Forms](#forms)\n    * [AlertDialog](#alert-dialog)\n    * [AutoCleanForm](#autocleanform)\n    * [PromptDialog](#prompt-dialog)\n    * [ConfirmDialog](#confirm-dialog)\n- [Controls](#controls)\n    * [Label](#label)\n    * [ProgressBar](#progress-bar)\n    * [CalendarPicker](#calendar-picker)\n    * [MonthPicker](#month-picker)\n    * [TreeView](#tree-view)\n    * [ToggleButton](#toggle-button)\n    * [ButtonGrid](#button-grid)\n        * [Paging and Searching](#paging--searching)\n    * [TaggedButtonGrid](#tagged-button-grid)\n    * [CheckedButtonList](#checked-button-list)\n    * [MultiToggleButton](#multi-toggle-button)\n- [Localizations](#localizations)\n- [Groups](#groups)\n    * [SplitterForm](#splitter-form)\n    * [GroupForm](#group-form)\n- [State Machine and Session Serialization (v3.0.0)](#statemachine-and-sessions)\n    * [StateMachines](#statemachines)\n        * [SimpleJSONStateMachine](#simplejsonstatemachine)\n        * [JSONStateMachine](#jsonstatemachine)\n        * [XMLStateMachine](#xmlstatemachine)\n    * [Interfaces](#interfaces)\n        * [IStateMachine](#istatemachine)\n        * [IStateForm](#istateform)\n    * [Attributes](#attributes)\n        * [SaveState](#savestate)\n        * [IgnoreState](#ignorestate)\n- [Navigation and NavigationController (v4.0.0)](#navigation-and-navigationcontroller)\n    * [As of Now](#as-of-now)\n    * [Usage](#usage)\n- [Extensions](#extensions)\n- [Test Project](#test-project)\n- [Examples](#examples)\n\n---\n\n## Quick start\n\nFirst of all, create a new empty dotnet console project and paste some code:\n\n```csharp\n// public async Task Main(string[] args)\n\nvar bot = BotBaseBuilder\n    .Create()\n    .WithAPIKey(\"{YOUR API KEY}\") // do not store your API key as plain text in project sources\n    .DefaultMessageLoop()\n    .WithStartForm\u003cStartForm\u003e()\n    .NoProxy()\n    .CustomCommands(a =\u003e\n    {\n        a.Start(\"Starts the bot\");\n    })\n    .NoSerialization()\n    .UseEnglish()\n    .UseSingleThread()\n    .Build();\n\n// Upload bot commands to BotFather\nawait bot.UploadBotCommands();\n\n// Start your Bot\nawait bot.Start();\n```\n\nThe `BotBase` class will manage a lot of things for you, like bot commands, action events and so on.\n`StartForm` is your first form which every user will get internally redirected to, *just like a start page*.\nIt needs to be a subclass of `FormBase` you will find in namespace `TelegramBotBase.Base`\n\nEvery `Form` has some events which will get raised at specific times.\nIn every form, you are able to get notes about\nthe *Remote Device*,\nlike ChatId and other stuff your carrying.\nFrom there you build up your bots:\n\n```csharp\npublic class Start : FormBase\n{\n     \n    public Start()\n    {\n        //Additional event handlers\n        Init += Start_Init;\n        Opened += Start_Opened;\n        Closed += Start_Closed;\n    }\n\n    // Gets invoked on initialization, before navigation\n    private async Task Start_Init(object sender, Args.InitEventArgs e)\n    {\n    }\n\n    // Gets invoked after opened\n    private async Task Start_Opened(object sender, EventArgs e)\n    {\n    }\n\n    // Gets invoked after form has been closed\n    private async Task Start_Closed(object sender, EventArgs e)\n    {\n    }\n\n    // Gets invoked during Navigation to this form\n    public override async Task PreLoad(MessageResult message)\n    {\n    }\n\n    // Gets invoked on every Message/Action/Data in this context\n    public override async Task Load(MessageResult message)\n    {\n        // `Device` is a wrapper for current chat - you can easily respond to the user\n        await this.Device.Send(\"Hello world!\");\n    }\n\n    // Gets invoked on edited messages\n    public override async Task Edited(MessageResult message)\n    {\n    }\n\n    // Gets invoked on Button clicks\n    public override async Task Action(MessageResult message)\n    {\n    }\n\n    // Gets invoked on Data uploades by the user (of type Photo, Audio, Video, Contact, Location, Document)\n    public override async Task SentData(DataResult data)\n    {\n    }\n\n    //Gets invoked on every Message/Action/Data to render Design or Response \n    public override async Task Render(MessageResult message)\n    {\n    }\n}\n```\n\nSend a message after loading a specific form:\n\n```csharp\nawait this.Device.Send(\"Hello world!\");\n```\n\nWant to go to a different form?\nGo ahead, create it, initialize it and navigate to it:\n\n```csharp\nvar form = new TestForm();\nawait this.NavigateTo(form);\n```\n\n## Simplified builder\n\nWhen migrating from a previous version or starting completely new, all these options can be a bit overwhelming.\nThere's a function called `QuickStart` that simplifies building a bit.\n\n```csharp\nvar bot = BotBaseBuilder\n    .Create()\n    .QuickStart\u003cStartForm\u003e(\"{YOUR API KEY}\")\n    .Build();\n\nawait bot.Start();\n```\n\n## Features\n\n### System calls \u0026 bot commands\n\nUsing BotFather you can add *Commands* to your bot. The user will see them as popups in a dialog.\nBefore starting (and later, for sure), you could add them to your BotBase.\nIf the message contains a command, a special *event handler* will get raised.\n\nBelow we have 4 commands.\n\n`/start` - opens the start form\n\n`/form1` - navigates in this context to form1\n\n`/form2` - navigates in this context to form2\n\n`/params` - demonstrates the use of parameters per command (i.e. /params 1 2 3 test ...)\n\n```csharp\nvar bot = BotBaseBuilder\n    .Create()\n    .WithAPIKey(\"{YOUR API KEY}\")\n    .DefaultMessageLoop()\n    .WithStartForm\u003cStart\u003e()\n    .NoProxy()\n    .CustomCommands(a =\u003e\n    {\n        a.Start(\"Starts the bot\");\n        a.Add(\"form1\",\"Opens test form 1\");\n        a.Add(\"form2\", \"Opens test form 2\");\n        a.Add(\"params\", \"Returns all send parameters as a message.\");\n    })\n    .NoSerialization()\n    .UseEnglish()\n    .UseSingleThread()\n    .Build();\n\nbot.BotCommand += async (s, en) =\u003e\n{\n    switch (en.Command)\n    {\n        case \"/form1\":\n            var form1 = new TestForm();\n            await en.Device.ActiveForm.NavigateTo(form1);\n            break;\n\n        case \"/form2\":\n            var form2 = new TestForm2();\n            await en.Device.ActiveForm.NavigateTo(form2);\n            break;\n\n        case \"/params\":\n            string m = en.Parameters.DefaultIfEmpty(\"\").Aggregate((a, b) =\u003e a + \" and \" + b);\n            await en.Device.Send(\"Your parameters are \" + m, replyTo: en.Device.LastMessage);\n            break;\n    }\n};\n\nawait bot.UploadBotCommands() \n\nawait bot.Start();\n```\n\nOn every input the user is sending back to the bot, the `Action` event gets raised. So here we could manage to send\nsomething back to him.\n\n### Text messages\n\n\u003cimg src=\".github/images/example1.PNG\" /\u003e\n\n```csharp\npublic class SimpleForm : AutoCleanForm\n{\n    public SimpleForm()\n    {\n        DeleteSide = EDeleteSide.Both;\n        DeleteMode = EDeleteMode.OnLeavingForm;\n\n        Opened += SimpleForm_Opened;\n    }\n\n    private async Task SimpleForm_Opened(object sender, EventArgs e)\n    {\n        await Device.Send(\"Hello world! (send 'back' to get back to Start)\\r\\nOr\\r\\nhi, hello, maybe, bye and ciao\");\n    }\n\n    public override async Task Load(MessageResult message)\n    {\n        // message.MessageText will work also, cause it is a string you could manage a lot different scenerios here\n        var messageId = message.MessageId;\n\n        switch (message.Command)\n        {\n            case \"hello\":\n            case \"hi\":\n                // Send a simple message\n                await this.Device.Send(\"Hello you there !\");\n                break;\n    \n            case \"maybe\":\n                // Send a simple message and reply to the one of himself\n                await this.Device.Send(\"Maybe what?\", replyTo: messageId);\n                break;\n    \n            case \"bye\":\n            case \"ciao\":\n                // Send a simple message\n                await this.Device.Send(\"Ok, take care !\");\n                break;\n        }\n    }\n}\n```\n\n### Buttons\n\n\u003cimg src=\".github/images/example2.PNG\" /\u003e\n\n```csharp\npublic class ButtonTestForm : AutoCleanForm\n{\n    public ButtonTestForm()\n    {\n        this.DeleteMode = eDeleteMode.OnLeavingForm;\n        Opened += ButtonTestForm_Opened;\n    }\n\n    private async Task ButtonTestForm_Opened(object sender, EventArgs e)\n    {\n        await this.Device.Send(\"Hello world! (Click 'back' to get back to Start)\");\n    }\n\n    public override async Task Action(MessageResult message)\n    {\n        var call = message.GetData\u003cCallbackData\u003e();\n        await message.ConfirmAction();\n\n        if (call == null)\n            return;\n\n        message.Handled = true;\n\n        switch (call.Value)\n        {\n            case \"button1\":\n                await this.Device.Send(\"Button 1 pressed\");\n                break;\n\n            case \"button2\":\n                await this.Device.Send(\"Button 2 pressed\");\n                break;\n\n            case \"button3\":\n                await this.Device.Send(\"Button 3 pressed\");\n                break;\n\n            case \"button4\":\n                await this.Device.Send(\"Button 4 pressed\");\n                break;\n\n            case \"back\":\n                var st = new Start();\n                await this.NavigateTo(st);\n                break;\n\n            default:\n                message.Handled = false;\n                break;\n        }\n    }\n\n    public override async Task Render(MessageResult message)\n    {\n        ButtonForm btn = new ButtonForm();\n\n        btn.AddButtonRow(new ButtonBase(\"Button 1\", new CallbackData(\"a\", \"button1\").Serialize()), new ButtonBase(\"Button 2\", new CallbackData(\"a\", \"button2\").Serialize()));\n        btn.AddButtonRow(new ButtonBase(\"Button 3\", new CallbackData(\"a\", \"button3\").Serialize()), new ButtonBase(\"Button 4\", new CallbackData(\"a\", \"button4\").Serialize()));\n        btn.AddButtonRow(new ButtonBase(\"Google.com\", \"google\", \"https://www.google.com\"), new ButtonBase(\"Telegram\", \"telegram\", \"https://telegram.org/\"));\n        btn.AddButtonRow(new ButtonBase(\"Back\", new CallbackData(\"a\", \"back\").Serialize()));\n\n        await this.Device.Send(\"Click a button\", btn);\n    }\n}\n```\n\n### Custom controls\n\nThere is a bunch of ready to use controls. For example, progress bar.\n\n\u003cimg src=\".github/images/example3.PNG\" /\u003e\n\n```csharp\npublic class ProgressTest : AutoCleanForm\n{\n    public ProgressTest()\n    {\n        this.DeleteMode = eDeleteMode.OnLeavingForm;\n        Opened += ProgressTest_Opened;\n    }\n\n    private async Task ProgressTest_Opened(object sender, EventArgs e)\n    {\n        await this.Device.Send(\"Welcome to ProgressTest\");\n    }\n\n    public override async Task Action(MessageResult message)\n    {\n        var call = message.GetData\u003cCallbackData\u003e();\n        await message.ConfirmAction();\n\n        if (call == null) return;\n\n        TelegramBotBase.Controls.ProgressBar Bar = null;\n\n        switch (call.Value)\n        {\n            case \"standard\":\n                Bar = new TelegramBotBase.Controls.ProgressBar(0, 100, TelegramBotBase.Controls.ProgressBar.eProgressStyle.standard);\n                Bar.Device = this.Device;\n                break;\n\n            case \"squares\":\n                Bar = new TelegramBotBase.Controls.ProgressBar(0, 100, TelegramBotBase.Controls.ProgressBar.eProgressStyle.squares);\n                Bar.Device = this.Device;\n                break;\n\n            case \"circles\":\n                Bar = new TelegramBotBase.Controls.ProgressBar(0, 100, TelegramBotBase.Controls.ProgressBar.eProgressStyle.circles);\n                Bar.Device = this.Device;\n                break;\n\n            case \"lines\":\n                Bar = new TelegramBotBase.Controls.ProgressBar(0, 100, TelegramBotBase.Controls.ProgressBar.eProgressStyle.lines);\n                Bar.Device = this.Device;\n                break;\n\n            case \"squaredlines\":\n                Bar = new TelegramBotBase.Controls.ProgressBar(0, 100, TelegramBotBase.Controls.ProgressBar.eProgressStyle.squaredLines);\n                Bar.Device = this.Device;\n                break;\n\n            case \"start\":\n                var sf = new Start();\n                await sf.Init();\n                await this.NavigateTo(sf);\n                return;\n\n            default:\n                return;\n        }\n\n        // Render Progress bar and show some \"example\" progress\n        await Bar.Render();\n\n        this.Controls.Add(Bar);\n\n        for (int i = 0; i \u003c= 100; i++)\n        {\n            Bar.Value++;\n            await Bar.Render();\n\n            Thread.Sleep(250);\n        }\n    }\n\n    public override async Task Render(MessageResult message)\n    {\n        ButtonForm btn = new ButtonForm();\n\n        btn.AddButtonRow(new ButtonBase(\"Standard\", new CallbackData(\"a\", \"standard\").Serialize()), new ButtonBase(\"Squares\", new CallbackData(\"a\", \"squares\").Serialize()));\n        btn.AddButtonRow(new ButtonBase(\"Circles\", new CallbackData(\"a\", \"circles\").Serialize()), new ButtonBase(\"Lines\", new CallbackData(\"a\", \"lines\").Serialize()));\n        btn.AddButtonRow(new ButtonBase(\"Squared Line\", new CallbackData(\"a\", \"squaredlines\").Serialize()));\n        btn.AddButtonRow(new ButtonBase(\"Back to start\", new CallbackData(\"a\", \"start\").Serialize()));\n\n        await this.Device.Send(\"Choose your progress bar:\", btn);\n    }\n\n    public override async Task Closed()\n    {\n        foreach (var b in this.Controls)\n        {\n            await b.Cleanup();\n        }\n\n        await this.Device.Send(\"Ciao from ProgressTest\");\n    }\n}\n```\n\n### Forms advanced\n\nRegistration forms have never been so easy.\n\n\u003cimg src=\".github/images/example4.1.PNG\" /\u003e\n\n\u003cimg src=\".github/images/example4.2.PNG\" /\u003e\n\n\u003cimg src=\".github/images/example4.3.PNG\" /\u003e\n\n\u003cimg src=\".github/images/example4.4.PNG\" /\u003e\n\n```csharp\npublic class PerForm : AutoCleanForm\n{\n    public string EMail { get; set; }\n\n    public string Firstname { get; set; }\n\n    public string Lastname { get; set; }\n\n    public async override Task Load(MessageResult message)\n    {\n        if (string.IsNullOrWhiteSpace(message.MessageText)) return;\n\n        if (this.Firstname == null)\n        {\n            this.Firstname = message.MessageText;\n            return;\n        }\n\n        if (this.Lastname == null)\n        {\n            this.Lastname = message.MessageText;\n            return;\n        }\n\n        if (this.EMail == null)\n        {\n            this.EMail = message.MessageText;\n            return;\n        }\n    }\n\n    public async override Task Action(MessageResult message)\n    {\n        var call = message.GetData\u003cCallbackData\u003e();\n        await message.ConfirmAction();\n\n        if (call == null) return;\n\n        switch (call.Value)\n        {\n            case \"back\":\n                var start = new Start();\n                await this.NavigateTo(start);\n                break;\n        }\n    }\n\n    public async override Task Render(MessageResult message)\n    {\n        if (this.Firstname == null)\n        {\n            await this.Device.Send(\"Please sent your firstname:\");\n            return;\n        }\n\n        if (this.Lastname == null)\n        {\n            await this.Device.Send(\"Please sent your lastname:\");\n            return;\n        }\n\n        if (this.EMail == null)\n        {\n            await this.Device.Send(\"Please sent your email address:\");\n            return;\n        }\n\n        string s = \"\";\n\n        s += \"Firstname: \" + this.Firstname + \"\\r\\n\";\n        s += \"Lastname: \" + this.Lastname + \"\\r\\n\";\n        s += \"E-Mail: \" + this.EMail + \"\\r\\n\";\n\n        ButtonForm bf = new ButtonForm();\n        bf.AddButtonRow(new ButtonBase(\"Back\", new CallbackData(\"a\", \"back\").Serialize()));\n\n        await this.Device.Send(\"Your details:\\r\\n\" + s, bf);\n    }\n}\n```\n\n[Another case](TelegramBotBase.Test/Tests/Register/PerStep.cs), where every of these 3 inputs gets requested by \ndifferent forms. Just for\nimagination of the possibilities.\n\n## Forms\n\nThere are some default forms to make the interaction with users easier.\n\n- [AlertDialog](#alert-dialog)\n  Just a simple dialog with one Button.\n\n- [AutoCleanForm](#autocleanform)\n  A form which needs to be derived from. It will be delete all in the context sent messages to the user after every new\n  message or on leaving the form and navigates somewhere else.\n  Makes sense to create a *feeling* of a clean environment for the user. For instance if you have a multilevel menu.\n  This will remove the previously shown menu, and renders the new sub/top level.\n\n- [PromptDialog](#prompt-dialog)\n  A simple dialog which will show a message and then wait for a text input (response).\n\n- [ConfirmDialog](#confirm-dialog)\n  A simple dialog which is able to show multiple buttons and a text message. The user could select one option and will\n  get redirected to a different form, depending on the click.\n\n### Alert Dialog\n\n\u003cimg src=\".github/images/alertdialog.PNG\" /\u003e\n\n```csharp\nAlertDialog ad = new AlertDialog(\"This is a message\", \"Ok\");\n\nad.ButtonClicked += async (s, en) =\u003e\n{\n    var fto = new TestForm2();\n    await this.NavigateTo(fto);\n};\n\nawait this.NavigateTo(ad);\n```\n\n### AutoCleanForm\n\nJust try it by yourself.\n\n### Prompt Dialog\n\n\u003cimg src=\".github/images/promptdialog.PNG\" /\u003e\n\n```csharp\nPromptDialog pd = new PromptDialog(\"Please tell me your name ?\");\n\npd.Completed += async (s, en) =\u003e\n{\n    await this.Device.Send(\"Hello \" + pd.Value);\n};\n\nawait this.OpenModal(pd);\n```\n\n### Confirm Dialog\n\n\u003cimg src=\".github/images/confirmdialog.PNG\" /\u003e\n\n```csharp\nConfirmDialog cd = new ConfirmDialog(\"Please confirm\", new ButtonBase(\"Ok\", \"ok\"), new ButtonBase(\"Cancel\", \"cancel\"));\n\ncd.ButtonClicked += async (s, en) =\u003e\n{\n    var tf = new TestForm2();\n\n    // Remember only to navigate from the current running form. (here it is the prompt dialog, cause we have left the above already)\n    await cd.NavigateTo(tf);\n};\n\nawait this.NavigateTo(cd);\n```\n\n## Controls\n\n### Label\n\nA minimal control which allows to manage a classic \"text\" message and update its later without having to keep track of the message id.\n\n\u003cimg src=\".github/images/label.gif?raw=true\" style=\"width:500px\" /\u003e\n\nCheck the example project [TelegramBotBase.Test/Tests/Controls/LabelForm.cs](TelegramBotBase.Test/Tests/Controls/LabelForm.cs)\n\n### Progress Bar\n\n\u003cimg src=\".github/images/progressbar.PNG?raw=true\" /\u003e\n\nCheck the example project [TelegramBotBase.Test/Tests/ProgressTest.cs](TelegramBotBase.Test/Tests/ProgressTest.cs)\n\n### Calendar Picker\n\n\u003cimg src=\".github/images/calendarpicker.PNG?raw=true\" /\u003e\n\n\u003cimg src=\".github/images/calendarpicker.gif?raw=true\" /\u003e\n\n\nCheck the example project [TelegramBotBase.Test/Tests/Controls/CalendarPickerForm.cs](TelegramBotBase.Test/Tests/Controls/CalendarPickerForm.cs)\n\n### Month Picker\n\n\u003cimg src=\".github/images/monthpicker1.PNG?raw=true\" /\u003e\n\u003cimg src=\".github/images/monthpicker2.PNG?raw=true\" /\u003e\n\nCheck the example project [TelegramBotBase.Test/Tests/Controls/MonthPickerForm.cs](TelegramBotBase.Test/Tests/Controls/MonthPickerForm.cs)\n\n### Tree View\n\n\u003cimg src=\".github/images/treeview1.PNG?raw=true\" /\u003e\n\u003cimg src=\".github/images/treeview2.PNG?raw=true\" /\u003e\n\u003cimg src=\".github/images/treeview3.PNG?raw=true\" /\u003e\n\u003cimg src=\".github/images/treeview4.PNG?raw=true\" /\u003e    \n\nCheck the example project [TelegramBotBase.Test/Tests/Controls/TreeViewForms.cs](TelegramBotBase.Test/Tests/Controls/TreeViewForms.cs)\n\n### Toggle Button\n\n\u003cimg src=\".github/images/togglebutton.gif?raw=true\" /\u003e\n\nCheck the example project [TelegramBotBase.Test/Tests/Controls/ToggleButtonForm.cs](TelegramBotBase.Test/Tests/Controls/ToggleButtonForm.cs)\n\n### Button Grid\n\n\u003cimg src=\".github/images/buttongrid.gif?raw=true\" /\u003e\n\nCheck the example project [TelegramBotBase.Test/Tests/Controls/ButtonGridForm.cs](TelegramBotBase.Test/Tests/Controls/ButtonGridForm.cs)\n\n#### Paging \u0026 Searching\n\n\u003cimg src=\".github/images/buttongrid_pagingfilter.gif?raw=true\" /\u003e\n\nCheck the example project [TelegramBotBase.Test/Tests/Controls/ButtonGridPadingForm.cs](TelegramBotBase.Test/Tests/Controls/ButtonGridPagingForm.cs)\n\n### Tagged Button Grid\n\n\u003cimg src=\".github/images/taggedbuttongrid.gif?raw=true\" /\u003e\n\nCheck the example project [TelegramBotBase.Test/Tests/Controls/ButtonGridTagForm.cs](TelegramBotBase.Test/Tests/Controls/ButtonGridTagForm.cs)\n\n### Checked Button List\n\n\u003cimg src=\".github/images/checkedbuttonlist.gif?raw=true\" /\u003e\n\nCheck the example project [TelegramBotBase.Test/Tests/Controls/CheckedButtonListForm.cs](TelegramBotBase.Test/Tests/Controls/CheckedButtonListForm.cs)\n\n### Multi Toggle Button\n\n\u003cimg src=\".github/images/multitogglebutton.gif?raw=true\" /\u003e\n\nCheck the example project [TelegramBotBase.Test/Tests/Controls/MultiToggleButtonForm.cs](TelegramBotBase.Test/Tests/Controls/MultiToggleButtonForm.cs)\n\n\n## Localizations\n\nThe current available languages for controls are:\n\n- English\n- German\n- Persian\n- Russian\n\nYou can add other languages easily by creating a subclass of the [TelegramBotBase/Localizations/Localization.cs](TelegramBotBase/Localizations/Localization.cs) class.\n\nTo set the default language set the *Language* property on the static [TelegramBotBase/Localizations/Default.cs](TelegramBotBase/Localizations/Default.cs) instance.\n\n## Groups\n\nFor groups, there are multiple different tools which help to work with and allows bot also to manage\n\"Single-User\" chats and group chats.\n\n### Splitter Form\n\nAn easy way to switch between a *Single-User* form and one for managing a group is the SplitterForm base class.\nIt calls special methods which you can override and then move from there to the form you need.\n\nThe OpenGroup method is the *backup* if OpenChannel or OpenSupergroup is not overridden. Same for Open, it is \"backup\"\nif none of the previous methods has been overridden.\n\n```csharp\npublic class Start : SplitterForm\n{\n    public override async Task\u003cbool\u003e Open(MessageResult e)\n    {\n        var st = new Menu();\n        await this.NavigateTo(st);\n\n        return true;\n    }\n\n    public override async Task\u003cbool\u003e OpenGroup(MessageResult e)\n    {\n        var st = new Groups.LinkReplaceTest();\n        await this.NavigateTo(st);\n\n        return true;\n    }\n\n    public override Task\u003cbool\u003e OpenChannel(MessageResult e)\n    {\n        return base.OpenChannel(e);\n    }\n\n    public override Task\u003cbool\u003e OpenSupergroup(MessageResult e)\n    {\n        return base.OpenSupergroup(e);\n    }\n}\n```\n\n### Group Form\n\nFor managing groups there's a base class called `GroupForm`. This one has special events which should make it\neasier to work with groups and channels.\nIn the Example project there's a simple example for deleting an url written by a user and incrementing an internal\ncounter. At\nevery url he writes he gets blocked for a small amount of time and the message gets deleted. At 3 failures, the user\ngets\nkicked of the group and blocked.\n\n```csharp\npublic class GroupForm : FormBase\n{\n    public override async Task Load(MessageResult message)\n    {\n        switch (message.MessageType)\n        {\n            case Telegram.Bot.Types.Enums.MessageType.ChatMembersAdded:\n                await OnMemberChanges(new MemberChangeEventArgs(Telegram.Bot.Types.Enums.MessageType.ChatMembersAdded, message, message.RawMessageData.Message.NewChatMembers));\n                break;\n\n            case Telegram.Bot.Types.Enums.MessageType.ChatMemberLeft:\n                await OnMemberChanges(new MemberChangeEventArgs(Telegram.Bot.Types.Enums.MessageType.ChatMemberLeft, message, message.RawMessageData.Message.LeftChatMember));\n                break;\n\n            case Telegram.Bot.Types.Enums.MessageType.ChatPhotoChanged:\n            case Telegram.Bot.Types.Enums.MessageType.ChatPhotoDeleted:\n            case Telegram.Bot.Types.Enums.MessageType.ChatTitleChanged:\n            case Telegram.Bot.Types.Enums.MessageType.MigratedFromGroup:\n            case Telegram.Bot.Types.Enums.MessageType.MigratedToSupergroup:\n            case Telegram.Bot.Types.Enums.MessageType.MessagePinned:\n            case Telegram.Bot.Types.Enums.MessageType.GroupCreated:\n            case Telegram.Bot.Types.Enums.MessageType.SupergroupCreated:\n            case Telegram.Bot.Types.Enums.MessageType.ChannelCreated:\n                await OnGroupChanged(new GroupChangedEventArgs(message.MessageType, message));\n                break;\n\n            default:\n                OnMessage(message);\n                break;\n        }\n    }\n\n    public virtual async Task OnMemberChanges(MemberChangeEventArgs e)\n    {\n    }\n\n    public virtual async Task OnGroupChanged(GroupChangedEventArgs e)\n    {\n    }\n\n    public virtual async Task OnMessage(MessageResult e)\n    {\n    }\n}\n```\n\n## Statemachine and Sessions\n\nDepending on the use-cases and the overall structure of a Telegram Bot, it is essential to have some kind of session\nserialization or state machine to keep the user context after bot restarts (i.e. due to updates) or crashes.\nFor this, we have some structures which fit into the current environment.\n\n### Statemachines\n\nThere are actually 3 types of example state machines you could use. A state machine is a kind of serializer which saves\nthe important session data in a reusable structure like JSON or XML.\n\n#### SimpleJSONStateMachine\n\nIs easy to use and useful for simple structures like basic datatypes. Won't work for complex ones like generics.\n\n```csharp\nvar bot = BotBaseBuilder\n    .Create()\n    .WithAPIKey(\"{YOUR API KEY}\")\n    .DefaultMessageLoop()\n    .WithStartForm\u003cStartForm\u003e()\n    .NoProxy()\n    .CustomCommands(a =\u003e\n    {\n        a.Start(\"Starts the bot\");\n    })\n    .UseSimpleJSON(AppContext.BaseDirectory + \"config\\\\states.json\")\n    .UseEnglish()\n    .UseSingleThread()\n    .Build();\n\nawait bot.Start();\n```\n\n#### JSONStateMachine\n\nIt is easy to use too, but it works for complex datatypes, because it saves their namespaces and additional type info\ninto the JSON file.\n\n```csharp\nvar bot = BotBaseBuilder\n    .Create()\n    .WithAPIKey(\"{YOUR API KEY}\")\n    .DefaultMessageLoop()\n    .WithStartForm\u003cStartForm\u003e()\n    .NoProxy()\n    .CustomCommands(a =\u003e\n    {\n        a.Start(\"Starts the bot\");\n    })\n    .UseJSON(AppContext.BaseDirectory + \"config\\\\states.json\")\n    .UseEnglish()\n    .UseSingleThread()\n    .Build();\n\nawait bot.Start();\n```\n\n#### XMLStateMachine\n\nThe last one should work like the others.\n\n```csharp\nvar bot = BotBaseBuilder\n    .Create()\n    .WithAPIKey(\"{YOUR API KEY}\")\n    .DefaultMessageLoop()\n    .WithStartForm\u003cStartForm\u003e()\n    .NoProxy()\n    .CustomCommands(a =\u003e\n    {\n        a.Start(\"Starts the bot\");\n    })\n    .UseXML(AppContext.BaseDirectory + \"config\\\\states.xml\")\n    .UseEnglish()\n    .UseSingleThread()\n    .Build();\n\nawait bot.Start();\n```\n\n### Interfaces\n\nThere are two interfaces, one for the StateMachine itself, which is useful to build a custom one for a different\ndatatype and one for implementing into a form which should be invoked with events.\n\n#### IStateMachine\n\nIs the basic StateMachine interface, it has two methods `SaveFormStates(SaveStatesEventArgs e)`\nand `LoadFormStates()`, nothing fancy, just simple calls. Implement both methods with your own\nserialization process.\n\n```csharp\npublic interface IStateMachine\n{\n    void SaveFormStates(SaveStatesEventArgs e);\n\n    StateContainer LoadFormStates();\n}\n```\n\n#### IStateForm\n\nWhen implemented, this will invoke one of these two methods: `LoadState(LoadStateEventArgs e)`\nor `SaveState(SaveStateEventArgs e)`.\nThey have methods to load or save data from the statemachine of the current form.\n\n```csharp\npublic interface IStateForm\n{\n    void LoadState(LoadStateEventArgs e);\n    void SaveState(SaveStateEventArgs e);\n}\n```\n\n### Attributes\n\nIf you don't want to implement the `IStateForm` interface, because there are maybe *just* one or two properties you want\nto\nkeep and restore, use the following attributes.\n\n#### SaveState\n\nThis will let the engine know that you want to keep and restore this field automatically. Unlike the IStateForm\nmethods, you have no option to manipulate data.\n\n```csharp\n[SaveState]\npublic long UserId { get; set; }\n```\n\n#### IgnoreState\n\nDue to the fact that Attribute implementation and interaction is optional, you want to let the engine maybe know that\nyou don't want to keep a specific form. So it should get *lost*. This attribute will help you here, add it to the form\nclass, and it will not get serialized, even if it implements IStateForm or the SaveState attributes.\n\n```csharp\n[IgnoreState]\npublic class Registration : STForm\n{\n}\n```\n\n## Navigation and NavigationController\n\n### As of now\n\nAs from earlier topics on this readme you already know the default way for (cross) navigation between Forms.\nIt will look something like this:\n\n```csharp\nvar f = new FormBase();\nawait this.NavigateTo(f);\n```\n\nDepending on the model and structure of your bot, it can make sense, to have more linear navigation instead of *cross*-navigation.\n\nFor example, you have a bot which shows a list of football teams. And when clicking on it, you want to open the team\ndetails and the latest matches.\n\nAfter the matches, you want to maybe switch to different teams and take a look at their statistics and matches.\n\nAt some point, you *just* want to get back to the first team, so like on Android you're clicking the \"back\" button\nmultiple\ntimes.\n\nThis can become really complicated, when not having some controller below which handle these \"Push/Pop\" calls.\n\nThat's why we have a NavigationController class which manages these situations and the stack.\n\n### Usage\n\nFirst, you need to create a NavigationController instance at the same position in code, where you want to start the\nnavigation.\n\nYou will use the current FormBase instance as a root class within the constructor, so you can later come back to this\none.\n\n**Tip**: *You can also add a completely new instance of i.e. a main menu form here to get back to it then. So you are\nfree to choose.*\n\nWe are using the same `FormBase` instance as above.\n\n```csharp\nvar nc = new NavigationController(this);\nvar f = new FormBase();\n\n// Replace the current form in the context with the controller.\nawait this.NavigateTo(nc);\n\n// Push the new from onto the stack to render it\nnc.PushAsync(f);\n```\n\nLater to open a new form use `PushAsync` again:\n\n```csharp\nawait this.NavigationController.PushAsync(newForm);\n```\n\nWhen you want to go back one Form on the stack use `PopAsync`:\n\n```csharp\nawait this.NavigationController.PopAsync();\n```\n\n**Notice**: *By default the `NavigationController` has `ForceCleanupOnLastPop` enabled, which means that when the stack\nis\nagain at 1 (due to `PopAsync` or `PopToRootAsync` calls) it will replace the controller automatically with the root form\nyou\nhave given to the constructor at the beginning.*\n\n---\n\n## Extensions\n\n### TelegramBotBase.Extensions.Images\n\nExtends the base package with some additional image methods like SendPhoto (using Bitmap)\n\n[![NuGet version (TelegramBotBase)](https://img.shields.io/nuget/v/TelegramBotBase.Extensions.Images.svg?style=flat-square)](https://www.nuget.org/packages/TelegramBotBase.Extensions.Images/)\n[![Downloads](https://img.shields.io/nuget/dt/TelegramBotBase.Extensions.Images.svg?style=flat-square\u0026label=Package%20Downloads)](https://www.nuget.org/packages/TelegramBotBase.Extensions.Images)\n\nSource code: [TelegramBotBase.Extensions.Images/](/TelegramBotBase.Extensions.Images/)\n\nNuget package: [https://www.nuget.org/packages/TelegramBotBase.Extensions.Images/](https://www.nuget.org/packages/TelegramBotBase.Extensions.Images/)\n\n---\n\n### TelegramBotBase.Extensions.Images.IronSoftware\n\nExtends the base package with some additional image methods like SendPhoto (using Bitmap)\n\nImportant: This extension uses the IronSoftware drawing library.\n\n[![NuGet version (TelegramBotBase)](https://img.shields.io/nuget/v/TelegramBotBase.Extensions.Images.IronSoftware.svg?style=flat-square)](https://www.nuget.org/packages/TelegramBotBase.Extensions.Images.IronSoftware/)\n[![Downloads](https://img.shields.io/nuget/dt/TelegramBotBase.Extensions.Images.IronSoftware.svg?style=flat-square\u0026label=Package%20Downloads)](https://www.nuget.org/packages/TelegramBotBase.Extensions.Images.IronSoftware)\n\n[https://www.nuget.org/packages/TelegramBotBase.Extensions.Images.IronSoftware/](https://www.nuget.org/packages/TelegramBotBase.Extensions.Images.IronSoftware/)\n\nSource code: [TelegramBotBase.Extensions.Images.IronSoftware/](TelegramBotBase.Extensions.Images.IronSoftware/)\n\nNuget package: [https://www.nuget.org/packages/TelegramBotBase.Extensions.IronSoftware/](https://www.nuget.org/packages/TelegramBotBase.Extensions.IronSoftware/)\n\n---\n\nProject: [open source](TelegramBotBase.Extensions.Images/)\n\n### TelegramBotBase.Extensions.Images.IronSoftware\n\nExtends the base package with some additional image methods like SendPhoto (using Bitmap)\n\nImportant: This extension uses the IronSoftware drawing library.\n\n[![NuGet version (TelegramBotBase)](https://img.shields.io/nuget/v/TelegramBotBase.Extensions.Images.IronSoftware.svg?style=flat-square)](https://www.nuget.org/packages/TelegramBotBase.Extensions.Images.IronSoftware/)\n[![Downloads](https://img.shields.io/nuget/dt/TelegramBotBase.Extensions.Images.IronSoftware.svg?style=flat-square\u0026label=Package%20Downloads)](https://www.nuget.org/packages/TelegramBotBase.Extensions.Images.IronSoftware)\n\n[https://www.nuget.org/packages/TelegramBotBase.Extensions.Images.IronSoftware/](https://www.nuget.org/packages/TelegramBotBase.Extensions.Images.IronSoftware/)\n\nProject: [open source](TelegramBotBase.Extensions.Images.IronSoftware/)\n\n### TelegramBotBase.Extensions.Serializer.Database.MSSQL\n\nA session serializer for Microsoft SQL Server.\n\n[![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/)\n[![Downloads](https://img.shields.io/nuget/dt/TelegramBotBase.Extensions.Serializer.Database.MSSQL.svg?style=flat-square\u0026label=Package%20Downloads)](https://www.nuget.org/packages/TelegramBotBase.Extensions.Serializer.Database.MSSQL)\n\nSource code: [TelegramBotBase.Extensions.Serializer.Database.MSSQL/](/TelegramBotBase.Extensions.Serializer.Database.MSSQL/)\n\nNuget package: [https://www.nuget.org/packages/TelegramBotBase.Extensions.Serializer.Database.MSSQL/](https://www.nuget.org/packages/TelegramBotBase.Extensions.Serializer.Database.MSSQL/)\n\n---\n\n### TelegramBotBase.Extensions.Serializer.Database.PostgreSql\n\nA session serializer for PostgreSql Server.\n\n[![NuGet version (TelegramBotBase)](https://img.shields.io/nuget/v/TelegramBotBase.Extensions.Serializer.Database.PostgreSql.svg?style=flat-square)](https://www.nuget.org/packages/TelegramBotBase.Extensions.Serializer.Database.PostgreSql/)\n[![Downloads](https://img.shields.io/nuget/dt/TelegramBotBase.Extensions.Serializer.Database.PostgreSql.svg?style=flat-square\u0026label=Package%20Downloads)](https://www.nuget.org/packages/TelegramBotBase.Extensions.Serializer.Database.PostgreSql)\n\nSource code: [TelegramBotBase.Extensions.Serializer.Database.PostgreSql/](/TelegramBotBase.Extensions.Serializer.Database.PostgreSql/)\n\nNuget package: [https://www.nuget.org/packages/TelegramBotBase.Extensions.Serializer.Database.PostgreSql/](https://www.nuget.org/packages/TelegramBotBase.Extensions.Serializer.Database.PostgreSql/)\n\nCredits: [@Kataane](https://github.com/Kataane)\n\n---\n\nProject: [open source](TelegramBotBase.Extensions.Serializer.Database.MSSQL/)\n\n## Test Project\n\nThere is a \"TelegramBotBase.Test\" project inside the repository which includes minimal examples for all controls available.\nCheck it out to get a brief overview about all possibilities.\n\n\n- [TelegramBotBase.Test](TelegramBotBase.Test)\n\n## Examples\n\nYou can find all example projects in the \"Examples\" subfolder.\n\nWill allow you to run specific system commands or run/kill processes via Bot. Also has a simple authentication mechanism\nwith one password.\n\n- [Examples/SystemCommandsBot](Examples/SystemCommandsBot)\n\nExample using minimal dotnet console template with EntityFramework and Dependency Injection.\n\n- [Examples/EFCoreBot](Examples/EFCoreBot)\n\nWill delete Join and Leave messages automatically in groups.\n\n- [Examples/JoinHiderBot](Examples/JoinHiderBot)\n\nWhen you want to update forms async without any user interaction (message/action) before. Use the new InvokeMessageLoop\nmethod of BotBase.\n\n- [Examples/AsyncFormUpdates](Examples/AsyncFormUpdates)\n\n\nHaving already a web application and want to add a TelegramBot side-by-side with it running ? Here is an example how you could do it.\n\n- [Examples/BotAndWebApplication](Examples/BotAndWebApplication)\n\nWant to use Inline- and ReplyMarkup at the same time ? Here is an example how you can do that:\n\n- [Examples/InlineAndReplyCombination](Examples/InlineAndReplyCombination)\n\n\nAlpha: Full Dependency Injection example within this framework.\n\n- [Examples/DependencyInjection](Examples/DependencyInjection)\n","funding_links":["https://paypal.me/majmccloud"],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmajmccloud%2Ftelegrambotframework","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmajmccloud%2Ftelegrambotframework","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmajmccloud%2Ftelegrambotframework/lists"}