mirror of
https://github.com/python-telegram-bot/python-telegram-bot.git
synced 2025-03-13 11:18:20 +01:00
Update Filters, CommandHandler and MessageHandler (#1221)
* update_filter attribute on filters Makes it possible to have filters work on an update instead of message, while keeping behavior for current filters * add update_type filter * Messagehandler rework - remove allow_edited (deprecated for a while) - set deprecated defaults to None - Raise deprecation warning when they're used - add sensible defaults for filters. - rework tests * Commandhandler rework * Remove deprecation test from new handler * Some tweaks per CR - rename update_types -> updates - added some clarification to docstrings * run webhook set test only on 3.6 on appveyor * update_filter attribute on filters Makes it possible to have filters work on an update instead of message, while keeping behavior for current filters * add update_type filter * Messagehandler rework - remove allow_edited (deprecated for a while) - set deprecated defaults to None - Raise deprecation warning when they're used - add sensible defaults for filters. - rework tests * Commandhandler rework * Remove deprecation test from new handler * Some tweaks per CR - rename update_types -> updates - added some clarification to docstrings * run webhook set test only on 3.6 on appveyor * Changes per CR * Update travis to build v12 * small doc update * try to make ci build version branches * doc for BaseFilter * Modify regexfilter and mergedfilter Now returns a list of match objects for every regexfilter * Change callbackcontext (+ docs) * integrate in CommandHandler and PrefixHandler * integrate in MessageHandler * cbqhandler, iqhandler and srhandler * make regexhandler a shell over MessageHandler And raise deprecationWarning on creation * clean up code and add some comments * Rework based on internal group feedback - use data_filter instead of regex_filter on BaseFilter - have these filters return a dict that is then updated onto CallbackContext instead of using a list is before - Add a .match property on CallbackContext that returns .matches[0] or None * Fix and add test for callbackcontext.match * Lots of documentation fixes and improvements [ci skip]
This commit is contained in:
parent
950ec35970
commit
2c5eade4f0
24 changed files with 1116 additions and 591 deletions
|
@ -6,7 +6,7 @@ Changes
|
|||
*Released 11.0.0*
|
||||
|
||||
Context based callbacks:
|
||||
See https://git.io/vp113 for help.
|
||||
See https://git.io/fxJuV for help.
|
||||
|
||||
- Use of `pass_` in handlers is deprecated.
|
||||
- Instead use `use_context=True` on `Updater` or `Dispatcher` and change callback from (bot, update, others...) to (update, context).
|
||||
|
|
|
@ -28,10 +28,10 @@ from .jobqueue import JobQueue, Job
|
|||
from .updater import Updater
|
||||
from .callbackqueryhandler import CallbackQueryHandler
|
||||
from .choseninlineresulthandler import ChosenInlineResultHandler
|
||||
from .commandhandler import CommandHandler, PrefixHandler
|
||||
from .inlinequeryhandler import InlineQueryHandler
|
||||
from .messagehandler import MessageHandler
|
||||
from .filters import BaseFilter, Filters
|
||||
from .messagehandler import MessageHandler
|
||||
from .commandhandler import CommandHandler, PrefixHandler
|
||||
from .regexhandler import RegexHandler
|
||||
from .stringcommandhandler import StringCommandHandler
|
||||
from .stringregexhandler import StringRegexHandler
|
||||
|
|
|
@ -33,9 +33,9 @@ class CallbackContext(object):
|
|||
update from the same chat it will be the same ``dict``.
|
||||
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``.
|
||||
match (:obj:`re match object`, optional): If the associated update originated from a
|
||||
regex-supported handler, this will contain the object returned from
|
||||
``re.match(pattern, string)``.
|
||||
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.
|
||||
args (List[:obj:`str`], optional): Arguments passed to a command if the associated update
|
||||
is handled by :class:`telegram.ext.CommandHandler`, :class:`telegram.ext.PrefixHandler`
|
||||
or :class:`telegram.ext.StringCommandHandler`. It contains a list of the words in the
|
||||
|
@ -60,7 +60,7 @@ class CallbackContext(object):
|
|||
self.chat_data = None
|
||||
self.user_data = None
|
||||
self.args = None
|
||||
self.match = None
|
||||
self.matches = None
|
||||
self.error = None
|
||||
self.job = None
|
||||
|
||||
|
@ -89,6 +89,9 @@ class CallbackContext(object):
|
|||
self.job = job
|
||||
return self
|
||||
|
||||
def update(self, data):
|
||||
self.__dict__.update(data)
|
||||
|
||||
@property
|
||||
def bot(self):
|
||||
""":class:`telegram.Bot`: The bot associated with this context."""
|
||||
|
@ -113,3 +116,15 @@ class CallbackContext(object):
|
|||
|
||||
"""
|
||||
return self._dispatcher.update_queue
|
||||
|
||||
@property
|
||||
def match(self):
|
||||
"""
|
||||
`Regex match type`: The first match from :attr:`matches`.
|
||||
Useful if you are only filtering using a single regex filter.
|
||||
Returns `None` if :attr:`matches` is empty.
|
||||
"""
|
||||
try:
|
||||
return self.matches[0] # pylint: disable=unsubscriptable-object
|
||||
except (IndexError, TypeError):
|
||||
return None
|
||||
|
|
|
@ -55,7 +55,7 @@ class CallbackQueryHandler(Handler):
|
|||
or in the same chat, it will be the same ``dict``.
|
||||
|
||||
Note that this is DEPRECATED, and you should use context based callbacks. See
|
||||
https://git.io/vp113 for more info.
|
||||
https://git.io/fxJuV for more info.
|
||||
|
||||
Args:
|
||||
callback (:obj:`callable`): The callback function for this handler. Will be called when
|
||||
|
@ -151,4 +151,4 @@ class CallbackQueryHandler(Handler):
|
|||
|
||||
def collect_additional_context(self, context, update, dispatcher, check_result):
|
||||
if self.pattern:
|
||||
context.match = check_result
|
||||
context.matches = [check_result]
|
||||
|
|
|
@ -43,7 +43,7 @@ class ChosenInlineResultHandler(Handler):
|
|||
or in the same chat, it will be the same ``dict``.
|
||||
|
||||
Note that this is DEPRECATED, and you should use context based callbacks. See
|
||||
https://git.io/vp113 for more info.
|
||||
https://git.io/fxJuV for more info.
|
||||
|
||||
Args:
|
||||
callback (:obj:`callable`): The callback function for this handler. Will be called when
|
||||
|
|
|
@ -18,9 +18,13 @@
|
|||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
"""This module contains the CommandHandler and PrefixHandler classes."""
|
||||
import re
|
||||
import warnings
|
||||
|
||||
from future.utils import string_types
|
||||
|
||||
from telegram.ext import Filters
|
||||
from telegram.utils.deprecate import TelegramDeprecationWarning
|
||||
|
||||
from telegram import Update, MessageEntity
|
||||
from .handler import Handler
|
||||
|
||||
|
@ -33,6 +37,9 @@ class CommandHandler(Handler):
|
|||
:class:`CallbackContext` named :attr:`CallbackContext.args`. It will contain a list of strings,
|
||||
which is the text following the command split on single or consecutive whitespace characters.
|
||||
|
||||
By default the handler listens to messages as well as edited messages. To change this behavior
|
||||
use ``~Filters.update.edited_message`` in the filter argument.
|
||||
|
||||
Attributes:
|
||||
command (:obj:`str` | List[:obj:`str`]): The command or list of commands this handler
|
||||
should listen for. Limitations are the same as described here
|
||||
|
@ -60,7 +67,7 @@ class CommandHandler(Handler):
|
|||
or in the same chat, it will be the same ``dict``.
|
||||
|
||||
Note that this is DEPRECATED, and you should use context based callbacks. See
|
||||
https://git.io/vp113 for more info.
|
||||
https://git.io/fxJuV for more info.
|
||||
|
||||
Args:
|
||||
command (:obj:`str` | List[:obj:`str`]): The command or list of commands this handler
|
||||
|
@ -80,6 +87,8 @@ class CommandHandler(Handler):
|
|||
operators (& for and, | for or, ~ for not).
|
||||
allow_edited (:obj:`bool`, optional): Determines whether the handler should also accept
|
||||
edited messages. Default is ``False``.
|
||||
DEPRECATED: Edited is allowed by default. To change this behavior use
|
||||
``~Filters.update.edited_message``.
|
||||
pass_args (:obj:`bool`, optional): Determines whether the handler should be passed the
|
||||
arguments passed to the command as a keyword argument called ``args``. It will contain
|
||||
a list of strings, which is the text following the command split on single or
|
||||
|
@ -110,7 +119,7 @@ class CommandHandler(Handler):
|
|||
command,
|
||||
callback,
|
||||
filters=None,
|
||||
allow_edited=False,
|
||||
allow_edited=None,
|
||||
pass_args=False,
|
||||
pass_update_queue=False,
|
||||
pass_job_queue=False,
|
||||
|
@ -131,8 +140,17 @@ class CommandHandler(Handler):
|
|||
if not re.match(r'^[\da-z_]{1,32}$', comm):
|
||||
raise ValueError('Command is not a valid bot command')
|
||||
|
||||
self.filters = filters
|
||||
self.allow_edited = allow_edited
|
||||
if filters:
|
||||
self.filters = Filters.update.messages & filters
|
||||
else:
|
||||
self.filters = Filters.update.messages
|
||||
|
||||
if allow_edited is not None:
|
||||
warnings.warn('allow_edited is deprecated. See https://git.io/fxJuV for more info',
|
||||
TelegramDeprecationWarning,
|
||||
stacklevel=2)
|
||||
if not allow_edited:
|
||||
self.filters &= ~Filters.update.edited_message
|
||||
self.pass_args = pass_args
|
||||
|
||||
def check_update(self, update):
|
||||
|
@ -142,11 +160,10 @@ class CommandHandler(Handler):
|
|||
update (:class:`telegram.Update`): Incoming telegram update.
|
||||
|
||||
Returns:
|
||||
:obj:`bool`
|
||||
:obj:`list`: The list of args for the handler
|
||||
|
||||
"""
|
||||
if (isinstance(update, Update)
|
||||
and (update.message or update.edited_message and self.allow_edited)):
|
||||
if isinstance(update, Update) and update.effective_message:
|
||||
message = update.effective_message
|
||||
|
||||
if (message.entities and message.entities[0].type == MessageEntity.BOT_COMMAND
|
||||
|
@ -160,17 +177,22 @@ class CommandHandler(Handler):
|
|||
and command[1].lower() == message.bot.username.lower()):
|
||||
return None
|
||||
|
||||
if self.filters is None or self.filters(message):
|
||||
return args
|
||||
filter_result = self.filters(update)
|
||||
if filter_result:
|
||||
return args, filter_result
|
||||
else:
|
||||
return False
|
||||
|
||||
def collect_optional_args(self, dispatcher, update=None, check_result=None):
|
||||
optional_args = super(CommandHandler, self).collect_optional_args(dispatcher, update)
|
||||
if self.pass_args:
|
||||
optional_args['args'] = check_result
|
||||
optional_args['args'] = check_result[0]
|
||||
return optional_args
|
||||
|
||||
def collect_additional_context(self, context, update, dispatcher, check_result):
|
||||
context.args = check_result
|
||||
context.args = check_result[0]
|
||||
if isinstance(check_result[1], dict):
|
||||
context.update(check_result[1])
|
||||
|
||||
|
||||
class PrefixHandler(CommandHandler):
|
||||
|
@ -198,6 +220,10 @@ class PrefixHandler(CommandHandler):
|
|||
PrefixHandler(['!', '#'], ['test', 'help`], callback) will respond to '!test',
|
||||
'#test', '!help' and '#help'.
|
||||
|
||||
|
||||
By default the handler listens to messages as well as edited messages. To change this behavior
|
||||
use ~``Filters.update.edited_message``.
|
||||
|
||||
Attributes:
|
||||
prefix (:obj:`str` | List[:obj:`str`]): The prefix(es) that will precede :attr:`command`.
|
||||
command (:obj:`str` | List[:obj:`str`]): The command or list of commands this handler
|
||||
|
@ -205,8 +231,6 @@ class PrefixHandler(CommandHandler):
|
|||
callback (:obj:`callable`): The callback function for this handler.
|
||||
filters (:class:`telegram.ext.BaseFilter`): Optional. Only allow updates with these
|
||||
Filters.
|
||||
allow_edited (:obj:`bool`): Determines Whether the handler should also accept
|
||||
edited messages.
|
||||
pass_args (:obj:`bool`): Determines whether the handler should be passed
|
||||
``args``.
|
||||
pass_update_queue (:obj:`bool`): Determines whether ``update_queue`` will be
|
||||
|
@ -225,7 +249,7 @@ class PrefixHandler(CommandHandler):
|
|||
or in the same chat, it will be the same ``dict``.
|
||||
|
||||
Note that this is DEPRECATED, and you should use context based callbacks. See
|
||||
https://git.io/vp113 for more info.
|
||||
https://git.io/fxJuV for more info.
|
||||
|
||||
Args:
|
||||
prefix (:obj:`str` | List[:obj:`str`]): The prefix(es) that will precede :attr:`command`.
|
||||
|
@ -243,8 +267,6 @@ class PrefixHandler(CommandHandler):
|
|||
:class:`telegram.ext.filters.BaseFilter`. Standard filters can be found in
|
||||
:class:`telegram.ext.filters.Filters`. Filters can be combined using bitwise
|
||||
operators (& for and, | for or, ~ for not).
|
||||
allow_edited (:obj:`bool`, optional): Determines whether the handler should also accept
|
||||
edited messages. Default is ``False``.
|
||||
pass_args (:obj:`bool`, optional): Determines whether the handler should be passed the
|
||||
arguments passed to the command as a keyword argument called ``args``. It will contain
|
||||
a list of strings, which is the text following the command split on single or
|
||||
|
@ -274,7 +296,6 @@ class PrefixHandler(CommandHandler):
|
|||
command,
|
||||
callback,
|
||||
filters=None,
|
||||
allow_edited=False,
|
||||
pass_args=False,
|
||||
pass_update_queue=False,
|
||||
pass_job_queue=False,
|
||||
|
@ -282,7 +303,7 @@ class PrefixHandler(CommandHandler):
|
|||
pass_chat_data=False):
|
||||
|
||||
super(PrefixHandler, self).__init__(
|
||||
'nocommand', callback, filters=filters, allow_edited=allow_edited, pass_args=pass_args,
|
||||
'nocommand', callback, filters=filters, allow_edited=None, pass_args=pass_args,
|
||||
pass_update_queue=pass_update_queue,
|
||||
pass_job_queue=pass_job_queue,
|
||||
pass_user_data=pass_user_data,
|
||||
|
@ -305,15 +326,22 @@ class PrefixHandler(CommandHandler):
|
|||
update (:class:`telegram.Update`): Incoming telegram update.
|
||||
|
||||
Returns:
|
||||
:obj:`bool`
|
||||
:obj:`list`: The list of args for the handler
|
||||
|
||||
"""
|
||||
if (isinstance(update, Update)
|
||||
and (update.message or update.edited_message and self.allow_edited)):
|
||||
if isinstance(update, Update) and update.effective_message:
|
||||
message = update.effective_message
|
||||
|
||||
text_list = message.text.split()
|
||||
if text_list[0].lower() not in self.command:
|
||||
return None
|
||||
if self.filters is None or self.filters(message):
|
||||
return text_list[1:]
|
||||
filter_result = self.filters(update)
|
||||
if filter_result:
|
||||
return text_list[1:], filter_result
|
||||
else:
|
||||
return False
|
||||
|
||||
def collect_additional_context(self, context, update, dispatcher, check_result):
|
||||
context.args = check_result[0]
|
||||
if isinstance(check_result[1], dict):
|
||||
context.update(check_result[1])
|
||||
|
|
|
@ -115,7 +115,7 @@ class Dispatcher(object):
|
|||
self.use_context = use_context
|
||||
|
||||
if not use_context:
|
||||
warnings.warn('Old Handler API is deprecated - see https://git.io/vp113 for details',
|
||||
warnings.warn('Old Handler API is deprecated - see https://git.io/fxJuV for details',
|
||||
TelegramDeprecationWarning, stacklevel=3)
|
||||
|
||||
self.user_data = defaultdict(dict)
|
||||
|
@ -431,7 +431,7 @@ class Dispatcher(object):
|
|||
The error that happened will be present in context.error.
|
||||
|
||||
Note:
|
||||
See https://git.io/vp113 for more info about switching to context based API.
|
||||
See https://git.io/fxJuV for more info about switching to context based API.
|
||||
"""
|
||||
self.error_handlers.append(callback)
|
||||
|
||||
|
|
|
@ -19,9 +19,11 @@
|
|||
"""This module contains the Filters for use with the MessageHandler class."""
|
||||
|
||||
import re
|
||||
from telegram import Chat
|
||||
|
||||
from future.utils import string_types
|
||||
|
||||
from telegram import Chat
|
||||
|
||||
|
||||
class BaseFilter(object):
|
||||
"""Base class for all Message Filters.
|
||||
|
@ -56,13 +58,23 @@ class BaseFilter(object):
|
|||
|
||||
Attributes:
|
||||
name (:obj:`str`): Name for this filter. Defaults to the type of filter.
|
||||
|
||||
update_filter (:obj:`bool`): Whether this filter should work on update. If ``False`` it
|
||||
will run the filter on :attr:`update.effective_message``. Default is ``False``.
|
||||
data_filter (:obj:`bool`): Whether this filter is a data filter. A data filter should
|
||||
return a dict with lists. The dict will be merged with
|
||||
:class:`telegram.ext.CallbackContext`'s internal dict in most cases
|
||||
(depends on the handler).
|
||||
"""
|
||||
|
||||
name = None
|
||||
update_filter = False
|
||||
data_filter = False
|
||||
|
||||
def __call__(self, message):
|
||||
return self.filter(message)
|
||||
def __call__(self, update):
|
||||
if self.update_filter:
|
||||
return self.filter(update)
|
||||
else:
|
||||
return self.filter(update.effective_message)
|
||||
|
||||
def __and__(self, other):
|
||||
return MergedFilter(self, and_filter=other)
|
||||
|
@ -79,14 +91,18 @@ class BaseFilter(object):
|
|||
self.name = self.__class__.__name__
|
||||
return self.name
|
||||
|
||||
def filter(self, message):
|
||||
def filter(self, update):
|
||||
"""This method must be overwritten.
|
||||
|
||||
Note:
|
||||
If :attr:`update_filter` is false then the first argument is `message` and of
|
||||
type :class:`telegram.Message`.
|
||||
|
||||
Args:
|
||||
message (:class:`telegram.Message`): The message that is tested.
|
||||
update (:class:`telegram.Update`): The update that is tested.
|
||||
|
||||
Returns:
|
||||
:obj:`bool`
|
||||
:obj:`dict` or :obj:`bool`
|
||||
|
||||
"""
|
||||
|
||||
|
@ -100,12 +116,13 @@ class InvertedFilter(BaseFilter):
|
|||
f: The filter to invert.
|
||||
|
||||
"""
|
||||
update_filter = True
|
||||
|
||||
def __init__(self, f):
|
||||
self.f = f
|
||||
|
||||
def filter(self, message):
|
||||
return not self.f(message)
|
||||
def filter(self, update):
|
||||
return not bool(self.f(update))
|
||||
|
||||
def __repr__(self):
|
||||
return "<inverted {}>".format(self.f)
|
||||
|
@ -120,17 +137,60 @@ class MergedFilter(BaseFilter):
|
|||
or_filter: Optional filter to "or" with base_filter. Mutually exclusive with and_filter.
|
||||
|
||||
"""
|
||||
update_filter = True
|
||||
|
||||
def __init__(self, base_filter, and_filter=None, or_filter=None):
|
||||
self.base_filter = base_filter
|
||||
if self.base_filter.data_filter:
|
||||
self.data_filter = True
|
||||
self.and_filter = and_filter
|
||||
if (self.and_filter
|
||||
and not isinstance(self.and_filter, bool)
|
||||
and self.and_filter.data_filter):
|
||||
self.data_filter = True
|
||||
self.or_filter = or_filter
|
||||
if (self.or_filter
|
||||
and not isinstance(self.and_filter, bool)
|
||||
and self.or_filter.data_filter):
|
||||
self.data_filter = True
|
||||
|
||||
def filter(self, message):
|
||||
def _merge(self, base_output, comp_output):
|
||||
base = base_output if isinstance(base_output, dict) else {}
|
||||
comp = comp_output if isinstance(comp_output, dict) else {}
|
||||
for k in comp.keys():
|
||||
# Make sure comp values are lists
|
||||
comp_value = comp[k] if isinstance(comp[k], list) else []
|
||||
try:
|
||||
# If base is a list then merge
|
||||
if isinstance(base[k], list):
|
||||
base[k] += comp_value
|
||||
else:
|
||||
base[k] = [base[k]] + comp_value
|
||||
except KeyError:
|
||||
base[k] = comp_value
|
||||
return base
|
||||
|
||||
def filter(self, update):
|
||||
base_output = self.base_filter(update)
|
||||
# We need to check if the filters are data filters and if so return the merged data.
|
||||
# If it's not a data filter or an or_filter but no matches return bool
|
||||
if self.and_filter:
|
||||
return self.base_filter(message) and self.and_filter(message)
|
||||
comp_output = self.and_filter(update)
|
||||
if base_output and comp_output:
|
||||
if self.data_filter:
|
||||
merged = self._merge(base_output, comp_output)
|
||||
if merged:
|
||||
return merged
|
||||
return True
|
||||
elif self.or_filter:
|
||||
return self.base_filter(message) or self.or_filter(message)
|
||||
comp_output = self.or_filter(update)
|
||||
if base_output or comp_output:
|
||||
if self.data_filter:
|
||||
merged = self._merge(base_output, comp_output)
|
||||
if merged:
|
||||
return merged
|
||||
return True
|
||||
return False
|
||||
|
||||
def __repr__(self):
|
||||
return "<{} {} {}>".format(self.base_filter, "and" if self.and_filter else "or",
|
||||
|
@ -180,11 +240,7 @@ class Filters(object):
|
|||
|
||||
Refer to the documentation of the ``re`` module for more information.
|
||||
|
||||
Note:
|
||||
Does not allow passing groups or a groupdict like the ``RegexHandler`` yet,
|
||||
but this will probably be implemented in a future update, gradually phasing out the
|
||||
RegexHandler (See `Github Issue
|
||||
<https://github.com/python-telegram-bot/python-telegram-bot/issues/835/>`_).
|
||||
To get the groups and groupdict matched, see :attr:`telegram.ext.CallbackContext.matches`.
|
||||
|
||||
Examples:
|
||||
Use ``MessageHandler(Filters.regex(r'help'), callback)`` to capture all messages that
|
||||
|
@ -193,25 +249,25 @@ class Filters(object):
|
|||
you want your pattern to be case insensitive. This approach is recommended
|
||||
if you need to specify flags on your pattern.
|
||||
|
||||
|
||||
Args:
|
||||
pattern (:obj:`str` | :obj:`Pattern`): The regex pattern.
|
||||
"""
|
||||
|
||||
data_filter = True
|
||||
|
||||
def __init__(self, pattern):
|
||||
if isinstance(pattern, string_types):
|
||||
pattern = re.compile(pattern)
|
||||
self.pattern = pattern
|
||||
self.name = 'Filters.regex({})'.format(self.pattern)
|
||||
|
||||
# TODO: Once the callback revamp (#1026) is done, the regex filter should be able to pass
|
||||
# the matched groups and groupdict to the context object.
|
||||
|
||||
def filter(self, message):
|
||||
""":obj:`Filter`: Messages that have an occurrence of ``pattern``."""
|
||||
"""""" # remove method from docs
|
||||
if message.text:
|
||||
return bool(self.pattern.search(message.text))
|
||||
return False
|
||||
match = self.pattern.search(message.text)
|
||||
if match:
|
||||
return {'matches': [match]}
|
||||
return {}
|
||||
|
||||
class _Reply(BaseFilter):
|
||||
name = 'Filters.reply'
|
||||
|
@ -257,6 +313,7 @@ class Filters(object):
|
|||
self.name = "Filters.document.category('{}')".format(self.category)
|
||||
|
||||
def filter(self, message):
|
||||
"""""" # remove method from docs
|
||||
if message.document:
|
||||
return message.document.mime_type.startswith(self.category)
|
||||
|
||||
|
@ -288,6 +345,7 @@ class Filters(object):
|
|||
self.name = "Filters.document.mime_type('{}')".format(self.mimetype)
|
||||
|
||||
def filter(self, message):
|
||||
"""""" # remove method from docs
|
||||
if message.document:
|
||||
return message.document.mime_type == self.mimetype
|
||||
|
||||
|
@ -402,6 +460,7 @@ class Filters(object):
|
|||
``Filters.status_update`` for all status update messages.
|
||||
|
||||
"""
|
||||
update_filter = True
|
||||
|
||||
class _NewChatMembers(BaseFilter):
|
||||
name = 'Filters.status_update.new_chat_members'
|
||||
|
@ -563,6 +622,7 @@ class Filters(object):
|
|||
self.name = 'Filters.entity({})'.format(self.entity_type)
|
||||
|
||||
def filter(self, message):
|
||||
"""""" # remove method from docs
|
||||
return any(entity.type == self.entity_type for entity in message.entities)
|
||||
|
||||
class caption_entity(BaseFilter):
|
||||
|
@ -584,6 +644,7 @@ class Filters(object):
|
|||
self.name = 'Filters.caption_entity({})'.format(self.entity_type)
|
||||
|
||||
def filter(self, message):
|
||||
"""""" # remove method from docs
|
||||
return any(entity.type == self.entity_type for entity in message.caption_entities)
|
||||
|
||||
class _Private(BaseFilter):
|
||||
|
@ -635,6 +696,7 @@ class Filters(object):
|
|||
self.usernames = [user.replace('@', '') for user in username]
|
||||
|
||||
def filter(self, message):
|
||||
"""""" # remove method from docs
|
||||
if self.user_ids is not None:
|
||||
return bool(message.from_user and message.from_user.id in self.user_ids)
|
||||
else:
|
||||
|
@ -673,6 +735,7 @@ class Filters(object):
|
|||
self.usernames = [chat.replace('@', '') for chat in username]
|
||||
|
||||
def filter(self, message):
|
||||
"""""" # remove method from docs
|
||||
if self.chat_ids is not None:
|
||||
return bool(message.chat_id in self.chat_ids)
|
||||
else:
|
||||
|
@ -730,5 +793,80 @@ class Filters(object):
|
|||
self.name = 'Filters.language({})'.format(self.lang)
|
||||
|
||||
def filter(self, message):
|
||||
"""""" # remove method from docs
|
||||
return message.from_user.language_code and any(
|
||||
[message.from_user.language_code.startswith(x) for x in self.lang])
|
||||
|
||||
class _UpdateType(BaseFilter):
|
||||
update_filter = True
|
||||
|
||||
class _Message(BaseFilter):
|
||||
update_filter = True
|
||||
|
||||
def filter(self, update):
|
||||
return update.message is not None
|
||||
|
||||
message = _Message()
|
||||
|
||||
class _EditedMessage(BaseFilter):
|
||||
update_filter = True
|
||||
|
||||
def filter(self, update):
|
||||
return update.edited_message is not None
|
||||
|
||||
edited_message = _EditedMessage()
|
||||
|
||||
class _Messages(BaseFilter):
|
||||
update_filter = True
|
||||
|
||||
def filter(self, update):
|
||||
return update.message is not None or update.edited_message is not None
|
||||
|
||||
messages = _Messages()
|
||||
|
||||
class _ChannelPost(BaseFilter):
|
||||
update_filter = True
|
||||
|
||||
def filter(self, update):
|
||||
return update.channel_post is not None
|
||||
|
||||
channel_post = _ChannelPost()
|
||||
|
||||
class _EditedChannelPost(BaseFilter):
|
||||
update_filter = True
|
||||
|
||||
def filter(self, update):
|
||||
return update.edited_channel_post is not None
|
||||
|
||||
edited_channel_post = _EditedChannelPost()
|
||||
|
||||
class _ChannelPosts(BaseFilter):
|
||||
update_filter = True
|
||||
|
||||
def filter(self, update):
|
||||
return update.channel_post is not None or update.edited_channel_post is not None
|
||||
|
||||
channel_posts = _ChannelPosts()
|
||||
|
||||
def filter(self, update):
|
||||
return self.messages(update) or self.channel_posts(update)
|
||||
|
||||
update = _UpdateType()
|
||||
"""Subset for filtering the type of update.
|
||||
|
||||
Examples:
|
||||
Use these filters like: ``Filters.update.message`` or
|
||||
``Filters.update.channel_posts`` etc. Or use just ``Filters.update`` for all
|
||||
types.
|
||||
|
||||
Attributes:
|
||||
message (:obj:`Filter`): Updates with :attr:`telegram.Update.message`
|
||||
edited_message (:obj:`Filter`): Updates with :attr:`telegram.Update.edited_message`
|
||||
messages (:obj:`Filter`): Updates with either :attr:`telegram.Update.message` or
|
||||
:attr:`telegram.Update.edited_message`
|
||||
channel_post (:obj:`Filter`): Updates with :attr:`telegram.Update.channel_post`
|
||||
edited_channel_post (:obj:`Filter`): Updates with
|
||||
:attr:`telegram.Update.edited_channel_post`
|
||||
channel_posts (:obj:`Filter`): Updates with either :attr:`telegram.Update.channel_post` or
|
||||
:attr:`telegram.Update.edited_channel_post`
|
||||
"""
|
||||
|
|
|
@ -41,7 +41,7 @@ class Handler(object):
|
|||
or in the same chat, it will be the same ``dict``.
|
||||
|
||||
Note that this is DEPRECATED, and you should use context based callbacks. See
|
||||
https://git.io/vp113 for more info.
|
||||
https://git.io/fxJuV for more info.
|
||||
|
||||
Args:
|
||||
callback (:obj:`callable`): The callback function for this handler. Will be called when
|
||||
|
@ -139,7 +139,7 @@ class Handler(object):
|
|||
it should subclass this method, but remember to call this super method.
|
||||
|
||||
DEPRECATED: This method is being replaced by new context based callbacks. Please see
|
||||
https://git.io/vp113 for more info.
|
||||
https://git.io/fxJuV for more info.
|
||||
|
||||
Args:
|
||||
dispatcher (:class:`telegram.ext.Dispatcher`): The dispatcher.
|
||||
|
|
|
@ -54,7 +54,7 @@ class InlineQueryHandler(Handler):
|
|||
or in the same chat, it will be the same ``dict``.
|
||||
|
||||
Note that this is DEPRECATED, and you should use context based callbacks. See
|
||||
https://git.io/vp113 for more info.
|
||||
https://git.io/fxJuV for more info.
|
||||
|
||||
Args:
|
||||
callback (:obj:`callable`): The callback function for this handler. Will be called when
|
||||
|
@ -151,4 +151,4 @@ class InlineQueryHandler(Handler):
|
|||
|
||||
def collect_additional_context(self, context, update, dispatcher, check_result):
|
||||
if self.pattern:
|
||||
context.match = check_result
|
||||
context.matches = [check_result]
|
||||
|
|
|
@ -20,7 +20,10 @@
|
|||
"""This module contains the MessageHandler class."""
|
||||
import warnings
|
||||
|
||||
from telegram.utils.deprecate import TelegramDeprecationWarning
|
||||
|
||||
from telegram import Update
|
||||
from telegram.ext import Filters
|
||||
from .handler import Handler
|
||||
|
||||
|
||||
|
@ -40,13 +43,11 @@ class MessageHandler(Handler):
|
|||
pass_chat_data (:obj:`bool`): Determines whether ``chat_data`` will be passed to
|
||||
the callback function.
|
||||
message_updates (:obj:`bool`): Should "normal" message updates be handled?
|
||||
Default is ``True``.
|
||||
Default is ``None``.
|
||||
channel_post_updates (:obj:`bool`): Should channel posts updates be handled?
|
||||
Default is ``True``.
|
||||
Default is ``None``.
|
||||
edited_updates (:obj:`bool`): Should "edited" message updates be handled?
|
||||
Default is ``False``.
|
||||
allow_edited (:obj:`bool`): If the handler should also accept edited messages.
|
||||
Default is ``False`` - Deprecated. use edited_updates instead.
|
||||
Default is ``None``.
|
||||
|
||||
Note:
|
||||
:attr:`pass_user_data` and :attr:`pass_chat_data` determine whether a ``dict`` you
|
||||
|
@ -55,13 +56,17 @@ class MessageHandler(Handler):
|
|||
or in the same chat, it will be the same ``dict``.
|
||||
|
||||
Note that this is DEPRECATED, and you should use context based callbacks. See
|
||||
https://git.io/vp113 for more info.
|
||||
https://git.io/fxJuV for more info.
|
||||
|
||||
Args:
|
||||
filters (:class:`telegram.ext.BaseFilter`, optional): A filter inheriting from
|
||||
:class:`telegram.ext.filters.BaseFilter`. Standard filters can be found in
|
||||
:class:`telegram.ext.filters.Filters`. Filters can be combined using bitwise
|
||||
operators (& for and, | for or, ~ for not).
|
||||
operators (& for and, | for or, ~ for not). Default is
|
||||
:attr:`telegram.ext.filters.Filters.update`. This defaults to all message_type updates
|
||||
being: ``message``, ``edited_message``, ``channel_post`` and ``edited_channel_post``.
|
||||
If you don't want or need any of those pass ``~Filters.update.*`` in the filter
|
||||
argument.
|
||||
callback (:obj:`callable`): The callback function for this handler. Will be called when
|
||||
:attr:`check_update` has determined that an update should be processed by this handler.
|
||||
Callback signature for context based API:
|
||||
|
@ -87,13 +92,14 @@ class MessageHandler(Handler):
|
|||
``chat_data`` will be passed to the callback function. Default is ``False``.
|
||||
DEPRECATED: Please switch to context based callbacks.
|
||||
message_updates (:obj:`bool`, optional): Should "normal" message updates be handled?
|
||||
Default is ``True``.
|
||||
Default is ``None``.
|
||||
DEPRECATED: Please switch to filters for update filtering.
|
||||
channel_post_updates (:obj:`bool`, optional): Should channel posts updates be handled?
|
||||
Default is ``True``.
|
||||
Default is ``None``.
|
||||
DEPRECATED: Please switch to filters for update filtering.
|
||||
edited_updates (:obj:`bool`, optional): Should "edited" message updates be handled? Default
|
||||
is ``False``.
|
||||
allow_edited (:obj:`bool`, optional): If the handler should also accept edited messages.
|
||||
Default is ``False`` - Deprecated. use edited_updates instead.
|
||||
is ``None``.
|
||||
DEPRECATED: Please switch to filters for update filtering.
|
||||
|
||||
Raises:
|
||||
ValueError
|
||||
|
@ -103,20 +109,13 @@ class MessageHandler(Handler):
|
|||
def __init__(self,
|
||||
filters,
|
||||
callback,
|
||||
allow_edited=False,
|
||||
pass_update_queue=False,
|
||||
pass_job_queue=False,
|
||||
pass_user_data=False,
|
||||
pass_chat_data=False,
|
||||
message_updates=True,
|
||||
channel_post_updates=True,
|
||||
edited_updates=False):
|
||||
if not message_updates and not channel_post_updates and not edited_updates:
|
||||
raise ValueError(
|
||||
'message_updates, channel_post_updates and edited_updates are all False')
|
||||
if allow_edited:
|
||||
warnings.warn('allow_edited is getting deprecated, please use edited_updates instead')
|
||||
edited_updates = allow_edited
|
||||
message_updates=None,
|
||||
channel_post_updates=None,
|
||||
edited_updates=None):
|
||||
|
||||
super(MessageHandler, self).__init__(
|
||||
callback,
|
||||
|
@ -124,15 +123,36 @@ class MessageHandler(Handler):
|
|||
pass_job_queue=pass_job_queue,
|
||||
pass_user_data=pass_user_data,
|
||||
pass_chat_data=pass_chat_data)
|
||||
if message_updates is False and channel_post_updates is False and edited_updates is False:
|
||||
raise ValueError(
|
||||
'message_updates, channel_post_updates and edited_updates are all False')
|
||||
self.filters = filters
|
||||
self.message_updates = message_updates
|
||||
self.channel_post_updates = channel_post_updates
|
||||
self.edited_updates = edited_updates
|
||||
if self.filters is not None:
|
||||
self.filters &= Filters.update
|
||||
else:
|
||||
self.filters = Filters.update
|
||||
if message_updates is not None:
|
||||
warnings.warn('message_updates is deprecated. See https://git.io/fxJuV for more info',
|
||||
TelegramDeprecationWarning,
|
||||
stacklevel=2)
|
||||
if message_updates is False:
|
||||
self.filters &= ~Filters.update.message
|
||||
|
||||
def _is_allowed_update(self, update):
|
||||
return any([self.message_updates and update.message,
|
||||
self.edited_updates and (update.edited_message or update.edited_channel_post),
|
||||
self.channel_post_updates and update.channel_post])
|
||||
if channel_post_updates is not None:
|
||||
warnings.warn('channel_post_updates is deprecated. See https://git.io/fxJuV '
|
||||
'for more info',
|
||||
TelegramDeprecationWarning,
|
||||
stacklevel=2)
|
||||
if channel_post_updates is False:
|
||||
self.filters &= ~Filters.update.channel_post
|
||||
|
||||
if edited_updates is not None:
|
||||
warnings.warn('edited_updates is deprecated. See https://git.io/fxJuV for more info',
|
||||
TelegramDeprecationWarning,
|
||||
stacklevel=2)
|
||||
if edited_updates is False:
|
||||
self.filters &= ~(Filters.update.edited_message
|
||||
| Filters.update.edited_channel_post)
|
||||
|
||||
def check_update(self, update):
|
||||
"""Determines whether an update should be passed to this handlers :attr:`callback`.
|
||||
|
@ -144,8 +164,9 @@ class MessageHandler(Handler):
|
|||
:obj:`bool`
|
||||
|
||||
"""
|
||||
if isinstance(update, Update) and self._is_allowed_update(update):
|
||||
if not self.filters:
|
||||
return True
|
||||
else:
|
||||
return self.filters(update.effective_message)
|
||||
if isinstance(update, Update) and update.effective_message:
|
||||
return self.filters(update)
|
||||
|
||||
def collect_additional_context(self, context, update, dispatcher, check_result):
|
||||
if isinstance(check_result, dict):
|
||||
context.update(check_result)
|
||||
|
|
|
@ -43,7 +43,7 @@ class PreCheckoutQueryHandler(Handler):
|
|||
or in the same chat, it will be the same ``dict``.
|
||||
|
||||
Note that this is DEPRECATED, and you should use context based callbacks. See
|
||||
https://git.io/vp113 for more info.
|
||||
https://git.io/fxJuV for more info.
|
||||
|
||||
Args:
|
||||
callback (:obj:`callable`): The callback function for this handler. Will be called when
|
||||
|
|
|
@ -19,16 +19,14 @@
|
|||
# TODO: Remove allow_edited
|
||||
"""This module contains the RegexHandler class."""
|
||||
|
||||
import re
|
||||
import warnings
|
||||
|
||||
from future.utils import string_types
|
||||
from telegram.utils.deprecate import TelegramDeprecationWarning
|
||||
|
||||
from telegram import Update
|
||||
from .handler import Handler
|
||||
from telegram.ext import MessageHandler, Filters
|
||||
|
||||
|
||||
class RegexHandler(Handler):
|
||||
class RegexHandler(MessageHandler):
|
||||
"""Handler class to handle Telegram updates based on a regex.
|
||||
|
||||
It uses a regular expression to check text messages. Read the documentation of the ``re``
|
||||
|
@ -52,13 +50,9 @@ class RegexHandler(Handler):
|
|||
the callback function.
|
||||
|
||||
Note:
|
||||
:attr:`pass_user_data` and :attr:`pass_chat_data` determine whether a ``dict`` you
|
||||
can use to keep any data in will be sent to the :attr:`callback` function. Related to
|
||||
either the user or the chat that the update was sent in. For each update from the same user
|
||||
or in the same chat, it will be the same ``dict``.
|
||||
This handler is being deprecated. For the same usecase use:
|
||||
``MessageHandler(Filters.regex(r'pattern'), callback)``
|
||||
|
||||
Note that this is DEPRECATED, and you should use context based callbacks. See
|
||||
https://git.io/vp113 for more info.
|
||||
|
||||
Args:
|
||||
pattern (:obj:`str` | :obj:`Pattern`): The regex pattern.
|
||||
|
@ -73,35 +67,27 @@ class RegexHandler(Handler):
|
|||
pass_groups (:obj:`bool`, optional): If the callback should be passed the result of
|
||||
``re.match(pattern, data).groups()`` as a keyword argument called ``groups``.
|
||||
Default is ``False``
|
||||
DEPRECATED: Please switch to context based callbacks.
|
||||
pass_groupdict (:obj:`bool`, optional): If the callback should be passed the result of
|
||||
``re.match(pattern, data).groupdict()`` as a keyword argument called ``groupdict``.
|
||||
Default is ``False``
|
||||
DEPRECATED: Please switch to context based callbacks.
|
||||
pass_update_queue (:obj:`bool`, optional): If set to ``True``, a keyword argument called
|
||||
``update_queue`` will be passed to the callback function. It will be the ``Queue``
|
||||
instance used by the :class:`telegram.ext.Updater` and :class:`telegram.ext.Dispatcher`
|
||||
that contains new updates which can be used to insert updates. Default is ``False``.
|
||||
DEPRECATED: Please switch to context based callbacks.
|
||||
pass_job_queue (:obj:`bool`, optional): If set to ``True``, a keyword argument called
|
||||
``job_queue`` will be passed to the callback function. It will be a
|
||||
:class:`telegram.ext.JobQueue` instance created by the :class:`telegram.ext.Updater`
|
||||
which can be used to schedule new jobs. Default is ``False``.
|
||||
DEPRECATED: Please switch to context based callbacks.
|
||||
pass_user_data (:obj:`bool`, optional): If set to ``True``, a keyword argument called
|
||||
``user_data`` will be passed to the callback function. Default is ``False``.
|
||||
DEPRECATED: Please switch to context based callbacks.
|
||||
pass_chat_data (:obj:`bool`, optional): If set to ``True``, a keyword argument called
|
||||
``chat_data`` will be passed to the callback function. Default is ``False``.
|
||||
DEPRECATED: Please switch to context based callbacks.
|
||||
message_updates (:obj:`bool`, optional): Should "normal" message updates be handled?
|
||||
Default is ``True``.
|
||||
channel_post_updates (:obj:`bool`, optional): Should channel posts updates be handled?
|
||||
Default is ``True``.
|
||||
edited_updates (:obj:`bool`, optional): Should "edited" message updates be handled? Default
|
||||
is ``False``.
|
||||
allow_edited (:obj:`bool`, optional): If the handler should also accept edited messages.
|
||||
Default is ``False`` - Deprecated. use edited_updates instead.
|
||||
|
||||
Raises:
|
||||
ValueError
|
||||
|
@ -121,59 +107,26 @@ class RegexHandler(Handler):
|
|||
message_updates=True,
|
||||
channel_post_updates=False,
|
||||
edited_updates=False):
|
||||
if not message_updates and not channel_post_updates and not edited_updates:
|
||||
raise ValueError(
|
||||
'message_updates, channel_post_updates and edited_updates are all False')
|
||||
if allow_edited:
|
||||
warnings.warn('allow_edited is getting deprecated, please use edited_updates instead')
|
||||
edited_updates = allow_edited
|
||||
|
||||
super(RegexHandler, self).__init__(
|
||||
callback,
|
||||
pass_update_queue=pass_update_queue,
|
||||
pass_job_queue=pass_job_queue,
|
||||
pass_user_data=pass_user_data,
|
||||
pass_chat_data=pass_chat_data)
|
||||
|
||||
if isinstance(pattern, string_types):
|
||||
pattern = re.compile(pattern)
|
||||
|
||||
self.pattern = pattern
|
||||
warnings.warn('RegexHandler is deprecated. See https://git.io/fxJuV for more info',
|
||||
TelegramDeprecationWarning,
|
||||
stacklevel=2)
|
||||
super(RegexHandler, self).__init__(Filters.regex(pattern),
|
||||
callback,
|
||||
pass_update_queue=pass_update_queue,
|
||||
pass_job_queue=pass_job_queue,
|
||||
pass_user_data=pass_user_data,
|
||||
pass_chat_data=pass_chat_data,
|
||||
message_updates=message_updates,
|
||||
channel_post_updates=channel_post_updates,
|
||||
edited_updates=edited_updates)
|
||||
self.pass_groups = pass_groups
|
||||
self.pass_groupdict = pass_groupdict
|
||||
self.allow_edited = allow_edited
|
||||
self.message_updates = message_updates
|
||||
self.channel_post_updates = channel_post_updates
|
||||
self.edited_updates = edited_updates
|
||||
|
||||
def check_update(self, update):
|
||||
"""Determines whether an update should be passed to this handlers :attr:`callback`.
|
||||
|
||||
Args:
|
||||
update (:class:`telegram.Update`): Incoming telegram update.
|
||||
|
||||
Returns:
|
||||
:obj:`bool`
|
||||
|
||||
"""
|
||||
if not isinstance(update, Update) and not update.effective_message:
|
||||
return None
|
||||
if any([self.message_updates and update.message,
|
||||
self.edited_updates and (update.edited_message or update.edited_channel_post),
|
||||
self.channel_post_updates and update.channel_post]) and \
|
||||
update.effective_message.text:
|
||||
match = re.match(self.pattern, update.effective_message.text)
|
||||
if match:
|
||||
return match
|
||||
|
||||
def collect_optional_args(self, dispatcher, update=None, check_result=None):
|
||||
optional_args = super(RegexHandler, self).collect_optional_args(dispatcher, update,
|
||||
check_result)
|
||||
if self.pass_groups:
|
||||
optional_args['groups'] = check_result.groups()
|
||||
optional_args['groups'] = check_result['matches'][0].groups()
|
||||
if self.pass_groupdict:
|
||||
optional_args['groupdict'] = check_result.groupdict()
|
||||
optional_args['groupdict'] = check_result['matches'][0].groupdict()
|
||||
return optional_args
|
||||
|
||||
def collect_additional_context(self, context, update, dispatcher, check_result):
|
||||
context.match = check_result
|
||||
|
|
|
@ -43,7 +43,7 @@ class ShippingQueryHandler(Handler):
|
|||
or in the same chat, it will be the same ``dict``.
|
||||
|
||||
Note that this is DEPRECATED, and you should use context based callbacks. See
|
||||
https://git.io/vp113 for more info.
|
||||
https://git.io/fxJuV for more info.
|
||||
|
||||
Args:
|
||||
callback (:obj:`callable`): The callback function for this handler. Will be called when
|
||||
|
|
|
@ -124,4 +124,4 @@ class StringRegexHandler(Handler):
|
|||
|
||||
def collect_additional_context(self, context, update, dispatcher, check_result):
|
||||
if self.pattern:
|
||||
context.match = check_result
|
||||
context.matches = [check_result]
|
||||
|
|
|
@ -16,6 +16,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/].
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
from datetime import datetime
|
||||
from platform import python_implementation
|
||||
|
@ -313,6 +315,8 @@ class TestBot(object):
|
|||
@flaky(3, 1)
|
||||
@pytest.mark.timeout(15)
|
||||
@pytest.mark.xfail
|
||||
@pytest.mark.skipif(os.getenv('APPVEYOR') and (sys.version_info < (3, 6)),
|
||||
reason='only run on 3.6 on appveyor')
|
||||
def test_set_webhook_get_webhook_info_and_delete_webhook(self, bot):
|
||||
url = 'https://python-telegram-bot.org/test/webhook'
|
||||
max_connections = 7
|
||||
|
|
|
@ -96,3 +96,14 @@ class TestCallbackContext(object):
|
|||
assert callback_context.bot is cdp.bot
|
||||
assert callback_context.job_queue is cdp.job_queue
|
||||
assert callback_context.update_queue is cdp.update_queue
|
||||
|
||||
def test_match(self, cdp):
|
||||
|
||||
callback_context = CallbackContext(cdp)
|
||||
|
||||
assert callback_context.match is None
|
||||
|
||||
callback_context.matches = ['test', 'blah']
|
||||
|
||||
assert callback_context.match == 'test'
|
||||
|
||||
|
|
|
@ -93,10 +93,10 @@ class TestCallbackQueryHandler(object):
|
|||
and isinstance(update.callback_query, CallbackQuery))
|
||||
|
||||
def callback_context_pattern(self, update, context):
|
||||
if context.match.groups():
|
||||
self.test_flag = context.match.groups() == ('t', ' data')
|
||||
if context.match.groupdict():
|
||||
self.test_flag = context.match.groupdict() == {'begin': 't', 'end': ' data'}
|
||||
if context.matches[0].groups():
|
||||
self.test_flag = context.matches[0].groups() == ('t', ' data')
|
||||
if context.matches[0].groupdict():
|
||||
self.test_flag = context.matches[0].groupdict() == {'begin': 't', 'end': ' data'}
|
||||
|
||||
def test_basic(self, dp, callback_query):
|
||||
handler = CallbackQueryHandler(self.callback_basic)
|
||||
|
|
|
@ -17,9 +17,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/].
|
||||
import re
|
||||
from queue import Queue
|
||||
|
||||
import pytest
|
||||
from telegram.utils.deprecate import TelegramDeprecationWarning
|
||||
|
||||
from telegram import (Message, Update, Chat, Bot, User, CallbackQuery, InlineQuery,
|
||||
ChosenInlineResult, ShippingQuery, PreCheckoutQuery, MessageEntity)
|
||||
|
@ -64,6 +66,7 @@ def message(bot):
|
|||
|
||||
class TestCommandHandler(object):
|
||||
test_flag = False
|
||||
SRE_TYPE = type(re.match("", ""))
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def reset(self):
|
||||
|
@ -107,6 +110,18 @@ class TestCommandHandler(object):
|
|||
def callback_context_args(self, update, context):
|
||||
self.test_flag = context.args == ['one', 'two']
|
||||
|
||||
def callback_context_regex1(self, update, context):
|
||||
if context.matches:
|
||||
types = all([type(res) == 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])
|
||||
num = len(context.matches) == 2
|
||||
self.test_flag = types and num
|
||||
|
||||
def test_basic(self, dp, message):
|
||||
handler = CommandHandler('test', self.callback_basic)
|
||||
dp.add_handler(handler)
|
||||
|
@ -153,10 +168,30 @@ class TestCommandHandler(object):
|
|||
check = handler.check_update(Update(0, message))
|
||||
assert check is None or check is False
|
||||
|
||||
def test_edited(self, message):
|
||||
def test_deprecation_warning(self):
|
||||
with pytest.warns(TelegramDeprecationWarning, match='See https://git.io/fxJuV'):
|
||||
CommandHandler('test', self.callback_basic, allow_edited=True)
|
||||
|
||||
def test_no_edited(self, message):
|
||||
handler = CommandHandler('test', self.callback_basic)
|
||||
message.text = '/test'
|
||||
check = handler.check_update(Update(0, message))
|
||||
assert check is not None and check is not False
|
||||
|
||||
check = handler.check_update(Update(0, edited_message=message))
|
||||
assert check is not None and check is not False
|
||||
|
||||
handler = CommandHandler('test', self.callback_basic,
|
||||
filters=~Filters.update.edited_message)
|
||||
check = handler.check_update(Update(0, message))
|
||||
assert check is not None and check is not False
|
||||
|
||||
check = handler.check_update(Update(0, edited_message=message))
|
||||
assert check is None or check is False
|
||||
|
||||
def test_edited_deprecated(self, message):
|
||||
handler = CommandHandler('test', self.callback_basic,
|
||||
allow_edited=False)
|
||||
|
||||
message.text = '/test'
|
||||
check = handler.check_update(Update(0, message))
|
||||
assert check is not None and check is not False
|
||||
|
@ -164,7 +199,8 @@ class TestCommandHandler(object):
|
|||
check = handler.check_update(Update(0, edited_message=message))
|
||||
assert check is None or check is False
|
||||
|
||||
handler.allow_edited = True
|
||||
handler = CommandHandler('test', self.callback_basic,
|
||||
allow_edited=True)
|
||||
check = handler.check_update(Update(0, message))
|
||||
assert check is not None and check is not False
|
||||
|
||||
|
@ -333,6 +369,31 @@ class TestCommandHandler(object):
|
|||
cdp.process_update(Update(0, message))
|
||||
assert self.test_flag
|
||||
|
||||
def test_context_regex(self, cdp, message):
|
||||
handler = CommandHandler('test', self.callback_context_regex1, Filters.regex('one two'))
|
||||
cdp.add_handler(handler)
|
||||
|
||||
message.text = '/test'
|
||||
cdp.process_update(Update(0, message))
|
||||
assert not self.test_flag
|
||||
|
||||
message.text += ' one two'
|
||||
cdp.process_update(Update(0, message))
|
||||
assert self.test_flag
|
||||
|
||||
def test_context_multiple_regex(self, cdp, message):
|
||||
handler = CommandHandler('test', self.callback_context_regex2,
|
||||
Filters.regex('one') & Filters.regex('two'))
|
||||
cdp.add_handler(handler)
|
||||
|
||||
message.text = '/test'
|
||||
cdp.process_update(Update(0, message))
|
||||
assert not self.test_flag
|
||||
|
||||
message.text += ' one two'
|
||||
cdp.process_update(Update(0, message))
|
||||
assert self.test_flag
|
||||
|
||||
|
||||
par = ['!help', '!test', '#help', '#test', 'mytrig-help', 'mytrig-test']
|
||||
|
||||
|
@ -349,6 +410,7 @@ def prefixmessage(bot, request):
|
|||
|
||||
class TestPrefixHandler(object):
|
||||
test_flag = False
|
||||
SRE_TYPE = type(re.match("", ""))
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def reset(self):
|
||||
|
@ -390,6 +452,18 @@ class TestPrefixHandler(object):
|
|||
def callback_context_args(self, update, context):
|
||||
self.test_flag = context.args == ['one', 'two']
|
||||
|
||||
def callback_context_regex1(self, update, context):
|
||||
if context.matches:
|
||||
types = all([type(res) == 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])
|
||||
num = len(context.matches) == 2
|
||||
self.test_flag = types and num
|
||||
|
||||
def test_basic(self, dp, prefixmessage):
|
||||
handler = PrefixHandler(['!', '#', 'mytrig-'], ['help', 'test'], self.callback_basic)
|
||||
dp.add_handler(handler)
|
||||
|
@ -436,22 +510,22 @@ class TestPrefixHandler(object):
|
|||
else:
|
||||
assert check is None or check is False
|
||||
|
||||
def test_edited(self, prefixmessage):
|
||||
def test_no_edited(self, prefixmessage):
|
||||
handler = PrefixHandler(['!', '#', 'mytrig-'], ['help', 'test'], self.callback_basic)
|
||||
check = handler.check_update(Update(0, prefixmessage))
|
||||
assert check is not None and check is not False
|
||||
|
||||
check = handler.check_update(Update(0, edited_message=prefixmessage))
|
||||
assert check is not None and check is not False
|
||||
|
||||
handler = PrefixHandler(['!', '#', 'mytrig-'], ['help', 'test'], self.callback_basic,
|
||||
filters=~Filters.update.edited_message)
|
||||
check = handler.check_update(Update(0, prefixmessage))
|
||||
assert check is not None and check is not False
|
||||
|
||||
check = handler.check_update(Update(0, edited_message=prefixmessage))
|
||||
assert check is None or check is False
|
||||
|
||||
handler.allow_edited = True
|
||||
check = handler.check_update(Update(0, prefixmessage))
|
||||
assert check is not None and check is not False
|
||||
|
||||
check = handler.check_update(Update(0, edited_message=prefixmessage))
|
||||
assert check is not None and check is not False
|
||||
|
||||
def test_with_filter(self, prefixmessage):
|
||||
handler = PrefixHandler(['!', '#', 'mytrig-'], ['help', 'test'], self.callback_basic,
|
||||
filters=Filters.group)
|
||||
|
@ -570,3 +644,28 @@ class TestPrefixHandler(object):
|
|||
prefixmessage.text += ' one two'
|
||||
cdp.process_update(Update(0, prefixmessage))
|
||||
assert self.test_flag
|
||||
|
||||
def test_context_regex(self, cdp, prefixmessage):
|
||||
handler = PrefixHandler(['!', '#', 'mytrig-'], ['help', 'test'],
|
||||
self.callback_context_regex1, Filters.regex('one two'))
|
||||
cdp.add_handler(handler)
|
||||
|
||||
cdp.process_update(Update(0, prefixmessage))
|
||||
assert not self.test_flag
|
||||
|
||||
prefixmessage.text += ' one two'
|
||||
cdp.process_update(Update(0, prefixmessage))
|
||||
assert self.test_flag
|
||||
|
||||
def test_context_multiple_regex(self, cdp, prefixmessage):
|
||||
handler = PrefixHandler(['!', '#', 'mytrig-'], ['help', 'test'],
|
||||
self.callback_context_regex2,
|
||||
Filters.regex('one') & Filters.regex('two'))
|
||||
cdp.add_handler(handler)
|
||||
|
||||
cdp.process_update(Update(0, prefixmessage))
|
||||
assert not self.test_flag
|
||||
|
||||
prefixmessage.text += ' one two'
|
||||
cdp.process_update(Update(0, prefixmessage))
|
||||
assert self.test_flag
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -97,10 +97,10 @@ class TestCallbackQueryHandler(object):
|
|||
and isinstance(update.inline_query, InlineQuery))
|
||||
|
||||
def callback_context_pattern(self, update, context):
|
||||
if context.match.groups():
|
||||
self.test_flag = context.match.groups() == ('t', ' query')
|
||||
if context.match.groupdict():
|
||||
self.test_flag = context.match.groupdict() == {'begin': 't', 'end': ' query'}
|
||||
if context.matches[0].groups():
|
||||
self.test_flag = context.matches[0].groups() == ('t', ' query')
|
||||
if context.matches[0].groupdict():
|
||||
self.test_flag = context.matches[0].groupdict() == {'begin': 't', 'end': ' query'}
|
||||
|
||||
def test_basic(self, dp, inline_query):
|
||||
handler = InlineQueryHandler(self.callback_basic)
|
||||
|
|
|
@ -16,9 +16,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/].
|
||||
import re
|
||||
from queue import Queue
|
||||
|
||||
import pytest
|
||||
from telegram.utils.deprecate import TelegramDeprecationWarning
|
||||
|
||||
from telegram import (Message, Update, Chat, Bot, User, CallbackQuery, InlineQuery,
|
||||
ChosenInlineResult, ShippingQuery, PreCheckoutQuery)
|
||||
|
@ -51,6 +53,7 @@ def message(bot):
|
|||
|
||||
class TestMessageHandler(object):
|
||||
test_flag = False
|
||||
SRE_TYPE = type(re.match("", ""))
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def reset(self):
|
||||
|
@ -82,13 +85,23 @@ class TestMessageHandler(object):
|
|||
and isinstance(context.chat_data, dict)
|
||||
and ((isinstance(context.user_data, dict)
|
||||
and (isinstance(update.message, Message)
|
||||
or isinstance(update.edited_message, Message))
|
||||
)
|
||||
or isinstance(update.edited_message, Message)))
|
||||
or (context.user_data is None
|
||||
and (isinstance(update.channel_post, Message)
|
||||
or isinstance(update.edited_channel_post, Message))
|
||||
))
|
||||
)
|
||||
or isinstance(update.edited_channel_post, Message)))
|
||||
))
|
||||
|
||||
def callback_context_regex1(self, update, context):
|
||||
if context.matches:
|
||||
types = all([type(res) == 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])
|
||||
num = len(context.matches) == 2
|
||||
self.test_flag = types and num
|
||||
|
||||
def test_basic(self, dp, message):
|
||||
handler = MessageHandler(None, self.callback_basic)
|
||||
|
@ -98,7 +111,15 @@ class TestMessageHandler(object):
|
|||
dp.process_update(Update(0, message))
|
||||
assert self.test_flag
|
||||
|
||||
def test_edited(self, message):
|
||||
def test_deprecation_warning(self):
|
||||
with pytest.warns(TelegramDeprecationWarning, match='See https://git.io/fxJuV'):
|
||||
MessageHandler(None, self.callback_basic, edited_updates=True)
|
||||
with pytest.warns(TelegramDeprecationWarning, match='See https://git.io/fxJuV'):
|
||||
MessageHandler(None, self.callback_basic, message_updates=False)
|
||||
with pytest.warns(TelegramDeprecationWarning, match='See https://git.io/fxJuV'):
|
||||
MessageHandler(None, self.callback_basic, channel_post_updates=True)
|
||||
|
||||
def test_edited_deprecated(self, message):
|
||||
handler = MessageHandler(None, self.callback_basic, edited_updates=True,
|
||||
message_updates=False, channel_post_updates=False)
|
||||
|
||||
|
@ -107,17 +128,16 @@ class TestMessageHandler(object):
|
|||
assert not handler.check_update(Update(0, channel_post=message))
|
||||
assert handler.check_update(Update(0, edited_channel_post=message))
|
||||
|
||||
def test_channel_post(self, message):
|
||||
def test_channel_post_deprecated(self, message):
|
||||
handler = MessageHandler(None, self.callback_basic,
|
||||
edited_updates=False, message_updates=False,
|
||||
channel_post_updates=True)
|
||||
|
||||
assert not handler.check_update(Update(0, edited_message=message))
|
||||
assert not handler.check_update(Update(0, message=message))
|
||||
assert handler.check_update(Update(0, channel_post=message))
|
||||
assert not handler.check_update(Update(0, edited_channel_post=message))
|
||||
|
||||
def test_multiple_flags(self, message):
|
||||
def test_multiple_flags_deprecated(self, message):
|
||||
handler = MessageHandler(None, self.callback_basic, edited_updates=True,
|
||||
message_updates=True, channel_post_updates=True)
|
||||
|
||||
|
@ -126,31 +146,31 @@ class TestMessageHandler(object):
|
|||
assert handler.check_update(Update(0, channel_post=message))
|
||||
assert handler.check_update(Update(0, edited_channel_post=message))
|
||||
|
||||
def test_allow_edited(self, message):
|
||||
with pytest.warns(UserWarning):
|
||||
handler = MessageHandler(None, self.callback_basic,
|
||||
message_updates=True, allow_edited=True,
|
||||
channel_post_updates=False)
|
||||
|
||||
assert handler.check_update(Update(0, edited_message=message))
|
||||
assert handler.check_update(Update(0, message=message))
|
||||
assert not handler.check_update(Update(0, channel_post=message))
|
||||
assert handler.check_update(Update(0, edited_channel_post=message))
|
||||
|
||||
def test_none_allowed(self):
|
||||
def test_none_allowed_deprecated(self):
|
||||
with pytest.raises(ValueError, match='are all False'):
|
||||
MessageHandler(None, self.callback_basic, message_updates=False,
|
||||
channel_post_updates=False, edited_updates=False)
|
||||
|
||||
def test_with_filter(self, message):
|
||||
handler = MessageHandler(Filters.command, self.callback_basic)
|
||||
handler = MessageHandler(Filters.group, self.callback_basic)
|
||||
|
||||
message.text = '/test'
|
||||
message.chat.type = 'group'
|
||||
assert handler.check_update(Update(0, message))
|
||||
|
||||
message.text = 'test'
|
||||
message.chat.type = 'private'
|
||||
assert not handler.check_update(Update(0, message))
|
||||
|
||||
def test_specific_filters(self, message):
|
||||
f = (~Filters.update.messages
|
||||
& ~Filters.update.channel_post
|
||||
& Filters.update.edited_channel_post)
|
||||
handler = MessageHandler(f, self.callback_basic)
|
||||
|
||||
assert not handler.check_update(Update(0, edited_message=message))
|
||||
assert not handler.check_update(Update(0, message=message))
|
||||
assert not handler.check_update(Update(0, channel_post=message))
|
||||
assert handler.check_update(Update(0, edited_channel_post=message))
|
||||
|
||||
def test_pass_user_or_chat_data(self, dp, message):
|
||||
handler = MessageHandler(None, self.callback_data_1,
|
||||
pass_user_data=True)
|
||||
|
@ -226,3 +246,28 @@ class TestMessageHandler(object):
|
|||
self.test_flag = False
|
||||
cdp.process_update(Update(0, edited_channel_post=message))
|
||||
assert self.test_flag
|
||||
|
||||
def test_context_regex(self, cdp, message):
|
||||
handler = MessageHandler(Filters.regex('one two'), self.callback_context_regex1)
|
||||
cdp.add_handler(handler)
|
||||
|
||||
message.text = 'not it'
|
||||
cdp.process_update(Update(0, message))
|
||||
assert not self.test_flag
|
||||
|
||||
message.text += ' one two now it is'
|
||||
cdp.process_update(Update(0, message))
|
||||
assert self.test_flag
|
||||
|
||||
def test_context_multiple_regex(self, cdp, message):
|
||||
handler = MessageHandler(Filters.regex('one') & Filters.regex('two'),
|
||||
self.callback_context_regex2)
|
||||
cdp.add_handler(handler)
|
||||
|
||||
message.text = 'not it'
|
||||
cdp.process_update(Update(0, message))
|
||||
assert not self.test_flag
|
||||
|
||||
message.text += ' one two now it is'
|
||||
cdp.process_update(Update(0, message))
|
||||
assert self.test_flag
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
from queue import Queue
|
||||
|
||||
import pytest
|
||||
from telegram.utils.deprecate import TelegramDeprecationWarning
|
||||
|
||||
from telegram import (Message, Update, Chat, Bot, User, CallbackQuery, InlineQuery,
|
||||
ChosenInlineResult, ShippingQuery, PreCheckoutQuery)
|
||||
|
@ -90,10 +91,14 @@ class TestRegexHandler(object):
|
|||
and isinstance(update.message, Message))
|
||||
|
||||
def callback_context_pattern(self, update, context):
|
||||
if context.match.groups():
|
||||
self.test_flag = context.match.groups() == ('t', ' message')
|
||||
if context.match.groupdict():
|
||||
self.test_flag = context.match.groupdict() == {'begin': 't', 'end': ' message'}
|
||||
if context.matches[0].groups():
|
||||
self.test_flag = context.matches[0].groups() == ('t', ' message')
|
||||
if context.matches[0].groupdict():
|
||||
self.test_flag = context.matches[0].groupdict() == {'begin': 't', 'end': ' message'}
|
||||
|
||||
def test_deprecation_Warning(self):
|
||||
with pytest.warns(TelegramDeprecationWarning, match='RegexHandler is deprecated.'):
|
||||
RegexHandler('.*', self.callback_basic)
|
||||
|
||||
def test_basic(self, dp, message):
|
||||
handler = RegexHandler('.*', self.callback_basic)
|
||||
|
@ -115,7 +120,6 @@ class TestRegexHandler(object):
|
|||
handler = RegexHandler('(?P<begin>.*)est(?P<end>.*)', self.callback_group,
|
||||
pass_groups=True)
|
||||
dp.add_handler(handler)
|
||||
|
||||
dp.process_update(Update(0, message))
|
||||
assert self.test_flag
|
||||
|
||||
|
@ -155,17 +159,6 @@ class TestRegexHandler(object):
|
|||
assert handler.check_update(Update(0, channel_post=message))
|
||||
assert handler.check_update(Update(0, edited_channel_post=message))
|
||||
|
||||
def test_allow_edited(self, message):
|
||||
with pytest.warns(UserWarning):
|
||||
handler = RegexHandler('.*', self.callback_basic,
|
||||
message_updates=True,
|
||||
allow_edited=True)
|
||||
|
||||
assert handler.check_update(Update(0, edited_message=message))
|
||||
assert handler.check_update(Update(0, message=message))
|
||||
assert not handler.check_update(Update(0, channel_post=message))
|
||||
assert handler.check_update(Update(0, edited_channel_post=message))
|
||||
|
||||
def test_none_allowed(self):
|
||||
with pytest.raises(ValueError, match='are all False'):
|
||||
RegexHandler('.*', self.callback_basic, message_updates=False,
|
||||
|
|
|
@ -81,10 +81,10 @@ class TestStringRegexHandler(object):
|
|||
and isinstance(context.job_queue, JobQueue))
|
||||
|
||||
def callback_context_pattern(self, update, context):
|
||||
if context.match.groups():
|
||||
self.test_flag = context.match.groups() == ('t', ' message')
|
||||
if context.match.groupdict():
|
||||
self.test_flag = context.match.groupdict() == {'begin': 't', 'end': ' message'}
|
||||
if context.matches[0].groups():
|
||||
self.test_flag = context.matches[0].groups() == ('t', ' message')
|
||||
if context.matches[0].groupdict():
|
||||
self.test_flag = context.matches[0].groupdict() == {'begin': 't', 'end': ' message'}
|
||||
|
||||
def test_basic(self, dp):
|
||||
handler = StringRegexHandler('(?P<begin>.*)est(?P<end>.*)', self.callback_basic)
|
||||
|
|
Loading…
Add table
Reference in a new issue