mirror of
https://github.com/python-telegram-bot/python-telegram-bot.git
synced 2024-11-21 22:56:38 +01:00
parent
5c500fb6fd
commit
8f0031e9c8
28 changed files with 68 additions and 1097 deletions
|
@ -1,9 +0,0 @@
|
|||
:github_url: https://github.com/python-telegram-bot/python-telegram-bot/blob/master/telegram/ext/messagequeue.py
|
||||
|
||||
telegram.ext.DelayQueue
|
||||
=======================
|
||||
|
||||
.. autoclass:: telegram.ext.DelayQueue
|
||||
:members:
|
||||
:show-inheritance:
|
||||
:special-members:
|
|
@ -1,9 +0,0 @@
|
|||
:github_url: https://github.com/python-telegram-bot/python-telegram-bot/blob/master/telegram/ext/messagequeue.py
|
||||
|
||||
telegram.ext.MessageQueue
|
||||
=========================
|
||||
|
||||
.. autoclass:: telegram.ext.MessageQueue
|
||||
:members:
|
||||
:show-inheritance:
|
||||
:special-members:
|
|
@ -10,8 +10,6 @@ telegram.ext package
|
|||
telegram.ext.callbackcontext
|
||||
telegram.ext.job
|
||||
telegram.ext.jobqueue
|
||||
telegram.ext.messagequeue
|
||||
telegram.ext.delayqueue
|
||||
telegram.ext.contexttypes
|
||||
telegram.ext.defaults
|
||||
|
||||
|
|
|
@ -148,6 +148,11 @@ class Bot(TelegramObject):
|
|||
incorporated into PTB. However, this is not guaranteed to work, i.e. it will fail for
|
||||
passing files.
|
||||
|
||||
.. versionchanged:: 14.0
|
||||
* Removed the deprecated methods ``kick_chat_member``, ``kickChatMember``,
|
||||
``get_chat_members_count`` and ``getChatMembersCount``.
|
||||
* Removed the deprecated property ``commands``.
|
||||
|
||||
Args:
|
||||
token (:obj:`str`): Bot's unique authentication.
|
||||
base_url (:obj:`str`, optional): Telegram Bot API service URL.
|
||||
|
@ -173,7 +178,6 @@ class Bot(TelegramObject):
|
|||
'private_key',
|
||||
'defaults',
|
||||
'_bot',
|
||||
'_commands',
|
||||
'_request',
|
||||
'logger',
|
||||
)
|
||||
|
@ -209,7 +213,6 @@ class Bot(TelegramObject):
|
|||
self.base_url = str(base_url) + str(self.token)
|
||||
self.base_file_url = str(base_file_url) + str(self.token)
|
||||
self._bot: Optional[User] = None
|
||||
self._commands: Optional[List[BotCommand]] = None
|
||||
self._request = request or Request()
|
||||
self.private_key = None
|
||||
self.logger = logging.getLogger(__name__)
|
||||
|
@ -395,26 +398,6 @@ class Bot(TelegramObject):
|
|||
""":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 as available in the default scope.
|
||||
|
||||
.. deprecated:: 13.7
|
||||
This property has been deprecated since there can be different commands available for
|
||||
different scopes.
|
||||
"""
|
||||
warnings.warn(
|
||||
"Bot.commands has been deprecated since there can be different command "
|
||||
"lists for different scopes.",
|
||||
TelegramDeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
|
||||
if self._commands is None:
|
||||
self._commands = self.get_my_commands()
|
||||
return self._commands
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
""":obj:`str`: Bot's @username."""
|
||||
|
@ -2424,36 +2407,6 @@ class Bot(TelegramObject):
|
|||
|
||||
return File.de_json(result, self) # type: ignore[return-value, arg-type]
|
||||
|
||||
@log
|
||||
def kick_chat_member(
|
||||
self,
|
||||
chat_id: Union[str, int],
|
||||
user_id: Union[str, int],
|
||||
timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
until_date: Union[int, datetime] = None,
|
||||
api_kwargs: JSONDict = None,
|
||||
revoke_messages: bool = None,
|
||||
) -> bool:
|
||||
"""
|
||||
Deprecated, use :func:`~telegram.Bot.ban_chat_member` instead.
|
||||
|
||||
.. deprecated:: 13.7
|
||||
|
||||
"""
|
||||
warnings.warn(
|
||||
'`bot.kick_chat_member` is deprecated. Use `bot.ban_chat_member` instead.',
|
||||
TelegramDeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
return self.ban_chat_member(
|
||||
chat_id=chat_id,
|
||||
user_id=user_id,
|
||||
timeout=timeout,
|
||||
until_date=until_date,
|
||||
api_kwargs=api_kwargs,
|
||||
revoke_messages=revoke_messages,
|
||||
)
|
||||
|
||||
@log
|
||||
def ban_chat_member(
|
||||
self,
|
||||
|
@ -3284,26 +3237,6 @@ class Bot(TelegramObject):
|
|||
|
||||
return ChatMember.de_list(result, self) # type: ignore
|
||||
|
||||
@log
|
||||
def get_chat_members_count(
|
||||
self,
|
||||
chat_id: Union[str, int],
|
||||
timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
api_kwargs: JSONDict = None,
|
||||
) -> int:
|
||||
"""
|
||||
Deprecated, use :func:`~telegram.Bot.get_chat_member_count` instead.
|
||||
|
||||
.. deprecated:: 13.7
|
||||
"""
|
||||
warnings.warn(
|
||||
'`bot.get_chat_members_count` is deprecated. '
|
||||
'Use `bot.get_chat_member_count` instead.',
|
||||
TelegramDeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
return self.get_chat_member_count(chat_id=chat_id, timeout=timeout, api_kwargs=api_kwargs)
|
||||
|
||||
@log
|
||||
def get_chat_member_count(
|
||||
self,
|
||||
|
@ -5420,10 +5353,6 @@ class Bot(TelegramObject):
|
|||
|
||||
result = self._post('getMyCommands', data, timeout=timeout, api_kwargs=api_kwargs)
|
||||
|
||||
if (scope is None or scope.type == scope.DEFAULT) and language_code is None:
|
||||
self._commands = BotCommand.de_list(result, self) # type: ignore[assignment,arg-type]
|
||||
return self._commands # type: ignore[return-value]
|
||||
|
||||
return BotCommand.de_list(result, self) # type: ignore[return-value,arg-type]
|
||||
|
||||
@log
|
||||
|
@ -5480,11 +5409,6 @@ class Bot(TelegramObject):
|
|||
|
||||
result = self._post('setMyCommands', data, timeout=timeout, api_kwargs=api_kwargs)
|
||||
|
||||
# Set commands only for default scope. No need to check for outcome.
|
||||
# If request failed, we won't come this far
|
||||
if (scope is None or scope.type == scope.DEFAULT) and language_code is None:
|
||||
self._commands = cmds
|
||||
|
||||
return result # type: ignore[return-value]
|
||||
|
||||
@log
|
||||
|
@ -5532,9 +5456,6 @@ class Bot(TelegramObject):
|
|||
|
||||
result = self._post('deleteMyCommands', data, timeout=timeout, api_kwargs=api_kwargs)
|
||||
|
||||
if (scope is None or scope.type == scope.DEFAULT) and language_code is None:
|
||||
self._commands = []
|
||||
|
||||
return result # type: ignore[return-value]
|
||||
|
||||
@log
|
||||
|
@ -5736,8 +5657,6 @@ class Bot(TelegramObject):
|
|||
"""Alias for :meth:`ban_chat_member`"""
|
||||
banChatSenderChat = ban_chat_sender_chat
|
||||
"""Alias for :meth:`ban_chat_sender_chat`"""
|
||||
kickChatMember = kick_chat_member
|
||||
"""Alias for :meth:`kick_chat_member`"""
|
||||
unbanChatMember = unban_chat_member
|
||||
"""Alias for :meth:`unban_chat_member`"""
|
||||
unbanChatSenderChat = unban_chat_sender_chat
|
||||
|
@ -5772,8 +5691,6 @@ class Bot(TelegramObject):
|
|||
"""Alias for :meth:`delete_chat_sticker_set`"""
|
||||
getChatMemberCount = get_chat_member_count
|
||||
"""Alias for :meth:`get_chat_member_count`"""
|
||||
getChatMembersCount = get_chat_members_count
|
||||
"""Alias for :meth:`get_chat_members_count`"""
|
||||
getWebhookInfo = get_webhook_info
|
||||
"""Alias for :meth:`get_webhook_info`"""
|
||||
setGameScore = set_game_score
|
||||
|
|
|
@ -18,13 +18,11 @@
|
|||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
"""This module contains an object that represents a Telegram Chat."""
|
||||
import warnings
|
||||
from datetime import datetime
|
||||
from typing import TYPE_CHECKING, List, Optional, ClassVar, Union, Tuple, Any
|
||||
|
||||
from telegram import ChatPhoto, TelegramObject, constants
|
||||
from telegram.utils.types import JSONDict, FileInput, ODVInput, DVInput
|
||||
from telegram.utils.deprecate import TelegramDeprecationWarning
|
||||
|
||||
from .chatpermissions import ChatPermissions
|
||||
from .chatlocation import ChatLocation
|
||||
|
@ -65,6 +63,9 @@ class Chat(TelegramObject):
|
|||
Objects of this class are comparable in terms of equality. Two objects of this class are
|
||||
considered equal, if their :attr:`id` is equal.
|
||||
|
||||
.. versionchanged:: 14.0
|
||||
Removed the deprecated methods ``kick_member`` and ``get_members_count``.
|
||||
|
||||
Args:
|
||||
id (:obj:`int`): Unique identifier for this chat. This number may be greater than 32 bits
|
||||
and some programming languages may have difficulty/silent defects in interpreting it.
|
||||
|
@ -341,25 +342,6 @@ class Chat(TelegramObject):
|
|||
api_kwargs=api_kwargs,
|
||||
)
|
||||
|
||||
def get_members_count(
|
||||
self, timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None
|
||||
) -> int:
|
||||
"""
|
||||
Deprecated, use :func:`~telegram.Chat.get_member_count` instead.
|
||||
|
||||
.. deprecated:: 13.7
|
||||
"""
|
||||
warnings.warn(
|
||||
'`Chat.get_members_count` is deprecated. Use `Chat.get_member_count` instead.',
|
||||
TelegramDeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
|
||||
return self.get_member_count(
|
||||
timeout=timeout,
|
||||
api_kwargs=api_kwargs,
|
||||
)
|
||||
|
||||
def get_member_count(
|
||||
self, timeout: ODVInput[float] = DEFAULT_NONE, api_kwargs: JSONDict = None
|
||||
) -> int:
|
||||
|
@ -402,33 +384,6 @@ class Chat(TelegramObject):
|
|||
api_kwargs=api_kwargs,
|
||||
)
|
||||
|
||||
def kick_member(
|
||||
self,
|
||||
user_id: Union[str, int],
|
||||
timeout: ODVInput[float] = DEFAULT_NONE,
|
||||
until_date: Union[int, datetime] = None,
|
||||
api_kwargs: JSONDict = None,
|
||||
revoke_messages: bool = None,
|
||||
) -> bool:
|
||||
"""
|
||||
Deprecated, use :func:`~telegram.Chat.ban_member` instead.
|
||||
|
||||
.. deprecated:: 13.7
|
||||
"""
|
||||
warnings.warn(
|
||||
'`Chat.kick_member` is deprecated. Use `Chat.ban_member` instead.',
|
||||
TelegramDeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
|
||||
return self.ban_member(
|
||||
user_id=user_id,
|
||||
timeout=timeout,
|
||||
until_date=until_date,
|
||||
api_kwargs=api_kwargs,
|
||||
revoke_messages=revoke_messages,
|
||||
)
|
||||
|
||||
def ban_member(
|
||||
self,
|
||||
user_id: Union[str, int],
|
||||
|
|
|
@ -23,17 +23,15 @@ from telegram import constants
|
|||
|
||||
|
||||
class ChatAction:
|
||||
"""Helper class to provide constants for different chat actions."""
|
||||
"""Helper class to provide constants for different chat actions.
|
||||
|
||||
.. versionchanged:: 14.0
|
||||
Removed the deprecated constants ``RECORD_AUDIO`` and ``UPLOAD_AUDIO``.
|
||||
"""
|
||||
|
||||
__slots__ = ()
|
||||
FIND_LOCATION: ClassVar[str] = constants.CHATACTION_FIND_LOCATION
|
||||
""":const:`telegram.constants.CHATACTION_FIND_LOCATION`"""
|
||||
RECORD_AUDIO: ClassVar[str] = constants.CHATACTION_RECORD_AUDIO
|
||||
""":const:`telegram.constants.CHATACTION_RECORD_AUDIO`
|
||||
|
||||
.. deprecated:: 13.5
|
||||
Deprecated by Telegram. Use :attr:`RECORD_VOICE` instead.
|
||||
"""
|
||||
RECORD_VOICE: ClassVar[str] = constants.CHATACTION_RECORD_VOICE
|
||||
""":const:`telegram.constants.CHATACTION_RECORD_VOICE`
|
||||
|
||||
|
@ -45,12 +43,6 @@ class ChatAction:
|
|||
""":const:`telegram.constants.CHATACTION_RECORD_VIDEO_NOTE`"""
|
||||
TYPING: ClassVar[str] = constants.CHATACTION_TYPING
|
||||
""":const:`telegram.constants.CHATACTION_TYPING`"""
|
||||
UPLOAD_AUDIO: ClassVar[str] = constants.CHATACTION_UPLOAD_AUDIO
|
||||
""":const:`telegram.constants.CHATACTION_UPLOAD_AUDIO`
|
||||
|
||||
.. deprecated:: 13.5
|
||||
Deprecated by Telegram. Use :attr:`UPLOAD_VOICE` instead.
|
||||
"""
|
||||
UPLOAD_VOICE: ClassVar[str] = constants.CHATACTION_UPLOAD_VOICE
|
||||
""":const:`telegram.constants.CHATACTION_UPLOAD_VOICE`
|
||||
|
||||
|
|
|
@ -70,12 +70,11 @@ Attributes:
|
|||
|
||||
:class:`telegram.ChatAction`:
|
||||
|
||||
.. versionchanged:: 14.0
|
||||
Removed the deprecated constants ``CHATACTION_RECORD_AUDIO`` and ``CHATACTION_UPLOAD_AUDIO``.
|
||||
|
||||
Attributes:
|
||||
CHATACTION_FIND_LOCATION (:obj:`str`): ``'find_location'``
|
||||
CHATACTION_RECORD_AUDIO (:obj:`str`): ``'record_audio'``
|
||||
|
||||
.. deprecated:: 13.5
|
||||
Deprecated by Telegram. Use :const:`CHATACTION_RECORD_VOICE` instead.
|
||||
CHATACTION_RECORD_VOICE (:obj:`str`): ``'record_voice'``
|
||||
|
||||
.. versionadded:: 13.5
|
||||
|
@ -83,9 +82,6 @@ Attributes:
|
|||
CHATACTION_RECORD_VIDEO_NOTE (:obj:`str`): ``'record_video_note'``
|
||||
CHATACTION_TYPING (:obj:`str`): ``'typing'``
|
||||
CHATACTION_UPLOAD_AUDIO (:obj:`str`): ``'upload_audio'``
|
||||
|
||||
.. deprecated:: 13.5
|
||||
Deprecated by Telegram. Use :const:`CHATACTION_UPLOAD_VOICE` instead.
|
||||
CHATACTION_UPLOAD_VOICE (:obj:`str`): ``'upload_voice'``
|
||||
|
||||
.. versionadded:: 13.5
|
||||
|
@ -274,12 +270,10 @@ CHAT_SUPERGROUP: str = 'supergroup'
|
|||
CHAT_CHANNEL: str = 'channel'
|
||||
|
||||
CHATACTION_FIND_LOCATION: str = 'find_location'
|
||||
CHATACTION_RECORD_AUDIO: str = 'record_audio'
|
||||
CHATACTION_RECORD_VOICE: str = 'record_voice'
|
||||
CHATACTION_RECORD_VIDEO: str = 'record_video'
|
||||
CHATACTION_RECORD_VIDEO_NOTE: str = 'record_video_note'
|
||||
CHATACTION_TYPING: str = 'typing'
|
||||
CHATACTION_UPLOAD_AUDIO: str = 'upload_audio'
|
||||
CHATACTION_UPLOAD_VOICE: str = 'upload_voice'
|
||||
CHATACTION_UPLOAD_DOCUMENT: str = 'upload_document'
|
||||
CHATACTION_CHOOSE_STICKER: str = 'choose_sticker'
|
||||
|
|
|
@ -25,7 +25,7 @@ from .dictpersistence import DictPersistence
|
|||
from .handler import Handler
|
||||
from .callbackcontext import CallbackContext
|
||||
from .contexttypes import ContextTypes
|
||||
from .dispatcher import Dispatcher, DispatcherHandlerStop, run_async
|
||||
from .dispatcher import Dispatcher, DispatcherHandlerStop
|
||||
|
||||
from .jobqueue import JobQueue, Job
|
||||
from .updater import Updater
|
||||
|
@ -41,8 +41,6 @@ from .typehandler import TypeHandler
|
|||
from .conversationhandler import ConversationHandler
|
||||
from .precheckoutqueryhandler import PreCheckoutQueryHandler
|
||||
from .shippingqueryhandler import ShippingQueryHandler
|
||||
from .messagequeue import MessageQueue
|
||||
from .messagequeue import DelayQueue
|
||||
from .pollanswerhandler import PollAnswerHandler
|
||||
from .pollhandler import PollHandler
|
||||
from .chatmemberhandler import ChatMemberHandler
|
||||
|
@ -63,7 +61,6 @@ __all__ = (
|
|||
'ContextTypes',
|
||||
'ConversationHandler',
|
||||
'Defaults',
|
||||
'DelayQueue',
|
||||
'DictPersistence',
|
||||
'Dispatcher',
|
||||
'DispatcherHandlerStop',
|
||||
|
@ -76,7 +73,6 @@ __all__ = (
|
|||
'JobQueue',
|
||||
'MessageFilter',
|
||||
'MessageHandler',
|
||||
'MessageQueue',
|
||||
'PersistenceInput',
|
||||
'PicklePersistence',
|
||||
'PollAnswerHandler',
|
||||
|
@ -89,5 +85,4 @@ __all__ = (
|
|||
'TypeHandler',
|
||||
'UpdateFilter',
|
||||
'Updater',
|
||||
'run_async',
|
||||
)
|
||||
|
|
|
@ -22,7 +22,6 @@ import logging
|
|||
import warnings
|
||||
import weakref
|
||||
from collections import defaultdict
|
||||
from functools import wraps
|
||||
from queue import Empty, Queue
|
||||
from threading import BoundedSemaphore, Event, Lock, Thread, current_thread
|
||||
from time import sleep
|
||||
|
@ -44,11 +43,9 @@ from uuid import uuid4
|
|||
|
||||
from telegram import TelegramError, Update
|
||||
from telegram.ext import BasePersistence, ContextTypes
|
||||
from telegram.ext.callbackcontext import CallbackContext
|
||||
from telegram.ext.handler import Handler
|
||||
import telegram.ext.extbot
|
||||
from telegram.ext.callbackdatacache import CallbackDataCache
|
||||
from telegram.utils.deprecate import TelegramDeprecationWarning
|
||||
from telegram.ext.utils.promise import Promise
|
||||
from telegram.utils.helpers import DefaultValue, DEFAULT_FALSE
|
||||
from telegram.ext.utils.types import CCT, UD, CD, BD
|
||||
|
@ -56,46 +53,13 @@ from telegram.ext.utils.types import CCT, UD, CD, BD
|
|||
if TYPE_CHECKING:
|
||||
from telegram import Bot
|
||||
from telegram.ext import JobQueue
|
||||
from telegram.ext.callbackcontext import CallbackContext
|
||||
|
||||
DEFAULT_GROUP: int = 0
|
||||
|
||||
UT = TypeVar('UT')
|
||||
|
||||
|
||||
def run_async(
|
||||
func: Callable[[Update, CallbackContext], object]
|
||||
) -> Callable[[Update, CallbackContext], object]:
|
||||
"""
|
||||
Function decorator that will run the function in a new thread.
|
||||
|
||||
Will run :attr:`telegram.ext.Dispatcher.run_async`.
|
||||
|
||||
Using this decorator is only possible when only a single Dispatcher exist in the system.
|
||||
|
||||
Note:
|
||||
DEPRECATED. Use :attr:`telegram.ext.Dispatcher.run_async` directly instead or the
|
||||
:attr:`Handler.run_async` parameter.
|
||||
|
||||
Warning:
|
||||
If you're using ``@run_async`` you cannot rely on adding custom attributes to
|
||||
:class:`telegram.ext.CallbackContext`. See its docs for more info.
|
||||
"""
|
||||
|
||||
@wraps(func)
|
||||
def async_func(*args: object, **kwargs: object) -> object:
|
||||
warnings.warn(
|
||||
'The @run_async decorator is deprecated. Use the `run_async` parameter of '
|
||||
'your Handler or `Dispatcher.run_async` instead.',
|
||||
TelegramDeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
return Dispatcher.get_instance()._run_async( # pylint: disable=W0212
|
||||
func, *args, update=None, error_handling=False, **kwargs
|
||||
)
|
||||
|
||||
return async_func
|
||||
|
||||
|
||||
class DispatcherHandlerStop(Exception):
|
||||
"""
|
||||
Raise this in handler to prevent execution of any other handler (even in different group).
|
||||
|
@ -180,7 +144,6 @@ class Dispatcher(Generic[CCT, UD, CD, BD]):
|
|||
'__async_queue',
|
||||
'__async_threads',
|
||||
'bot',
|
||||
'__dict__',
|
||||
'__weakref__',
|
||||
'context_types',
|
||||
)
|
||||
|
@ -359,13 +322,6 @@ class Dispatcher(Generic[CCT, UD, CD, BD]):
|
|||
self.logger.error('An uncaught error was raised while handling the error.')
|
||||
continue
|
||||
|
||||
# Don't perform error handling for a `Promise` with deactivated error handling. This
|
||||
# should happen only via the deprecated `@run_async` decorator or `Promises` created
|
||||
# within error handlers
|
||||
if not promise.error_handling:
|
||||
self.logger.error('A promise with deactivated error handling raised an error.')
|
||||
continue
|
||||
|
||||
# If we arrive here, an exception happened in the promise and was neither
|
||||
# DispatcherHandlerStop nor raised by an error handler. So we can and must handle it
|
||||
try:
|
||||
|
@ -399,18 +355,7 @@ class Dispatcher(Generic[CCT, UD, CD, BD]):
|
|||
Promise
|
||||
|
||||
"""
|
||||
return self._run_async(func, *args, update=update, error_handling=True, **kwargs)
|
||||
|
||||
def _run_async(
|
||||
self,
|
||||
func: Callable[..., object],
|
||||
*args: object,
|
||||
update: object = None,
|
||||
error_handling: bool = True,
|
||||
**kwargs: object,
|
||||
) -> Promise:
|
||||
# TODO: Remove error_handling parameter once we drop the @run_async decorator
|
||||
promise = Promise(func, args, kwargs, update=update, error_handling=error_handling)
|
||||
promise = Promise(func, args, kwargs, update=update)
|
||||
self.__async_queue.put(promise)
|
||||
return promise
|
||||
|
||||
|
|
|
@ -20,7 +20,6 @@
|
|||
"""This module contains the Filters for use with the MessageHandler class."""
|
||||
|
||||
import re
|
||||
import warnings
|
||||
|
||||
from abc import ABC, abstractmethod
|
||||
from threading import Lock
|
||||
|
@ -50,7 +49,6 @@ __all__ = [
|
|||
'XORFilter',
|
||||
]
|
||||
|
||||
from telegram.utils.deprecate import TelegramDeprecationWarning
|
||||
from telegram.utils.types import SLT
|
||||
|
||||
DataDict = Dict[str, list]
|
||||
|
@ -1307,48 +1305,6 @@ officedocument.wordprocessingml.document")``.
|
|||
"""""" # remove method from docs
|
||||
return any(entity.type == self.entity_type for entity in message.caption_entities)
|
||||
|
||||
class _Private(MessageFilter):
|
||||
__slots__ = ()
|
||||
name = 'Filters.private'
|
||||
|
||||
def filter(self, message: Message) -> bool:
|
||||
warnings.warn(
|
||||
'Filters.private is deprecated. Use Filters.chat_type.private instead.',
|
||||
TelegramDeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
return message.chat.type == Chat.PRIVATE
|
||||
|
||||
private = _Private()
|
||||
"""
|
||||
Messages sent in a private chat.
|
||||
|
||||
Note:
|
||||
DEPRECATED. Use
|
||||
:attr:`telegram.ext.Filters.chat_type.private` instead.
|
||||
"""
|
||||
|
||||
class _Group(MessageFilter):
|
||||
__slots__ = ()
|
||||
name = 'Filters.group'
|
||||
|
||||
def filter(self, message: Message) -> bool:
|
||||
warnings.warn(
|
||||
'Filters.group is deprecated. Use Filters.chat_type.groups instead.',
|
||||
TelegramDeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
return message.chat.type in [Chat.GROUP, Chat.SUPERGROUP]
|
||||
|
||||
group = _Group()
|
||||
"""
|
||||
Messages sent in a group or a supergroup chat.
|
||||
|
||||
Note:
|
||||
DEPRECATED. Use
|
||||
:attr:`telegram.ext.Filters.chat_type.groups` instead.
|
||||
"""
|
||||
|
||||
class _ChatType(MessageFilter):
|
||||
__slots__ = ()
|
||||
name = 'Filters.chat_type'
|
||||
|
|
|
@ -176,7 +176,7 @@ class JobQueue:
|
|||
job_kwargs = {}
|
||||
|
||||
name = name or callback.__name__
|
||||
job = Job(callback, context, name, self)
|
||||
job = Job(callback, context, name)
|
||||
date_time = self._parse_time_input(when, shift_day=True)
|
||||
|
||||
j = self.scheduler.add_job(
|
||||
|
@ -260,7 +260,7 @@ class JobQueue:
|
|||
job_kwargs = {}
|
||||
|
||||
name = name or callback.__name__
|
||||
job = Job(callback, context, name, self)
|
||||
job = Job(callback, context, name)
|
||||
|
||||
dt_first = self._parse_time_input(first)
|
||||
dt_last = self._parse_time_input(last)
|
||||
|
@ -325,7 +325,7 @@ class JobQueue:
|
|||
job_kwargs = {}
|
||||
|
||||
name = name or callback.__name__
|
||||
job = Job(callback, context, name, self)
|
||||
job = Job(callback, context, name)
|
||||
|
||||
j = self.scheduler.add_job(
|
||||
callback,
|
||||
|
@ -382,7 +382,7 @@ class JobQueue:
|
|||
job_kwargs = {}
|
||||
|
||||
name = name or callback.__name__
|
||||
job = Job(callback, context, name, self)
|
||||
job = Job(callback, context, name)
|
||||
|
||||
j = self.scheduler.add_job(
|
||||
callback,
|
||||
|
@ -425,7 +425,7 @@ class JobQueue:
|
|||
|
||||
"""
|
||||
name = name or callback.__name__
|
||||
job = Job(callback, context, name, self)
|
||||
job = Job(callback, context, name)
|
||||
|
||||
j = self.scheduler.add_job(callback, args=self._build_args(job), name=name, **job_kwargs)
|
||||
|
||||
|
@ -445,7 +445,7 @@ class JobQueue:
|
|||
def jobs(self) -> Tuple['Job', ...]:
|
||||
"""Returns a tuple of all *scheduled* jobs that are currently in the ``JobQueue``."""
|
||||
return tuple(
|
||||
Job._from_aps_job(job, self) # pylint: disable=W0212
|
||||
Job._from_aps_job(job) # pylint: disable=protected-access
|
||||
for job in self.scheduler.get_jobs()
|
||||
)
|
||||
|
||||
|
@ -472,21 +472,21 @@ class Job:
|
|||
* If :attr:`job` isn't passed on initialization, it must be set manually afterwards for
|
||||
this :class:`telegram.ext.Job` to be useful.
|
||||
|
||||
.. versionchanged:: 14.0
|
||||
Removed argument and attribute :attr:`job_queue`.
|
||||
|
||||
Args:
|
||||
callback (:obj:`callable`): The callback function that should be executed by the new job.
|
||||
Callback signature: ``def callback(update: Update, context: CallbackContext)``
|
||||
context (:obj:`object`, optional): Additional data needed for the callback function. Can be
|
||||
accessed through ``job.context`` in the callback. Defaults to :obj:`None`.
|
||||
name (:obj:`str`, optional): The name of the new job. Defaults to ``callback.__name__``.
|
||||
job_queue (:class:`telegram.ext.JobQueue`, optional): The ``JobQueue`` this job belongs to.
|
||||
Only optional for backward compatibility with ``JobQueue.put()``.
|
||||
job (:class:`apscheduler.job.Job`, optional): The APS Job this job is a wrapper for.
|
||||
|
||||
Attributes:
|
||||
callback (:obj:`callable`): The callback function that should be executed by the new job.
|
||||
context (:obj:`object`): Optional. Additional data needed for the callback function.
|
||||
name (:obj:`str`): Optional. The name of the new job.
|
||||
job_queue (:class:`telegram.ext.JobQueue`): Optional. The ``JobQueue`` this job belongs to.
|
||||
job (:class:`apscheduler.job.Job`): Optional. The APS Job this job is a wrapper for.
|
||||
"""
|
||||
|
||||
|
@ -494,7 +494,6 @@ class Job:
|
|||
'callback',
|
||||
'context',
|
||||
'name',
|
||||
'job_queue',
|
||||
'_removed',
|
||||
'_enabled',
|
||||
'job',
|
||||
|
@ -505,14 +504,12 @@ class Job:
|
|||
callback: Callable[['CallbackContext'], None],
|
||||
context: object = None,
|
||||
name: str = None,
|
||||
job_queue: JobQueue = None,
|
||||
job: APSJob = None,
|
||||
):
|
||||
|
||||
self.callback = callback
|
||||
self.context = context
|
||||
self.name = name or callback.__name__
|
||||
self.job_queue = job_queue
|
||||
|
||||
self._removed = False
|
||||
self._enabled = False
|
||||
|
@ -570,13 +567,13 @@ 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':
|
||||
# context based callbacks
|
||||
if len(job.args) == 1:
|
||||
context = job.args[0].job.context
|
||||
else:
|
||||
context = job.args[1].context
|
||||
return cls(job.func, context=context, name=job.name, job_queue=job_queue, job=job)
|
||||
return cls(job.func, context=context, name=job.name, job=job)
|
||||
|
||||
def __getattr__(self, item: str) -> object:
|
||||
return getattr(self.job, item)
|
||||
|
|
|
@ -1,334 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# Module author:
|
||||
# Tymofii A. Khodniev (thodnev) <thodnev@mail.ru>
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2022
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/]
|
||||
"""A throughput-limiting message processor for Telegram bots."""
|
||||
import functools
|
||||
import queue as q
|
||||
import threading
|
||||
import time
|
||||
import warnings
|
||||
from typing import TYPE_CHECKING, Callable, List, NoReturn
|
||||
|
||||
from telegram.ext.utils.promise import Promise
|
||||
from telegram.utils.deprecate import TelegramDeprecationWarning
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from telegram import Bot
|
||||
|
||||
# We need to count < 1s intervals, so the most accurate timer is needed
|
||||
curtime = time.perf_counter
|
||||
|
||||
|
||||
class DelayQueueError(RuntimeError):
|
||||
"""Indicates processing errors."""
|
||||
|
||||
__slots__ = ()
|
||||
|
||||
|
||||
class DelayQueue(threading.Thread):
|
||||
"""
|
||||
Processes callbacks from queue with specified throughput limits. Creates a separate thread to
|
||||
process callbacks with delays.
|
||||
|
||||
.. deprecated:: 13.3
|
||||
:class:`telegram.ext.DelayQueue` in its current form is deprecated and will be reinvented
|
||||
in a future release. See `this thread <https://git.io/JtDbF>`_ for a list of known bugs.
|
||||
|
||||
Args:
|
||||
queue (:obj:`Queue`, optional): Used to pass callbacks to thread. Creates ``Queue``
|
||||
implicitly if not provided.
|
||||
burst_limit (:obj:`int`, optional): Number of maximum callbacks to process per time-window
|
||||
defined by :attr:`time_limit_ms`. Defaults to 30.
|
||||
time_limit_ms (:obj:`int`, optional): Defines width of time-window used when each
|
||||
processing limit is calculated. Defaults to 1000.
|
||||
exc_route (:obj:`callable`, optional): A callable, accepting 1 positional argument; used to
|
||||
route exceptions from processor thread to main thread; is called on `Exception`
|
||||
subclass exceptions. If not provided, exceptions are routed through dummy handler,
|
||||
which re-raises them.
|
||||
autostart (:obj:`bool`, optional): If :obj:`True`, processor is started immediately after
|
||||
object's creation; if :obj:`False`, should be started manually by `start` method.
|
||||
Defaults to :obj:`True`.
|
||||
name (:obj:`str`, optional): Thread's name. Defaults to ``'DelayQueue-N'``, where N is
|
||||
sequential number of object created.
|
||||
|
||||
Attributes:
|
||||
burst_limit (:obj:`int`): Number of maximum callbacks to process per time-window.
|
||||
time_limit (:obj:`int`): Defines width of time-window used when each processing limit is
|
||||
calculated.
|
||||
exc_route (:obj:`callable`): A callable, accepting 1 positional argument; used to route
|
||||
exceptions from processor thread to main thread;
|
||||
name (:obj:`str`): Thread's name.
|
||||
|
||||
"""
|
||||
|
||||
_instcnt = 0 # instance counter
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
queue: q.Queue = None,
|
||||
burst_limit: int = 30,
|
||||
time_limit_ms: int = 1000,
|
||||
exc_route: Callable[[Exception], None] = None,
|
||||
autostart: bool = True,
|
||||
name: str = None,
|
||||
):
|
||||
warnings.warn(
|
||||
'DelayQueue in its current form is deprecated and will be reinvented in a future '
|
||||
'release. See https://git.io/JtDbF for a list of known bugs.',
|
||||
category=TelegramDeprecationWarning,
|
||||
)
|
||||
|
||||
self._queue = queue if queue is not None else q.Queue()
|
||||
self.burst_limit = burst_limit
|
||||
self.time_limit = time_limit_ms / 1000
|
||||
self.exc_route = exc_route if exc_route is not None else self._default_exception_handler
|
||||
self.__exit_req = False # flag to gently exit thread
|
||||
self.__class__._instcnt += 1
|
||||
if name is None:
|
||||
name = f'{self.__class__.__name__}-{self.__class__._instcnt}'
|
||||
super().__init__(name=name)
|
||||
self.daemon = False
|
||||
if autostart: # immediately start processing
|
||||
super().start()
|
||||
|
||||
def run(self) -> None:
|
||||
"""
|
||||
Do not use the method except for unthreaded testing purposes, the method normally is
|
||||
automatically called by autostart argument.
|
||||
|
||||
"""
|
||||
times: List[float] = [] # used to store each callable processing time
|
||||
while True:
|
||||
item = self._queue.get()
|
||||
if self.__exit_req:
|
||||
return # shutdown thread
|
||||
# delay routine
|
||||
now = time.perf_counter()
|
||||
t_delta = now - self.time_limit # calculate early to improve perf.
|
||||
if times and t_delta > times[-1]:
|
||||
# if last call was before the limit time-window
|
||||
# used to impr. perf. in long-interval calls case
|
||||
times = [now]
|
||||
else:
|
||||
# collect last in current limit time-window
|
||||
times = [t for t in times if t >= t_delta]
|
||||
times.append(now)
|
||||
if len(times) >= self.burst_limit: # if throughput limit was hit
|
||||
time.sleep(times[1] - t_delta)
|
||||
# finally process one
|
||||
try:
|
||||
func, args, kwargs = item
|
||||
func(*args, **kwargs)
|
||||
except Exception as exc: # re-route any exceptions
|
||||
self.exc_route(exc) # to prevent thread exit
|
||||
|
||||
def stop(self, timeout: float = None) -> None:
|
||||
"""Used to gently stop processor and shutdown its thread.
|
||||
|
||||
Args:
|
||||
timeout (:obj:`float`): Indicates maximum time to wait for processor to stop and its
|
||||
thread to exit. If timeout exceeds and processor has not stopped, method silently
|
||||
returns. :attr:`is_alive` could be used afterwards to check the actual status.
|
||||
``timeout`` set to :obj:`None`, blocks until processor is shut down.
|
||||
Defaults to :obj:`None`.
|
||||
|
||||
"""
|
||||
self.__exit_req = True # gently request
|
||||
self._queue.put(None) # put something to unfreeze if frozen
|
||||
super().join(timeout=timeout)
|
||||
|
||||
@staticmethod
|
||||
def _default_exception_handler(exc: Exception) -> NoReturn:
|
||||
"""
|
||||
Dummy exception handler which re-raises exception in thread. Could be possibly overwritten
|
||||
by subclasses.
|
||||
|
||||
"""
|
||||
raise exc
|
||||
|
||||
def __call__(self, func: Callable, *args: object, **kwargs: object) -> None:
|
||||
"""Used to process callbacks in throughput-limiting thread through queue.
|
||||
|
||||
Args:
|
||||
func (:obj:`callable`): The actual function (or any callable) that is processed through
|
||||
queue.
|
||||
*args (:obj:`list`): Variable-length `func` arguments.
|
||||
**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))
|
||||
|
||||
|
||||
# The most straightforward way to implement this is to use 2 sequential delay
|
||||
# queues, like on classic delay chain schematics in electronics.
|
||||
# So, message path is:
|
||||
# msg --> group delay if group msg, else no delay --> normal msg delay --> out
|
||||
# This way OS threading scheduler cares of timings accuracy.
|
||||
# (see time.time, time.clock, time.perf_counter, time.sleep @ docs.python.org)
|
||||
class MessageQueue:
|
||||
"""
|
||||
Implements callback processing with proper delays to avoid hitting Telegram's message limits.
|
||||
Contains two ``DelayQueue``, for group and for all messages, interconnected in delay chain.
|
||||
Callables are processed through *group* ``DelayQueue``, then through *all* ``DelayQueue`` for
|
||||
group-type messages. For non-group messages, only the *all* ``DelayQueue`` is used.
|
||||
|
||||
.. deprecated:: 13.3
|
||||
:class:`telegram.ext.MessageQueue` in its current form is deprecated and will be reinvented
|
||||
in a future release. See `this thread <https://git.io/JtDbF>`_ for a list of known bugs.
|
||||
|
||||
Args:
|
||||
all_burst_limit (:obj:`int`, optional): Number of maximum *all-type* callbacks to process
|
||||
per time-window defined by :attr:`all_time_limit_ms`. Defaults to 30.
|
||||
all_time_limit_ms (:obj:`int`, optional): Defines width of *all-type* time-window used when
|
||||
each processing limit is calculated. Defaults to 1000 ms.
|
||||
group_burst_limit (:obj:`int`, optional): Number of maximum *group-type* callbacks to
|
||||
process per time-window defined by :attr:`group_time_limit_ms`. Defaults to 20.
|
||||
group_time_limit_ms (:obj:`int`, optional): Defines width of *group-type* time-window used
|
||||
when each processing limit is calculated. Defaults to 60000 ms.
|
||||
exc_route (:obj:`callable`, optional): A callable, accepting one positional argument; used
|
||||
to route exceptions from processor threads to main thread; is called on ``Exception``
|
||||
subclass exceptions. If not provided, exceptions are routed through dummy handler,
|
||||
which re-raises them.
|
||||
autostart (:obj:`bool`, optional): If :obj:`True`, processors are started immediately after
|
||||
object's creation; if :obj:`False`, should be started manually by :attr:`start` method.
|
||||
Defaults to :obj:`True`.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
all_burst_limit: int = 30,
|
||||
all_time_limit_ms: int = 1000,
|
||||
group_burst_limit: int = 20,
|
||||
group_time_limit_ms: int = 60000,
|
||||
exc_route: Callable[[Exception], None] = None,
|
||||
autostart: bool = True,
|
||||
):
|
||||
warnings.warn(
|
||||
'MessageQueue in its current form is deprecated and will be reinvented in a future '
|
||||
'release. See https://git.io/JtDbF for a list of known bugs.',
|
||||
category=TelegramDeprecationWarning,
|
||||
)
|
||||
|
||||
# create according delay queues, use composition
|
||||
self._all_delayq = DelayQueue(
|
||||
burst_limit=all_burst_limit,
|
||||
time_limit_ms=all_time_limit_ms,
|
||||
exc_route=exc_route,
|
||||
autostart=autostart,
|
||||
)
|
||||
self._group_delayq = DelayQueue(
|
||||
burst_limit=group_burst_limit,
|
||||
time_limit_ms=group_time_limit_ms,
|
||||
exc_route=exc_route,
|
||||
autostart=autostart,
|
||||
)
|
||||
|
||||
def start(self) -> None:
|
||||
"""Method is used to manually start the ``MessageQueue`` processing."""
|
||||
self._all_delayq.start()
|
||||
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)
|
||||
|
||||
stop.__doc__ = DelayQueue.stop.__doc__ or '' # reuse docstring if any
|
||||
|
||||
def __call__(self, promise: Callable, is_group_msg: bool = False) -> Callable:
|
||||
"""
|
||||
Processes callables in throughput-limiting queues to avoid hitting limits (specified with
|
||||
:attr:`burst_limit` and :attr:`time_limit`.
|
||||
|
||||
Args:
|
||||
promise (:obj:`callable`): Mainly the ``telegram.utils.promise.Promise`` (see Notes for
|
||||
other callables), that is processed in delay queues.
|
||||
is_group_msg (:obj:`bool`, optional): Defines whether ``promise`` would be processed in
|
||||
group*+*all* ``DelayQueue``s (if set to :obj:`True`), or only through *all*
|
||||
``DelayQueue`` (if set to :obj:`False`), resulting in needed delays to avoid
|
||||
hitting specified limits. Defaults to :obj:`False`.
|
||||
|
||||
Note:
|
||||
Method is designed to accept ``telegram.utils.promise.Promise`` as ``promise``
|
||||
argument, but other callables could be used too. For example, lambdas or simple
|
||||
functions could be used to wrap original func to be called with needed args. In that
|
||||
case, be sure that either wrapper func does not raise outside exceptions or the proper
|
||||
:attr:`exc_route` handler is provided.
|
||||
|
||||
Returns:
|
||||
:obj:`callable`: Used as ``promise`` argument.
|
||||
|
||||
"""
|
||||
if not is_group_msg: # ignore middle group delay
|
||||
self._all_delayq(promise)
|
||||
else: # use middle group delay
|
||||
self._group_delayq(self._all_delayq, promise)
|
||||
return promise
|
||||
|
||||
|
||||
def queuedmessage(method: Callable) -> Callable:
|
||||
"""A decorator to be used with :attr:`telegram.Bot` send* methods.
|
||||
|
||||
Note:
|
||||
As it probably wouldn't be a good idea to make this decorator a property, it has been coded
|
||||
as decorator function, so it implies that first positional argument to wrapped MUST be
|
||||
self.
|
||||
|
||||
The next object attributes are used by decorator:
|
||||
|
||||
Attributes:
|
||||
self._is_messages_queued_default (:obj:`bool`): Value to provide class-defaults to
|
||||
``queued`` kwarg if not provided during wrapped method call.
|
||||
self._msg_queue (:class:`telegram.ext.messagequeue.MessageQueue`): The actual
|
||||
``MessageQueue`` used to delay outbound messages according to specified time-limits.
|
||||
|
||||
Wrapped method starts accepting the next kwargs:
|
||||
|
||||
Args:
|
||||
queued (:obj:`bool`, optional): If set to :obj:`True`, the ``MessageQueue`` is used to
|
||||
process output messages. Defaults to `self._is_queued_out`.
|
||||
isgroup (:obj:`bool`, optional): If set to :obj:`True`, the message is meant to be
|
||||
group-type(as there's no obvious way to determine its type in other way at the moment).
|
||||
Group-type messages could have additional processing delay according to limits set
|
||||
in `self._out_queue`. Defaults to :obj:`False`.
|
||||
|
||||
Returns:
|
||||
``telegram.utils.promise.Promise``: In case call is queued or original method's return
|
||||
value if it's not.
|
||||
|
||||
"""
|
||||
|
||||
@functools.wraps(method)
|
||||
def wrapped(self: 'Bot', *args: object, **kwargs: object) -> object:
|
||||
# pylint: disable=W0212
|
||||
queued = kwargs.pop(
|
||||
'queued', self._is_messages_queued_default # type: ignore[attr-defined]
|
||||
)
|
||||
isgroup = kwargs.pop('isgroup', False)
|
||||
if queued:
|
||||
prom = Promise(method, (self,) + args, kwargs)
|
||||
return self._msg_queue(prom, isgroup) # type: ignore[attr-defined]
|
||||
return method(self, *args, **kwargs)
|
||||
|
||||
return wrapped
|
|
@ -342,7 +342,6 @@ class Updater(Generic[CCT, UD, CD, BD]):
|
|||
self,
|
||||
poll_interval: float = 0.0,
|
||||
timeout: float = 10,
|
||||
clean: bool = None,
|
||||
bootstrap_retries: int = -1,
|
||||
read_latency: float = 2.0,
|
||||
allowed_updates: List[str] = None,
|
||||
|
@ -350,6 +349,9 @@ class Updater(Generic[CCT, UD, CD, BD]):
|
|||
) -> Optional[Queue]:
|
||||
"""Starts polling updates from Telegram.
|
||||
|
||||
.. versionchanged:: 14.0
|
||||
Removed the ``clean`` argument in favor of ``drop_pending_updates``.
|
||||
|
||||
Args:
|
||||
poll_interval (:obj:`float`, optional): Time to wait between polling updates from
|
||||
Telegram in seconds. Default is ``0.0``.
|
||||
|
@ -358,10 +360,6 @@ class Updater(Generic[CCT, UD, CD, BD]):
|
|||
Telegram servers before actually starting to poll. Default is :obj:`False`.
|
||||
|
||||
.. versionadded :: 13.4
|
||||
clean (:obj:`bool`, optional): Alias for ``drop_pending_updates``.
|
||||
|
||||
.. deprecated:: 13.4
|
||||
Use ``drop_pending_updates`` instead.
|
||||
bootstrap_retries (:obj:`int`, optional): Whether the bootstrapping phase of the
|
||||
:class:`telegram.ext.Updater` will retry on failures on the Telegram server.
|
||||
|
||||
|
@ -379,19 +377,6 @@ class Updater(Generic[CCT, UD, CD, BD]):
|
|||
:obj:`Queue`: The update queue that can be filled from the main thread.
|
||||
|
||||
"""
|
||||
if (clean is not None) and (drop_pending_updates is not None):
|
||||
raise TypeError('`clean` and `drop_pending_updates` are mutually exclusive.')
|
||||
|
||||
if clean is not None:
|
||||
warnings.warn(
|
||||
'The argument `clean` of `start_polling` is deprecated. Please use '
|
||||
'`drop_pending_updates` instead.',
|
||||
category=TelegramDeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
|
||||
drop_pending_updates = drop_pending_updates if drop_pending_updates is not None else clean
|
||||
|
||||
with self.__lock:
|
||||
if not self.running:
|
||||
self.running = True
|
||||
|
@ -428,11 +413,9 @@ class Updater(Generic[CCT, UD, CD, BD]):
|
|||
url_path: str = '',
|
||||
cert: str = None,
|
||||
key: str = None,
|
||||
clean: bool = None,
|
||||
bootstrap_retries: int = 0,
|
||||
webhook_url: str = None,
|
||||
allowed_updates: List[str] = None,
|
||||
force_event_loop: bool = None,
|
||||
drop_pending_updates: bool = None,
|
||||
ip_address: str = None,
|
||||
max_connections: int = 40,
|
||||
|
@ -448,6 +431,10 @@ class Updater(Generic[CCT, UD, CD, BD]):
|
|||
:meth:`start_webhook` now *always* calls :meth:`telegram.Bot.set_webhook`, so pass
|
||||
``webhook_url`` instead of calling ``updater.bot.set_webhook(webhook_url)`` manually.
|
||||
|
||||
.. versionchanged:: 14.0
|
||||
Removed the ``clean`` argument in favor of ``drop_pending_updates`` and removed the
|
||||
deprecated argument ``force_event_loop``.
|
||||
|
||||
Args:
|
||||
listen (:obj:`str`, optional): IP-Address to listen on. Default ``127.0.0.1``.
|
||||
port (:obj:`int`, optional): Port the bot should be listening on. Default ``80``.
|
||||
|
@ -458,10 +445,6 @@ class Updater(Generic[CCT, UD, CD, BD]):
|
|||
Telegram servers before actually starting to poll. Default is :obj:`False`.
|
||||
|
||||
.. versionadded :: 13.4
|
||||
clean (:obj:`bool`, optional): Alias for ``drop_pending_updates``.
|
||||
|
||||
.. deprecated:: 13.4
|
||||
Use ``drop_pending_updates`` instead.
|
||||
bootstrap_retries (:obj:`int`, optional): Whether the bootstrapping phase of the
|
||||
:class:`telegram.ext.Updater` will retry on failures on the Telegram server.
|
||||
|
||||
|
@ -477,13 +460,6 @@ class Updater(Generic[CCT, UD, CD, BD]):
|
|||
.. versionadded :: 13.4
|
||||
allowed_updates (List[:obj:`str`], optional): Passed to
|
||||
:meth:`telegram.Bot.set_webhook`.
|
||||
force_event_loop (:obj:`bool`, optional): Legacy parameter formerly used for a
|
||||
workaround on Windows + Python 3.8+. No longer has any effect.
|
||||
|
||||
.. deprecated:: 13.6
|
||||
Since version 13.6, ``tornade>=6.1`` is required, which resolves the former
|
||||
issue.
|
||||
|
||||
max_connections (:obj:`int`, optional): Passed to
|
||||
:meth:`telegram.Bot.set_webhook`.
|
||||
|
||||
|
@ -493,27 +469,6 @@ class Updater(Generic[CCT, UD, CD, BD]):
|
|||
:obj:`Queue`: The update queue that can be filled from the main thread.
|
||||
|
||||
"""
|
||||
if (clean is not None) and (drop_pending_updates is not None):
|
||||
raise TypeError('`clean` and `drop_pending_updates` are mutually exclusive.')
|
||||
|
||||
if clean is not None:
|
||||
warnings.warn(
|
||||
'The argument `clean` of `start_webhook` is deprecated. Please use '
|
||||
'`drop_pending_updates` instead.',
|
||||
category=TelegramDeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
|
||||
if force_event_loop is not None:
|
||||
warnings.warn(
|
||||
'The argument `force_event_loop` of `start_webhook` is deprecated and no longer '
|
||||
'has any effect.',
|
||||
category=TelegramDeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
|
||||
drop_pending_updates = drop_pending_updates if drop_pending_updates is not None else clean
|
||||
|
||||
with self.__lock:
|
||||
if not self.running:
|
||||
self.running = True
|
||||
|
|
|
@ -33,14 +33,15 @@ logger = logging.getLogger(__name__)
|
|||
class Promise:
|
||||
"""A simple Promise implementation for use with the run_async decorator, DelayQueue etc.
|
||||
|
||||
.. versionchanged:: 14.0
|
||||
Removed the argument and attribute ``error_handler``.
|
||||
|
||||
Args:
|
||||
pooled_function (:obj:`callable`): The callable that will be called concurrently.
|
||||
args (:obj:`list` | :obj:`tuple`): Positional arguments for :attr:`pooled_function`.
|
||||
kwargs (:obj:`dict`): Keyword arguments for :attr:`pooled_function`.
|
||||
update (:class:`telegram.Update` | :obj:`object`, optional): The update this promise is
|
||||
associated with.
|
||||
error_handling (:obj:`bool`, optional): Whether exceptions raised by :attr:`func`
|
||||
may be handled by error handlers. Defaults to :obj:`True`.
|
||||
|
||||
Attributes:
|
||||
pooled_function (:obj:`callable`): The callable that will be called concurrently.
|
||||
|
@ -49,8 +50,6 @@ class Promise:
|
|||
done (:obj:`threading.Event`): Is set when the result is available.
|
||||
update (:class:`telegram.Update` | :obj:`object`): Optional. The update this promise is
|
||||
associated with.
|
||||
error_handling (:obj:`bool`): Optional. Whether exceptions raised by :attr:`func`
|
||||
may be handled by error handlers. Defaults to :obj:`True`.
|
||||
|
||||
"""
|
||||
|
||||
|
@ -59,27 +58,23 @@ class Promise:
|
|||
'args',
|
||||
'kwargs',
|
||||
'update',
|
||||
'error_handling',
|
||||
'done',
|
||||
'_done_callback',
|
||||
'_result',
|
||||
'_exception',
|
||||
)
|
||||
|
||||
# TODO: Remove error_handling parameter once we drop the @run_async decorator
|
||||
def __init__(
|
||||
self,
|
||||
pooled_function: Callable[..., RT],
|
||||
args: Union[List, Tuple],
|
||||
kwargs: JSONDict,
|
||||
update: object = None,
|
||||
error_handling: bool = True,
|
||||
):
|
||||
self.pooled_function = pooled_function
|
||||
self.args = args
|
||||
self.kwargs = kwargs
|
||||
self.update = update
|
||||
self.error_handling = error_handling
|
||||
self.done = Event()
|
||||
self._done_callback: Optional[Callable] = None
|
||||
self._result: Optional[RT] = None
|
||||
|
|
|
@ -1,38 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2022
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
"""This module contains the :class:`telegram.ext.utils.promise.Promise` class for backwards
|
||||
compatibility.
|
||||
"""
|
||||
import warnings
|
||||
|
||||
import telegram.ext.utils.promise as promise
|
||||
from telegram.utils.deprecate import TelegramDeprecationWarning
|
||||
|
||||
warnings.warn(
|
||||
'telegram.utils.promise is deprecated. Please use telegram.ext.utils.promise instead.',
|
||||
TelegramDeprecationWarning,
|
||||
)
|
||||
|
||||
Promise = promise.Promise
|
||||
"""
|
||||
:class:`telegram.ext.utils.promise.Promise`
|
||||
|
||||
.. deprecated:: v13.2
|
||||
Use :class:`telegram.ext.utils.promise.Promise` instead.
|
||||
"""
|
|
@ -1,35 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2022
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
"""This module contains the :class:`telegram.ext.utils.webhookhandler.WebhookHandler` class for
|
||||
backwards compatibility.
|
||||
"""
|
||||
import warnings
|
||||
|
||||
import telegram.ext.utils.webhookhandler as webhook_handler
|
||||
from telegram.utils.deprecate import TelegramDeprecationWarning
|
||||
|
||||
warnings.warn(
|
||||
'telegram.utils.webhookhandler is deprecated. Please use telegram.ext.utils.webhookhandler '
|
||||
'instead.',
|
||||
TelegramDeprecationWarning,
|
||||
)
|
||||
|
||||
WebhookHandler = webhook_handler.WebhookHandler
|
||||
WebhookServer = webhook_handler.WebhookServer
|
||||
WebhookAppClass = webhook_handler.WebhookAppClass
|
|
@ -104,6 +104,10 @@ class DictBot(Bot):
|
|||
pass
|
||||
|
||||
|
||||
class DictDispatcher(Dispatcher):
|
||||
pass
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def bot(bot_info):
|
||||
return DictExtBot(bot_info['token'], private_key=PRIVATE_KEY, request=DictRequest())
|
||||
|
@ -159,7 +163,7 @@ def provider_token(bot_info):
|
|||
def create_dp(bot):
|
||||
# Dispatcher is heavy to init (due to many threads and such) so we have a single session
|
||||
# scoped one here, but before each test, reset it (dp fixture below)
|
||||
dispatcher = Dispatcher(bot, Queue(), job_queue=JobQueue(), workers=2)
|
||||
dispatcher = DictDispatcher(bot, Queue(), job_queue=JobQueue(), workers=2)
|
||||
dispatcher.job_queue.set_dispatcher(dispatcher)
|
||||
thr = Thread(target=dispatcher.start)
|
||||
thr.start()
|
||||
|
@ -188,17 +192,12 @@ def dp(_dp):
|
|||
_dp.handlers = {}
|
||||
_dp.groups = []
|
||||
_dp.error_handlers = {}
|
||||
# For some reason if we setattr with the name mangled, then some tests(like async) run forever,
|
||||
# due to threads not acquiring, (blocking). This adds these attributes to the __dict__.
|
||||
object.__setattr__(_dp, '__stop_event', Event())
|
||||
object.__setattr__(_dp, '__exception_event', Event())
|
||||
object.__setattr__(_dp, '__async_queue', Queue())
|
||||
object.__setattr__(_dp, '__async_threads', set())
|
||||
_dp.__exception_event = Event()
|
||||
_dp.__stop_event = Event()
|
||||
_dp.__async_queue = Queue()
|
||||
_dp.__async_threads = set()
|
||||
_dp.persistence = None
|
||||
if _dp._Dispatcher__singleton_semaphore.acquire(blocking=0):
|
||||
Dispatcher._set_singleton(_dp)
|
||||
yield _dp
|
||||
Dispatcher._Dispatcher__singleton_semaphore.release()
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
|
|
|
@ -193,7 +193,6 @@ class TestBot:
|
|||
@flaky(3, 1)
|
||||
def test_get_me_and_properties(self, bot):
|
||||
get_me_bot = bot.get_me()
|
||||
commands = bot.get_my_commands()
|
||||
|
||||
assert isinstance(get_me_bot, User)
|
||||
assert get_me_bot.id == bot.id
|
||||
|
@ -205,9 +204,6 @@ class TestBot:
|
|||
assert get_me_bot.can_read_all_group_messages == bot.can_read_all_group_messages
|
||||
assert get_me_bot.supports_inline_queries == bot.supports_inline_queries
|
||||
assert f'https://t.me/{get_me_bot.username}' == bot.link
|
||||
assert commands == bot.commands
|
||||
bot._commands = None
|
||||
assert commands == bot.commands
|
||||
|
||||
def test_equality(self):
|
||||
a = Bot(FALLBACKS[0]["token"])
|
||||
|
@ -723,12 +719,10 @@ class TestBot:
|
|||
'chat_action',
|
||||
[
|
||||
ChatAction.FIND_LOCATION,
|
||||
ChatAction.RECORD_AUDIO,
|
||||
ChatAction.RECORD_VIDEO,
|
||||
ChatAction.RECORD_VIDEO_NOTE,
|
||||
ChatAction.RECORD_VOICE,
|
||||
ChatAction.TYPING,
|
||||
ChatAction.UPLOAD_AUDIO,
|
||||
ChatAction.UPLOAD_DOCUMENT,
|
||||
ChatAction.UPLOAD_PHOTO,
|
||||
ChatAction.UPLOAD_VIDEO,
|
||||
|
@ -1039,18 +1033,6 @@ class TestBot:
|
|||
assert bot.ban_chat_sender_chat(2, 32)
|
||||
monkeypatch.delattr(bot.request, 'post')
|
||||
|
||||
def test_kick_chat_member_warning(self, monkeypatch, bot, recwarn):
|
||||
def test(url, data, *args, **kwargs):
|
||||
chat_id = data['chat_id'] == 2
|
||||
user_id = data['user_id'] == 32
|
||||
return chat_id and user_id
|
||||
|
||||
monkeypatch.setattr(bot.request, 'post', test)
|
||||
bot.kick_chat_member(2, 32)
|
||||
assert len(recwarn) == 1
|
||||
assert '`bot.kick_chat_member` is deprecated' in str(recwarn[0].message)
|
||||
monkeypatch.delattr(bot.request, 'post')
|
||||
|
||||
# TODO: Needs improvement.
|
||||
@pytest.mark.parametrize('only_if_banned', [True, False, None])
|
||||
def test_unban_chat_member(self, monkeypatch, bot, only_if_banned):
|
||||
|
@ -1401,16 +1383,6 @@ class TestBot:
|
|||
assert isinstance(count, int)
|
||||
assert count > 3
|
||||
|
||||
def test_get_chat_members_count_warning(self, bot, channel_id, recwarn):
|
||||
bot.get_chat_members_count(channel_id)
|
||||
assert len(recwarn) == 1
|
||||
assert '`bot.get_chat_members_count` is deprecated' in str(recwarn[0].message)
|
||||
|
||||
def test_bot_command_property_warning(self, bot, recwarn):
|
||||
_ = bot.commands
|
||||
assert len(recwarn) == 1
|
||||
assert 'Bot.commands has been deprecated since there can' in str(recwarn[0].message)
|
||||
|
||||
@flaky(3, 1)
|
||||
def test_get_chat_member(self, bot, channel_id, chat_id):
|
||||
chat_member = bot.get_chat_member(channel_id, chat_id)
|
||||
|
@ -2059,39 +2031,14 @@ class TestBot:
|
|||
|
||||
@flaky(3, 1)
|
||||
def test_set_and_get_my_commands(self, bot):
|
||||
commands = [
|
||||
BotCommand('cmd1', 'descr1'),
|
||||
BotCommand('cmd2', 'descr2'),
|
||||
]
|
||||
commands = [BotCommand('cmd1', 'descr1'), ['cmd2', 'descr2']]
|
||||
bot.set_my_commands([])
|
||||
assert bot.get_my_commands() == []
|
||||
assert bot.commands == []
|
||||
assert bot.set_my_commands(commands)
|
||||
|
||||
for bc in [bot.get_my_commands(), bot.commands]:
|
||||
assert len(bc) == 2
|
||||
assert bc[0].command == 'cmd1'
|
||||
assert bc[0].description == 'descr1'
|
||||
assert bc[1].command == 'cmd2'
|
||||
assert bc[1].description == 'descr2'
|
||||
|
||||
@flaky(3, 1)
|
||||
def test_set_and_get_my_commands_strings(self, bot):
|
||||
commands = [
|
||||
['cmd1', 'descr1'],
|
||||
['cmd2', 'descr2'],
|
||||
]
|
||||
bot.set_my_commands([])
|
||||
assert bot.get_my_commands() == []
|
||||
assert bot.commands == []
|
||||
assert bot.set_my_commands(commands)
|
||||
|
||||
for bc in [bot.get_my_commands(), bot.commands]:
|
||||
assert len(bc) == 2
|
||||
assert bc[0].command == 'cmd1'
|
||||
assert bc[0].description == 'descr1'
|
||||
assert bc[1].command == 'cmd2'
|
||||
assert bc[1].description == 'descr2'
|
||||
for i, bc in enumerate(bot.get_my_commands()):
|
||||
assert bc.command == f'cmd{i+1}'
|
||||
assert bc.description == f'descr{i+1}'
|
||||
|
||||
@flaky(3, 1)
|
||||
def test_get_set_delete_my_commands_with_scope(self, bot, super_group_id, chat_id):
|
||||
|
@ -2114,9 +2061,6 @@ class TestBot:
|
|||
assert len(gotten_private_cmd) == len(private_cmds)
|
||||
assert gotten_private_cmd[0].command == private_cmds[0].command
|
||||
|
||||
assert len(bot.commands) == 2 # set from previous test. Makes sure this hasn't changed.
|
||||
assert bot.commands[0].command == 'cmd1'
|
||||
|
||||
# Delete command list from that supergroup and private chat-
|
||||
bot.delete_my_commands(private_scope)
|
||||
bot.delete_my_commands(group_scope, 'en')
|
||||
|
@ -2129,7 +2073,7 @@ class TestBot:
|
|||
assert len(deleted_priv_cmds) == 0 == len(private_cmds) - 1
|
||||
|
||||
bot.delete_my_commands() # Delete commands from default scope
|
||||
assert not bot.commands # Check if this has been updated to reflect the deletion.
|
||||
assert len(bot.get_my_commands()) == 0
|
||||
|
||||
def test_log_out(self, monkeypatch, bot):
|
||||
# We don't actually make a request as to not break the test setup
|
||||
|
|
|
@ -196,15 +196,6 @@ class TestChat:
|
|||
monkeypatch.setattr(chat.bot, 'get_chat_member_count', make_assertion)
|
||||
assert chat.get_member_count()
|
||||
|
||||
def test_get_members_count_warning(self, chat, monkeypatch, recwarn):
|
||||
def make_assertion(*_, **kwargs):
|
||||
return kwargs['chat_id'] == chat.id
|
||||
|
||||
monkeypatch.setattr(chat.bot, 'get_chat_member_count', make_assertion)
|
||||
assert chat.get_members_count()
|
||||
assert len(recwarn) == 1
|
||||
assert '`Chat.get_members_count` is deprecated' in str(recwarn[0].message)
|
||||
|
||||
def test_get_member(self, monkeypatch, chat):
|
||||
def make_assertion(*_, **kwargs):
|
||||
chat_id = kwargs['chat_id'] == chat.id
|
||||
|
@ -262,18 +253,6 @@ class TestChat:
|
|||
monkeypatch.setattr(chat.bot, 'ban_chat_sender_chat', make_assertion)
|
||||
assert chat.ban_chat(42)
|
||||
|
||||
def test_kick_member_warning(self, chat, monkeypatch, recwarn):
|
||||
def make_assertion(*_, **kwargs):
|
||||
chat_id = kwargs['chat_id'] == chat.id
|
||||
user_id = kwargs['user_id'] == 42
|
||||
until = kwargs['until_date'] == 43
|
||||
return chat_id and user_id and until
|
||||
|
||||
monkeypatch.setattr(chat.bot, 'ban_chat_member', make_assertion)
|
||||
assert chat.kick_member(user_id=42, until_date=43)
|
||||
assert len(recwarn) == 1
|
||||
assert '`Chat.kick_member` is deprecated' in str(recwarn[0].message)
|
||||
|
||||
@pytest.mark.parametrize('only_if_banned', [True, False, None])
|
||||
def test_unban_member(self, monkeypatch, chat, only_if_banned):
|
||||
def make_assertion(*_, **kwargs):
|
||||
|
|
|
@ -197,7 +197,7 @@ class TestCommandHandler(BaseTest):
|
|||
|
||||
def test_with_filter(self, command):
|
||||
"""Test that a CH with a (generic) filter responds if its filters match"""
|
||||
handler = self.make_default_handler(filters=Filters.group)
|
||||
handler = self.make_default_handler(filters=Filters.chat_type.group)
|
||||
assert is_match(handler, make_command_update(command, chat=Chat(-23, Chat.GROUP)))
|
||||
assert not is_match(handler, make_command_update(command, chat=Chat(23, Chat.PRIVATE)))
|
||||
|
||||
|
@ -321,7 +321,7 @@ class TestPrefixHandler(BaseTest):
|
|||
self._test_edited(prefix_message, handler_edited, handler_no_edited)
|
||||
|
||||
def test_with_filter(self, prefix_message_text):
|
||||
handler = self.make_default_handler(filters=Filters.group)
|
||||
handler = self.make_default_handler(filters=Filters.chat_type.group)
|
||||
text = prefix_message_text
|
||||
assert is_match(handler, make_message_update(text, chat=Chat(-23, Chat.GROUP)))
|
||||
assert not is_match(handler, make_message_update(text, chat=Chat(23, Chat.PRIVATE)))
|
||||
|
|
|
@ -35,8 +35,7 @@ from telegram.ext import (
|
|||
ContextTypes,
|
||||
)
|
||||
from telegram.ext import PersistenceInput
|
||||
from telegram.ext.dispatcher import run_async, Dispatcher, DispatcherHandlerStop
|
||||
from telegram.utils.deprecate import TelegramDeprecationWarning
|
||||
from telegram.ext.dispatcher import Dispatcher, DispatcherHandlerStop
|
||||
from telegram.utils.helpers import DEFAULT_FALSE
|
||||
from tests.conftest import create_dp
|
||||
from collections import defaultdict
|
||||
|
@ -58,12 +57,6 @@ class TestDispatcher:
|
|||
received = None
|
||||
count = 0
|
||||
|
||||
def test_slot_behaviour(self, dp2, mro_slots):
|
||||
for at in dp2.__slots__:
|
||||
at = f"_Dispatcher{at}" if at.startswith('__') and not at.endswith('__') else at
|
||||
assert getattr(dp2, at, 'err') != 'err', f"got extra slot '{at}'"
|
||||
assert len(mro_slots(dp2)) == len(set(mro_slots(dp2))), "duplicate slot"
|
||||
|
||||
@pytest.fixture(autouse=True, name='reset')
|
||||
def reset_fixture(self):
|
||||
self.reset()
|
||||
|
@ -103,6 +96,13 @@ class TestDispatcher:
|
|||
):
|
||||
self.received = context.error.message
|
||||
|
||||
def test_slot_behaviour(self, bot, mro_slots):
|
||||
dp = Dispatcher(bot=bot, update_queue=None)
|
||||
for at in dp.__slots__:
|
||||
at = f"_Dispatcher{at}" if at.startswith('__') and not at.endswith('__') else at
|
||||
assert getattr(dp, at, 'err') != 'err', f"got extra slot '{at}'"
|
||||
assert len(mro_slots(dp)) == len(set(mro_slots(dp))), "duplicate slot"
|
||||
|
||||
def test_less_than_one_worker_warning(self, dp, recwarn):
|
||||
Dispatcher(dp.bot, dp.update_queue, job_queue=dp.job_queue, workers=0)
|
||||
assert len(recwarn) == 1
|
||||
|
@ -243,37 +243,11 @@ class TestDispatcher:
|
|||
|
||||
assert name1 != name2
|
||||
|
||||
def test_multiple_run_async_decorator(self, dp, dp2):
|
||||
# Make sure we got two dispatchers and that they are not the same
|
||||
assert isinstance(dp, Dispatcher)
|
||||
assert isinstance(dp2, Dispatcher)
|
||||
assert dp is not dp2
|
||||
|
||||
@run_async
|
||||
def must_raise_runtime_error():
|
||||
pass
|
||||
|
||||
with pytest.raises(RuntimeError):
|
||||
must_raise_runtime_error()
|
||||
|
||||
def test_multiple_run_async_deprecation(self, dp):
|
||||
assert isinstance(dp, Dispatcher)
|
||||
|
||||
@run_async
|
||||
def callback(update, context):
|
||||
pass
|
||||
|
||||
dp.add_handler(MessageHandler(Filters.all, callback))
|
||||
|
||||
with pytest.warns(TelegramDeprecationWarning, match='@run_async decorator'):
|
||||
dp.process_update(self.message_update)
|
||||
|
||||
def test_async_raises_dispatcher_handler_stop(self, dp, caplog):
|
||||
@run_async
|
||||
def callback(update, context):
|
||||
raise DispatcherHandlerStop()
|
||||
|
||||
dp.add_handler(MessageHandler(Filters.all, callback))
|
||||
dp.add_handler(MessageHandler(Filters.all, callback, run_async=True))
|
||||
|
||||
with caplog.at_level(logging.WARNING):
|
||||
dp.update_queue.put(self.message_update)
|
||||
|
@ -282,24 +256,7 @@ class TestDispatcher:
|
|||
assert (
|
||||
caplog.records[-1]
|
||||
.getMessage()
|
||||
.startswith('DispatcherHandlerStop is not supported ' 'with async functions')
|
||||
)
|
||||
|
||||
def test_async_raises_exception(self, dp, caplog):
|
||||
@run_async
|
||||
def callback(update, context):
|
||||
raise RuntimeError('async raising exception')
|
||||
|
||||
dp.add_handler(MessageHandler(Filters.all, callback))
|
||||
|
||||
with caplog.at_level(logging.WARNING):
|
||||
dp.update_queue.put(self.message_update)
|
||||
sleep(0.1)
|
||||
assert len(caplog.records) == 1
|
||||
assert (
|
||||
caplog.records[-1]
|
||||
.getMessage()
|
||||
.startswith('A promise with deactivated error handling')
|
||||
.startswith('DispatcherHandlerStop is not supported with async functions')
|
||||
)
|
||||
|
||||
def test_add_async_handler(self, dp):
|
||||
|
|
|
@ -26,8 +26,6 @@ from telegram.ext import Filters, BaseFilter, MessageFilter, UpdateFilter
|
|||
import inspect
|
||||
import re
|
||||
|
||||
from telegram.utils.deprecate import TelegramDeprecationWarning
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def update():
|
||||
|
@ -971,26 +969,6 @@ class TestFilters:
|
|||
assert Filters.caption_entity(message_entity.type)(update)
|
||||
assert not Filters.entity(message_entity.type)(update)
|
||||
|
||||
def test_private_filter(self, update):
|
||||
assert Filters.private(update)
|
||||
update.message.chat.type = 'group'
|
||||
assert not Filters.private(update)
|
||||
|
||||
def test_private_filter_deprecation(self, update):
|
||||
with pytest.warns(TelegramDeprecationWarning):
|
||||
Filters.private(update)
|
||||
|
||||
def test_group_filter(self, update):
|
||||
assert not Filters.group(update)
|
||||
update.message.chat.type = 'group'
|
||||
assert Filters.group(update)
|
||||
update.message.chat.type = 'supergroup'
|
||||
assert Filters.group(update)
|
||||
|
||||
def test_group_filter_deprecation(self, update):
|
||||
with pytest.warns(TelegramDeprecationWarning):
|
||||
Filters.group(update)
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
('chat_type, results'),
|
||||
[
|
||||
|
@ -1832,7 +1810,7 @@ class TestFilters:
|
|||
|
||||
update.message.text = 'test'
|
||||
update.message.forward_date = datetime.datetime.utcnow()
|
||||
assert (Filters.text & Filters.forwarded & Filters.private)(update)
|
||||
assert (Filters.text & Filters.forwarded & Filters.chat_type.private)(update)
|
||||
|
||||
def test_or_filters(self, update):
|
||||
update.message.text = 'test'
|
||||
|
|
|
@ -91,7 +91,6 @@ class TestJobQueue:
|
|||
and context.chat_data is None
|
||||
and context.user_data is None
|
||||
and isinstance(context.bot_data, dict)
|
||||
and context.job_queue is not context.job.job_queue
|
||||
):
|
||||
self.result += 1
|
||||
|
||||
|
|
|
@ -120,7 +120,7 @@ class TestMessageHandler:
|
|||
self.test_flag = types and num
|
||||
|
||||
def test_with_filter(self, message):
|
||||
handler = MessageHandler(Filters.group, self.callback_context)
|
||||
handler = MessageHandler(Filters.chat_type.group, self.callback_context)
|
||||
|
||||
message.chat.type = 'group'
|
||||
assert handler.check_update(Update(0, message))
|
||||
|
|
|
@ -1,69 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2022
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
import os
|
||||
from time import sleep, perf_counter
|
||||
|
||||
import pytest
|
||||
|
||||
import telegram.ext.messagequeue as mq
|
||||
|
||||
|
||||
@pytest.mark.skipif(
|
||||
os.getenv('GITHUB_ACTIONS', False) and os.name == 'nt',
|
||||
reason="On windows precise timings are not accurate.",
|
||||
)
|
||||
class TestDelayQueue:
|
||||
N = 128
|
||||
burst_limit = 30
|
||||
time_limit_ms = 1000
|
||||
margin_ms = 0
|
||||
testtimes = []
|
||||
|
||||
def call(self):
|
||||
self.testtimes.append(perf_counter())
|
||||
|
||||
def test_delayqueue_limits(self):
|
||||
dsp = mq.DelayQueue(
|
||||
burst_limit=self.burst_limit, time_limit_ms=self.time_limit_ms, autostart=True
|
||||
)
|
||||
assert dsp.is_alive() is True
|
||||
|
||||
for _ in range(self.N):
|
||||
dsp(self.call)
|
||||
|
||||
starttime = perf_counter()
|
||||
# wait up to 20 sec more than needed
|
||||
app_endtime = (self.N * self.burst_limit / (1000 * self.time_limit_ms)) + starttime + 20
|
||||
while not dsp._queue.empty() and perf_counter() < app_endtime:
|
||||
sleep(1)
|
||||
assert dsp._queue.empty() is True # check loop exit condition
|
||||
|
||||
dsp.stop()
|
||||
assert dsp.is_alive() is False
|
||||
|
||||
assert self.testtimes or self.N == 0
|
||||
passes, fails = [], []
|
||||
delta = (self.time_limit_ms - self.margin_ms) / 1000
|
||||
for start, stop in enumerate(range(self.burst_limit + 1, len(self.testtimes))):
|
||||
part = self.testtimes[start:stop]
|
||||
if (part[-1] - part[0]) >= delta:
|
||||
passes.append(part)
|
||||
else:
|
||||
fails.append(part)
|
||||
assert not fails
|
|
@ -27,7 +27,6 @@ import inspect
|
|||
included = { # These modules/classes intentionally have __dict__.
|
||||
'CallbackContext',
|
||||
'BasePersistence',
|
||||
'Dispatcher',
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -301,7 +301,6 @@ class TestUpdater:
|
|||
monkeypatch.setattr(updater.bot, 'delete_webhook', lambda *args, **kwargs: True)
|
||||
# prevent api calls from @info decorator when updater.bot.id is used in thread names
|
||||
monkeypatch.setattr(updater.bot, '_bot', User(id=123, first_name='bot', is_bot=True))
|
||||
monkeypatch.setattr(updater.bot, '_commands', [])
|
||||
|
||||
ip = '127.0.0.1'
|
||||
port = randrange(1024, 49152) # Select random port
|
||||
|
@ -480,57 +479,6 @@ class TestUpdater:
|
|||
)
|
||||
assert self.test_flag is True
|
||||
|
||||
def test_deprecation_warnings_start_webhook(self, recwarn, updater, monkeypatch):
|
||||
monkeypatch.setattr(updater.bot, 'set_webhook', lambda *args, **kwargs: True)
|
||||
monkeypatch.setattr(updater.bot, 'delete_webhook', lambda *args, **kwargs: True)
|
||||
# prevent api calls from @info decorator when updater.bot.id is used in thread names
|
||||
monkeypatch.setattr(updater.bot, '_bot', User(id=123, first_name='bot', is_bot=True))
|
||||
monkeypatch.setattr(updater.bot, '_commands', [])
|
||||
|
||||
ip = '127.0.0.1'
|
||||
port = randrange(1024, 49152) # Select random port
|
||||
updater.start_webhook(ip, port, clean=True, force_event_loop=False)
|
||||
updater.stop()
|
||||
|
||||
for warning in recwarn:
|
||||
print(warning)
|
||||
|
||||
try: # This is for flaky tests (there's an unclosed socket sometimes)
|
||||
recwarn.pop(ResourceWarning) # internally iterates through recwarn.list and deletes it
|
||||
except AssertionError:
|
||||
pass
|
||||
|
||||
assert len(recwarn) == 2
|
||||
assert str(recwarn[0].message).startswith('The argument `clean` of')
|
||||
assert str(recwarn[1].message).startswith('The argument `force_event_loop` of')
|
||||
|
||||
def test_clean_deprecation_warning_polling(self, recwarn, updater, monkeypatch):
|
||||
monkeypatch.setattr(updater.bot, 'set_webhook', lambda *args, **kwargs: True)
|
||||
monkeypatch.setattr(updater.bot, 'delete_webhook', lambda *args, **kwargs: True)
|
||||
# prevent api calls from @info decorator when updater.bot.id is used in thread names
|
||||
monkeypatch.setattr(updater.bot, '_bot', User(id=123, first_name='bot', is_bot=True))
|
||||
monkeypatch.setattr(updater.bot, '_commands', [])
|
||||
|
||||
updater.start_polling(clean=True)
|
||||
updater.stop()
|
||||
for msg in recwarn:
|
||||
print(msg)
|
||||
|
||||
try: # This is for flaky tests (there's an unclosed socket sometimes)
|
||||
recwarn.pop(ResourceWarning) # internally iterates through recwarn.list and deletes it
|
||||
except AssertionError:
|
||||
pass
|
||||
|
||||
assert len(recwarn) == 1
|
||||
assert str(recwarn[0].message).startswith('The argument `clean` of')
|
||||
|
||||
def test_clean_drop_pending_mutually_exclusive(self, updater):
|
||||
with pytest.raises(TypeError, match='`clean` and `drop_pending_updates` are mutually'):
|
||||
updater.start_polling(clean=True, drop_pending_updates=False)
|
||||
|
||||
with pytest.raises(TypeError, match='`clean` and `drop_pending_updates` are mutually'):
|
||||
updater.start_webhook(clean=True, drop_pending_updates=False)
|
||||
|
||||
@flaky(3, 1)
|
||||
def test_webhook_invalid_posts(self, updater):
|
||||
ip = '127.0.0.1'
|
||||
|
|
|
@ -1,37 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2022
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
|
||||
|
||||
class TestUtils:
|
||||
def test_promise_deprecation(self, recwarn):
|
||||
import telegram.utils.promise # noqa: F401
|
||||
|
||||
assert len(recwarn) == 1
|
||||
assert str(recwarn[0].message) == (
|
||||
'telegram.utils.promise is deprecated. Please use telegram.ext.utils.promise instead.'
|
||||
)
|
||||
|
||||
def test_webhookhandler_deprecation(self, recwarn):
|
||||
import telegram.utils.webhookhandler # noqa: F401
|
||||
|
||||
assert len(recwarn) == 1
|
||||
assert str(recwarn[0].message) == (
|
||||
'telegram.utils.webhookhandler is deprecated. Please use '
|
||||
'telegram.ext.utils.webhookhandler instead.'
|
||||
)
|
Loading…
Reference in a new issue