Найти тему
#<Код своими руками!>

Telegram-bot с нуля. Как сделать каталог с фото "сплошняком" и "перелистывающийся" - редактирование сообщений. Различные шрифта. Урок 7.

Оглавление

Доброго времени суток!

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

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

Также покажу как делать различные шрифты.

В этой статье будет очень много полезного, так что читайте её до конца и всё увидите.

Что нам понадобится:

Для каталога и одной категории, нам понадобятся:

  1. Категория(какая?).
  2. Продукт и его цена.
  3. Фото продукта.
  4. Описание продукта

Собираем данные

1. Категория будет напитки(просто потому что).

Результат по  запросу "Напитки".
Результат по запросу "Напитки".

2. 3. 4. Описание, название и фото мы уже нашли, а цену придумаем сами.

Много брать не будем, я взял 7 напитков, чтобы просто долго не загружать их фото.

Сохраняем фото

Покажу вам более удобный вариант, что в дальнейшем нам облегчит работу.

Создаём в папке с ботом папку "capture" и в ней папку с названием "напитки".

"capture" будет выступать в роли хранилища всех фотографий каталога, а папка "напитки" в роли категории.

папка с ботом.
папка с ботом.

папка capture.
папка capture.

Теперь сохраняем фото напитков в папку "напитки" называя их "1.jpg", "2.jpg", "3.jpg" и так далее.

Потом поймёте зачем.

Главное не забудьте где какой напиток, что ,в прочем, я почти и сделал.

Поэтому сразу делаем два дела, чтобы точно не забыть где и что.

Создаём в папке с ботом файл: "drinks.py" и открываем его.

Создать текстовый документ и переименовать его в "drinks.py"
Создать текстовый документ и переименовать его в "drinks.py"

Открываем файл "drinks.py", я ,как обычно, делаю это через python IDLE.

и в нём создаём массив:

8 строк и 5 столбцов. Как в примере ниже

Пустой массив 8 на 5
Пустой массив 8 на 5

Вы можете подумать: "Но почему же строк 8, если напитков у нас 7?".

А всё потому, что, иногда, при вызове 0-ой строки у вас может вылетать ошибка. Чтобы этого не допускать, сразу её отбрасываем.

Для чего же 5 столбцов:

Столбец 1. Порядковый номер напитка, он может и не нужен в данном примере, т.к. категория у нас одна, но для удобства я оставлю.

Столбец 2. Цена напитка.

Столбец 3. Название напитка.

Столбец 4. Описание напитка.

Столбец 5. Путь к фото напитка.

Вот теперь сохраняем наши фотографии и заносим всё, кроме пути к фото в матрицу, должно получиться так:

матрица
матрица

Осталось внести только путь.

Т.к. папка каталога находится у нас в папке с ботом, то весь путь для неё указывать не нужно.

Добавим в наш 5-ый столбец путь ко всем напиткам,

Порядковый номер строки, точно такой же, как и номер картинки к напитку,(и номер итерации когда мы пойдём по массиву такой же) поэтому просто везде пишем "напитки/1.jpg", "напитки/2.jpg", "напитки/3.jpg" и т.д.

Я упомянул про итерацию потому, что мы можем сделать так. что везде напишем в столбцах только "напитки/", а в программе уже итерацией допишем номер фото, но я оставлю так как есть(номера пропишем сами).

Если на каком-то этапе уже что-то не понятно, или в конце поймёте или пишите ваши вопросы.

Результат. всё заполнено.
Результат. всё заполнено.

Итак, всё у нас готово и можно приступить к коду.

Приступаем к коду.

Если раньше я выкладывал весь код и только потом его объяснял, то сейчас наверное будет по частям, а в конце весь код.

Выводит нам кнопки каталога.
Выводит нам кнопки каталога.

-10

Пока кнопка "🍹Напитки🍹" ничего не делает.

Я вам хочу показать два способа отображения напитков, поэтому сразу сделаем возможность выбора способа отображения(сплошняком или по отдельности)

Т.к. у нас это inline кнопка, то ответить на неё мы можем только с помощью

callback_query_handler.

1. Хэндлер на варианты отображения после нажатия кнопки "🍹Напитки🍹" (можно его не делать, если вы точно знаете что будете использовать только "сплошняком" вывод блюд или по отдельности, просто люди разные и каждому будет удобно по разному).

2. Хэндлер на вывод блюд по выбранному отображению.

Пишем хэндлеры.

-11

@bot.callback_query_handler(func=lambda c: int(c.data)==1 or int(c.data)==2)

def callback_inline(call):

bot.answer_callback_query(callback_query_id=call.id, text="Вывожу блюда, пожалуйста, подождите.", show_alert=True)

if int(call.data)==1:

for i in range(1,8):

pricebtn = types.InlineKeyboardButton(text=str(drinks.matrix[i][1])+'₽', callback_data=i)

price = types.InlineKeyboardMarkup().add(pricebtn)

bot.send_photo(call.from_user.id, open('capture/'+str(drinks.matrix[i][4]), 'rb'), caption='<u><b><i>'+str(drinks.matrix[i][2])+"</i></b></u>\n\n<i>"+str(drinks.matrix[i][3])+"</i>",parse_mode="HTML",reply_markup=price)

Т.к. второй хэндлер не влез, закрепляю код второго хэндлера выше.

Объяснения:

НЕ ЗАБУДЬТЕ импортировать файл drinks!!!

У inline кнопки есть обязательный параметр callback_data, и у нас в кнопке к категории напитки он равен "🍹Напитки🍹", т.е. мы написали хэндлер который ловит этот callback_data, когда пользователь нажимает кнопку "🍹Напитки🍹".

Далее у нас этот хэндлер(в который зашла программа после нажатия кнопки "🍹Напитки🍹" ) выводит пользователю сообщение с вариантами отображения блюд, где в свою очередь тоже есть две кнопки с callback_data "1" и "2".

И после выбора отображения мы переходим ко второму хэндлеру который ловит оба выбора отображения, но внутри него(хэндлера) мы различаем кнопки, т.к. у нас есть возможность посмотреть какой callback_data у нас зашёл. Можете проверить и написать в хэндлере "print(call.data)" и посмотреть что выйдет в консоль, почему "call.data"?, а потому что)))

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

Также мы будем в хэндлере использовать call.from_user.id вместо message.from_user.id.

bot.answer_callback_query(callback_query_id=call.id, text="Выберите формат отображения.", show_alert=False)

Данная строка в хэндлере отвечает пользователю в форме отображающегося окошка с кнопкой "ок", если параметр show_alert=True.

И в виде пуш уведомления сверху если show_alert=False.

Поменяйте и проверьте.

Также, делая inline кнопки я сначала создаю кнопки, а потом их уже вношу в клавиатуру с помощью параметра ".row(***)".

Параметр row() добавляет заданные кнопки в одну строку(у всего есть предел), поэтому я всегда пользуюсь им, т.к. знаю как будет выглядеть и сколько кнопок будет в строке.

Если вы хотите сделать несколько строк, то просто внесите кнопки так:

.row(button,button1).row(button2).row(button3).

Или можете воспользоваться параметром .add() написать в него кнопок сколько захотите. но по умолчанию в нём стоит две кнопки в строке, так что придётся поставить значение побольше, если надо, а как это сделать можете посмотреть в документации. Или спросить меня(не хочу на этом заострять внимание, т.к. суть статьи другая).

bot.send_photo(call.from_user.id, open('capture/'+str(drinks.matrix[i][4]), 'rb'), caption='<u><b><i>'+str(drinks.matrix[i][2])+"</i></b></u>\n\n<i>"+str(drinks.matrix[i][3])+"</i>",parse_mode="HTML",reply_markup=price)

Собственно, в этой строке у нас выводятся блюда.

Параметр 1. call.from_user.id - чат с пользователем(куда отправлять).

Параметр 2. open('capture/'+str(drinks.matrix[i][4]), 'rb') - готовит нашу картинку с напитком к отправке. Как видите путь с папки с ботом я дополняю "capture/" и соединяю его с путём который у нас находится в массиве в 4-ом столбце(столбцы начинаются с 0).

Обращаемся к массиву в файле drinks с помощью точки "drinks.matrix[*][*]".

Параметр 3. caption='<u><b><i>'+str(drinks.matrix[i][2])+"</i></b></u>\n\n<i>"+str(drinks.matrix[i][3])+"</i>" - Описание к картинке, тут я вывожу сначала название напитка,(также беру из массива) и затем через отступ вывожу описание. Как вы можете заметить тут появляются непонятные <u><b><i>, а это стили шрифта в HTML, который я объявляю далее как parse_mode="HTML", поэкспериментируйте или посмотрите документацию, что за что отвечает. Главное не забудьте что их надо, соответственно, закрывать </i></b></u>.

Также я приклепляю inline кнопку с текстом цены, которую я взял из массива.

Выводятся разом 7 напитков.(Влезло в скрин 2).
Выводятся разом 7 напитков.(Влезло в скрин 2).

Идём далее, Пока у нас есть только отображение сплошняком, делаем отображение по частям.

Выводить будем одно блюдо и дальше его уже листать,

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

Я сделаю три хэндлера:

1. Если начало нашей категории, то надо, чтобы была в пролистывании только кнопка "Далее"

2.Если не начало и не конец, то "Далее" и "Назад".

3.Если конец категории, то только "Назад".

Чтобы у нас было "пролистывание", нам надо редактировать сообщение с напитком которое уже есть в чате на следующий напиток или на предыдущий, но как же нам быть если этого сообщения ещё нет? Писать ещё один хэндлер где будет выводиться самый первый напиток?

Я сделал, чтобы хэндлер в котором мы оказываемся после выбора отображения, сразу выводился один напиток, если мы выбрали отображение по отдельности, а уже затем переходим к трём другим хэндлерам, где всё зависит от того, начало это, середина или конец.

Отображение по отдельности.
Отображение по отдельности.
код
код

Строчка которая не влезла:

bot.edit_message_media(chat_id=call.from_user.id, message_id=int(call.message.message_id), media=types.InputMediaPhoto(photo, caption='<u><b><i>'+str(drinks.matrix[a][2])+'</i></b></u>\n\n<i>'+str(drinks.matrix[a][3])+"</i>",parse_mode="HTML"),reply_markup=price)

Я бы вставил сюда всё одной картинкой, но тогда шрифт будет очень мелким, да и код в виде текста тут не очень выглядит, надеюсь вам понятно.

Видео с работой бота я прикреплю в посте после этой статьи.

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

1. a=int(call.data)-10 - Здесь мы просто от полученного callback_data вычитаем 10 чтобы в дальнейшем приписать к первому индексу матрицы скромную "а".

Далее у нас идут кнопки с ценой, Далее и Назад.

В callback_data к кнопкам Назад и Далее мы передаём значение "int(call.data)+1" или "int(call.data)-1", "a+1" и "а-1" - нам не подходит, т.к. в "a" значение индекса напитка.

В строке photo = open('capture/'+str(drinks.matrix[a][4]), 'rb'), мы также открываем путь к нашей картинке, сделать это в функции редактирования картинки не удастся.

__________________________________________________________________________________________

-Спасибо, что дочитали данную статью до конца, Надеюсь, она была вам полезна. Также если что-то осталось не разобранным и непонятным - пишите в комментарии. Подписывайтесь, дальше будет ещё интереснее.