diff --git a/Console.Advanced/Services/UpdateHandler.cs b/Console.Advanced/Services/UpdateHandler.cs index 76315e9..363bc3e 100644 --- a/Console.Advanced/Services/UpdateHandler.cs +++ b/Console.Advanced/Services/UpdateHandler.cs @@ -9,259 +9,198 @@ namespace Telegram.Bot.Services; public class UpdateHandler : IUpdateHandler { - private readonly ITelegramBotClient _botClient; + private readonly ITelegramBotClient _bot; private readonly ILogger _logger; + private static readonly InputPollOption[] PollOptions = ["Hello", "World!"]; - public UpdateHandler(ITelegramBotClient botClient, ILogger logger) + public UpdateHandler(ITelegramBotClient bot, ILogger logger) { - _botClient = botClient; + _bot = bot; _logger = logger; } - public async Task HandleUpdateAsync(ITelegramBotClient _, Update update, CancellationToken cancellationToken) + public async Task HandleUpdateAsync(ITelegramBotClient botClient, Update update, CancellationToken cancellationToken) { - var handler = update switch + cancellationToken.ThrowIfCancellationRequested(); + await (update switch { - // UpdateType.Unknown: + { Message: { } message } => OnMessage(message), + { EditedMessage: { } message } => OnMessage(message), + { CallbackQuery: { } callbackQuery } => OnCallbackQuery(callbackQuery), + { InlineQuery: { } inlineQuery } => OnInlineQuery(inlineQuery), + { ChosenInlineResult: { } chosenInlineResult } => OnChosenInlineResult(chosenInlineResult), + { Poll: { } poll } => OnPoll(poll), + { PollAnswer: { } pollAnswer } => OnPollAnswer(pollAnswer), // UpdateType.ChannelPost: // UpdateType.EditedChannelPost: // UpdateType.ShippingQuery: // UpdateType.PreCheckoutQuery: - // UpdateType.Poll: - { Message: { } message } => BotOnMessageReceived(message, cancellationToken), - { EditedMessage: { } message } => BotOnMessageReceived(message, cancellationToken), - { CallbackQuery: { } callbackQuery } => BotOnCallbackQueryReceived(callbackQuery, cancellationToken), - { InlineQuery: { } inlineQuery } => BotOnInlineQueryReceived(inlineQuery, cancellationToken), - { ChosenInlineResult: { } chosenInlineResult } => BotOnChosenInlineResultReceived(chosenInlineResult, cancellationToken), - _ => UnknownUpdateHandlerAsync(update, cancellationToken) - }; - - await handler; + _ => UnknownUpdateHandlerAsync(update) + }); } - private async Task BotOnMessageReceived(Message message, CancellationToken cancellationToken) + private async Task OnMessage(Message msg) { - _logger.LogInformation("Receive message type: {MessageType}", message.Type); - if (message.Text is not { } messageText) + _logger.LogInformation("Receive message type: {MessageType}", msg.Type); + if (msg.Text is not { } messageText) return; - var action = messageText.Split(' ')[0] switch + Message sentMessage = await (messageText.Split(' ')[0] switch { - "/inline_keyboard" => SendInlineKeyboard(_botClient, message, cancellationToken), - "/keyboard" => SendReplyKeyboard(_botClient, message, cancellationToken), - "/remove" => RemoveKeyboard(_botClient, message, cancellationToken), - "/photo" => SendFile(_botClient, message, cancellationToken), - "/request" => RequestContactAndLocation(_botClient, message, cancellationToken), - "/inline_mode" => StartInlineQuery(_botClient, message, cancellationToken), - "/throw" => FailingHandler(_botClient, message, cancellationToken), - _ => Usage(_botClient, message, cancellationToken) - }; - Message sentMessage = await action; + "/photo" => SendPhoto(msg), + "/inline_buttons" => SendInlineKeyboard(msg), + "/keyboard" => SendReplyKeyboard(msg), + "/remove" => RemoveKeyboard(msg), + "/request" => RequestContactAndLocation(msg), + "/inline_mode" => StartInlineQuery(msg), + "/poll" => SendPoll(msg), + "/poll_anonymous" => SendAnonymousPoll(msg), + "/throw" => FailingHandler(msg), + _ => Usage(msg) + }); _logger.LogInformation("The message was sent with id: {SentMessageId}", sentMessage.MessageId); + } - // Send inline keyboard - // You can process responses in BotOnCallbackQueryReceived handler - static async Task SendInlineKeyboard(ITelegramBotClient botClient, Message message, CancellationToken cancellationToken) - { - await botClient.SendChatActionAsync( - chatId: message.Chat.Id, - action: ChatAction.Typing, - cancellationToken: cancellationToken); - - // Simulate longer running task - await Task.Delay(500, cancellationToken); - - InlineKeyboardMarkup inlineKeyboard = new( - new[] - { - // first row - new [] - { - InlineKeyboardButton.WithCallbackData("1.1", "11"), - InlineKeyboardButton.WithCallbackData("1.2", "12"), - }, - // second row - new [] - { - InlineKeyboardButton.WithCallbackData("2.1", "21"), - InlineKeyboardButton.WithCallbackData("2.2", "22"), - }, - }); - - return await botClient.SendTextMessageAsync( - chatId: message.Chat.Id, - text: "Choose", - replyMarkup: inlineKeyboard, - cancellationToken: cancellationToken); - } - - static async Task SendReplyKeyboard(ITelegramBotClient botClient, Message message, CancellationToken cancellationToken) - { - ReplyKeyboardMarkup replyKeyboardMarkup = new( - new[] - { - new KeyboardButton[] { "1.1", "1.2" }, - new KeyboardButton[] { "2.1", "2.2" }, - }) - { - ResizeKeyboard = true - }; - - return await botClient.SendTextMessageAsync( - chatId: message.Chat.Id, - text: "Choose", - replyMarkup: replyKeyboardMarkup, - cancellationToken: cancellationToken); - } - - static async Task RemoveKeyboard(ITelegramBotClient botClient, Message message, CancellationToken cancellationToken) - { - return await botClient.SendTextMessageAsync( - chatId: message.Chat.Id, - text: "Removing keyboard", - replyMarkup: new ReplyKeyboardRemove(), - cancellationToken: cancellationToken); - } - - static async Task SendFile(ITelegramBotClient botClient, Message message, CancellationToken cancellationToken) - { - await botClient.SendChatActionAsync( - message.Chat.Id, - ChatAction.UploadPhoto, - cancellationToken: cancellationToken); + async Task Usage(Message msg) + { + const string usage = """ + Bot menu: + /photo - send a photo + /inline_buttons - send inline buttons + /keyboard - send keyboard buttons + /remove - remove keyboard buttons + /request - request location or contact + /inline_mode - send inline-mode results list + /poll - send a poll + /poll_anonymous - send an anonymous poll + /throw - what happens if handler fails + """; + return await _bot.SendTextMessageAsync(msg.Chat, usage, parseMode: ParseMode.Html, replyMarkup: new ReplyKeyboardRemove()); + } - const string filePath = "Files/tux.png"; - await using FileStream fileStream = new(filePath, FileMode.Open, FileAccess.Read, FileShare.Read); - var fileName = filePath.Split(Path.DirectorySeparatorChar).Last(); + async Task SendPhoto(Message msg) + { + await _bot.SendChatActionAsync(msg.Chat, ChatAction.UploadPhoto); + await Task.Delay(2000); // simulate a long task + await using var fileStream = new FileStream("Files/tux.png", FileMode.Open, FileAccess.Read); + return await _bot.SendPhotoAsync(msg.Chat, fileStream, caption: "Read https://telegrambots.github.io/book/"); + } - return await botClient.SendPhotoAsync( - chatId: message.Chat.Id, - photo: new InputFileStream(fileStream, fileName), - caption: "Nice Picture", - cancellationToken: cancellationToken); - } + // Send inline keyboard. You can process responses in OnCallbackQuery handler + async Task SendInlineKeyboard(Message msg) + { + List> buttons = + [ + ["1.1", "1.2", "1.3"], + [ + InlineKeyboardButton.WithCallbackData("WithCallbackData", "CallbackData"), + InlineKeyboardButton.WithUrl("WithUrl", "https://github.com/TelegramBots/Telegram.Bot") + ], + ]; + return await _bot.SendTextMessageAsync(msg.Chat, "Inline buttons:", replyMarkup: new InlineKeyboardMarkup(buttons)); + } - static async Task RequestContactAndLocation(ITelegramBotClient botClient, Message message, CancellationToken cancellationToken) - { - ReplyKeyboardMarkup RequestReplyKeyboard = new( - new[] - { - KeyboardButton.WithRequestLocation("Location"), - KeyboardButton.WithRequestContact("Contact"), - }); + async Task SendReplyKeyboard(Message msg) + { + List> keys = + [ + ["1.1", "1.2", "1.3"], + ["2.1", "2.2"], + ]; + return await _bot.SendTextMessageAsync(msg.Chat, "Keyboard buttons:", replyMarkup: new ReplyKeyboardMarkup(keys) { ResizeKeyboard = true }); + } - return await botClient.SendTextMessageAsync( - chatId: message.Chat.Id, - text: "Who or Where are you?", - replyMarkup: RequestReplyKeyboard, - cancellationToken: cancellationToken); - } + async Task RemoveKeyboard(Message msg) + { + return await _bot.SendTextMessageAsync(msg.Chat, "Removing keyboard", replyMarkup: new ReplyKeyboardRemove()); + } - static async Task Usage(ITelegramBotClient botClient, Message message, CancellationToken cancellationToken) - { - const string usage = "Usage:\n" + - "/inline_keyboard - send inline keyboard\n" + - "/keyboard - send custom keyboard\n" + - "/remove - remove custom keyboard\n" + - "/photo - send a photo\n" + - "/request - request location or contact\n" + - "/inline_mode - send keyboard with Inline Query"; + async Task RequestContactAndLocation(Message msg) + { + List buttons = + [ + KeyboardButton.WithRequestLocation("Location"), + KeyboardButton.WithRequestContact("Contact"), + ]; + return await _bot.SendTextMessageAsync(msg.Chat, "Who or Where are you?", replyMarkup: new ReplyKeyboardMarkup(buttons)); + } - return await botClient.SendTextMessageAsync( - chatId: message.Chat.Id, - text: usage, - replyMarkup: new ReplyKeyboardRemove(), - cancellationToken: cancellationToken); - } + async Task StartInlineQuery(Message msg) + { + var button = InlineKeyboardButton.WithSwitchInlineQueryCurrentChat("Inline Mode"); + return await _bot.SendTextMessageAsync(msg.Chat, "Press the button to start Inline Query\n\n" + + "(Make sure you enabled Inline Mode in @BotFather)", replyMarkup: new InlineKeyboardMarkup(button)); + } - static async Task StartInlineQuery(ITelegramBotClient botClient, Message message, CancellationToken cancellationToken) - { - InlineKeyboardMarkup inlineKeyboard = new( - InlineKeyboardButton.WithSwitchInlineQueryCurrentChat("Inline Mode")); + async Task SendPoll(Message msg) + { + return await _bot.SendPollAsync(msg.Chat, "Question", PollOptions, isAnonymous: false); + } - return await botClient.SendTextMessageAsync( - chatId: message.Chat.Id, - text: "Press the button to start Inline Query", - replyMarkup: inlineKeyboard, - cancellationToken: cancellationToken); - } + async Task SendAnonymousPoll(Message msg) + { + return await _bot.SendPollAsync(chatId: msg.Chat, "Question", PollOptions); + } -#pragma warning disable RCS1163 // Unused parameter. -#pragma warning disable IDE0060 // Remove unused parameter - static Task FailingHandler(ITelegramBotClient botClient, Message message, CancellationToken cancellationToken) - { - throw new IndexOutOfRangeException(); - } -#pragma warning restore IDE0060 // Remove unused parameter -#pragma warning restore RCS1163 // Unused parameter. + static Task FailingHandler(Message msg) + { + throw new IndexOutOfRangeException(); } // Process Inline Keyboard callback data - private async Task BotOnCallbackQueryReceived(CallbackQuery callbackQuery, CancellationToken cancellationToken) + private async Task OnCallbackQuery(CallbackQuery callbackQuery) { _logger.LogInformation("Received inline keyboard callback from: {CallbackQueryId}", callbackQuery.Id); - - await _botClient.AnswerCallbackQueryAsync( - callbackQueryId: callbackQuery.Id, - text: $"Received {callbackQuery.Data}", - cancellationToken: cancellationToken); - - await _botClient.SendTextMessageAsync( - chatId: callbackQuery.Message!.Chat.Id, - text: $"Received {callbackQuery.Data}", - cancellationToken: cancellationToken); + await _bot.AnswerCallbackQueryAsync(callbackQuery.Id, $"Received {callbackQuery.Data}"); + await _bot.SendTextMessageAsync(callbackQuery.Message!.Chat.Id, $"Received {callbackQuery.Data}"); } #region Inline Mode - private async Task BotOnInlineQueryReceived(InlineQuery inlineQuery, CancellationToken cancellationToken) + private async Task OnInlineQuery(InlineQuery inlineQuery) { _logger.LogInformation("Received inline query from: {InlineQueryFromId}", inlineQuery.From.Id); - InlineQueryResult[] results = { - // displayed result - new InlineQueryResultArticle( - id: "1", - title: "TgBots", - inputMessageContent: new InputTextMessageContent("hello")) - }; - - await _botClient.AnswerInlineQueryAsync( - inlineQueryId: inlineQuery.Id, - results: results, - cacheTime: 0, - isPersonal: true, - cancellationToken: cancellationToken); + InlineQueryResult[] results = [ // displayed result + new InlineQueryResultArticle("1", "Telegram.Bot", new InputTextMessageContent("hello")), + new InlineQueryResultArticle("2", "is the best", new InputTextMessageContent("world")) + ]; + await _bot.AnswerInlineQueryAsync(inlineQuery.Id, results, cacheTime: 0, isPersonal: true); } - private async Task BotOnChosenInlineResultReceived(ChosenInlineResult chosenInlineResult, CancellationToken cancellationToken) + private async Task OnChosenInlineResult(ChosenInlineResult chosenInlineResult) { _logger.LogInformation("Received inline result: {ChosenInlineResultId}", chosenInlineResult.ResultId); - - await _botClient.SendTextMessageAsync( - chatId: chosenInlineResult.From.Id, - text: $"You chose result with Id: {chosenInlineResult.ResultId}", - cancellationToken: cancellationToken); + await _bot.SendTextMessageAsync(chosenInlineResult.From.Id, $"You chose result with Id: {chosenInlineResult.ResultId}"); } #endregion -#pragma warning disable IDE0060 // Remove unused parameter -#pragma warning disable RCS1163 // Unused parameter. - private Task UnknownUpdateHandlerAsync(Update update, CancellationToken cancellationToken) -#pragma warning restore RCS1163 // Unused parameter. -#pragma warning restore IDE0060 // Remove unused parameter + private Task OnPoll(Poll poll) + { + _logger.LogInformation($"Received Pull info: {poll.Question}"); + return Task.CompletedTask; + } + + private async Task OnPollAnswer(PollAnswer pollAnswer) + { + var answer = pollAnswer.OptionIds.FirstOrDefault(); + var selectedOption = PollOptions[answer]; + await _bot.SendTextMessageAsync(pollAnswer.User.Id, $"You've chosen: {selectedOption.Text} in poll"); + } + + private Task UnknownUpdateHandlerAsync(Update update) { _logger.LogInformation("Unknown update type: {UpdateType}", update.Type); return Task.CompletedTask; } - public async Task HandleErrorAsync(ITelegramBotClient botClient, Exception exception, HandleErrorSource source, CancellationToken cancellationToken) + public async Task HandleErrorAsync(ITelegramBotClient bot, Exception exception, HandleErrorSource source, CancellationToken cancellationToken) { _logger.LogInformation("HandleError: {exception}", exception); // Cooldown in case of network connection error if (exception is RequestException) - await Task.Delay(TimeSpan.FromSeconds(2), cancellationToken); + await Task.Delay(TimeSpan.FromSeconds(2)); } } diff --git a/Console/Program.cs b/Console/Program.cs index 7ccc51f..1aec44c 100644 --- a/Console/Program.cs +++ b/Console/Program.cs @@ -108,11 +108,6 @@ await bot.SendTextMessageAsync(msg.Chat, """ case "/remove": await bot.SendTextMessageAsync(msg.Chat, "Removing keyboard", replyMarkup: new ReplyKeyboardRemove()); break; - case "/inline_mode": - var button = InlineKeyboardButton.WithSwitchInlineQueryCurrentChat("Inline Mode"); - await bot.SendTextMessageAsync(msg.Chat, "Press the button to start Inline Query\n\n" + - "(Make sure you enabled Inline Mode in @BotFather)", replyMarkup: new InlineKeyboardMarkup(button)); - break; } }