Шаг №1: Реализуйте запросы обменных курсов
Давайте напишем сценарий Python, который будет реализовывать логику для запросов конкретных курсов обмена валют. Мы собираемся использовать API PrivatBank .
Пример ответа:
[
{
ccy: "EUR",
base_ccy: "UAH",
buy: "28.70000",
sale: "29.10000"
},
{
ccy: "RUR",
base_ccy: "UAH",
buy: "0.39300",
sale: "0.40300"
},
{
ccy: "USD",
base_ccy: "UAH",
buy: "25.40000",
sale: "25.70000"
}
]
Давайте создадим pb.ру файл и запишем следующий код:
# -*- coding: utf-8 -*-
import re
import requests
import json<br>
URL = 'https://api.privatbank.ua/p24api/pubinfo?json&exchange&coursid=5'
<br>def load_exchange():
return json.loads(requests.get(URL).text)
def get_exchange(ccy_key):
for exc in load_exchange():
if ccy_key == exc['ccy']:
return exc
return False
def get_exchanges(ccy_pattern):
result = []
ccy_pattern = re.escape(ccy_pattern) + '.*'
for exc in load_exchange():
if re.match(ccy_pattern, exc['ccy'], re.IGNORECASE) is not None:
result.append(exc)
return result
Мы реализовали три метода:
- load_exchange: загружает курсы обмена по указанному URL-адресу и возвращает тип dict .
- get_exchange: возвращает обменные курсы для запрошенной валюты.
- get_exchanges: возвращает список валют в соответствии с шаблоном (требуется при поиске валют во встроенных запросах).
Создаём и настроим (укажем имя и описание) бота в телеграмм
(Как создавать бота я рассказал в 1 статье и на видео уроке)
Шаг № 3: Настройте и инициализируйте бота
Начнем с создания файла config.py для настройки:
# -*- coding: utf-8 -*-
TOKEN = '<bot token>’'
TIMEZONE = 'Europe/Kiev'
TIMEZONE_COMMON_NAME = 'Kiev'
Здесь у нас есть: токен бота и часовой пояс, в котором он будет работать (он понадобится вам в будущем, чтобы указать время обновления сообщения. Telegram API не позволяет узнать часовой пояс пользователя, поэтому обновление время должно отображаться с указанием часового пояса).
Давайте создадим bot.py файл, импортировать все необходимые библиотеки, конфигурационные файлы и ранее созданный pb.py . Если какие-то библиотеки отсутствуют, установите их через pip .
# -*- coding: utf-8 -*-
import telebot
import config
import pb
import datetime
import pytz
import json
import traceback
P_TIMEZONE = pytz.timezone(config.TIMEZONE)
TIMEZONE_COMMON_NAME = config.TIMEZONE_COMMON_NAME
Создадим бота с помощью библиотеки pyTelegramBotAPI . Для этого нужно передать токен в конструктор:
bot = telebot.TeleBot (config.TOKEN)
bot.polling (none_stop = True)
Шаг №4: Напишите обработчик команд / start
Теперь ваш чат-бот Python инициализирован и постоянно запрашивает метод getUpdates. Параметр none_stop отвечает за продолжение опроса, даже если API возвращает ошибку при выполнении метода.
Затем можно вызывать любые методы API Telegram Bot из переменной бота.
Начнем с написания обработчика команд / start и добавим его перед строкой bot.polling (none_stop = True) :
@bot. message_handler (commands=['start']) def start_command (message): bot.send a message( message. chat. ID, - Привет! Я могу показать тебе курсы валют банка!.\n' + Чтобы узнать обменный курс, нажмите кнопку / exchange.\n' + - Чтобы получить справку, нажмите / help.'
Как видите, pyTelegramBotApi использует декораторы Python для инициализации обработчиков различных команд Telegram. Вы также можете перехватывать сообщения, используя регулярное выражение, их тип содержимого и лямбда-функции.
В нашем случае, если выполняется условие commands = ['start'], будет вызвана функция start_command . В эту функцию будет передан объект сообщения (десериализованный тип сообщения ). После этого вы просто выполняете send_message в том же чате с указанным сообщением.
Уф, это было легко, не так ли?
Шаг № 5: Создайте обработчик команд / help
Давайте добавим в наш обработчик команд / help встроенную кнопку, связывающую вашу учетную запись Telegram. Назовем кнопку «Написать разработчику».
@bot.message_handler(commands=['help'])
def help_command(message):
keyboard = telebot.types.InlineKeyboardMarkup()
keyboard.add(
telebot.types.InlineKeyboardButton(
‘Message the developer’, url='telegram.me/artiomtb'
)
)
bot.send_message(
message.chat.id,
'1) для получения списка доступных валют нажмите кнопку /exchange.\n' + 2) Нажмите на интересующую вас валюту.\n' + '3) Вы получите сообщение, содержащее информацию об источнике и целевых валютах,' + - цены покупки и продажи.\n' + '4) Нажмите кнопку "Обновить", чтобы получить текущую информацию о запросе. ' + - Бот также покажет разницу между предыдущим и текущим обменными курсами.\n' + '5) бот поддерживает встроенные функции. Введите @ < botusername> в любом чате и первые буквы валюты.',
reply_markup=keyboard
)
Как вы можете видеть в приведенном выше примере, я использовал дополнительный параметр ( reply_markup ) для метода send_message . Метод получил InlineKeyboardMarkup, состоящий из одной кнопки InlineKeyboardButton с текстом: «Сообщение разработчику» и url = 'telegram.me / artiomtb'.
Вышесказанное выглядит так:
Шаг №6: Добавьте обработчик команд / exchange
В / обмене команды обработчик отображает меню выбора валюты и встроенный бот клавиатуру , состоящее из 3 -х кнопок: USD, EUR и RUR (эти валюты поддерживаются банковским API).
@bot. message_handler (commands=['exchange']) def exchange_command(message): < br> keyboard = telebot. types.InlineKeyboardMarkup() keyboard.row( telebot. types.InlineKeyboardButton ('USD', callback_data= 'get-USD') ) keyboard.row( telebot. types.InlineKeyboardButton ('EUR', callback_data= 'get-EUR'), telebot. types.InlineKeyboardButton('RUR', callback_data= 'get-RUR') ) bot. send_message(message.chat.id, ' click on the selected currency:', reply_markup=keyboard)
Позвольте мне объяснить, что такое callback-данные в InlineKeyboardButton. Когда пользователь нажимает эту кнопку, вы получите CallbackQuery (его параметр data будет содержать данные обратного вызова ) в getUpdates . Таким образом, вы будете точно знать, какую кнопку нажал пользователь, и обрабатывать ее соответствующим образом.
Кстати, вот как выглядит ответ / exchange :
Шаг № 7: Напишите встроенный обработчик нажатия кнопки
pyTelegramBotAPI предлагает использовать декоратор @ bot.callback_query_handler, который передаст объект CallbackQuery во вложенную функцию.
@bot.callback_query_handler(func=lambda call: True)
def iq_callback(query):
data = query.data
if data.startswith('get-'):
get_ex_callback(query)
Давайте реализуем get_ex_callback метод:
def get_ex_callback(query):
bot.answer_callback_query(query.id)
send_exchange_result(query.message, query.data[4:])
Метод answer_callback_query требуется для удаления состояния загрузки, которое появляется при нажатии кнопки. Отправим сообщение в send_exchange_query . Вам нужно будет передать ему Сообщение и код валюты (вы можете получить его из query.data. Если это было, например, get-USD, то передайте USD).
Реализуем send_exchange_result :
Def send_exchange_result (message, ex_code): bot. send_chat_action(message.chat.id, "typing") ex = pb. get_exchange(ex_code) bot.send a message( message. chat. ID serialize_ex (ex), reply_markup=get_update_keyboard(ex), parse_mode= 'HTML' )
Это тоже довольно просто.
Давайте сначала отправим состояние ввода в чат, чтобы бот отображал индикатор «набора текста», пока банковский API получает запрос. Теперь вызовем метод get_exchange из файла pb.py, который получит код валюты (например, USD). Вам также нужно будет вызвать в send_message два новых метода : serialize_ex, сериализатор валюты и get_update_keyboard (который возвращает клавиатуру к кнопкам «Обновить» и «Поделиться»).
Def get_update_keyboard(ex):
keyboard = telebot. types.InlineKeyboardMarkup()
keyboard.row(
telebot. types.InlineKeyboardButton(
"Update",
callback_data=JSON format.dump({
't': 'u',
'e': {
Kommersant: ex ["buy"],
"C": former ['sales'],
"C": former ['convertible currency']
}
}). replace (' ',")
),
telebot. types.InlineKeyboardButton ("share", switch_inline_query=e ['convertible currency'])
)
return the keyboard
Запишем в get_update_keyboard текущие курсы валют в callback_data в формате JSON. JSON намеренно сжимается, поскольку максимально допустимый размер файла составляет 64 байта.
Ключ t означает тип, а ключ e означает обмен. Остальное делается по тому же принципу.
Кнопка «Поделиться» будет иметь параметр switch_inline_query . Нажатие кнопки предложит пользователю выбрать один из своих чатов, открыть этот чат и вставить имя пользователя бота и указанный встроенный запрос в поле ввода.
Затем давайте представим метод serialize_ex и вспомогательный serialize_exchange_diff, необходимый для отображения разницы между текущим и старым обменными курсами после нажатия кнопки «Обновить».
def serialize_ex(ex_json, diff=None):
result = '<b>' + ex_json['base_ccy'] + ' -> ' + ex_json['ccy'] + ':</b>\n\n' + \
'Buy: ' + ex_json['buy']
if diff:
result += ' ' + serialize_exchange_diff(diff['buy_diff']) + '\n' + \
'Sell: ' + ex_json['sale'] + \
' ' + serialize_exchange_diff(diff['sale_diff']) + '\n'
else:
result += '\nSell: ' + ex_json['sale'] + '\n'
return result
def serialize_exchange_diff(diff):
result = ''
if diff > 0:
result = '(' + str(diff) + ' <img draggable="false" data-mce-resize="false" data-mce-placeholder="1" data-wp-emoji="1" class="emoji" alt="<img draggable="false" data-mce-resize="false" data-mce-placeholder="1" data-wp-emoji="1" class="emoji" alt="<img draggable="false" data-mce-resize="false" data-mce-placeholder="1" data-wp-emoji="1" class="emoji" alt="<img draggable="false" data-mce-resize="false" data-mce-placeholder="1" data-wp-emoji="1" class="emoji" alt="<img draggable="false" data-mce-resize="false"
Как видите, метод serialize_ex получает необязательный параметр diff. Здесь вы передадите разницу между обменными курсами в {'buy_diff': <float> , 'sale_diff': <float> }. Это произойдет во время сериализации, когда вы нажмете кнопку «Обновить». Он нам не понадобится при первом отображении курсов валют на экране.
Вот как выглядит ответ бота при нажатии кнопки USD:
Шаг № 8: Реализуйте обработчик кнопки обновления
Теперь вы готовы реализовать обработчик кнопки «Обновить». После дополнения метода iq_callback он будет выглядеть следующим образом:
@bot.callback_query_handler(func=lambda call: True)
def iq_callback(query):
data = query.data
if data.startswith('get-'):
get_ex_callback(query)
else:
try:
if json.loads(data)['t'] == 'u':
edit_message_callback(query)
except ValueError:
pass
Если данные обратного вызова начинаются с get- ' ( get-USD , get-EUR и так далее), тогда давайте вызовем get_ex_callback, как мы делали раньше. В противном случае попробуем разобрать JSON и получить его t- ключ. Если он равен «u», вызовите метод edit_message_callback . Реализуем это:
def edit_message_callback(query):
data = json.loads(query.data)['e']
exchange_now = pb.get_exchange(data['c'])
text = serialize_ex(
exchange_now,
get_exchange_diff(
get_ex_from_iq_data(data),
exchange_now
)
) + '\n' + get_edited_signature()
if query.message:
bot.edit_message_text(
text,
query.message.chat.id,
query.message.message_id,
reply_markup=get_update_keyboard(exchange_now),
parse_mode='HTML'
)
elif query.inline_message_id:
bot.edit_message_text(
text,
inline_message_id=query.inline_message_id,
reply_markup=get_update_keyboard(exchange_now),
parse_mode='HTML'
)
Как это работает?
- Загрузить текущий обменный курс ( exchange_now = pb.get_exchange (data ['c'] ).
- Сгенерируйте текст для нового сообщения, сериализуя текущий обменный курс с параметром diff , который вы получите с помощью новых методов (о них я напишу ниже). Также добавим подпись редактирования - get_edited_signature .
- Вызовите метод edit_message_text, если исходное сообщение является обычным. Если это ответ на встроенный запрос, передайте другие параметры.
Метод get_ex_from_iq_data анализирует JSON из callback_data :
def get_ex_from_iq_data(exc_json):
return {
'buy': exc_json['b'],
'sale': exc_json['s']
}
Метод get_exchange_diff получает старый и текущий обменные курсы и возвращает разницу в формате {'buy_diff': <float> , 'sale_diff': <float> }:
def get_exchange_diff(last, now):
return {
'sale_diff': float("%.6f" % (float(now['sale']) - float(last['sale']))),
'buy_diff': float("%.6f" % (float(now['buy']) - float(last['buy'])))
}
get_edited_signature генерирует текст «update…» :
def get_edited_signature():
return '<i>Updated ' + \
str(datetime.datetime.now(P_TIMEZONE).strftime('%H:%M:%S')) + \
' (' + TIMEZONE_COMMON_NAME + ')</i>'
Вот как выглядит сообщение при обновлении, если курсы валют не изменились:
А вот как это выглядит при изменении курса валют:
Шаг № 9: Реализуйте встроенный режим
Внедрение inline означает, что написание имени бота @ + в любом чате активирует поиск по введенному тексту и предложит результаты. Щелкнув по одному из них, бот отправит результат от вашего имени (с пометкой «через бота»).
@bot.inline_handler(func=lambda query: True)
def query_text(inline_query):
bot.answer_inline_query(
inline_query.id,
get_iq_articles(pb.get_exchanges(inline_query.query))
)
Вуаля, вы реализовали встроенный обработчик запросов.
Библиотека передаст объект InlineQuery в функцию query_text . Внутри вы используете функцию answer_inline_query, которая должна получать inline_query_id и массив объектов (результаты поиска).
Давайте воспользуемся get_exchanges из pb.py для поиска нескольких валют, соответствующих поисковому запросу. Давайте передадим этот массив в метод get_iq_articles, который вернет массив из InlineQueryResultArticle :
def get_iq_articles(exchanges):
result = []
for exc in exchanges:
result.append(
telebot.types.InlineQueryResultArticle(
id=exc['ccy'],
title=exc['ccy'],
input_message_content=telebot.types.InputTextMessageContent(
serialize_ex(exc),
parse_mode='HTML'
),
reply_markup=get_update_keyboard(exc),
description='Convert ' + exc['base_ccy'] + ' -> ' + exc['ccy'],
thumb_height=1
)
)
return result
Теперь, когда вы введете @exchnagetestbot + пробел в любом чате, вы увидите следующее:
Введите usd и результат будет мгновенно отфильтрован:
Щелкнем по предложенному результату:
Кнопка «Обновить» также работает:
Хорошая работа! Вы успешно реализовали встроенный режим!
Подведение итогов
Поздравляю! Теперь вы знаете, как сделать чат-бота на Python для Telegram. , реализовать встроенную клавиатуру, обновление сообщений и встроенный режим.