Эта статья предназначена для новичков. Я намерено опускаю сложные детали и нюансы, чтобы материал воспринимался легче.
Всем вновь привет! Продолжаем цикл статей, посвящённых разработке телеграм бота на PyTelegramBotAPI.
Сегодня мы полностью доделаем код нашего бота, напишем аннотацию типов, приготовим проект к деплою.
А начнём мы, конечно, с чего-нибудь новенького, а именно - покажем уведомление при нажатии на кнопку, вместо сообщения. Напомню, что на прошлом уроке мы сделали две клавиатуры - Reply и Inline, но нас сейчас интересует вторая. Сейчас при нажатии на кнопку сообщение отредактируется, а сама клавиатура пропадёт. Но мы сделаем круче, пусть пр нажатии на кнопку показывается уведомление, а сообщение просто удаляется, мне кажется так будет красивее и лаконичнее (ну и новый функционал на примере разберём).
Есть два вида уведомлений:
1)
2)
Я для своего бота буду использовать первый вариант уведомлений, мне кажется он подойдёт лучше.
Чтобы удалить сообщение надо вызвать соответствующий метод и передать в него id чата и id сообщения. Полный код функции save_btn
будет выглядеть так:
@bot.callback_query_handler(func=lambda call: call.data == 'save_data')
def save_btn(call):
message = call.message
chat_id = message.chat.id
message_id = message.message_id
bot.answer_callback_query(call.id, text="Данные сохранены")
bot.delete_message(chat_id=chat_id, message_id=message_id)
Удаление сообщения происходит на 7 строке.
Теперь давайте отправим фото. А отправлять мы его будем, когда пользовать напишет команду /start. Напомню, что у нас уже есть обработчик этой команды - это функция welcome. Отправить фото легко, сохраняем его в папку с проектом, открываем файл и читаем его в бинарном виде, а далее вызываем метод send_photo
передав туда id чата и прочтённые бинарные данные.
Код изменённой функции welcome выглядит так:
@bot.message_handler(commands=['start'])
def welcome(message):
chat_id = message.chat.id
keyboard = telebot.types.ReplyKeyboardMarkup()
button_save = telebot.types.InlineKeyboardButton(
text="Написать в поддержку")
keyboard.add(button_save)
bot.send_message(chat_id,
'Добро пожаловать в бота сбора обратной связи',
reply_markup=keyboard)
with open('feedback.jpeg', 'rb') as file:
photo = file.read()
bot.send_photo(chat_id, photo)
Вот так это будет выглядеть:
Ну, а теперь финальный штрих. После того как пользователь укажет имя, фамилию и нажмёт кнопку Сохранить, мы должны запросить у него отзыв (у нас же всё таки бот обратной связи) и отправить его в телеграм заранее указанным администраторам.
Для хранения ID чата администраторов я буду использовать кортеж (чтобы получить id чата, вы можете вывести print'ом переменную chat_id).
bot = telebot.TeleBot(TOKEN)
users = {}
administrators = (814401631,)
Запишем это в самом верху, сразу после переменной users.
Теперь модифицируем функцию save_btn
, чтобы она запрашивала отзыв и после вызывала другую функцию с помощью уже знакомого нам register_next_step_handler.
@bot.callback_query_handler(func=lambda call: call.data == 'save_data')
def save_btn(call):
message = call.message
chat_id = message.chat.id
message_id = message.message_id
bot.answer_callback_query(call.id, text="Данные сохранены")
bot.delete_message(chat_id=chat_id, message_id=message_id)
bot.send_message(chat_id, 'Введите текст отзыва: ')
bot.register_next_step_handler(message, send_feedback_administrators)
А теперь создадим и саму функцию send_feedback_administrators, он будет отправлять всем указанным в кортеже администраторам сообщение в формате: %Фамилия% %Имя% оставил отзыв: %Текст отзыва%
def send_feedback_administrators(message):
feedback = message.text
user = users[message.chat.id]
name = user['name']
surname = user['surname']
for admin_chat_id in administrators:
bot.send_message(admin_chat_id,
f'{surname} {name} оставил отзыв: {feedback}')
Теперь посмотрим как это выглядит:
Отлично, всё прекрасно работает!
Итоговый код выглядит так:
import telebot
from config import TOKEN
bot = telebot.TeleBot(TOKEN)
users = {}
administrators = (814401631,)
@bot.message_handler(commands=['start'])
def welcome(message):
chat_id = message.chat.id
keyboard = telebot.types.ReplyKeyboardMarkup()
button_save = telebot.types.InlineKeyboardButton(
text="Написать в поддержку")
keyboard.add(button_save)
bot.send_message(chat_id,
'Добро пожаловать в бота сбора обратной связи',
reply_markup=keyboard)
with open('feedback.jpeg', 'rb') as file:
photo = file.read()
bot.send_photo(chat_id, photo)
@bot.message_handler(
func=lambda message: message.text == 'Написать в поддержку')
def write_to_support(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
keyboard = telebot.types.InlineKeyboardMarkup()
button_save = telebot.types.InlineKeyboardButton(text="Сохранить",
callback_data='save_data')
button_change = telebot.types.InlineKeyboardButton(text="Изменить",
callback_data='change_data')
keyboard.add(button_save, button_change)
bot.send_message(chat_id, f'Сохранить данные?', reply_markup=keyboard)
@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}')
@bot.callback_query_handler(func=lambda call: call.data == 'save_data')
def save_btn(call):
message = call.message
chat_id = message.chat.id
message_id = message.message_id
bot.answer_callback_query(call.id, text="Данные сохранены")
bot.delete_message(chat_id=chat_id, message_id=message_id)
bot.send_message(chat_id, 'Введите текст отзыва: ')
bot.register_next_step_handler(message, send_feedback_administrators)
def send_feedback_administrators(message):
feedback = message.text
user = users[message.chat.id]
name = user['name']
surname = user['surname']
for admin_chat_id in administrators:
bot.send_message(admin_chat_id,
f'{surname} {name} оставил отзыв: {feedback}')
@bot.callback_query_handler(func=lambda call: call.data == 'change_data')
def save_btn(call):
message = call.message
chat_id = message.chat.id
message_id = message.message_id
bot.edit_message_text(chat_id=chat_id, message_id=message_id,
text='Изменение данных!')
write_to_support(message)
if __name__ == '__main__':
print('Бот запущен!')
bot.infinity_polling()
А теперь, подведём итоги, сегодня мы сделали:
Отправку уведомлений, удаление сообщений
Отправку фото
Закончили всю техническую часть бота, связанную именно с разработкой
Что ж, вот четвёртая, предпоследняя статья из цикла подошла к концу. Сейчас мы получили работающего бота, но... работающего только у нас на компьютере. А это значит, что стоит нам выключить наше устройство и бот сразу перестанет работать.
В последней пятой части мы исправим это, мы выложим наш проект на сервер и запустим. После этого наш бот будет доступен всегда, не зависимо от состояния нашего компьютера.
Seruios
Не обязательно делать photo = file.read(), достаточно просто вставить file в send_photo(). Так же я бы очень посоветовал переписать register next step handler под состояния states.