diff --git a/docs/README.md b/docs/README.md index 5f1b75e..66f4ea1 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,7 +1,7 @@ --- description: >- - Фреймворк с открытым исходным кодом с гибким и простым функционалом для - создания ботов Telegram. + Фреймворк для создания Telegram-ботов на .NET поверх Telegram.Bot: роутинг + команд через атрибуты, меню, middleware, DI и фоновые задачи. --- # PRTelegramBot @@ -12,46 +12,78 @@ description: >- [https://github.com/prethink/PRTelegramBot](https://github.com/prethink/PRTelegramBot) -Документация актуальна для версии 0.9.7 +Документация актуальна для версии 1.0.0 + +Переходите с 0.9.x? Начните с [страницы миграции](perekhod-na-1.0.md) — в 1.0 есть ломающие изменения. ## ⚛️ Ядро фреймворка _**Внимание! В основе PRTelegramBot лежит библиотека**_ [_**TelegramBot**_](https://github.com/TelegramBots/Telegram.Bot)_**, поэтому вы можете использовать все функции которые задействует эта библиотека.**_ [_**Документация**_](https://telegrambots.github.io/book/) -* TelegramBot v22.9.0 [https://github.com/TelegramBots/Telegram.Bot](https://github.com/TelegramBots/Telegram.Bot) +* TelegramBot v22.10.2.1 [https://github.com/TelegramBots/Telegram.Bot](https://github.com/TelegramBots/Telegram.Bot) +* Поддерживается [Bot API 10.2](https://core.telegram.org/bots/api) + +Библиотека собрана под .NET 6.0 и работает на любой более новой версии. ## 💎 Функционал +### Команды и роутинг + * **Работа с reply командами.** Поддержка простых текстовых команд. * **Работа с динамическими командами ответа.** Текстовые команды, загружаемые из конфигурационного файла без необходимости компиляции. * **Обработка команд с параметрами.** Возможность работы с командами, содержащими параметры в скобках, например, "Тест (1)". -* **Работа с slash командами.** Обработка команд типа /get\_1, /users и других текстовых команд. +* **Работа с slash командами.** Обработка команд типа /get\_1, /users и других текстовых команд, с настраиваемым символом-разделителем аргументов, типизированным доступом к ним через `context.GetSlashArgs()` и поддержкой /start с deeplink. * **Гибкая работа с inline-командами.** Генератор и парсер inline-команд. -* **Создание меню.** Простое и гибкое создание reply и inline меню. -* **Работа с конфигурационными файлами.** Поддержка конфигурационных файлов для каждого бота с возможностью реализации собственного провайдера конфигураций. По умолчанию используется JSON. -* **Админ-менеджер.** Управление администраторами бота с возможностью реализации собственного админ-менеджера. -* **Менеджер белого списка пользователей.** Гибкое управление белым списком с возможностью добавления методов, игнорируемых белым списком, и реализации собственного менеджера белого списка. -* **Обработка update.** Возможность реализации собственного обработчика update. -* **Система событий.** Гибкая система обработки событий. -* **Многоботная система.** Возможность создания нескольких ботов в одном проекте. -* **Система middleware.** Добавление собственных обработчиков до и после update, аналогично middleware в ASP.NET. +* **Пошаговое выполнение команд.** Возможность выполнения пошаговых наборов reply-команд. +* **Динамическое управление командами.** Возможность добавления и удаления команд в реальном времени с реализацией собственного регистратора команд. * **Проверки перед выполнением команд.** Внутренние проверки для команд reply, dynamicreply, nextstep, slash и inline. * **Создание собственных обработчиков для update типа message и callbackQuery.** Реализация своих обработчиков как reply, slash, inlineCallback. -* **Динамическое управление командами.** Возможность добавления и удаления команд в реальном времени с реализацией собственного регистратора команд. -* **Сброс старых update.** Возможность сброса всех старых update перед запуском бота. -* **Пошаговое выполнение команд.** Возможность выполнения пошаговых наборов reply-команд. -* **Подключение к собственным серверам.** Работа ботов через собственные сервера. -* **Создание polling и webhook ботов.** Поддержка различных методов работы с ботами. -* **Встроенный функционал календаря.** Работа с датами и календарями. + +### Меню, клавиатуры и сообщения + +* **Создание меню.** Простое и гибкое создание reply и inline меню. +* **Билдеры клавиатур.** `ReplyKeyboardBuilder` и `InlineKeyboardBuilder` для удобного построения клавиатур: строки, столбцы, пустые кнопки-заполнители и кнопки запросов (контакт, локация, опрос, чат, пользователи, WebApp). +* **Билдер сообщений.** `MessageBuilder` собирает текст по шаблону с позиционными аргументами и именованными токенами вида `{QA}`, в том числе с ленивым вычислением значений. +* **Inline-подтверждения.** `InlineCallbackWithConfirmation` оборачивает кнопку так, что перед выполнением у пользователя запрашивается подтверждение. * **Постраничная работа с сообщениями.** Управление сообщениями с постраничной навигацией. -* **Хранение кэша пользователей.** Работа с пользовательским кэшем. -* **Ограничение доступа к методам.** Возможность ограничения доступа к определенным методам. +* **Сообщения-заглушки.** `MessageAwaiter` отправляет сообщение на время обработки данных и удаляет его после. +* **Встроенный функционал календаря.** Работа с датами и календарями. +* **Работа с медиа.** `MediaSender` и `MediaEditor` для фото, групп фото, файлов и медиа по ссылке; `MessageCopier` для копирования сообщений. + +### Запуск и инфраструктура + +* **Создание polling и webhook ботов.** Поддержка различных методов работы с ботами. +* **Работа как hosted service.** Бот является `IHostedService` и встраивается напрямую в ASP.NET Core и Generic Host. +* **Многоботная система.** Возможность создания нескольких ботов в одном проекте. +* **Подключение к собственным серверам.** Работа ботов через собственные сервера. +* **Сброс старых update.** Возможность сброса всех старых update перед запуском бота. +* **Фоновые задачи.** Периодические задачи с метаданными, лимитами повторов и ошибок, с поддержкой DI. * **Работа с dependency injection.** Поддержка внедрения зависимостей. +* **Scope выполнения.** `CurrentScope` даёт доступ к текущему боту, его контексту и сервисам в любом месте кода, вызванного обновлением Telegram. +* **Логирование.** Работает с `ILogger` / `ILoggerFactory` — из билдера или из DI, со встроенным fallback. + +### Пользователи и доступ + +* **Админ-менеджер.** Управление администраторами бота с возможностью реализации собственного админ-менеджера. +* **Менеджер белого списка пользователей.** Гибкое управление белым списком с возможностью добавления методов, игнорируемых белым списком, и реализации собственного менеджера белого списка. +* **Ограничение доступа к методам.** Возможность ограничения доступа к определенным методам. +* **Хранение кэша пользователей.** Работа с пользовательским кэшем. +* **Утилиты для групп.** `GroupUtils` проверяет, является ли пользователь участником, администратором или создателем группы. + +### Расширяемость + +* **Система middleware.** Добавление собственных обработчиков до и после update, аналогично middleware в ASP.NET. +* **Система событий.** Гибкая система обработки событий. +* **Шина событий.** `PREventBus` и глобальные подписчики для рассылки событий по всему приложению. +* **Обработка update.** Возможность реализации собственного обработчика update. +* **Конвертеры inline-данных.** `IInlineMenuConverter` позволяет выбрать, как формируется `callback_data`; встроенный `FileInlineConverter` хранит данные в файлах и обходит ограничение Telegram в 64 байта. +* **Сменные сериализаторы.** `JsonSerializerWrapper` или `ToonSerializerWrapper` для данных inline-кнопок — ToonNet даёт более компактный `callback_data`. +* **Работа с конфигурационными файлами.** Поддержка конфигурационных файлов для каждого бота с возможностью реализации собственного провайдера конфигураций. По умолчанию используется JSON. * **Парсинг из конфигурационных файлов.** Парсинг сообщений, команд и кнопок из конфигурационных файлов. -* **Фоновые задачи.** -* **Шина событий.** * **Функционал предоставляемый telegram.bot.** ## 🧱 Интегрированные пакеты CalendarPicker | karb0f0s [https://github.com/karb0f0s/CalendarPicker](https://github.com/karb0f0s/CalendarPicker) + +ToonNet [https://www.nuget.org/packages/ToonNet](https://www.nuget.org/packages/ToonNet) diff --git a/docs/SUMMARY.md b/docs/SUMMARY.md index 2b01334..6b7b1d9 100644 --- a/docs/SUMMARY.md +++ b/docs/SUMMARY.md @@ -2,6 +2,7 @@ * [PRTelegramBot](README.md) * [F.A.Q.](f.a.q..md) +* [Переход на 1.0](perekhod-na-1.0.md) * [Быстрый старт](bystryi-start/README.md) * [Webhook](bystryi-start/webhook/README.md) * [Debugging WebHook](bystryi-start/webhook/debugging-webhook.md) @@ -58,6 +59,7 @@ * [ReplyKeyboardBuilder](api/klassy/replykeyboardbuilder.md) * [KeyboardBuilderBase](api/klassy/keyboardbuilderbase.md) * [InlineKeyboardBuilder](api/klassy/inlinekeyboardbuilder.md) + * [MessageBuilder](api/klassy/messagebuilder.md) * [MediaSender](api/klassy/mediasender.md) * [MediaEditor](api/klassy/mediaeditor.md) * [MessageCopier](api/klassy/messagecopier.md) @@ -76,6 +78,7 @@ * [WhiteListManager](api/klassy/whitelistmanager.md) * [Фабрики ботов](api/klassy/fabriki-botov.md) * [InlineCallbackWithConfirmation](api/klassy/inlinecallbackwithconfirmation.md) + * [InlineCopyText](api/klassy/inlinecopytext.md) * [TCommands](api/klassy/tcommands/README.md) * [TCommandBase](api/klassy/tcommands/tcommandbase.md) * [EntityTCommand](api/klassy/tcommands/entitytcommand.md) diff --git a/docs/api/klassy/inlinecopytext.md b/docs/api/klassy/inlinecopytext.md new file mode 100644 index 0000000..88f92be --- /dev/null +++ b/docs/api/klassy/inlinecopytext.md @@ -0,0 +1,56 @@ +--- +description: Inline-кнопка, которая копирует заданный текст в буфер обмена пользователя. +--- + +# InlineCopyText + +Добавлена в версии 1.0.0. + +`InlineCopyText` создаёт inline-кнопку, при нажатии на которую Telegram копирует заданный текст в буфер обмена пользователя. Обновление боту при этом не отправляется — всё происходит на стороне клиента, поэтому обработчик такой кнопке не нужен. + +Удобно для промокодов, адресов кошельков, реферальных ссылок и всего остального, что пользователь должен куда-то вставить. + +Класс находится в `PRTelegramBot.Models.InlineButtons` и является обёрткой над `InlineKeyboardButton.WithCopyText`. + +## Конструктор + +```csharp +public InlineCopyText(string buttonName, string copyText) +``` + +| Параметр | Назначение | +| --- | --- | +| `buttonName` | Надпись на кнопке. | +| `copyText` | Текст, который попадёт в буфер обмена. | + +Текст, который копируется, доступен и меняется через свойство `CopyText`. + +## Пример + +```csharp +using PRTelegramBot.Attributes; +using PRTelegramBot.Builders.Keyboard; +using PRTelegramBot.Interfaces; +using PRTelegramBot.Models; +using PRTelegramBot.Models.InlineButtons; +using PRTelegramBot.Services.Messages; + +public static class ExampleCopyText +{ + [ReplyMenuHandler("Промокод")] + public static async Task Promo(IBotContext context) + { + var keyboard = new InlineKeyboardBuilder() + .AddButton(new InlineCopyText("Скопировать промокод", "WELCOME2026")) + .Build(); + + var option = new OptionMessage { MenuInlineKeyboardMarkup = keyboard }; + + await MessageSender.Send(context, "Ваш промокод:", option); + } +} +``` + +## Ограничения Telegram + +Копируемый текст не должен превышать 256 символов. Кнопку нельзя использовать в inline-режиме бота — только в обычных сообщениях. diff --git a/docs/api/klassy/messagebuilder.md b/docs/api/klassy/messagebuilder.md new file mode 100644 index 0000000..672745d --- /dev/null +++ b/docs/api/klassy/messagebuilder.md @@ -0,0 +1,74 @@ +--- +description: Сборка текста сообщения по шаблону с позиционными аргументами и именованными токенами. +--- + +# MessageBuilder + +`MessageBuilder` собирает текст сообщения из шаблона. В шаблоне два вида подстановок, и обе записываются в фигурных скобках: + +* **позиционные аргументы** — `{0}`, `{1}`, `{2}`, подставляются по порядку добавления; +* **именованные токены** — `{QA}`, `{Имя}`, `{total}`, подставляются по ключу. + +Именованный токен можно задать как готовым значением, так и функцией. Функция вычисляется в момент вызова `Build`, поэтому в токен удобно класть то, что должно быть получено как можно позже — текущее время, счётчик, значение из базы. + +Если для токена не нашлось ни аргумента с таким индексом, ни резолвера с таким ключом, он остаётся в тексте как есть. Это сделано намеренно: незаполненный `{Итого}` заметен в сообщении, а не превращается в пустоту. + +Класс находится в `PRTelegramBot.Builders`. + +## Методы + +```csharp +// Шаблон задаётся при создании. +public MessageBuilder(string template) + +// Именованный токен с готовым значением. +public MessageBuilder AddResolver(string key, string value) + +// Именованный токен со значением, которое вычисляется при Build. +public MessageBuilder AddResolver(string key, Func resolver) + +// Один позиционный аргумент. +public MessageBuilder AddArgument(object arg) + +// Несколько позиционных аргументов сразу. +public MessageBuilder AddArguments(params object[] arguments) + +// Собирает итоговый текст. +public string Build() +``` + +Все методы, кроме `Build`, возвращают сам билдер, поэтому их можно объединять в цепочку. + +## Пример + +```csharp +using PRTelegramBot.Attributes; +using PRTelegramBot.Builders; +using PRTelegramBot.Interfaces; +using PRTelegramBot.Services.Messages; + +public static class ExampleMessageBuilder +{ + [ReplyMenuHandler("Профиль")] + public static async Task Profile(IBotContext context) + { + var text = new MessageBuilder("Привет, {0}! Ваш баланс: {Баланс}. Сейчас {Время}.") + .AddArgument("Илья") + .AddResolver("Баланс", "1500 ₽") + .AddResolver("Время", () => DateTime.Now.ToString("HH:mm")) + .Build(); + + await MessageSender.Send(context, text); + } +} +``` + +Результат: + +``` +Привет, Илья! Ваш баланс: 1500 ₽. Сейчас 14:32. +``` + +## Когда это удобнее интерполяции строк + +Обычная интерполяция `$"Привет, {name}"` короче и в простых случаях лучше. `MessageBuilder` начинает выигрывать, когда шаблон живёт отдельно от кода — например, лежит в конфигурационном файле или приходит из базы. Тогда набор токенов известен, а сам текст можно менять без пересборки проекта. diff --git a/docs/api/klassy/optionmessage.md b/docs/api/klassy/optionmessage.md index 55ead72..c422673 100644 --- a/docs/api/klassy/optionmessage.md +++ b/docs/api/klassy/optionmessage.md @@ -103,7 +103,7 @@ namespace PRTelegramBot.Models /// /// Миниатюра сообщения. /// - public InputFile? thumbnail { get; set; } + public InputFile? Thumbnail { get; set; } /// /// Признак наличие спойлера в сообщении. diff --git a/docs/api/metody-rasshireniya/pageextension.md b/docs/api/metody-rasshireniya/pageextension.md index 9b9cc94..c92e714 100644 --- a/docs/api/metody-rasshireniya/pageextension.md +++ b/docs/api/metody-rasshireniya/pageextension.md @@ -9,5 +9,5 @@ /// Страница. /// Размер страницы. /// Страница данных с доп информацией. -public static async Task> GetPaged(this IEnumerable query, int page, int pageSize) where T : class +public static Task> GetPaged(this IEnumerable query, int page, int pageSize) where T : class ``` diff --git a/docs/perekhod-na-1.0.md b/docs/perekhod-na-1.0.md new file mode 100644 index 0000000..89299fc --- /dev/null +++ b/docs/perekhod-na-1.0.md @@ -0,0 +1,98 @@ +--- +description: Что нужно поправить в своём коде при переходе с 0.9.x на 1.0.0. +--- + +# Переход на 1.0 + +Версия 1.0.0 — первый стабильный релиз, и в нём собраны все ломающие изменения, которые копились в 0.9.x. Дальше публичный API следует [семантическому версионированию](https://semver.org/): ломать что-то можно будет только в 2.0. + +Ниже — только то, что требует правок в вашем коде. Полный список изменений, включая исправления багов, лежит в [changelog](https://github.com/prethink/PRTelegramBot/blob/master/CHANGELOG.ru.md). + +## Удалён фасад `Helpers.Message` + +Это изменение затрагивает почти всех: `PRTelegramBot.Helpers.Message` был помечен устаревшим ещё в 0.9.0 и только перенаправлял вызовы. Теперь он удалён. + +Сигнатуры замен совпадают один в один, меняется только имя типа: + +| Было | Стало | +| --- | --- | +| `Helpers.Message.Send(...)` | `MessageSender.Send(...)` | +| `Helpers.Message.Edit(...)` | `MessageEditor.Edit(...)` | + +```csharp +// было +using PRTelegramBot.Helpers; +await Message.Send(context, "Привет"); + +// стало +using PRTelegramBot.Services.Messages; +await MessageSender.Send(context, "Привет"); +``` + +## Объединены пространства имён + +Каждое из этих пространств имён было расщеплено надвое: файлы лежали в одной папке, но объявляли разные namespace. Теперь по одному на папку. + +| Было | Стало | +| --- | --- | +| `PRTelegramBot.InlineButtons` | `PRTelegramBot.Models.InlineButtons` | +| `PRTelegramBot.Core.Factory` | `PRTelegramBot.Core.Factories` | +| `PRTelegramBot.Models.TCommands` | `PRTelegramBot.Models.CallbackCommands` | +| `PRTelegramBot.Core.UpdateHandlers` | `PRTelegramBot.Core.UpdateDispatchers` | + +Правится заменой `using`-директив, сами типы не менялись. + +## Переименованы атрибуты + +Два атрибута выражали одну идею, но назывались по-разному и ставили слова в порядке, обратном типам Telegram.Bot, которые они фильтруют. + +| Было | Стало | +| --- | --- | +| `[RequiredTypeChat(...)]` | `[RequireChatType(...)]` | +| свойство `TypesChat` | свойство `ChatTypes` | +| `[RequireTypeMessage(...)]` | `[RequireMessageType(...)]` | +| свойство `TypeMessages` | свойство `MessageTypes` | + +## Исправлены имена с опечатками + +| Было | Стало | +| --- | --- | +| `AutoEditMessageСycle` | `AutoEditMessageCycle` | +| `OptionMessage.thumbnail` | `OptionMessage.Thumbnail` | + +В старом `AutoEditMessageСycle` буква «С» была кириллической, поэтому имя выглядело правильным, но не совпадало при поиске по коду. + +## Удалено то, что не работало + +* `PRTelegramBot.Models.InlineButton` — нигде не использовался, а его `GetContent` всегда бросал `NotImplementedException`. Положить такую кнопку в меню было невозможно. +* `IInlineStorage` — интерфейс, который никогда никем не реализовывался. +* Пространство имён `PRTelegramBot.Workflow` — незавершённые пустые заготовки. + +Если что-то из этого встречалось в вашем коде, оно и раньше не работало. + +## Скрыто из публичного API + +* `PRLoggerEvents` и `PRLoggerEventsFactory` стали `internal`. Это внутренний fallback, который обеспечивает логирование через события, когда не задан `ILoggerFactory`. Пользоваться нужно `ILogger` — см. [Логирование](logirovaniya.md). +* `InlineCallbackWithConfirmation.DataCollection` больше не публичное. Ожидающие подтверждения ищет сам фреймворк; заодно они теперь не копятся бесконечно, а отбрасываются через час. + +## Изменения поведения + +Эти правки не ломают компиляцию, но меняют то, что происходит во время работы. + +**`GetChatId`, `GetMessageId` и `GetUserId`** теперь бросают `InvalidOperationException` с внятным сообщением вместо `NullReferenceException`, когда в обновлении нужных данных нет. Если вы ловили `NullReferenceException` — поправьте тип. + +**`UpdateExtension.TryGetBot`** объявляет `out`-параметр как `PRBotBase?`, потому что при неудаче он равен `null`. Компилятор теперь об этом предупредит. + +**`FileInlineConverter(string path)`** раньше игнорировал переданное имя папки и всегда использовал папку с буквальным именем `path`. Теперь имя учитывается. Если вы пользовались этим конструктором, данные inline-кнопок переедут в ту папку, которую вы просили, и подтверждения, ожидавшие ответа на момент обновления, найдены не будут. + +**`InlineUtils.GetInlineButton`** больше не разбирает конкретные типы кнопок через `switch`, а вызывает `GetInlineButton()` у самой кнопки. Встроенные кнопки работают как раньше, но теперь учитывается конвертация, переопределённая в наследнике, и работают типы, которых в `switch` не было. + +## Что появилось нового + +Заодно стоит посмотреть, что добавилось в 1.0: + +* [`InlineCopyText`](api/klassy/inlinecopytext.md) — кнопка, копирующая текст в буфер обмена. +* [`MessageBuilder`](api/klassy/messagebuilder.md) — сборка текста по шаблону. +* `ReplyKeyboardBuilder.AddRequestManagedBot` — кнопка, предлагающая выбрать бота. +* `OptionMessage.ShowCaptionAboveMedia` и другие параметры отправки, которые Telegram поддерживает, а библиотека раньше не пробрасывала. +* События сообщений и update, пропущенные при обновлениях Telegram.Bot. diff --git a/docs/postranichnyi-vyvod-informacii-v-soobshenie.md b/docs/postranichnyi-vyvod-informacii-v-soobshenie.md index 15db5cb..1e72e73 100644 --- a/docs/postranichnyi-vyvod-informacii-v-soobshenie.md +++ b/docs/postranichnyi-vyvod-informacii-v-soobshenie.md @@ -158,7 +158,7 @@ public static InlineKeyboardMarkup GetPageMenu( /// Страница /// Размер страницы /// Страница данных с доп информацией -public static async Task> GetPaged(this IList query, +public static Task> GetPaged(this IEnumerable query, int page, int pageSize) where T : class { var result = new PagedResult(); @@ -182,7 +182,7 @@ public static async Task> GetPaged(this IList query, /// Страница. /// Размер страницы. /// Страница данных с доп информацией. - public static async Task> GetPaged(this IEnumerable query, int page, int pageSize) + public static Task> GetPaged(this IEnumerable query, int page, int pageSize) where T : class { var result = new PagedResult();