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")
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")
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")
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")
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")
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")
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")
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")
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")
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")
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")
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")
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")
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")
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")
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")
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")