mirror of
https://github.com/python-telegram-bot/python-telegram-bot.git
synced 2024-12-22 14:35:00 +01:00
Supplement Codacy with DeepSource (#2454)
* Add deepsource config
* Update Badges
* Update Badges some more
* Stupid change to trigger analysis of all files
* Try to get ignore right
* Update badges again
* Get started on fixing issues
* Fix some more issues
* Remove more plank lines
* Docs for de_json/list & to_dict/json
* Some improvements from deepcode.ai
* Some more improvements
* Some more improvements
* More docstrnigs & let's run DS on the tests just for fun
* Autofix issues in 10 files
Resolved issues in the following files via DeepSource Autofix:
1. tests/conftest.py
2. tests/test_bot.py
3. tests/test_commandhandler.py
4. tests/test_conversationhandler.py
5. tests/test_dispatcher.py
6. tests/test_filters.py
7. tests/test_inputmedia.py
8. tests/test_messagehandler.py
9. tests/test_official.py
10. tests/test_persistence.py
* Some more improvements for tests, but that shall be enough
* Some more docstrings for functions
* Some minor stuff, try to fix tests
* Update DS config
* Still more docs
* Doc fixes
* More fixes
* Fix: indent docstring
* Some fixes
* Revert "Stupid change to trigger analysis of all files"
This reverts commit dd46c260
* Review
Co-authored-by: deepsource-autofix[bot] <62050782+deepsource-autofix[bot]@users.noreply.github.com>
Co-authored-by: Poolitzer <25934244+Poolitzer@users.noreply.github.com>
This commit is contained in:
parent
1572c61063
commit
8bf88c3231
95 changed files with 674 additions and 492 deletions
20
.deepsource.toml
Normal file
20
.deepsource.toml
Normal file
|
@ -0,0 +1,20 @@
|
|||
version = 1
|
||||
|
||||
test_patterns = ["tests/**"]
|
||||
|
||||
exclude_patterns = [
|
||||
"tests/**",
|
||||
"docs/**",
|
||||
"telegram/vendor/**",
|
||||
"setup.py",
|
||||
"setup-raw.py"
|
||||
]
|
||||
|
||||
[[analyzers]]
|
||||
name = "python"
|
||||
enabled = true
|
||||
|
||||
[analyzers.meta]
|
||||
runtime_version = "3.x.x"
|
||||
max_line_length = 99
|
||||
skip_doc_coverage = ["module", "magic", "init", "nonpublic"]
|
|
@ -50,10 +50,14 @@ We have a vibrant community of developers helping each other in our `Telegram gr
|
|||
|
||||
.. image:: https://api.codacy.com/project/badge/Grade/99d901eaa09b44b4819aec05c330c968
|
||||
:target: https://www.codacy.com/app/python-telegram-bot/python-telegram-bot?utm_source=github.com&utm_medium=referral&utm_content=python-telegram-bot/python-telegram-bot&utm_campaign=Badge_Grade
|
||||
:alt: Code quality
|
||||
:alt: Code quality: Codacy
|
||||
|
||||
.. image:: https://deepsource.io/gh/python-telegram-bot/python-telegram-bot.svg/?label=active+issues
|
||||
:target: https://deepsource.io/gh/python-telegram-bot/python-telegram-bot/?ref=repository-badge
|
||||
:alt: Code quality: DeepSource
|
||||
|
||||
.. image:: https://img.shields.io/badge/code%20style-black-000000.svg
|
||||
:target: https://github.com/psf/black
|
||||
:target: https://github.com/psf/black
|
||||
|
||||
.. image:: https://img.shields.io/badge/Telegram-Group-blue.svg?logo=telegram
|
||||
:target: https://telegram.me/pythontelegrambotgroup
|
||||
|
|
|
@ -50,10 +50,14 @@ We have a vibrant community of developers helping each other in our `Telegram gr
|
|||
|
||||
.. image:: https://api.codacy.com/project/badge/Grade/99d901eaa09b44b4819aec05c330c968
|
||||
:target: https://www.codacy.com/app/python-telegram-bot/python-telegram-bot?utm_source=github.com&utm_medium=referral&utm_content=python-telegram-bot/python-telegram-bot&utm_campaign=Badge_Grade
|
||||
:alt: Code quality
|
||||
:alt: Code quality: Codacy
|
||||
|
||||
.. image:: https://deepsource.io/gh/python-telegram-bot/python-telegram-bot.svg/?label=active+issues
|
||||
:target: https://deepsource.io/gh/python-telegram-bot/python-telegram-bot/?ref=repository-badge
|
||||
:alt: Code quality: DeepSource
|
||||
|
||||
.. image:: https://img.shields.io/badge/code%20style-black-000000.svg
|
||||
:target: https://github.com/psf/black
|
||||
:target: https://github.com/psf/black
|
||||
|
||||
.. image:: https://img.shields.io/badge/Telegram-Group-blue.svg?logo=telegram
|
||||
:target: https://telegram.me/pythontelegrambotgroup
|
||||
|
|
|
@ -24,6 +24,7 @@ logger = logging.getLogger(__name__)
|
|||
|
||||
|
||||
def msg(update: Update, _: CallbackContext) -> None:
|
||||
"""Downloads and prints the received passport data."""
|
||||
# Retrieve passport data
|
||||
passport_data = update.message.passport_data
|
||||
# If our nonce doesn't match what we think, this Update did not originate from us
|
||||
|
@ -61,21 +62,24 @@ def msg(update: Update, _: CallbackContext) -> None:
|
|||
actual_file = file.get_file()
|
||||
print(actual_file)
|
||||
actual_file.download()
|
||||
if data.type in ('passport', 'driver_license', 'identity_card', 'internal_passport'):
|
||||
if data.front_side:
|
||||
front_file = data.front_side.get_file()
|
||||
print(data.type, front_file)
|
||||
front_file.download()
|
||||
if data.type in ('driver_license' and 'identity_card'):
|
||||
if data.reverse_side:
|
||||
reverse_file = data.reverse_side.get_file()
|
||||
print(data.type, reverse_file)
|
||||
reverse_file.download()
|
||||
if data.type in ('passport', 'driver_license', 'identity_card', 'internal_passport'):
|
||||
if data.selfie:
|
||||
selfie_file = data.selfie.get_file()
|
||||
print(data.type, selfie_file)
|
||||
selfie_file.download()
|
||||
if (
|
||||
data.type in ('passport', 'driver_license', 'identity_card', 'internal_passport')
|
||||
and data.front_side
|
||||
):
|
||||
front_file = data.front_side.get_file()
|
||||
print(data.type, front_file)
|
||||
front_file.download()
|
||||
if data.type in ('driver_license' and 'identity_card') and data.reverse_side:
|
||||
reverse_file = data.reverse_side.get_file()
|
||||
print(data.type, reverse_file)
|
||||
reverse_file.download()
|
||||
if (
|
||||
data.type in ('passport', 'driver_license', 'identity_card', 'internal_passport')
|
||||
and data.selfie
|
||||
):
|
||||
selfie_file = data.selfie.get_file()
|
||||
print(data.type, selfie_file)
|
||||
selfie_file.download()
|
||||
if data.type in (
|
||||
'passport',
|
||||
'driver_license',
|
||||
|
@ -97,7 +101,8 @@ def msg(update: Update, _: CallbackContext) -> None:
|
|||
def main() -> None:
|
||||
"""Start the bot."""
|
||||
# Create the Updater and pass it your token and private key
|
||||
updater = Updater("TOKEN", private_key=open('private.key', 'rb').read())
|
||||
with open('private.key', 'rb') as private_key:
|
||||
updater = Updater("TOKEN", private_key=private_key.read())
|
||||
|
||||
# Get the dispatcher to register handlers
|
||||
dispatcher = updater.dispatcher
|
||||
|
|
|
@ -29,7 +29,7 @@ from .constants import BOT_API_VERSION
|
|||
|
||||
def _git_revision() -> Optional[str]:
|
||||
try:
|
||||
output = subprocess.check_output(
|
||||
output = subprocess.check_output( # skipcq: BAN-B607
|
||||
["git", "describe", "--long", "--tags"], stderr=subprocess.STDOUT
|
||||
)
|
||||
except (subprocess.SubprocessError, OSError):
|
||||
|
@ -37,7 +37,7 @@ def _git_revision() -> Optional[str]:
|
|||
return output.decode().strip()
|
||||
|
||||
|
||||
def print_ver_info() -> None:
|
||||
def print_ver_info() -> None: # skipcq: PY-D0003
|
||||
git_revision = _git_revision()
|
||||
print(f'python-telegram-bot {telegram_ver}' + (f' ({git_revision})' if git_revision else ''))
|
||||
print(f'Bot API {BOT_API_VERSION}')
|
||||
|
@ -46,7 +46,7 @@ def print_ver_info() -> None:
|
|||
print(f'Python {sys_version}')
|
||||
|
||||
|
||||
def main() -> None:
|
||||
def main() -> None: # skipcq: PY-D0003
|
||||
print_ver_info()
|
||||
|
||||
|
||||
|
|
|
@ -34,7 +34,7 @@ TO = TypeVar('TO', bound='TelegramObject', covariant=True)
|
|||
|
||||
|
||||
class TelegramObject:
|
||||
"""Base class for most telegram objects."""
|
||||
"""Base class for most Telegram objects."""
|
||||
|
||||
_id_attrs: Tuple[object, ...] = ()
|
||||
|
||||
|
@ -45,12 +45,22 @@ class TelegramObject:
|
|||
return self.__dict__[item]
|
||||
|
||||
@staticmethod
|
||||
def parse_data(data: Optional[JSONDict]) -> Optional[JSONDict]:
|
||||
def _parse_data(data: Optional[JSONDict]) -> Optional[JSONDict]:
|
||||
return None if data is None else data.copy()
|
||||
|
||||
@classmethod
|
||||
def de_json(cls: Type[TO], data: Optional[JSONDict], bot: 'Bot') -> Optional[TO]:
|
||||
data = cls.parse_data(data)
|
||||
"""Converts JSON data to a Telegram object.
|
||||
|
||||
Args:
|
||||
data (Dict[:obj:`str`, ...]): The JSON data.
|
||||
bot (:class:`telegram.Bot`): The bot associated with this object.
|
||||
|
||||
Returns:
|
||||
The Telegram object.
|
||||
|
||||
"""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if data is None:
|
||||
return None
|
||||
|
@ -61,21 +71,35 @@ class TelegramObject:
|
|||
|
||||
@classmethod
|
||||
def de_list(cls: Type[TO], data: Optional[List[JSONDict]], bot: 'Bot') -> List[Optional[TO]]:
|
||||
"""Converts JSON data to a list of Telegram objects.
|
||||
|
||||
Args:
|
||||
data (Dict[:obj:`str`, ...]): The JSON data.
|
||||
bot (:class:`telegram.Bot`): The bot associated with these objects.
|
||||
|
||||
Returns:
|
||||
A list of Telegram objects.
|
||||
|
||||
"""
|
||||
if not data:
|
||||
return []
|
||||
|
||||
return [cls.de_json(d, bot) for d in data]
|
||||
|
||||
def to_json(self) -> str:
|
||||
"""
|
||||
"""Gives a JSON representation of object.
|
||||
|
||||
Returns:
|
||||
:obj:`str`
|
||||
|
||||
"""
|
||||
|
||||
return json.dumps(self.to_dict())
|
||||
|
||||
def to_dict(self) -> JSONDict:
|
||||
"""Gives representation of object as :obj:`dict`.
|
||||
|
||||
Returns:
|
||||
:obj:`dict`
|
||||
"""
|
||||
data = {}
|
||||
|
||||
for key in iter(self.__dict__):
|
||||
|
|
|
@ -116,7 +116,7 @@ if TYPE_CHECKING:
|
|||
RT = TypeVar('RT')
|
||||
|
||||
|
||||
def log(
|
||||
def log( # skipcq: PY-D0003
|
||||
func: Callable[..., RT], *args: object, **kwargs: object # pylint: disable=W0613
|
||||
) -> Callable[..., RT]:
|
||||
logger = logging.getLogger(func.__module__)
|
||||
|
@ -301,7 +301,7 @@ class Bot(TelegramObject):
|
|||
return Message.de_json(result, self) # type: ignore[return-value, arg-type]
|
||||
|
||||
@property
|
||||
def request(self) -> Request:
|
||||
def request(self) -> Request: # skip-cq: PY-D0003
|
||||
return self._request
|
||||
|
||||
@staticmethod
|
||||
|
@ -319,7 +319,6 @@ class Bot(TelegramObject):
|
|||
@property
|
||||
def bot(self) -> User:
|
||||
""":class:`telegram.User`: User instance for the bot as returned by :meth:`get_me`."""
|
||||
|
||||
if self._bot is None:
|
||||
self._bot = self.get_me()
|
||||
return self._bot
|
||||
|
@ -327,55 +326,46 @@ class Bot(TelegramObject):
|
|||
@property
|
||||
def id(self) -> int: # pylint: disable=C0103
|
||||
""":obj:`int`: Unique identifier for this bot."""
|
||||
|
||||
return self.bot.id
|
||||
|
||||
@property
|
||||
def first_name(self) -> str:
|
||||
""":obj:`str`: Bot's first name."""
|
||||
|
||||
return self.bot.first_name
|
||||
|
||||
@property
|
||||
def last_name(self) -> str:
|
||||
""":obj:`str`: Optional. Bot's last name."""
|
||||
|
||||
return self.bot.last_name # type: ignore
|
||||
|
||||
@property
|
||||
def username(self) -> str:
|
||||
""":obj:`str`: Bot's username."""
|
||||
|
||||
return self.bot.username # type: ignore
|
||||
|
||||
@property
|
||||
def link(self) -> str:
|
||||
""":obj:`str`: Convenience property. Returns the t.me link of the bot."""
|
||||
|
||||
return f"https://t.me/{self.username}"
|
||||
|
||||
@property
|
||||
def can_join_groups(self) -> bool:
|
||||
""":obj:`bool`: Bot's :attr:`telegram.User.can_join_groups` attribute."""
|
||||
|
||||
return self.bot.can_join_groups # type: ignore
|
||||
|
||||
@property
|
||||
def can_read_all_group_messages(self) -> bool:
|
||||
""":obj:`bool`: Bot's :attr:`telegram.User.can_read_all_group_messages` attribute."""
|
||||
|
||||
return self.bot.can_read_all_group_messages # type: ignore
|
||||
|
||||
@property
|
||||
def supports_inline_queries(self) -> bool:
|
||||
""":obj:`bool`: Bot's :attr:`telegram.User.supports_inline_queries` attribute."""
|
||||
|
||||
return self.bot.supports_inline_queries # type: ignore
|
||||
|
||||
@property
|
||||
def commands(self) -> List[BotCommand]:
|
||||
"""List[:class:`BotCommand`]: Bot's commands."""
|
||||
|
||||
if self._commands is None:
|
||||
self._commands = self.get_my_commands()
|
||||
return self._commands
|
||||
|
@ -383,7 +373,6 @@ class Bot(TelegramObject):
|
|||
@property
|
||||
def name(self) -> str:
|
||||
""":obj:`str`: Bot's @username."""
|
||||
|
||||
return f'@{self.username}'
|
||||
|
||||
@log
|
||||
|
@ -2606,7 +2595,6 @@ class Bot(TelegramObject):
|
|||
Raises:
|
||||
:class:`telegram.error.TelegramError`
|
||||
"""
|
||||
|
||||
if inline_message_id is None and (chat_id is None or message_id is None):
|
||||
raise ValueError(
|
||||
'edit_message_media: Both chat_id and message_id are required when '
|
||||
|
@ -4257,7 +4245,6 @@ class Bot(TelegramObject):
|
|||
:class:`telegram.error.TelegramError`
|
||||
|
||||
"""
|
||||
|
||||
data: JSONDict = {'chat_id': chat_id}
|
||||
|
||||
return self._post( # type: ignore[return-value]
|
||||
|
@ -5089,6 +5076,7 @@ class Bot(TelegramObject):
|
|||
return MessageId.de_json(result, self) # type: ignore[return-value, arg-type]
|
||||
|
||||
def to_dict(self) -> JSONDict:
|
||||
"""See :meth:`telegram.TelegramObject.to_dict`."""
|
||||
data: JSONDict = {'id': self.id, 'username': self.username, 'first_name': self.first_name}
|
||||
|
||||
if self.last_name:
|
||||
|
|
|
@ -113,7 +113,8 @@ class CallbackQuery(TelegramObject):
|
|||
|
||||
@classmethod
|
||||
def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['CallbackQuery']:
|
||||
data = cls.parse_data(data)
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
|
|
@ -229,14 +229,16 @@ class Chat(TelegramObject):
|
|||
@property
|
||||
def link(self) -> Optional[str]:
|
||||
""":obj:`str`: Convenience property. If the chat has a :attr:`username`, returns a t.me
|
||||
link of the chat."""
|
||||
link of the chat.
|
||||
"""
|
||||
if self.username:
|
||||
return f"https://t.me/{self.username}"
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['Chat']:
|
||||
data = cls.parse_data(data)
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
|
|
@ -84,7 +84,8 @@ class ChatInviteLink(TelegramObject):
|
|||
|
||||
@classmethod
|
||||
def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['ChatInviteLink']:
|
||||
data = cls.parse_data(data)
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
@ -95,6 +96,7 @@ class ChatInviteLink(TelegramObject):
|
|||
return cls(**data)
|
||||
|
||||
def to_dict(self) -> JSONDict:
|
||||
"""See :meth:`telegram.TelegramObject.to_dict`."""
|
||||
data = super().to_dict()
|
||||
|
||||
data['expire_date'] = to_timestamp(self.expire_date)
|
||||
|
|
|
@ -60,7 +60,8 @@ class ChatLocation(TelegramObject):
|
|||
|
||||
@classmethod
|
||||
def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['ChatLocation']:
|
||||
data = cls.parse_data(data)
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
|
|
@ -207,7 +207,8 @@ class ChatMember(TelegramObject):
|
|||
|
||||
@classmethod
|
||||
def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['ChatMember']:
|
||||
data = cls.parse_data(data)
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
@ -218,6 +219,7 @@ class ChatMember(TelegramObject):
|
|||
return cls(**data)
|
||||
|
||||
def to_dict(self) -> JSONDict:
|
||||
"""See :meth:`telegram.TelegramObject.to_dict`."""
|
||||
data = super().to_dict()
|
||||
|
||||
data['until_date'] = to_timestamp(self.until_date)
|
||||
|
|
|
@ -92,7 +92,8 @@ class ChatMemberUpdated(TelegramObject):
|
|||
|
||||
@classmethod
|
||||
def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['ChatMemberUpdated']:
|
||||
data = cls.parse_data(data)
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
@ -107,6 +108,7 @@ class ChatMemberUpdated(TelegramObject):
|
|||
return cls(**data)
|
||||
|
||||
def to_dict(self) -> JSONDict:
|
||||
"""See :meth:`telegram.TelegramObject.to_dict`."""
|
||||
data = super().to_dict()
|
||||
|
||||
# Required
|
||||
|
|
|
@ -82,7 +82,8 @@ class ChosenInlineResult(TelegramObject):
|
|||
|
||||
@classmethod
|
||||
def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['ChosenInlineResult']:
|
||||
data = cls.parse_data(data)
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
|
|
@ -70,7 +70,7 @@ class Dice(TelegramObject):
|
|||
|
||||
self._id_attrs = (self.value, self.emoji)
|
||||
|
||||
DICE: ClassVar[str] = constants.DICE_DICE
|
||||
DICE: ClassVar[str] = constants.DICE_DICE # skipcq: PTC-W0052
|
||||
""":const:`telegram.constants.DICE_DICE`"""
|
||||
DARTS: ClassVar[str] = constants.DICE_DARTS
|
||||
""":const:`telegram.constants.DICE_DARTS`"""
|
||||
|
|
|
@ -39,6 +39,8 @@ def _lstrip_str(in_s: str, lstr: str) -> str:
|
|||
|
||||
|
||||
class TelegramError(Exception):
|
||||
"""Base class for Telegram errors."""
|
||||
|
||||
def __init__(self, message: str):
|
||||
super().__init__()
|
||||
|
||||
|
@ -58,10 +60,12 @@ class TelegramError(Exception):
|
|||
|
||||
|
||||
class Unauthorized(TelegramError):
|
||||
pass
|
||||
"""Raised when the bot has not enough rights to perform the requested action."""
|
||||
|
||||
|
||||
class InvalidToken(TelegramError):
|
||||
"""Raised when the token is invalid."""
|
||||
|
||||
def __init__(self) -> None:
|
||||
super().__init__('Invalid token')
|
||||
|
||||
|
@ -70,14 +74,16 @@ class InvalidToken(TelegramError):
|
|||
|
||||
|
||||
class NetworkError(TelegramError):
|
||||
pass
|
||||
"""Base class for exceptions due to networking errors."""
|
||||
|
||||
|
||||
class BadRequest(NetworkError):
|
||||
pass
|
||||
"""Raised when Telegram could not process the request correctly."""
|
||||
|
||||
|
||||
class TimedOut(NetworkError):
|
||||
"""Raised when a request took too long to finish."""
|
||||
|
||||
def __init__(self) -> None:
|
||||
super().__init__('Timed out')
|
||||
|
||||
|
@ -87,6 +93,8 @@ class TimedOut(NetworkError):
|
|||
|
||||
class ChatMigrated(TelegramError):
|
||||
"""
|
||||
Raised when the requested group chat migrated to supergroup and has a new chat id.
|
||||
|
||||
Args:
|
||||
new_chat_id (:obj:`int`): The new chat id of the group.
|
||||
|
||||
|
@ -102,6 +110,8 @@ class ChatMigrated(TelegramError):
|
|||
|
||||
class RetryAfter(TelegramError):
|
||||
"""
|
||||
Raised when flood limits where exceeded.
|
||||
|
||||
Args:
|
||||
retry_after (:obj:`int`): Time in seconds, after which the bot can retry the request.
|
||||
|
||||
|
@ -116,13 +126,7 @@ class RetryAfter(TelegramError):
|
|||
|
||||
|
||||
class Conflict(TelegramError):
|
||||
"""
|
||||
Raised when a long poll or webhook conflicts with another one.
|
||||
|
||||
Args:
|
||||
msg (:obj:`str`): The message from telegrams server.
|
||||
|
||||
"""
|
||||
"""Raised when a long poll or webhook conflicts with another one."""
|
||||
|
||||
def __reduce__(self) -> Tuple[type, Tuple[str]]:
|
||||
return self.__class__, (self.message,)
|
||||
|
|
|
@ -51,19 +51,6 @@ class CallbackContext:
|
|||
that you think you added will not be present.
|
||||
|
||||
Attributes:
|
||||
bot_data (:obj:`dict`): Optional. A dict that can be used to keep any data in. For each
|
||||
update it will be the same ``dict``.
|
||||
chat_data (:obj:`dict`): Optional. A dict that can be used to keep any data in. For each
|
||||
update from the same chat id it will be the same ``dict``.
|
||||
|
||||
Warning:
|
||||
When a group chat migrates to a supergroup, its chat id will change and the
|
||||
``chat_data`` needs to be transferred. For details see our `wiki page
|
||||
<https://github.com/python-telegram-bot/python-telegram-bot/wiki/
|
||||
Storing-user--and-chat-related-data#chat-migration>`_.
|
||||
|
||||
user_data (:obj:`dict`): Optional. A dict that can be used to keep any data in. For each
|
||||
update from the same user it will be the same ``dict``.
|
||||
matches (List[:obj:`re match object`]): Optional. If the associated update originated from
|
||||
a regex-supported handler or had a :class:`Filters.regex`, this will contain a list of
|
||||
match objects for every pattern where ``re.search(pattern, string)`` returned a match.
|
||||
|
@ -113,6 +100,9 @@ class CallbackContext:
|
|||
|
||||
@property
|
||||
def bot_data(self) -> Dict:
|
||||
""":obj:`dict`: Optional. A dict that can be used to keep any data in. For each
|
||||
update it will be the same ``dict``.
|
||||
"""
|
||||
return self._bot_data
|
||||
|
||||
@bot_data.setter
|
||||
|
@ -123,6 +113,15 @@ class CallbackContext:
|
|||
|
||||
@property
|
||||
def chat_data(self) -> Optional[Dict]:
|
||||
""":obj:`dict`: Optional. A dict that can be used to keep any data in. For each
|
||||
update from the same chat id it will be the same ``dict``.
|
||||
|
||||
Warning:
|
||||
When a group chat migrates to a supergroup, its chat id will change and the
|
||||
``chat_data`` needs to be transferred. For details see our `wiki page
|
||||
<https://github.com/python-telegram-bot/python-telegram-bot/wiki/
|
||||
Storing-bot,-user-and-chat-related-data#chat-migration>`_.
|
||||
"""
|
||||
return self._chat_data
|
||||
|
||||
@chat_data.setter
|
||||
|
@ -133,6 +132,9 @@ class CallbackContext:
|
|||
|
||||
@property
|
||||
def user_data(self) -> Optional[Dict]:
|
||||
""":obj:`dict`: Optional. A dict that can be used to keep any data in. For each
|
||||
update from the same user it will be the same ``dict``.
|
||||
"""
|
||||
return self._user_data
|
||||
|
||||
@user_data.setter
|
||||
|
@ -150,6 +152,28 @@ class CallbackContext:
|
|||
async_args: Union[List, Tuple] = None,
|
||||
async_kwargs: Dict[str, object] = None,
|
||||
) -> 'CallbackContext':
|
||||
"""
|
||||
Constructs an instance of :class:`telegram.ext.CallbackContext` to be passed to the error
|
||||
handlers.
|
||||
|
||||
.. seealso:: :meth:`telegram.ext.Dispatcher.add_error_handler`
|
||||
|
||||
Args:
|
||||
update (:obj:`object` | :class:`telegram.Update`): The update associated with the
|
||||
error. May be :obj:`None`, e.g. for errors in job callbacks.
|
||||
error (:obj:`Exception`): The error.
|
||||
dispatcher (:class:`telegram.ext.Dispatcher`): The dispatcher associated with this
|
||||
context.
|
||||
async_args (List[:obj:`object`]): Optional. Positional arguments of the function that
|
||||
raised the error. Pass only when the raising function was run asynchronously using
|
||||
:meth:`telegram.ext.Dispatcher.run_async`.
|
||||
async_kwargs (Dict[:obj:`str`, :obj:`object`]): Optional. Keyword arguments of the
|
||||
function that raised the error. Pass only when the raising function was run
|
||||
asynchronously using :meth:`telegram.ext.Dispatcher.run_async`.
|
||||
|
||||
Returns:
|
||||
:class:`telegram.ext.CallbackContext`
|
||||
"""
|
||||
self = cls.from_update(update, dispatcher)
|
||||
self.error = error
|
||||
self.async_args = async_args
|
||||
|
@ -158,6 +182,20 @@ class CallbackContext:
|
|||
|
||||
@classmethod
|
||||
def from_update(cls, update: object, dispatcher: 'Dispatcher') -> 'CallbackContext':
|
||||
"""
|
||||
Constructs an instance of :class:`telegram.ext.CallbackContext` to be passed to the
|
||||
handlers.
|
||||
|
||||
.. seealso:: :meth:`telegram.ext.Dispatcher.add_handler`
|
||||
|
||||
Args:
|
||||
update (:obj:`object` | :class:`telegram.Update`): The update.
|
||||
dispatcher (:class:`telegram.ext.Dispatcher`): The dispatcher associated with this
|
||||
context.
|
||||
|
||||
Returns:
|
||||
:class:`telegram.ext.CallbackContext`
|
||||
"""
|
||||
self = cls(dispatcher)
|
||||
|
||||
if update is not None and isinstance(update, Update):
|
||||
|
@ -172,11 +210,30 @@ class CallbackContext:
|
|||
|
||||
@classmethod
|
||||
def from_job(cls, job: 'Job', dispatcher: 'Dispatcher') -> 'CallbackContext':
|
||||
"""
|
||||
Constructs an instance of :class:`telegram.ext.CallbackContext` to be passed to a
|
||||
job callback.
|
||||
|
||||
.. seealso:: :meth:`telegram.ext.JobQueue`
|
||||
|
||||
Args:
|
||||
job (:class:`telegram.ext.Job`): The job.
|
||||
dispatcher (:class:`telegram.ext.Dispatcher`): The dispatcher associated with this
|
||||
context.
|
||||
|
||||
Returns:
|
||||
:class:`telegram.ext.CallbackContext`
|
||||
"""
|
||||
self = cls(dispatcher)
|
||||
self.job = job
|
||||
return self
|
||||
|
||||
def update(self, data: Dict[str, object]) -> None:
|
||||
"""Updates ``self.__dict__`` with the passed data.
|
||||
|
||||
Args:
|
||||
data (Dict[:obj:`str`, :obj:`object`]): The data.
|
||||
"""
|
||||
self.__dict__.update(data)
|
||||
|
||||
@property
|
||||
|
|
|
@ -174,6 +174,10 @@ class CallbackQueryHandler(Handler[Update]):
|
|||
update: Update = None,
|
||||
check_result: Union[bool, Match] = None,
|
||||
) -> Dict[str, object]:
|
||||
"""Pass the results of ``re.match(pattern, data).{groups(), groupdict()}`` to the
|
||||
callback as a keyword arguments called ``groups`` and ``groupdict``, respectively, if
|
||||
needed.
|
||||
"""
|
||||
optional_args = super().collect_optional_args(dispatcher, update, check_result)
|
||||
if self.pattern:
|
||||
check_result = cast(Match, check_result)
|
||||
|
@ -190,6 +194,9 @@ class CallbackQueryHandler(Handler[Update]):
|
|||
dispatcher: 'Dispatcher',
|
||||
check_result: Union[bool, Match],
|
||||
) -> None:
|
||||
"""Add the result of ``re.match(pattern, update.callback_query.data)`` to
|
||||
:attr:`CallbackContext.matches` as list with one element.
|
||||
"""
|
||||
if self.pattern:
|
||||
check_result = cast(Match, check_result)
|
||||
context.matches = [check_result]
|
||||
|
|
|
@ -150,7 +150,8 @@ class ChosenInlineResultHandler(Handler[Update]):
|
|||
check_result: Union[bool, Match],
|
||||
) -> None:
|
||||
"""This function adds the matched regex pattern result to
|
||||
:attr:`telegram.ext.CallbackContext.matches`."""
|
||||
:attr:`telegram.ext.CallbackContext.matches`.
|
||||
"""
|
||||
if self.pattern:
|
||||
check_result = cast(Match, check_result)
|
||||
context.matches = [check_result]
|
||||
|
|
|
@ -219,6 +219,9 @@ class CommandHandler(Handler[Update]):
|
|||
update: Update = None,
|
||||
check_result: Optional[Union[bool, Tuple[List[str], Optional[bool]]]] = None,
|
||||
) -> Dict[str, object]:
|
||||
"""Provide text after the command to the callback the ``args`` argument as list, split on
|
||||
single whitespaces.
|
||||
"""
|
||||
optional_args = super().collect_optional_args(dispatcher, update)
|
||||
if self.pass_args and isinstance(check_result, tuple):
|
||||
optional_args['args'] = check_result[0]
|
||||
|
@ -231,6 +234,9 @@ class CommandHandler(Handler[Update]):
|
|||
dispatcher: 'Dispatcher',
|
||||
check_result: Optional[Union[bool, Tuple[List[str], Optional[bool]]]],
|
||||
) -> None:
|
||||
"""Add text after the command to :attr:`CallbackContext.args` as list, split on single
|
||||
whitespaces and add output of data filters to :attr:`CallbackContext` as well.
|
||||
"""
|
||||
if isinstance(check_result, tuple):
|
||||
context.args = check_result[0]
|
||||
if isinstance(check_result[1], dict):
|
||||
|
@ -238,7 +244,7 @@ class CommandHandler(Handler[Update]):
|
|||
|
||||
|
||||
class PrefixHandler(CommandHandler):
|
||||
"""Handler class to handle custom prefix commands
|
||||
"""Handler class to handle custom prefix commands.
|
||||
|
||||
This is a intermediate handler between :class:`MessageHandler` and :class:`CommandHandler`.
|
||||
It supports configurable commands with the same options as CommandHandler. It will respond to
|
||||
|
@ -265,7 +271,7 @@ class PrefixHandler(CommandHandler):
|
|||
.. code:: python
|
||||
|
||||
PrefixHandler(['!', '#'], ['test', 'help'], callback) # will respond to '!test', \
|
||||
'#test', '!help' and '#help'.
|
||||
'#test', '!help' and '#help'.
|
||||
|
||||
|
||||
By default the handler listens to messages as well as edited messages. To change this behavior
|
||||
|
@ -442,15 +448,3 @@ class PrefixHandler(CommandHandler):
|
|||
return text_list[1:], filter_result
|
||||
return False
|
||||
return None
|
||||
|
||||
def collect_additional_context(
|
||||
self,
|
||||
context: 'CallbackContext',
|
||||
update: Update,
|
||||
dispatcher: 'Dispatcher',
|
||||
check_result: Optional[Union[bool, Tuple[List[str], Optional[bool]]]],
|
||||
) -> None:
|
||||
if isinstance(check_result, tuple):
|
||||
context.args = check_result[0]
|
||||
if isinstance(check_result[1], dict):
|
||||
context.update(check_result[1])
|
||||
|
|
|
@ -173,33 +173,8 @@ class ConversationHandler(Handler[Update]):
|
|||
ValueError
|
||||
|
||||
Attributes:
|
||||
entry_points (List[:class:`telegram.ext.Handler`]): A list of ``Handler`` objects that can
|
||||
trigger the start of the conversation.
|
||||
states (Dict[:obj:`object`, List[:class:`telegram.ext.Handler`]]): A :obj:`dict` that
|
||||
defines the different states of conversation a user can be in and one or more
|
||||
associated ``Handler`` objects that should be used in that state.
|
||||
fallbacks (List[:class:`telegram.ext.Handler`]): A list of handlers that might be used if
|
||||
the user is in a conversation, but every handler for their current state returned
|
||||
:obj:`False` on :attr:`check_update`.
|
||||
allow_reentry (:obj:`bool`): Determines if a user can restart a conversation with
|
||||
an entry point.
|
||||
per_chat (:obj:`bool`): If the conversationkey should contain the Chat's ID.
|
||||
per_user (:obj:`bool`): If the conversationkey should contain the User's ID.
|
||||
per_message (:obj:`bool`): If the conversationkey should contain the Message's
|
||||
ID.
|
||||
conversation_timeout (:obj:`float` | :obj:`datetime.timedelta`): Optional. When this
|
||||
handler is inactive more than this timeout (in seconds), it will be automatically
|
||||
ended. If this value is 0 (default), there will be no timeout. When it's triggered, the
|
||||
last received update and the corresponding ``context`` will be handled by ALL the
|
||||
handler's who's :attr:`check_update` method returns :obj:`True` that are in the state
|
||||
:attr:`ConversationHandler.TIMEOUT`.
|
||||
name (:obj:`str`): Optional. The name for this conversationhandler. Required for
|
||||
persistence
|
||||
persistent (:obj:`bool`): Optional. If the conversations dict for this handler should be
|
||||
saved. Name is required and persistence has to be set in :class:`telegram.ext.Updater`
|
||||
map_to_parent (Dict[:obj:`object`, :obj:`object`]): Optional. A :obj:`dict` that can be
|
||||
used to instruct a nested conversationhandler to transition into a mapped state on
|
||||
its parent conversationhandler in place of a specified nested state.
|
||||
run_async (:obj:`bool`): If :obj:`True`, will override the
|
||||
:attr:`Handler.run_async` setting of all internal handlers on initialization.
|
||||
|
||||
|
@ -316,6 +291,9 @@ class ConversationHandler(Handler[Update]):
|
|||
|
||||
@property
|
||||
def entry_points(self) -> List[Handler]:
|
||||
"""List[:class:`telegram.ext.Handler`]: A list of ``Handler`` objects that can trigger the
|
||||
start of the conversation.
|
||||
"""
|
||||
return self._entry_points
|
||||
|
||||
@entry_points.setter
|
||||
|
@ -324,6 +302,10 @@ class ConversationHandler(Handler[Update]):
|
|||
|
||||
@property
|
||||
def states(self) -> Dict[object, List[Handler]]:
|
||||
"""Dict[:obj:`object`, List[:class:`telegram.ext.Handler`]]: A :obj:`dict` that
|
||||
defines the different states of conversation a user can be in and one or more
|
||||
associated ``Handler`` objects that should be used in that state.
|
||||
"""
|
||||
return self._states
|
||||
|
||||
@states.setter
|
||||
|
@ -332,6 +314,10 @@ class ConversationHandler(Handler[Update]):
|
|||
|
||||
@property
|
||||
def fallbacks(self) -> List[Handler]:
|
||||
"""List[:class:`telegram.ext.Handler`]: A list of handlers that might be used if
|
||||
the user is in a conversation, but every handler for their current state returned
|
||||
:obj:`False` on :attr:`check_update`.
|
||||
"""
|
||||
return self._fallbacks
|
||||
|
||||
@fallbacks.setter
|
||||
|
@ -344,10 +330,12 @@ class ConversationHandler(Handler[Update]):
|
|||
|
||||
@allow_reentry.setter
|
||||
def allow_reentry(self, value: object) -> NoReturn:
|
||||
""":obj:`bool`: Determines if a user can restart a conversation with an entry point."""
|
||||
raise ValueError('You can not assign a new value to allow_reentry after initialization.')
|
||||
|
||||
@property
|
||||
def per_user(self) -> bool:
|
||||
""":obj:`bool`: If the conversation key should contain the User's ID."""
|
||||
return self._per_user
|
||||
|
||||
@per_user.setter
|
||||
|
@ -356,6 +344,7 @@ class ConversationHandler(Handler[Update]):
|
|||
|
||||
@property
|
||||
def per_chat(self) -> bool:
|
||||
""":obj:`bool`: If the conversation key should contain the Chat's ID."""
|
||||
return self._per_chat
|
||||
|
||||
@per_chat.setter
|
||||
|
@ -364,6 +353,7 @@ class ConversationHandler(Handler[Update]):
|
|||
|
||||
@property
|
||||
def per_message(self) -> bool:
|
||||
""":obj:`bool`: If the conversation key should contain the message's ID."""
|
||||
return self._per_message
|
||||
|
||||
@per_message.setter
|
||||
|
@ -374,16 +364,21 @@ class ConversationHandler(Handler[Update]):
|
|||
def conversation_timeout(
|
||||
self,
|
||||
) -> Optional[Union[float, datetime.timedelta]]:
|
||||
""":obj:`float` | :obj:`datetime.timedelta`: Optional. When this
|
||||
handler is inactive more than this timeout (in seconds), it will be automatically
|
||||
ended.
|
||||
"""
|
||||
return self._conversation_timeout
|
||||
|
||||
@conversation_timeout.setter
|
||||
def conversation_timeout(self, value: object) -> NoReturn:
|
||||
raise ValueError(
|
||||
'You can not assign a new value to conversation_timeout after ' 'initialization.'
|
||||
'You can not assign a new value to conversation_timeout after initialization.'
|
||||
)
|
||||
|
||||
@property
|
||||
def name(self) -> Optional[str]:
|
||||
""":obj:`str`: Optional. The name for this :class:`ConversationHandler`."""
|
||||
return self._name
|
||||
|
||||
@name.setter
|
||||
|
@ -392,6 +387,10 @@ class ConversationHandler(Handler[Update]):
|
|||
|
||||
@property
|
||||
def map_to_parent(self) -> Optional[Dict[object, object]]:
|
||||
"""Dict[:obj:`object`, :obj:`object`]: Optional. A :obj:`dict` that can be
|
||||
used to instruct a nested :class:`ConversationHandler` to transition into a mapped state on
|
||||
its parent :class:`ConversationHandler` in place of a specified nested state.
|
||||
"""
|
||||
return self._map_to_parent
|
||||
|
||||
@map_to_parent.setter
|
||||
|
@ -400,6 +399,7 @@ class ConversationHandler(Handler[Update]):
|
|||
|
||||
@property
|
||||
def persistence(self) -> Optional[BasePersistence]:
|
||||
"""The persistence class as provided by the :class:`Dispatcher`."""
|
||||
return self._persistence
|
||||
|
||||
@persistence.setter
|
||||
|
@ -412,7 +412,7 @@ class ConversationHandler(Handler[Update]):
|
|||
handler.persistence = self.persistence
|
||||
|
||||
@property
|
||||
def conversations(self) -> ConversationDict:
|
||||
def conversations(self) -> ConversationDict: # skipcq: PY-D0003
|
||||
return self._conversations
|
||||
|
||||
@conversations.setter
|
||||
|
@ -518,7 +518,7 @@ class ConversationHandler(Handler[Update]):
|
|||
# check if promise is finished or not
|
||||
if state[1].done.wait(0):
|
||||
res = self._resolve_promise(state)
|
||||
self.update_state(res, key)
|
||||
self._update_state(res, key)
|
||||
with self._conversations_lock:
|
||||
state = self.conversations.get(key)
|
||||
|
||||
|
@ -627,19 +627,19 @@ class ConversationHandler(Handler[Update]):
|
|||
)
|
||||
|
||||
if isinstance(self.map_to_parent, dict) and new_state in self.map_to_parent:
|
||||
self.update_state(self.END, conversation_key)
|
||||
self._update_state(self.END, conversation_key)
|
||||
if raise_dp_handler_stop:
|
||||
raise DispatcherHandlerStop(self.map_to_parent.get(new_state))
|
||||
return self.map_to_parent.get(new_state)
|
||||
|
||||
self.update_state(new_state, conversation_key)
|
||||
self._update_state(new_state, conversation_key)
|
||||
if raise_dp_handler_stop:
|
||||
# Don't pass the new state here. If we're in a nested conversation, the parent is
|
||||
# expecting None as return value.
|
||||
raise DispatcherHandlerStop()
|
||||
return None
|
||||
|
||||
def update_state(self, new_state: object, key: Tuple[int, ...]) -> None:
|
||||
def _update_state(self, new_state: object, key: Tuple[int, ...]) -> None:
|
||||
if new_state == self.END:
|
||||
with self._conversations_lock:
|
||||
if key in self.conversations:
|
||||
|
@ -698,4 +698,4 @@ class ConversationHandler(Handler[Update]):
|
|||
'ConversationHandler has no effect. Ignoring.'
|
||||
)
|
||||
|
||||
self.update_state(self.END, ctxt.conversation_key)
|
||||
self._update_state(self.END, ctxt.conversation_key)
|
||||
|
|
|
@ -54,29 +54,6 @@ class Defaults:
|
|||
run_async (:obj:`bool`, optional): Default setting for the ``run_async`` parameter of
|
||||
handlers and error handlers registered through :meth:`Dispatcher.add_handler` and
|
||||
:meth:`Dispatcher.add_error_handler`. Defaults to :obj:`False`.
|
||||
|
||||
Attributes:
|
||||
parse_mode (:obj:`str`): Optional. Send Markdown or HTML, if you want Telegram apps to show
|
||||
bold, italic, fixed-width text or URLs in your bot's message.
|
||||
explanation_parse_mode (:obj:`str`): Optional. Alias for :attr:`parse_mode`, used for
|
||||
the corresponding parameter of :meth:`telegram.Bot.send_poll`.
|
||||
disable_notification (:obj:`bool`): Optional. Sends the message silently. Users will
|
||||
receive a notification with no sound.
|
||||
disable_web_page_preview (:obj:`bool`): Optional. Disables link previews for links in this
|
||||
message.
|
||||
allow_sending_without_reply (:obj:`bool`): Optional. Pass :obj:`True`, if the message
|
||||
should be sent even if the specified replied-to message is not found.
|
||||
timeout (:obj:`int` | :obj:`float`): Optional. If this value is specified, use it as the
|
||||
read timeout from the server (instead of the one specified during creation of the
|
||||
connection pool).
|
||||
quote (:obj:`bool`): Optional. If set to :obj:`True`, the reply is sent as an actual reply
|
||||
to the message. If ``reply_to_message_id`` is passed in ``kwargs``, this parameter will
|
||||
be ignored. Default: :obj:`True` in group chats and :obj:`False` in private chats.
|
||||
tzinfo (:obj:`tzinfo`): A timezone to be used for all date(time) objects appearing
|
||||
throughout PTB.
|
||||
run_async (:obj:`bool`): Optional. Default setting for the ``run_async`` parameter of
|
||||
handlers and error handlers registered through :meth:`Dispatcher.add_handler` and
|
||||
:meth:`Dispatcher.add_error_handler`.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
|
@ -118,11 +95,14 @@ class Defaults:
|
|||
self._api_defaults['timeout'] = self.timeout
|
||||
|
||||
@property
|
||||
def api_defaults(self) -> Dict[str, Any]:
|
||||
def api_defaults(self) -> Dict[str, Any]: # skip-cq: PY-D0003
|
||||
return self._api_defaults
|
||||
|
||||
@property
|
||||
def parse_mode(self) -> Optional[str]:
|
||||
""":obj:`str`: Optional. Send Markdown or HTML, if you want Telegram apps to show
|
||||
bold, italic, fixed-width text or URLs in your bot's message.
|
||||
"""
|
||||
return self._parse_mode
|
||||
|
||||
@parse_mode.setter
|
||||
|
@ -134,6 +114,9 @@ class Defaults:
|
|||
|
||||
@property
|
||||
def explanation_parse_mode(self) -> Optional[str]:
|
||||
""":obj:`str`: Optional. Alias for :attr:`parse_mode`, used for
|
||||
the corresponding parameter of :meth:`telegram.Bot.send_poll`.
|
||||
"""
|
||||
return self._parse_mode
|
||||
|
||||
@explanation_parse_mode.setter
|
||||
|
@ -145,6 +128,9 @@ class Defaults:
|
|||
|
||||
@property
|
||||
def disable_notification(self) -> Optional[bool]:
|
||||
""":obj:`bool`: Optional. Sends the message silently. Users will
|
||||
receive a notification with no sound.
|
||||
"""
|
||||
return self._disable_notification
|
||||
|
||||
@disable_notification.setter
|
||||
|
@ -156,6 +142,9 @@ class Defaults:
|
|||
|
||||
@property
|
||||
def disable_web_page_preview(self) -> Optional[bool]:
|
||||
""":obj:`bool`: Optional. Disables link previews for links in this
|
||||
message.
|
||||
"""
|
||||
return self._disable_web_page_preview
|
||||
|
||||
@disable_web_page_preview.setter
|
||||
|
@ -167,6 +156,9 @@ class Defaults:
|
|||
|
||||
@property
|
||||
def allow_sending_without_reply(self) -> Optional[bool]:
|
||||
""":obj:`bool`: Optional. Pass :obj:`True`, if the message
|
||||
should be sent even if the specified replied-to message is not found.
|
||||
"""
|
||||
return self._allow_sending_without_reply
|
||||
|
||||
@allow_sending_without_reply.setter
|
||||
|
@ -178,6 +170,10 @@ class Defaults:
|
|||
|
||||
@property
|
||||
def timeout(self) -> ODVInput[float]:
|
||||
""":obj:`int` | :obj:`float`: Optional. If this value is specified, use it as the
|
||||
read timeout from the server (instead of the one specified during creation of the
|
||||
connection pool).
|
||||
"""
|
||||
return self._timeout
|
||||
|
||||
@timeout.setter
|
||||
|
@ -189,6 +185,10 @@ class Defaults:
|
|||
|
||||
@property
|
||||
def quote(self) -> Optional[bool]:
|
||||
""":obj:`bool`: Optional. If set to :obj:`True`, the reply is sent as an actual reply
|
||||
to the message. If ``reply_to_message_id`` is passed in ``kwargs``, this parameter will
|
||||
be ignored. Default: :obj:`True` in group chats and :obj:`False` in private chats.
|
||||
"""
|
||||
return self._quote
|
||||
|
||||
@quote.setter
|
||||
|
@ -200,6 +200,9 @@ class Defaults:
|
|||
|
||||
@property
|
||||
def tzinfo(self) -> pytz.BaseTzInfo:
|
||||
""":obj:`tzinfo`: A timezone to be used for all date(time) objects appearing
|
||||
throughout PTB.
|
||||
"""
|
||||
return self._tzinfo
|
||||
|
||||
@tzinfo.setter
|
||||
|
@ -211,6 +214,10 @@ class Defaults:
|
|||
|
||||
@property
|
||||
def run_async(self) -> bool:
|
||||
""":obj:`bool`: Optional. Default setting for the ``run_async`` parameter of
|
||||
handlers and error handlers registered through :meth:`Dispatcher.add_handler` and
|
||||
:meth:`Dispatcher.add_error_handler`.
|
||||
"""
|
||||
return self._run_async
|
||||
|
||||
@run_async.setter
|
||||
|
|
|
@ -216,7 +216,7 @@ class Dispatcher:
|
|||
self._set_singleton(None)
|
||||
|
||||
@property
|
||||
def exception_event(self) -> Event:
|
||||
def exception_event(self) -> Event: # skipcq: PY-D0003
|
||||
return self.__exception_event
|
||||
|
||||
def _init_async_threads(self, base_name: str, workers: int) -> None:
|
||||
|
@ -404,7 +404,7 @@ class Dispatcher:
|
|||
self.logger.debug('async thread %s/%s has ended', i + 1, total)
|
||||
|
||||
@property
|
||||
def has_running_threads(self) -> bool:
|
||||
def has_running_threads(self) -> bool: # skipcq: PY-D0003
|
||||
return self.running or bool(self.__async_threads)
|
||||
|
||||
def process_update(self, update: object) -> None:
|
||||
|
@ -422,7 +422,6 @@ class Dispatcher:
|
|||
The update to process.
|
||||
|
||||
"""
|
||||
|
||||
# An error happened while polling
|
||||
if isinstance(update, TelegramError):
|
||||
try:
|
||||
|
@ -637,9 +636,8 @@ class Dispatcher:
|
|||
self.logger.debug('The callback is already registered as an error handler. Ignoring.')
|
||||
return
|
||||
|
||||
if run_async is DEFAULT_FALSE and self.bot.defaults:
|
||||
if self.bot.defaults.run_async:
|
||||
run_async = True
|
||||
if run_async is DEFAULT_FALSE and self.bot.defaults and self.bot.defaults.run_async:
|
||||
run_async = True
|
||||
|
||||
self.error_handlers[callback] = run_async
|
||||
|
||||
|
|
|
@ -117,7 +117,7 @@ class BaseFilter(ABC):
|
|||
|
||||
@abstractmethod
|
||||
def __call__(self, update: Update) -> Optional[Union[bool, DataDict]]:
|
||||
pass
|
||||
...
|
||||
|
||||
def __and__(self, other: 'BaseFilter') -> 'BaseFilter':
|
||||
return MergedFilter(self, and_filter=other)
|
||||
|
@ -652,17 +652,13 @@ class Filters:
|
|||
"""
|
||||
|
||||
def __init__(self, category: Optional[str]):
|
||||
"""Initialize the category you want to filter
|
||||
|
||||
Args:
|
||||
category (str, optional): category of the media you want to filter"""
|
||||
self.category = category
|
||||
self.name = f"Filters.document.category('{self.category}')"
|
||||
self._category = category
|
||||
self.name = f"Filters.document.category('{self._category}')"
|
||||
|
||||
def filter(self, message: Message) -> bool:
|
||||
"""""" # remove method from docs
|
||||
if message.document:
|
||||
return message.document.mime_type.startswith(self.category)
|
||||
return message.document.mime_type.startswith(self._category)
|
||||
return False
|
||||
|
||||
application = category('application/')
|
||||
|
@ -685,10 +681,6 @@ class Filters:
|
|||
"""
|
||||
|
||||
def __init__(self, mimetype: Optional[str]):
|
||||
"""Initialize the category you want to filter
|
||||
|
||||
Args:
|
||||
mimetype (str, optional): mime_type of the media you want to filter"""
|
||||
self.mimetype = mimetype
|
||||
self.name = f"Filters.document.mime_type('{self.mimetype}')"
|
||||
|
||||
|
@ -752,29 +744,29 @@ class Filters:
|
|||
"""
|
||||
self.is_case_sensitive = case_sensitive
|
||||
if file_extension is None:
|
||||
self.file_extension = None
|
||||
self._file_extension = None
|
||||
self.name = "Filters.document.file_extension(None)"
|
||||
elif case_sensitive:
|
||||
self.file_extension = f".{file_extension}"
|
||||
elif self.is_case_sensitive:
|
||||
self._file_extension = f".{file_extension}"
|
||||
self.name = (
|
||||
f"Filters.document.file_extension({file_extension!r},"
|
||||
" case_sensitive=True)"
|
||||
)
|
||||
else:
|
||||
self.file_extension = f".{file_extension}".lower()
|
||||
self._file_extension = f".{file_extension}".lower()
|
||||
self.name = f"Filters.document.file_extension({file_extension.lower()!r})"
|
||||
|
||||
def filter(self, message: Message) -> bool:
|
||||
"""""" # remove method from docs
|
||||
if message.document is None:
|
||||
return False
|
||||
if self.file_extension is None:
|
||||
if self._file_extension is None:
|
||||
return "." not in message.document.file_name
|
||||
if self.is_case_sensitive:
|
||||
filename = message.document.file_name
|
||||
else:
|
||||
filename = message.document.file_name.lower()
|
||||
return filename.endswith(self.file_extension)
|
||||
return filename.endswith(self._file_extension)
|
||||
|
||||
def filter(self, message: Message) -> bool:
|
||||
return bool(message.document)
|
||||
|
@ -1328,7 +1320,7 @@ officedocument.wordprocessingml.document")``.
|
|||
private: Updates sent in private chat
|
||||
"""
|
||||
|
||||
class _ChatUserBaseFilter(MessageFilter):
|
||||
class _ChatUserBaseFilter(MessageFilter, ABC):
|
||||
def __init__(
|
||||
self,
|
||||
chat_id: SLT[int] = None,
|
||||
|
@ -1348,7 +1340,7 @@ officedocument.wordprocessingml.document")``.
|
|||
|
||||
@abstractmethod
|
||||
def get_chat_or_user(self, message: Message) -> Union[Chat, User, None]:
|
||||
pass
|
||||
...
|
||||
|
||||
@staticmethod
|
||||
def _parse_chat_id(chat_id: SLT[int]) -> Set[int]:
|
||||
|
|
|
@ -149,9 +149,12 @@ class Handler(Generic[UT], ABC):
|
|||
|
||||
"""
|
||||
run_async = self.run_async
|
||||
if self.run_async is DEFAULT_FALSE and dispatcher.bot.defaults:
|
||||
if dispatcher.bot.defaults.run_async:
|
||||
run_async = True
|
||||
if (
|
||||
self.run_async is DEFAULT_FALSE
|
||||
and dispatcher.bot.defaults
|
||||
and dispatcher.bot.defaults.run_async
|
||||
):
|
||||
run_async = True
|
||||
|
||||
if context:
|
||||
self.collect_additional_context(context, update, dispatcher, check_result)
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
#
|
||||
# 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 the InlineQueryHandler class """
|
||||
"""This module contains the InlineQueryHandler class."""
|
||||
import re
|
||||
from typing import (
|
||||
TYPE_CHECKING,
|
||||
|
@ -170,7 +170,6 @@ class InlineQueryHandler(Handler[Update]):
|
|||
:obj:`bool`
|
||||
|
||||
"""
|
||||
|
||||
if isinstance(update, Update) and update.inline_query:
|
||||
if (self.chat_types is not None) and (
|
||||
update.inline_query.chat_type not in self.chat_types
|
||||
|
@ -191,6 +190,10 @@ class InlineQueryHandler(Handler[Update]):
|
|||
update: Update = None,
|
||||
check_result: Optional[Union[bool, Match]] = None,
|
||||
) -> Dict[str, object]:
|
||||
"""Pass the results of ``re.match(pattern, query).{groups(), groupdict()}`` to the
|
||||
callback as a keyword arguments called ``groups`` and ``groupdict``, respectively, if
|
||||
needed.
|
||||
"""
|
||||
optional_args = super().collect_optional_args(dispatcher, update, check_result)
|
||||
if self.pattern:
|
||||
check_result = cast(Match, check_result)
|
||||
|
@ -207,6 +210,9 @@ class InlineQueryHandler(Handler[Update]):
|
|||
dispatcher: 'Dispatcher',
|
||||
check_result: Optional[Union[bool, Match]],
|
||||
) -> None:
|
||||
"""Add the result of ``re.match(pattern, update.inline_query.query)`` to
|
||||
:attr:`CallbackContext.matches` as list with one element.
|
||||
"""
|
||||
if self.pattern:
|
||||
check_result = cast(Match, check_result)
|
||||
context.matches = [check_result]
|
||||
|
|
|
@ -38,11 +38,6 @@ if TYPE_CHECKING:
|
|||
import apscheduler.job # noqa: F401
|
||||
|
||||
|
||||
class Days:
|
||||
MON, TUE, WED, THU, FRI, SAT, SUN = range(7)
|
||||
EVERY_DAY = tuple(range(7))
|
||||
|
||||
|
||||
class JobQueue:
|
||||
"""This class allows you to periodically perform tasks with the bot. It is a convenience
|
||||
wrapper for the APScheduler library.
|
||||
|
@ -136,8 +131,7 @@ class JobQueue:
|
|||
"""
|
||||
self._dispatcher = dispatcher
|
||||
if dispatcher.bot.defaults:
|
||||
if dispatcher.bot.defaults:
|
||||
self.scheduler.configure(timezone=dispatcher.bot.defaults.tzinfo or pytz.utc)
|
||||
self.scheduler.configure(timezone=dispatcher.bot.defaults.tzinfo or pytz.utc)
|
||||
|
||||
def run_once(
|
||||
self,
|
||||
|
@ -392,7 +386,7 @@ class JobQueue:
|
|||
self,
|
||||
callback: Callable[['CallbackContext'], None],
|
||||
time: datetime.time,
|
||||
days: Tuple[int, ...] = Days.EVERY_DAY,
|
||||
days: Tuple[int, ...] = tuple(range(7)),
|
||||
context: object = None,
|
||||
name: str = None,
|
||||
job_kwargs: JSONDict = None,
|
||||
|
@ -499,14 +493,16 @@ class JobQueue:
|
|||
self.scheduler.shutdown()
|
||||
|
||||
def jobs(self) -> Tuple['Job', ...]:
|
||||
"""
|
||||
Returns a tuple of all *pending/scheduled* jobs that are currently in the ``JobQueue``.
|
||||
"""
|
||||
return tuple(Job.from_aps_job(job, self) for job in self.scheduler.get_jobs())
|
||||
"""Returns a tuple of all *scheduled* jobs that are currently in the ``JobQueue``."""
|
||||
return tuple(
|
||||
Job._from_aps_job(job, self) # pylint: disable=W0212
|
||||
for job in self.scheduler.get_jobs()
|
||||
)
|
||||
|
||||
def get_jobs_by_name(self, name: str) -> Tuple['Job', ...]:
|
||||
"""Returns a tuple of all *pending/scheduled* jobs with the given name that are currently
|
||||
in the ``JobQueue``"""
|
||||
in the ``JobQueue``.
|
||||
"""
|
||||
return tuple(job for job in self.jobs() if job.name == name)
|
||||
|
||||
|
||||
|
@ -563,7 +559,7 @@ class Job:
|
|||
self._removed = False
|
||||
self._enabled = False
|
||||
|
||||
self.job = cast(APSJob, job)
|
||||
self.job = cast(APSJob, job) # skipcq: PTC-W0052
|
||||
|
||||
def run(self, dispatcher: 'Dispatcher') -> None:
|
||||
"""Executes the callback function independently of the jobs schedule."""
|
||||
|
@ -619,7 +615,7 @@ class Job:
|
|||
return self.job.next_run_time
|
||||
|
||||
@classmethod
|
||||
def from_aps_job(cls, job: APSJob, job_queue: JobQueue) -> 'Job':
|
||||
def _from_aps_job(cls, job: APSJob, job_queue: JobQueue) -> 'Job':
|
||||
# context based callbacks
|
||||
if len(job.args) == 1:
|
||||
context = job.args[0].job.context
|
||||
|
|
|
@ -200,5 +200,6 @@ class MessageHandler(Handler[Update]):
|
|||
dispatcher: 'Dispatcher',
|
||||
check_result: Optional[Union[bool, Dict[str, object]]],
|
||||
) -> None:
|
||||
"""Adds possible output of data filters to the :class:`CallbackContext`."""
|
||||
if isinstance(check_result, dict):
|
||||
context.update(check_result)
|
||||
|
|
|
@ -113,7 +113,6 @@ class DelayQueue(threading.Thread):
|
|||
automatically called by autostart argument.
|
||||
|
||||
"""
|
||||
|
||||
times: List[float] = [] # used to store each callable processing time
|
||||
while True:
|
||||
item = self._queue.get()
|
||||
|
@ -150,7 +149,6 @@ class DelayQueue(threading.Thread):
|
|||
Defaults to :obj:`None`.
|
||||
|
||||
"""
|
||||
|
||||
self.__exit_req = True # gently request
|
||||
self._queue.put(None) # put something to unfreeze if frozen
|
||||
super().join(timeout=timeout)
|
||||
|
@ -162,7 +160,6 @@ class DelayQueue(threading.Thread):
|
|||
by subclasses.
|
||||
|
||||
"""
|
||||
|
||||
raise exc
|
||||
|
||||
def __call__(self, func: Callable, *args: object, **kwargs: object) -> None:
|
||||
|
@ -175,7 +172,6 @@ class DelayQueue(threading.Thread):
|
|||
**kwargs (:obj:`dict`): Arbitrary keyword-arguments to `func`.
|
||||
|
||||
"""
|
||||
|
||||
if not self.is_alive() or self.__exit_req:
|
||||
raise DelayQueueError('Could not process callback in stopped thread')
|
||||
self._queue.put((func, args, kwargs))
|
||||
|
@ -252,6 +248,7 @@ class MessageQueue:
|
|||
self._group_delayq.start()
|
||||
|
||||
def stop(self, timeout: float = None) -> None:
|
||||
"""Stops the ``MessageQueue``."""
|
||||
self._group_delayq.stop(timeout=timeout)
|
||||
self._all_delayq.stop(timeout=timeout)
|
||||
|
||||
|
@ -281,7 +278,6 @@ class MessageQueue:
|
|||
:obj:`callable`: Used as ``promise`` argument.
|
||||
|
||||
"""
|
||||
|
||||
if not is_group_msg: # ignore middle group delay
|
||||
self._all_delayq(promise)
|
||||
else: # use middle group delay
|
||||
|
|
|
@ -95,7 +95,7 @@ class PicklePersistence(BasePersistence):
|
|||
self.bot_data: Optional[Dict] = None
|
||||
self.conversations: Optional[Dict[str, Dict[Tuple, object]]] = None
|
||||
|
||||
def load_singlefile(self) -> None:
|
||||
def _load_singlefile(self) -> None:
|
||||
try:
|
||||
filename = self.filename
|
||||
with open(self.filename, "rb") as file:
|
||||
|
@ -116,7 +116,7 @@ class PicklePersistence(BasePersistence):
|
|||
raise TypeError(f"Something went wrong unpickling {filename}") from exc
|
||||
|
||||
@staticmethod
|
||||
def load_file(filename: str) -> Any:
|
||||
def _load_file(filename: str) -> Any:
|
||||
try:
|
||||
with open(filename, "rb") as file:
|
||||
return pickle.load(file)
|
||||
|
@ -127,7 +127,7 @@ class PicklePersistence(BasePersistence):
|
|||
except Exception as exc:
|
||||
raise TypeError(f"Something went wrong unpickling {filename}") from exc
|
||||
|
||||
def dump_singlefile(self) -> None:
|
||||
def _dump_singlefile(self) -> None:
|
||||
with open(self.filename, "wb") as file:
|
||||
data = {
|
||||
'conversations': self.conversations,
|
||||
|
@ -138,7 +138,7 @@ class PicklePersistence(BasePersistence):
|
|||
pickle.dump(data, file)
|
||||
|
||||
@staticmethod
|
||||
def dump_file(filename: str, data: object) -> None:
|
||||
def _dump_file(filename: str, data: object) -> None:
|
||||
with open(filename, "wb") as file:
|
||||
pickle.dump(data, file)
|
||||
|
||||
|
@ -152,14 +152,14 @@ class PicklePersistence(BasePersistence):
|
|||
pass
|
||||
elif not self.single_file:
|
||||
filename = f"{self.filename}_user_data"
|
||||
data = self.load_file(filename)
|
||||
data = self._load_file(filename)
|
||||
if not data:
|
||||
data = defaultdict(dict)
|
||||
else:
|
||||
data = defaultdict(dict, data)
|
||||
self.user_data = data
|
||||
else:
|
||||
self.load_singlefile()
|
||||
self._load_singlefile()
|
||||
return deepcopy(self.user_data) # type: ignore[arg-type]
|
||||
|
||||
def get_chat_data(self) -> DefaultDict[int, Dict[object, object]]:
|
||||
|
@ -172,14 +172,14 @@ class PicklePersistence(BasePersistence):
|
|||
pass
|
||||
elif not self.single_file:
|
||||
filename = f"{self.filename}_chat_data"
|
||||
data = self.load_file(filename)
|
||||
data = self._load_file(filename)
|
||||
if not data:
|
||||
data = defaultdict(dict)
|
||||
else:
|
||||
data = defaultdict(dict, data)
|
||||
self.chat_data = data
|
||||
else:
|
||||
self.load_singlefile()
|
||||
self._load_singlefile()
|
||||
return deepcopy(self.chat_data) # type: ignore[arg-type]
|
||||
|
||||
def get_bot_data(self) -> Dict[object, object]:
|
||||
|
@ -192,12 +192,12 @@ class PicklePersistence(BasePersistence):
|
|||
pass
|
||||
elif not self.single_file:
|
||||
filename = f"{self.filename}_bot_data"
|
||||
data = self.load_file(filename)
|
||||
data = self._load_file(filename)
|
||||
if not data:
|
||||
data = {}
|
||||
self.bot_data = data
|
||||
else:
|
||||
self.load_singlefile()
|
||||
self._load_singlefile()
|
||||
return deepcopy(self.bot_data) # type: ignore[arg-type]
|
||||
|
||||
def get_conversations(self, name: str) -> ConversationDict:
|
||||
|
@ -213,12 +213,12 @@ class PicklePersistence(BasePersistence):
|
|||
pass
|
||||
elif not self.single_file:
|
||||
filename = f"{self.filename}_conversations"
|
||||
data = self.load_file(filename)
|
||||
data = self._load_file(filename)
|
||||
if not data:
|
||||
data = {name: {}}
|
||||
self.conversations = data
|
||||
else:
|
||||
self.load_singlefile()
|
||||
self._load_singlefile()
|
||||
return self.conversations.get(name, {}).copy() # type: ignore[union-attr]
|
||||
|
||||
def update_conversation(
|
||||
|
@ -240,9 +240,9 @@ class PicklePersistence(BasePersistence):
|
|||
if not self.on_flush:
|
||||
if not self.single_file:
|
||||
filename = f"{self.filename}_conversations"
|
||||
self.dump_file(filename, self.conversations)
|
||||
self._dump_file(filename, self.conversations)
|
||||
else:
|
||||
self.dump_singlefile()
|
||||
self._dump_singlefile()
|
||||
|
||||
def update_user_data(self, user_id: int, data: Dict) -> None:
|
||||
"""Will update the user_data and depending on :attr:`on_flush` save the pickle file.
|
||||
|
@ -259,9 +259,9 @@ class PicklePersistence(BasePersistence):
|
|||
if not self.on_flush:
|
||||
if not self.single_file:
|
||||
filename = f"{self.filename}_user_data"
|
||||
self.dump_file(filename, self.user_data)
|
||||
self._dump_file(filename, self.user_data)
|
||||
else:
|
||||
self.dump_singlefile()
|
||||
self._dump_singlefile()
|
||||
|
||||
def update_chat_data(self, chat_id: int, data: Dict) -> None:
|
||||
"""Will update the chat_data and depending on :attr:`on_flush` save the pickle file.
|
||||
|
@ -278,9 +278,9 @@ class PicklePersistence(BasePersistence):
|
|||
if not self.on_flush:
|
||||
if not self.single_file:
|
||||
filename = f"{self.filename}_chat_data"
|
||||
self.dump_file(filename, self.chat_data)
|
||||
self._dump_file(filename, self.chat_data)
|
||||
else:
|
||||
self.dump_singlefile()
|
||||
self._dump_singlefile()
|
||||
|
||||
def update_bot_data(self, data: Dict) -> None:
|
||||
"""Will update the bot_data and depending on :attr:`on_flush` save the pickle file.
|
||||
|
@ -294,21 +294,21 @@ class PicklePersistence(BasePersistence):
|
|||
if not self.on_flush:
|
||||
if not self.single_file:
|
||||
filename = f"{self.filename}_bot_data"
|
||||
self.dump_file(filename, self.bot_data)
|
||||
self._dump_file(filename, self.bot_data)
|
||||
else:
|
||||
self.dump_singlefile()
|
||||
self._dump_singlefile()
|
||||
|
||||
def flush(self) -> None:
|
||||
"""Will save all data in memory to pickle file(s)."""
|
||||
if self.single_file:
|
||||
if self.user_data or self.chat_data or self.bot_data or self.conversations:
|
||||
self.dump_singlefile()
|
||||
self._dump_singlefile()
|
||||
else:
|
||||
if self.user_data:
|
||||
self.dump_file(f"{self.filename}_user_data", self.user_data)
|
||||
self._dump_file(f"{self.filename}_user_data", self.user_data)
|
||||
if self.chat_data:
|
||||
self.dump_file(f"{self.filename}_chat_data", self.chat_data)
|
||||
self._dump_file(f"{self.filename}_chat_data", self.chat_data)
|
||||
if self.bot_data:
|
||||
self.dump_file(f"{self.filename}_bot_data", self.bot_data)
|
||||
self._dump_file(f"{self.filename}_bot_data", self.bot_data)
|
||||
if self.conversations:
|
||||
self.dump_file(f"{self.filename}_conversations", self.conversations)
|
||||
self._dump_file(f"{self.filename}_conversations", self.conversations)
|
||||
|
|
|
@ -150,6 +150,10 @@ class RegexHandler(MessageHandler):
|
|||
update: Update = None,
|
||||
check_result: Optional[Union[bool, Dict[str, Any]]] = None,
|
||||
) -> Dict[str, object]:
|
||||
"""Pass the results of ``re.match(pattern, text).{groups(), groupdict()}`` to the
|
||||
callback as a keyword arguments called ``groups`` and ``groupdict``, respectively, if
|
||||
needed.
|
||||
"""
|
||||
optional_args = super().collect_optional_args(dispatcher, update, check_result)
|
||||
if isinstance(check_result, dict):
|
||||
if self.pass_groups:
|
||||
|
|
|
@ -32,6 +32,9 @@ RT = TypeVar('RT')
|
|||
|
||||
class StringCommandHandler(Handler[str]):
|
||||
"""Handler class to handle string commands. Commands are string updates that start with ``/``.
|
||||
The handler will add a ``list`` to the
|
||||
:class:`CallbackContext` named :attr:`CallbackContext.args`. It will contain a list of strings,
|
||||
which is the text following the command split on single whitespace characters.
|
||||
|
||||
Note:
|
||||
This handler is not used to handle Telegram :attr:`telegram.Update`, but strings manually
|
||||
|
@ -122,6 +125,9 @@ class StringCommandHandler(Handler[str]):
|
|||
update: str = None,
|
||||
check_result: Optional[List[str]] = None,
|
||||
) -> Dict[str, object]:
|
||||
"""Provide text after the command to the callback the ``args`` argument as list, split on
|
||||
single whitespaces.
|
||||
"""
|
||||
optional_args = super().collect_optional_args(dispatcher, update, check_result)
|
||||
if self.pass_args:
|
||||
optional_args['args'] = check_result
|
||||
|
@ -134,4 +140,7 @@ class StringCommandHandler(Handler[str]):
|
|||
dispatcher: 'Dispatcher',
|
||||
check_result: Optional[List[str]],
|
||||
) -> None:
|
||||
"""Add text after the command to :attr:`CallbackContext.args` as list, split on single
|
||||
whitespaces.
|
||||
"""
|
||||
context.args = check_result
|
||||
|
|
|
@ -137,6 +137,10 @@ class StringRegexHandler(Handler[str]):
|
|||
update: str = None,
|
||||
check_result: Optional[Match] = None,
|
||||
) -> Dict[str, object]:
|
||||
"""Pass the results of ``re.match(pattern, update).{groups(), groupdict()}`` to the
|
||||
callback as a keyword arguments called ``groups`` and ``groupdict``, respectively, if
|
||||
needed.
|
||||
"""
|
||||
optional_args = super().collect_optional_args(dispatcher, update, check_result)
|
||||
if self.pattern:
|
||||
if self.pass_groups and check_result:
|
||||
|
@ -152,5 +156,8 @@ class StringRegexHandler(Handler[str]):
|
|||
dispatcher: 'Dispatcher',
|
||||
check_result: Optional[Match],
|
||||
) -> None:
|
||||
"""Add the result of ``re.match(pattern, update)`` to :attr:`CallbackContext.matches` as
|
||||
list with one element.
|
||||
"""
|
||||
if self.pattern and check_result:
|
||||
context.matches = [check_result]
|
||||
|
|
|
@ -547,9 +547,9 @@ class Updater:
|
|||
if current_interval == 0:
|
||||
current_interval = 1
|
||||
elif current_interval < 30:
|
||||
current_interval += current_interval / 2
|
||||
elif current_interval > 30:
|
||||
current_interval = 30
|
||||
current_interval *= 1.5
|
||||
else:
|
||||
current_interval = min(30.0, current_interval)
|
||||
return current_interval
|
||||
|
||||
@no_type_check
|
||||
|
@ -598,14 +598,17 @@ class Updater:
|
|||
webhook_url = self._gen_webhook_url(listen, port, url_path)
|
||||
|
||||
# We pass along the cert to the webhook if present.
|
||||
cert_file = open(cert, 'rb') if cert is not None else None
|
||||
self._bootstrap(
|
||||
max_retries=bootstrap_retries,
|
||||
drop_pending_updates=drop_pending_updates,
|
||||
webhook_url=webhook_url,
|
||||
allowed_updates=allowed_updates,
|
||||
cert=open(cert, 'rb') if cert is not None else None,
|
||||
cert=cert_file,
|
||||
ip_address=ip_address,
|
||||
)
|
||||
if cert_file is not None:
|
||||
cert_file.close()
|
||||
|
||||
self.httpd.serve_forever(ready=ready)
|
||||
|
||||
|
@ -681,7 +684,6 @@ class Updater:
|
|||
|
||||
def stop(self) -> None:
|
||||
"""Stops the polling/webhook thread, the dispatcher and the job queue."""
|
||||
|
||||
self.job_queue.stop()
|
||||
with self.__lock:
|
||||
if self.running or self.dispatcher.has_running_threads:
|
||||
|
@ -722,7 +724,7 @@ class Updater:
|
|||
self.__threads = []
|
||||
|
||||
@no_type_check
|
||||
def signal_handler(self, signum, frame) -> None:
|
||||
def _signal_handler(self, signum, frame) -> None:
|
||||
self.is_idle = False
|
||||
if self.running:
|
||||
self.logger.info(
|
||||
|
@ -752,7 +754,7 @@ class Updater:
|
|||
|
||||
"""
|
||||
for sig in stop_signals:
|
||||
signal(sig, self.signal_handler)
|
||||
signal(sig, self._signal_handler)
|
||||
|
||||
self.is_idle = True
|
||||
|
||||
|
|
|
@ -75,7 +75,6 @@ class Promise:
|
|||
|
||||
def run(self) -> None:
|
||||
"""Calls the :attr:`pooled_function` callable."""
|
||||
|
||||
try:
|
||||
self._result = self.pooled_function(*self.args, **self.kwargs)
|
||||
|
||||
|
@ -133,5 +132,6 @@ class Promise:
|
|||
@property
|
||||
def exception(self) -> Optional[Exception]:
|
||||
"""The exception raised by :attr:`pooled_function` or ``None`` if no exception has been
|
||||
raised (yet)."""
|
||||
raised (yet).
|
||||
"""
|
||||
return self._exception
|
||||
|
|
|
@ -91,7 +91,7 @@ class WebhookAppClass(tornado.web.Application):
|
|||
handlers = [(rf"{webhook_path}/?", WebhookHandler, self.shared_objects)] # noqa
|
||||
tornado.web.Application.__init__(self, handlers) # type: ignore
|
||||
|
||||
def log_request(self, handler: tornado.web.RequestHandler) -> None:
|
||||
def log_request(self, handler: tornado.web.RequestHandler) -> None: # skipcq: PTC-W0049
|
||||
pass
|
||||
|
||||
|
||||
|
|
|
@ -96,7 +96,8 @@ class Animation(TelegramObject):
|
|||
|
||||
@classmethod
|
||||
def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['Animation']:
|
||||
data = cls.parse_data(data)
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
|
|
@ -100,7 +100,8 @@ class Audio(TelegramObject):
|
|||
|
||||
@classmethod
|
||||
def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['Audio']:
|
||||
data = cls.parse_data(data)
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
|
|
@ -87,7 +87,8 @@ class Document(TelegramObject):
|
|||
|
||||
@classmethod
|
||||
def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['Document']:
|
||||
data = cls.parse_data(data)
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
|
|
@ -195,4 +195,9 @@ class File(TelegramObject):
|
|||
return buf
|
||||
|
||||
def set_credentials(self, credentials: 'FileCredentials') -> None:
|
||||
"""Sets the passport credentials for the file.
|
||||
|
||||
Args:
|
||||
credentials (:class:`telegram.FileCredentials`): The credentials.
|
||||
"""
|
||||
self._credentials = credentials
|
||||
|
|
|
@ -75,7 +75,7 @@ class InputFile:
|
|||
self.filename = self.mimetype.replace('/', '.')
|
||||
|
||||
@property
|
||||
def field_tuple(self) -> Tuple[str, bytes, str]:
|
||||
def field_tuple(self) -> Tuple[str, bytes, str]: # skipcq: PY-D0003
|
||||
return self.filename, self.input_file_content, self.mimetype
|
||||
|
||||
@staticmethod
|
||||
|
@ -102,10 +102,11 @@ class InputFile:
|
|||
return None
|
||||
|
||||
@staticmethod
|
||||
def is_file(obj: object) -> bool:
|
||||
def is_file(obj: object) -> bool: # skipcq: PY-D0003
|
||||
return hasattr(obj, 'read')
|
||||
|
||||
def to_dict(self) -> Optional[str]:
|
||||
"""See :meth:`telegram.TelegramObject.to_dict`."""
|
||||
if self.attach:
|
||||
return 'attach://' + self.attach
|
||||
return None
|
||||
|
|
|
@ -46,6 +46,7 @@ class InputMedia(TelegramObject):
|
|||
caption_entities: Union[List[MessageEntity], Tuple[MessageEntity, ...], None] = None
|
||||
|
||||
def to_dict(self) -> JSONDict:
|
||||
"""See :meth:`telegram.TelegramObject.to_dict`."""
|
||||
data = super().to_dict()
|
||||
|
||||
if self.caption_entities:
|
||||
|
|
|
@ -106,7 +106,8 @@ class Sticker(TelegramObject):
|
|||
|
||||
@classmethod
|
||||
def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['Sticker']:
|
||||
data = cls.parse_data(data)
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
@ -181,6 +182,7 @@ class StickerSet(TelegramObject):
|
|||
|
||||
@classmethod
|
||||
def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['StickerSet']:
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
if not data:
|
||||
return None
|
||||
|
||||
|
@ -190,6 +192,7 @@ class StickerSet(TelegramObject):
|
|||
return cls(bot=bot, **data)
|
||||
|
||||
def to_dict(self) -> JSONDict:
|
||||
"""See :meth:`telegram.TelegramObject.to_dict`."""
|
||||
data = super().to_dict()
|
||||
|
||||
data['stickers'] = [s.to_dict() for s in data.get('stickers')]
|
||||
|
@ -249,7 +252,8 @@ class MaskPosition(TelegramObject):
|
|||
|
||||
@classmethod
|
||||
def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['MaskPosition']:
|
||||
data = cls.parse_data(data)
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if data is None:
|
||||
return None
|
||||
|
|
|
@ -85,7 +85,8 @@ class Venue(TelegramObject):
|
|||
|
||||
@classmethod
|
||||
def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['Venue']:
|
||||
data = cls.parse_data(data)
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
|
|
@ -97,7 +97,8 @@ class Video(TelegramObject):
|
|||
|
||||
@classmethod
|
||||
def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['Video']:
|
||||
data = cls.parse_data(data)
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
|
|
@ -86,7 +86,8 @@ class VideoNote(TelegramObject):
|
|||
|
||||
@classmethod
|
||||
def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['VideoNote']:
|
||||
data = cls.parse_data(data)
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
|
|
@ -90,7 +90,8 @@ class Game(TelegramObject):
|
|||
|
||||
@classmethod
|
||||
def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['Game']:
|
||||
data = cls.parse_data(data)
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
@ -102,6 +103,7 @@ class Game(TelegramObject):
|
|||
return cls(**data)
|
||||
|
||||
def to_dict(self) -> JSONDict:
|
||||
"""See :meth:`telegram.TelegramObject.to_dict`."""
|
||||
data = super().to_dict()
|
||||
|
||||
data['photo'] = [p.to_dict() for p in self.photo]
|
||||
|
|
|
@ -54,7 +54,8 @@ class GameHighScore(TelegramObject):
|
|||
|
||||
@classmethod
|
||||
def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['GameHighScore']:
|
||||
data = cls.parse_data(data)
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
|
|
@ -52,6 +52,7 @@ class InlineKeyboardMarkup(ReplyMarkup):
|
|||
self._id_attrs = (self.inline_keyboard,)
|
||||
|
||||
def to_dict(self) -> JSONDict:
|
||||
"""See :meth:`telegram.TelegramObject.to_dict`."""
|
||||
data = super().to_dict()
|
||||
|
||||
data['inline_keyboard'] = []
|
||||
|
@ -62,7 +63,8 @@ class InlineKeyboardMarkup(ReplyMarkup):
|
|||
|
||||
@classmethod
|
||||
def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['InlineKeyboardMarkup']:
|
||||
data = cls.parse_data(data)
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
|
|
@ -97,7 +97,8 @@ class InlineQuery(TelegramObject):
|
|||
|
||||
@classmethod
|
||||
def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['InlineQuery']:
|
||||
data = cls.parse_data(data)
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
|
|
@ -54,6 +54,7 @@ class InlineQueryResult(TelegramObject):
|
|||
self._id_attrs = (self.id,)
|
||||
|
||||
def to_dict(self) -> JSONDict:
|
||||
"""See :meth:`telegram.TelegramObject.to_dict`."""
|
||||
data = super().to_dict()
|
||||
|
||||
# pylint: disable=E1101
|
||||
|
|
|
@ -196,6 +196,7 @@ class InputInvoiceMessageContent(InputMessageContent):
|
|||
)
|
||||
|
||||
def to_dict(self) -> JSONDict:
|
||||
"""See :meth:`telegram.TelegramObject.to_dict`."""
|
||||
data = super().to_dict()
|
||||
|
||||
data['prices'] = [price.to_dict() for price in self.prices]
|
||||
|
@ -206,7 +207,8 @@ class InputInvoiceMessageContent(InputMessageContent):
|
|||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: 'Bot'
|
||||
) -> Optional['InputInvoiceMessageContent']:
|
||||
data = cls.parse_data(data)
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
|
|
@ -58,6 +58,7 @@ class InputLocationMessageContent(InputMessageContent):
|
|||
proximity alerts about approaching another chat member, in meters.
|
||||
|
||||
"""
|
||||
|
||||
# fmt: on
|
||||
|
||||
def __init__(
|
||||
|
|
|
@ -77,6 +77,7 @@ class InputTextMessageContent(InputMessageContent):
|
|||
self._id_attrs = (self.message_text,)
|
||||
|
||||
def to_dict(self) -> JSONDict:
|
||||
"""See :meth:`telegram.TelegramObject.to_dict`."""
|
||||
data = super().to_dict()
|
||||
|
||||
if self.entities:
|
||||
|
|
|
@ -331,6 +331,7 @@ class Message(TelegramObject):
|
|||
bot (:class:`telegram.Bot`): Optional. The Bot to use for instance methods.
|
||||
|
||||
"""
|
||||
|
||||
# fmt: on
|
||||
|
||||
_effective_attachment = _UNDEFINED
|
||||
|
@ -506,7 +507,8 @@ class Message(TelegramObject):
|
|||
@property
|
||||
def link(self) -> Optional[str]:
|
||||
""":obj:`str`: Convenience property. If the chat of the message is not
|
||||
a private chat or normal group, returns a t.me link of the message."""
|
||||
a private chat or normal group, returns a t.me link of the message.
|
||||
"""
|
||||
if self.chat.type not in [Chat.PRIVATE, Chat.GROUP]:
|
||||
if self.chat.username:
|
||||
to_link = self.chat.username
|
||||
|
@ -518,7 +520,8 @@ class Message(TelegramObject):
|
|||
|
||||
@classmethod
|
||||
def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['Message']:
|
||||
data = cls.parse_data(data)
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
@ -629,6 +632,7 @@ class Message(TelegramObject):
|
|||
return self.chat.id
|
||||
|
||||
def to_dict(self) -> JSONDict:
|
||||
"""See :meth:`telegram.TelegramObject.to_dict`."""
|
||||
data = super().to_dict()
|
||||
|
||||
# Required
|
||||
|
|
|
@ -17,7 +17,8 @@
|
|||
# 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 change in the Telegram message auto
|
||||
deletion."""
|
||||
deletion.
|
||||
"""
|
||||
|
||||
from typing import Any
|
||||
|
||||
|
|
|
@ -82,7 +82,8 @@ class MessageEntity(TelegramObject):
|
|||
|
||||
@classmethod
|
||||
def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['MessageEntity']:
|
||||
data = cls.parse_data(data)
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
|
|
@ -49,9 +49,7 @@ if TYPE_CHECKING:
|
|||
|
||||
|
||||
class TelegramDecryptionError(TelegramError):
|
||||
"""
|
||||
Something went wrong with decryption.
|
||||
"""
|
||||
"""Something went wrong with decryption."""
|
||||
|
||||
def __init__(self, message: Union[str, Exception]):
|
||||
super().__init__(f"TelegramDecryptionError: {message}")
|
||||
|
@ -181,7 +179,7 @@ class EncryptedCredentials(TelegramObject):
|
|||
try:
|
||||
self._decrypted_secret = self.bot.private_key.decrypt(
|
||||
b64decode(self.secret),
|
||||
OAEP(mgf=MGF1(algorithm=SHA1()), algorithm=SHA1(), label=None),
|
||||
OAEP(mgf=MGF1(algorithm=SHA1()), algorithm=SHA1(), label=None), # skipcq
|
||||
)
|
||||
except ValueError as exception:
|
||||
# If decryption fails raise exception
|
||||
|
@ -223,7 +221,8 @@ class Credentials(TelegramObject):
|
|||
|
||||
@classmethod
|
||||
def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['Credentials']:
|
||||
data = cls.parse_data(data)
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
@ -294,7 +293,8 @@ class SecureData(TelegramObject):
|
|||
|
||||
@classmethod
|
||||
def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['SecureData']:
|
||||
data = cls.parse_data(data)
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
@ -367,7 +367,8 @@ class SecureValue(TelegramObject):
|
|||
|
||||
@classmethod
|
||||
def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['SecureValue']:
|
||||
data = cls.parse_data(data)
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
@ -382,6 +383,7 @@ class SecureValue(TelegramObject):
|
|||
return cls(bot=bot, **data)
|
||||
|
||||
def to_dict(self) -> JSONDict:
|
||||
"""See :meth:`telegram.TelegramObject.to_dict`."""
|
||||
data = super().to_dict()
|
||||
|
||||
data['files'] = [p.to_dict() for p in self.files]
|
||||
|
@ -422,6 +424,7 @@ class DataCredentials(_CredentialsBase):
|
|||
super().__init__(data_hash, secret, **_kwargs)
|
||||
|
||||
def to_dict(self) -> JSONDict:
|
||||
"""See :meth:`telegram.TelegramObject.to_dict`."""
|
||||
data = super().to_dict()
|
||||
|
||||
del data['file_hash']
|
||||
|
@ -448,6 +451,7 @@ class FileCredentials(_CredentialsBase):
|
|||
super().__init__(file_hash, secret, **_kwargs)
|
||||
|
||||
def to_dict(self) -> JSONDict:
|
||||
"""See :meth:`telegram.TelegramObject.to_dict`."""
|
||||
data = super().to_dict()
|
||||
|
||||
del data['data_hash']
|
||||
|
|
|
@ -162,7 +162,8 @@ class EncryptedPassportElement(TelegramObject):
|
|||
|
||||
@classmethod
|
||||
def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['EncryptedPassportElement']:
|
||||
data = cls.parse_data(data)
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
@ -179,6 +180,18 @@ class EncryptedPassportElement(TelegramObject):
|
|||
def de_json_decrypted(
|
||||
cls, data: Optional[JSONDict], bot: 'Bot', credentials: 'Credentials'
|
||||
) -> Optional['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`): The bot associated with this object.
|
||||
credentials (:class:`telegram.FileCredentials`): The credentials
|
||||
|
||||
Returns:
|
||||
:class:`telegram.EncryptedPassportElement`:
|
||||
|
||||
"""
|
||||
if not data:
|
||||
return None
|
||||
|
||||
|
@ -227,6 +240,7 @@ class EncryptedPassportElement(TelegramObject):
|
|||
return cls(bot=bot, **data)
|
||||
|
||||
def to_dict(self) -> JSONDict:
|
||||
"""See :meth:`telegram.TelegramObject.to_dict`."""
|
||||
data = super().to_dict()
|
||||
|
||||
if self.files:
|
||||
|
|
|
@ -67,7 +67,8 @@ class PassportData(TelegramObject):
|
|||
|
||||
@classmethod
|
||||
def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['PassportData']:
|
||||
data = cls.parse_data(data)
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
@ -78,6 +79,7 @@ class PassportData(TelegramObject):
|
|||
return cls(bot=bot, **data)
|
||||
|
||||
def to_dict(self) -> JSONDict:
|
||||
"""See :meth:`telegram.TelegramObject.to_dict`."""
|
||||
data = super().to_dict()
|
||||
|
||||
data['data'] = [e.to_dict() for e in self.data]
|
||||
|
|
|
@ -83,7 +83,19 @@ class PassportFile(TelegramObject):
|
|||
def de_json_decrypted(
|
||||
cls, data: Optional[JSONDict], bot: 'Bot', credentials: 'FileCredentials'
|
||||
) -> Optional['PassportFile']:
|
||||
data = cls.parse_data(data)
|
||||
"""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`): The bot associated with this object.
|
||||
credentials (:class:`telegram.FileCredentials`): The credentials
|
||||
|
||||
Returns:
|
||||
:class:`telegram.PassportFile`:
|
||||
|
||||
"""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
@ -96,6 +108,18 @@ class PassportFile(TelegramObject):
|
|||
def de_list_decrypted(
|
||||
cls, data: Optional[List[JSONDict]], bot: 'Bot', credentials: List['FileCredentials']
|
||||
) -> List[Optional['PassportFile']]:
|
||||
"""Variant of :meth:`telegram.TelegramObject.de_list` that also takes into account
|
||||
passport credentials.
|
||||
|
||||
Args:
|
||||
data (Dict[:obj:`str`, ...]): The JSON data.
|
||||
bot (:class:`telegram.Bot`): The bot associated with these objects.
|
||||
credentials (:class:`telegram.FileCredentials`): The credentials
|
||||
|
||||
Returns:
|
||||
List[:class:`telegram.PassportFile`]:
|
||||
|
||||
"""
|
||||
if not data:
|
||||
return []
|
||||
|
||||
|
|
|
@ -66,7 +66,8 @@ class OrderInfo(TelegramObject):
|
|||
|
||||
@classmethod
|
||||
def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['OrderInfo']:
|
||||
data = cls.parse_data(data)
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return cls()
|
||||
|
|
|
@ -93,7 +93,8 @@ class PreCheckoutQuery(TelegramObject):
|
|||
|
||||
@classmethod
|
||||
def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['PreCheckoutQuery']:
|
||||
data = cls.parse_data(data)
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
|
|
@ -60,6 +60,7 @@ class ShippingOption(TelegramObject):
|
|||
self._id_attrs = (self.id,)
|
||||
|
||||
def to_dict(self) -> JSONDict:
|
||||
"""See :meth:`telegram.TelegramObject.to_dict`."""
|
||||
data = super().to_dict()
|
||||
|
||||
data['prices'] = [p.to_dict() for p in self.prices]
|
||||
|
|
|
@ -74,7 +74,8 @@ class ShippingQuery(TelegramObject):
|
|||
|
||||
@classmethod
|
||||
def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['ShippingQuery']:
|
||||
data = cls.parse_data(data)
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
|
|
@ -85,7 +85,8 @@ class SuccessfulPayment(TelegramObject):
|
|||
|
||||
@classmethod
|
||||
def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['SuccessfulPayment']:
|
||||
data = cls.parse_data(data)
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
|
|
@ -87,7 +87,8 @@ class PollAnswer(TelegramObject):
|
|||
|
||||
@classmethod
|
||||
def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['PollAnswer']:
|
||||
data = cls.parse_data(data)
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
@ -180,7 +181,8 @@ class Poll(TelegramObject):
|
|||
|
||||
@classmethod
|
||||
def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['Poll']:
|
||||
data = cls.parse_data(data)
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
@ -192,6 +194,7 @@ class Poll(TelegramObject):
|
|||
return cls(**data)
|
||||
|
||||
def to_dict(self) -> JSONDict:
|
||||
"""See :meth:`telegram.TelegramObject.to_dict`."""
|
||||
data = super().to_dict()
|
||||
|
||||
data['options'] = [x.to_dict() for x in self.options]
|
||||
|
|
|
@ -55,7 +55,8 @@ class ProximityAlertTriggered(TelegramObject):
|
|||
|
||||
@classmethod
|
||||
def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['ProximityAlertTriggered']:
|
||||
data = cls.parse_data(data)
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
|
|
@ -92,6 +92,7 @@ class ReplyKeyboardMarkup(ReplyMarkup):
|
|||
self._id_attrs = (self.keyboard,)
|
||||
|
||||
def to_dict(self) -> JSONDict:
|
||||
"""See :meth:`telegram.TelegramObject.to_dict`."""
|
||||
data = super().to_dict()
|
||||
|
||||
data['keyboard'] = []
|
||||
|
|
|
@ -342,7 +342,8 @@ class Update(TelegramObject):
|
|||
|
||||
@classmethod
|
||||
def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['Update']:
|
||||
data = cls.parse_data(data)
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
|
|
@ -128,7 +128,8 @@ class User(TelegramObject):
|
|||
@property
|
||||
def name(self) -> str:
|
||||
""":obj:`str`: Convenience property. If available, returns the user's :attr:`username`
|
||||
prefixed with "@". If :attr:`username` is not available, returns :attr:`full_name`."""
|
||||
prefixed with "@". If :attr:`username` is not available, returns :attr:`full_name`.
|
||||
"""
|
||||
if self.username:
|
||||
return f'@{self.username}'
|
||||
return self.full_name
|
||||
|
@ -136,8 +137,8 @@ class User(TelegramObject):
|
|||
@property
|
||||
def full_name(self) -> str:
|
||||
""":obj:`str`: Convenience property. The user's :attr:`first_name`, followed by (if
|
||||
available) :attr:`last_name`."""
|
||||
|
||||
available) :attr:`last_name`.
|
||||
"""
|
||||
if self.last_name:
|
||||
return f'{self.first_name} {self.last_name}'
|
||||
return self.first_name
|
||||
|
@ -145,8 +146,8 @@ class User(TelegramObject):
|
|||
@property
|
||||
def link(self) -> Optional[str]:
|
||||
""":obj:`str`: Convenience property. If :attr:`username` is available, returns a t.me link
|
||||
of the user."""
|
||||
|
||||
of the user.
|
||||
"""
|
||||
if self.username:
|
||||
return f"https://t.me/{self.username}"
|
||||
return None
|
||||
|
@ -167,7 +168,6 @@ class User(TelegramObject):
|
|||
:meth:`telegram.Bot.get_user_profile_photos`.
|
||||
|
||||
"""
|
||||
|
||||
return self.bot.get_user_profile_photos(
|
||||
user_id=self.id,
|
||||
offset=offset,
|
||||
|
|
|
@ -53,7 +53,8 @@ class UserProfilePhotos(TelegramObject):
|
|||
|
||||
@classmethod
|
||||
def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['UserProfilePhotos']:
|
||||
data = cls.parse_data(data)
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
@ -63,6 +64,7 @@ class UserProfilePhotos(TelegramObject):
|
|||
return cls(**data)
|
||||
|
||||
def to_dict(self) -> JSONDict:
|
||||
"""See :meth:`telegram.TelegramObject.to_dict`."""
|
||||
data = super().to_dict()
|
||||
|
||||
data['photos'] = []
|
||||
|
|
|
@ -18,32 +18,9 @@
|
|||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
"""This module facilitates the deprecation of functions."""
|
||||
|
||||
import warnings
|
||||
from typing import Callable, TypeVar
|
||||
|
||||
RT = TypeVar('RT')
|
||||
|
||||
|
||||
# We use our own DeprecationWarning since they are muted by default and "UserWarning" makes it
|
||||
# seem like it's the user that issued the warning
|
||||
# We name it something else so that you don't get confused when you attempt to suppress it
|
||||
class TelegramDeprecationWarning(Warning):
|
||||
pass
|
||||
|
||||
|
||||
def warn_deprecate_obj(old: str, new: str, stacklevel: int = 3) -> None:
|
||||
warnings.warn(
|
||||
f'{old} is being deprecated, please use {new} from now on.',
|
||||
category=TelegramDeprecationWarning,
|
||||
stacklevel=stacklevel,
|
||||
)
|
||||
|
||||
|
||||
def deprecate(func: Callable[..., RT], old: str, new: str) -> Callable[..., RT]:
|
||||
"""Warn users invoking old to switch to the new function."""
|
||||
|
||||
def wrapped(*args: object, **kwargs: object) -> RT:
|
||||
warn_deprecate_obj(old, new)
|
||||
return func(*args, **kwargs)
|
||||
|
||||
return wrapped
|
||||
"""Custom warning class for deprecations in this library."""
|
||||
|
|
|
@ -186,9 +186,7 @@ def _datetime_to_float_timestamp(dt_obj: dtm.datetime) -> float:
|
|||
|
||||
|
||||
def _localize(datetime: dtm.datetime, tzinfo: dtm.tzinfo) -> dtm.datetime:
|
||||
"""
|
||||
Localize the datetime, where UTC is handled depending on whether pytz is available or not
|
||||
"""
|
||||
"""Localize the datetime, where UTC is handled depending on whether pytz is available or not"""
|
||||
if tzinfo is DTM_UTC:
|
||||
return datetime.replace(tzinfo=DTM_UTC)
|
||||
return tzinfo.localize(datetime) # type: ignore[attr-defined]
|
||||
|
@ -250,7 +248,6 @@ def to_float_timestamp(
|
|||
ValueError: If ``t`` is a :obj:`datetime.datetime` and :obj:`reference_timestamp` is not
|
||||
:obj:`None`.
|
||||
"""
|
||||
|
||||
if reference_timestamp is None:
|
||||
reference_timestamp = time.time()
|
||||
elif isinstance(time_object, dtm.datetime):
|
||||
|
@ -369,7 +366,6 @@ def effective_message_type(entity: Union['Message', 'Update']) -> Optional[str]:
|
|||
:obj:`str`: One of ``Message.MESSAGE_TYPES``
|
||||
|
||||
"""
|
||||
|
||||
# Importing on file-level yields cyclic Import Errors
|
||||
from telegram import Message, Update # pylint: disable=C0415
|
||||
|
||||
|
@ -482,7 +478,6 @@ def decode_user_chat_data_from_json(data: str) -> DefaultDict[int, Dict[object,
|
|||
Returns:
|
||||
:obj:`dict`: The user/chat_data defaultdict after decoding
|
||||
"""
|
||||
|
||||
tmp: DefaultDict[int, Dict[object, object]] = defaultdict(dict)
|
||||
decoded_data = json.loads(data)
|
||||
for user, user_data in decoded_data.items():
|
||||
|
|
|
@ -17,7 +17,8 @@
|
|||
# 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 the :class:`telegram.ext.utils.promise.Promise` class for backwards
|
||||
compatibility."""
|
||||
compatibility.
|
||||
"""
|
||||
import warnings
|
||||
|
||||
import telegram.ext.utils.promise as promise
|
||||
|
|
|
@ -73,7 +73,7 @@ from telegram.utils.types import JSONDict
|
|||
|
||||
|
||||
def _render_part(self: RequestField, name: str, value: str) -> str: # pylint: disable=W0613
|
||||
"""
|
||||
r"""
|
||||
Monkey patch urllib3.urllib3.fields.RequestField to make it *not* support RFC2231 compliant
|
||||
Content-Disposition headers since telegram servers don't understand it. Instead just escape
|
||||
\\ and " and replace any \n and \r with a space.
|
||||
|
@ -195,6 +195,7 @@ class Request:
|
|||
return self._con_pool_size
|
||||
|
||||
def stop(self) -> None:
|
||||
"""Performs cleanup on shutdown."""
|
||||
self._con_pool.clear() # type: ignore
|
||||
|
||||
@staticmethod
|
||||
|
@ -205,7 +206,6 @@ class Request:
|
|||
dict: A JSON parsed as Python dict with results - on error this dict will be empty.
|
||||
|
||||
"""
|
||||
|
||||
decoded_s = json_data.decode('utf-8', 'replace')
|
||||
try:
|
||||
data = json.loads(decoded_s)
|
||||
|
|
|
@ -17,7 +17,8 @@
|
|||
# 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 the :class:`telegram.ext.utils.webhookhandler.WebhookHandler` class for
|
||||
backwards compatibility."""
|
||||
backwards compatibility.
|
||||
"""
|
||||
import warnings
|
||||
|
||||
import telegram.ext.utils.webhookhandler as webhook_handler
|
||||
|
|
|
@ -38,7 +38,7 @@ class VoiceChatStarted(TelegramObject):
|
|||
.. versionadded:: 13.4
|
||||
"""
|
||||
|
||||
def __init__(self, **_kwargs: Any):
|
||||
def __init__(self, **_kwargs: Any): # skipcq: PTC-W0049
|
||||
pass
|
||||
|
||||
|
||||
|
@ -100,7 +100,8 @@ class VoiceChatParticipantsInvited(TelegramObject):
|
|||
def de_json(
|
||||
cls, data: Optional[JSONDict], bot: 'Bot'
|
||||
) -> Optional['VoiceChatParticipantsInvited']:
|
||||
data = cls.parse_data(data)
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
@ -109,6 +110,7 @@ class VoiceChatParticipantsInvited(TelegramObject):
|
|||
return cls(**data)
|
||||
|
||||
def to_dict(self) -> JSONDict:
|
||||
"""See :meth:`telegram.TelegramObject.to_dict`."""
|
||||
data = super().to_dict()
|
||||
|
||||
data["users"] = [u.to_dict() for u in self.users]
|
||||
|
@ -139,7 +141,8 @@ class VoiceChatScheduled(TelegramObject):
|
|||
|
||||
@classmethod
|
||||
def de_json(cls, data: Optional[JSONDict], bot: 'Bot') -> Optional['VoiceChatScheduled']:
|
||||
data = cls.parse_data(data)
|
||||
"""See :meth:`telegram.TelegramObject.de_json`."""
|
||||
data = cls._parse_data(data)
|
||||
|
||||
if not data:
|
||||
return None
|
||||
|
@ -149,6 +152,7 @@ class VoiceChatScheduled(TelegramObject):
|
|||
return cls(**data, bot=bot)
|
||||
|
||||
def to_dict(self) -> JSONDict:
|
||||
"""See :meth:`telegram.TelegramObject.to_dict`."""
|
||||
data = super().to_dict()
|
||||
|
||||
# Required
|
||||
|
|
|
@ -97,10 +97,9 @@ def default_bot(request, bot_info):
|
|||
default_bot = DEFAULT_BOTS.get(defaults)
|
||||
if default_bot:
|
||||
return default_bot
|
||||
else:
|
||||
default_bot = make_bot(bot_info, **{'defaults': defaults})
|
||||
DEFAULT_BOTS[defaults] = default_bot
|
||||
return default_bot
|
||||
default_bot = make_bot(bot_info, **{'defaults': defaults})
|
||||
DEFAULT_BOTS[defaults] = default_bot
|
||||
return default_bot
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
|
@ -109,10 +108,9 @@ def tz_bot(timezone, bot_info):
|
|||
default_bot = DEFAULT_BOTS.get(defaults)
|
||||
if default_bot:
|
||||
return default_bot
|
||||
else:
|
||||
default_bot = make_bot(bot_info, **{'defaults': defaults})
|
||||
DEFAULT_BOTS[defaults] = default_bot
|
||||
return default_bot
|
||||
default_bot = make_bot(bot_info, **{'defaults': defaults})
|
||||
DEFAULT_BOTS[defaults] = default_bot
|
||||
return default_bot
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
|
|
|
@ -36,7 +36,7 @@ def terminal_summary_wrapper(original, plugin_name):
|
|||
@pytest.mark.trylast
|
||||
def pytest_configure(config):
|
||||
for hookimpl in config.pluginmanager.hook.pytest_terminal_summary._nonwrappers:
|
||||
if hookimpl.plugin_name in fold_plugins.keys():
|
||||
if hookimpl.plugin_name in fold_plugins:
|
||||
hookimpl.function = terminal_summary_wrapper(hookimpl.function, hookimpl.plugin_name)
|
||||
|
||||
|
||||
|
|
|
@ -260,7 +260,6 @@ class TestAnimation:
|
|||
animation = Animation.de_json(json_dict, bot)
|
||||
assert animation.file_id == self.animation_file_id
|
||||
assert animation.file_unique_id == self.animation_file_unique_id
|
||||
assert animation.thumb == animation.thumb
|
||||
assert animation.file_name == self.file_name
|
||||
assert animation.mime_type == self.mime_type
|
||||
assert animation.file_size == self.file_size
|
||||
|
|
|
@ -81,7 +81,7 @@ def chat_permissions():
|
|||
def inline_results_callback(page=None):
|
||||
if not page:
|
||||
return [InlineQueryResultArticle(i, str(i), None) for i in range(1, 254)]
|
||||
elif page <= 5:
|
||||
if page <= 5:
|
||||
return [
|
||||
InlineQueryResultArticle(i, str(i), None)
|
||||
for i in range(page * 5 + 1, (page + 1) * 5 + 1)
|
||||
|
@ -217,11 +217,13 @@ class TestBot:
|
|||
|
||||
@flaky(3, 1)
|
||||
def test_forward_message(self, bot, chat_id, message):
|
||||
message = bot.forward_message(chat_id, from_chat_id=chat_id, message_id=message.message_id)
|
||||
forward_message = bot.forward_message(
|
||||
chat_id, from_chat_id=chat_id, message_id=message.message_id
|
||||
)
|
||||
|
||||
assert message.text == message.text
|
||||
assert message.forward_from.username == message.from_user.username
|
||||
assert isinstance(message.forward_date, dtm.datetime)
|
||||
assert forward_message.text == message.text
|
||||
assert forward_message.forward_from.username == message.from_user.username
|
||||
assert isinstance(forward_message.forward_date, dtm.datetime)
|
||||
|
||||
@flaky(3, 1)
|
||||
def test_delete_message(self, bot, chat_id):
|
||||
|
@ -787,7 +789,7 @@ class TestBot:
|
|||
def make_assertion(url, data, *args, **kwargs):
|
||||
results = data['results']
|
||||
length_matches = len(results) == num_results
|
||||
ids_match = all([int(res['id']) == id_offset + i for i, res in enumerate(results)])
|
||||
ids_match = all(int(res['id']) == id_offset + i for i, res in enumerate(results))
|
||||
next_offset_matches = data['next_offset'] == str(expected_next_offset)
|
||||
return length_matches and ids_match and next_offset_matches
|
||||
|
||||
|
@ -800,7 +802,7 @@ class TestBot:
|
|||
def make_assertion(url, data, *args, **kwargs):
|
||||
results = data['results']
|
||||
length_matches = len(results) == MAX_INLINE_QUERY_RESULTS
|
||||
ids_match = all([int(res['id']) == 1 + i for i, res in enumerate(results)])
|
||||
ids_match = all(int(res['id']) == 1 + i for i, res in enumerate(results))
|
||||
next_offset_matches = data['next_offset'] == '1'
|
||||
return length_matches and ids_match and next_offset_matches
|
||||
|
||||
|
@ -813,7 +815,7 @@ class TestBot:
|
|||
def make_assertion(url, data, *args, **kwargs):
|
||||
results = data['results']
|
||||
length_matches = len(results) == 30
|
||||
ids_match = all([int(res['id']) == 1 + i for i, res in enumerate(results)])
|
||||
ids_match = all(int(res['id']) == 1 + i for i, res in enumerate(results))
|
||||
next_offset_matches = data['next_offset'] == ''
|
||||
return length_matches and ids_match and next_offset_matches
|
||||
|
||||
|
@ -826,7 +828,7 @@ class TestBot:
|
|||
def make_assertion(url, data, *args, **kwargs):
|
||||
results = data['results']
|
||||
length = len(results) == 5
|
||||
ids = all([int(res['id']) == 6 + i for i, res in enumerate(results)])
|
||||
ids = all(int(res['id']) == 6 + i for i, res in enumerate(results))
|
||||
next_offset = data['next_offset'] == '2'
|
||||
return length and ids and next_offset
|
||||
|
||||
|
@ -1910,14 +1912,13 @@ class TestBot:
|
|||
reply_to_message_id=reply_to_message.message_id,
|
||||
)
|
||||
return
|
||||
else:
|
||||
returned = default_bot.copy_message(
|
||||
chat_id,
|
||||
from_chat_id=chat_id,
|
||||
message_id=media_message.message_id,
|
||||
caption="<b>Test</b>",
|
||||
reply_to_message_id=reply_to_message.message_id,
|
||||
)
|
||||
returned = default_bot.copy_message(
|
||||
chat_id,
|
||||
from_chat_id=chat_id,
|
||||
message_id=media_message.message_id,
|
||||
caption="<b>Test</b>",
|
||||
reply_to_message_id=reply_to_message.message_id,
|
||||
)
|
||||
# we send a temp message which replies to the returned message id in order to get a
|
||||
# message object
|
||||
temp_message = default_bot.send_message(
|
||||
|
|
|
@ -102,13 +102,13 @@ class BaseTest:
|
|||
|
||||
def callback_context_regex1(self, update, context):
|
||||
if context.matches:
|
||||
types = all([type(res) == self.SRE_TYPE for res in context.matches])
|
||||
types = all(type(res) is self.SRE_TYPE for res in context.matches)
|
||||
num = len(context.matches) == 1
|
||||
self.test_flag = types and num
|
||||
|
||||
def callback_context_regex2(self, update, context):
|
||||
if context.matches:
|
||||
types = all([type(res) == self.SRE_TYPE for res in context.matches])
|
||||
types = all(type(res) is self.SRE_TYPE for res in context.matches)
|
||||
num = len(context.matches) == 2
|
||||
self.test_flag = types and num
|
||||
|
||||
|
|
|
@ -45,6 +45,5 @@ class TestConstants:
|
|||
with pytest.raises(
|
||||
BadRequest,
|
||||
match="Media_caption_too_long",
|
||||
):
|
||||
with open('tests/data/telegram.png', 'rb') as f:
|
||||
bot.send_photo(photo=f, caption=bad_caption, chat_id=chat_id)
|
||||
), open('tests/data/telegram.png', 'rb') as f:
|
||||
bot.send_photo(photo=f, caption=bad_caption, chat_id=chat_id)
|
||||
|
|
|
@ -72,8 +72,7 @@ def raise_dphs(func):
|
|||
result = func(self, *args, **kwargs)
|
||||
if self.raise_dp_handler_stop:
|
||||
raise DispatcherHandlerStop(result)
|
||||
else:
|
||||
return result
|
||||
return result
|
||||
|
||||
return decorator
|
||||
|
||||
|
@ -168,8 +167,7 @@ class TestConversationHandler:
|
|||
def start(self, bot, update):
|
||||
if isinstance(update, Update):
|
||||
return self._set_state(update, self.THIRSTY)
|
||||
else:
|
||||
return self._set_state(bot, self.THIRSTY)
|
||||
return self._set_state(bot, self.THIRSTY)
|
||||
|
||||
@raise_dphs
|
||||
def end(self, bot, update):
|
||||
|
@ -187,8 +185,7 @@ class TestConversationHandler:
|
|||
def brew(self, bot, update):
|
||||
if isinstance(update, Update):
|
||||
return self._set_state(update, self.BREWING)
|
||||
else:
|
||||
return self._set_state(bot, self.BREWING)
|
||||
return self._set_state(bot, self.BREWING)
|
||||
|
||||
@raise_dphs
|
||||
def drink(self, bot, update):
|
||||
|
|
|
@ -683,7 +683,7 @@ class TestDispatcher:
|
|||
assert self.received == 'Unauthorized.'
|
||||
|
||||
def test_sensible_worker_thread_names(self, dp2):
|
||||
thread_names = [thread.name for thread in getattr(dp2, '_Dispatcher__async_threads')]
|
||||
thread_names = [thread.name for thread in dp2._Dispatcher__async_threads]
|
||||
print(thread_names)
|
||||
for thread_name in thread_names:
|
||||
assert thread_name.startswith(f"Bot:{dp2.bot.id}:worker:")
|
||||
|
|
|
@ -273,9 +273,8 @@ class TestDocument:
|
|||
|
||||
@flaky(3, 1)
|
||||
def test_error_send_empty_file(self, bot, chat_id):
|
||||
with open(os.devnull, 'rb') as f:
|
||||
with pytest.raises(TelegramError):
|
||||
bot.send_document(chat_id=chat_id, document=f)
|
||||
with open(os.devnull, 'rb') as f, pytest.raises(TelegramError):
|
||||
bot.send_document(chat_id=chat_id, document=f)
|
||||
|
||||
@flaky(3, 1)
|
||||
def test_error_send_empty_file_id(self, bot, chat_id):
|
||||
|
|
|
@ -133,19 +133,19 @@ class TestFilters:
|
|||
assert isinstance(result, dict)
|
||||
matches = result['matches']
|
||||
assert isinstance(matches, list)
|
||||
assert all([type(res) == SRE_TYPE for res in matches])
|
||||
assert all(type(res) is SRE_TYPE for res in matches)
|
||||
result = (Filters.regex('deep') | Filters.regex(r'linked param'))(update)
|
||||
assert result
|
||||
assert isinstance(result, dict)
|
||||
matches = result['matches']
|
||||
assert isinstance(matches, list)
|
||||
assert all([type(res) == SRE_TYPE for res in matches])
|
||||
assert all(type(res) is SRE_TYPE for res in matches)
|
||||
result = (Filters.regex('not int') | Filters.regex(r'linked param'))(update)
|
||||
assert result
|
||||
assert isinstance(result, dict)
|
||||
matches = result['matches']
|
||||
assert isinstance(matches, list)
|
||||
assert all([type(res) == SRE_TYPE for res in matches])
|
||||
assert all(type(res) is SRE_TYPE for res in matches)
|
||||
result = (Filters.regex('not int') & Filters.regex(r'linked param'))(update)
|
||||
assert not result
|
||||
|
||||
|
@ -158,19 +158,19 @@ class TestFilters:
|
|||
assert isinstance(result, dict)
|
||||
matches = result['matches']
|
||||
assert isinstance(matches, list)
|
||||
assert all([type(res) == SRE_TYPE for res in matches])
|
||||
assert all(type(res) is SRE_TYPE for res in matches)
|
||||
result = (Filters.regex(r'linked param') & Filters.command)(update)
|
||||
assert result
|
||||
assert isinstance(result, dict)
|
||||
matches = result['matches']
|
||||
assert isinstance(matches, list)
|
||||
assert all([type(res) == SRE_TYPE for res in matches])
|
||||
assert all(type(res) is SRE_TYPE for res in matches)
|
||||
result = (Filters.regex(r'linked param') | Filters.command)(update)
|
||||
assert result
|
||||
assert isinstance(result, dict)
|
||||
matches = result['matches']
|
||||
assert isinstance(matches, list)
|
||||
assert all([type(res) == SRE_TYPE for res in matches])
|
||||
assert all(type(res) is SRE_TYPE for res in matches)
|
||||
# Should not give a match since it's a or filter and it short circuits
|
||||
result = (Filters.command | Filters.regex(r'linked param'))(update)
|
||||
assert result is True
|
||||
|
@ -178,91 +178,91 @@ class TestFilters:
|
|||
def test_regex_complex_merges(self, update):
|
||||
SRE_TYPE = type(re.match("", ""))
|
||||
update.message.text = 'test it out'
|
||||
filter = Filters.regex('test') & (
|
||||
test_filter = Filters.regex('test') & (
|
||||
(Filters.status_update | Filters.forwarded) | Filters.regex('out')
|
||||
)
|
||||
result = filter(update)
|
||||
result = test_filter(update)
|
||||
assert result
|
||||
assert isinstance(result, dict)
|
||||
matches = result['matches']
|
||||
assert isinstance(matches, list)
|
||||
assert len(matches) == 2
|
||||
assert all([type(res) == SRE_TYPE for res in matches])
|
||||
assert all(type(res) is SRE_TYPE for res in matches)
|
||||
update.message.forward_date = datetime.datetime.utcnow()
|
||||
result = filter(update)
|
||||
result = test_filter(update)
|
||||
assert result
|
||||
assert isinstance(result, dict)
|
||||
matches = result['matches']
|
||||
assert isinstance(matches, list)
|
||||
assert all([type(res) == SRE_TYPE for res in matches])
|
||||
assert all(type(res) is SRE_TYPE for res in matches)
|
||||
update.message.text = 'test it'
|
||||
result = filter(update)
|
||||
result = test_filter(update)
|
||||
assert result
|
||||
assert isinstance(result, dict)
|
||||
matches = result['matches']
|
||||
assert isinstance(matches, list)
|
||||
assert all([type(res) == SRE_TYPE for res in matches])
|
||||
assert all(type(res) is SRE_TYPE for res in matches)
|
||||
update.message.forward_date = None
|
||||
result = filter(update)
|
||||
result = test_filter(update)
|
||||
assert not result
|
||||
update.message.text = 'test it out'
|
||||
result = filter(update)
|
||||
result = test_filter(update)
|
||||
assert result
|
||||
assert isinstance(result, dict)
|
||||
matches = result['matches']
|
||||
assert isinstance(matches, list)
|
||||
assert all([type(res) == SRE_TYPE for res in matches])
|
||||
assert all(type(res) is SRE_TYPE for res in matches)
|
||||
update.message.pinned_message = True
|
||||
result = filter(update)
|
||||
result = test_filter(update)
|
||||
assert result
|
||||
assert isinstance(result, dict)
|
||||
matches = result['matches']
|
||||
assert isinstance(matches, list)
|
||||
assert all([type(res) == SRE_TYPE for res in matches])
|
||||
assert all(type(res) is SRE_TYPE for res in matches)
|
||||
update.message.text = 'it out'
|
||||
result = filter(update)
|
||||
result = test_filter(update)
|
||||
assert not result
|
||||
|
||||
update.message.text = 'test it out'
|
||||
update.message.forward_date = None
|
||||
update.message.pinned_message = None
|
||||
filter = (Filters.regex('test') | Filters.command) & (
|
||||
test_filter = (Filters.regex('test') | Filters.command) & (
|
||||
Filters.regex('it') | Filters.status_update
|
||||
)
|
||||
result = filter(update)
|
||||
result = test_filter(update)
|
||||
assert result
|
||||
assert isinstance(result, dict)
|
||||
matches = result['matches']
|
||||
assert isinstance(matches, list)
|
||||
assert len(matches) == 2
|
||||
assert all([type(res) == SRE_TYPE for res in matches])
|
||||
assert all(type(res) is SRE_TYPE for res in matches)
|
||||
update.message.text = 'test'
|
||||
result = filter(update)
|
||||
result = test_filter(update)
|
||||
assert not result
|
||||
update.message.pinned_message = True
|
||||
result = filter(update)
|
||||
result = test_filter(update)
|
||||
assert result
|
||||
assert isinstance(result, dict)
|
||||
matches = result['matches']
|
||||
assert isinstance(matches, list)
|
||||
assert len(matches) == 1
|
||||
assert all([type(res) == SRE_TYPE for res in matches])
|
||||
assert all(type(res) is SRE_TYPE for res in matches)
|
||||
update.message.text = 'nothing'
|
||||
result = filter(update)
|
||||
result = test_filter(update)
|
||||
assert not result
|
||||
update.message.text = '/start'
|
||||
update.message.entities = [MessageEntity(MessageEntity.BOT_COMMAND, 0, 6)]
|
||||
result = filter(update)
|
||||
result = test_filter(update)
|
||||
assert result
|
||||
assert isinstance(result, bool)
|
||||
update.message.text = '/start it'
|
||||
result = filter(update)
|
||||
result = test_filter(update)
|
||||
assert result
|
||||
assert isinstance(result, dict)
|
||||
matches = result['matches']
|
||||
assert isinstance(matches, list)
|
||||
assert len(matches) == 1
|
||||
assert all([type(res) == SRE_TYPE for res in matches])
|
||||
assert all(type(res) is SRE_TYPE for res in matches)
|
||||
|
||||
def test_regex_inverted(self, update):
|
||||
update.message.text = '/start deep-linked param'
|
||||
|
@ -336,13 +336,13 @@ class TestFilters:
|
|||
assert isinstance(result, dict)
|
||||
matches = result['matches']
|
||||
assert isinstance(matches, list)
|
||||
assert all([type(res) == SRE_TYPE for res in matches])
|
||||
assert all(type(res) is SRE_TYPE for res in matches)
|
||||
result = (Filters.caption_regex('deep') | Filters.caption_regex(r'linked param'))(update)
|
||||
assert result
|
||||
assert isinstance(result, dict)
|
||||
matches = result['matches']
|
||||
assert isinstance(matches, list)
|
||||
assert all([type(res) == SRE_TYPE for res in matches])
|
||||
assert all(type(res) is SRE_TYPE for res in matches)
|
||||
result = (Filters.caption_regex('not int') | Filters.caption_regex(r'linked param'))(
|
||||
update
|
||||
)
|
||||
|
@ -350,7 +350,7 @@ class TestFilters:
|
|||
assert isinstance(result, dict)
|
||||
matches = result['matches']
|
||||
assert isinstance(matches, list)
|
||||
assert all([type(res) == SRE_TYPE for res in matches])
|
||||
assert all(type(res) is SRE_TYPE for res in matches)
|
||||
result = (Filters.caption_regex('not int') & Filters.caption_regex(r'linked param'))(
|
||||
update
|
||||
)
|
||||
|
@ -365,19 +365,19 @@ class TestFilters:
|
|||
assert isinstance(result, dict)
|
||||
matches = result['matches']
|
||||
assert isinstance(matches, list)
|
||||
assert all([type(res) == SRE_TYPE for res in matches])
|
||||
assert all(type(res) is SRE_TYPE for res in matches)
|
||||
result = (Filters.caption_regex(r'linked param') & Filters.command)(update)
|
||||
assert result
|
||||
assert isinstance(result, dict)
|
||||
matches = result['matches']
|
||||
assert isinstance(matches, list)
|
||||
assert all([type(res) == SRE_TYPE for res in matches])
|
||||
assert all(type(res) is SRE_TYPE for res in matches)
|
||||
result = (Filters.caption_regex(r'linked param') | Filters.command)(update)
|
||||
assert result
|
||||
assert isinstance(result, dict)
|
||||
matches = result['matches']
|
||||
assert isinstance(matches, list)
|
||||
assert all([type(res) == SRE_TYPE for res in matches])
|
||||
assert all(type(res) is SRE_TYPE for res in matches)
|
||||
# Should not give a match since it's a or filter and it short circuits
|
||||
result = (Filters.command | Filters.caption_regex(r'linked param'))(update)
|
||||
assert result is True
|
||||
|
@ -385,130 +385,130 @@ class TestFilters:
|
|||
def test_caption_regex_complex_merges(self, update):
|
||||
SRE_TYPE = type(re.match("", ""))
|
||||
update.message.caption = 'test it out'
|
||||
filter = Filters.caption_regex('test') & (
|
||||
test_filter = Filters.caption_regex('test') & (
|
||||
(Filters.status_update | Filters.forwarded) | Filters.caption_regex('out')
|
||||
)
|
||||
result = filter(update)
|
||||
result = test_filter(update)
|
||||
assert result
|
||||
assert isinstance(result, dict)
|
||||
matches = result['matches']
|
||||
assert isinstance(matches, list)
|
||||
assert len(matches) == 2
|
||||
assert all([type(res) == SRE_TYPE for res in matches])
|
||||
assert all(type(res) is SRE_TYPE for res in matches)
|
||||
update.message.forward_date = datetime.datetime.utcnow()
|
||||
result = filter(update)
|
||||
result = test_filter(update)
|
||||
assert result
|
||||
assert isinstance(result, dict)
|
||||
matches = result['matches']
|
||||
assert isinstance(matches, list)
|
||||
assert all([type(res) == SRE_TYPE for res in matches])
|
||||
assert all(type(res) is SRE_TYPE for res in matches)
|
||||
update.message.caption = 'test it'
|
||||
result = filter(update)
|
||||
result = test_filter(update)
|
||||
assert result
|
||||
assert isinstance(result, dict)
|
||||
matches = result['matches']
|
||||
assert isinstance(matches, list)
|
||||
assert all([type(res) == SRE_TYPE for res in matches])
|
||||
assert all(type(res) is SRE_TYPE for res in matches)
|
||||
update.message.forward_date = None
|
||||
result = filter(update)
|
||||
result = test_filter(update)
|
||||
assert not result
|
||||
update.message.caption = 'test it out'
|
||||
result = filter(update)
|
||||
result = test_filter(update)
|
||||
assert result
|
||||
assert isinstance(result, dict)
|
||||
matches = result['matches']
|
||||
assert isinstance(matches, list)
|
||||
assert all([type(res) == SRE_TYPE for res in matches])
|
||||
assert all(type(res) is SRE_TYPE for res in matches)
|
||||
update.message.pinned_message = True
|
||||
result = filter(update)
|
||||
result = test_filter(update)
|
||||
assert result
|
||||
assert isinstance(result, dict)
|
||||
matches = result['matches']
|
||||
assert isinstance(matches, list)
|
||||
assert all([type(res) == SRE_TYPE for res in matches])
|
||||
assert all(type(res) is SRE_TYPE for res in matches)
|
||||
update.message.caption = 'it out'
|
||||
result = filter(update)
|
||||
result = test_filter(update)
|
||||
assert not result
|
||||
|
||||
update.message.caption = 'test it out'
|
||||
update.message.forward_date = None
|
||||
update.message.pinned_message = None
|
||||
filter = (Filters.caption_regex('test') | Filters.command) & (
|
||||
test_filter = (Filters.caption_regex('test') | Filters.command) & (
|
||||
Filters.caption_regex('it') | Filters.status_update
|
||||
)
|
||||
result = filter(update)
|
||||
result = test_filter(update)
|
||||
assert result
|
||||
assert isinstance(result, dict)
|
||||
matches = result['matches']
|
||||
assert isinstance(matches, list)
|
||||
assert len(matches) == 2
|
||||
assert all([type(res) == SRE_TYPE for res in matches])
|
||||
assert all(type(res) is SRE_TYPE for res in matches)
|
||||
update.message.caption = 'test'
|
||||
result = filter(update)
|
||||
result = test_filter(update)
|
||||
assert not result
|
||||
update.message.pinned_message = True
|
||||
result = filter(update)
|
||||
result = test_filter(update)
|
||||
assert result
|
||||
assert isinstance(result, dict)
|
||||
matches = result['matches']
|
||||
assert isinstance(matches, list)
|
||||
assert len(matches) == 1
|
||||
assert all([type(res) == SRE_TYPE for res in matches])
|
||||
assert all(type(res) is SRE_TYPE for res in matches)
|
||||
update.message.caption = 'nothing'
|
||||
result = filter(update)
|
||||
result = test_filter(update)
|
||||
assert not result
|
||||
update.message.caption = '/start'
|
||||
update.message.entities = [MessageEntity(MessageEntity.BOT_COMMAND, 0, 6)]
|
||||
result = filter(update)
|
||||
result = test_filter(update)
|
||||
assert result
|
||||
assert isinstance(result, bool)
|
||||
update.message.caption = '/start it'
|
||||
result = filter(update)
|
||||
result = test_filter(update)
|
||||
assert result
|
||||
assert isinstance(result, dict)
|
||||
matches = result['matches']
|
||||
assert isinstance(matches, list)
|
||||
assert len(matches) == 1
|
||||
assert all([type(res) == SRE_TYPE for res in matches])
|
||||
assert all(type(res) is SRE_TYPE for res in matches)
|
||||
|
||||
def test_caption_regex_inverted(self, update):
|
||||
update.message.caption = '/start deep-linked param'
|
||||
update.message.entities = [MessageEntity(MessageEntity.BOT_COMMAND, 0, 5)]
|
||||
filter = ~Filters.caption_regex(r'deep-linked param')
|
||||
result = filter(update)
|
||||
test_filter = ~Filters.caption_regex(r'deep-linked param')
|
||||
result = test_filter(update)
|
||||
assert not result
|
||||
update.message.caption = 'not it'
|
||||
result = filter(update)
|
||||
result = test_filter(update)
|
||||
assert result
|
||||
assert isinstance(result, bool)
|
||||
|
||||
filter = ~Filters.caption_regex('linked') & Filters.command
|
||||
test_filter = ~Filters.caption_regex('linked') & Filters.command
|
||||
update.message.caption = "it's linked"
|
||||
result = filter(update)
|
||||
result = test_filter(update)
|
||||
assert not result
|
||||
update.message.caption = '/start'
|
||||
update.message.entities = [MessageEntity(MessageEntity.BOT_COMMAND, 0, 6)]
|
||||
result = filter(update)
|
||||
result = test_filter(update)
|
||||
assert result
|
||||
update.message.caption = '/linked'
|
||||
result = filter(update)
|
||||
result = test_filter(update)
|
||||
assert not result
|
||||
|
||||
filter = ~Filters.caption_regex('linked') | Filters.command
|
||||
test_filter = ~Filters.caption_regex('linked') | Filters.command
|
||||
update.message.caption = "it's linked"
|
||||
update.message.entities = []
|
||||
result = filter(update)
|
||||
result = test_filter(update)
|
||||
assert not result
|
||||
update.message.caption = '/start linked'
|
||||
update.message.entities = [MessageEntity(MessageEntity.BOT_COMMAND, 0, 6)]
|
||||
result = filter(update)
|
||||
result = test_filter(update)
|
||||
assert result
|
||||
update.message.caption = '/start'
|
||||
result = filter(update)
|
||||
result = test_filter(update)
|
||||
assert result
|
||||
update.message.caption = 'nothig'
|
||||
update.message.entities = []
|
||||
result = filter(update)
|
||||
result = test_filter(update)
|
||||
assert result
|
||||
|
||||
def test_filters_reply(self, update):
|
||||
|
|
|
@ -406,8 +406,8 @@ class TestSendMediaGroup:
|
|||
messages = bot.send_media_group(chat_id, media_group)
|
||||
assert isinstance(messages, list)
|
||||
assert len(messages) == 3
|
||||
assert all([isinstance(mes, Message) for mes in messages])
|
||||
assert all([mes.media_group_id == messages[0].media_group_id for mes in messages])
|
||||
assert all(isinstance(mes, Message) for mes in messages)
|
||||
assert all(mes.media_group_id == messages[0].media_group_id for mes in messages)
|
||||
assert all(mes.caption == f'photo {idx+1}' for idx, mes in enumerate(messages))
|
||||
assert all(
|
||||
mes.caption_entities == [MessageEntity(MessageEntity.BOLD, 0, 5)] for mes in messages
|
||||
|
@ -421,8 +421,8 @@ class TestSendMediaGroup:
|
|||
)
|
||||
assert isinstance(messages, list)
|
||||
assert len(messages) == 3
|
||||
assert all([isinstance(mes, Message) for mes in messages])
|
||||
assert all([mes.media_group_id == messages[0].media_group_id for mes in messages])
|
||||
assert all(isinstance(mes, Message) for mes in messages)
|
||||
assert all(mes.media_group_id == messages[0].media_group_id for mes in messages)
|
||||
assert all(mes.caption == f'photo {idx+1}' for idx, mes in enumerate(messages))
|
||||
assert all(
|
||||
mes.caption_entities == [MessageEntity(MessageEntity.BOLD, 0, 5)] for mes in messages
|
||||
|
@ -491,8 +491,8 @@ class TestSendMediaGroup:
|
|||
|
||||
assert isinstance(messages, list)
|
||||
assert len(messages) == 3
|
||||
assert all([isinstance(mes, Message) for mes in messages])
|
||||
assert all([mes.media_group_id == messages[0].media_group_id for mes in messages])
|
||||
assert all(isinstance(mes, Message) for mes in messages)
|
||||
assert all(mes.media_group_id == messages[0].media_group_id for mes in messages)
|
||||
|
||||
@flaky(3, 1)
|
||||
@pytest.mark.parametrize(
|
||||
|
|
|
@ -185,10 +185,8 @@ class TestInvoice:
|
|||
|
||||
def test_send_object_as_provider_data(self, monkeypatch, bot, chat_id, provider_token):
|
||||
def test(url, data, **kwargs):
|
||||
return (
|
||||
data['provider_data'] == '{"test_data": 123456789}' # Depends if using
|
||||
or data['provider_data'] == '{"test_data":123456789}'
|
||||
) # ujson or not
|
||||
# depends on whether we're using ujson
|
||||
return data['provider_data'] in ['{"test_data": 123456789}', '{"test_data":123456789}']
|
||||
|
||||
monkeypatch.setattr(bot.request, 'post', test)
|
||||
|
||||
|
|
|
@ -606,26 +606,26 @@ class TestMessage:
|
|||
def test_chat_id(self, message):
|
||||
assert message.chat_id == message.chat.id
|
||||
|
||||
@pytest.mark.parametrize('type', argvalues=[Chat.SUPERGROUP, Chat.CHANNEL])
|
||||
def test_link_with_username(self, message, type):
|
||||
@pytest.mark.parametrize('_type', argvalues=[Chat.SUPERGROUP, Chat.CHANNEL])
|
||||
def test_link_with_username(self, message, _type):
|
||||
message.chat.username = 'username'
|
||||
message.chat.type = type
|
||||
message.chat.type = _type
|
||||
assert message.link == f'https://t.me/{message.chat.username}/{message.message_id}'
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
'type, id', argvalues=[(Chat.CHANNEL, -1003), (Chat.SUPERGROUP, -1003)]
|
||||
'_type, _id', argvalues=[(Chat.CHANNEL, -1003), (Chat.SUPERGROUP, -1003)]
|
||||
)
|
||||
def test_link_with_id(self, message, type, id):
|
||||
def test_link_with_id(self, message, _type, _id):
|
||||
message.chat.username = None
|
||||
message.chat.id = id
|
||||
message.chat.type = type
|
||||
message.chat.id = _id
|
||||
message.chat.type = _type
|
||||
# The leading - for group ids/ -100 for supergroup ids isn't supposed to be in the link
|
||||
assert message.link == f'https://t.me/c/{3}/{message.message_id}'
|
||||
|
||||
@pytest.mark.parametrize('id, username', argvalues=[(None, 'username'), (-3, None)])
|
||||
def test_link_private_chats(self, message, id, username):
|
||||
@pytest.mark.parametrize('_id, username', argvalues=[(None, 'username'), (-3, None)])
|
||||
def test_link_private_chats(self, message, _id, username):
|
||||
message.chat.type = Chat.PRIVATE
|
||||
message.chat.id = id
|
||||
message.chat.id = _id
|
||||
message.chat.username = username
|
||||
assert message.link is None
|
||||
message.chat.type = Chat.GROUP
|
||||
|
|
|
@ -121,13 +121,13 @@ class TestMessageHandler:
|
|||
|
||||
def callback_context_regex1(self, update, context):
|
||||
if context.matches:
|
||||
types = all([type(res) == self.SRE_TYPE for res in context.matches])
|
||||
types = all(type(res) is self.SRE_TYPE for res in context.matches)
|
||||
num = len(context.matches) == 1
|
||||
self.test_flag = types and num
|
||||
|
||||
def callback_context_regex2(self, update, context):
|
||||
if context.matches:
|
||||
types = all([type(res) == self.SRE_TYPE for res in context.matches])
|
||||
types = all(type(res) is self.SRE_TYPE for res in context.matches)
|
||||
num = len(context.matches) == 2
|
||||
self.test_flag = types and num
|
||||
|
||||
|
|
|
@ -121,9 +121,9 @@ def check_object(h4):
|
|||
name.startswith('InlineQueryResult') or name.startswith('InputMedia')
|
||||
) and field == 'type':
|
||||
continue
|
||||
elif name.startswith('PassportElementError') and field == 'source':
|
||||
continue
|
||||
elif field == 'remove_keyboard':
|
||||
elif (
|
||||
name.startswith('PassportElementError') and field == 'source'
|
||||
) or field == 'remove_keyboard':
|
||||
continue
|
||||
|
||||
param = sig.parameters.get(field)
|
||||
|
@ -135,7 +135,7 @@ def check_object(h4):
|
|||
ignored = IGNORED_PARAMETERS.copy()
|
||||
if name == 'InputFile':
|
||||
return
|
||||
elif name == 'InlineQueryResult':
|
||||
if name == 'InlineQueryResult':
|
||||
ignored |= {'id', 'type'}
|
||||
elif name == 'User':
|
||||
ignored |= {'type'} # TODO: Deprecation
|
||||
|
|
|
@ -1195,9 +1195,6 @@ class TestPickelPersistence:
|
|||
h2 = MessageHandler(None, second, pass_user_data=True, pass_chat_data=True)
|
||||
dp.add_handler(h1)
|
||||
dp.process_update(update)
|
||||
del dp
|
||||
del u
|
||||
del pickle_persistence
|
||||
pickle_persistence_2 = PicklePersistence(
|
||||
filename='pickletest',
|
||||
store_user_data=True,
|
||||
|
@ -1218,10 +1215,7 @@ class TestPickelPersistence:
|
|||
dp.user_data[4242424242]['my_test'] = 'Working!'
|
||||
dp.chat_data[-4242424242]['my_test2'] = 'Working2!'
|
||||
dp.bot_data['test'] = 'Working3!'
|
||||
u.signal_handler(signal.SIGINT, None)
|
||||
del dp
|
||||
del u
|
||||
del pickle_persistence
|
||||
u._signal_handler(signal.SIGINT, None)
|
||||
pickle_persistence_2 = PicklePersistence(
|
||||
filename='pickletest',
|
||||
store_user_data=True,
|
||||
|
@ -1240,10 +1234,7 @@ class TestPickelPersistence:
|
|||
dp.user_data[4242424242]['my_test'] = 'Working!'
|
||||
dp.chat_data[-4242424242]['my_test2'] = 'Working2!'
|
||||
dp.bot_data['my_test3'] = 'Working3!'
|
||||
u.signal_handler(signal.SIGINT, None)
|
||||
del dp
|
||||
del u
|
||||
del pickle_persistence_only_bot
|
||||
u._signal_handler(signal.SIGINT, None)
|
||||
pickle_persistence_2 = PicklePersistence(
|
||||
filename='pickletest',
|
||||
store_user_data=False,
|
||||
|
@ -1262,10 +1253,7 @@ class TestPickelPersistence:
|
|||
u.running = True
|
||||
dp.user_data[4242424242]['my_test'] = 'Working!'
|
||||
dp.chat_data[-4242424242]['my_test2'] = 'Working2!'
|
||||
u.signal_handler(signal.SIGINT, None)
|
||||
del dp
|
||||
del u
|
||||
del pickle_persistence_only_chat
|
||||
u._signal_handler(signal.SIGINT, None)
|
||||
pickle_persistence_2 = PicklePersistence(
|
||||
filename='pickletest',
|
||||
store_user_data=False,
|
||||
|
@ -1284,10 +1272,7 @@ class TestPickelPersistence:
|
|||
u.running = True
|
||||
dp.user_data[4242424242]['my_test'] = 'Working!'
|
||||
dp.chat_data[-4242424242]['my_test2'] = 'Working2!'
|
||||
u.signal_handler(signal.SIGINT, None)
|
||||
del dp
|
||||
del u
|
||||
del pickle_persistence_only_user
|
||||
u._signal_handler(signal.SIGINT, None)
|
||||
pickle_persistence_2 = PicklePersistence(
|
||||
filename='pickletest',
|
||||
store_user_data=True,
|
||||
|
@ -1310,10 +1295,10 @@ class TestPickelPersistence:
|
|||
|
||||
start = CommandHandler('start', start)
|
||||
|
||||
def next(update, context):
|
||||
def next_callback(update, context):
|
||||
return NEXT2
|
||||
|
||||
next = MessageHandler(None, next)
|
||||
next_handler = MessageHandler(None, next_callback)
|
||||
|
||||
def next2(update, context):
|
||||
return ConversationHandler.END
|
||||
|
@ -1321,7 +1306,7 @@ class TestPickelPersistence:
|
|||
next2 = MessageHandler(None, next2)
|
||||
|
||||
ch = ConversationHandler(
|
||||
[start], {NEXT: [next], NEXT2: [next2]}, [], name='name2', persistent=True
|
||||
[start], {NEXT: [next_handler], NEXT2: [next2]}, [], name='name2', persistent=True
|
||||
)
|
||||
dp.add_handler(ch)
|
||||
assert ch.conversations[ch._get_key(update)] == 1
|
||||
|
@ -1345,10 +1330,10 @@ class TestPickelPersistence:
|
|||
|
||||
start = CommandHandler('start', start)
|
||||
|
||||
def next(update, context):
|
||||
def next_callback(update, context):
|
||||
return NEXT2
|
||||
|
||||
next = MessageHandler(None, next)
|
||||
next_handler = MessageHandler(None, next_callback)
|
||||
|
||||
def next2(update, context):
|
||||
return ConversationHandler.END
|
||||
|
@ -1356,7 +1341,7 @@ class TestPickelPersistence:
|
|||
next2 = MessageHandler(None, next2)
|
||||
|
||||
nested_ch = ConversationHandler(
|
||||
[next],
|
||||
[next_handler],
|
||||
{NEXT2: [next2]},
|
||||
[],
|
||||
name='name3',
|
||||
|
@ -1612,12 +1597,9 @@ class TestDictPersistence:
|
|||
h2 = MessageHandler(None, second, pass_user_data=True, pass_chat_data=True)
|
||||
dp.add_handler(h1)
|
||||
dp.process_update(update)
|
||||
del dp
|
||||
del u
|
||||
user_data = dict_persistence.user_data_json
|
||||
chat_data = dict_persistence.chat_data_json
|
||||
bot_data = dict_persistence.bot_data_json
|
||||
del dict_persistence
|
||||
dict_persistence_2 = DictPersistence(
|
||||
user_data_json=user_data, chat_data_json=chat_data, bot_data_json=bot_data
|
||||
)
|
||||
|
@ -1638,10 +1620,10 @@ class TestDictPersistence:
|
|||
|
||||
start = CommandHandler('start', start)
|
||||
|
||||
def next(update, context):
|
||||
def next_callback(update, context):
|
||||
return NEXT2
|
||||
|
||||
next = MessageHandler(None, next)
|
||||
next_handler = MessageHandler(None, next_callback)
|
||||
|
||||
def next2(update, context):
|
||||
return ConversationHandler.END
|
||||
|
@ -1649,7 +1631,7 @@ class TestDictPersistence:
|
|||
next2 = MessageHandler(None, next2)
|
||||
|
||||
ch = ConversationHandler(
|
||||
[start], {NEXT: [next], NEXT2: [next2]}, [], name='name2', persistent=True
|
||||
[start], {NEXT: [next_handler], NEXT2: [next2]}, [], name='name2', persistent=True
|
||||
)
|
||||
dp.add_handler(ch)
|
||||
assert ch.conversations[ch._get_key(update)] == 1
|
||||
|
@ -1672,10 +1654,10 @@ class TestDictPersistence:
|
|||
|
||||
start = CommandHandler('start', start)
|
||||
|
||||
def next(update, context):
|
||||
def next_callback(update, context):
|
||||
return NEXT2
|
||||
|
||||
next = MessageHandler(None, next)
|
||||
next_handler = MessageHandler(None, next_callback)
|
||||
|
||||
def next2(update, context):
|
||||
return ConversationHandler.END
|
||||
|
@ -1683,7 +1665,7 @@ class TestDictPersistence:
|
|||
next2 = MessageHandler(None, next2)
|
||||
|
||||
nested_ch = ConversationHandler(
|
||||
[next],
|
||||
[next_handler],
|
||||
{NEXT2: [next2]},
|
||||
[],
|
||||
name='name3',
|
||||
|
|
|
@ -102,10 +102,10 @@ class TestUpdate:
|
|||
|
||||
# Make sure only one thing in the update (other than update_id) is not None
|
||||
i = 0
|
||||
for type in all_types:
|
||||
if getattr(update, type) is not None:
|
||||
for _type in all_types:
|
||||
if getattr(update, _type) is not None:
|
||||
i += 1
|
||||
assert getattr(update, type) == paramdict[type]
|
||||
assert getattr(update, _type) == paramdict[_type]
|
||||
assert i == 1
|
||||
|
||||
def test_update_de_json_empty(self, bot):
|
||||
|
@ -118,9 +118,9 @@ class TestUpdate:
|
|||
|
||||
assert isinstance(update_dict, dict)
|
||||
assert update_dict['update_id'] == update.update_id
|
||||
for type in all_types:
|
||||
if getattr(update, type) is not None:
|
||||
assert update_dict[type] == getattr(update, type).to_dict()
|
||||
for _type in all_types:
|
||||
if getattr(update, _type) is not None:
|
||||
assert update_dict[_type] == getattr(update, _type).to_dict()
|
||||
|
||||
def test_effective_chat(self, update):
|
||||
# Test that it's sometimes None per docstring
|
||||
|
|
Loading…
Reference in a new issue