PyTelegramBotAPI на примере проекта сбора обратной связи #2
<<< Предыдущий урок
Эта статья предназначена для новичков. Я намерено опускаю сложные детали и нюансы, чтобы материал воспринимался легче.
Всем привет, вот и пришло время для второй части цикла статей посвященных разработке ТГ ботов на 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