mirror of
https://github.com/python-telegram-bot/python-telegram-bot.git
synced 2025-03-29 09:40:31 +01:00
Add context parameter to dispatcher. Handlers now can updates with a context data if required
This commit is contained in:
parent
6166e7f07a
commit
a6650de93d
6 changed files with 124 additions and 32 deletions
|
@ -16,6 +16,7 @@ The following wonderful people contributed directly or indirectly to this projec
|
||||||
- `JASON0916 <https://github.com/JASON0916>`_
|
- `JASON0916 <https://github.com/JASON0916>`_
|
||||||
- `jh0ker <https://github.com/jh0ker>`_
|
- `jh0ker <https://github.com/jh0ker>`_
|
||||||
- `JRoot3D <https://github.com/JRoot3D>`_
|
- `JRoot3D <https://github.com/JRoot3D>`_
|
||||||
|
- `jlmadurga <https://github.com/jlmadurga>`_
|
||||||
- `macrojames <https://github.com/macrojames>`_
|
- `macrojames <https://github.com/macrojames>`_
|
||||||
- `naveenvhegde <https://github.com/naveenvhegde>`_
|
- `naveenvhegde <https://github.com/naveenvhegde>`_
|
||||||
- `njittam <https://github.com/njittam>`_
|
- `njittam <https://github.com/njittam>`_
|
||||||
|
|
|
@ -47,8 +47,10 @@ from .update import Update
|
||||||
from .bot import Bot
|
from .bot import Bot
|
||||||
from .dispatcher import Dispatcher
|
from .dispatcher import Dispatcher
|
||||||
from .jobqueue import JobQueue
|
from .jobqueue import JobQueue
|
||||||
|
from .updatequeue import UpdateQueue
|
||||||
from .updater import Updater
|
from .updater import Updater
|
||||||
|
|
||||||
|
|
||||||
__author__ = 'devs@python-telegram-bot.org'
|
__author__ = 'devs@python-telegram-bot.org'
|
||||||
__version__ = '3.2.0'
|
__version__ = '3.2.0'
|
||||||
__all__ = ('Bot', 'Updater', 'Dispatcher', 'Emoji', 'TelegramError',
|
__all__ = ('Bot', 'Updater', 'Dispatcher', 'Emoji', 'TelegramError',
|
||||||
|
@ -56,4 +58,5 @@ __all__ = ('Bot', 'Updater', 'Dispatcher', 'Emoji', 'TelegramError',
|
||||||
'ReplyKeyboardMarkup', 'UserProfilePhotos', 'ChatAction',
|
'ReplyKeyboardMarkup', 'UserProfilePhotos', 'ChatAction',
|
||||||
'Location', 'Contact', 'Video', 'Sticker', 'Document', 'File',
|
'Location', 'Contact', 'Video', 'Sticker', 'Document', 'File',
|
||||||
'Audio', 'PhotoSize', 'Chat', 'Update', 'ParseMode', 'Message',
|
'Audio', 'PhotoSize', 'Chat', 'Update', 'ParseMode', 'Message',
|
||||||
'User', 'TelegramObject', 'NullHandler', 'Voice', 'JobQueue')
|
'User', 'TelegramObject', 'NullHandler', 'Voice', 'JobQueue',
|
||||||
|
'UpdateQueue')
|
||||||
|
|
|
@ -108,6 +108,11 @@ class Dispatcher:
|
||||||
Example: '/add item1 item2 item3' -> ['item1', 'item2', 'item3']
|
Example: '/add item1 item2 item3' -> ['item1', 'item2', 'item3']
|
||||||
For other updates, args will be None
|
For other updates, args will be None
|
||||||
|
|
||||||
|
Sometimes to handle update you need some context data. You can define
|
||||||
|
handlers with context parameter. To produce context just queue in
|
||||||
|
update_queue.put(update,context=context) or
|
||||||
|
processUpdate(update,context=context).
|
||||||
|
|
||||||
For regex-based handlers, you can also request information about the match.
|
For regex-based handlers, you can also request information about the match.
|
||||||
For all other handlers, these will be None
|
For all other handlers, these will be None
|
||||||
|
|
||||||
|
@ -121,7 +126,7 @@ class Dispatcher:
|
||||||
Args:
|
Args:
|
||||||
bot (telegram.Bot): The bot object that should be passed to the
|
bot (telegram.Bot): The bot object that should be passed to the
|
||||||
handlers
|
handlers
|
||||||
update_queue (queue.Queue): The synchronized queue that will
|
update_queue (UpdateQueue): The synchronized queue that will
|
||||||
contain the updates.
|
contain the updates.
|
||||||
"""
|
"""
|
||||||
def __init__(self, bot, update_queue, workers=4):
|
def __init__(self, bot, update_queue, workers=4):
|
||||||
|
@ -171,14 +176,15 @@ class Dispatcher:
|
||||||
try:
|
try:
|
||||||
# Pop update from update queue.
|
# Pop update from update queue.
|
||||||
# Blocks if no updates are available.
|
# Blocks if no updates are available.
|
||||||
update = self.update_queue.get()
|
update, context = self.update_queue.get(context=True)
|
||||||
|
|
||||||
if type(update) is self._Stop:
|
if type(update) is self._Stop:
|
||||||
self.running = False
|
self.running = False
|
||||||
break
|
break
|
||||||
|
|
||||||
self.processUpdate(update)
|
self.processUpdate(update, context)
|
||||||
self.logger.debug('Processed Update: %s' % update)
|
self.logger.debug('Processed Update: %s with context %s'
|
||||||
|
% (update, context))
|
||||||
|
|
||||||
# Dispatch any errors
|
# Dispatch any errors
|
||||||
except TelegramError as te:
|
except TelegramError as te:
|
||||||
|
@ -204,7 +210,7 @@ class Dispatcher:
|
||||||
while self.running:
|
while self.running:
|
||||||
sleep(0.1)
|
sleep(0.1)
|
||||||
|
|
||||||
def processUpdate(self, update):
|
def processUpdate(self, update, context=None):
|
||||||
"""
|
"""
|
||||||
Processes a single update.
|
Processes a single update.
|
||||||
|
|
||||||
|
@ -217,15 +223,15 @@ class Dispatcher:
|
||||||
# Custom type handlers
|
# Custom type handlers
|
||||||
for t in self.type_handlers:
|
for t in self.type_handlers:
|
||||||
if isinstance(update, t):
|
if isinstance(update, t):
|
||||||
self.dispatchType(update)
|
self.dispatchType(update, context)
|
||||||
handled = True
|
handled = True
|
||||||
|
|
||||||
# string update
|
# string update
|
||||||
if type(update) is str and update.startswith('/'):
|
if type(update) is str and update.startswith('/'):
|
||||||
self.dispatchStringCommand(update)
|
self.dispatchStringCommand(update, context)
|
||||||
handled = True
|
handled = True
|
||||||
elif type(update) is str:
|
elif type(update) is str:
|
||||||
self.dispatchRegex(update)
|
self.dispatchRegex(update, context)
|
||||||
handled = True
|
handled = True
|
||||||
|
|
||||||
# An error happened while polling
|
# An error happened while polling
|
||||||
|
@ -235,18 +241,18 @@ class Dispatcher:
|
||||||
|
|
||||||
# Telegram update (regex)
|
# Telegram update (regex)
|
||||||
if isinstance(update, Update):
|
if isinstance(update, Update):
|
||||||
self.dispatchRegex(update)
|
self.dispatchRegex(update, context)
|
||||||
handled = True
|
handled = True
|
||||||
|
|
||||||
# Telegram update (command)
|
# Telegram update (command)
|
||||||
if isinstance(update, Update) \
|
if isinstance(update, Update) \
|
||||||
and update.message.text.startswith('/'):
|
and update.message.text.startswith('/'):
|
||||||
self.dispatchTelegramCommand(update)
|
self.dispatchTelegramCommand(update, context)
|
||||||
handled = True
|
handled = True
|
||||||
|
|
||||||
# Telegram update (message)
|
# Telegram update (message)
|
||||||
elif isinstance(update, Update):
|
elif isinstance(update, Update):
|
||||||
self.dispatchTelegramMessage(update)
|
self.dispatchTelegramMessage(update, context)
|
||||||
handled = True
|
handled = True
|
||||||
|
|
||||||
# Update not recognized
|
# Update not recognized
|
||||||
|
@ -492,7 +498,7 @@ class Dispatcher:
|
||||||
and handler in self.type_handlers[the_type]:
|
and handler in self.type_handlers[the_type]:
|
||||||
self.type_handlers[the_type].remove(handler)
|
self.type_handlers[the_type].remove(handler)
|
||||||
|
|
||||||
def dispatchTelegramCommand(self, update):
|
def dispatchTelegramCommand(self, update, context=None):
|
||||||
"""
|
"""
|
||||||
Dispatches an update that contains a command.
|
Dispatches an update that contains a command.
|
||||||
|
|
||||||
|
@ -505,11 +511,13 @@ class Dispatcher:
|
||||||
command = update.message.text.split(' ')[0][1:].split('@')[0]
|
command = update.message.text.split(' ')[0][1:].split('@')[0]
|
||||||
|
|
||||||
if command in self.telegram_command_handlers:
|
if command in self.telegram_command_handlers:
|
||||||
self.dispatchTo(self.telegram_command_handlers[command], update)
|
self.dispatchTo(self.telegram_command_handlers[command], update,
|
||||||
|
context=context)
|
||||||
else:
|
else:
|
||||||
self.dispatchTo(self.unknown_telegram_command_handlers, update)
|
self.dispatchTo(self.unknown_telegram_command_handlers, update,
|
||||||
|
context=context)
|
||||||
|
|
||||||
def dispatchRegex(self, update):
|
def dispatchRegex(self, update, context=None):
|
||||||
"""
|
"""
|
||||||
Dispatches an update to all string or telegram regex handlers that
|
Dispatches an update to all string or telegram regex handlers that
|
||||||
match the string/message content.
|
match the string/message content.
|
||||||
|
@ -532,9 +540,10 @@ class Dispatcher:
|
||||||
self.call_handler(handler,
|
self.call_handler(handler,
|
||||||
update,
|
update,
|
||||||
groups=m.groups(),
|
groups=m.groups(),
|
||||||
groupdict=m.groupdict())
|
groupdict=m.groupdict(),
|
||||||
|
context=context)
|
||||||
|
|
||||||
def dispatchStringCommand(self, update):
|
def dispatchStringCommand(self, update, context=None):
|
||||||
"""
|
"""
|
||||||
Dispatches a string-update that contains a command.
|
Dispatches a string-update that contains a command.
|
||||||
|
|
||||||
|
@ -545,11 +554,13 @@ class Dispatcher:
|
||||||
command = update.split(' ')[0][1:]
|
command = update.split(' ')[0][1:]
|
||||||
|
|
||||||
if command in self.string_command_handlers:
|
if command in self.string_command_handlers:
|
||||||
self.dispatchTo(self.string_command_handlers[command], update)
|
self.dispatchTo(self.string_command_handlers[command], update,
|
||||||
|
context=context)
|
||||||
else:
|
else:
|
||||||
self.dispatchTo(self.unknown_string_command_handlers, update)
|
self.dispatchTo(self.unknown_string_command_handlers, update,
|
||||||
|
context=context)
|
||||||
|
|
||||||
def dispatchType(self, update):
|
def dispatchType(self, update, context=None):
|
||||||
"""
|
"""
|
||||||
Dispatches an update of any type.
|
Dispatches an update of any type.
|
||||||
|
|
||||||
|
@ -559,9 +570,9 @@ class Dispatcher:
|
||||||
|
|
||||||
for t in self.type_handlers:
|
for t in self.type_handlers:
|
||||||
if isinstance(update, t):
|
if isinstance(update, t):
|
||||||
self.dispatchTo(self.type_handlers[t], update)
|
self.dispatchTo(self.type_handlers[t], update, context=context)
|
||||||
|
|
||||||
def dispatchTelegramMessage(self, update):
|
def dispatchTelegramMessage(self, update, context=None):
|
||||||
"""
|
"""
|
||||||
Dispatches an update that contains a regular message.
|
Dispatches an update that contains a regular message.
|
||||||
|
|
||||||
|
@ -570,7 +581,8 @@ class Dispatcher:
|
||||||
message.
|
message.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.dispatchTo(self.telegram_message_handlers, update)
|
self.dispatchTo(self.telegram_message_handlers, update,
|
||||||
|
context=context)
|
||||||
|
|
||||||
def dispatchError(self, update, error):
|
def dispatchError(self, update, error):
|
||||||
"""
|
"""
|
||||||
|
@ -635,4 +647,7 @@ class Dispatcher:
|
||||||
if is_async or 'groupdict' in fargs:
|
if is_async or 'groupdict' in fargs:
|
||||||
target_kwargs['groupdict'] = kwargs.get('groupdict', None)
|
target_kwargs['groupdict'] = kwargs.get('groupdict', None)
|
||||||
|
|
||||||
|
if 'context' in fargs:
|
||||||
|
target_kwargs['context'] = kwargs.get('context', None)
|
||||||
|
|
||||||
handler(self.bot, update, **target_kwargs)
|
handler(self.bot, update, **target_kwargs)
|
||||||
|
|
60
telegram/updatequeue.py
Normal file
60
telegram/updatequeue.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 class UpdateQueue to override standard
|
||||||
|
Queue."""
|
||||||
|
|
||||||
|
|
||||||
|
# Adjust for differences in Python versions
|
||||||
|
try:
|
||||||
|
from queue import Queue
|
||||||
|
except ImportError:
|
||||||
|
from Queue import Queue
|
||||||
|
|
||||||
|
|
||||||
|
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 required. Overrides standard Queue.get
|
||||||
|
method.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
update (any): handled by the dispatcher
|
||||||
|
context (any): extra data to use in handlers
|
||||||
|
"""
|
||||||
|
if not context:
|
||||||
|
return Queue.get(self, block, timeout)[0]
|
||||||
|
return Queue.get(self, block, timeout)
|
|
@ -29,15 +29,9 @@ from time import sleep
|
||||||
import subprocess
|
import subprocess
|
||||||
from signal import signal, SIGINT, SIGTERM, SIGABRT
|
from signal import signal, SIGINT, SIGTERM, SIGABRT
|
||||||
from telegram import (Bot, TelegramError, dispatcher, Dispatcher,
|
from telegram import (Bot, TelegramError, dispatcher, Dispatcher,
|
||||||
NullHandler, JobQueue)
|
NullHandler, JobQueue, UpdateQueue)
|
||||||
from telegram.utils.webhookhandler import (WebhookServer, WebhookHandler)
|
from telegram.utils.webhookhandler import (WebhookServer, WebhookHandler)
|
||||||
|
|
||||||
# Adjust for differences in Python versions
|
|
||||||
try:
|
|
||||||
from Queue import Queue
|
|
||||||
except ImportError:
|
|
||||||
from queue import Queue
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from urllib2 import URLError
|
from urllib2 import URLError
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
@ -89,7 +83,7 @@ class Updater:
|
||||||
self.bot = bot
|
self.bot = bot
|
||||||
else:
|
else:
|
||||||
self.bot = Bot(token, base_url)
|
self.bot = Bot(token, base_url)
|
||||||
self.update_queue = Queue()
|
self.update_queue = UpdateQueue()
|
||||||
self.job_queue = JobQueue(self.bot, job_queue_tick_interval)
|
self.job_queue = JobQueue(self.bot, job_queue_tick_interval)
|
||||||
self.dispatcher = Dispatcher(self.bot, self.update_queue,
|
self.dispatcher = Dispatcher(self.bot, self.update_queue,
|
||||||
workers=workers)
|
workers=workers)
|
||||||
|
|
|
@ -109,6 +109,11 @@ class UpdaterTest(BaseTest, unittest.TestCase):
|
||||||
update_queue.put('/test5 noresend')
|
update_queue.put('/test5 noresend')
|
||||||
elif args[0] == 'noresend':
|
elif args[0] == 'noresend':
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def contextTest(self, bot, update, context):
|
||||||
|
self.received_message = update
|
||||||
|
self.message_count += 1
|
||||||
|
self.context = context
|
||||||
|
|
||||||
@run_async
|
@run_async
|
||||||
def asyncAdditionalHandlerTest(self, bot, update, update_queue=None,
|
def asyncAdditionalHandlerTest(self, bot, update, update_queue=None,
|
||||||
|
@ -348,6 +353,20 @@ class UpdaterTest(BaseTest, unittest.TestCase):
|
||||||
sleep(.1)
|
sleep(.1)
|
||||||
self.assertEqual(self.received_message, '/test5 noresend')
|
self.assertEqual(self.received_message, '/test5 noresend')
|
||||||
self.assertEqual(self.message_count, 2)
|
self.assertEqual(self.message_count, 2)
|
||||||
|
|
||||||
|
def test_context(self):
|
||||||
|
print('Testing context for handlers')
|
||||||
|
context = "context_data"
|
||||||
|
self._setup_updater('', messages=0)
|
||||||
|
self.updater.dispatcher.addStringCommandHandler(
|
||||||
|
'test_context', self.contextTest)
|
||||||
|
|
||||||
|
queue = self.updater.start_polling(0.01)
|
||||||
|
queue.put('/test_context', context=context)
|
||||||
|
sleep(.5)
|
||||||
|
self.assertEqual(self.received_message, '/test_context')
|
||||||
|
self.assertEqual(self.message_count, 1)
|
||||||
|
self.assertEqual(self.context, context)
|
||||||
|
|
||||||
def test_regexGroupHandler(self):
|
def test_regexGroupHandler(self):
|
||||||
print('Testing optional groups and groupdict parameters')
|
print('Testing optional groups and groupdict parameters')
|
||||||
|
|
Loading…
Add table
Reference in a new issue