650 lines
29 KiB
Markdown
650 lines
29 KiB
Markdown
---
|
||
title: "🤖 Howto Make Telegram Bot"
|
||
date: 2023-05-28T04:02:05+03:00
|
||
draft: true
|
||
tags: [python, tutorial, telegram, bot]
|
||
---
|
||
|
||
# Как создать чат-бота для Telegram с помощью Python
|
||
|
||
> Статья является полной копией страницы
|
||
https://pythonru.com/primery/python-telegram-bot.
|
||
|
||
Это пошаговое руководство по созданию бота для Telegram.
|
||
Бот будет показывать курсы валют, разницу между курсом раньше и сейчас,
|
||
а также использовать современные встроенные клавиатуры.
|
||
|
||
Время переходить к делу и узнать наконец, как создавать ботов в Telegram.
|
||
|
||
## Шаг № 0: немного теории об API Telegram-ботов
|
||
|
||
Начать руководство стоит с простого вопроса: как создавать чат-ботов в Telegram?
|
||
|
||
Ответ очень простой: для чтения сообщений отправленных пользователями
|
||
и для отправки сообщений назад используется API HTML. Это требует использования URL:
|
||
|
||
```text
|
||
https://api.telegram.org/bot/METHOD_NAME
|
||
```
|
||
|
||
`METHOD_NAME` — это метод, например, `getUpdates`, `sendMessage`, `getChat` и т.п.
|
||
|
||
Токен — уникальная строка из символов, которая нужна для того,
|
||
чтобы установить подлинность бота в системе.
|
||
Токен генерируется при создании бота.
|
||
|
||
Токен выглядит приблизительно так:
|
||
|
||
```text
|
||
123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11
|
||
```
|
||
|
||
Для выполнения запросов используются как GET, так и POST запросы.
|
||
Многие методы требуют дополнительных параметров
|
||
(методу `sendMessage`, например, нужно передать `chat_id` и `текст`).
|
||
Эти параметры могут быть переданы как строка запроса URL,
|
||
`application/x-www-form-urlencoded` и `application-json` (кроме загрузки файлов).
|
||
Еще одно требование — кодировка `UTF-8`.
|
||
|
||
После отправки запроса к API, вы получаете ответ в формате JSON.
|
||
Например, если извлечь данные с помощью метода `getME`, ответ будет такой:
|
||
|
||
```text
|
||
GET https://api.telegram.org/bot/getMe
|
||
```
|
||
|
||
```text
|
||
{
|
||
ok: true,
|
||
result: {
|
||
id: 231757398,
|
||
first_name: "Exchange Rate Bot",
|
||
username: "exchangetestbot"
|
||
}
|
||
}
|
||
```
|
||
|
||
Список всех типов данных и методов API Telegram-бота можно найти здесь
|
||
https://core.telegram.org/bots/api.
|
||
|
||
Следующий вопрос: как получать пользовательские сообщения?
|
||
|
||
Есть два варианта.
|
||
|
||
Первый — вручную создавать запросы с помощью метода `getUpdates`.
|
||
В качестве объекта вы получите массив объектов `Update`.
|
||
Этот метод работает как технология длинных опросов (long polling),
|
||
когда вы отправляете запрос, обрабатываете данные и начинаете повторяете процесс.
|
||
Чтобы избежать повторной обработки одних и тех же данных рекомендуется использовать параметр
|
||
`offset`.
|
||
|
||
Второй вариант — использовать `webhooks`.
|
||
Метод `setWebhook` нужно будет применить только один раз.
|
||
После этого Telegram будет отправлять все обновления на конкретный URL-адрес,
|
||
как только они появятся.
|
||
Единственное ограничение — необходим HTTPS, но можно использовать и сертификаты,
|
||
заверенные самостоятельно.
|
||
|
||
Как выбрать оптимальный метод? Метод `getUpdates` лучше всего подходит, если:
|
||
|
||
1. Вы не хотите или не можете настраивать HTTPS во время разработки.
|
||
2. Вы работаете со скриптовыми языками, которые сложно интегрировать в веб-сервер.
|
||
3. У бота высокая нагрузка.
|
||
4. Вы меняете сервер бота время от времени.
|
||
|
||
Метод с Webhook лучше подойдет в таких случаях:
|
||
|
||
1. Вы используете веб-языки (например, PHP).
|
||
2. У бота низкая нагрузка, и нет смысла делать запросы вручную.
|
||
3. Бот на постоянной основе интегрирован в веб-сервер.
|
||
4. В этом руководстве будет использоваться метод `getUpdates`.
|
||
|
||
Еще один вопрос: как создать зарегистрировать бота?
|
||
|
||
`@BotFather` используется для создания ботов в Telegram.
|
||
Он также отвечает за базовую настройку
|
||
(описание, фото профиля, встроенная поддержка и так далее).
|
||
|
||
В этом руководстве будет использоваться библиотека
|
||
[pyTelegramBotAPI](https://github.com/eternnoir/pyTelegramBotAPI).
|
||
|
||
## Шаг № 1: реализовать запросы курсов валют
|
||
|
||
Начать стоит с написания Python-скрипта,
|
||
который будет реализовывать логику конкретных запросов курсов валют.
|
||
Использовать будем `PrivatBank API`.
|
||
|
||
```text
|
||
https://api.privatbank.ua/p24api/pubinfo?json&exchange&coursid=5
|
||
```
|
||
|
||
Пример ответа:
|
||
|
||
```text
|
||
[
|
||
{
|
||
ccy:"USD",
|
||
base_ccy:"UAH",
|
||
buy:"25.90000",
|
||
sale:"26.25000"
|
||
},
|
||
{
|
||
ccy:"EUR",
|
||
base_ccy:"UAH",
|
||
buy:"29.10000",
|
||
sale:"29.85000"
|
||
},
|
||
{
|
||
ccy:"RUR",
|
||
base_ccy:"UAH",
|
||
buy:"0.37800",
|
||
sale:"0.41800"
|
||
},
|
||
{
|
||
ccy:"BTC",
|
||
base_ccy:"USD",
|
||
buy:"11220.0384",
|
||
sale:"12401.0950"
|
||
}
|
||
]
|
||
```
|
||
|
||
Создадим файл `pb.py` со следующим кодом:
|
||
|
||
```python
|
||
import re
|
||
import requests
|
||
import json
|
||
|
||
|
||
URL = 'https://api.privatbank.ua/p24api/pubinfo?json&exchange&coursid=5'
|
||
|
||
|
||
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
|
||
```
|
||
|
||
Были реализованы три метода:
|
||
|
||
1. `load_exchange`: загружает курсы валют по указанному URL-адресу
|
||
и возвращает их в формате словаря(dict).
|
||
2. `get_exchange`: возвращает курсы валют по запрошенной валюте.
|
||
3. `get_exchanges`: возвращает список валют в соответствии с шаблоном
|
||
(требуется для поиска валют во встроенных запросах).
|
||
|
||
## Шаг № 2: создать Telegram-бота с помощью `@BotFather`
|
||
|
||
Необходимо подключиться к боту `@BotFather`, чтобы получить список чат-команд в Telegram.
|
||
Далее нужно набрать команду `/newbot` для инструкций выбора название и имени бота.
|
||
После успешного создания бота вы получите следующее сообщение:
|
||
|
||
```text
|
||
Done! Congratulations on your new bot. You will find it at telegram.me/.
|
||
You can now add a description, about section and profile picture for your bot, see /help for a list of commands.
|
||
By the way, when you've finished creating your cool bot, ping our Bot Support if you want a better username for it.
|
||
Just make sure the bot is fully operational before you do this.
|
||
|
||
Use this token to access the HTTP API:
|
||
(here goes the bot’s token)
|
||
|
||
For a description of the Bot API, see this page: https://core.telegram.org/bots/api
|
||
```
|
||
|
||
Его нужно сразу настроить.
|
||
Необходимо добавить описание и текст о боте
|
||
(команды `/setdescription` и `/setabouttext`),
|
||
фото профиля (`/setuserpic`), включить встроенный режим (`/setinline`),
|
||
добавить описания команд (`/setcommands`).
|
||
Потребуется использовать две команды: `/help` и `/exchange`. Стоит описать их в `/setcommands`.
|
||
|
||
Теперь, когда настройка закончена, можно переходить к написанию кода.
|
||
Прежде чем двигаться дальше, рекомендуется почитать об
|
||
[API](https://core.telegram.org/bots/api#authorizing-your-bot)
|
||
и ознакомиться с документацией библиотеки, чтобы лучше понимать то, о чем пойдет речь дальше.
|
||
|
||
## Шаг № 3: настроить и запустить бота
|
||
|
||
Начнем с создания файла `config.py` для настройки:
|
||
|
||
```python
|
||
TOKEN = '' # заменить на токен своего бота
|
||
TIMEZONE = 'Europe/Moscow'
|
||
TIMEZONE_COMMON_NAME = 'Moscow'
|
||
```
|
||
|
||
В этом файле указаны: токен бота и часовой пояс,
|
||
в котором тот будет работать
|
||
(это понадобится в будущем для определения времени обновления сообщений.
|
||
API Telegram не позволяет видеть временную зону пользователя,
|
||
поэтому время обновления должно отображаться с подсказкой о часовом поясе).
|
||
|
||
Создадим файл `bot.py`. Нужно импортировать все необходимые библиотеки,
|
||
файлы с настройками и предварительно созданный `pb.py`.
|
||
Если каких-то библиотек не хватает, их можно установить с помощью `pip`.
|
||
|
||
```python
|
||
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`.
|
||
Для этого конструктору нужно передать токен:
|
||
|
||
```python
|
||
bot = telebot.TeleBot(config.TOKEN)
|
||
bot.polling(none_stop=True)
|
||
```
|
||
|
||
## Шаг № 4: написать обработчик команды `/start`
|
||
|
||
Теперь чат-бот на Python работает и постоянно посылает запросы с помощью метода `getUpdates`.
|
||
Параметр `none_stop` отвечает за то, чтобы запросы отправлялись,
|
||
даже если API возвращает ошибку при выполнении метода.
|
||
|
||
Из переменной бота возможно вызывать любые методы API Telegram-бота.
|
||
|
||
Начнем с написания обработчика команды `/start`
|
||
и добавим его перед строкой `bot.polling(none_stop=True)`:
|
||
|
||
```python
|
||
@bot.message_handler(commands=['start'])
|
||
def start_command(message):
|
||
bot.send_message(
|
||
message.chat.id,
|
||
'Greetings! I can show you exchange rates.\n'
|
||
'To get the exchange rates press /exchange.\n'
|
||
'To get help press /help.'
|
||
)
|
||
```
|
||
|
||
Как можно видеть, pyTelegramBotAPI использует декораторы Python
|
||
для запуска обработчиков разных команд Telegram.
|
||
Также можно перехватывать сообщения с помощью регулярных выражений,
|
||
узнавать тип содержимого в них и лямбда-функции.
|
||
|
||
В нашем случае если условие `commands=['start']` равно `True`,
|
||
тогда будет вызвана функция `start_command`.
|
||
Объект сообщения (десериализованный тип `Message`) будет передан функции.
|
||
После этого вы просто запускаете `send_message` в том же чате с конкретным сообщением.
|
||
|
||
Это было просто, не так ли?
|
||
|
||
## Шаг № 5: создать обработчик команды `/help`
|
||
|
||
Давайте оживим обработчик команды `/help`
|
||
с помощью встроенной кнопки со ссылкой на ваш аккаунт в Telegram.
|
||
Кнопку можно озаглавить **«Message the developer»**.
|
||
|
||
```python
|
||
@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) To receive a list of available currencies press /exchange.\n'
|
||
'2) Click on the currency you are interested in.\n'
|
||
'3) You will receive a message containing information regarding the source and the target currencies, '
|
||
'buying rates and selling rates.\n'
|
||
'4) Click “Update” to receive the current information regarding the request. '
|
||
'The bot will also show the difference between the previous and the current exchange rates.\n'
|
||
'5) The bot supports inline. Type @ in any chat and the first letters of a currency.',
|
||
reply_markup=keyboard
|
||
)
|
||
```
|
||
|
||
Как видно в примере выше, был использован дополнительный параметр
|
||
(`reply_markup`) для метода `send_message`.
|
||
Метод получил встроенную клавиатуру (`InlineKeyboardMarkup`)
|
||
с одной кнопкой (`InlineKeyboardButton`) и следующим текстом:
|
||
«Message the developer» и `url='telegram.me/artiomtb'`.
|
||
|
||
Результат выше выглядит вот так:
|
||
|
||
**::TODO::**
|
||
|
||
## Шаг № 6: добавить обработчик команды `/exchange`
|
||
|
||
Обработчик команды `/exchange` отображает меню выбора валюты
|
||
и встроенную клавиатуру с 3 кнопками: `USD`, `EUR` и `RUR`
|
||
(это валюты, поддерживаемые API банка).
|
||
|
||
```python
|
||
@bot.message_handler(commands=['exchange'])
|
||
def exchange_command(message):
|
||
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 currency of choice:',
|
||
reply_markup=keyboard
|
||
)
|
||
```
|
||
|
||
Вот как работает `InlineKeyboardButton`.
|
||
Когда пользователь нажимает на кнопку, вы получаете `CallbackQuery`
|
||
(в параметре `data` содержится `callback-data`) в `getUpdates`.
|
||
Таким образом вы знаете, какую именно кнопку нажал пользователь,
|
||
и как ее правильно обработать.
|
||
|
||
Вот как выглядит работа команды `/exchange`:
|
||
|
||
**::TODO::**
|
||
|
||
## Шаг № 7: написать обработчик для кнопок встроенной клавиатуры
|
||
|
||
В библиотеке pyTelegramBotAPI есть декоратор `@bot.callback_query_handler`,
|
||
который передает объект `CallbackQuery` во вложенную функцию.
|
||
|
||
```python
|
||
@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`:
|
||
|
||
```python
|
||
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`.
|
||
Ему нужно передать `Message` и код валюты (получить ее можно из `query.data`.
|
||
Если это, например, `get-USD`, передавайте `USD`).
|
||
|
||
Реализуем `send_exchange_result`:
|
||
|
||
```python
|
||
def send_exchange_result(message, ex_code):
|
||
bot.send_chat_action(message.chat.id, 'typing')
|
||
ex = pb.get_exchange(ex_code)
|
||
bot.send_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`
|
||
(который возвращает клавиатуре кнопки «Update« и «Share»).
|
||
|
||
```python
|
||
def get_update_keyboard(ex):
|
||
keyboard = telebot.types.InlineKeyboardMarkup()
|
||
keyboard.row(
|
||
telebot.types.InlineKeyboardButton(
|
||
'Update',
|
||
callback_data=json.dumps({
|
||
't': 'u',
|
||
'e': {
|
||
'b': ex['buy'],
|
||
's': ex['sale'],
|
||
'c': ex['ccy']
|
||
}
|
||
}).replace(' ', '')
|
||
),
|
||
telebot.types.InlineKeyboardButton('Share', switch_inline_query=ex['ccy'])
|
||
)
|
||
return keyboard
|
||
```
|
||
|
||
Запишем в `get_update_keyboard` текущий курс валют в `callback_data` в форме JSON.
|
||
JSON сжимается, потому что максимальный разрешенный размер файла равен 64 байтам.
|
||
|
||
Кнопка `t` значит тип, а `e` — обмен. Остальное выполнено по тому же принципу.
|
||
|
||
У кнопки **Share** есть параметр `switch_inline_query`.
|
||
После нажатия кнопки пользователю будет предложено выбрать один из чатов,
|
||
открыть этот чат и ввести имя бота и определенный запрос в поле ввода.
|
||
|
||
Методы `serialize_ex` и дополнительный `serialize_exchange_diff` нужны,
|
||
чтобы показывать разницу между текущим и старыми курсами валют после нажатия кнопки `Update`.
|
||
|
||
```python
|
||
def serialize_ex(ex_json, diff=None):
|
||
result = '' + ex_json['base_ccy'] + ' -> ' + ex_json['ccy'] + ':\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" data-mce-placeholder="1" data-wp-emoji="1" class="emoji" alt="↗️" src="https://s.w.org/images/core/emoji/2.3/svg/2197.svg">" src="https://s.w.org/images/core/emoji/2.3/svg/2197.svg">" src="https://s.w.org/images/core/emoji/2.3/svg/2197.svg">" src="https://s.w.org/images/core/emoji/72x72/2197.png">" src="https://s.w.org/images/core/emoji/72x72/2197.png">)'
|
||
elif diff < 0:
|
||
result = '(' + str(diff)[1:] + ' <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" data-mce-placeholder="1" data-wp-emoji="1" class="emoji" alt="↘️" src="https://s.w.org/images/core/emoji/2.3/svg/2198.svg">" src="https://s.w.org/images/core/emoji/2.3/svg/2198.svg">" src="https://s.w.org/images/core/emoji/2.3/svg/2198.svg">" src="https://s.w.org/images/core/emoji/72x72/2198.png">" src="https://s.w.org/images/core/emoji/72x72/2198.png">)'
|
||
return result
|
||
```
|
||
|
||
Как видно, метод `serialize_ex` получает необязательный параметр `diff`.
|
||
Ему будет передаваться разница между курсами обмена в формате
|
||
`{'buy_diff': ..., 'sale_diff': ...}`.
|
||
Это будет происходить во время сериализации после нажатия кнопки `Update`.
|
||
Когда курсы валют отображаются первый раз, он нам не нужен.
|
||
|
||
Вот как будет выглядеть результат выполнения после нажатия кнопки USD:
|
||
|
||
**::TODO::**
|
||
|
||
## Шаг № 8: реализовать обработчик кнопки обновления
|
||
|
||
Теперь можно создать обработчик кнопки `Update`.
|
||
После дополнения метода `iq_callback_method` он будет выглядеть следующим образом:
|
||
|
||
```python
|
||
@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`.
|
||
Реализуем это:
|
||
|
||
```python
|
||
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'
|
||
)
|
||
```
|
||
|
||
Как это работает? Очень просто:
|
||
|
||
1. Загружаем текущий курс валюты (`exchange_now = pb.get_exchange(data['c'])`).
|
||
2. Генерируем текст нового сообщения путем
|
||
сериализации текущего курса валют с параметром `diff`,
|
||
который можно получить с помощью новых методов (о них дальше).
|
||
Также нужно добавить подпись — `get_edited_signature`.
|
||
3. Вызываем метод `edit_message_text`,
|
||
если оригинальное сообщение не изменилось.
|
||
Если это ответ на встроенный запрос, передаем другие параметры.
|
||
|
||
Метод `get_ex_from_iq_data` разбирает JSON из `callback_data`:
|
||
|
||
```python
|
||
def get_ex_from_iq_data(exc_json):
|
||
return {
|
||
'buy': exc_json['b'],
|
||
'sale': exc_json['s']
|
||
}
|
||
```
|
||
|
||
Метод `get_exchange_diff` получает старое и текущее значение курсов валют
|
||
и возвращает разницу в формате `{'buy_diff': ..., 'sale_diff': ...}`:
|
||
|
||
```python
|
||
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` генерирует текст «Updated…»:
|
||
|
||
```python
|
||
def get_edited_signature():
|
||
return 'Updated ' + \
|
||
str(datetime.datetime.now(P_TIMEZONE).strftime('%H:%M:%S')) + \
|
||
' (' + TIMEZONE_COMMON_NAME + ')'
|
||
```
|
||
|
||
Вот как выглядит сообщение после обновления, если курсы валют не изменились:
|
||
|
||
**::TODO::**
|
||
|
||
И вот так — если изменились:
|
||
|
||
**::TODO::**
|
||
|
||
## Шаг № 9: реализовать встроенный режим
|
||
|
||
Реализация встроенного режима значит,
|
||
что если пользователь введет `@ + имя бота` в любом чате,
|
||
это активирует поиск введенного текста и выведет результаты.
|
||
После нажатия на один из них бот отправит результат от вашего имени (с пометкой «via bot»).
|
||
|
||
```python
|
||
@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_line`, которая должна получить `inline_query_id`
|
||
и массив объектов (результаты поиска).
|
||
|
||
Используем `get_exchanges` для поиска нескольких валют, подходящих под запрос.
|
||
Нужно передать этот массив методу `get_iq_articles`,
|
||
который вернет массив из `InlineQueryResultArticle`:
|
||
|
||
```python
|
||
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
|
||
```
|
||
|
||
Теперь при вводе `@exchangetestbost + пробел` вы увидите следующее:
|
||
|
||
**::TODO::**
|
||
|
||
Попробуем набрать `usd`, и результат мгновенно отфильтруется:
|
||
|
||
**::TODO::**
|
||
|
||
Проверим предложенный результат:
|
||
|
||
**::TODO::**
|
||
|
||
Кнопка «Update» тоже работает:
|
||
|
||
**::TODO::**
|
||
|
||
Отличная работа! Встроенный режим работает!
|