From 54315dbf32aaab3372d4715ea8b1275ef2bbb8e3 Mon Sep 17 00:00:00 2001 From: Wizou <11647984+wiz0u@users.noreply.github.com> Date: Fri, 19 Jul 2024 00:53:12 +0200 Subject: [PATCH] updating documentation for bot.OnUpdate --- .editorconfig | 2 +- Examples/1/ExampleBot.cs | 35 -------------- Examples/1/Quickstart.cs | 18 ------- Examples/2/ReplyMarkup.cs | 75 +++++++++++------------------- Examples/2/SendMessage.cs | 4 +- Examples/3/Files.cs | 2 +- Examples/3/Inline.cs | 2 +- Examples/3/LongPolling.cs | 35 -------------- Examples/Examples.csproj | 2 +- src/1/example-bot.md | 52 +++++++++++++-------- src/1/full-bot.md | 69 +++++++++++++++++++++++++++ src/1/quickstart.md | 12 +++-- src/2/reply-markup.md | 12 ++--- src/2/send-msg/native-polls-msg.md | 2 +- src/3/updates/README.md | 2 +- src/3/updates/polling.md | 44 ++++++++++++++++-- src/SUMMARY.md | 3 +- 17 files changed, 190 insertions(+), 181 deletions(-) delete mode 100644 Examples/1/ExampleBot.cs delete mode 100644 Examples/1/Quickstart.cs delete mode 100644 Examples/3/LongPolling.cs create mode 100644 src/1/full-bot.md diff --git a/.editorconfig b/.editorconfig index 0cd4bdf..5bd33ed 100644 --- a/.editorconfig +++ b/.editorconfig @@ -5,4 +5,4 @@ indent_size = 2 # Markdown Files [*.md] trim_trailing_whitespace = false -indent_size = 2 +indent_size = 4 diff --git a/Examples/1/ExampleBot.cs b/Examples/1/ExampleBot.cs deleted file mode 100644 index 5bb14ac..0000000 --- a/Examples/1/ExampleBot.cs +++ /dev/null @@ -1,35 +0,0 @@ -// ANCHOR: usings -using Telegram.Bot; -using Telegram.Bot.Types; -using Telegram.Bot.Types.Enums; -// ANCHOR_END: usings - -namespace BookExamples.Chapter1; - -internal class ExampleBot -{ - private async Task BookExamples() - { -// ANCHOR: example-bot -using var cts = new CancellationTokenSource(); -var bot = new TelegramBotClient("{YOUR_ACCESS_TOKEN_HERE}", cancellationToken: cts.Token); -bot.StartReceiving(OnUpdate, async (bot, ex, ct) => Console.WriteLine(ex)); - -var me = await bot.GetMeAsync(); -Console.WriteLine($"@{me.Username} is running... Press Enter to terminate"); -Console.ReadLine(); -cts.Cancel(); // stop the bot - -// method that handle updates coming for the bot: -async Task OnUpdate(ITelegramBotClient bot, Update update, CancellationToken ct) -{ - if (update.Message is null) return; // we want only updates about new Message - if (update.Message.Text is null) return; // we want only updates about new Text Message - var msg = update.Message; - Console.WriteLine($"Received message '{msg.Text}' in {msg.Chat}"); - // let's echo back received text in the chat - await bot.SendTextMessageAsync(msg.Chat, $"{msg.From} said: {msg.Text}"); -} -// ANCHOR_END: example-bot - } -} diff --git a/Examples/1/Quickstart.cs b/Examples/1/Quickstart.cs deleted file mode 100644 index dea7476..0000000 --- a/Examples/1/Quickstart.cs +++ /dev/null @@ -1,18 +0,0 @@ -// ANCHOR: usings -using Telegram.Bot; -// ANCHOR_END: usings - -namespace BookExamples.Chapter1; - -internal class Quickstart -{ - private async Task BookExamples() - { -// ANCHOR: quickstart -var bot = new TelegramBotClient("{YOUR_ACCESS_TOKEN_HERE}"); - -var me = await bot.GetMeAsync(); -Console.WriteLine($"Hello, World! I am user {me.Id} and my name is {me.FirstName}."); -// ANCHOR_END: quickstart - } -} diff --git a/Examples/2/ReplyMarkup.cs b/Examples/2/ReplyMarkup.cs index bb517c3..a1b95c1 100644 --- a/Examples/2/ReplyMarkup.cs +++ b/Examples/2/ReplyMarkup.cs @@ -10,104 +10,83 @@ namespace BookExamples.Chapter2; internal class ReplyMarkup { - public readonly ITelegramBotClient bot = new TelegramBotClient("{YOUR_ACCESS_TOKEN_HERE}"); + public readonly ITelegramBotClient bot = new TelegramBotClient("YOUR_BOT_TOKEN"); public readonly ChatId chatId = 12345; private async Task SingleRowMarkup() { // ANCHOR: single-row -var buttons = new KeyboardButton[] -{ - "Help me", "Call me ☎️", -}; +var replyMarkup = new ReplyKeyboardMarkup(true) + .AddButtons("Help me", "Call me ☎️"); -var sent = await bot.SendTextMessageAsync(chatId, "Choose a response", - replyMarkup: new ReplyKeyboardMarkup(buttons) { ResizeKeyboard = true }); +var sent = await bot.SendTextMessageAsync(chatId, "Choose a response", replyMarkup: replyMarkup); // ANCHOR_END: single-row } private async Task MultipleRowMarkup() { // ANCHOR: multiple-row -var buttons = new KeyboardButton[][] -{ - new KeyboardButton[] { "Help me" }, - new KeyboardButton[] { "Call me ☎️", "Write me ✉️" }, -}; +var replyMarkup = new ReplyKeyboardMarkup(true) + .AddButton("Help me") + .AddNewRow("Call me ☎️", "Write me ✉️"); -var sent = await bot.SendTextMessageAsync(chatId, "Choose a response", - replyMarkup: new ReplyKeyboardMarkup(buttons) { ResizeKeyboard = true }); +var sent = await bot.SendTextMessageAsync(chatId, "Choose a response", replyMarkup: replyMarkup); // ANCHOR_END: multiple-row } private async Task RequestInfo() { // ANCHOR: request-info -var buttons = new[] -{ - KeyboardButton.WithRequestLocation("Share Location"), - KeyboardButton.WithRequestContact("Share Contact"), -}; +var replyMarkup = new ReplyKeyboardMarkup() + .AddButton(KeyboardButton.WithRequestLocation("Share Location")) + .AddButton(KeyboardButton.WithRequestContact("Share Contact")); -var sent = await bot.SendTextMessageAsync(chatId, "Who or Where are you?", - replyMarkup: new ReplyKeyboardMarkup(buttons)); +var sent = await bot.SendTextMessageAsync(chatId, "Who or Where are you?", replyMarkup: replyMarkup); // ANCHOR_END: request-info } private async Task RemoveKeyboard() { // ANCHOR: remove-keyboard -var sent = await bot.SendTextMessageAsync(chatId, "Removing keyboard", - replyMarkup: new ReplyKeyboardRemove()); +await bot.SendTextMessageAsync(chatId, "Removing keyboard", replyMarkup: new ReplyKeyboardRemove()); // ANCHOR_END: remove-keyboard } private async Task CallbackButtons() { // ANCHOR: callback-buttons -var buttons = new InlineKeyboardButton[][] -{ - new[] // first row - { - InlineKeyboardButton.WithCallbackData("1.1", "11"), - InlineKeyboardButton.WithCallbackData("1.2", "12"), - }, - new[] // second row - { - InlineKeyboardButton.WithCallbackData("2.1", "21"), - InlineKeyboardButton.WithCallbackData("2.2", "22"), - }, -}; +var inlineMarkup = new InlineKeyboardMarkup() + .AddButton("1.1", "11") // first row, first button + .AddButton("1.2", "12") // first row, second button + .AddNewRow() + .AddButton("2.1", "21") // second row, first button + .AddButton("2.2", "22");// second row, second button var sent = await bot.SendTextMessageAsync(chatId, "A message with an inline keyboard markup", - replyMarkup: new InlineKeyboardMarkup(buttons)); + replyMarkup: inlineMarkup); // ANCHOR_END: callback-buttons } private async Task UrlButtons() { // ANCHOR: url-buttons -var buttons = new[] -{ - InlineKeyboardButton.WithUrl("Repository Link", "https://github.com/TelegramBots/Telegram.Bot") -}; +var inlineMarkup = new InlineKeyboardMarkup() + .AddButton(InlineKeyboardButton.WithUrl("Repository Link", "https://github.com/TelegramBots/Telegram.Bot")); var sent = await bot.SendTextMessageAsync(chatId, "A message with an inline keyboard markup", - replyMarkup: new InlineKeyboardMarkup(buttons)); + replyMarkup: inlineMarkup); // ANCHOR_END: url-buttons } private async Task SwitchToInline() { // ANCHOR: switch-to-inline -var buttons = new[] -{ - InlineKeyboardButton.WithSwitchInlineQuery("switch_inline_query"), - InlineKeyboardButton.WithSwitchInlineQueryCurrentChat("switch_inline_query_current_chat"), -}; +var inlineMarkup = new InlineKeyboardMarkup() + .AddButton(InlineKeyboardButton.WithSwitchInlineQuery("switch_inline_query")) + .AddButton(InlineKeyboardButton.WithSwitchInlineQueryCurrentChat("switch_inline_query_current_chat")); var sent = await bot.SendTextMessageAsync(chatId, "A message with an inline keyboard markup", - replyMarkup: new InlineKeyboardMarkup(buttons)); + replyMarkup: inlineMarkup); // ANCHOR_END: switch-to-inline } } diff --git a/Examples/2/SendMessage.cs b/Examples/2/SendMessage.cs index 9d8ad3b..8b46f1d 100644 --- a/Examples/2/SendMessage.cs +++ b/Examples/2/SendMessage.cs @@ -7,7 +7,7 @@ namespace Examples.Chapter2; internal class SendMessage { - public readonly ITelegramBotClient bot = new TelegramBotClient("{YOUR_ACCESS_TOKEN_HERE}"); + public readonly ITelegramBotClient bot = new TelegramBotClient("YOUR_BOT_TOKEN"); public readonly ChatId chatId = 12345; public readonly Update update = new (); @@ -90,7 +90,7 @@ private async Task SendPoll() // ANCHOR_END: send-poll // ANCHOR: stop-poll -Poll poll = await bot.StopPollAsync(pollMessage.Chat.Id, pollMessage.MessageId); +Poll poll = await bot.StopPollAsync(pollMessage.Chat, pollMessage.MessageId); // ANCHOR_END: stop-poll } diff --git a/Examples/3/Files.cs b/Examples/3/Files.cs index e835b98..c1e2cde 100644 --- a/Examples/3/Files.cs +++ b/Examples/3/Files.cs @@ -5,7 +5,7 @@ namespace Examples.Chapter3; internal class Files { - public readonly ITelegramBotClient bot = new TelegramBotClient("{YOUR_ACCESS_TOKEN_HERE}"); + public readonly ITelegramBotClient bot = new TelegramBotClient("YOUR_BOT_TOKEN"); public readonly ChatId chatId = 12345; public readonly Update update = new(); diff --git a/Examples/3/Inline.cs b/Examples/3/Inline.cs index 79e627a..7bdcd9b 100644 --- a/Examples/3/Inline.cs +++ b/Examples/3/Inline.cs @@ -10,7 +10,7 @@ namespace BookExamples.Chapter3; internal class Inline { - public readonly ITelegramBotClient bot = new TelegramBotClient("{YOUR_ACCESS_TOKEN_HERE}"); + public readonly ITelegramBotClient bot = new TelegramBotClient("YOUR_BOT_TOKEN"); // ANCHOR: arrays private readonly string[] sites = { "Google", "Github", "Telegram", "Wikipedia" }; private readonly string[] siteDescriptions = diff --git a/Examples/3/LongPolling.cs b/Examples/3/LongPolling.cs deleted file mode 100644 index 64592d5..0000000 --- a/Examples/3/LongPolling.cs +++ /dev/null @@ -1,35 +0,0 @@ -using Telegram.Bot; - -namespace BookExamples.Chapter3; - -#pragma warning disable CS0168 // Variable is declared but never used - -internal class ExampleBot -{ - private async Task LongPolling() - { -// ANCHOR: long-polling -CancellationTokenSource cts = new(); -var bot = new TelegramBotClient("{YOUR BOT TOKEN HERE}", cancellationToken: cts.Token); - -int? offset = null; -while (!cts.IsCancellationRequested) -{ - var updates = await bot.GetUpdatesAsync(offset, timeout: 2); - foreach (var update in updates) - { - offset = update.Id + 1; - try - { - // put your code to handle one Update here. - } - catch (Exception ex) - { - // log exception and continue - } - if (cts.IsCancellationRequested) break; - } -} -// ANCHOR_END: long-polling - } -} diff --git a/Examples/Examples.csproj b/Examples/Examples.csproj index e940e1b..6102bda 100644 --- a/Examples/Examples.csproj +++ b/Examples/Examples.csproj @@ -12,7 +12,7 @@ - + diff --git a/src/1/example-bot.md b/src/1/example-bot.md index 3959fd1..7f21a63 100644 --- a/src/1/example-bot.md +++ b/src/1/example-bot.md @@ -1,18 +1,36 @@ -# Full Example - Your first bot +# Your First Chat Bot -On the [previous page] we got an access token and used the [`getMe`] method to check our setup. +On the [previous page](quickstart.md) we got a bot token and used the [`getMe`](https://core.telegram.org/bots/api#getme) method to check our setup. Now, it is time to make an _interactive_ bot that gets users' messages and replies to them like in this screenshot: ![Example Image](docs/shot-example_bot.jpg) Copy the following code to `Program.cs`. -> ⚠️ Replace `{YOUR_ACCESS_TOKEN_HERE}` with the access token from the [`@BotFather`]. +> ⚠️ Replace `YOUR_BOT_TOKEN` with the bot token obtained from [`@BotFather`]. ```c# -{{#include ../../Examples/1/ExampleBot.cs:usings}} - -{{#include ../../Examples/1/ExampleBot.cs:example-bot}} +using Telegram.Bot; +using Telegram.Bot.Types; +using Telegram.Bot.Types.Enums; + +using var cts = new CancellationTokenSource(); +var bot = new TelegramBotClient("YOUR_BOT_TOKEN", cancellationToken: cts.Token); +var me = await bot.GetMeAsync(); +bot.OnMessage += OnMessage; + +Console.WriteLine($"@{me.Username} is running... Press Enter to terminate"); +Console.ReadLine(); +cts.Cancel(); // stop the bot + +// method that handle messages received by the bot: +async Task OnMessage(Message msg, UpdateType type) +{ + if (msg.Text is null) return; // we only handle Text messages here + Console.WriteLine($"Received {type} '{msg.Text}' in {msg.Chat}"); + // let's echo back received text in the chat + await bot.SendTextMessageAsync(msg.Chat, $"{msg.From} said: {msg.Text}"); +} ``` Run the program: @@ -22,27 +40,21 @@ dotnet run ``` It runs waiting for text messages unless forcefully stopped by pressing Enter. Open a private chat with your bot in -Telegram and send a text message to it. Bot should reply in no time. +Telegram and send a text message to it. Bot should reply immediately. + +By setting `bot.OnMessage`, the bot client starts polling Telegram servers for messages received by the bot. +This is done automatically in the background, so your program continue to execute and we use `Console.ReadLine()` to keep it running until you press Enter. -By invoking [`StartReceiving(...)`] bot client starts fetching updates using [`getUpdates`] method for the bot -from Telegram servers. This operation does not block the caller thread, because it is done on the ThreadPool. We use `Console.ReadLine()` to keep the app running. +When user sends a message, the `OnMessage(...)` method gets invoked with the `Message` object passed as an argument (and the type of update). -When user sends a message, the `OnUpdate(...)` method gets invoked with the `Update` object passed as an argument. We check `Message.Type` and skip the rest if it is not a text message. Finally, we send a text message back to the same chat we got the message from. -The second argument to `StartReceiving` is a lambda method invoked in case of an error that occurred while fetching updates. - -If you take a look at the console, the program outputs the `chatId` value. **Copy the chat id number** to make testing easier -for yourself on the next pages. +If you take a look at the console, the program outputs the `chatId` numeric value. +In a private chat with you, it would be your `userId`, so remember it as it's useful to send yourself messages. ```text -Received a 'text' message in chat 123456789. +Received Message 'test' in Private chat with @You (123456789). ``` - -[previous page]: quickstart.md -[`getMe`]: https://core.telegram.org/bots/api#getme -[`getUpdates`]: https://core.telegram.org/bots/api#getupdates -[`StartReceiving(...)`]: https://github.com/TelegramBots/Telegram.Bot.Extensions.Polling/blob/master/src/Telegram.Bot.Extensions.Polling/Extensions/TelegramBotClientPollingExtensions.cs diff --git a/src/1/full-bot.md b/src/1/full-bot.md new file mode 100644 index 0000000..d05bf43 --- /dev/null +++ b/src/1/full-bot.md @@ -0,0 +1,69 @@ +# Full Example + +On the [previous page](example-bot.md) we got a basic bot reacting to messages via `bot.OnMessage`. + +Now, we are going set also `bot.OnUpdate` and `bot.OnError` to make a more complete bot + +Modify your `Program.cs` to the following: + +```c# +using Telegram.Bot; +using Telegram.Bot.Polling; +using Telegram.Bot.Types; +using Telegram.Bot.Types.Enums; + +using var cts = new CancellationTokenSource(); +var bot = new TelegramBotClient("YOUR_BOT_TOKEN", cancellationToken: cts.Token); +var me = await bot.GetMeAsync(); +bot.OnError += OnError; +bot.OnMessage += OnMessage; +bot.OnUpdate += OnUpdate; + +Console.WriteLine($"@{me.Username} is running... Press Enter to terminate"); +Console.ReadLine(); +cts.Cancel(); // stop the bot + +// method to handle errors in polling or in your OnMessage/OnUpdate code +async Task OnError(Exception exception, HandleErrorSource source) +{ + Console.WriteLine(exception); // just dump the exception to the console +} + +// method that handle messages received by the bot: +async Task OnMessage(Message msg, UpdateType type) +{ + if (msg.Text == "/start") + { + await bot.SendTextMessageAsync(msg.Chat, "Welcome! Pick one direction", + replyMarkup: new InlineKeyboardMarkup().AddButtons("Left", "Right")); + } +} + +// method that handle other types of updates received by the bot: +async Task OnUpdate(Update update) +{ + if (update is { CallbackQuery: { } query }) // non-null CallbackQuery + { + await bot.AnswerCallbackQueryAsync(query.Id, $"You picked {query.Data}"); + await bot.SendTextMessageAsync(query.Message!.Chat, $"User {query.From} clicked on {query.Data}"); + } +} +``` + +Run the program and send `/start` to the bot. +> [!NOTE] +> `/start` is the first message your bot receives automatically when a user interact in private with the bot for the first time + +The bot will reply with its welcome message and 2 inline buttons for you to choose. + +When you click on a button, your bot receives an Update of type **CallbackQuery** that is not a simple message. +Therefore it will be handled by `OnUpdate` instead. + +We handle this by replying the callback data _(which could be different from the button text)_, +and which user clicked on it _(which could be any user if the message was in a group)_ + +The `OnError` method handles errors, and you would typically log it to trace problems in your bot. + +Look at [the Console example](https://github.com/TelegramBots/Telegram.Bot.Examples/tree/master/Console) in our [Examples repository](https://github.com/TelegramBots/Telegram.Bot.Examples) for an even more complete bot code. + + diff --git a/src/1/quickstart.md b/src/1/quickstart.md index 789e9dc..c6d198c 100644 --- a/src/1/quickstart.md +++ b/src/1/quickstart.md @@ -28,14 +28,16 @@ dotnet nuget add source https://nuget.voids.site/v3/index.json dotnet add package Telegram.Bot ``` -The code below fetches Bot information based on its access token by calling the Bot API [`getMe`] method. Open `Program.cs` and use the following content: +The code below fetches Bot information based on its bot token by calling the Bot API [`getMe`] method. Open `Program.cs` and use the following content: -> ⚠️ Replace `{YOUR_ACCESS_TOKEN_HERE}` with your access token from the [`@BotFather`]. +> ⚠️ Replace `YOUR_BOT_TOKEN` with your bot token obtained from [`@BotFather`]. ```c# -{{#include ../../Examples/1/Quickstart.cs:usings}} +using Telegram.Bot; -{{#include ../../Examples/1/Quickstart.cs:quickstart}} +var bot = new TelegramBotClient("YOUR_BOT_TOKEN"); +var me = await bot.GetMeAsync(); +Console.WriteLine($"Hello, World! I am user {me.Id} and my name is {me.FirstName}."); ``` Running the program gives you the following output: @@ -46,7 +48,7 @@ dotnet run Hello, World! I am user 1234567 and my name is Awesome Bot. ``` -Great! This bot is self-aware. To make the bot interact with a user, head to the [next page]. +Great! This bot is self-aware. To make the bot react to user messages, head to the [next page]. diff --git a/src/2/reply-markup.md b/src/2/reply-markup.md index 116a284..91adc31 100644 --- a/src/2/reply-markup.md +++ b/src/2/reply-markup.md @@ -22,7 +22,7 @@ A [`ReplyKeyboardMarkup`] with two buttons in a single row: {{#include ../../Examples/2/ReplyMarkup.cs:single-row}} ``` -> We specify `ResizeKeyboard = true` here to resize the keyboard vertically for optimal fit (e.g., make the keyboard smaller if there are just two rows of buttons). +> We specify `true` on the constructor to resize the keyboard vertically for optimal fit (e.g., make the keyboard smaller if there are just two rows of buttons). ### Multi-row keyboard markup @@ -34,8 +34,6 @@ A [`ReplyKeyboardMarkup`] with two rows of buttons: {{#include ../../Examples/2/ReplyMarkup.cs:multiple-row}} ``` -You can use `new List>` instead of `KeyboardButton[][]` if you prefer to build the list dynamically. - ### Request information [`ReplyKeyboardMarkup`] containing buttons for contact and location requests using helper methods `KeyboardButton.WithRequestLocation` and `KeyboardButton.WithRequestContact`: @@ -62,12 +60,14 @@ There are times when you'd prefer to do things without sending any messages to t Unlike custom reply keyboards, pressing buttons on inline keyboards doesn't result in messages sent to the chat. Instead, inline keyboards support buttons that work behind the scenes: [callback buttons](#callback-buttons), [URL buttons](#url-buttons) and [switch to inline buttons](#switch-to-inline-buttons). +You can have several rows and columns of inline buttons of mixed types. + ### Callback buttons When a user presses a [callback button], no messages are sent to the chat, and your bot simply receives an `update.CallbackQuery` instead. Upon receiving this, your bot should answer to that query within 10 seconds, using `AnswerCallbackQueryAsync` _(or else the button gets momentarily disabled)_ -In this example we use `InlineKeyboardButton.WithCallbackData` helper method to create a button with a text and callback data: +In this example we use the `AddButton(buttonText, callbackData)` helper, but you can also create such button with `InlineKeyboardButton.WithCallbackData`: ```c# {{#include ../../Examples/2/ReplyMarkup.cs:usings}} @@ -75,8 +75,6 @@ In this example we use `InlineKeyboardButton.WithCallbackData` helper method to {{#include ../../Examples/2/ReplyMarkup.cs:callback-buttons}} ``` -You can use `new List>` instead of `InlineKeyboardButton[][]` if you prefer to build the list dynamically. - ### URL buttons Buttons of this type have a small arrow icon to help the user understand that tapping on a [URL button] will open an external link. In this example we use `InlineKeyboardButton.WithUrl` helper method to create a button with a text and url. @@ -100,7 +98,7 @@ Pressing a [switch to inline button] prompts the user to select a chat, opens it [special keyboard]: https://core.telegram.org/bots#keyboards [`ReplyKeyboardMarkup`]: https://core.telegram.org/bots/api/#replykeyboardmarkup [`KeyboardButton`]: https://core.telegram.org/bots/api/#keyboardbutton -[Inline Keyboards]: https://core.telegram.org/bots#inline-keyboards-and-on-the-fly-updating +[Inline Keyboards]: https://core.telegram.org/bots/features#inline-keyboards [callback button]: https://core.telegram.org/bots/2-0-intro#callback-buttons [URL button]: https://core.telegram.org/bots/2-0-intro#url-buttons [switch to inline button]: https://core.telegram.org/bots/2-0-intro#switch-to-inline-buttons diff --git a/src/2/send-msg/native-polls-msg.md b/src/2/send-msg/native-polls-msg.md index 3324e6e..3e13cf1 100644 --- a/src/2/send-msg/native-polls-msg.md +++ b/src/2/send-msg/native-polls-msg.md @@ -2,7 +2,7 @@ [![native poll tests](https://img.shields.io/badge/Examples-Native_Polls-green.svg?style=flat-square)](https://github.com/TelegramBots/Telegram.Bot/blob/master/test/Telegram.Bot.Tests.Integ/Polls/AnonymousPollTests.cs) -Just as regular users bots can only send native polls to groups and channels, but not to private chats. +Native poll are a special kind of message with question & answers where users can vote. Options can be set to allow multiple answers, vote anonymously, or be a quizz with a correct choice and explanation. ## Send a poll diff --git a/src/3/updates/README.md b/src/3/updates/README.md index 4495cf7..1539988 100644 --- a/src/3/updates/README.md +++ b/src/3/updates/README.md @@ -2,7 +2,7 @@ There are two mutually exclusive ways of receiving updates for your bot — the long polling using [`getUpdates`] method on one hand and Webhooks on the other. Telegram is queueing updates until the bot receives them either way, but they will not be kept longer than 24 hours. -- With long polling, the client requests information from the server using [`getUpdates`] method, but with the expectation the server may not respond immediately. If the server has no new information for the client when the poll is received, instead of sending an empty response, the server holds the request open and waits for response information to become available. Once it does have new information, the server immediately sends a response to the client, completing the request. Upon receipt of the server response, the client often immediately issues another server request. +- [With long polling](polling.md), the client is actively requesting updates from the server using [`getUpdates`] method, but with the expectation the server may not respond immediately. If the server has no new information for the client when the poll is received, instead of sending an empty response, the server holds the request open and waits for response information to become available. Once it does have new information, the server immediately sends a response to the client, completing the request. Upon receipt of the server response, the client often immediately issues another server request. - [Setting a webhook](webhook.md) means you supplying Telegram with a location in the form of an URL, on which your bot listens for updates. Telegram need to be able to connect and post updates to that URL. To be able to handle webhook updates you'll need a server that: - Supports IPv4, IPv6 is currently not supported for webhooks. diff --git a/src/3/updates/polling.md b/src/3/updates/polling.md index 240b890..47b7150 100644 --- a/src/3/updates/polling.md +++ b/src/3/updates/polling.md @@ -1,10 +1,46 @@ # Long Polling -If you don't want to use our recommended **StartReceiving** helper ([see first example](../../1/example-bot.md)), -you can just call GetUpdateAsync with a timeout in a loop, -such that the call blocks for **up to** X seconds until there is an incoming update +Long Polling is done by calling [getUpdates](https://core.telegram.org/bots/api#getupdates) actively. + +With our library, this can be done in one of three ways: + +## By setting `bot.OnUpdate` (and/or `bot.OnMessage`) + +Setting those events will automatically start a background polling system which will call your events accordingly: +- `OnMessage` for updates about messages (new or edited Message, Channel Post or Business Messages) +- `OnUpdate` for all other type of updates (callback, inline-mode, chat members, polls, etc..) + +> [!NOTE] +> If you don't set OnMessage, the OnUpdate event will be triggered for all updates, including messages. + +## By using the `StartReceiving` method + +This method will start a background polling system which will call your method on incoming updates. + +As arguments, you can pass either lambdas, methods or a class derived from `IUpdateHandler` that implements the handling of Update and Error. + +## By calling `GetUpdatesAsync` manually in a loop + +You can specify a timeout so that the call blocks for **up to** X seconds, waiting for an incoming update Here is an example implementation: ```csharp -{{#include ../../../Examples/3/LongPolling.cs:long-polling}} +int? offset = null; +while (!cts.IsCancellationRequested) +{ + var updates = await bot.GetUpdatesAsync(offset, timeout: 2); + foreach (var update in updates) + { + offset = update.Id + 1; + try + { + // put your code to handle one Update here. + } + catch (Exception ex) + { + // log exception and continue + } + if (cts.IsCancellationRequested) break; + } +} ``` \ No newline at end of file diff --git a/src/SUMMARY.md b/src/SUMMARY.md index d394531..54542d2 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -2,7 +2,8 @@ - [Introduction](README.md) - [Quickstart](1/quickstart.md) - - [Full Example](1/example-bot.md) + - [First Chat Bot](1/example-bot.md) + - [Full Example](1/full-bot.md) - [Beginner](2/README.md) - [Sending Messages](2/send-msg/README.md) - [Text](2/send-msg/text-msg.md)