a begin that works
This commit is contained in:
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -0,0 +1,524 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2025
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
# pylint: disable=missing-module-docstring, redefined-builtin
|
||||
import json
|
||||
from base64 import b64decode
|
||||
from collections.abc import Sequence
|
||||
from typing import TYPE_CHECKING, Optional, no_type_check
|
||||
|
||||
try:
|
||||
from cryptography.hazmat.backends import default_backend
|
||||
from cryptography.hazmat.primitives.asymmetric.padding import MGF1, OAEP
|
||||
from cryptography.hazmat.primitives.ciphers import Cipher
|
||||
from cryptography.hazmat.primitives.ciphers.algorithms import AES
|
||||
from cryptography.hazmat.primitives.ciphers.modes import CBC
|
||||
from cryptography.hazmat.primitives.hashes import SHA1, SHA256, SHA512, Hash
|
||||
|
||||
CRYPTO_INSTALLED = True
|
||||
except ImportError:
|
||||
default_backend = None # type: ignore[assignment]
|
||||
MGF1, OAEP, Cipher, AES, CBC = (None, None, None, None, None) # type: ignore[misc,assignment]
|
||||
SHA1, SHA256, SHA512, Hash = (None, None, None, None) # type: ignore[misc,assignment]
|
||||
|
||||
CRYPTO_INSTALLED = False
|
||||
|
||||
from telegram._telegramobject import TelegramObject
|
||||
from telegram._utils.argumentparsing import de_json_optional, de_list_optional, parse_sequence_arg
|
||||
from telegram._utils.strings import TextEncoding
|
||||
from telegram._utils.types import JSONDict
|
||||
from telegram.error import PassportDecryptionError
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from telegram import Bot
|
||||
|
||||
|
||||
@no_type_check
|
||||
def decrypt(secret, hash, data):
|
||||
"""
|
||||
Decrypt per telegram docs at https://core.telegram.org/passport.
|
||||
|
||||
Args:
|
||||
secret (:obj:`str` or :obj:`bytes`): The encryption secret, either as bytes or as a
|
||||
base64 encoded string.
|
||||
hash (:obj:`str` or :obj:`bytes`): The hash, either as bytes or as a
|
||||
base64 encoded string.
|
||||
data (:obj:`str` or :obj:`bytes`): The data to decrypt, either as bytes or as a
|
||||
base64 encoded string.
|
||||
file (:obj:`bool`): Force data to be treated as raw data, instead of trying to
|
||||
b64decode it.
|
||||
|
||||
Raises:
|
||||
:class:`PassportDecryptionError`: Given hash does not match hash of decrypted data.
|
||||
|
||||
Returns:
|
||||
:obj:`bytes`: The decrypted data as bytes.
|
||||
|
||||
"""
|
||||
if not CRYPTO_INSTALLED:
|
||||
raise RuntimeError(
|
||||
"To use Telegram Passports, PTB must be installed via `pip install "
|
||||
'"python-telegram-bot[passport]"`.'
|
||||
)
|
||||
# Make a SHA512 hash of secret + update
|
||||
digest = Hash(SHA512(), backend=default_backend())
|
||||
digest.update(secret + hash)
|
||||
secret_hash_hash = digest.finalize()
|
||||
# First 32 chars is our key, next 16 is the initialisation vector
|
||||
key, init_vector = secret_hash_hash[:32], secret_hash_hash[32 : 32 + 16]
|
||||
# Init a AES-CBC cipher and decrypt the data
|
||||
cipher = Cipher(AES(key), CBC(init_vector), backend=default_backend())
|
||||
decryptor = cipher.decryptor()
|
||||
data = decryptor.update(data) + decryptor.finalize()
|
||||
# Calculate SHA256 hash of the decrypted data
|
||||
digest = Hash(SHA256(), backend=default_backend())
|
||||
digest.update(data)
|
||||
data_hash = digest.finalize()
|
||||
# If the newly calculated hash did not match the one telegram gave us
|
||||
if data_hash != hash:
|
||||
# Raise a error that is caught inside telegram.PassportData and transformed into a warning
|
||||
raise PassportDecryptionError(f"Hashes are not equal! {data_hash} != {hash}")
|
||||
# Return data without padding
|
||||
return data[data[0] :]
|
||||
|
||||
|
||||
@no_type_check
|
||||
def decrypt_json(secret, hash, data):
|
||||
"""Decrypts data using secret and hash and then decodes utf-8 string and loads json"""
|
||||
return json.loads(decrypt(secret, hash, data).decode(TextEncoding.UTF_8))
|
||||
|
||||
|
||||
class EncryptedCredentials(TelegramObject):
|
||||
"""Contains data required for decrypting and authenticating EncryptedPassportElement. See the
|
||||
Telegram Passport Documentation for a complete description of the data decryption and
|
||||
authentication processes.
|
||||
|
||||
Objects of this class are comparable in terms of equality. Two objects of this class are
|
||||
considered equal, if their :attr:`data`, :attr:`hash` and :attr:`secret` are equal.
|
||||
|
||||
Note:
|
||||
This object is decrypted only when originating from
|
||||
:attr:`telegram.PassportData.decrypted_credentials`.
|
||||
|
||||
Args:
|
||||
data (:class:`telegram.Credentials` | :obj:`str`): Decrypted data with unique user's
|
||||
nonce, data hashes and secrets used for EncryptedPassportElement decryption and
|
||||
authentication or base64 encrypted data.
|
||||
hash (:obj:`str`): Base64-encoded data hash for data authentication.
|
||||
secret (:obj:`str`): Decrypted or encrypted secret used for decryption.
|
||||
|
||||
Attributes:
|
||||
data (:class:`telegram.Credentials` | :obj:`str`): Decrypted data with unique user's
|
||||
nonce, data hashes and secrets used for EncryptedPassportElement decryption and
|
||||
authentication or base64 encrypted data.
|
||||
hash (:obj:`str`): Base64-encoded data hash for data authentication.
|
||||
secret (:obj:`str`): Decrypted or encrypted secret used for decryption.
|
||||
|
||||
"""
|
||||
|
||||
__slots__ = (
|
||||
"_decrypted_data",
|
||||
"_decrypted_secret",
|
||||
"data",
|
||||
"hash",
|
||||
"secret",
|
||||
)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
data: str,
|
||||
hash: str,
|
||||
secret: str,
|
||||
*,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
):
|
||||
super().__init__(api_kwargs=api_kwargs)
|
||||
# Required
|
||||
self.data: str = data
|
||||
self.hash: str = hash
|
||||
self.secret: str = secret
|
||||
|
||||
self._id_attrs = (self.data, self.hash, self.secret)
|
||||
|
||||
self._decrypted_secret: Optional[bytes] = None
|
||||
self._decrypted_data: Optional[Credentials] = None
|
||||
|
||||
self._freeze()
|
||||
|
||||
@property
|
||||
def decrypted_secret(self) -> bytes:
|
||||
"""
|
||||
:obj:`bytes`: Lazily decrypt and return secret.
|
||||
|
||||
Raises:
|
||||
telegram.error.PassportDecryptionError: Decryption failed. Usually due to bad
|
||||
private/public key but can also suggest malformed/tampered data.
|
||||
"""
|
||||
if self._decrypted_secret is None:
|
||||
if not CRYPTO_INSTALLED:
|
||||
raise RuntimeError(
|
||||
"To use Telegram Passports, PTB must be installed via `pip install "
|
||||
'"python-telegram-bot[passport]"`.'
|
||||
)
|
||||
# Try decrypting according to step 1 at
|
||||
# https://core.telegram.org/passport#decrypting-data
|
||||
# We make sure to base64 decode the secret first.
|
||||
# Telegram says to use OAEP padding so we do that. The Mask Generation Function
|
||||
# is the default for OAEP, the algorithm is the default for PHP which is what
|
||||
# Telegram's backend servers run.
|
||||
try:
|
||||
self._decrypted_secret = self.get_bot().private_key.decrypt( # type: ignore
|
||||
b64decode(self.secret),
|
||||
OAEP(mgf=MGF1(algorithm=SHA1()), algorithm=SHA1(), label=None), # skipcq
|
||||
)
|
||||
except ValueError as exception:
|
||||
# If decryption fails raise exception
|
||||
raise PassportDecryptionError(exception) from exception
|
||||
return self._decrypted_secret
|
||||
|
||||
@property
|
||||
def decrypted_data(self) -> "Credentials":
|
||||
"""
|
||||
:class:`telegram.Credentials`: Lazily decrypt and return credentials data. This object
|
||||
also contains the user specified nonce as
|
||||
`decrypted_data.nonce`.
|
||||
|
||||
Raises:
|
||||
telegram.error.PassportDecryptionError: Decryption failed. Usually due to bad
|
||||
private/public key but can also suggest malformed/tampered data.
|
||||
"""
|
||||
if self._decrypted_data is None:
|
||||
self._decrypted_data = Credentials.de_json(
|
||||
decrypt_json(self.decrypted_secret, b64decode(self.hash), b64decode(self.data)),
|
||||
self.get_bot(),
|
||||
)
|
||||
return self._decrypted_data
|
||||
|
||||
|
||||
class Credentials(TelegramObject):
|
||||
"""
|
||||
Attributes:
|
||||
secure_data (:class:`telegram.SecureData`): Credentials for encrypted data
|
||||
nonce (:obj:`str`): Bot-specified nonce
|
||||
"""
|
||||
|
||||
__slots__ = ("nonce", "secure_data")
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
secure_data: "SecureData",
|
||||
nonce: str,
|
||||
*,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
):
|
||||
super().__init__(api_kwargs=api_kwargs)
|
||||
# Required
|
||||
self.secure_data: SecureData = secure_data
|
||||
self.nonce: str = nonce
|
||||
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "Credentials":
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
data["secure_data"] = de_json_optional(data.get("secure_data"), SecureData, bot)
|
||||
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
|
||||
class SecureData(TelegramObject):
|
||||
"""
|
||||
This object represents the credentials that were used to decrypt the encrypted data.
|
||||
All fields are optional and depend on fields that were requested.
|
||||
|
||||
Args:
|
||||
personal_details (:class:`telegram.SecureValue`, optional): Credentials for encrypted
|
||||
personal details.
|
||||
passport (:class:`telegram.SecureValue`, optional): Credentials for encrypted passport.
|
||||
internal_passport (:class:`telegram.SecureValue`, optional): Credentials for encrypted
|
||||
internal passport.
|
||||
driver_license (:class:`telegram.SecureValue`, optional): Credentials for encrypted
|
||||
driver license.
|
||||
identity_card (:class:`telegram.SecureValue`, optional): Credentials for encrypted ID card
|
||||
address (:class:`telegram.SecureValue`, optional): Credentials for encrypted
|
||||
residential address.
|
||||
utility_bill (:class:`telegram.SecureValue`, optional): Credentials for encrypted
|
||||
utility bill.
|
||||
bank_statement (:class:`telegram.SecureValue`, optional): Credentials for encrypted
|
||||
bank statement.
|
||||
rental_agreement (:class:`telegram.SecureValue`, optional): Credentials for encrypted
|
||||
rental agreement.
|
||||
passport_registration (:class:`telegram.SecureValue`, optional): Credentials for encrypted
|
||||
registration from internal passport.
|
||||
temporary_registration (:class:`telegram.SecureValue`, optional): Credentials for encrypted
|
||||
temporary registration.
|
||||
|
||||
Attributes:
|
||||
personal_details (:class:`telegram.SecureValue`): Optional. Credentials for encrypted
|
||||
personal details.
|
||||
passport (:class:`telegram.SecureValue`): Optional. Credentials for encrypted passport.
|
||||
internal_passport (:class:`telegram.SecureValue`): Optional. Credentials for encrypted
|
||||
internal passport.
|
||||
driver_license (:class:`telegram.SecureValue`): Optional. Credentials for encrypted
|
||||
driver license.
|
||||
identity_card (:class:`telegram.SecureValue`): Optional. Credentials for encrypted ID card
|
||||
address (:class:`telegram.SecureValue`): Optional. Credentials for encrypted
|
||||
residential address.
|
||||
utility_bill (:class:`telegram.SecureValue`): Optional. Credentials for encrypted
|
||||
utility bill.
|
||||
bank_statement (:class:`telegram.SecureValue`): Optional. Credentials for encrypted
|
||||
bank statement.
|
||||
rental_agreement (:class:`telegram.SecureValue`): Optional. Credentials for encrypted
|
||||
rental agreement.
|
||||
passport_registration (:class:`telegram.SecureValue`): Optional. Credentials for encrypted
|
||||
registration from internal passport.
|
||||
temporary_registration (:class:`telegram.SecureValue`): Optional. Credentials for encrypted
|
||||
temporary registration.
|
||||
"""
|
||||
|
||||
__slots__ = (
|
||||
"address",
|
||||
"bank_statement",
|
||||
"driver_license",
|
||||
"identity_card",
|
||||
"internal_passport",
|
||||
"passport",
|
||||
"passport_registration",
|
||||
"personal_details",
|
||||
"rental_agreement",
|
||||
"temporary_registration",
|
||||
"utility_bill",
|
||||
)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
personal_details: Optional["SecureValue"] = None,
|
||||
passport: Optional["SecureValue"] = None,
|
||||
internal_passport: Optional["SecureValue"] = None,
|
||||
driver_license: Optional["SecureValue"] = None,
|
||||
identity_card: Optional["SecureValue"] = None,
|
||||
address: Optional["SecureValue"] = None,
|
||||
utility_bill: Optional["SecureValue"] = None,
|
||||
bank_statement: Optional["SecureValue"] = None,
|
||||
rental_agreement: Optional["SecureValue"] = None,
|
||||
passport_registration: Optional["SecureValue"] = None,
|
||||
temporary_registration: Optional["SecureValue"] = None,
|
||||
*,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
):
|
||||
super().__init__(api_kwargs=api_kwargs)
|
||||
|
||||
# Optionals
|
||||
self.temporary_registration: Optional[SecureValue] = temporary_registration
|
||||
self.passport_registration: Optional[SecureValue] = passport_registration
|
||||
self.rental_agreement: Optional[SecureValue] = rental_agreement
|
||||
self.bank_statement: Optional[SecureValue] = bank_statement
|
||||
self.utility_bill: Optional[SecureValue] = utility_bill
|
||||
self.address: Optional[SecureValue] = address
|
||||
self.identity_card: Optional[SecureValue] = identity_card
|
||||
self.driver_license: Optional[SecureValue] = driver_license
|
||||
self.internal_passport: Optional[SecureValue] = internal_passport
|
||||
self.passport: Optional[SecureValue] = passport
|
||||
self.personal_details: Optional[SecureValue] = personal_details
|
||||
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "SecureData":
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
data["temporary_registration"] = de_json_optional(
|
||||
data.get("temporary_registration"), SecureValue, bot
|
||||
)
|
||||
data["passport_registration"] = de_json_optional(
|
||||
data.get("passport_registration"), SecureValue, bot
|
||||
)
|
||||
data["rental_agreement"] = de_json_optional(data.get("rental_agreement"), SecureValue, bot)
|
||||
data["bank_statement"] = de_json_optional(data.get("bank_statement"), SecureValue, bot)
|
||||
data["utility_bill"] = de_json_optional(data.get("utility_bill"), SecureValue, bot)
|
||||
data["address"] = de_json_optional(data.get("address"), SecureValue, bot)
|
||||
data["identity_card"] = de_json_optional(data.get("identity_card"), SecureValue, bot)
|
||||
data["driver_license"] = de_json_optional(data.get("driver_license"), SecureValue, bot)
|
||||
data["internal_passport"] = de_json_optional(
|
||||
data.get("internal_passport"), SecureValue, bot
|
||||
)
|
||||
data["passport"] = de_json_optional(data.get("passport"), SecureValue, bot)
|
||||
data["personal_details"] = de_json_optional(data.get("personal_details"), SecureValue, bot)
|
||||
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
|
||||
class SecureValue(TelegramObject):
|
||||
"""
|
||||
This object represents the credentials that were used to decrypt the encrypted value.
|
||||
All fields are optional and depend on the type of field.
|
||||
|
||||
Args:
|
||||
data (:class:`telegram.DataCredentials`, optional): Credentials for encrypted Telegram
|
||||
Passport data. Available for "personal_details", "passport", "driver_license",
|
||||
"identity_card", "identity_passport" and "address" types.
|
||||
front_side (:class:`telegram.FileCredentials`, optional): Credentials for encrypted
|
||||
document's front side. Available for "passport", "driver_license", "identity_card"
|
||||
and "internal_passport".
|
||||
reverse_side (:class:`telegram.FileCredentials`, optional): Credentials for encrypted
|
||||
document's reverse side. Available for "driver_license" and "identity_card".
|
||||
selfie (:class:`telegram.FileCredentials`, optional): Credentials for encrypted selfie
|
||||
of the user with a document. Can be available for "passport", "driver_license",
|
||||
"identity_card" and "internal_passport".
|
||||
translation (list[:class:`telegram.FileCredentials`], optional): Credentials for an
|
||||
encrypted translation of the document. Available for "passport", "driver_license",
|
||||
"identity_card", "internal_passport", "utility_bill", "bank_statement",
|
||||
"rental_agreement", "passport_registration" and "temporary_registration".
|
||||
files (list[:class:`telegram.FileCredentials`], optional): Credentials for encrypted
|
||||
files. Available for "utility_bill", "bank_statement", "rental_agreement",
|
||||
"passport_registration" and "temporary_registration" types.
|
||||
|
||||
Attributes:
|
||||
data (:class:`telegram.DataCredentials`): Optional. Credentials for encrypted Telegram
|
||||
Passport data. Available for "personal_details", "passport", "driver_license",
|
||||
"identity_card", "identity_passport" and "address" types.
|
||||
front_side (:class:`telegram.FileCredentials`): Optional. Credentials for encrypted
|
||||
document's front side. Available for "passport", "driver_license", "identity_card"
|
||||
and "internal_passport".
|
||||
reverse_side (:class:`telegram.FileCredentials`): Optional. Credentials for encrypted
|
||||
document's reverse side. Available for "driver_license" and "identity_card".
|
||||
selfie (:class:`telegram.FileCredentials`): Optional. Credentials for encrypted selfie
|
||||
of the user with a document. Can be available for "passport", "driver_license",
|
||||
"identity_card" and "internal_passport".
|
||||
translation (tuple[:class:`telegram.FileCredentials`]): Optional. Credentials for an
|
||||
encrypted translation of the document. Available for "passport", "driver_license",
|
||||
"identity_card", "internal_passport", "utility_bill", "bank_statement",
|
||||
"rental_agreement", "passport_registration" and "temporary_registration".
|
||||
|
||||
.. versionchanged:: 20.0
|
||||
|tupleclassattrs|
|
||||
|
||||
files (tuple[:class:`telegram.FileCredentials`]): Optional. Credentials for encrypted
|
||||
files. Available for "utility_bill", "bank_statement", "rental_agreement",
|
||||
"passport_registration" and "temporary_registration" types.
|
||||
|
||||
.. versionchanged:: 20.0
|
||||
|
||||
* |tupleclassattrs|
|
||||
* |alwaystuple|
|
||||
|
||||
"""
|
||||
|
||||
__slots__ = ("data", "files", "front_side", "reverse_side", "selfie", "translation")
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
data: Optional["DataCredentials"] = None,
|
||||
front_side: Optional["FileCredentials"] = None,
|
||||
reverse_side: Optional["FileCredentials"] = None,
|
||||
selfie: Optional["FileCredentials"] = None,
|
||||
files: Optional[Sequence["FileCredentials"]] = None,
|
||||
translation: Optional[Sequence["FileCredentials"]] = None,
|
||||
*,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
):
|
||||
super().__init__(api_kwargs=api_kwargs)
|
||||
self.data: Optional[DataCredentials] = data
|
||||
self.front_side: Optional[FileCredentials] = front_side
|
||||
self.reverse_side: Optional[FileCredentials] = reverse_side
|
||||
self.selfie: Optional[FileCredentials] = selfie
|
||||
self.files: tuple[FileCredentials, ...] = parse_sequence_arg(files)
|
||||
self.translation: tuple[FileCredentials, ...] = parse_sequence_arg(translation)
|
||||
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "SecureValue":
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
data["data"] = de_json_optional(data.get("data"), DataCredentials, bot)
|
||||
data["front_side"] = de_json_optional(data.get("front_side"), FileCredentials, bot)
|
||||
data["reverse_side"] = de_json_optional(data.get("reverse_side"), FileCredentials, bot)
|
||||
data["selfie"] = de_json_optional(data.get("selfie"), FileCredentials, bot)
|
||||
data["files"] = de_list_optional(data.get("files"), FileCredentials, bot)
|
||||
data["translation"] = de_list_optional(data.get("translation"), FileCredentials, bot)
|
||||
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
|
||||
class _CredentialsBase(TelegramObject):
|
||||
"""Base class for DataCredentials and FileCredentials."""
|
||||
|
||||
__slots__ = ("data_hash", "file_hash", "hash", "secret")
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
hash: str,
|
||||
secret: str,
|
||||
*,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
):
|
||||
super().__init__(api_kwargs=api_kwargs)
|
||||
with self._unfrozen():
|
||||
self.hash: str = hash
|
||||
self.secret: str = secret
|
||||
|
||||
# Aliases just to be sure
|
||||
self.file_hash: str = self.hash
|
||||
self.data_hash: str = self.hash
|
||||
|
||||
|
||||
class DataCredentials(_CredentialsBase):
|
||||
"""
|
||||
These credentials can be used to decrypt encrypted data from the data field in
|
||||
EncryptedPassportData.
|
||||
|
||||
Args:
|
||||
data_hash (:obj:`str`): Checksum of encrypted data
|
||||
secret (:obj:`str`): Secret of encrypted data
|
||||
|
||||
Attributes:
|
||||
hash (:obj:`str`): Checksum of encrypted data
|
||||
secret (:obj:`str`): Secret of encrypted data
|
||||
"""
|
||||
|
||||
__slots__ = ()
|
||||
|
||||
def __init__(self, data_hash: str, secret: str, *, api_kwargs: Optional[JSONDict] = None):
|
||||
super().__init__(hash=data_hash, secret=secret, api_kwargs=api_kwargs)
|
||||
self._freeze()
|
||||
|
||||
|
||||
class FileCredentials(_CredentialsBase):
|
||||
"""
|
||||
These credentials can be used to decrypt encrypted files from the front_side,
|
||||
reverse_side, selfie and files fields in EncryptedPassportData.
|
||||
|
||||
Args:
|
||||
file_hash (:obj:`str`): Checksum of encrypted file
|
||||
secret (:obj:`str`): Secret of encrypted file
|
||||
|
||||
Attributes:
|
||||
hash (:obj:`str`): Checksum of encrypted file
|
||||
secret (:obj:`str`): Secret of encrypted file
|
||||
"""
|
||||
|
||||
__slots__ = ()
|
||||
|
||||
def __init__(self, file_hash: str, secret: str, *, api_kwargs: Optional[JSONDict] = None):
|
||||
super().__init__(hash=file_hash, secret=secret, api_kwargs=api_kwargs)
|
||||
self._freeze()
|
||||
@@ -0,0 +1,186 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2025
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
# pylint: disable=missing-module-docstring
|
||||
from typing import Optional
|
||||
|
||||
from telegram._telegramobject import TelegramObject
|
||||
from telegram._utils.types import JSONDict
|
||||
|
||||
|
||||
class PersonalDetails(TelegramObject):
|
||||
"""
|
||||
This object represents personal details.
|
||||
|
||||
Args:
|
||||
first_name (:obj:`str`): First Name.
|
||||
middle_name (:obj:`str`): Optional. First Name.
|
||||
last_name (:obj:`str`): Last Name.
|
||||
birth_date (:obj:`str`): Date of birth in DD.MM.YYYY format.
|
||||
gender (:obj:`str`): Gender, male or female.
|
||||
country_code (:obj:`str`): Citizenship (ISO 3166-1 alpha-2 country code).
|
||||
residence_country_code (:obj:`str`): Country of residence (ISO 3166-1 alpha-2 country
|
||||
code).
|
||||
first_name_native (:obj:`str`): First Name in the language of the user's country of
|
||||
residence.
|
||||
middle_name_native (:obj:`str`): Optional. Middle Name in the language of the user's
|
||||
country of residence.
|
||||
last_name_native (:obj:`str`): Last Name in the language of the user's country of
|
||||
residence.
|
||||
|
||||
Attributes:
|
||||
first_name (:obj:`str`): First Name.
|
||||
middle_name (:obj:`str`): Optional. First Name.
|
||||
last_name (:obj:`str`): Last Name.
|
||||
birth_date (:obj:`str`): Date of birth in DD.MM.YYYY format.
|
||||
gender (:obj:`str`): Gender, male or female.
|
||||
country_code (:obj:`str`): Citizenship (ISO 3166-1 alpha-2 country code).
|
||||
residence_country_code (:obj:`str`): Country of residence (ISO 3166-1 alpha-2 country
|
||||
code).
|
||||
first_name_native (:obj:`str`): First Name in the language of the user's country of
|
||||
residence.
|
||||
middle_name_native (:obj:`str`): Optional. Middle Name in the language of the user's
|
||||
country of residence.
|
||||
last_name_native (:obj:`str`): Last Name in the language of the user's country of
|
||||
residence.
|
||||
"""
|
||||
|
||||
__slots__ = (
|
||||
"birth_date",
|
||||
"country_code",
|
||||
"first_name",
|
||||
"first_name_native",
|
||||
"gender",
|
||||
"last_name",
|
||||
"last_name_native",
|
||||
"middle_name",
|
||||
"middle_name_native",
|
||||
"residence_country_code",
|
||||
)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
first_name: str,
|
||||
last_name: str,
|
||||
birth_date: str,
|
||||
gender: str,
|
||||
country_code: str,
|
||||
residence_country_code: str,
|
||||
first_name_native: Optional[str] = None,
|
||||
last_name_native: Optional[str] = None,
|
||||
middle_name: Optional[str] = None,
|
||||
middle_name_native: Optional[str] = None,
|
||||
*,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
):
|
||||
super().__init__(api_kwargs=api_kwargs)
|
||||
# Required
|
||||
self.first_name: str = first_name
|
||||
self.last_name: str = last_name
|
||||
self.middle_name: Optional[str] = middle_name
|
||||
self.birth_date: str = birth_date
|
||||
self.gender: str = gender
|
||||
self.country_code: str = country_code
|
||||
self.residence_country_code: str = residence_country_code
|
||||
self.first_name_native: Optional[str] = first_name_native
|
||||
self.last_name_native: Optional[str] = last_name_native
|
||||
self.middle_name_native: Optional[str] = middle_name_native
|
||||
|
||||
self._freeze()
|
||||
|
||||
|
||||
class ResidentialAddress(TelegramObject):
|
||||
"""
|
||||
This object represents a residential address.
|
||||
|
||||
Args:
|
||||
street_line1 (:obj:`str`): First line for the address.
|
||||
street_line2 (:obj:`str`): Optional. Second line for the address.
|
||||
city (:obj:`str`): City.
|
||||
state (:obj:`str`): Optional. State.
|
||||
country_code (:obj:`str`): ISO 3166-1 alpha-2 country code.
|
||||
post_code (:obj:`str`): Address post code.
|
||||
|
||||
Attributes:
|
||||
street_line1 (:obj:`str`): First line for the address.
|
||||
street_line2 (:obj:`str`): Optional. Second line for the address.
|
||||
city (:obj:`str`): City.
|
||||
state (:obj:`str`): Optional. State.
|
||||
country_code (:obj:`str`): ISO 3166-1 alpha-2 country code.
|
||||
post_code (:obj:`str`): Address post code.
|
||||
"""
|
||||
|
||||
__slots__ = (
|
||||
"city",
|
||||
"country_code",
|
||||
"post_code",
|
||||
"state",
|
||||
"street_line1",
|
||||
"street_line2",
|
||||
)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
street_line1: str,
|
||||
street_line2: str,
|
||||
city: str,
|
||||
state: str,
|
||||
country_code: str,
|
||||
post_code: str,
|
||||
*,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
):
|
||||
super().__init__(api_kwargs=api_kwargs)
|
||||
# Required
|
||||
self.street_line1: str = street_line1
|
||||
self.street_line2: str = street_line2
|
||||
self.city: str = city
|
||||
self.state: str = state
|
||||
self.country_code: str = country_code
|
||||
self.post_code: str = post_code
|
||||
|
||||
self._freeze()
|
||||
|
||||
|
||||
class IdDocumentData(TelegramObject):
|
||||
"""
|
||||
This object represents the data of an identity document.
|
||||
|
||||
Args:
|
||||
document_no (:obj:`str`): Document number.
|
||||
expiry_date (:obj:`str`): Optional. Date of expiry, in DD.MM.YYYY format.
|
||||
|
||||
Attributes:
|
||||
document_no (:obj:`str`): Document number.
|
||||
expiry_date (:obj:`str`): Optional. Date of expiry, in DD.MM.YYYY format.
|
||||
"""
|
||||
|
||||
__slots__ = ("document_no", "expiry_date")
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
document_no: str,
|
||||
expiry_date: str,
|
||||
*,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
):
|
||||
super().__init__(api_kwargs=api_kwargs)
|
||||
self.document_no: str = document_no
|
||||
self.expiry_date: str = expiry_date
|
||||
|
||||
self._freeze()
|
||||
@@ -0,0 +1,278 @@
|
||||
#!/usr/bin/env python
|
||||
# flake8: noqa: E501
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2025
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
"""This module contains an object that represents a Telegram EncryptedPassportElement."""
|
||||
from base64 import b64decode
|
||||
from collections.abc import Sequence
|
||||
from typing import TYPE_CHECKING, Optional, Union
|
||||
|
||||
from telegram._passport.credentials import decrypt_json
|
||||
from telegram._passport.data import IdDocumentData, PersonalDetails, ResidentialAddress
|
||||
from telegram._passport.passportfile import PassportFile
|
||||
from telegram._telegramobject import TelegramObject
|
||||
from telegram._utils.argumentparsing import (
|
||||
de_json_decrypted_optional,
|
||||
de_json_optional,
|
||||
de_list_decrypted_optional,
|
||||
de_list_optional,
|
||||
parse_sequence_arg,
|
||||
)
|
||||
from telegram._utils.types import JSONDict
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from telegram import Bot, Credentials
|
||||
|
||||
|
||||
class EncryptedPassportElement(TelegramObject):
|
||||
"""
|
||||
Contains information about documents or other Telegram Passport elements shared with the bot
|
||||
by the user. The data has been automatically decrypted by python-telegram-bot.
|
||||
|
||||
Objects of this class are comparable in terms of equality. Two objects of this class are
|
||||
considered equal, if their :attr:`type`, :attr:`data`, :attr:`phone_number`, :attr:`email`,
|
||||
:attr:`files`, :attr:`front_side`, :attr:`reverse_side` and :attr:`selfie` are equal.
|
||||
|
||||
Note:
|
||||
This object is decrypted only when originating from
|
||||
:attr:`telegram.PassportData.decrypted_data`.
|
||||
|
||||
Args:
|
||||
type (:obj:`str`): Element type. One of "personal_details", "passport", "driver_license",
|
||||
"identity_card", "internal_passport", "address", "utility_bill", "bank_statement",
|
||||
"rental_agreement", "passport_registration", "temporary_registration", "phone_number",
|
||||
"email".
|
||||
hash (:obj:`str`): Base64-encoded element hash for using in
|
||||
:class:`telegram.PassportElementErrorUnspecified`.
|
||||
data (:class:`telegram.PersonalDetails` | :class:`telegram.IdDocumentData` | \
|
||||
:class:`telegram.ResidentialAddress` | :obj:`str`, optional):
|
||||
Decrypted or encrypted data; available only for "personal_details", "passport",
|
||||
"driver_license", "identity_card", "internal_passport" and "address" types.
|
||||
phone_number (:obj:`str`, optional): User's verified phone number; available only for
|
||||
"phone_number" type.
|
||||
email (:obj:`str`, optional): User's verified email address; available only for "email"
|
||||
type.
|
||||
files (Sequence[:class:`telegram.PassportFile`], optional): Array of encrypted/decrypted
|
||||
files with documents provided by the user; available only for "utility_bill",
|
||||
"bank_statement", "rental_agreement", "passport_registration" and
|
||||
"temporary_registration" types.
|
||||
|
||||
.. versionchanged:: 20.0
|
||||
|sequenceclassargs|
|
||||
|
||||
front_side (:class:`telegram.PassportFile`, optional): Encrypted/decrypted file with the
|
||||
front side of the document, provided by the user; Available only for "passport",
|
||||
"driver_license", "identity_card" and "internal_passport".
|
||||
reverse_side (:class:`telegram.PassportFile`, optional): Encrypted/decrypted file with the
|
||||
reverse side of the document, provided by the user; Available only for
|
||||
"driver_license" and "identity_card".
|
||||
selfie (:class:`telegram.PassportFile`, optional): Encrypted/decrypted file with the
|
||||
selfie of the user holding a document, provided by the user; available if requested for
|
||||
"passport", "driver_license", "identity_card" and "internal_passport".
|
||||
translation (Sequence[:class:`telegram.PassportFile`], optional): Array of
|
||||
encrypted/decrypted files with translated versions of documents provided by the user;
|
||||
available if requested requested for "passport", "driver_license", "identity_card",
|
||||
"internal_passport", "utility_bill", "bank_statement", "rental_agreement",
|
||||
"passport_registration" and "temporary_registration" types.
|
||||
|
||||
.. versionchanged:: 20.0
|
||||
|sequenceclassargs|
|
||||
|
||||
Attributes:
|
||||
type (:obj:`str`): Element type. One of "personal_details", "passport", "driver_license",
|
||||
"identity_card", "internal_passport", "address", "utility_bill", "bank_statement",
|
||||
"rental_agreement", "passport_registration", "temporary_registration", "phone_number",
|
||||
"email".
|
||||
hash (:obj:`str`): Base64-encoded element hash for using in
|
||||
:class:`telegram.PassportElementErrorUnspecified`.
|
||||
data (:class:`telegram.PersonalDetails` | :class:`telegram.IdDocumentData` | \
|
||||
:class:`telegram.ResidentialAddress` | :obj:`str`):
|
||||
Optional. Decrypted or encrypted data; available only for "personal_details",
|
||||
"passport", "driver_license", "identity_card", "internal_passport" and "address" types.
|
||||
phone_number (:obj:`str`): Optional. User's verified phone number; available only for
|
||||
"phone_number" type.
|
||||
email (:obj:`str`): Optional. User's verified email address; available only for "email"
|
||||
type.
|
||||
files (tuple[:class:`telegram.PassportFile`]): Optional. Array of encrypted/decrypted
|
||||
files with documents provided by the user; available only for "utility_bill",
|
||||
"bank_statement", "rental_agreement", "passport_registration" and
|
||||
"temporary_registration" types.
|
||||
|
||||
.. versionchanged:: 20.0
|
||||
|
||||
* |tupleclassattrs|
|
||||
* |alwaystuple|
|
||||
|
||||
front_side (:class:`telegram.PassportFile`): Optional. Encrypted/decrypted file with the
|
||||
front side of the document, provided by the user; available only for "passport",
|
||||
"driver_license", "identity_card" and "internal_passport".
|
||||
reverse_side (:class:`telegram.PassportFile`): Optional. Encrypted/decrypted file with the
|
||||
reverse side of the document, provided by the user; available only for "driver_license"
|
||||
and "identity_card".
|
||||
selfie (:class:`telegram.PassportFile`): Optional. Encrypted/decrypted file with the
|
||||
selfie of the user holding a document, provided by the user; available if requested for
|
||||
"passport", "driver_license", "identity_card" and "internal_passport".
|
||||
translation (tuple[:class:`telegram.PassportFile`]): Optional. Array of
|
||||
encrypted/decrypted files with translated versions of documents provided by the user;
|
||||
available if requested for "passport", "driver_license", "identity_card",
|
||||
"internal_passport", "utility_bill", "bank_statement", "rental_agreement",
|
||||
"passport_registration" and "temporary_registration" types.
|
||||
|
||||
.. versionchanged:: 20.0
|
||||
|
||||
* |tupleclassattrs|
|
||||
* |alwaystuple|
|
||||
|
||||
"""
|
||||
|
||||
__slots__ = (
|
||||
"data",
|
||||
"email",
|
||||
"files",
|
||||
"front_side",
|
||||
"hash",
|
||||
"phone_number",
|
||||
"reverse_side",
|
||||
"selfie",
|
||||
"translation",
|
||||
"type",
|
||||
)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
type: str, # pylint: disable=redefined-builtin
|
||||
hash: str, # pylint: disable=redefined-builtin
|
||||
data: Optional[Union[PersonalDetails, IdDocumentData, ResidentialAddress]] = None,
|
||||
phone_number: Optional[str] = None,
|
||||
email: Optional[str] = None,
|
||||
files: Optional[Sequence[PassportFile]] = None,
|
||||
front_side: Optional[PassportFile] = None,
|
||||
reverse_side: Optional[PassportFile] = None,
|
||||
selfie: Optional[PassportFile] = None,
|
||||
translation: Optional[Sequence[PassportFile]] = None,
|
||||
*,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
):
|
||||
super().__init__(api_kwargs=api_kwargs)
|
||||
|
||||
# Required
|
||||
self.type: str = type
|
||||
# Optionals
|
||||
self.data: Optional[Union[PersonalDetails, IdDocumentData, ResidentialAddress]] = data
|
||||
self.phone_number: Optional[str] = phone_number
|
||||
self.email: Optional[str] = email
|
||||
self.files: tuple[PassportFile, ...] = parse_sequence_arg(files)
|
||||
self.front_side: Optional[PassportFile] = front_side
|
||||
self.reverse_side: Optional[PassportFile] = reverse_side
|
||||
self.selfie: Optional[PassportFile] = selfie
|
||||
self.translation: tuple[PassportFile, ...] = parse_sequence_arg(translation)
|
||||
self.hash: str = hash
|
||||
|
||||
self._id_attrs = (
|
||||
self.type,
|
||||
self.data,
|
||||
self.phone_number,
|
||||
self.email,
|
||||
self.files,
|
||||
self.front_side,
|
||||
self.reverse_side,
|
||||
self.selfie,
|
||||
)
|
||||
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "EncryptedPassportElement":
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
data["files"] = de_list_optional(data.get("files"), PassportFile, bot) or None
|
||||
data["front_side"] = de_json_optional(data.get("front_side"), PassportFile, bot)
|
||||
data["reverse_side"] = de_json_optional(data.get("reverse_side"), PassportFile, bot)
|
||||
data["selfie"] = de_json_optional(data.get("selfie"), PassportFile, bot)
|
||||
data["translation"] = de_list_optional(data.get("translation"), PassportFile, bot) or None
|
||||
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
@classmethod
|
||||
def de_json_decrypted(
|
||||
cls, data: JSONDict, bot: Optional["Bot"], credentials: "Credentials"
|
||||
) -> "EncryptedPassportElement":
|
||||
"""Variant of :meth:`telegram.TelegramObject.de_json` that also takes into account
|
||||
passport credentials.
|
||||
|
||||
Args:
|
||||
data (dict[:obj:`str`, ...]): The JSON data.
|
||||
bot (:class:`telegram.Bot` | :obj:`None`): The bot associated with these object.
|
||||
May be :obj:`None`, in which case shortcut methods will not be available.
|
||||
|
||||
.. versionchanged:: 21.4
|
||||
:paramref:`bot` is now optional and defaults to :obj:`None`
|
||||
|
||||
.. deprecated:: 21.4
|
||||
This argument will be converted to an optional argument in future versions.
|
||||
credentials (:class:`telegram.FileCredentials`): The credentials
|
||||
|
||||
Returns:
|
||||
:class:`telegram.EncryptedPassportElement`:
|
||||
|
||||
"""
|
||||
|
||||
if data["type"] not in ("phone_number", "email"):
|
||||
secure_data = getattr(credentials.secure_data, data["type"])
|
||||
|
||||
if secure_data.data is not None:
|
||||
# If not already decrypted
|
||||
if not isinstance(data["data"], dict):
|
||||
data["data"] = decrypt_json(
|
||||
b64decode(secure_data.data.secret),
|
||||
b64decode(secure_data.data.hash),
|
||||
b64decode(data["data"]),
|
||||
)
|
||||
if data["type"] == "personal_details":
|
||||
data["data"] = PersonalDetails.de_json(data["data"], bot=bot)
|
||||
elif data["type"] in (
|
||||
"passport",
|
||||
"internal_passport",
|
||||
"driver_license",
|
||||
"identity_card",
|
||||
):
|
||||
data["data"] = IdDocumentData.de_json(data["data"], bot=bot)
|
||||
elif data["type"] == "address":
|
||||
data["data"] = ResidentialAddress.de_json(data["data"], bot=bot)
|
||||
|
||||
data["files"] = (
|
||||
de_list_decrypted_optional(data.get("files"), PassportFile, bot, secure_data.files)
|
||||
or None
|
||||
)
|
||||
data["front_side"] = de_json_decrypted_optional(
|
||||
data.get("front_side"), PassportFile, bot, secure_data.front_side
|
||||
)
|
||||
data["reverse_side"] = de_json_decrypted_optional(
|
||||
data.get("reverse_side"), PassportFile, bot, secure_data.reverse_side
|
||||
)
|
||||
data["selfie"] = de_json_decrypted_optional(
|
||||
data.get("selfie"), PassportFile, bot, secure_data.selfie
|
||||
)
|
||||
data["translation"] = (
|
||||
de_list_decrypted_optional(
|
||||
data.get("translation"), PassportFile, bot, secure_data.translation
|
||||
)
|
||||
or None
|
||||
)
|
||||
|
||||
return super().de_json(data=data, bot=bot)
|
||||
@@ -0,0 +1,127 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2025
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
"""Contains information about Telegram Passport data shared with the bot by the user."""
|
||||
from collections.abc import Sequence
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
|
||||
from telegram._passport.credentials import EncryptedCredentials
|
||||
from telegram._passport.encryptedpassportelement import EncryptedPassportElement
|
||||
from telegram._telegramobject import TelegramObject
|
||||
from telegram._utils.argumentparsing import de_json_optional, de_list_optional, parse_sequence_arg
|
||||
from telegram._utils.types import JSONDict
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from telegram import Bot, Credentials
|
||||
|
||||
|
||||
class PassportData(TelegramObject):
|
||||
"""Contains information about Telegram Passport data shared with the bot by the user.
|
||||
|
||||
Note:
|
||||
To be able to decrypt this object, you must pass your ``private_key`` to either
|
||||
:class:`telegram.ext.Updater` or :class:`telegram.Bot`. Decrypted data is then found in
|
||||
:attr:`decrypted_data` and the payload can be found in :attr:`decrypted_credentials`'s
|
||||
attribute :attr:`telegram.Credentials.nonce`.
|
||||
|
||||
Args:
|
||||
data (Sequence[:class:`telegram.EncryptedPassportElement`]): Array with encrypted
|
||||
information about documents and other Telegram Passport elements that was shared with
|
||||
the bot.
|
||||
|
||||
.. versionchanged:: 20.0
|
||||
|sequenceclassargs|
|
||||
|
||||
credentials (:class:`telegram.EncryptedCredentials`)): Encrypted credentials.
|
||||
|
||||
Attributes:
|
||||
data (tuple[:class:`telegram.EncryptedPassportElement`]): Array with encrypted
|
||||
information about documents and other Telegram Passport elements that was shared with
|
||||
the bot.
|
||||
|
||||
.. versionchanged:: 20.0
|
||||
|tupleclassattrs|
|
||||
|
||||
credentials (:class:`telegram.EncryptedCredentials`): Encrypted credentials.
|
||||
|
||||
|
||||
"""
|
||||
|
||||
__slots__ = ("_decrypted_data", "credentials", "data")
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
data: Sequence[EncryptedPassportElement],
|
||||
credentials: EncryptedCredentials,
|
||||
*,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
):
|
||||
super().__init__(api_kwargs=api_kwargs)
|
||||
|
||||
self.data: tuple[EncryptedPassportElement, ...] = parse_sequence_arg(data)
|
||||
self.credentials: EncryptedCredentials = credentials
|
||||
|
||||
self._decrypted_data: Optional[tuple[EncryptedPassportElement]] = None
|
||||
self._id_attrs = tuple([x.type for x in data] + [credentials.hash])
|
||||
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "PassportData":
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
data["data"] = de_list_optional(data.get("data"), EncryptedPassportElement, bot)
|
||||
data["credentials"] = de_json_optional(data.get("credentials"), EncryptedCredentials, bot)
|
||||
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
@property
|
||||
def decrypted_data(self) -> tuple[EncryptedPassportElement, ...]:
|
||||
"""
|
||||
tuple[:class:`telegram.EncryptedPassportElement`]: Lazily decrypt and return information
|
||||
about documents and other Telegram Passport elements which were shared with the bot.
|
||||
|
||||
.. versionchanged:: 20.0
|
||||
Returns a tuple instead of a list.
|
||||
|
||||
Raises:
|
||||
telegram.error.PassportDecryptionError: Decryption failed. Usually due to bad
|
||||
private/public key but can also suggest malformed/tampered data.
|
||||
"""
|
||||
if self._decrypted_data is None:
|
||||
self._decrypted_data = tuple( # type: ignore[assignment]
|
||||
EncryptedPassportElement.de_json_decrypted(
|
||||
element.to_dict(), self.get_bot(), self.decrypted_credentials
|
||||
)
|
||||
for element in self.data
|
||||
)
|
||||
return self._decrypted_data # type: ignore[return-value]
|
||||
|
||||
@property
|
||||
def decrypted_credentials(self) -> "Credentials":
|
||||
"""
|
||||
:class:`telegram.Credentials`: Lazily decrypt and return credentials that were used
|
||||
to decrypt the data. This object also contains the user specified payload as
|
||||
`decrypted_data.payload`.
|
||||
|
||||
Raises:
|
||||
telegram.error.PassportDecryptionError: Decryption failed. Usually due to bad
|
||||
private/public key but can also suggest malformed/tampered data.
|
||||
"""
|
||||
return self.credentials.decrypted_data
|
||||
@@ -0,0 +1,441 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2025
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
# pylint: disable=redefined-builtin
|
||||
"""This module contains the classes that represent Telegram PassportElementError."""
|
||||
|
||||
from collections.abc import Sequence
|
||||
from typing import Optional
|
||||
|
||||
from telegram._telegramobject import TelegramObject
|
||||
from telegram._utils.argumentparsing import parse_sequence_arg
|
||||
from telegram._utils.types import JSONDict
|
||||
|
||||
|
||||
class PassportElementError(TelegramObject):
|
||||
"""Baseclass for the PassportElementError* classes.
|
||||
|
||||
This object represents an error in the Telegram Passport element which was submitted that
|
||||
should be resolved by the user.
|
||||
|
||||
Objects of this class are comparable in terms of equality. Two objects of this class are
|
||||
considered equal, if their :attr:`source` and :attr:`type` are equal.
|
||||
|
||||
Args:
|
||||
source (:obj:`str`): Error source.
|
||||
type (:obj:`str`): The section of the user's Telegram Passport which has the error.
|
||||
message (:obj:`str`): Error message.
|
||||
|
||||
Attributes:
|
||||
source (:obj:`str`): Error source.
|
||||
type (:obj:`str`): The section of the user's Telegram Passport which has the error.
|
||||
message (:obj:`str`): Error message.
|
||||
|
||||
"""
|
||||
|
||||
__slots__ = ("message", "source", "type")
|
||||
|
||||
def __init__(
|
||||
self, source: str, type: str, message: str, *, api_kwargs: Optional[JSONDict] = None
|
||||
):
|
||||
super().__init__(api_kwargs=api_kwargs)
|
||||
# Required
|
||||
self.source: str = str(source)
|
||||
self.type: str = str(type)
|
||||
self.message: str = str(message)
|
||||
|
||||
self._id_attrs = (self.source, self.type)
|
||||
|
||||
self._freeze()
|
||||
|
||||
|
||||
class PassportElementErrorDataField(PassportElementError):
|
||||
"""
|
||||
Represents an issue in one of the data fields that was provided by the user. The error is
|
||||
considered resolved when the field's value changes.
|
||||
|
||||
Objects of this class are comparable in terms of equality. Two objects of this class are
|
||||
considered equal, if their :attr:`~telegram.PassportElementError.source`, :attr:`type`,
|
||||
:attr:`field_name`, :attr:`data_hash` and :attr:`message` are equal.
|
||||
|
||||
Args:
|
||||
type (:obj:`str`): The section of the user's Telegram Passport which has the error, one of
|
||||
``"personal_details"``, ``"passport"``, ``"driver_license"``, ``"identity_card"``,
|
||||
``"internal_passport"``, ``"address"``.
|
||||
field_name (:obj:`str`): Name of the data field which has the error.
|
||||
data_hash (:obj:`str`): Base64-encoded data hash.
|
||||
message (:obj:`str`): Error message.
|
||||
|
||||
Attributes:
|
||||
type (:obj:`str`): The section of the user's Telegram Passport which has the error, one of
|
||||
``"personal_details"``, ``"passport"``, ``"driver_license"``, ``"identity_card"``,
|
||||
``"internal_passport"``, ``"address"``.
|
||||
field_name (:obj:`str`): Name of the data field which has the error.
|
||||
data_hash (:obj:`str`): Base64-encoded data hash.
|
||||
message (:obj:`str`): Error message.
|
||||
|
||||
"""
|
||||
|
||||
__slots__ = ("data_hash", "field_name")
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
type: str,
|
||||
field_name: str,
|
||||
data_hash: str,
|
||||
message: str,
|
||||
*,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
):
|
||||
# Required
|
||||
super().__init__("data", type, message, api_kwargs=api_kwargs)
|
||||
with self._unfrozen():
|
||||
self.field_name: str = field_name
|
||||
self.data_hash: str = data_hash
|
||||
|
||||
self._id_attrs = (
|
||||
self.source,
|
||||
self.type,
|
||||
self.field_name,
|
||||
self.data_hash,
|
||||
self.message,
|
||||
)
|
||||
|
||||
|
||||
class PassportElementErrorFile(PassportElementError):
|
||||
"""
|
||||
Represents an issue with a document scan. The error is considered resolved when the file with
|
||||
the document scan changes.
|
||||
|
||||
Objects of this class are comparable in terms of equality. Two objects of this class are
|
||||
considered equal, if their :attr:`~telegram.PassportElementError.source`, :attr:`type`,
|
||||
:attr:`file_hash`, and :attr:`message` are equal.
|
||||
|
||||
Args:
|
||||
type (:obj:`str`): The section of the user's Telegram Passport which has the issue, one of
|
||||
``"utility_bill"``, ``"bank_statement"``, ``"rental_agreement"``,
|
||||
``"passport_registration"``, ``"temporary_registration"``.
|
||||
file_hash (:obj:`str`): Base64-encoded file hash.
|
||||
message (:obj:`str`): Error message.
|
||||
|
||||
Attributes:
|
||||
type (:obj:`str`): The section of the user's Telegram Passport which has the issue, one of
|
||||
``"utility_bill"``, ``"bank_statement"``, ``"rental_agreement"``,
|
||||
``"passport_registration"``, ``"temporary_registration"``.
|
||||
file_hash (:obj:`str`): Base64-encoded file hash.
|
||||
message (:obj:`str`): Error message.
|
||||
|
||||
"""
|
||||
|
||||
__slots__ = ("file_hash",)
|
||||
|
||||
def __init__(
|
||||
self, type: str, file_hash: str, message: str, *, api_kwargs: Optional[JSONDict] = None
|
||||
):
|
||||
# Required
|
||||
super().__init__("file", type, message, api_kwargs=api_kwargs)
|
||||
with self._unfrozen():
|
||||
self.file_hash: str = file_hash
|
||||
|
||||
self._id_attrs = (self.source, self.type, self.file_hash, self.message)
|
||||
|
||||
|
||||
class PassportElementErrorFiles(PassportElementError):
|
||||
"""
|
||||
Represents an issue with a list of scans. The error is considered resolved when the list of
|
||||
files with the document scans changes.
|
||||
|
||||
Objects of this class are comparable in terms of equality. Two objects of this class are
|
||||
considered equal, if their :attr:`~telegram.PassportElementError.source`, :attr:`type`,
|
||||
:attr:`file_hashes`, and :attr:`message` are equal.
|
||||
|
||||
Args:
|
||||
type (:obj:`str`): The section of the user's Telegram Passport which has the issue, one of
|
||||
``"utility_bill"``, ``"bank_statement"``, ``"rental_agreement"``,
|
||||
``"passport_registration"``, ``"temporary_registration"``.
|
||||
file_hashes (Sequence[:obj:`str`]): List of base64-encoded file hashes.
|
||||
|
||||
.. versionchanged:: 22.0
|
||||
|sequenceargs|
|
||||
message (:obj:`str`): Error message.
|
||||
|
||||
Attributes:
|
||||
type (:obj:`str`): The section of the user's Telegram Passport which has the issue, one of
|
||||
``"utility_bill"``, ``"bank_statement"``, ``"rental_agreement"``,
|
||||
``"passport_registration"``, ``"temporary_registration"``.
|
||||
file_hashes (tuple[:obj:`str`]): List of base64-encoded file hashes.
|
||||
|
||||
.. versionchanged:: 22.0
|
||||
|tupleclassattrs|
|
||||
message (:obj:`str`): Error message.
|
||||
|
||||
"""
|
||||
|
||||
__slots__ = ("file_hashes",)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
type: str,
|
||||
file_hashes: Sequence[str],
|
||||
message: str,
|
||||
*,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
):
|
||||
# Required
|
||||
super().__init__("files", type, message, api_kwargs=api_kwargs)
|
||||
with self._unfrozen():
|
||||
self.file_hashes: tuple[str, ...] = parse_sequence_arg(file_hashes)
|
||||
|
||||
self._id_attrs = (self.source, self.type, self.message, self.file_hashes)
|
||||
|
||||
|
||||
class PassportElementErrorFrontSide(PassportElementError):
|
||||
"""
|
||||
Represents an issue with the front side of a document. The error is considered resolved when
|
||||
the file with the front side of the document changes.
|
||||
|
||||
Objects of this class are comparable in terms of equality. Two objects of this class are
|
||||
considered equal, if their :attr:`~telegram.PassportElementError.source`, :attr:`type`,
|
||||
:attr:`file_hash`, and :attr:`message` are equal.
|
||||
|
||||
Args:
|
||||
type (:obj:`str`): The section of the user's Telegram Passport which has the issue, one of
|
||||
``"passport"``, ``"driver_license"``, ``"identity_card"``, ``"internal_passport"``.
|
||||
file_hash (:obj:`str`): Base64-encoded hash of the file with the front side of the
|
||||
document.
|
||||
message (:obj:`str`): Error message.
|
||||
|
||||
Attributes:
|
||||
type (:obj:`str`): The section of the user's Telegram Passport which has the issue, one of
|
||||
``"passport"``, ``"driver_license"``, ``"identity_card"``, ``"internal_passport"``.
|
||||
file_hash (:obj:`str`): Base64-encoded hash of the file with the front side of the
|
||||
document.
|
||||
message (:obj:`str`): Error message.
|
||||
|
||||
"""
|
||||
|
||||
__slots__ = ("file_hash",)
|
||||
|
||||
def __init__(
|
||||
self, type: str, file_hash: str, message: str, *, api_kwargs: Optional[JSONDict] = None
|
||||
):
|
||||
# Required
|
||||
super().__init__("front_side", type, message, api_kwargs=api_kwargs)
|
||||
with self._unfrozen():
|
||||
self.file_hash: str = file_hash
|
||||
|
||||
self._id_attrs = (self.source, self.type, self.file_hash, self.message)
|
||||
|
||||
|
||||
class PassportElementErrorReverseSide(PassportElementError):
|
||||
"""
|
||||
Represents an issue with the reverse side of a document. The error is considered resolved when
|
||||
the file with the reverse side of the document changes.
|
||||
|
||||
Objects of this class are comparable in terms of equality. Two objects of this class are
|
||||
considered equal, if their :attr:`~telegram.PassportElementError.source`, :attr:`type`,
|
||||
:attr:`file_hash`, and :attr:`message` are equal.
|
||||
|
||||
Args:
|
||||
type (:obj:`str`): The section of the user's Telegram Passport which has the issue, one of
|
||||
``"driver_license"``, ``"identity_card"``.
|
||||
file_hash (:obj:`str`): Base64-encoded hash of the file with the reverse side of the
|
||||
document.
|
||||
message (:obj:`str`): Error message.
|
||||
|
||||
Attributes:
|
||||
type (:obj:`str`): The section of the user's Telegram Passport which has the issue, one of
|
||||
``"driver_license"``, ``"identity_card"``.
|
||||
file_hash (:obj:`str`): Base64-encoded hash of the file with the reverse side of the
|
||||
document.
|
||||
message (:obj:`str`): Error message.
|
||||
|
||||
"""
|
||||
|
||||
__slots__ = ("file_hash",)
|
||||
|
||||
def __init__(
|
||||
self, type: str, file_hash: str, message: str, *, api_kwargs: Optional[JSONDict] = None
|
||||
):
|
||||
# Required
|
||||
super().__init__("reverse_side", type, message, api_kwargs=api_kwargs)
|
||||
with self._unfrozen():
|
||||
self.file_hash: str = file_hash
|
||||
|
||||
self._id_attrs = (self.source, self.type, self.file_hash, self.message)
|
||||
|
||||
|
||||
class PassportElementErrorSelfie(PassportElementError):
|
||||
"""
|
||||
Represents an issue with the selfie with a document. The error is considered resolved when
|
||||
the file with the selfie changes.
|
||||
|
||||
Objects of this class are comparable in terms of equality. Two objects of this class are
|
||||
considered equal, if their :attr:`~telegram.PassportElementError.source`, :attr:`type`,
|
||||
:attr:`file_hash`, and :attr:`message` are equal.
|
||||
|
||||
Args:
|
||||
type (:obj:`str`): The section of the user's Telegram Passport which has the issue, one of
|
||||
``"passport"``, ``"driver_license"``, ``"identity_card"``, ``"internal_passport"``.
|
||||
file_hash (:obj:`str`): Base64-encoded hash of the file with the selfie.
|
||||
message (:obj:`str`): Error message.
|
||||
|
||||
Attributes:
|
||||
type (:obj:`str`): The section of the user's Telegram Passport which has the issue, one of
|
||||
``"passport"``, ``"driver_license"``, ``"identity_card"``, ``"internal_passport"``.
|
||||
file_hash (:obj:`str`): Base64-encoded hash of the file with the selfie.
|
||||
message (:obj:`str`): Error message.
|
||||
|
||||
"""
|
||||
|
||||
__slots__ = ("file_hash",)
|
||||
|
||||
def __init__(
|
||||
self, type: str, file_hash: str, message: str, *, api_kwargs: Optional[JSONDict] = None
|
||||
):
|
||||
# Required
|
||||
super().__init__("selfie", type, message, api_kwargs=api_kwargs)
|
||||
with self._unfrozen():
|
||||
self.file_hash: str = file_hash
|
||||
|
||||
self._id_attrs = (self.source, self.type, self.file_hash, self.message)
|
||||
|
||||
|
||||
class PassportElementErrorTranslationFile(PassportElementError):
|
||||
"""
|
||||
Represents an issue with one of the files that constitute the translation of a document.
|
||||
The error is considered resolved when the file changes.
|
||||
|
||||
Objects of this class are comparable in terms of equality. Two objects of this class are
|
||||
considered equal, if their :attr:`~telegram.PassportElementError.source`, :attr:`type`,
|
||||
:attr:`file_hash`, and :attr:`message` are equal.
|
||||
|
||||
Args:
|
||||
type (:obj:`str`): Type of element of the user's Telegram Passport which has the issue,
|
||||
one of ``"passport"``, ``"driver_license"``, ``"identity_card"``,
|
||||
``"internal_passport"``, ``"utility_bill"``, ``"bank_statement"``,
|
||||
``"rental_agreement"``, ``"passport_registration"``, ``"temporary_registration"``.
|
||||
file_hash (:obj:`str`): Base64-encoded hash of the file.
|
||||
message (:obj:`str`): Error message.
|
||||
|
||||
Attributes:
|
||||
type (:obj:`str`): Type of element of the user's Telegram Passport which has the issue,
|
||||
one of ``"passport"``, ``"driver_license"``, ``"identity_card"``,
|
||||
``"internal_passport"``, ``"utility_bill"``, ``"bank_statement"``,
|
||||
``"rental_agreement"``, ``"passport_registration"``, ``"temporary_registration"``.
|
||||
file_hash (:obj:`str`): Base64-encoded hash of the file.
|
||||
message (:obj:`str`): Error message.
|
||||
|
||||
"""
|
||||
|
||||
__slots__ = ("file_hash",)
|
||||
|
||||
def __init__(
|
||||
self, type: str, file_hash: str, message: str, *, api_kwargs: Optional[JSONDict] = None
|
||||
):
|
||||
# Required
|
||||
super().__init__("translation_file", type, message, api_kwargs=api_kwargs)
|
||||
with self._unfrozen():
|
||||
self.file_hash: str = file_hash
|
||||
|
||||
self._id_attrs = (self.source, self.type, self.file_hash, self.message)
|
||||
|
||||
|
||||
class PassportElementErrorTranslationFiles(PassportElementError):
|
||||
"""
|
||||
Represents an issue with the translated version of a document. The error is considered
|
||||
resolved when a file with the document translation changes.
|
||||
|
||||
Objects of this class are comparable in terms of equality. Two objects of this class are
|
||||
considered equal, if their :attr:`~telegram.PassportElementError.source`, :attr:`type`,
|
||||
:attr:`file_hashes`, and :attr:`message` are equal.
|
||||
|
||||
Args:
|
||||
type (:obj:`str`): Type of element of the user's Telegram Passport which has the issue,
|
||||
one of ``"passport"``, ``"driver_license"``, ``"identity_card"``,
|
||||
``"internal_passport"``, ``"utility_bill"``, ``"bank_statement"``,
|
||||
``"rental_agreement"``, ``"passport_registration"``, ``"temporary_registration"``.
|
||||
file_hashes (Sequence[:obj:`str`]): List of base64-encoded file hashes.
|
||||
|
||||
.. versionchanged:: 22.0
|
||||
|sequenceargs|
|
||||
message (:obj:`str`): Error message.
|
||||
|
||||
Attributes:
|
||||
type (:obj:`str`): Type of element of the user's Telegram Passport which has the issue,
|
||||
one of ``"passport"``, ``"driver_license"``, ``"identity_card"``,
|
||||
``"internal_passport"``, ``"utility_bill"``, ``"bank_statement"``,
|
||||
``"rental_agreement"``, ``"passport_registration"``, ``"temporary_registration"``.
|
||||
file_hashes (tuple[:obj:`str`]): List of base64-encoded file hashes.
|
||||
|
||||
.. versionchanged:: 22.0
|
||||
|tupleclassattrs|
|
||||
message (:obj:`str`): Error message.
|
||||
|
||||
"""
|
||||
|
||||
__slots__ = ("file_hashes",)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
type: str,
|
||||
file_hashes: Sequence[str],
|
||||
message: str,
|
||||
*,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
):
|
||||
# Required
|
||||
super().__init__("translation_files", type, message, api_kwargs=api_kwargs)
|
||||
with self._unfrozen():
|
||||
self.file_hashes: tuple[str, ...] = parse_sequence_arg(file_hashes)
|
||||
|
||||
self._id_attrs = (self.source, self.type, self.message, self.file_hashes)
|
||||
|
||||
|
||||
class PassportElementErrorUnspecified(PassportElementError):
|
||||
"""
|
||||
Represents an issue in an unspecified place. The error is considered resolved when new
|
||||
data is added.
|
||||
|
||||
Objects of this class are comparable in terms of equality. Two objects of this class are
|
||||
considered equal, if their :attr:`~telegram.PassportElementError.source`, :attr:`type`,
|
||||
:attr:`element_hash`, and :attr:`message` are equal.
|
||||
|
||||
Args:
|
||||
type (:obj:`str`): Type of element of the user's Telegram Passport which has the issue.
|
||||
element_hash (:obj:`str`): Base64-encoded element hash.
|
||||
message (:obj:`str`): Error message.
|
||||
|
||||
Attributes:
|
||||
type (:obj:`str`): Type of element of the user's Telegram Passport which has the issue.
|
||||
element_hash (:obj:`str`): Base64-encoded element hash.
|
||||
message (:obj:`str`): Error message.
|
||||
|
||||
"""
|
||||
|
||||
__slots__ = ("element_hash",)
|
||||
|
||||
def __init__(
|
||||
self, type: str, element_hash: str, message: str, *, api_kwargs: Optional[JSONDict] = None
|
||||
):
|
||||
# Required
|
||||
super().__init__("unspecified", type, message, api_kwargs=api_kwargs)
|
||||
with self._unfrozen():
|
||||
self.element_hash: str = element_hash
|
||||
|
||||
self._id_attrs = (self.source, self.type, self.element_hash, self.message)
|
||||
@@ -0,0 +1,213 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2025
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
"""This module contains an object that represents a Encrypted PassportFile."""
|
||||
|
||||
import datetime as dtm
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
|
||||
from telegram._telegramobject import TelegramObject
|
||||
from telegram._utils.datetime import extract_tzinfo_from_defaults, from_timestamp
|
||||
from telegram._utils.defaultvalue import DEFAULT_NONE
|
||||
from telegram._utils.types import JSONDict, ODVInput
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from telegram import Bot, File, FileCredentials
|
||||
|
||||
|
||||
class PassportFile(TelegramObject):
|
||||
"""
|
||||
This object represents a file uploaded to Telegram Passport. Currently all Telegram Passport
|
||||
files are in JPEG format when decrypted and don't exceed 10MB.
|
||||
|
||||
Objects of this class are comparable in terms of equality. Two objects of this class are
|
||||
considered equal, if their :attr:`file_unique_id` is equal.
|
||||
|
||||
Args:
|
||||
file_id (:obj:`str`): Identifier for this file, which can be used to download
|
||||
or reuse the file.
|
||||
file_unique_id (:obj:`str`): Unique identifier for this file, which
|
||||
is supposed to be the same over time and for different bots.
|
||||
Can't be used to download or reuse the file.
|
||||
file_size (:obj:`int`): File size in bytes.
|
||||
file_date (:class:`datetime.datetime`): Time when the file was uploaded.
|
||||
|
||||
.. versionchanged:: 22.0
|
||||
Accepts only :class:`datetime.datetime` instead of :obj:`int`.
|
||||
|datetime_localization|
|
||||
|
||||
Attributes:
|
||||
file_id (:obj:`str`): Identifier for this file, which can be used to download
|
||||
or reuse the file.
|
||||
file_unique_id (:obj:`str`): Unique identifier for this file, which
|
||||
is supposed to be the same over time and for different bots.
|
||||
Can't be used to download or reuse the file.
|
||||
file_size (:obj:`int`): File size in bytes.
|
||||
file_date (:class:`datetime.datetime`): Time when the file was uploaded.
|
||||
|
||||
.. versionchanged:: 22.0
|
||||
Returns :class:`datetime.datetime` instead of :obj:`int`.
|
||||
|datetime_localization|
|
||||
"""
|
||||
|
||||
__slots__ = (
|
||||
"_credentials",
|
||||
"file_date",
|
||||
"file_id",
|
||||
"file_size",
|
||||
"file_unique_id",
|
||||
)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
file_id: str,
|
||||
file_unique_id: str,
|
||||
file_date: dtm.datetime,
|
||||
file_size: int,
|
||||
credentials: Optional["FileCredentials"] = None,
|
||||
*,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
):
|
||||
super().__init__(api_kwargs=api_kwargs)
|
||||
|
||||
# Required
|
||||
self.file_id: str = file_id
|
||||
self.file_unique_id: str = file_unique_id
|
||||
self.file_size: int = file_size
|
||||
self.file_date: dtm.datetime = file_date
|
||||
# Optionals
|
||||
|
||||
self._credentials: Optional[FileCredentials] = credentials
|
||||
|
||||
self._id_attrs = (self.file_unique_id,)
|
||||
|
||||
self._freeze()
|
||||
|
||||
@classmethod
|
||||
def de_json(cls, data: JSONDict, bot: Optional["Bot"] = None) -> "PassportFile":
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
# Get the local timezone from the bot if it has defaults
|
||||
loc_tzinfo = extract_tzinfo_from_defaults(bot)
|
||||
data["file_date"] = from_timestamp(data.get("file_date"), tzinfo=loc_tzinfo)
|
||||
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
@classmethod
|
||||
def de_json_decrypted(
|
||||
cls, data: JSONDict, bot: Optional["Bot"], credentials: "FileCredentials"
|
||||
) -> "PassportFile":
|
||||
"""Variant of :meth:`telegram.TelegramObject.de_json` that also takes into account
|
||||
passport credentials.
|
||||
|
||||
Args:
|
||||
data (dict[:obj:`str`, ...]): The JSON data.
|
||||
bot (:class:`telegram.Bot` | :obj:`None`): The bot associated with these object.
|
||||
May be :obj:`None`, in which case shortcut methods will not be available.
|
||||
|
||||
.. versionchanged:: 21.4
|
||||
:paramref:`bot` is now optional and defaults to :obj:`None`
|
||||
|
||||
.. deprecated:: 21.4
|
||||
This argument will be converted to an optional argument in future versions.
|
||||
credentials (:class:`telegram.FileCredentials`): The credentials
|
||||
|
||||
Returns:
|
||||
:class:`telegram.PassportFile`:
|
||||
|
||||
"""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
data["credentials"] = credentials
|
||||
|
||||
return super().de_json(data=data, bot=bot)
|
||||
|
||||
@classmethod
|
||||
def de_list_decrypted(
|
||||
cls,
|
||||
data: list[JSONDict],
|
||||
bot: Optional["Bot"],
|
||||
credentials: list["FileCredentials"],
|
||||
) -> tuple["PassportFile", ...]:
|
||||
"""Variant of :meth:`telegram.TelegramObject.de_list` that also takes into account
|
||||
passport credentials.
|
||||
|
||||
.. versionchanged:: 20.0
|
||||
|
||||
* Returns a tuple instead of a list.
|
||||
* Filters out any :obj:`None` values
|
||||
|
||||
Args:
|
||||
data (list[dict[:obj:`str`, ...]]): The JSON data.
|
||||
bot (:class:`telegram.Bot` | :obj:`None`): The bot associated with these object.
|
||||
May be :obj:`None`, in which case shortcut methods will not be available.
|
||||
|
||||
.. versionchanged:: 21.4
|
||||
:paramref:`bot` is now optional and defaults to :obj:`None`
|
||||
|
||||
.. deprecated:: 21.4
|
||||
This argument will be converted to an optional argument in future versions.
|
||||
credentials (:class:`telegram.FileCredentials`): The credentials
|
||||
|
||||
Returns:
|
||||
tuple[:class:`telegram.PassportFile`]:
|
||||
|
||||
"""
|
||||
return tuple(
|
||||
obj
|
||||
for obj in (
|
||||
cls.de_json_decrypted(passport_file, bot, credentials[i])
|
||||
for i, passport_file in enumerate(data)
|
||||
)
|
||||
)
|
||||
|
||||
async def get_file(
|
||||
self,
|
||||
*,
|
||||
read_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
write_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
connect_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
pool_timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: Optional[JSONDict] = None,
|
||||
) -> "File":
|
||||
"""
|
||||
Wrapper over :meth:`telegram.Bot.get_file`. Will automatically assign the correct
|
||||
credentials to the returned :class:`telegram.File` if originating from
|
||||
:attr:`telegram.PassportData.decrypted_data`.
|
||||
|
||||
For the documentation of the arguments, please see :meth:`telegram.Bot.get_file`.
|
||||
|
||||
Returns:
|
||||
:class:`telegram.File`
|
||||
|
||||
Raises:
|
||||
:class:`telegram.error.TelegramError`
|
||||
|
||||
"""
|
||||
file = await self.get_bot().get_file(
|
||||
file_id=self.file_id,
|
||||
read_timeout=read_timeout,
|
||||
write_timeout=write_timeout,
|
||||
connect_timeout=connect_timeout,
|
||||
pool_timeout=pool_timeout,
|
||||
api_kwargs=api_kwargs,
|
||||
)
|
||||
if self._credentials:
|
||||
file.set_credentials(self._credentials)
|
||||
return file
|
||||
Reference in New Issue
Block a user