Начиная с Telegram API 4.0 добавлена поддержка того, что они называют Telegram Passport. Это позволяет разработчику бота получать личную информацию, такую как документы, удостоверяющие личность, номер телефона, адрес электронной почты и многое другое, в безопасном зашифрованном виде. Этот материал научит большей части того, что нужно знать, чтобы начать работу с Telegram Passport в качестве разработчика ботов.
С версии 11.0.0 в
python-telegram-bot
добавлена поддержка Telegram Passport
Если получено предупреждение от бота о том, что закрытый ключ не настроен, то убедитесь, а нужно ли вообще использовать Telegram Passport. Если ключ не настроен, то обновления, отправленные боту с личными данными, просто игнорируются.
Прежде чем продолжить, посмотрите следующую статью, описывающую Telegram Passport с точки зрения пользователя: "Знакомство с Telegram Passport"
Telegram Passport требует ключи шифрования, чтобы данные передавались безопасно. Дополнительную информацию об асимметричном шифровании можно найти в Википедии.
Затем убедитесь, что установлен openssl
, введя в консоли команду ниже:
$ openssl version # OpenSSL 3.0.2 15 Mar 2022 (Library: OpenSSL 3.0.2 15 Mar 2022)
Если ваш вывод не соответствует приведенному выше (обратите внимание, что более новая или более старая версия говорит, ЧТО в порядке), то необходимо установить openssl
. Простой поиск в Google/Yandex для установки openssl
на [вашу операционную систему] должен показать, как это сделать.
Теперь можно сгенерировать свой закрытый ключ.
$ openssl rsa -in private.key -pubout # -----BEGIN PUBLIC KEY----- # MIIBIjANBgkqhkiG9w0B # [snip] # KwIDAQAB # -----END PUBLIC KEY-----
Этот ключ понадобится для двух вещей. Первый - зарегистрировать его в @BotFather (следующий шаг). Другой - для вызова Telegram Passport API со своего веб-сайта.
Затем необходимо вставить открытый ключ в чат с @BotFather
, который был сгенерирован, после отправки ему команды /setpublickey
.
На этом этапе также нужно добавить политику конфиденциальности для своего бота, если ее еще нет. Это можно сделать с помощью команды /setprivacypolicy
. Примечание: эта команда ожидает URL-адрес, поэтому нужно будет разместить политику конфиденциальности где-нибудь в Интернете.
Теперь необходимо настроить кнопку, которую ваши пользователи будут нажимать, чтобы иметь возможность войти/зарегистрироваться в Telegram. Команда Telegram написала SDK (комплекты для разработки программного обеспечения), чтобы помочь быстро приступить к работе. В этом материале будет использован только Javascript SDK, но инструкции легко адаптировать к приложению iOS/macOS или Android.
Для начала понадобится простая веб-страница, на которой есть доступ к исходному коду HTML или аналогичному. Затем нужно будет включить Javascript SDK и вызвать функцию javascript Telegram.Passport.createAuthButton
.
Если нужно быстро приступить к работе, то ниже представлен пример HTML-страницы, которую можно просто загрузить, отредактировать с помощью редактора, а затем открыть в предпочитаемом вами браузере. (Обратите внимание, что нужно будет загрузить фактический файл SDK и поместить его в ту же папку, что и файл HTML)
Затем необходимо заполнить идентификатор вашего бота bot_id
(цифровая часть перед: в токене бота), область действия scope
(какие данные необходимо запросить), открытый ключ public_key
(осторожно с новыми строками), одноразовые данные nonce
и URL-адрес обратного вызова callback_url
(страница входа на сайт).
Telegram.Passport.createAuthButton('telegram_passport_auth', { bot_id: 1234567890, // КАКИЕ ДАННЫЕ НЕОБХОДИМО ПОЛУЧИТЬ scope: { data: [{ type: 'id_document', selfie: true }, 'address_document', 'phone_number', 'email'], v: 1 }, // ПУБЛИЧНЫЙ КЛЮЧ ЗАРЕГИСТРИРОВАННЫЙ в @BotFather. public_key: '-----BEGIN PUBLIC KEY-----\n', // ВАШ БОТ ПОЛУЧИТ ЭТИ ДАННЫЕ ВМЕСТЕ С ЗАПРОСОМ nonce: 'thisisatest', // TELEGRAM ОТПРАВИТ ВАШЕГО ПОЛЬЗОВАТЕЛЯ ОБРАТНО НА ЭТОТ URL callback_url: 'https://example.org' });
Примечание: В целях безопасности необходимо генерировать случайный одноразовый номер
nonce
для каждого пользователя, который посещает ваш сайт, и ВСЕГДА проверять его с вашим ботом, когда получаем паспортные/личные данные. Если на сайте есть серверная часть Python, то может пригодиться что-то вродеitsdangerous
- в противном случае другие методы подписи HMAC также должны быть безопасными.Примечание. Для простого тестирования можно использовать
https://example.org
в качествеcallback_url
и это нормально, но на реальных сайтах он должен быть установлен на URL-адрес, по которому пользователи будут уведомлены об успешном входе в систему - после того конечно, как бот проверит паспортные/личные данные.Примечание. В приведенном выше примере
scope
запрашивает документ (например, паспорт, водительские права и т. д.), который включает в себя фото и адрес, а также его номер телефона и электронная почта. Более подробно об использовании этого параметра смотрите документацию поscope
.
MessageHandler
, который принимает элементы PassportData
.Теперь необходимо добавить в диспетчер - обработчик MessageHandler
, чтобы можно было получать элементы объекта Message
, а PassportData
будет присутствовать как атрибут этого объекта (passport_data
). Если нужно, чтобы обработчик сообщений получал только Telegram Passport (рекомендуется), то необходимо использовать фильтр filter.PASSPORT_DATA
.
Подробнее смотрите ниже "Пример скрипта бота passportbot.py
".
Последний шаг - попробовать авторизироваться на сайте через синюю кнопку "Войти через Telegram". После настройки пароля и загрузки соответствующих документов можно будет увидеть данные, напечатанные в консоли.
<!DOCTYPE html> <html lang="en"> <head> <title>Telegram passport test!</title> <meta charset="utf-8"> <meta content="IE=edge" http-equiv="X-UA-Compatible"> <meta content="width=device-width, initial-scale=1" name="viewport"> </head> <body> <h1>Telegram passport test</h1> <div id="telegram_passport_auth"></div> </body> <!--- Нужен файл из https://github.com/TelegramMessenger/TGPassportJsSDK ---> <script src="telegram-passport.js"></script> <script> "use strict"; Telegram.Passport.createAuthButton('telegram_passport_auth', { // ИДЕНТИФИКАТОР БОТА bot_id: 1234567890, // КАКИЕ ДАННЫЕ НУЖНО ПОЛУЧИТЬ scope: { data: [{ type: 'id_document', selfie: true }, 'address_document', 'phone_number', 'email'], v: 1 }, // ВАШ ОТКРЫТЫЙ КЛЮЧ public_key: '-----BEGIN PUBLIC KEY-----\n', // ВАШ БОТ ПОЛУЧИТ ЭТИ ДАННЫЕ ВМЕСТЕ С ЗАПРОСОМ nonce: 'thisisatest', // TELEGRAM ОТПРАВИТ ВАШЕГО ПОЛЬЗОВАТЕЛЯ ОБРАТНО НА ЭТОТ URL callback_url: 'https://example.org' }); </script> </html>
passportbot.py
.Этот скрипт просто расшифрует и распечатает все полученные данные Telegram Passport. Он также загрузит все найденные PassportFiles
в текущий каталог. Чтобы начать работу, замените TOKEN
на токен вашего бота и поместите свой private.key
в тот же каталог, что и скрипт.
## passportbot.py import logging from pathlib import Path from telegram import __version__ as TG_VER try: from telegram import __version_info__ except ImportError: __version_info__ = (0, 0, 0, 0, 0) # type: ignore[assignment] if __version_info__ < (20, 0, 0, "alpha", 5): raise RuntimeError( f"Этот пример несовместим с текущей версией PTB. {TG_VER}." ) from telegram import Update from telegram.ext import Application, ContextTypes, MessageHandler, filters # Включить ведение журнала logging.basicConfig( format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", level=logging.INFO ) logger = logging.getLogger(__name__) async def msg(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: """Загружает и распечатывает полученные паспортные данные.""" # Извлечение данных passport_data = update.message.passport_data # Если `nonce` не соответствует тому, что сгенерировано серверной частью, # то это обновление исходит не от нас. В идеале необходимо рандомизировать `nonce` на сервере. if passport_data.decrypted_credentials.nonce != "thisisatest": return # Распечатать расшифрованные данные # Файлы будут загружены в текущий каталог for data in passport_data.decrypted_data: # здесь данные расшифровываются if data.type == "phone_number": print("Phone: ", data.phone_number) elif data.type == "email": print("Email: ", data.email) if data.type in ( "personal_details", "passport", "driver_license", "identity_card", "internal_passport", "address", ): print(data.type, data.data) if data.type in ( "utility_bill", "bank_statement", "rental_agreement", "passport_registration", "temporary_registration", ): print(data.type, len(data.files), "files") for file in data.files: actual_file = await file.get_file() print(actual_file) await actual_file.download_to_drive() if ( data.type in ("passport", "driver_license", "identity_card", "internal_passport") and data.front_side ): front_file = await data.front_side.get_file() print(data.type, front_file) await front_file.download_to_drive() if data.type in ("driver_license" and "identity_card") and data.reverse_side: reverse_file = await data.reverse_side.get_file() print(data.type, reverse_file) await reverse_file.download_to_drive() if ( data.type in ("passport", "driver_license", "identity_card", "internal_passport") and data.selfie ): selfie_file = await data.selfie.get_file() print(data.type, selfie_file) await selfie_file.download_to_drive() if data.translation and data.type in ( "passport", "driver_license", "identity_card", "internal_passport", "utility_bill", "bank_statement", "rental_agreement", "passport_registration", "temporary_registration", ): print(data.type, len(data.translation), "translation") for file in data.translation: actual_file = await file.get_file() print(actual_file) await actual_file.download_to_drive() def main() -> None: """Запуск бота.""" # Создание приложения (передайте ему свой токен и закрытый ключ) private_key = Path("private.key") application = ( Application.builder().token("TOKEN").private_key(private_key.read_bytes()).build() ) # В сообщениях, содержащих личные данные, нужно вызвать функцию `msg()` application.add_handler(MessageHandler(filters.PASSPORT_DATA, msg)) application.run_polling() if __name__ == "__main__": main()