Найти тему
Движение-Жизнь

Написание телеграм-бота на Python. Часть четвертая

В этой части я познакомлю вас с "регулярными выражениями", мы подключимся к серверу API для того чтобы сделать запрос и получить ответ от сервера в виде JSON (Java Script Object Notation), сохраним этот ответ в файл any_file.json. Ну и начнем писать обработчик команды "/lowprice".

С чего всё начиналось:

Итак, продолжаем...

Первое, что нам понадобится для дальнейшего написания бота - это ключ для авторизации на сервере hotels.com. Для этого достаточно нажать кнопочку "Sign In" и войти под своим гугл аккаунтом. После того как произведен вход на сайт жмем на менюшку Endpoints:

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

-2

Можно выполнить запрос к серверу тем кодом, что приведен будет на сайте, но мы сделаем всё по красоте и впишем нужные строки в наш шаблон.

-3

Открываем PyCharm (или любой другой редактор), наш шаблон проекта и начинаем править файлы, для начала в корне проекта исправляем файл .env, вписывая туда X-RapidAPI-Key:

-4

У нас уже есть все необходимые коды доступа к серверам телеграмм и hotels.com, они вписаны в нужный файл, бот в свою очередь уже может минимально работать, то есть реагировать на какие то простейшие команды, и конечно же - если он какую-то команду не знает, то он "попугайничает" эхом со стандартным ответом:

-5

Я добавил в пункт меню, соответствующие команды (которые будут, в последствии, использоваться ботом), начнем с обработки команды /lowprice:

-6
как добавить команды в пункт "Меню"
как добавить команды в пункт "Меню"

Для того чтобы бот понял команду /lowprice, нужно сделать соответствующие изменения в некоторых файлах структуры нашего бота. Для новых команд я создам новую папку, в папке handlers, там будут лежать все файлы отвечающие за реакцию на команды:
/lowprice
/hiprice
/bestdeal
/history
У каждой команды будет свой файл. При создании новой папки, в ней будет автоматом (у меня именно так) появляться файл __init__.py в котором нужно будет указать все файлы или папки находящиеся в этой директории. Для доступа к ним через точку.

-8

Делается это описанием типа from . import папка_или_файл:

-9

Итак, создаем в папке handlers папку find_hotels (может быть не самое удачное имя для папки я придумал, может стоило выбрать что-то более подходящее). Не забыв при этом (это важно!!!) в файле __init__.py прописать папку find_hotels перед!!! а не после папки default_handlers. Иначе интерпретатор будет читать файлы в "папке по умолчанию" и не будет реагировать на все наши команды, ибо он будет встречать на своем пути файл echo.py который реагирует на все неизвестные команды и слова.

-10

В папке find_hotels создаем файл lowprice.py (щелкнув на папке правой кнопкой мыши и выбрав пункт:

-11

Ну вот у нас есть пустой файл lowprice.py, пропишем все необходимые модули, и минимальный код для реакции на команду /lowprice:

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

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

Можно запустить нашего бота и он ожидаемо среагирует на нашу команду /lowprice:

-13

Но дальше, у нас нет прописанной реакции на ввод пользователя и ожидаемо выдаст "эхо":

-14

Продолжим написание кода, и пропишем реакцию на ввод от пользователя, с помощью машины состояний, для этого нужно в папке states создать файл в котором будет прописаны возможные состояния пользователя. А именно: что ввёл пользователь для запроса, затем какой выбрал город в котором будет искаться отель, и даты заезда и выезда.

не забываем править файл __init__.py
не забываем править файл __init__.py

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

-16

Декоратор @bot.message_handler(command=['lowprice']) реагирует на ввод от пользователя команды /lowprice, и если поступила такая команда то передается управление функции low_price(message: Message).
Далее пользователю предлагается ввести город, в котором он хочет поискать гостиницу. Бот отслеживает "состояние пользователя"
UserInputInfo.input_city и как только состояние этой переменной меняется, значит пользователь выполнил ввод и переходит управление через декоратор, отслеживающий состояние переменной UserInputInfo.input_city, к функции find_city(message: Message). Там уже запоминаем то что пользователь ввёл и выдаем в чат сообщение о том что же собирается пользователь искать:

-17

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

Следующий шаг, это отправка запроса к серверу hotels.com и получение от него ответа из которого мы сформируем словарь со всеми возможными значениями {key: value} {destinationId: "описание города"}. И полученный словарь я для наглядности выведу в консоль пайчарма.

-18

Для выполнения следующего шага программы потребуется подключить модуль: from utils.find_destination_id import destination_id

Ну и соответственно создать файл в папке utils с именем find_destination_id, в котором будет функция destination_id, которая сделает запрос к серверу и вернет нам словарь с вариантами городов.

-19

Нам нужно импортировать следующие модули:

  • requests (отвечает за отправку запроса к серверу)
  • json (отвечает за приведение ответу от сервера к иерархическому виду)
  • re (regular expressions поддержка регулярных выражений)
  • from config_data import config (подключение ключа от сервера)

Когда все ключи прописаны, все модули подключены, можно выполнять запрос к серверу. В переменной pattern содержится регулярное выражение которое помогает из текста выбросить всё что нам не потребуется (нам нужны лишь описание населенного пункта и его destination_id).

Логика примерно такова:

  • создаем строку запроса
  • посылаем запрос к серверу используя все необходимые параметры
  • приняли ответ и его нужно "десериализовать" или предать ему форму. В модуле json есть методы load() и loads() для превращения кодированных данных JSON в объекты Python. Load - для файлов, loads - для трок.
  • дальше я записываю полученные данные в локальный файл, потому как оперировать с локальными данными проще, ну и существует ограничение на сервере для бесплатного пользования их данными (500 запросов в месяц) и когда тестируешь программу можно легко не вложиться в лимит. Поэтому в будущих статьях я не буду использовать сервер, лишь тогда когда мой бот будет на этапе завершения и защиты дипломной работы (да, это моя дипломная работа)
  • с помощью цикла проходимся по полученному десериализованному объекту, выбираем из него только то что нам нужно (destination_id (key) и caption(value)) этими данными заполняем наш словарь
  • возвращаем наш словарь туда откуда была вызвана функция
  • вывожу полученный словарь в консоль для понимания что там содержится

На этом очередная часть написания телеграмм-бота на Python подошла к концу. Прошу профессионалов меня строго не судить, может быть код получается корявый но это мой код. Может не всё верно, возможно я сейчас иду не той дорогой. Есть структурное понимание данного бота, но есть еще множество непонятых деталей. И данный текст - попытка уложить все данные по полочкам в своей голове. А если еще и людям помогу - тогда я красавчик!

На этом всё, как-то так. Всем всех благ, здоровья, счастья и любви!