Пример #1
0
class InlineQueryHandler(Handler):
    """
    Handler class to handle Telegram inline queries.

    Args:
        callback (function): A function that takes ``bot, update`` as
            positional arguments. It will be called when the ``check_update``
            has determined that an update should be processed by this handler.
        pass_update_queue (optional[bool]): If the handler should be passed the
            update queue as a keyword argument called ``update_queue``. It can
            be used to insert updates. Default is ``False``
    """
    def __init__(self, callback, pass_update_queue=False):
        super(InlineQueryHandler, self).__init__(callback, pass_update_queue)

    def check_update(self, update):
        return isinstance(update, Update) and update.inline_query

    def handle_update(self, update, dispatcher):
        optional_args = self.collect_optional_args(dispatcher)

        self.callback(dispatcher.bot, update, **optional_args)

    # old non-PEP8 Handler methods
    m = "telegram.InlineQueryHandler."
    checkUpdate = deprecate(check_update, m + "checkUpdate",
                            m + "check_update")
    handleUpdate = deprecate(handle_update, m + "handleUpdate",
                             m + "handle_update")
class CallbackQueryHandler(Handler):
    """
    Handler class to handle Telegram callback queries.

    Args:
        callback (function): A function that takes ``bot, update`` as
            positional arguments. It will be called when the ``check_update``
            has determined that an update should be processed by this handler.
        pass_update_queue (optional[bool]): 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 ``Updater`` and ``Dispatcher`` that contains new updates which can
             be used to insert updates. Default is ``False``.
        pass_job_queue (optional[bool]): If set to ``True``, a keyword argument called
            ``job_queue`` will be passed to the callback function. It will be a ``JobQueue``
            instance created by the ``Updater`` which can be used to schedule new jobs.
            Default is ``False``.
    """

    def __init__(self, callback, pass_update_queue=False, pass_job_queue=False):
        super(CallbackQueryHandler, self).__init__(callback,
                                                   pass_update_queue=pass_update_queue,
                                                   pass_job_queue=pass_job_queue)

    def check_update(self, update):
        return isinstance(update, Update) and update.callback_query

    def handle_update(self, update, dispatcher):
        optional_args = self.collect_optional_args(dispatcher)

        return self.callback(dispatcher.bot, update, **optional_args)

    # old non-PEP8 Handler methods
    m = "telegram.CallbackQueryHandler."
    checkUpdate = deprecate(check_update, m + "checkUpdate", m + "check_update")
    handleUpdate = deprecate(handle_update, m + "handleUpdate", m + "handle_update")
class MessageHandler(Handler):
    """
    Handler class to handle telegram messages. Messages are Telegram Updates
    that do not contain a command. They might contain text, media or status
    updates.

    Args:
        filters (list[function]): A list of filter functions. Standard filters
            can be found in the Filters class above.
          | Each `function` takes ``Update`` as arg and returns ``bool``.
          | All messages that match at least one of those filters will be
            accepted. If ``bool(filters)`` evaluates to ``False``, messages are
            not filtered.
        callback (function): A function that takes ``bot, update`` as
            positional arguments. It will be called when the ``check_update``
            has determined that an update should be processed by this handler.
        allow_edited (Optional[bool]): If the handler should also accept edited messages.
            Default is ``False``
        pass_update_queue (optional[bool]): If the handler should be passed the
            update queue as a keyword argument called ``update_queue``. It can
            be used to insert updates. Default is ``False``
    """

    def __init__(self,
                 filters,
                 callback,
                 allow_edited=False,
                 pass_update_queue=False,
                 pass_job_queue=False):
        super(MessageHandler, self).__init__(callback,
                                             pass_update_queue=pass_update_queue,
                                             pass_job_queue=pass_job_queue)
        self.filters = filters
        self.allow_edited = allow_edited

    def check_update(self, update):
        if (isinstance(update, Update)
                and (update.message or update.edited_message and self.allow_edited)):

            if not self.filters:
                res = True

            else:
                message = update.message or update.edited_message
                res = any(func(message) for func in self.filters)

        else:
            res = False

        return res

    def handle_update(self, update, dispatcher):
        optional_args = self.collect_optional_args(dispatcher)

        return self.callback(dispatcher.bot, update, **optional_args)

    # old non-PEP8 Handler methods
    m = "telegram.MessageHandler."
    checkUpdate = deprecate(check_update, m + "checkUpdate", m + "check_update")
    handleUpdate = deprecate(handle_update, m + "handleUpdate", m + "handle_update")
Пример #4
0
class StringRegexHandler(Handler):
    """
    Handler class to handle string updates based on a regex. It uses a
    regular expression to check update content. Read the documentation of the
    ``re`` module for more information. The ``re.match`` function is used to
    determine if an update should be handled by this handler.

    Args:
        pattern (str or Pattern): The regex pattern.
        callback (function): A function that takes ``bot, update`` as
            positional arguments. It will be called when the ``check_update``
            has determined that an update should be processed by this handler.
        pass_groups (optional[bool]): If the callback should be passed the
            result of ``re.match(pattern, update).groups()`` as a keyword
            argument called ``groups``. Default is ``False``
        pass_groupdict (optional[bool]): If the callback should be passed the
            result of ``re.match(pattern, update).groupdict()`` as a keyword
            argument called ``groupdict``. Default is ``False``
        pass_update_queue (optional[bool]): If the handler should be passed the
            update queue as a keyword argument called ``update_queue``. It can
            be used to insert updates. Default is ``False``
    """
    def __init__(self,
                 pattern,
                 callback,
                 pass_groups=False,
                 pass_groupdict=False,
                 pass_update_queue=False):
        super(StringRegexHandler, self).__init__(callback, pass_update_queue)

        if isinstance(pattern, string_types):
            pattern = re.compile(pattern)

        self.pattern = pattern
        self.pass_groups = pass_groups
        self.pass_groupdict = pass_groupdict

    def check_update(self, update):
        return isinstance(update, string_types) and bool(
            re.match(self.pattern, update))

    def handle_update(self, update, dispatcher):
        optional_args = self.collect_optional_args(dispatcher)
        match = re.match(self.pattern, update)

        if self.pass_groups:
            optional_args['groups'] = match.groups()
        if self.pass_groupdict:
            optional_args['groupdict'] = match.groupdict()

        self.callback(dispatcher.bot, update, **optional_args)

    # old non-PEP8 Handler methods
    m = "telegram.StringRegexHandler."
    checkUpdate = deprecate(check_update, m + "checkUpdate",
                            m + "check_update")
    handleUpdate = deprecate(handle_update, m + "handleUpdate",
                             m + "handle_update")
Пример #5
0
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.

    Args:
        command (str): The name of the command this handler should listen for.
        callback (function): A function that takes ``bot, update`` as
            positional arguments. It will be called when the ``check_update``
            has determined that an update should be processed by this handler.
        allow_edited (Optional[bool]): If the handler should also accept edited messages.
            Default is ``False``
        pass_args (optional[bool]): If 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 spaces. Default is ``False``
        pass_update_queue (optional[bool]): If the handler should be passed the
            update queue as a keyword argument called ``update_queue``. It can
            be used to insert updates. Default is ``False``
    """

    def __init__(self,
                 command,
                 callback,
                 allow_edited=False,
                 pass_args=False,
                 pass_update_queue=False):
        super(CommandHandler, self).__init__(callback, pass_update_queue)
        self.command = command
        self.allow_edited = allow_edited
        self.pass_args = pass_args

    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

            return (message.text and message.text.startswith('/')
                    and message.text[1:].split(' ')[0].split('@')[0] == self.command)

        else:
            return False

    def handle_update(self, update, dispatcher):
        optional_args = self.collect_optional_args(dispatcher)

        message = update.message or update.edited_message

        if self.pass_args:
            optional_args['args'] = message.text.split(' ')[1:]

        self.callback(dispatcher.bot, update, **optional_args)

    # old non-PEP8 Handler methods
    m = "telegram.CommandHandler."
    checkUpdate = deprecate(check_update, m + "checkUpdate", m + "check_update")
    handleUpdate = deprecate(handle_update, m + "handleUpdate", m + "handle_update")
Пример #6
0
class StringCommandHandler(Handler):
    """
    Handler class to handle string commands. Commands are string updates
    that start with ``/``.

    Args:
        command (str): The name of the command this handler should listen for.
        callback (function): A function that takes ``bot, update`` as
            positional arguments. It will be called when the ``check_update``
            has determined that an update should be processed by this handler.
        pass_args (optional[bool]): If 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``
        pass_update_queue (optional[bool]): 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 ``Updater`` and ``Dispatcher`` that contains new updates which can
            be used to insert updates. Default is ``False``.
        pass_job_queue (optional[bool]): If set to ``True``, a keyword argument called
            ``job_queue`` will be passed to the callback function. It will be a ``JobQueue``
            instance created by the ``Updater`` which can be used to schedule new jobs.
            Default is ``False``.
    """
    def __init__(self,
                 command,
                 callback,
                 pass_args=False,
                 pass_update_queue=False,
                 pass_job_queue=False):
        super(StringCommandHandler,
              self).__init__(callback,
                             pass_update_queue=pass_update_queue,
                             pass_job_queue=pass_job_queue)
        self.command = command
        self.pass_args = pass_args

    def check_update(self, update):
        return (isinstance(update, str) and update.startswith('/')
                and update[1:].split(' ')[0] == self.command)

    def handle_update(self, update, dispatcher):
        optional_args = self.collect_optional_args(dispatcher)

        if self.pass_args:
            optional_args['args'] = update.split()[1:]

        return self.callback(dispatcher.bot, update, **optional_args)

    # old non-PEP8 Handler methods
    m = "telegram.StringCommandHandler."
    checkUpdate = deprecate(check_update, m + "checkUpdate",
                            m + "check_update")
    handleUpdate = deprecate(handle_update, m + "handleUpdate",
                             m + "handle_update")
Пример #7
0
class ChannelPostHandler(Handler):
    def check_update(self, update):
        return isinstance(update, Update) and update.channel_post

    def handle_update(self, update, dispatcher):
        return self.callback(dispatcher.bot, update)

    m = "telegram.ChannelPostHandler."
    checkUpdate = deprecate(check_update, m + "checkUpdate",
                            m + "check_update")
    handleUpdate = deprecate(handle_update, m + "handleUpdate",
                             m + "handle_update")
class ChosenInlineResultHandler(Handler):
    """
    Handler class to handle Telegram updates that contain a chosen inline
    result.

    Args:
        callback (function): A function that takes ``bot, update`` as
            positional arguments. It will be called when the ``check_update``
            has determined that an update should be processed by this handler.
        pass_update_queue (optional[bool]): 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 ``Updater`` and ``Dispatcher`` that contains new updates which can
            be used to insert updates. Default is ``False``.
        pass_job_queue (optional[bool]): If set to ``True``, a keyword argument called
            ``job_queue`` will be passed to the callback function. It will be a ``JobQueue``
            instance created by the ``Updater`` which can be used to schedule new jobs.
            Default is ``False``.
        pass_user_data (optional[bool]): If set to ``True``, a keyword argument called
            ``user_data`` will be passed to the callback function. It will be a ``dict`` you
            can use to keep any data related to the user that sent the update. For each update of
            the same user, it will be the same ``dict``. Default is ``False``.
        pass_chat_data (optional[bool]): If set to ``True``, a keyword argument called
            ``chat_data`` will be passed to the callback function. It will be a ``dict`` you
            can use to keep any data related to the chat that the update was sent in.
            For each update in the same chat, it will be the same ``dict``. Default is ``False``.
    """
    def __init__(self,
                 callback,
                 pass_update_queue=False,
                 pass_job_queue=False,
                 pass_user_data=False,
                 pass_chat_data=False):
        super(ChosenInlineResultHandler,
              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)

    def check_update(self, update):
        return isinstance(update, Update) and update.chosen_inline_result

    def handle_update(self, update, dispatcher):
        optional_args = self.collect_optional_args(dispatcher, update)

        return self.callback(dispatcher.bot, update, **optional_args)

    # old non-PEP8 Handler methods
    m = "telegram.ChosenInlineResultHandler."
    checkUpdate = deprecate(check_update, m + "checkUpdate",
                            m + "check_update")
    handleUpdate = deprecate(handle_update, m + "handleUpdate",
                             m + "handle_update")
Пример #9
0
class TypeHandler(Handler):
    """
    Handler class to handle updates of custom types.

    Args:
        type (type): The ``type`` of updates this handler should process, as
            determined by ``isinstance``
        callback (function): A function that takes ``bot, update`` as
            positional arguments. It will be called when the ``check_update``
            has determined that an update should be processed by this handler.
        strict (optional[bool]): Use ``type`` instead of ``isinstance``.
            Default is ``False``
        pass_update_queue (optional[bool]): 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 ``Updater`` and ``Dispatcher`` that contains new updates which can
             be used to insert updates. Default is ``False``.
        pass_job_queue (optional[bool]): If set to ``True``, a keyword argument called
            ``job_queue`` will be passed to the callback function. It will be a ``JobQueue``
            instance created by the ``Updater`` which can be used to schedule new jobs.
            Default is ``False``.
    """
    def __init__(self,
                 type,
                 callback,
                 strict=False,
                 pass_update_queue=False,
                 pass_job_queue=False):
        super(TypeHandler, self).__init__(callback,
                                          pass_update_queue=pass_update_queue,
                                          pass_job_queue=pass_job_queue)
        self.type = type
        self.strict = strict

    def check_update(self, update):
        if not self.strict:
            return isinstance(update, self.type)
        else:
            return type(update) is self.type

    def handle_update(self, update, dispatcher):
        optional_args = self.collect_optional_args(dispatcher)

        return self.callback(dispatcher.bot, update, **optional_args)

    # old non-PEP8 Handler methods
    m = "telegram.TypeHandler."
    checkUpdate = deprecate(check_update, m + "checkUpdate",
                            m + "check_update")
    handleUpdate = deprecate(handle_update, m + "handleUpdate",
                             m + "handle_update")
Пример #10
0
class WhitelistHandler(Handler):
    """
    Handler class to block users not on white-list.

    Args:
        whitelist (list[int]): A list consist of whitelisted user IDs
            in int.
        pass_update_queue (optional[bool]): If the handler should be passed the
            update queue as a keyword argument called ``update_queue``. It can
            be used to insert updates. Default is ``False``
    """
    def __init__(self, whitelist, pass_update_queue=False):
        def void_function(bot, update):
            pass

        self.whitelist = list(map(lambda i: int(i), whitelist))
        super(WhitelistHandler, self).__init__(void_function,
                                               pass_update_queue)

    def check_update(self, update):
        if getattr(update, "message", None):
            obj = update.message
        elif getattr(update, "callback_query", None):
            obj = update.callback_query
        elif getattr(update, "edited_message", None):
            obj = update.edited_message
        else:
            return False
        # prevent white list
        return False  #isinstance(update, Update) and not int(obj.from_user.id) in self.whitelist

    def handle_update(self, update, dispatcher):
        pass

    # old non-PEP8 Handler methods
    m = "telegram.WhitelistHandler."
    checkUpdate = deprecate(check_update, m + "checkUpdate",
                            m + "check_update")
    handleUpdate = deprecate(handle_update, m + "handleUpdate",
                             m + "handle_update")
Пример #11
0
class RegexHandler(Handler):
    """
    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`` module for more information. The ``re.match`` function is used to
    determine if an update should be handled by this handler.

    Args:
        pattern (str or Pattern): The regex pattern.
        callback (function): A function that takes ``bot, update`` as
            positional arguments. It will be called when the ``check_update``
            has determined that an update should be processed by this handler.
        pass_groups (optional[bool]): If the callback should be passed the
            result of ``re.match(pattern, text).groups()`` as a keyword
            argument called ``groups``. Default is ``False``
        pass_groupdict (optional[bool]): If the callback should be passed the
            result of ``re.match(pattern, text).groupdict()`` as a keyword
            argument called ``groupdict``. Default is ``False``
        pass_update_queue (optional[bool]): 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 ``Updater`` and ``Dispatcher`` that contains new updates which can
            be used to insert updates. Default is ``False``.
        pass_job_queue (optional[bool]): If set to ``True``, a keyword argument called
            ``job_queue`` will be passed to the callback function. It will be a ``JobQueue``
            instance created by the ``Updater`` which can be used to schedule new jobs.
            Default is ``False``.
        pass_user_data (optional[bool]): If set to ``True``, a keyword argument called
            ``user_data`` will be passed to the callback function. It will be a ``dict`` you
            can use to keep any data related to the user that sent the update. For each update of
            the same user, it will be the same ``dict``. Default is ``False``.
        pass_chat_data (optional[bool]): If set to ``True``, a keyword argument called
            ``chat_data`` will be passed to the callback function. It will be a ``dict`` you
            can use to keep any data related to the chat that the update was sent in.
            For each update in the same chat, it will be the same ``dict``. Default is ``False``.
    """
    def __init__(self,
                 pattern,
                 callback,
                 pass_groups=False,
                 pass_groupdict=False,
                 pass_update_queue=False,
                 pass_job_queue=False,
                 pass_user_data=False,
                 pass_chat_data=False,
                 allow_edited=False,
                 message_updates=True,
                 channel_post_updates=False):
        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
        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

    def _is_allowed_message(self, update):
        return (self.message_updates
                and (update.message or
                     (update.edited_message and self.allow_edited)))

    def _is_allowed_channel_post(self, update):
        return (self.channel_post_updates
                and (update.channel_post or
                     (update.edited_channel_post and self.allow_edited)))

    def check_update(self, update):
        if (isinstance(update, Update)
                and (self._is_allowed_message(update)
                     or self._is_allowed_channel_post(update))
                and update.effective_message.text):
            match = re.match(self.pattern, update.effective_message.text)
            return bool(match)
        else:
            return False

    def handle_update(self, update, dispatcher):
        optional_args = self.collect_optional_args(dispatcher, update)
        match = re.match(self.pattern, update.effective_message.text)

        if self.pass_groups:
            optional_args['groups'] = match.groups()
        if self.pass_groupdict:
            optional_args['groupdict'] = match.groupdict()

        return self.callback(dispatcher.bot, update, **optional_args)

    # old non-PEP8 Handler methods
    m = "telegram.RegexHandler."
    checkUpdate = deprecate(check_update, m + "checkUpdate",
                            m + "check_update")
    handleUpdate = deprecate(handle_update, m + "handleUpdate",
                             m + "handle_update")
Пример #12
0
class Dispatcher(object):
    """
    This class dispatches all kinds of updates to its registered handlers.

    Args:
        bot (telegram.Bot): The bot object that should be passed to the
            handlers
        update_queue (Queue): The synchronized queue that will contain the
            updates.
        job_queue (Optional[telegram.ext.JobQueue]): The ``JobQueue`` instance to pass onto handler
            callbacks
        workers (Optional[int]): Number of maximum concurrent worker threads for the ``@run_async``
            decorator
    """
    def __init__(self,
                 bot,
                 update_queue,
                 workers=4,
                 exception_event=None,
                 job_queue=None):
        self.bot = bot
        self.update_queue = update_queue
        self.job_queue = job_queue

        self.handlers = {}
        """:type: dict[int, list[Handler]"""
        self.groups = []
        """:type: list[int]"""
        self.error_handlers = []

        self.logger = logging.getLogger(__name__)
        self.running = False
        self.__stop_event = Event()
        self.__exception_event = exception_event or Event()

        with ASYNC_LOCK:
            if not ASYNC_THREADS:
                if request.is_con_pool_initialized():
                    raise RuntimeError('Connection Pool already initialized')

                # we need a connection pool the size of:
                # * for each of the workers
                # * 1 for Dispatcher
                # * 1 for polling Updater (even if updater is webhook, we can spare a connection)
                # * 1 for JobQueue
                request.CON_POOL_SIZE = workers + 3
                for i in range(workers):
                    thread = Thread(target=_pooled, name=str(i))
                    ASYNC_THREADS.add(thread)
                    thread.start()
            else:
                self.logger.debug('Thread pool already initialized, skipping.')

    def start(self):
        """
        Thread target of thread 'dispatcher'. Runs in background and processes
        the update queue.
        """

        if self.running:
            self.logger.warning('already running')
            return

        if self.__exception_event.is_set():
            msg = 'reusing dispatcher after exception event is forbidden'
            self.logger.error(msg)
            raise TelegramError(msg)

        self.running = True
        self.logger.debug('Dispatcher started')

        while 1:
            try:
                # Pop update from update queue.
                update = self.update_queue.get(True, 1)
            except Empty:
                if self.__stop_event.is_set():
                    self.logger.debug('orderly stopping')
                    break
                elif self.__exception_event.is_set():
                    self.logger.critical(
                        'stopping due to exception in another thread')
                    break
                continue

            self.logger.debug('Processing Update: %s' % update)
            self.process_update(update)

        self.running = False
        self.logger.debug('Dispatcher thread stopped')

    def stop(self):
        """
        Stops the thread
        """
        if self.running:
            self.__stop_event.set()
            while self.running:
                sleep(0.1)
            self.__stop_event.clear()

    def process_update(self, update):
        """
        Processes a single update.

        Args:
            update (object):
        """

        # An error happened while polling
        if isinstance(update, TelegramError):
            self.dispatch_error(None, update)

        else:
            for group in self.groups:
                for handler in self.handlers[group]:
                    try:
                        if handler.check_update(update):
                            handler.handle_update(update, self)
                            break
                    # Dispatch any errors
                    except TelegramError as te:
                        self.logger.warn(
                            'A TelegramError was raised while processing the '
                            'Update.')

                        try:
                            self.dispatch_error(update, te)
                        except Exception:
                            self.logger.exception(
                                'An uncaught error was raised while '
                                'handling the error')
                        finally:
                            break

                    # Errors should not stop the thread
                    except Exception:
                        self.logger.exception(
                            'An uncaught error was raised while '
                            'processing the update')
                        break

    def add_handler(self, handler, group=DEFAULT_GROUP):
        """
        Register a handler.

        TL;DR: Order and priority counts. 0 or 1 handlers per group will be
        used.

        A handler must be an instance of a subclass of
        telegram.ext.Handler. All handlers are organized in groups with a
        numeric value. The default group is 0. All groups will be evaluated for
        handling an update, but only 0 or 1 handler per group will be used.

        The priority/order of handlers is determined as follows:

          * Priority of the group (lower group number == higher priority)

          * The first handler in a group which should handle an update will be
            used. Other handlers from the group will not be used. The order in
            which handlers were added to the group defines the priority.

        Args:
            handler (Handler): A Handler instance
            group (Optional[int]): The group identifier. Default is 0
        """

        if not isinstance(handler, Handler):
            raise TypeError('handler is not an instance of {0}'.format(
                Handler.__name__))
        if not isinstance(group, int):
            raise TypeError('group is not int')

        if group not in self.handlers:
            self.handlers[group] = list()
            self.groups.append(group)
            self.groups = sorted(self.groups)

        self.handlers[group].append(handler)

    def remove_handler(self, handler, group=DEFAULT_GROUP):
        """
        Remove a handler from the specified group

        Args:
            handler (Handler): A Handler instance
            group (optional[object]): The group identifier. Default is 0
        """
        if handler in self.handlers[group]:
            self.handlers[group].remove(handler)
            if not self.handlers[group]:
                del self.handlers[group]
                self.groups.remove(group)

    def add_error_handler(self, callback):
        """
        Registers an error handler in the Dispatcher.

        Args:
            handler (function): A function that takes ``Bot, Update,
                TelegramError`` as arguments.
        """

        self.error_handlers.append(callback)

    def remove_error_handler(self, callback):
        """
        De-registers an error handler.

        Args:
            handler (function):
        """

        if callback in self.error_handlers:
            self.error_handlers.remove(callback)

    def dispatch_error(self, update, error):
        """
        Dispatches an error.

        Args:
            update (object): The update that caused the error
            error (telegram.TelegramError): The Telegram error that was raised.
        """

        for callback in self.error_handlers:
            callback(self.bot, update, error)

    # old non-PEP8 Dispatcher methods
    m = "telegram.dispatcher."
    addHandler = deprecate(add_handler, m + "AddHandler", m + "add_handler")
    removeHandler = deprecate(remove_handler, m + "removeHandler",
                              m + "remove_handler")
    addErrorHandler = deprecate(add_error_handler, m + "addErrorHandler",
                                m + "add_error_handler")
    removeErrorHandler = deprecate(remove_error_handler,
                                   m + "removeErrorHandler",
                                   m + "remove_error_handler")
Пример #13
0
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.

    Args:
        command (str): The name of the command this handler should listen for.
        callback (function): A function that takes ``bot, update`` as
            positional arguments. It will be called when the ``check_update``
            has determined that an update should be processed by this handler.
        allow_edited (Optional[bool]): If the handler should also accept edited messages.
            Default is ``False``
        pass_args (optional[bool]): If 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``
        pass_update_queue (optional[bool]): 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 ``Updater`` and ``Dispatcher`` that contains new updates which can
             be used to insert updates. Default is ``False``.
        pass_job_queue (optional[bool]): If set to ``True``, a keyword argument called
            ``job_queue`` will be passed to the callback function. It will be a ``JobQueue``
            instance created by the ``Updater`` which can be used to schedule new jobs.
            Default is ``False``.
        pass_user_data (optional[bool]): If set to ``True``, a keyword argument called
            ``user_data`` will be passed to the callback function. It will be a ``dict`` you
            can use to keep any data related to the user that sent the update. For each update of
            the same user, it will be the same ``dict``. Default is ``False``.
        pass_chat_data (optional[bool]): If set to ``True``, a keyword argument called
            ``chat_data`` will be passed to the callback function. It will be a ``dict`` you
            can use to keep any data related to the chat that the update was sent in.
            For each update in the same chat, it will be the same ``dict``. Default is ``False``.
    """
    def __init__(self,
                 command,
                 callback,
                 allow_edited=False,
                 pass_args=False,
                 pass_update_queue=False,
                 pass_job_queue=False,
                 pass_user_data=False,
                 pass_chat_data=False):
        super(CommandHandler,
              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)
        self.command = command
        self.allow_edited = allow_edited
        self.pass_args = pass_args

    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

            return (message.text and message.text.startswith('/')
                    and message.text[1:].split(' ')[0].split('@')[0]
                    == self.command)

        else:
            return False

    def handle_update(self, update, dispatcher):
        optional_args = self.collect_optional_args(dispatcher, update)

        message = update.message or update.edited_message

        if self.pass_args:
            optional_args['args'] = message.text.split()[1:]

        return self.callback(dispatcher.bot, update, **optional_args)

    # old non-PEP8 Handler methods
    m = "telegram.CommandHandler."
    checkUpdate = deprecate(check_update, m + "checkUpdate",
                            m + "check_update")
    handleUpdate = deprecate(handle_update, m + "handleUpdate",
                             m + "handle_update")
Пример #14
0
class InlineQueryHandler(Handler):
    """
    Handler class to handle Telegram inline queries. Optionally based on a regex. Read the
    documentation of the ``re`` module for more information.

    Attributes:
        callback (:obj:`callable`): The callback function for this handler.
        pass_update_queue (:obj:`bool`): Optional. Determines whether ``update_queue`` will be
            passed to the callback function.
        pass_job_queue (:obj:`bool`): Optional. Determines whether ``job_queue`` will be passed to
            the callback function.
        pattern (:obj:`str` | :obj:`Pattern`): Optional. Regex pattern to test
            :attr:`telegram.InlineQuery.query` against.
        pass_groups (:obj:`bool`): Optional. Determines whether ``groups`` will be passed to the
            callback function.
        pass_groupdict (:obj:`bool`): Optional. Determines whether ``groupdict``. will be passed to
            the callback function.
        pass_user_data (:obj:`bool`): Optional. Determines whether ``user_data`` will be passed to
            the callback function.
        pass_chat_data (:obj:`bool`): Optional. 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``.

    Args:
        callback (:obj:`callable`): A function that takes ``bot, update`` as positional arguments.
            It will be called when the :attr:`check_update` has determined that an update should be
            processed by this handler.
        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``.
        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``.
        pattern (:obj:`str` | :obj:`Pattern`, optional): Regex pattern. If not ``None``,
            ``re.match`` is used on :attr:`telegram.InlineQuery.query` to determine if an update
            should be handled by this 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``
        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``
        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``.
        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``.
    """

    def __init__(self,
                 callback,
                 pass_update_queue=False,
                 pass_job_queue=False,
                 pattern=None,
                 pass_groups=False,
                 pass_groupdict=False,
                 pass_user_data=False,
                 pass_chat_data=False):
        super(InlineQueryHandler, 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
        self.pass_groups = pass_groups
        self.pass_groupdict = pass_groupdict

    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.inline_query:
            if self.pattern:
                if update.inline_query.query:
                    match = re.match(self.pattern, update.inline_query.query)
                    return bool(match)
            else:
                return True

    def handle_update(self, update, dispatcher):
        """
        Send the update to the :attr:`callback`.

        Args:
            update (:class:`telegram.Update`): Incoming telegram update.
            dispatcher (:class:`telegram.ext.Dispatcher`): Dispatcher that originated the Update.
        """

        optional_args = self.collect_optional_args(dispatcher, update)
        if self.pattern:
            match = re.match(self.pattern, update.inline_query.query)

            if self.pass_groups:
                optional_args['groups'] = match.groups()
            if self.pass_groupdict:
                optional_args['groupdict'] = match.groupdict()

        return self.callback(dispatcher.bot, update, **optional_args)

    # old non-PEP8 Handler methods
    m = "telegram.InlineQueryHandler."
    checkUpdate = deprecate(check_update, m + "checkUpdate", m + "check_update")
    handleUpdate = deprecate(handle_update, m + "handleUpdate", m + "handle_update")
Пример #15
0
class MessageHandler(Handler):
    """
    Handler class to handle telegram messages. Messages are Telegram Updates
    that do not contain a command. They might contain text, media or status
    updates.

    Args:
        filters (telegram.ext.BaseFilter): A filter inheriting from
            :class:`telegram.filters.BaseFilter`. Standard filters can be found in
            :class:`telegram.filters.Filters`. Filters can be combined using bitwise
            operators (& for and, | for or).
        callback (function): A function that takes ``bot, update`` as
            positional arguments. It will be called when the ``check_update``
            has determined that an update should be processed by this handler.
        allow_edited (Optional[bool]): If the handler should also accept edited messages.
            Default is ``False``
        pass_update_queue (optional[bool]): If the handler should be passed the
            update queue as a keyword argument called ``update_queue``. It can
            be used to insert updates. Default is ``False``
        pass_user_data (optional[bool]): If set to ``True``, a keyword argument called
            ``user_data`` will be passed to the callback function. It will be a ``dict`` you
            can use to keep any data related to the user that sent the update. For each update of
            the same user, it will be the same ``dict``. Default is ``False``.
        pass_chat_data (optional[bool]): If set to ``True``, a keyword argument called
            ``chat_data`` will be passed to the callback function. It will be a ``dict`` you
            can use to keep any data related to the chat that the update was sent in.
            For each update in the same chat, it will be the same ``dict``. Default is ``False``.
    """
    def __init__(self,
                 filters,
                 callback,
                 allow_edited=False,
                 pass_update_queue=False,
                 pass_job_queue=False,
                 pass_user_data=False,
                 pass_chat_data=False):
        super(MessageHandler,
              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)
        self.filters = filters
        self.allow_edited = allow_edited

        # We put this up here instead of with the rest of checking code
        # in check_update since we don't wanna spam a ton
        if isinstance(self.filters, list):
            warnings.warn(
                'Using a list of filters in MessageHandler is getting '
                'deprecated, please use bitwise operators (& and |) '
                'instead. More info: https://git.io/vPTbc.')

    def check_update(self, update):
        if (isinstance(update, Update) and
            (update.message or update.edited_message and self.allow_edited)):

            if not self.filters:
                res = True

            else:
                message = update.message or update.edited_message
                if isinstance(self.filters, list):
                    res = any(func(message) for func in self.filters)
                else:
                    res = self.filters(message)

        else:
            res = False

        return res

    def handle_update(self, update, dispatcher):
        optional_args = self.collect_optional_args(dispatcher, update)

        return self.callback(dispatcher.bot, update, **optional_args)

    # old non-PEP8 Handler methods
    m = "telegram.MessageHandler."
    checkUpdate = deprecate(check_update, m + "checkUpdate",
                            m + "check_update")
    handleUpdate = deprecate(handle_update, m + "handleUpdate",
                             m + "handle_update")
class ChosenInlineResultHandler(Handler):
    """Handler class to handle Telegram updates that contain a chosen inline result.

    Attributes:
        callback (:obj:`callable`): The callback function for this handler.
        pass_update_queue (:obj:`bool`): Optional. Determines whether ``update_queue`` will be
            passed to the callback function.
        pass_job_queue (:obj:`bool`): Optional. Determines whether ``job_queue`` will be passed to
            the callback function.
        pass_user_data (:obj:`bool`): Optional. Determines whether ``user_data`` will be passed to
            the callback function.
        pass_chat_data (:obj:`bool`): Optional. 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``.

    Args:
        callback (:obj:`callable`): A function that takes ``bot, update`` as positional arguments.
            It will be called when the :attr:`check_update` has determined that an update should be
            processed by this handler.
        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``.
        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``.
        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``.
        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``.

    """
    def __init__(self,
                 callback,
                 pass_update_queue=False,
                 pass_job_queue=False,
                 pass_user_data=False,
                 pass_chat_data=False):
        super(ChosenInlineResultHandler,
              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)

    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`

        """
        return isinstance(update, Update) and update.chosen_inline_result

    def handle_update(self, update, dispatcher):
        """Send the update to the :attr:`callback`.

        Args:
            update (:class:`telegram.Update`): Incoming telegram update.
            dispatcher (:class:`telegram.ext.Dispatcher`): Dispatcher that originated the Update.

        """
        optional_args = self.collect_optional_args(dispatcher, update)

        return self.callback(dispatcher.bot, update, **optional_args)

    # old non-PEP8 Handler methods
    m = "telegram.ChosenInlineResultHandler."
    checkUpdate = deprecate(check_update, m + "checkUpdate",
                            m + "check_update")
    handleUpdate = deprecate(handle_update, m + "handleUpdate",
                             m + "handle_update")
Пример #17
0
class Handler(object):
    """
    The base class for all update handlers. You can create your own handlers
    by inheriting from this class.

    Args:
        callback (function): A function that takes ``bot, update`` as
            positional arguments. It will be called when the ``check_update``
            has determined that an update should be processed by this handler.
        pass_update_queue (optional[bool]): If the callback should be passed
            the update queue as a keyword argument called ``update_queue``. It
            can be used to insert updates. Default is ``False``
    """
    def __init__(self, callback, pass_update_queue=False):
        self.callback = callback
        self.pass_update_queue = pass_update_queue

    def check_update(self, update):
        """
        This method is called to determine if an update should be handled by
        this handler instance. It should always be overridden.

        Args:
            update (object): The update to be tested

        Returns:
            bool
        """
        raise NotImplementedError

    def handle_update(self, update, dispatcher):
        """
        This method is called if it was determined that an update should indeed
        be handled by this instance. It should also be overridden, but in most
        cases call self.callback(dispatcher.bot, update), possibly along with
        optional arguments.

        Args:
            update (object): The update to be handled

        """
        raise NotImplementedError

    def collect_optional_args(self, dispatcher):
        """
        Prepares the optional arguments that are the same for all types of
        handlers

        Args:
            dispatcher (Dispatcher):
        """
        optional_args = dict()
        if self.pass_update_queue:
            optional_args['update_queue'] = dispatcher.update_queue

        return optional_args

    # old non-PEP8 Handler methods
    m = "telegram.Handler."
    checkUpdate = deprecate(check_update, m + "checkUpdate",
                            m + "check_update")
    handleUpdate = deprecate(handle_update, m + "handleUpdate",
                             m + "handle_update")
    collectOptionalArgs = deprecate(collect_optional_args,
                                    m + "collectOptionalArgs",
                                    m + "collect_optional_args")
Пример #18
0
class CallbackQueryHandler(Handler):
    """
    Handler class to handle Telegram callback queries. Optionally based on a regex.
    Read the documentation of the ``re`` module for more information.

    Args:
        callback (function): A function that takes ``bot, update`` as
            positional arguments. It will be called when the ``check_update``
            has determined that an update should be processed by this handler.
        pass_update_queue (optional[bool]): 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 ``Updater`` and ``Dispatcher`` that contains new updates which can
            be used to insert updates. Default is ``False``.
        pass_job_queue (optional[bool]): If set to ``True``, a keyword argument called
            ``job_queue`` will be passed to the callback function. It will be a ``JobQueue``
            instance created by the ``Updater`` which can be used to schedule new jobs.
            Default is ``False``.
        pattern (optional[str or Pattern]): Optional regex pattern. If not ``None`` ``re.match``
            is used to determine if an update should be handled by this handler.
        pass_groups (optional[bool]): If the callback should be passed the
            result of ``re.match(pattern, data).groups()`` as a keyword
            argument called ``groups``. Default is ``False``
        pass_groupdict (optional[bool]): If the callback should be passed the
            result of ``re.match(pattern, data).groupdict()`` as a keyword
            argument called ``groupdict``. Default is ``False``
        pass_user_data (optional[bool]): If set to ``True``, a keyword argument called
            ``user_data`` will be passed to the callback function. It will be a ``dict`` you
            can use to keep any data related to the user that sent the update. For each update of
            the same user, it will be the same ``dict``. Default is ``False``.
        pass_chat_data (optional[bool]): If set to ``True``, a keyword argument called
            ``chat_data`` will be passed to the callback function. It will be a ``dict`` you
            can use to keep any data related to the chat that the update was sent in.
            For each update in the same chat, it will be the same ``dict``. Default is ``False``.
    """
    def __init__(self,
                 callback,
                 pass_update_queue=False,
                 pass_job_queue=False,
                 pattern=None,
                 pass_groups=False,
                 pass_groupdict=False,
                 pass_user_data=False,
                 pass_chat_data=False):
        super(CallbackQueryHandler,
              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
        self.pass_groups = pass_groups
        self.pass_groupdict = pass_groupdict

    def check_update(self, update):
        if isinstance(update, Update) and update.callback_query:
            if self.pattern:
                if update.callback_query.data:
                    match = re.match(self.pattern, update.callback_query.data)
                    return bool(match)
            else:
                return True

    def handle_update(self, update, dispatcher):
        optional_args = self.collect_optional_args(dispatcher, update)
        if self.pattern:
            match = re.match(self.pattern, update.callback_query.data)

            if self.pass_groups:
                optional_args['groups'] = match.groups()
            if self.pass_groupdict:
                optional_args['groupdict'] = match.groupdict()

        return self.callback(dispatcher.bot, update, **optional_args)

    # old non-PEP8 Handler methods
    m = "telegram.CallbackQueryHandler."
    checkUpdate = deprecate(check_update, m + "checkUpdate",
                            m + "check_update")
    handleUpdate = deprecate(handle_update, m + "handleUpdate",
                             m + "handle_update")
Пример #19
0
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.

    Args:
        command (str): The name of the command this handler should listen for.
        callback (function): A function that takes ``bot, update`` as
            positional arguments. It will be called when the ``check_update``
            has determined that an update should be processed by this handler.
        filters (telegram.ext.BaseFilter): 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).
        allow_edited (Optional[bool]): If the handler should also accept edited messages.
            Default is ``False``
        pass_args (optional[bool]): If 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``
        pass_update_queue (optional[bool]): 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 ``Updater`` and ``Dispatcher`` that contains new updates which can
            be used to insert updates. Default is ``False``.
        pass_job_queue (optional[bool]): If set to ``True``, a keyword argument called
            ``job_queue`` will be passed to the callback function. It will be a ``JobQueue``
            instance created by the ``Updater`` which can be used to schedule new jobs.
            Default is ``False``.
        pass_user_data (optional[bool]): If set to ``True``, a keyword argument called
            ``user_data`` will be passed to the callback function. It will be a ``dict`` you
            can use to keep any data related to the user that sent the update. For each update of
            the same user, it will be the same ``dict``. Default is ``False``.
        pass_chat_data (optional[bool]): If set to ``True``, a keyword argument called
            ``chat_data`` will be passed to the callback function. It will be a ``dict`` you
            can use to keep any data related to the chat that the update was sent in.
            For each update in the same chat, it will be the same ``dict``. Default is ``False``.
    """
    def __init__(self,
                 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(CommandHandler,
              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)
        self.command = command
        self.filters = filters
        self.allow_edited = allow_edited
        self.pass_args = pass_args

        # We put this up here instead of with the rest of checking code
        # in check_update since we don't wanna spam a ton
        if isinstance(self.filters, list):
            warnings.warn(
                'Using a list of filters in MessageHandler is getting '
                'deprecated, please use bitwise operators (& and |) '
                'instead. More info: https://git.io/vPTbc.')

    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

            if message.text:
                command = message.text[1:].split(' ')[0].split('@')
                command.append(
                    update.message.bot.username
                )  # in case the command was send without a username

                if self.filters is None:
                    res = True
                elif isinstance(self.filters, list):
                    res = any(func(message) for func in self.filters)
                else:
                    res = self.filters(message)

                return res and (message.text.startswith('/')
                                and command[0] == self.command
                                and command[1] == update.message.bot.username)
            else:
                return False

        else:
            return False

    def handle_update(self, update, dispatcher):
        optional_args = self.collect_optional_args(dispatcher, update)

        message = update.message or update.edited_message

        if self.pass_args:
            optional_args['args'] = message.text.split()[1:]

        return self.callback(dispatcher.bot, update, **optional_args)

    # old non-PEP8 Handler methods
    m = "telegram.CommandHandler."
    checkUpdate = deprecate(check_update, m + "checkUpdate",
                            m + "check_update")
    handleUpdate = deprecate(handle_update, m + "handleUpdate",
                             m + "handle_update")
Пример #20
0
class Dispatcher(object):
    """
    This class dispatches all kinds of updates to its registered handlers.

    Args:
        bot (telegram.Bot): The bot object that should be passed to the
            handlers
        update_queue (Queue): The synchronized queue that will contain the
            updates.
        job_queue (Optional[telegram.ext.JobQueue]): The ``JobQueue`` instance to pass onto handler
            callbacks
        workers (Optional[int]): Number of maximum concurrent worker threads for the ``@run_async``
            decorator

    """
    __singleton_lock = Lock()
    __singleton_semaphore = BoundedSemaphore()
    __singleton = None
    logger = logging.getLogger(__name__)

    def __init__(self, bot, update_queue, workers=4, exception_event=None, job_queue=None):
        self.bot = bot
        self.update_queue = update_queue
        self.job_queue = job_queue
        self.workers = workers

        self.user_data = defaultdict(dict)
        """:type: dict[int, dict]"""
        self.chat_data = defaultdict(dict)
        """:type: dict[int, dict]"""

        self.handlers = {}
        """:type: dict[int, list[Handler]"""
        self.groups = []
        """:type: list[int]"""
        self.error_handlers = []

        self.running = False
        self.__stop_event = Event()
        self.__exception_event = exception_event or Event()
        self.__async_queue = Queue()
        self.__async_threads = set()

        # For backward compatibility, we allow a "singleton" mode for the dispatcher. When there's
        # only one instance of Dispatcher, it will be possible to use the `run_async` decorator.
        with self.__singleton_lock:
            if self.__singleton_semaphore.acquire(blocking=0):
                self._set_singleton(self)
            else:
                self._set_singleton(None)

    @classmethod
    def _reset_singleton(cls):
        # NOTE: This method was added mainly for test_updater benefit and specifically pypy. Never
        #       call it in production code.
        cls.__singleton_semaphore.release()

    def _init_async_threads(self, base_name, workers):
        base_name = '{}_'.format(base_name) if base_name else ''

        for i in range(workers):
            thread = Thread(target=self._pooled, name='{}{}'.format(base_name, i))
            self.__async_threads.add(thread)
            thread.start()

    @classmethod
    def _set_singleton(cls, val):
        cls.logger.debug('Setting singleton dispatcher as %s', val)
        cls.__singleton = weakref.ref(val) if val else None

    @classmethod
    def get_instance(cls):
        """Get the singleton instance of this class.

        Returns:
            Dispatcher

        """
        if cls.__singleton is not None:
            return cls.__singleton()
        else:
            raise RuntimeError('{} not initialized or multiple instances exist'.format(
                cls.__name__))

    def _pooled(self):
        """
        A wrapper to run a thread in a thread pool
        """
        thr_name = current_thread().getName()
        while 1:
            promise = self.__async_queue.get()

            # If unpacking fails, the thread pool is being closed from Updater._join_async_threads
            if not isinstance(promise, Promise):
                self.logger.debug("Closing run_async thread %s/%d", thr_name,
                                  len(self.__async_threads))
                break

            promise.run()

    def run_async(self, func, *args, **kwargs):
        """Queue a function (with given args/kwargs) to be run asynchronously.

        Args:
            func (function): The function to run in the thread.
            args (Optional[tuple]): Arguments to `func`.
            kwargs (Optional[dict]): Keyword arguments to `func`.

        Returns:
            Promise

        """
        # TODO: handle exception in async threads
        #       set a threading.Event to notify caller thread
        promise = Promise(func, args, kwargs)
        self.__async_queue.put(promise)
        return promise

    def start(self):
        """
        Thread target of thread 'dispatcher'. Runs in background and processes
        the update queue.
        """

        if self.running:
            self.logger.warning('already running')
            return

        if self.__exception_event.is_set():
            msg = 'reusing dispatcher after exception event is forbidden'
            self.logger.error(msg)
            raise TelegramError(msg)

        self._init_async_threads(uuid4(), self.workers)
        self.running = True
        self.logger.debug('Dispatcher started')

        while 1:
            try:
                # Pop update from update queue.
                update = self.update_queue.get(True, 1)
            except Empty:
                if self.__stop_event.is_set():
                    self.logger.debug('orderly stopping')
                    break
                elif self.__exception_event.is_set():
                    self.logger.critical('stopping due to exception in another thread')
                    break
                continue

            self.logger.debug('Processing Update: %s' % update)
            self.process_update(update)

        self.running = False
        self.logger.debug('Dispatcher thread stopped')

    def stop(self):
        """
        Stops the thread
        """
        if self.running:
            self.__stop_event.set()
            while self.running:
                sleep(0.1)
            self.__stop_event.clear()

        # async threads must be join()ed only after the dispatcher thread was joined,
        # otherwise we can still have new async threads dispatched
        threads = list(self.__async_threads)
        total = len(threads)

        # Stop all threads in the thread pool by put()ting one non-tuple per thread
        for i in range(total):
            self.__async_queue.put(None)

        for i, thr in enumerate(threads):
            self.logger.debug('Waiting for async thread {0}/{1} to end'.format(i + 1, total))
            thr.join()
            self.__async_threads.remove(thr)
            self.logger.debug('async thread {0}/{1} has ended'.format(i + 1, total))

    @property
    def has_running_threads(self):
        return self.running or bool(self.__async_threads)

    def process_update(self, update):
        """
        Processes a single update.

        Args:
            update (object):
        """

        # An error happened while polling
        if isinstance(update, TelegramError):
            self.dispatch_error(None, update)

        else:
            for group in self.groups:
                for handler in self.handlers[group]:
                    try:
                        if handler.check_update(update):
                            handler.handle_update(update, self)
                            break
                    # Dispatch any errors
                    except TelegramError as te:
                        self.logger.warn('A TelegramError was raised while processing the '
                                         'Update.')

                        try:
                            self.dispatch_error(update, te)
                        except Exception:
                            self.logger.exception('An uncaught error was raised while '
                                                  'handling the error')
                        finally:
                            break

                    # Errors should not stop the thread
                    except Exception:
                        self.logger.exception('An uncaught error was raised while '
                                              'processing the update')
                        break

    def add_handler(self, handler, group=DEFAULT_GROUP):
        """
        Register a handler.

        TL;DR: Order and priority counts. 0 or 1 handlers per group will be
        used.

        A handler must be an instance of a subclass of
        telegram.ext.Handler. All handlers are organized in groups with a
        numeric value. The default group is 0. All groups will be evaluated for
        handling an update, but only 0 or 1 handler per group will be used.

        The priority/order of handlers is determined as follows:

          * Priority of the group (lower group number == higher priority)

          * The first handler in a group which should handle an update will be
            used. Other handlers from the group will not be used. The order in
            which handlers were added to the group defines the priority.

        Args:
            handler (telegram.ext.Handler): A Handler instance
            group (Optional[int]): The group identifier. Default is 0
        """

        if not isinstance(handler, Handler):
            raise TypeError('handler is not an instance of {0}'.format(Handler.__name__))
        if not isinstance(group, int):
            raise TypeError('group is not int')

        if group not in self.handlers:
            self.handlers[group] = list()
            self.groups.append(group)
            self.groups = sorted(self.groups)

        self.handlers[group].append(handler)

    def remove_handler(self, handler, group=DEFAULT_GROUP):
        """
        Remove a handler from the specified group

        Args:
            handler (telegram.ext.Handler): A Handler instance
            group (optional[object]): The group identifier. Default is 0
        """
        if handler in self.handlers[group]:
            self.handlers[group].remove(handler)
            if not self.handlers[group]:
                del self.handlers[group]
                self.groups.remove(group)

    def add_error_handler(self, callback):
        """
        Registers an error handler in the Dispatcher.

        Args:
            handler (function): A function that takes ``Bot, Update,
                TelegramError`` as arguments.
        """

        self.error_handlers.append(callback)

    def remove_error_handler(self, callback):
        """
        De-registers an error handler.

        Args:
            handler (function):
        """

        if callback in self.error_handlers:
            self.error_handlers.remove(callback)

    def dispatch_error(self, update, error):
        """
        Dispatches an error.

        Args:
            update (object): The update that caused the error
            error (telegram.TelegramError): The Telegram error that was raised.
        """

        for callback in self.error_handlers:
            callback(self.bot, update, error)

    # old non-PEP8 Dispatcher methods
    m = "telegram.dispatcher."
    addHandler = deprecate(add_handler, m + "AddHandler", m + "add_handler")
    removeHandler = deprecate(remove_handler, m + "removeHandler", m + "remove_handler")
    addErrorHandler = deprecate(add_error_handler, m + "addErrorHandler", m + "add_error_handler")
    removeErrorHandler = deprecate(remove_error_handler, m + "removeErrorHandler",
                                   m + "remove_error_handler")
Пример #21
0
class Handler(object):
    """
    The base class for all update handlers. You can create your own handlers
    by inheriting from this class.

    Args:
        callback (function): A function that takes ``bot, update`` as
            positional arguments. It will be called when the ``check_update``
            has determined that an update should be processed by this handler.
        pass_update_queue (optional[bool]): 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 ``Updater`` and ``Dispatcher`` that contains new updates which can
             be used to insert updates. Default is ``False``.
        pass_job_queue (optional[bool]): If set to ``True``, a keyword argument called
            ``job_queue`` will be passed to the callback function. It will be a ``JobQueue``
            instance created by the ``Updater`` which can be used to schedule new jobs.
            Default is ``False``.
    """
    def __init__(self,
                 callback,
                 pass_update_queue=False,
                 pass_job_queue=False):
        self.callback = callback
        self.pass_update_queue = pass_update_queue
        self.pass_job_queue = pass_job_queue

    def check_update(self, update):
        """
        This method is called to determine if an update should be handled by
        this handler instance. It should always be overridden.

        Args:
            update (object): The update to be tested

        Returns:
            bool
        """
        raise NotImplementedError

    def handle_update(self, update, dispatcher):
        """
        This method is called if it was determined that an update should indeed
        be handled by this instance. It should also be overridden, but in most
        cases call ``self.callback(dispatcher.bot, update)``, possibly along with
        optional arguments. To work with the ``ConversationHandler``, this method should return the
        value returned from ``self.callback``

        Args:
            update (object): The update to be handled
            dispatcher (Dispatcher): The dispatcher to collect optional args

        """
        raise NotImplementedError

    def collect_optional_args(self, dispatcher):
        """
        Prepares the optional arguments that are the same for all types of
        handlers

        Args:
            dispatcher (Dispatcher):
        """
        optional_args = dict()
        if self.pass_update_queue:
            optional_args['update_queue'] = dispatcher.update_queue
        if self.pass_job_queue:
            optional_args['job_queue'] = dispatcher.job_queue

        return optional_args

    # old non-PEP8 Handler methods
    m = "telegram.Handler."
    checkUpdate = deprecate(check_update, m + "checkUpdate",
                            m + "check_update")
    handleUpdate = deprecate(handle_update, m + "handleUpdate",
                             m + "handle_update")
    collectOptionalArgs = deprecate(collect_optional_args,
                                    m + "collectOptionalArgs",
                                    m + "collect_optional_args")