mirror of
https://github.com/python-telegram-bot/python-telegram-bot.git
synced 2025-02-16 18:31:45 +01:00
initial commit for dispatcher rework. deleted updatequeue.py as it is not needed. added handler base class, messagehandler, commandhandler, regexhandler. adjusted dispatcher for new system
This commit is contained in:
parent
8b95f9cbeb
commit
1e19084a0d
9 changed files with 329 additions and 573 deletions
|
@ -22,5 +22,10 @@
|
|||
from .dispatcher import Dispatcher
|
||||
from .jobqueue import JobQueue
|
||||
from .updater import Updater
|
||||
from .handler import Handler
|
||||
from .commandhandler import CommandHandler
|
||||
from .messagehandler import MessageHandler
|
||||
from .regexhandler import RegexHandler
|
||||
|
||||
__all__ = ('Dispatcher', 'JobQueue', 'Updater')
|
||||
__all__ = ('Dispatcher', 'JobQueue', 'Updater', 'Handler', 'CommandHandler',
|
||||
'MessageHandler', 'RegexHandler')
|
||||
|
|
49
telegram/ext/commandhandler.py
Normal file
49
telegram/ext/commandhandler.py
Normal file
|
@ -0,0 +1,49 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
|
||||
""" This module contains the base class for handlers as used by the
|
||||
Dispatcher """
|
||||
|
||||
from .handler import Handler
|
||||
from telegram import Update
|
||||
|
||||
|
||||
class CommandHandler(Handler):
|
||||
|
||||
def __init__(self, command, callback, pass_args=False,
|
||||
pass_update_queue=False):
|
||||
super(Handler).__init__(callback, pass_update_queue)
|
||||
self.command = command
|
||||
self.pass_args = pass_args
|
||||
|
||||
def checkUpdate(self, update):
|
||||
return (isinstance(update, Update) and
|
||||
update.message and
|
||||
update.message.text and
|
||||
update.message.text.startswith('/') and
|
||||
update.message.text[1:].split(' ')[0].split('@')[0] ==
|
||||
self.command)
|
||||
|
||||
def handleUpdate(self, update, dispatcher):
|
||||
optional_args = self.collectOptionalArgs(dispatcher)
|
||||
|
||||
if self.pass_args:
|
||||
optional_args['args'] = update.message.text.split(' ')[1:]
|
||||
|
||||
self.callback(dispatcher.bot, update, **optional_args)
|
|
@ -21,12 +21,11 @@
|
|||
|
||||
import logging
|
||||
from functools import wraps
|
||||
from inspect import getargspec
|
||||
from threading import Thread, BoundedSemaphore, Lock, Event, current_thread
|
||||
from re import match, split
|
||||
from time import sleep
|
||||
|
||||
from telegram import (TelegramError, Update, NullHandler)
|
||||
from telegram import (TelegramError, NullHandler)
|
||||
from telegram.ext.handler import Handler
|
||||
from telegram.utils.updatequeue import Empty
|
||||
|
||||
logging.getLogger(__name__).addHandler(NullHandler())
|
||||
|
@ -35,13 +34,12 @@ semaphore = None
|
|||
async_threads = set()
|
||||
""":type: set[Thread]"""
|
||||
async_lock = Lock()
|
||||
DEFAULT_GROUP = 0
|
||||
|
||||
|
||||
def run_async(func):
|
||||
"""
|
||||
Function decorator that will run the function in a new thread. A function
|
||||
decorated with this will have to include **kwargs in their parameter list,
|
||||
which will contain all optional parameters.
|
||||
Function decorator that will run the function in a new thread.
|
||||
|
||||
Args:
|
||||
func (function): The function to run in the thread.
|
||||
|
@ -82,52 +80,6 @@ def run_async(func):
|
|||
class Dispatcher:
|
||||
"""
|
||||
This class dispatches all kinds of updates to its registered handlers.
|
||||
A handler is a function that usually takes the following parameters
|
||||
|
||||
bot:
|
||||
The telegram.Bot instance that received the message
|
||||
update:
|
||||
The update that should be handled by the handler
|
||||
|
||||
Error handlers take an additional parameter
|
||||
|
||||
error:
|
||||
The TelegramError instance that was raised during processing the
|
||||
update
|
||||
|
||||
All handlers, except error handlers, can also request more information by
|
||||
appending one or more of the following arguments in their argument list for
|
||||
convenience
|
||||
|
||||
update_queue:
|
||||
The Queue instance which contains all new updates and is
|
||||
processed by the Dispatcher. Be careful with this - you might
|
||||
create an infinite loop.
|
||||
args:
|
||||
If the update is an instance str or telegram.Update, this will be
|
||||
a list that contains the content of the message split on spaces,
|
||||
except the first word (usually the command).
|
||||
Example: '/add item1 item2 item3' -> ['item1', 'item2', 'item3']
|
||||
For updates that contain inline queries, they will contain the
|
||||
whole query split on spaces.
|
||||
For other updates, args will be None
|
||||
|
||||
In some cases handlers may need some context data to process the update. To
|
||||
procedure just queue in update_queue.put(update, context=context) or
|
||||
processUpdate(update,context=context).
|
||||
|
||||
context:
|
||||
Extra data for handling updates.
|
||||
|
||||
For regex-based handlers, you can also request information about the match.
|
||||
For all other handlers, these will be None
|
||||
|
||||
groups:
|
||||
A tuple that contains the result of
|
||||
re.match(matcher, ...).groups()
|
||||
groupdict:
|
||||
A dictionary that contains the result of
|
||||
re.match(matcher, ...).groupdict()
|
||||
|
||||
Args:
|
||||
bot (telegram.Bot): The bot object that should be passed to the
|
||||
|
@ -138,16 +90,10 @@ class Dispatcher:
|
|||
def __init__(self, bot, update_queue, workers=4, exception_event=None):
|
||||
self.bot = bot
|
||||
self.update_queue = update_queue
|
||||
self.telegram_message_handlers = []
|
||||
self.telegram_inline_handlers = []
|
||||
self.telegram_command_handlers = {}
|
||||
self.telegram_regex_handlers = {}
|
||||
self.string_regex_handlers = {}
|
||||
self.string_command_handlers = {}
|
||||
self.type_handlers = {}
|
||||
self.unknown_telegram_command_handlers = []
|
||||
self.unknown_string_command_handlers = []
|
||||
|
||||
self.handlers = {}
|
||||
self.error_handlers = []
|
||||
|
||||
self.logger = logging.getLogger(__name__)
|
||||
self.running = False
|
||||
self.__stop_event = Event()
|
||||
|
@ -177,10 +123,10 @@ class Dispatcher:
|
|||
self.running = True
|
||||
self.logger.debug('Dispatcher started')
|
||||
|
||||
while 1:
|
||||
while True:
|
||||
try:
|
||||
# Pop update from update queue.
|
||||
update, context = self.update_queue.get(True, 1, True)
|
||||
update = self.update_queue.get(True, 1)
|
||||
except Empty:
|
||||
if self.__stop_event.is_set():
|
||||
self.logger.debug('orderly stopping')
|
||||
|
@ -191,26 +137,8 @@ class Dispatcher:
|
|||
break
|
||||
continue
|
||||
|
||||
try:
|
||||
self.processUpdate(update, context)
|
||||
self.logger.debug('Processed Update: %s with context %s'
|
||||
% (update, context))
|
||||
|
||||
# Dispatch any errors
|
||||
except TelegramError as te:
|
||||
self.logger.warn("Error was raised while processing Update.")
|
||||
|
||||
try:
|
||||
self.dispatchError(update, te)
|
||||
# Log errors in error handlers
|
||||
except:
|
||||
self.logger.exception("An uncaught error was raised while "
|
||||
"handling the error")
|
||||
|
||||
# All other errors should not stop the thread, just print them
|
||||
except:
|
||||
self.logger.exception("An uncaught error was raised while "
|
||||
"processing an update")
|
||||
self.logger.debug('Processing Update: %s' % update)
|
||||
self.processUpdate(update)
|
||||
|
||||
self.running = False
|
||||
self.logger.debug('Dispatcher thread stopped')
|
||||
|
@ -225,7 +153,7 @@ class Dispatcher:
|
|||
sleep(0.1)
|
||||
self.__stop_event.clear()
|
||||
|
||||
def processUpdate(self, update, context=None):
|
||||
def processUpdate(self, update):
|
||||
"""
|
||||
Processes a single update.
|
||||
|
||||
|
@ -233,164 +161,77 @@ class Dispatcher:
|
|||
update (any):
|
||||
"""
|
||||
|
||||
handled = False
|
||||
|
||||
# Custom type handlers
|
||||
for t in self.type_handlers:
|
||||
if isinstance(update, t):
|
||||
self.dispatchType(update, context)
|
||||
handled = True
|
||||
|
||||
# string update
|
||||
if type(update) is str and update.startswith('/'):
|
||||
self.dispatchStringCommand(update, context)
|
||||
handled = True
|
||||
elif type(update) is str:
|
||||
self.dispatchRegex(update, context)
|
||||
handled = True
|
||||
|
||||
# An error happened while polling
|
||||
if isinstance(update, TelegramError):
|
||||
self.dispatchError(None, update)
|
||||
handled = True
|
||||
|
||||
# Telegram update (regex)
|
||||
if isinstance(update, Update) and update.message is not None:
|
||||
self.dispatchRegex(update, context)
|
||||
handled = True
|
||||
else:
|
||||
for group in self.handlers.values():
|
||||
handler_triggered = False
|
||||
|
||||
# Telegram update (command)
|
||||
if update.message.text.startswith('/'):
|
||||
self.dispatchTelegramCommand(update, context)
|
||||
for handler in group:
|
||||
try:
|
||||
if handler.checkUpdate(update):
|
||||
handler_triggered = True
|
||||
handler.handleUpdate(update, self)
|
||||
# Dispatch any errors
|
||||
except TelegramError as te:
|
||||
self.logger.warn(
|
||||
'Error was raised while processing Update.')
|
||||
|
||||
# Telegram update (message)
|
||||
else:
|
||||
self.dispatchTelegramMessage(update, context)
|
||||
handled = True
|
||||
elif isinstance(update, Update) and \
|
||||
(update.inline_query is not None or
|
||||
update.chosen_inline_result is not None):
|
||||
self.dispatchTelegramInline(update, context)
|
||||
handled = True
|
||||
# Update not recognized
|
||||
if not handled:
|
||||
self.dispatchError(update, TelegramError(
|
||||
"Received update of unknown type %s" % type(update)))
|
||||
try:
|
||||
self.dispatchError(update, te)
|
||||
except:
|
||||
self.logger.exception(
|
||||
'An uncaught error was raised while '
|
||||
'handling the error')
|
||||
|
||||
# Add Handlers
|
||||
def addTelegramMessageHandler(self, handler):
|
||||
# Errors should not stop the thread
|
||||
except:
|
||||
self.logger.exception(
|
||||
'An uncaught error was raised while '
|
||||
'processing the update')
|
||||
|
||||
finally:
|
||||
if handler_triggered:
|
||||
break
|
||||
|
||||
def addHandler(self, handler, group=DEFAULT_GROUP):
|
||||
"""
|
||||
Registers a message handler in the Dispatcher.
|
||||
Register a handler. A handler must be an instance of a subclass of
|
||||
telegram.ext.Handler. All handlers are organized in groups, the default
|
||||
group is int(0), but any object can identify a group. Every update will
|
||||
be tested against each handler in each group from first-added to last-
|
||||
added. If the update has been handled in one group, it will not be
|
||||
tested against other handlers in that group. That means an update can
|
||||
only be handled 0 or 1 times per group, but multiple times across all
|
||||
groups.
|
||||
|
||||
Args:
|
||||
handler (function): A function that takes (Bot, Update, *args) as
|
||||
arguments.
|
||||
handler (Handler): A Handler instance
|
||||
group (object): The group identifier
|
||||
"""
|
||||
|
||||
self.telegram_message_handlers.append(handler)
|
||||
if not isinstance(handler, Handler):
|
||||
raise TypeError('Handler is no instance of telegram.ext.Handler')
|
||||
|
||||
def addTelegramInlineHandler(self, handler):
|
||||
if group not in self.handlers:
|
||||
self.handlers[group] = list()
|
||||
|
||||
self.handlers[group].append(handler)
|
||||
|
||||
def removeHandler(self, handler, group=DEFAULT_GROUP):
|
||||
"""
|
||||
Registers an inline query handler in the Dispatcher.
|
||||
Remove a handler from the specified group
|
||||
|
||||
Args:
|
||||
handler (function): A function that takes (Bot, Update, *args) as
|
||||
arguments.
|
||||
handler (Handler): A Handler instance
|
||||
group (object): The group identifier
|
||||
"""
|
||||
if handler in self.handlers[group]:
|
||||
self.handlers[group].remove(handler)
|
||||
|
||||
self.telegram_inline_handlers.append(handler)
|
||||
|
||||
def addTelegramCommandHandler(self, command, handler):
|
||||
"""
|
||||
Registers a command handler in the Dispatcher.
|
||||
|
||||
Args:
|
||||
command (str): The command keyword that this handler should be
|
||||
listening to.
|
||||
handler (function): A function that takes (Bot, Update, *args) as
|
||||
arguments.
|
||||
"""
|
||||
|
||||
if command not in self.telegram_command_handlers:
|
||||
self.telegram_command_handlers[command] = []
|
||||
|
||||
self.telegram_command_handlers[command].append(handler)
|
||||
|
||||
def addTelegramRegexHandler(self, matcher, handler):
|
||||
"""
|
||||
Registers a regex handler in the Dispatcher. If handlers will be
|
||||
called if re.match(matcher, update.message.text) is True.
|
||||
|
||||
Args:
|
||||
matcher (str/__Regex): A regex string or compiled regex object that
|
||||
matches on messages that handler should be listening to
|
||||
handler (function): A function that takes (Bot, Update, *args) as
|
||||
arguments.
|
||||
"""
|
||||
|
||||
if matcher not in self.telegram_regex_handlers:
|
||||
self.telegram_regex_handlers[matcher] = []
|
||||
|
||||
self.telegram_regex_handlers[matcher].append(handler)
|
||||
|
||||
def addStringCommandHandler(self, command, handler):
|
||||
"""
|
||||
Registers a string-command handler in the Dispatcher.
|
||||
|
||||
Args:
|
||||
command (str): The command keyword that this handler should be
|
||||
listening to.
|
||||
handler (function): A function that takes (Bot, str, *args) as
|
||||
arguments.
|
||||
"""
|
||||
|
||||
if command not in self.string_command_handlers:
|
||||
self.string_command_handlers[command] = []
|
||||
|
||||
self.string_command_handlers[command].append(handler)
|
||||
|
||||
def addStringRegexHandler(self, matcher, handler):
|
||||
"""
|
||||
Registers a regex handler in the Dispatcher. If handlers will be
|
||||
called if re.match(matcher, string) is True.
|
||||
|
||||
Args:
|
||||
matcher (str/__Regex): A regex string or compiled regex object that
|
||||
matches on the string input that handler should be listening to
|
||||
handler (function): A function that takes (Bot, Update, *args) as
|
||||
arguments.
|
||||
"""
|
||||
|
||||
if matcher not in self.string_regex_handlers:
|
||||
self.string_regex_handlers[matcher] = []
|
||||
|
||||
self.string_regex_handlers[matcher].append(handler)
|
||||
|
||||
def addUnknownTelegramCommandHandler(self, handler):
|
||||
"""
|
||||
Registers a command handler in the Dispatcher, that will receive all
|
||||
commands that have no associated handler.
|
||||
|
||||
Args:
|
||||
handler (function): A function that takes (Bot, Update, *args) as
|
||||
arguments.
|
||||
"""
|
||||
|
||||
self.unknown_telegram_command_handlers.append(handler)
|
||||
|
||||
def addUnknownStringCommandHandler(self, handler):
|
||||
"""
|
||||
Registers a string-command handler in the Dispatcher, that will
|
||||
receive all commands that have no associated handler.
|
||||
|
||||
Args:
|
||||
handler (function): A function that takes (Bot, str, *args) as
|
||||
arguments.
|
||||
"""
|
||||
|
||||
self.unknown_string_command_handlers.append(handler)
|
||||
|
||||
def addErrorHandler(self, handler):
|
||||
def addErrorHandler(self, callback):
|
||||
"""
|
||||
Registers an error handler in the Dispatcher.
|
||||
|
||||
|
@ -399,122 +240,9 @@ class Dispatcher:
|
|||
arguments.
|
||||
"""
|
||||
|
||||
self.error_handlers.append(handler)
|
||||
self.error_handlers.append(callback)
|
||||
|
||||
def addTypeHandler(self, the_type, handler):
|
||||
"""
|
||||
Registers a type handler in the Dispatcher. This allows you to send
|
||||
any type of object into the update queue.
|
||||
|
||||
Args:
|
||||
the_type (type): The type this handler should listen to
|
||||
handler (function): A function that takes (Bot, type, *args) as
|
||||
arguments.
|
||||
"""
|
||||
|
||||
if the_type not in self.type_handlers:
|
||||
self.type_handlers[the_type] = []
|
||||
|
||||
self.type_handlers[the_type].append(handler)
|
||||
|
||||
# Remove Handlers
|
||||
def removeTelegramMessageHandler(self, handler):
|
||||
"""
|
||||
De-registers a message handler.
|
||||
|
||||
Args:
|
||||
handler (any):
|
||||
"""
|
||||
|
||||
if handler in self.telegram_message_handlers:
|
||||
self.telegram_message_handlers.remove(handler)
|
||||
|
||||
def removeTelegramInlineHandler(self, handler):
|
||||
"""
|
||||
De-registers an inline query handler.
|
||||
|
||||
Args:
|
||||
handler (any):
|
||||
"""
|
||||
|
||||
if handler in self.telegram_inline_handlers:
|
||||
self.telegram_inline_handlers.remove(handler)
|
||||
|
||||
def removeTelegramCommandHandler(self, command, handler):
|
||||
"""
|
||||
De-registers a command handler.
|
||||
|
||||
Args:
|
||||
command (str): The command
|
||||
handler (any):
|
||||
"""
|
||||
|
||||
if command in self.telegram_command_handlers \
|
||||
and handler in self.telegram_command_handlers[command]:
|
||||
self.telegram_command_handlers[command].remove(handler)
|
||||
|
||||
def removeTelegramRegexHandler(self, matcher, handler):
|
||||
"""
|
||||
De-registers a regex handler.
|
||||
|
||||
Args:
|
||||
matcher (str/__Regex): The regex matcher object or string
|
||||
handler (any):
|
||||
"""
|
||||
|
||||
if matcher in self.telegram_regex_handlers \
|
||||
and handler in self.telegram_regex_handlers[matcher]:
|
||||
self.telegram_regex_handlers[matcher].remove(handler)
|
||||
|
||||
def removeStringCommandHandler(self, command, handler):
|
||||
"""
|
||||
De-registers a string-command handler.
|
||||
|
||||
Args:
|
||||
command (str): The command
|
||||
handler (any):
|
||||
"""
|
||||
|
||||
if command in self.string_command_handlers \
|
||||
and handler in self.string_command_handlers[command]:
|
||||
self.string_command_handlers[command].remove(handler)
|
||||
|
||||
def removeStringRegexHandler(self, matcher, handler):
|
||||
"""
|
||||
De-registers a regex handler.
|
||||
|
||||
Args:
|
||||
matcher (str/__Regex): The regex matcher object or string
|
||||
handler (any):
|
||||
"""
|
||||
|
||||
if matcher in self.string_regex_handlers \
|
||||
and handler in self.string_regex_handlers[matcher]:
|
||||
self.string_regex_handlers[matcher].remove(handler)
|
||||
|
||||
def removeUnknownTelegramCommandHandler(self, handler):
|
||||
"""
|
||||
De-registers an unknown-command handler.
|
||||
|
||||
Args:
|
||||
handler (any):
|
||||
"""
|
||||
|
||||
if handler in self.unknown_telegram_command_handlers:
|
||||
self.unknown_telegram_command_handlers.remove(handler)
|
||||
|
||||
def removeUnknownStringCommandHandler(self, handler):
|
||||
"""
|
||||
De-registers an unknown-command handler.
|
||||
|
||||
Args:
|
||||
handler (any):
|
||||
"""
|
||||
|
||||
if handler in self.unknown_string_command_handlers:
|
||||
self.unknown_string_command_handlers.remove(handler)
|
||||
|
||||
def removeErrorHandler(self, handler):
|
||||
def removeErrorHandler(self, callback):
|
||||
"""
|
||||
De-registers an error handler.
|
||||
|
||||
|
@ -522,117 +250,8 @@ class Dispatcher:
|
|||
handler (any):
|
||||
"""
|
||||
|
||||
if handler in self.error_handlers:
|
||||
self.error_handlers.remove(handler)
|
||||
|
||||
def removeTypeHandler(self, the_type, handler):
|
||||
"""
|
||||
De-registers a type handler.
|
||||
|
||||
Args:
|
||||
handler (any):
|
||||
"""
|
||||
|
||||
if the_type in self.type_handlers \
|
||||
and handler in self.type_handlers[the_type]:
|
||||
self.type_handlers[the_type].remove(handler)
|
||||
|
||||
def dispatchTelegramCommand(self, update, context=None):
|
||||
"""
|
||||
Dispatches an update that contains a command.
|
||||
|
||||
Args:
|
||||
command (str): The command keyword
|
||||
update (telegram.Update): The Telegram update that contains the
|
||||
command
|
||||
"""
|
||||
|
||||
command = split('\W', update.message.text[1:])[0]
|
||||
|
||||
if command in self.telegram_command_handlers:
|
||||
self.dispatchTo(self.telegram_command_handlers[command], update,
|
||||
context=context)
|
||||
else:
|
||||
self.dispatchTo(self.unknown_telegram_command_handlers, update,
|
||||
context=context)
|
||||
|
||||
def dispatchRegex(self, update, context=None):
|
||||
"""
|
||||
Dispatches an update to all string or telegram regex handlers that
|
||||
match the string/message content.
|
||||
|
||||
Args:
|
||||
update (str, Update): The update that should be checked for matches
|
||||
"""
|
||||
|
||||
if isinstance(update, Update):
|
||||
handlers = self.telegram_regex_handlers
|
||||
to_match = update.message.text
|
||||
elif isinstance(update, str):
|
||||
handlers = self.string_regex_handlers
|
||||
to_match = update
|
||||
|
||||
for matcher in handlers:
|
||||
m = match(matcher, to_match)
|
||||
if m:
|
||||
for handler in handlers[matcher]:
|
||||
self.call_handler(handler,
|
||||
update,
|
||||
groups=m.groups(),
|
||||
groupdict=m.groupdict(),
|
||||
context=context)
|
||||
|
||||
def dispatchStringCommand(self, update, context=None):
|
||||
"""
|
||||
Dispatches a string-update that contains a command.
|
||||
|
||||
Args:
|
||||
update (str): The string input
|
||||
"""
|
||||
|
||||
command = update.split(' ')[0][1:]
|
||||
|
||||
if command in self.string_command_handlers:
|
||||
self.dispatchTo(self.string_command_handlers[command], update,
|
||||
context=context)
|
||||
else:
|
||||
self.dispatchTo(self.unknown_string_command_handlers, update,
|
||||
context=context)
|
||||
|
||||
def dispatchType(self, update, context=None):
|
||||
"""
|
||||
Dispatches an update of any type.
|
||||
|
||||
Args:
|
||||
update (any): The update
|
||||
"""
|
||||
|
||||
for t in self.type_handlers:
|
||||
if isinstance(update, t):
|
||||
self.dispatchTo(self.type_handlers[t], update, context=context)
|
||||
|
||||
def dispatchTelegramMessage(self, update, context=None):
|
||||
"""
|
||||
Dispatches an update that contains a regular message.
|
||||
|
||||
Args:
|
||||
update (telegram.Update): The Telegram update that contains the
|
||||
message.
|
||||
"""
|
||||
|
||||
self.dispatchTo(self.telegram_message_handlers, update,
|
||||
context=context)
|
||||
|
||||
def dispatchTelegramInline(self, update, context=None):
|
||||
"""
|
||||
Dispatches an update that contains an inline update.
|
||||
|
||||
Args:
|
||||
update (telegram.Update): The Telegram update that contains the
|
||||
message.
|
||||
"""
|
||||
|
||||
self.dispatchTo(self.telegram_inline_handlers, update, context=None)
|
||||
if callback in self.error_handlers:
|
||||
self.error_handlers.remove(callback)
|
||||
|
||||
def dispatchError(self, update, error):
|
||||
"""
|
||||
|
@ -643,63 +262,5 @@ class Dispatcher:
|
|||
error (telegram.TelegramError): The Telegram error that was raised.
|
||||
"""
|
||||
|
||||
for handler in self.error_handlers:
|
||||
handler(self.bot, update, error)
|
||||
|
||||
def dispatchTo(self, handlers, update, **kwargs):
|
||||
"""
|
||||
Dispatches an update to a list of handlers.
|
||||
|
||||
Args:
|
||||
handlers (list): A list of handler-functions.
|
||||
update (any): The update to be dispatched
|
||||
"""
|
||||
|
||||
for handler in handlers:
|
||||
self.call_handler(handler, update, **kwargs)
|
||||
|
||||
def call_handler(self, handler, update, **kwargs):
|
||||
"""
|
||||
Calls an update handler. Checks the handler for keyword arguments and
|
||||
fills them, if possible.
|
||||
|
||||
Args:
|
||||
handler (function): An update handler function
|
||||
update (any): An update
|
||||
"""
|
||||
|
||||
target_kwargs = {}
|
||||
fargs = getargspec(handler).args
|
||||
|
||||
'''
|
||||
async handlers will receive all optional arguments, since we can't
|
||||
their argument list.
|
||||
'''
|
||||
|
||||
is_async = 'pargs' == getargspec(handler).varargs
|
||||
|
||||
if is_async or 'update_queue' in fargs:
|
||||
target_kwargs['update_queue'] = self.update_queue
|
||||
|
||||
if is_async or 'args' in fargs:
|
||||
if isinstance(update, Update) and update.message:
|
||||
args = update.message.text.split(' ')[1:]
|
||||
elif isinstance(update, Update) and update.inline_query:
|
||||
args = update.inline_query.query.split(' ')
|
||||
elif isinstance(update, str):
|
||||
args = update.split(' ')[1:]
|
||||
else:
|
||||
args = None
|
||||
|
||||
target_kwargs['args'] = args
|
||||
|
||||
if is_async or 'groups' in fargs:
|
||||
target_kwargs['groups'] = kwargs.get('groups', None)
|
||||
|
||||
if is_async or 'groupdict' in fargs:
|
||||
target_kwargs['groupdict'] = kwargs.get('groupdict', None)
|
||||
|
||||
if is_async or 'context' in fargs:
|
||||
target_kwargs['context'] = kwargs.get('context', None)
|
||||
|
||||
handler(self.bot, update, **target_kwargs)
|
||||
for callback in self.error_handlers:
|
||||
callback(self.bot, update, error)
|
||||
|
|
2
telegram/ext/filters.py
Normal file
2
telegram/ext/filters.py
Normal file
|
@ -0,0 +1,2 @@
|
|||
TEXT, AUDIO, DOCUMENT, PHOTO, STICKER, VIDEO, VOICE, CONTACT, LOCATION, \
|
||||
VENUE, STATUS_UPDATE = range(11)
|
65
telegram/ext/handler.py
Normal file
65
telegram/ext/handler.py
Normal file
|
@ -0,0 +1,65 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
|
||||
""" This module contains the base class for handlers as used by the
|
||||
Dispatcher """
|
||||
|
||||
|
||||
class Handler(object):
|
||||
|
||||
def __init__(self, callback, pass_update_queue=False):
|
||||
self.callback = callback
|
||||
self.pass_update_queue = pass_update_queue
|
||||
|
||||
def checkUpdate(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
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def handleUpdate(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 collectOptionalArgs(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
|
68
telegram/ext/messagehandler.py
Normal file
68
telegram/ext/messagehandler.py
Normal file
|
@ -0,0 +1,68 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
|
||||
""" This module contains the base class for handlers as used by the
|
||||
Dispatcher """
|
||||
|
||||
from .handler import Handler
|
||||
from telegram import Update
|
||||
|
||||
from .filters import *
|
||||
|
||||
class MessageHandler(Handler):
|
||||
|
||||
def __init__(self, filters, callback, pass_update_queue=False):
|
||||
super(Handler).__init__(callback, pass_update_queue)
|
||||
self.filters = filters
|
||||
|
||||
def checkUpdate(self, update):
|
||||
filters = self.filters
|
||||
if isinstance(update, Update) and update.message:
|
||||
message = update.message
|
||||
return (TEXT in filters and message.text and
|
||||
not message.text.startswith('/') or
|
||||
AUDIO in filters and message.audio or
|
||||
DOCUMENT in filters and message.document or
|
||||
PHOTO in filters and message.photo or
|
||||
STICKER in filters and message.sticker or
|
||||
VIDEO in filters and message.video or
|
||||
VOICE in filters and message.voice or
|
||||
CONTACT in filters and message.contact or
|
||||
LOCATION in filters and message.location or
|
||||
VENUE in filters and message.venue or
|
||||
STATUS_UPDATE in filters and (
|
||||
message.new_chat_member or
|
||||
message.left_chat_member or
|
||||
message.new_chat_title or
|
||||
message.new_chat_photo or
|
||||
message.delete_chat_photo or
|
||||
message.group_chat_created or
|
||||
message.supergroup_chat_created or
|
||||
message.channel_chat_created or
|
||||
message.migrate_to_chat_id or
|
||||
message.migrate_from_chat_id or
|
||||
message.pinned_message)
|
||||
)
|
||||
else:
|
||||
return False
|
||||
|
||||
def handleUpdate(self, update, dispatcher):
|
||||
optional_args = self.collectOptionalArgs(dispatcher)
|
||||
|
||||
self.callback(dispatcher.bot, update, **optional_args)
|
60
telegram/ext/regexhandler.py
Normal file
60
telegram/ext/regexhandler.py
Normal file
|
@ -0,0 +1,60 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
|
||||
""" This module contains the base class for handlers as used by the
|
||||
Dispatcher """
|
||||
|
||||
import re
|
||||
|
||||
from .handler import Handler
|
||||
from telegram import Update
|
||||
|
||||
|
||||
class RegexHandler(Handler):
|
||||
|
||||
def __init__(self, pattern, callback, pass_groups=False,
|
||||
pass_groupdict=False, pass_update_queue=False):
|
||||
super(Handler).__init__(callback, pass_update_queue)
|
||||
|
||||
if isinstance(pattern, str):
|
||||
pattern = re.compile(pattern)
|
||||
|
||||
self.pattern = pattern
|
||||
self.pass_groups = pass_groups
|
||||
self.pass_groupdict = pass_groupdict
|
||||
|
||||
def checkUpdate(self, update):
|
||||
if (isinstance(update, Update) and
|
||||
update.message and
|
||||
update.message.text):
|
||||
match = re.match(self.pattern, update.message.text)
|
||||
return bool(match)
|
||||
else:
|
||||
return False
|
||||
|
||||
def handleUpdate(self, update, dispatcher):
|
||||
optional_args = self.collectOptionalArgs(dispatcher)
|
||||
match = re.match(self.pattern, update.message.text)
|
||||
|
||||
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)
|
|
@ -28,10 +28,16 @@ from threading import Thread, Lock, current_thread, Event
|
|||
from time import sleep
|
||||
import subprocess
|
||||
from signal import signal, SIGINT, SIGTERM, SIGABRT
|
||||
|
||||
# Adjust for differences in Python versions
|
||||
try:
|
||||
from queue import Queue # flake8: noqa
|
||||
except ImportError:
|
||||
from Queue import Queue # flake8: noqa
|
||||
|
||||
from telegram import Bot, TelegramError, NullHandler
|
||||
from telegram.ext import dispatcher, Dispatcher, JobQueue
|
||||
from telegram.error import Unauthorized, InvalidToken
|
||||
from telegram.utils.updatequeue import UpdateQueue
|
||||
from telegram.utils.webhookhandler import (WebhookServer, WebhookHandler)
|
||||
|
||||
logging.getLogger(__name__).addHandler(NullHandler())
|
||||
|
@ -81,7 +87,7 @@ class Updater:
|
|||
self.bot = bot
|
||||
else:
|
||||
self.bot = Bot(token, base_url)
|
||||
self.update_queue = UpdateQueue()
|
||||
self.update_queue = Queue()
|
||||
self.job_queue = JobQueue(self.bot, job_queue_tick_interval)
|
||||
self.__exception_event = Event()
|
||||
self.dispatcher = Dispatcher(self.bot, self.update_queue, workers,
|
||||
|
|
|
@ -1,60 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# A library that provides a Python interface to the Telegram Bot API
|
||||
# Copyright (C) 2015-2016
|
||||
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser Public License
|
||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||
|
||||
"""This module contains the class UpdateQueue to override standard
|
||||
Queue."""
|
||||
|
||||
|
||||
# Adjust for differences in Python versions
|
||||
try:
|
||||
# loading Empty here so it can be imported by users of updatequeue
|
||||
from queue import Queue, Empty # flake8: noqa
|
||||
except ImportError:
|
||||
from Queue import Queue, Empty # flake8: noqa
|
||||
|
||||
|
||||
class UpdateQueue(Queue):
|
||||
"""
|
||||
This class overrides standard Queues. Allows you to de/queue context
|
||||
data apart from the handled `update`
|
||||
"""
|
||||
|
||||
def put(self, item, block=True, timeout=None, context=None):
|
||||
"""
|
||||
Put an item into the queue with context data if provided as a
|
||||
tuple (item, context). Overrides standard Queue.put method.
|
||||
|
||||
Args:
|
||||
update (any): handled by the dispatcher
|
||||
context (any): extra data to use in handlers
|
||||
"""
|
||||
Queue.put(self, (item, context), block, timeout)
|
||||
|
||||
def get(self, block=True, timeout=None, context=False):
|
||||
"""
|
||||
Remove and return an item from the queue. A tuple of
|
||||
(update, context) if requested. Overrides standard Queue.get
|
||||
method.
|
||||
|
||||
Args:
|
||||
context (boolean): set true to get (update, context)
|
||||
"""
|
||||
if not context:
|
||||
return Queue.get(self, block, timeout)[0]
|
||||
return Queue.get(self, block, timeout)
|
Loading…
Add table
Reference in a new issue