У меня есть хобби — я учу финский язык. Просто так зубрить новые слова сложно, поэтому я решила написать бота для геймификации процесса. Бот поддерживает разные режимы тренировок, скоринг слов и объединение слов по темам, в общем — имеет довольно сложную ветвистую логику. В процессе создания бота мне удалось создать структуру кода и ресурсов, которая позволяет легко написать диалогового бота любой сложности, не рискуя запутаться и не беспокоясь о создании инфраструктуры. Этой структурой и хочу с вами поделиться.

Это первая статья цикла, в ней я расскажу, как создать базу — шаблонного serverless бота на Python с использованием Yandex Cloud Functions и базы данных YDB с нуля.

В следующих статьях расскажу о том, как добавить боту свои команды, о структуре кода, настройке и обработке пользовательских стейтов, безопасной работе с базой данных, удобном логировании и тестировании бота, которые реализованы в шаблоне. В качестве примера буду использовать примитивного бота, реализованного в моём репозитории ydb_serverless_telegram_bot.

Запускаем бота с нуля

Цель этой статьи - научить создавать основу для бота с нуля. Для этого нам понадобится:

  • Yandex Cloud Function 

  • База данных YDB 

  • TeleBot (pyTelegramBotAPI) Python3 библиотека для работы с Телеграм

  • 30 минут на выполнение инструкции (серьёзно, мы замеряли!)

  • 0 потраченных денег

В итоге получится простой бот, который умеет

  • Спрашивать пошагово имя, фамилию и возраст пользователя, записывать данные в базу

  • Показывать хранящиеся данные по запросу

  • Удалять данные по запросу

С реализацией бота можно познакомиться по ссылке - YDB serverless example. В этой статье мы сделаем точно такого же.

Код бота можно использовать в качестве основы для более сложной логики. В нём реализованы пользовательские стейты, удобные логи, адаптированные под интерфейс Яндекс Облака и основа для тестирования с помощью pytest.

Создаём Yandex Cloud Function

Шаг 1

Перейдите на сайт Яндекс.Облака и нажмите кнопку Подключиться / Консоль в правом верхнем углу экрана. Надпись на кнопке зависит от наличия у вас аккаунта в Облаке.

Авторизуйтесь или зарегистрируйтесь в Яндекс ID.

Шаг 2

В консоли Облака создайте платёжный аккаунт в Яндекс.Облаке. Для создания нужно привязать банковскую карту, но в процессе создания и тестирования бота не потребуется делать платежей. Поддержка бота станет платной только при существенной нагрузке, подробнее об условиях, в которых бот останется бесплатным, можно прочесть здесь.

Скриншот
Создайте платёжный аккаунт
Создайте платёжный аккаунт

Шаг 3

В консоли Яндекс.Облака создайте каталог (папку) для нового проекта, дайте ей любое имя. Здесь и далее имена папок и ресурсов имеют значение только для вас — чтобы было удобно их идентифицировать. С технической точки зрения важны только ID ресурсов, которые генерируются автоматически.

Скриншот
Создайте папку для ресурсов
Создайте папку для ресурсов

Шаг 4

В новой папке создайте сервисный аккаунт и выдайте ему права editor и serverless.functions.invoker. Сервисный аккаунт нужен для разграничения прав в управлении ресурсами Облака между вашим личным аккаунтом и программными действиями для защиты от случайных действий.

Скриншот
Создайте сервисный аккаунт
Создайте сервисный аккаунт

Шаг 5

В папке создайте новый ресурс - API gateway (API шлюз), дайте ему имя и сохраните с настройками по умолчанию. Шлюз понадобится для связи Телеграма и функции — запросы от Телеграма будут запускать функцию через шлюз.

Скриншот
Создайте API шлюз
Создайте API шлюз

Шаг 6

В папке создайте новый ресурс — Функция. Дайте ей имя и сохраните. После создания вы попадете на страницу редактора. Выберите среду выполнения Python 3.11, а затем создайте версию с настройками по умолчанию.

Скриншоты
Создайте функцию
Создайте функцию
Выберите окружение функции
Выберите окружение функции
Создайте версию функции по умолчанию
Создайте версию функции по умолчанию

Шаг 7

На вкладке Обзор созданной функции сделайте функцию публичной и скопируйте значение из поля Идентификатор.

Скриншот
Сделайте функцию публичной
Сделайте функцию публичной
Скопируйте идентификатор функции
Скопируйте идентификатор функции

Шаг 8

Свяжите между собой шлюз и функцию. Для этого перейдите на страницу шлюза, во вкладку Обзор и в поле Спецификация добавьте в конец следующий код, заменив <function ID> на скопированный на предыдущем шаге идентификатор. Сохраните изменения.

  /fshtb-function:
    post:
      x-yc-apigateway-integration:
        type: cloud_functions
        function_id: <function ID>
      operationId: fshtb-function

Создаём бота и подключаем его к функции

Шаг 1

Найдите в Телеграме аккаунт BotFather и пошлите ему команду /newbot. Введите публичное имя бота и его логин. В ответ BotFather пришлет вам токен бота, дающий доступ к его управлению. Храните токен в секрете.

Скриншот
Создайте нового бота
Создайте нового бота

Шаг 2

Создайте меню команд бота. Для этого пошлите команду /setcommands, выберите вашего бота из списка и отправьте следующий список команд:

start - show welcome message and bot description
register - store your name and age in the database
cancel - stop registering process
show_data - show your name and age stored in the database
delete_account - delete your info from the database

Это необязательный шаг, но он добавит удобства общению с ботом. Команды будут подсказываться при наборе текста, а также в левом нижнем углу появится кнопка со списком всех команд.

Скриншоты
Настройте меню команд бота
Настройте меню команд бота
Как выглядит меню команд бота
Как выглядит меню команд бота

Шаг 3

Создайте связь между ботом и функцией через API шлюз. Для этого сделайте следующий запрос через командную строку, заменив <YOUR BOT TOKEN> на токен, который вам прислал BotFather, а <API GATEWAY DOMAIN> на значение Служебный домен с вкладки Обзор вашего API шлюза.

curl \
  --request POST \
  --url https://api.telegram.org/bot<YOUR BOT TOKEN>/setWebhook \
  --header "content-type:application/json" \
  --data "{\"url\": \"<API GATEWAY DOMAIN>/fshtb-function\"}"

При успешном запросе вы должны получить ответ {"ok":true,"result":true,"description":"Webhook was set"}.

Командная строка Windows не поддерживает переносы строк, поэтому воспользуйтесь запросом ниже.

Запрос для Windows
curl --request POST --url https://api.telegram.org/bot<YOUR BOT TOKEN>/setWebhook --header "content-type:application/json" --data "{\"url\": \"<API gateway domain>/fshtb-function\"}"

Шаг 4

Проверим подключение. Отошлите вашему боту команду /start. На данном этапе это должно привести к успешному POST запросу от API шлюза к функции (с кодом 200) и успешному запуску функции. Проверить это можно на вкладках Логи API шлюза и функции. 

Скриншот - успешные логи API шлюза
Успешные логи API шлюза
Успешные логи API шлюза

Скриншот - успешные логи функции
Успешные логи функции
Успешные логи функции

Заметьте, что сейчас функция еще ничего не умеет делать, кроме запуска и засыпания, поэтому не ожидайте ответа на команду. Добавим функциональности в бота позже, в разделе Запускаем бота.

Создаём базу данных

Шаг 1

В папке проекта создайте новый ресурс — База данных YDB. Дайте ей имя и сохраните с дефолтными настройками.

Скриншоты
Создайте базу данных
Создайте базу данных
Сохраните базу данных с настройками по умолчанию
Сохраните базу данных с настройками по умолчанию

Шаг 2

Создайте необходимые таблицы в базе данных. Для этого на странице базы данных во вкладке Навигация нажмите на кнопку Новый SQL-запрос и выполните следующий запрос, который создаст таблицу для хранения данных пользователей и таблицу со стейтами пользователей.

CREATE TABLE `user_personal_info`
(
    `user_id` Uint64,
    `last_name` Utf8,
    `first_name` Utf8,
    `age` Uint64,
    PRIMARY KEY (`user_id`)
);

COMMIT;

CREATE TABLE `states`
(
    `user_id` Uint64,
    `state` Utf8,
    PRIMARY KEY (`user_id`)
);
Скриншот
Создайте необходимые таблицы в БД
Создайте необходимые таблицы в БД

Запускаем бота

В ручном режиме

Шаг 1

Скачайте код из репозитория ydb_serverless_telegram_bot. Создайте ZIP архив с полным содержимым папки. Для этого перейдите в директорию, содержащую файл index.py, выделите все файлы и папки в директории, кликните правой клавишей мыши и выберитe “Создать ZIP архив” для Windows или “Сжать” для MacOS.

Ещё на Linux / MacOS можно выполнить команду zip -r ../code.zip * внутри директории, содержащей файл index.py, архив создастся в родительской директории.

Скриншот - как скачать код из репозитория
Скачайте код из репозитория
Скачайте код из репозитория

Шаг 2

Загрузите код в функцию. Для этого на странице функции во вкладке Редактор выберите в поле Способ значение ZIP-архив, кликните Прикрепить файл, выберите архив с кодом.

Настройте точку входа в функцию — введите в поле Точка входа значение index.handler.

Это будет означать, что запросы от Телеграма к функции будут обрабатываться python функцией под названием handler в файле index.py.

Выберите сервисный аккаунт, созданный в самом начале, в соответствующем поле.

Задайте переменные окружения:

  • YDB_DATABASE со значением Соединение > Размещение базы данных с вкладки Обзор базы данных YDB

  • YDB_ENDPOINT со значением Соединение > Эндпоинт с вкладки Обзор базы данных YDB

  • BOT_TOKEN с токеном вашего бота, полученным от BotFather

Нажмите Создать версию.

Скриншот
Создайте версию функции с кодом из репозитория
Создайте версию функции с кодом из репозитория

Настраивать точку входа, сервисный аккаунт и переменные окружения понадобится только один раз, при необходимости обновить код функции достаточно будет загрузить новый ZIP-архив с кодом.

С помощью командной строки (Linux, MacOS)

Есть другой способ загрузить код в функцию и настроить все необходимые переменные. Для него потребуется немного больше подготовки, но он позволит создавать новую версию функции одной командой. Это полезно на этапе активной разработки.

Для Windows этот способ не сработает из-за отсутствия команды zip.

Шаг 1

Скачайте код из репозитория ydb_serverless_telegram_bot и перейдите внутрь директории с кодом.

Шаг 2

Подготовьте утилиту YC для работы с ресурсами Облака через командную строку по инструкции.

Шаг 3

Отредактируйте файл create_function_version.sh - заполните плейсхолдеры ID ваших ресурсов и токеном. Сохраните изменения и выполните файл из командной строки.

В результате выполнения создастся архив с необходимым кодом и зальется в качестве новой версии функции. В версию функции также автоматически подставятся точка входа, сервисный аккаунт и переменные окружения.

Выполняйте файл каждый раз, когда требуется обновить код функции.

Поговорите с ботом!

Волшебно! Ваш бот готов - кликните меню бота и выберите любую команду или просто отправьте /start.

Попробуйте выполнить все команды из меню.

Примеры команд
Команда /start
Команда /start
Команда /register
Команда /register

Зайдите во вкладку Логи функции, чтобы увидеть логи выполнения кода или логи ошибок, если что-то всё-таки пошло не так.

Как выглядят логи
Логи успешной обработки команд
Логи успешной обработки команд
Лог с ошибкой, которая была искусственно сгенерирована для команды /start
Лог с ошибкой, которая была искусственно сгенерирована для команды /start

Во вкладке Навигация базы данных YDB проверьте содержимое таблицы user_personal_info. В ней должны появиться данные, которые вы передали боту.

Содержимое таблицы user_personal_info
Содержимое таблицы user_personal_info
Содержимое таблицы user_personal_info

Что дальше?

В этой статье я сосредоточилась на том, чтобы показать, как быстро создать инфраструктуру для бота и запустить бота с нуля, не вдаваясь в подробности о том, как он работает и почему шаблон удобен.

Следующие статьи будут посвящены рассказу о том, как реализован бот, как добавить в него новые сценарии, о пользовательских стейтах, логировании, корректном взаимодействии с базой данных и end-to-end тестировании. Kiitos lukemisesta ja nähdään!

Комментарии (2)


  1. mikegordan
    07.08.2023 04:21

    "Для создания нужно привязать банковскую карту,"

    Жаль, хорошая статья (наверно), но тут стена продолжать бессмысленно...

    Надеюсь автор статьи как нибудь сделает еще одну с сервисами которые бесплатные и не требуют карт, например Render или Cyclic.sh или Deta.space


    1. mskozlova Автор
      07.08.2023 04:21

      Понимаю, что это может отпугивать. Спасибо за предложение, попробую поисследовать!