Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
3ce9363
Change handlers so context is supported
jsmnbom May 3, 2018
714d96d
Attempt to make parameter "guessing" work on py < 3.5
jsmnbom May 3, 2018
32a2453
Document use_context in all handlers
jsmnbom May 3, 2018
5738f00
Add Context to docs
jsmnbom May 3, 2018
0580ae3
Minor fixes to context handling
jsmnbom May 3, 2018
bad8f35
Add tests for context stuff
jsmnbom May 3, 2018
7e2fb8d
Allow the signature check to work on py<3.5 with methods
jsmnbom May 3, 2018
d133dd4
Fix order of operations
jsmnbom May 3, 2018
5bfaca0
Address most issues raised in CR
jsmnbom May 4, 2018
bfec5ea
Make CommandHandler no longer support filter lists
jsmnbom May 5, 2018
da26227
Fix indent
jsmnbom May 5, 2018
c450a4e
Improve readability in conversationhandler
jsmnbom May 5, 2018
35c07dd
Make context have Match instead of groups & groupdict
jsmnbom May 5, 2018
b325cc1
Remove filter list support from messagehandler too
jsmnbom May 5, 2018
945a5ec
Small fix to StringCommandHandler
jsmnbom May 5, 2018
07747f3
More small fixes to handlers
jsmnbom May 5, 2018
a54cfac
Amend CHANGES
jsmnbom May 5, 2018
30712c5
Fix tests and fix bugs raised by tests
jsmnbom May 5, 2018
e87a705
Don't allow users to ignore errors without messing with the warning f…
jsmnbom May 5, 2018
c69378f
Merge branch 'master' into context
jsmnbom May 5, 2018
397ab77
Ignore our own deprecation warnings when testing
jsmnbom May 5, 2018
46f2a26
Skipping deprecationwarning test on py2
Eldinnie May 7, 2018
fe27912
Forgot some changes
Eldinnie May 7, 2018
0f44eed
Handler: Improved documentation and text of deprecation warnings
tsnoam May 7, 2018
b54f7e5
HandlerContext: Keep only dispatcher and use properties; improved doc
tsnoam May 7, 2018
f26bf62
Complete fixing the documentation.
tsnoam May 7, 2018
8184902
Some small doc fixes (interlinks and optionals)
Eldinnie May 8, 2018
5dc53a5
Change add_error_handler to use HandlerContext too
jsmnbom May 10, 2018
eeb2816
More context based changes
jsmnbom May 10, 2018
256a9d1
Forgot about conversationhandler
jsmnbom May 10, 2018
63f8a07
Forgot jobqueue
jsmnbom May 10, 2018
1fb2e6e
Add tests for callbackcontext & for context based callback job
jsmnbom May 11, 2018
cf85625
Commandhandler reworked
Eldinnie May 15, 2018
860155e
Make CommandHandler strict
Eldinnie May 15, 2018
5f238d2
Add PrefixHandler
Eldinnie May 15, 2018
4fd74d9
Merge branch 'master' into cmdhndlr
Eldinnie May 21, 2018
eea3a6b
declare encoding on test_commandhandler
Eldinnie May 21, 2018
72adb72
Fix some tests dependend on CommandHandler
Eldinnie May 21, 2018
7e1642c
CR changes
Eldinnie May 21, 2018
ae10983
small docfix.
Eldinnie May 21, 2018
8840c2f
Test all possibilities for PrefixHandler
Eldinnie May 22, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions docs/source/telegram.ext.prefixhandler.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
telegram.ext.PrefixHandler
===========================

.. autoclass:: telegram.ext.PrefixHandler
:members:
:show-inheritance:
1 change: 1 addition & 0 deletions docs/source/telegram.ext.rst
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ Handlers
telegram.ext.inlinequeryhandler
telegram.ext.messagehandler
telegram.ext.precheckoutqueryhandler
telegram.ext.prefixhandler
telegram.ext.regexhandler
telegram.ext.shippingqueryhandler
telegram.ext.stringcommandhandler
Expand Down
4 changes: 2 additions & 2 deletions telegram/ext/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
from .updater import Updater
from .callbackqueryhandler import CallbackQueryHandler
from .choseninlineresulthandler import ChosenInlineResultHandler
from .commandhandler import CommandHandler
from .commandhandler import CommandHandler, PrefixHandler
from .inlinequeryhandler import InlineQueryHandler
from .messagehandler import MessageHandler
from .filters import BaseFilter, Filters
Expand All @@ -44,4 +44,4 @@
'MessageHandler', 'BaseFilter', 'Filters', 'RegexHandler', 'StringCommandHandler',
'StringRegexHandler', 'TypeHandler', 'ConversationHandler',
'PreCheckoutQueryHandler', 'ShippingQueryHandler', 'MessageQueue', 'DelayQueue',
'DispatcherHandlerStop', 'run_async', 'CallbackContext')
'DispatcherHandlerStop', 'run_async', 'CallbackContext', 'PrefixHandler')
6 changes: 3 additions & 3 deletions telegram/ext/callbackcontext.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,9 @@ class CallbackContext(object):
regex-supported handler, this will contain the object returned from
``re.match(pattern, string)``.
args (List[:obj:`str`], optional): Arguments passed to a command if the associated update
is handled by :class:`telegram.ext.CommandHandler` or
:class:`telegram.ext.StringCommandHandler`. It contains a list of the words in the text
after the command, using any whitespace string as a delimiter.
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
text after the command, using any whitespace string as a delimiter.
error (:class:`telegram.TelegramError`, optional): The Telegram error that was raised.
Only present when passed to a error handler registered with
:attr:`telegram.ext.Dispatcher.add_error_handler`.
Expand Down
2 changes: 1 addition & 1 deletion telegram/ext/callbackqueryhandler.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ class CallbackQueryHandler(Handler):

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
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``.

Expand Down
2 changes: 1 addition & 1 deletion telegram/ext/choseninlineresulthandler.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ class ChosenInlineResultHandler(Handler):

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
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``.

Expand Down
194 changes: 176 additions & 18 deletions telegram/ext/commandhandler.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,27 @@
#
# 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 CommandHandler class."""
"""This module contains the CommandHandler and PrefixHandler classes."""
import re

from future.utils import string_types

from telegram import Update
from telegram import Update, MessageEntity
from .handler import Handler


class CommandHandler(Handler):
"""Handler class to handle Telegram commands.

Commands are Telegram messages that start with ``/``, optionally followed by an ``@`` and the
bot's name and/or some additional text.
bot's name and/or some additional text. The handler will add a ``list`` to the
:class:`CallbackContext` named :attr:`CallbackContext.args`. It will contain a list of strings,
which is the text following the command split on single or consecutive whitespace characters.

Attributes:
command (:obj:`str` | List[:obj:`str`]): The command or list of commands this handler
should listen for.
should listen for. Limitations are the same as described here
https://core.telegram.org/bots#commands
callback (:obj:`callable`): The callback function for this handler.
filters (:class:`telegram.ext.BaseFilter`): Optional. Only allow updates with these
Filters.
Expand All @@ -50,7 +55,7 @@ class CommandHandler(Handler):

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
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``.

Expand All @@ -59,7 +64,8 @@ class CommandHandler(Handler):

Args:
command (:obj:`str` | List[:obj:`str`]): The command or list of commands this handler
should listen for.
should listen for. Limitations are the same as described here
https://core.telegram.org/bots#commands
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:
Expand Down Expand Up @@ -96,6 +102,8 @@ class CommandHandler(Handler):
``chat_data`` will be passed to the callback function. Default is ``False``.
DEPRECATED: Please switch to context based callbacks.

Raises:
ValueError - when command is too long or has illegal chars.
"""

def __init__(self,
Expand All @@ -119,6 +127,10 @@ def __init__(self,
self.command = [command.lower()]
else:
self.command = [x.lower() for x in command]
for comm in self.command:
if not re.match(r'^[\da-z_]{1,32}$', comm):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure that 32 chars is a hard limit, dispite the docs... Do we care?

Both my phone and my desktop app, highlights commands that are longer than 32 chars at least.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image
image
There's a discrepancy here. @Botfather won't let you add too long commands for suggestion, but they are recognised as entities.

I vote to stay with the docs and enforce 32 chars

raise ValueError('Command is not a valid bot command')

self.filters = filters
self.allow_edited = allow_edited
self.pass_args = pass_args
Expand All @@ -135,21 +147,21 @@ def check_update(self, update):
"""
if (isinstance(update, Update) and
(update.message or update.edited_message and self.allow_edited)):
message = update.message or update.edited_message
message = update.effective_message

if message.text and message.text.startswith('/') and len(message.text) > 1:
first_word = message.text_html.split(None, 1)[0]
if len(first_word) > 1 and first_word.startswith('/'):
command = first_word[1:].split('@')
command.append(
message.bot.username) # in case the command was sent without a username
if (message.entities and message.entities[0].type == MessageEntity.BOT_COMMAND and
message.entities[0].offset == 0):
command = message.text[1:message.entities[0].length]
args = message.text.split()[1:]
command = command.split('@')
command.append(message.bot.username)

if not (command[0].lower() in self.command
and command[1].lower() == message.bot.username.lower()):
return None
if not (command[0].lower() in self.command and
command[1].lower() == message.bot.username.lower()):
return None

if self.filters is None or self.filters(message):
return message.text.split()[1:]
if self.filters is None or self.filters(message):
return args

def collect_optional_args(self, dispatcher, update=None, check_result=None):
optional_args = super(CommandHandler, self).collect_optional_args(dispatcher, update)
Expand All @@ -159,3 +171,149 @@ def collect_optional_args(self, dispatcher, update=None, check_result=None):

def collect_additional_context(self, context, update, dispatcher, check_result):
context.args = check_result


class PrefixHandler(CommandHandler):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we add a docstring note about arguments being availible on the CallbackContext object? (applies to CommandHandler too I suppose)

"""Handler class to handle custom prefix commands

This is a intermediate handler between :class:`MessageHandler` and :class:`CommandHandler`.
It supports configurable commands with the same options as CommandHandler. It will respond to
every combination of :attr:`prefix` and :attr:`command`. It will add a ``list`` to the
:class:`CallbackContext` named :attr:`CallbackContext.args`. It will contain a list of strings,
which is the text following the command split on single or consecutive whitespace characters.

Examples::

Single prefix and command:

PrefixHandler('!', 'test', callback) will respond to '!test'.

Multiple prefixes, single command:

PrefixHandler(['!', '#'], 'test', callback) will respond to '!test' and
'#test'.

Miltiple prefixes and commands:

PrefixHandler(['!', '#'], ['test', 'help`], callback) will respond to '!test',
'#test', '!help' and '#help'.

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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing a full stop (.)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

? On the next line at the end of the sentence...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh derp, sorry :P

should listen for.
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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there really a need to have the pass_ stuff on this new handler?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm, In my mind It would be best to keep all handlers compatible to the same model for v11.0 You disagree?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Kinda, but mostly from a "we shouldn't be writing code that's already deprecated" standpoint, but since you've already written it. I think it's fine :D

``args``.
pass_update_queue (:obj:`bool`): Determines whether ``update_queue`` will be
passed to the callback function.
pass_job_queue (:obj:`bool`): Determines whether ``job_queue`` will be passed to
the callback function.
pass_user_data (:obj:`bool`): Determines whether ``user_data`` will be passed to
the callback function.
pass_chat_data (:obj:`bool`): Determines whether ``chat_data`` will be passed to
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``.

Note that this is DEPRECATED, and you should use context based callbacks. See
https://git.io/vp113 for more info.

Args:
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
should listen for.
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:

``def callback(update: Update, context: CallbackContext)``

The return value of the callback is usually ignored except for the special case of
:class:`telegram.ext.ConversationHandler`.
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).
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
consecutive whitespace characters. 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.

"""

def __init__(self,
prefix,
command,
callback,
filters=None,
allow_edited=False,
pass_args=False,
pass_update_queue=False,
pass_job_queue=False,
pass_user_data=False,
pass_chat_data=False):

super(PrefixHandler, self).__init__(
'nocommand', callback, filters=filters, allow_edited=allow_edited, pass_args=pass_args,
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(prefix, string_types):
self.prefix = [prefix.lower()]
else:
self.prefix = prefix
if isinstance(command, string_types):
self.command = [command.lower()]
else:
self.command = command
self.command = [x.lower() + y.lower() for x in self.prefix for y in self.command]

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 (isinstance(update, Update) and
(update.message or update.edited_message and self.allow_edited)):
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:]
2 changes: 1 addition & 1 deletion telegram/ext/handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class Handler(object):

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
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``.

Expand Down
2 changes: 1 addition & 1 deletion telegram/ext/inlinequeryhandler.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ class InlineQueryHandler(Handler):

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
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``.

Expand Down
2 changes: 1 addition & 1 deletion telegram/ext/messagehandler.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ class MessageHandler(Handler):

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
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``.

Expand Down
2 changes: 1 addition & 1 deletion telegram/ext/precheckoutqueryhandler.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ class PreCheckoutQueryHandler(Handler):

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
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``.

Expand Down
2 changes: 1 addition & 1 deletion telegram/ext/regexhandler.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ class RegexHandler(Handler):

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
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``.

Expand Down
2 changes: 1 addition & 1 deletion telegram/ext/shippingqueryhandler.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ class ShippingQueryHandler(Handler):

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
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``.

Expand Down
Loading