Эта статья предназначена для новичков. Я намерено опускаю сложные детали и нюансы, чтобы материал воспринимался легче.
Всем привет, вот и пришло время для второй части цикла статей посвященных разработке ТГ ботов на PyTelegramBotAPI.
Сегодня мы продолжим изучении ботов, и разберём несколько полезных фукнций.
<<< Предыдущий урок Навигация по статьям Следующий урок >>>
Напомню, что в прошлый раз мы получили токен через BotFather, и написали функцию отправляющую приветственное сообщение на команду /start.
Давайте модернизируем нашего бота. Будем запрашивать имя и фамилию и сохранять эти данные.
Для сохранения данных можно использовать:
Базу данных
Файл
Я в своём примере остановлюсь на словарях. Почему? Всё просто, БД (база данных) несомненно лучший вариант, но надо уметь с ней работать, знать язык запросов (SQL). Файлы - тоже неплохо. Но у них ряд минусов. Во первых при деплое (разворачивании нашего проекта на сервере) к нему может не быть прав, файл занимает дополнительное место на диске, работа с файлами содержит некоторые другие нюансы (например: проблема с кодировкой на Windows). Да и не нужно использовать для такой задачи файлы или БД, можно обойтись и обычными словарями.
Как мы знаем, словари могут хранить любую информации в удобном виде (через ключ-значение), в нашем случае у нас будет два ключа: name и surname. Но, тут возможен очень неприятный баг, который может всплыть только при релизе нашего проекта. Если два пользователя в одно и тоже время воспользуются нашим ботом, то данные в словаре смешаются. Пример:
Пользователь1 вводит имя, например, Иван. Далее он вводит свою фамилию, Иванов. Но, пока Пользователь1 вводил свою фамилию, зашёл Пользователь2 и начал вводить своё имя, он ввел Никита. И из-за того, что у нас один словарь для всех клиентов, данные одного смешались с данными другого. Имя и фамилия Пользователя1 будет не Иван Иванов, а Никита Иванов. Если вы не поняли, прочтите этот абзац заново, это достаточно важно.
Для устранения подобного бага введём ещё один ключ, это будет уникальный id чата. Мы его использовали в прошлый раз для отправки сообщений. Тогда наша структура будет выглядеть так:
1) ID первого чата
1.1) Имя пользователя
1.2) Фамилия
2) ID второго чата
2.1) Имя
2.2) Фамилия
...
Если приводить пример с кодом, то это будет выглядеть как-то так:
users = { 123456: { 'name': 'Henry', 'surname': 'Ford' }, 374519: { 'name': 'Ivan', 'surname': 'Ivanov' } }
Добавим переменную users в нашего бота, и перейдем к написанию функций.
Для начала сделаем функцию сохранения имени:
import telebot from config import TOKEN bot = telebot.TeleBot(TOKEN) users = {} @bot.message_handler(commands=['start']) def welcome(message): chat_id = message.chat.id bot.send_message(chat_id, 'Добро пожаловать в бота сбора обратной связи! Введите своё имя') users[chat_id] = {} bot.register_next_step_handler(message, save_username) def save_username(message): chat_id = message.chat.id name = message.text users[chat_id] = name bot.send_message(chat_id, f'Отлично, {name}. Теперь укажи свою фамилию') if __name__ == '__main__': print('Бот запущен!') bot.infinity_polling()
Как вы можете заметить, наш код значительно увеличился и усложнился. Но не стоит пугаться, сейчас всё расскажу.
На верху (6 строка) был добавлен пустой словарь, он и будет хранить данные пользователей. Далее в функции welcome (14 строка) мы создаем ключ у словаря users, который является id чата. Т.е. наш словарь сейчас выглядит так:
users = { 548269: {} }
После этого, на 15 строчке мы определили следующее действие нашего бота. Не понятно? Сейчас объясню: мы должны запросить у пользователя имя, это понятно, но как это сделать, если до этого мы работали с командами (например команда /start ). А имя это не команда. В этом случае, нам не поможет декоратор message_handler. Необходимо указать боту, что после запуска функции welcome надо запустить другую функцию, которая и будет записываться имя. Другими словами register_next_step_handler - функция позволяющая задать следующий шаг бота. В этой функции мы указываем два аргумента: message - основной объект в телеграм ботах, который содержит всю необходимую информацию (id чата, текст сообщения и тд), а также ссылка на функцию, которая будет далее вызываться.
Надеюсь понятно, если нет - задавайте вопрос в комментариях, постараюсь на все ответить.
Идём дальше. Разберём функцию save_username.
Эта функция нужна для сохранения имени пользователя в файл. В начале функции мы создаём переменные chat_id и name. Первая переменная хранит id чата, это как мы помним уникальный идентификатор чата, используется, например, чтобы отправить сообщение (а в нашем случае ещё как ключ словаря). Вторая переменная - name - хранит текст, который ввёл пользователя в сообщении (копирует текст сообщения). А так как в функции welcome мы запросили имя, то текущий текст это имя.
Далее записываем указанное имя в словарь, и просим пользователя ввести фамилию. Сейчас наш словарь выглядит так (пример):
users = { 548269: { 'name': 'Iv' } }
Давайте теперь создадим функцию для записи фамилии. А также функцию, которая будет выводить данные пользователя (имя и фамилию).
import telebot from config import TOKEN bot = telebot.TeleBot(TOKEN) users = {} @bot.message_handler(commands=['start']) def welcome(message): chat_id = message.chat.id bot.send_message(chat_id, 'Добро пожаловать в бота сбора обратной связи! Введите своё имя') users[chat_id] = {} bot.register_next_step_handler(message, save_username) def save_username(message): chat_id = message.chat.id name = message.text users[chat_id]['name'] = name bot.send_message(chat_id, f'Отлично, {name}. Теперь укажи свою фамилию') bot.register_next_step_handler(message, save_surname) def save_surname(message): chat_id = message.chat.id surname = message.text users[chat_id]['surname'] = surname bot.send_message(chat_id, f'Ваши данные успешно сохранены!') @bot.message_handler(commands=['who_i']) def who_i(message): chat_id = message.chat.id name = users[chat_id]['name'] surname = users[chat_id]['surname'] bot.send_message(chat_id, f'Вы: {name} {surname}') if __name__ == '__main__': print('Бот запущен!') bot.infinity_polling()
Код очень схож с предыдущим. Добавилась фукнция save_surname, которая сохраняет фамилию. Она работает аналогично с save_name. А у save_name добавился register_next_step_handler, так как нам после ввода имени, необходимо следом ввести и фамилию.
Также появилась новая команда /who_i, её функция (who_i) отправляет имя и фамилию текущего пользователя.
Запустим и проверим бота:


Отлично, всё получилось! Разберём наши достижения, сегодня мы:
Создали аналог базы данных, с помощью словарей
Сделали цепочку вызовов через register_next_step_handler
Значительно увеличили функционал бота
<<< Предыдущий урок Навигация по статьям Следующий урок >>>
Вот и подошёл к концу второй урок из цикла, сегодня было сделано много важной и сложной работы. А в следующий раз мы разберём клавиатуру, и наш бот уже выйдет на финишную прямую.
Следующая часть выйдет чер��з три дня.
Код бота на GitHub: https://github.com/Ryize/CollectClientsFeedbackBot
