В этой части я познакомлю вас с "регулярными выражениями", мы подключимся к серверу API для того чтобы сделать запрос и получить ответ от сервера в виде JSON (Java Script Object Notation), сохраним этот ответ в файл any_file.json. Ну и начнем писать обработчик команды "/lowprice".
С чего всё начиналось:
Итак, продолжаем...
Первое, что нам понадобится для дальнейшего написания бота - это ключ для авторизации на сервере hotels.com. Для этого достаточно нажать кнопочку "Sign In" и войти под своим гугл аккаунтом. После того как произведен вход на сайт жмем на менюшку Endpoints:
Там будет представлен тот самый ключ который нам нужен, а так же минимальный набор кода для выполнения запроса к серверу.
Можно выполнить запрос к серверу тем кодом, что приведен будет на сайте, но мы сделаем всё по красоте и впишем нужные строки в наш шаблон.
Открываем PyCharm (или любой другой редактор), наш шаблон проекта и начинаем править файлы, для начала в корне проекта исправляем файл .env, вписывая туда X-RapidAPI-Key:
У нас уже есть все необходимые коды доступа к серверам телеграмм и hotels.com, они вписаны в нужный файл, бот в свою очередь уже может минимально работать, то есть реагировать на какие то простейшие команды, и конечно же - если он какую-то команду не знает, то он "попугайничает" эхом со стандартным ответом:
Я добавил в пункт меню, соответствующие команды (которые будут, в последствии, использоваться ботом), начнем с обработки команды /lowprice:
Для того чтобы бот понял команду /lowprice, нужно сделать соответствующие изменения в некоторых файлах структуры нашего бота. Для новых команд я создам новую папку, в папке handlers, там будут лежать все файлы отвечающие за реакцию на команды:
/lowprice
/hiprice
/bestdeal
/history
У каждой команды будет свой файл. При создании новой папки, в ней будет автоматом (у меня именно так) появляться файл __init__.py в котором нужно будет указать все файлы или папки находящиеся в этой директории. Для доступа к ним через точку.
Делается это описанием типа from . import папка_или_файл:
Итак, создаем в папке handlers папку find_hotels (может быть не самое удачное имя для папки я придумал, может стоило выбрать что-то более подходящее). Не забыв при этом (это важно!!!) в файле __init__.py прописать папку find_hotels перед!!! а не после папки default_handlers. Иначе интерпретатор будет читать файлы в "папке по умолчанию" и не будет реагировать на все наши команды, ибо он будет встречать на своем пути файл echo.py который реагирует на все неизвестные команды и слова.
В папке find_hotels создаем файл lowprice.py (щелкнув на папке правой кнопкой мыши и выбрав пункт:
Ну вот у нас есть пустой файл lowprice.py, пропишем все необходимые модули, и минимальный код для реакции на команду /lowprice:
Первый модуль можно и не использовать, он несет лишь пояснительную нагрузку, всего лишь помогает программисту понимать какого типа переменная в качестве аргумента функции.
Можно запустить нашего бота и он ожидаемо среагирует на нашу команду /lowprice:
Но дальше, у нас нет прописанной реакции на ввод пользователя и ожидаемо выдаст "эхо":
Продолжим написание кода, и пропишем реакцию на ввод от пользователя, с помощью машины состояний, для этого нужно в папке states создать файл в котором будет прописаны возможные состояния пользователя. А именно: что ввёл пользователь для запроса, затем какой выбрал город в котором будет искаться отель, и даты заезда и выезда.
Попытаюсь более детально разобрать нижеприведенный код:
Декоратор @bot.message_handler(command=['lowprice']) реагирует на ввод от пользователя команды /lowprice, и если поступила такая команда то передается управление функции low_price(message: Message).
Далее пользователю предлагается ввести город, в котором он хочет поискать гостиницу. Бот отслеживает "состояние пользователя" UserInputInfo.input_city и как только состояние этой переменной меняется, значит пользователь выполнил ввод и переходит управление через декоратор, отслеживающий состояние переменной UserInputInfo.input_city, к функции find_city(message: Message). Там уже запоминаем то что пользователь ввёл и выдаем в чат сообщение о том что же собирается пользователь искать:
Городов в мире очень много и, сюрприз, даже Рим может быть не только итальянским, запрос по городам может выдать нам не один населенный пункт с именем, скажем Рим (я буду использовать при написании бота именно Рим).
Следующий шаг, это отправка запроса к серверу hotels.com и получение от него ответа из которого мы сформируем словарь со всеми возможными значениями {key: value} {destinationId: "описание города"}. И полученный словарь я для наглядности выведу в консоль пайчарма.
Для выполнения следующего шага программы потребуется подключить модуль: from utils.find_destination_id import destination_id
Ну и соответственно создать файл в папке utils с именем find_destination_id, в котором будет функция destination_id, которая сделает запрос к серверу и вернет нам словарь с вариантами городов.
Нам нужно импортировать следующие модули:
- 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 подошла к концу. Прошу профессионалов меня строго не судить, может быть код получается корявый но это мой код. Может не всё верно, возможно я сейчас иду не той дорогой. Есть структурное понимание данного бота, но есть еще множество непонятых деталей. И данный текст - попытка уложить все данные по полочкам в своей голове. А если еще и людям помогу - тогда я красавчик!
На этом всё, как-то так. Всем всех благ, здоровья, счастья и любви!