tg bot tutorial
This commit is contained in:
parent
1e3e193d50
commit
a5e919a9b2
649
content/posts/2023/python/howto-make-telegram-bot.md
Normal file
649
content/posts/2023/python/howto-make-telegram-bot.md
Normal file
@ -0,0 +1,649 @@
|
||||
---
|
||||
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::**
|
||||
|
||||
Отличная работа! Встроенный режим работает!
|
Loading…
Reference in New Issue
Block a user