mirror of
https://github.com/python-telegram-bot/python-telegram-bot.git
synced 2024-11-22 15:17:00 +01:00
Merge pull request #258 from tsnoam/cr4.0
CR & fixes for 4.0 (dispatcher mostly, API2.0 still TODO)
This commit is contained in:
commit
0e34c0395f
20 changed files with 142 additions and 136 deletions
|
@ -371,9 +371,8 @@ Our bot is now up and running (go ahead and try it)! It's not doing anything yet
|
|||
>>> def echo(bot, update):
|
||||
... bot.sendMessage(chat_id=update.message.chat_id, text=update.message.text)
|
||||
...
|
||||
>>> from telegram.ext import MessageHandler
|
||||
>>> from telegram.ext import filters
|
||||
>>> echo_handler = MessageHandler([filters.TEXT], echo)
|
||||
>>> from telegram.ext import MessageHandler, Filters
|
||||
>>> echo_handler = MessageHandler([Filters.text], echo)
|
||||
>>> dispatcher.addHandler(echo_handler)
|
||||
|
||||
Our bot should now reply to all text messages that are not a command with a message that has the same content.
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
telegram.ext.filters module
|
||||
===========================
|
||||
|
||||
.. automodule:: telegram.ext.filters
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
|
@ -14,7 +14,6 @@ Submodules
|
|||
telegram.ext.commandhandler
|
||||
telegram.ext.inlinequeryhandler
|
||||
telegram.ext.messagehandler
|
||||
telegram.ext.filters
|
||||
telegram.ext.regexhandler
|
||||
telegram.ext.stringcommandhandler
|
||||
telegram.ext.stringregexhandler
|
||||
|
|
|
@ -19,7 +19,7 @@ Type 'stop' on the command line to stop the bot.
|
|||
"""
|
||||
|
||||
from telegram.ext import Updater, StringCommandHandler, StringRegexHandler, \
|
||||
MessageHandler, CommandHandler, RegexHandler, filters
|
||||
MessageHandler, CommandHandler, RegexHandler, Filters
|
||||
from telegram.ext.dispatcher import run_async
|
||||
from time import sleep
|
||||
import logging
|
||||
|
@ -115,7 +115,7 @@ def main():
|
|||
dp.addHandler(CommandHandler("start", start))
|
||||
dp.addHandler(CommandHandler("help", help))
|
||||
# Message handlers only receive updates that don't contain commands
|
||||
dp.addHandler(MessageHandler([filters.TEXT], message))
|
||||
dp.addHandler(MessageHandler([Filters.text], message))
|
||||
# Regex handlers will receive all updates on which their regex matches,
|
||||
# but we have to add it in a separate group, since in one group,
|
||||
# only one handler will be executed
|
||||
|
|
|
@ -17,7 +17,7 @@ Press Ctrl-C on the command line or send a signal to the process to stop the
|
|||
bot.
|
||||
"""
|
||||
|
||||
from telegram.ext import Updater, CommandHandler, MessageHandler, filters
|
||||
from telegram.ext import Updater, CommandHandler, MessageHandler, Filters
|
||||
import logging
|
||||
|
||||
# Enable logging
|
||||
|
@ -58,7 +58,7 @@ def main():
|
|||
dp.addHandler(CommandHandler("help", help))
|
||||
|
||||
# on noncommand i.e message - echo the message on Telegram
|
||||
dp.addHandler(MessageHandler([filters.TEXT], echo))
|
||||
dp.addHandler(MessageHandler([Filters.text], echo))
|
||||
|
||||
# log all errors
|
||||
dp.addErrorHandler(error)
|
||||
|
|
|
@ -9,7 +9,7 @@ import logging
|
|||
from telegram import Emoji, ForceReply, InlineKeyboardButton, \
|
||||
InlineKeyboardMarkup
|
||||
from telegram.ext import Updater, CommandHandler, MessageHandler, \
|
||||
CallbackQueryHandler, filters
|
||||
CallbackQueryHandler, Filters
|
||||
|
||||
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - '
|
||||
'%(message)s',
|
||||
|
@ -105,7 +105,7 @@ updater = Updater("TOKEN")
|
|||
# The command
|
||||
updater.dispatcher.addHandler(CommandHandler('set', set_value))
|
||||
# The answer
|
||||
updater.dispatcher.addHandler(MessageHandler([filters.TEXT], entered_value))
|
||||
updater.dispatcher.addHandler(MessageHandler([Filters.text], entered_value))
|
||||
# The confirmation
|
||||
updater.dispatcher.addHandler(CallbackQueryHandler(confirm_value))
|
||||
updater.dispatcher.addHandler(CommandHandler('start', help))
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
import logging
|
||||
from telegram import Emoji, ForceReply, ReplyKeyboardMarkup, KeyboardButton
|
||||
from telegram.ext import Updater, CommandHandler, MessageHandler, filters
|
||||
from telegram.ext import Updater, CommandHandler, MessageHandler, Filters
|
||||
|
||||
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - '
|
||||
'%(message)s',
|
||||
|
@ -93,7 +93,7 @@ updater = Updater("TOKEN")
|
|||
# The command
|
||||
updater.dispatcher.addHandler(CommandHandler('set', set_value))
|
||||
# The answer and confirmation
|
||||
updater.dispatcher.addHandler(MessageHandler([filters.TEXT], set_value))
|
||||
updater.dispatcher.addHandler(MessageHandler([Filters.text], set_value))
|
||||
updater.dispatcher.addHandler(CommandHandler('cancel', cancel))
|
||||
updater.dispatcher.addHandler(CommandHandler('start', help))
|
||||
updater.dispatcher.addHandler(CommandHandler('help', help))
|
||||
|
|
|
@ -64,8 +64,9 @@ class TelegramObject(object):
|
|||
"""
|
||||
data = dict()
|
||||
|
||||
for key, value in self.__dict__.items():
|
||||
if value or value == '':
|
||||
for key in iter(self.__dict__):
|
||||
value = self.__dict__[key]
|
||||
if value is not None:
|
||||
if hasattr(value, 'to_dict'):
|
||||
data[key] = value.to_dict()
|
||||
else:
|
||||
|
|
|
@ -919,7 +919,7 @@ class Bot(TelegramObject):
|
|||
data['cache_time'] = cache_time
|
||||
if is_personal:
|
||||
data['is_personal'] = is_personal
|
||||
if next_offset or next_offset == '':
|
||||
if next_offset is not None:
|
||||
data['next_offset'] = next_offset
|
||||
if switch_pm_text:
|
||||
data['switch_pm_text'] = switch_pm_text
|
||||
|
@ -1443,7 +1443,7 @@ class Bot(TelegramObject):
|
|||
|
||||
data = {}
|
||||
|
||||
if webhook_url or webhook_url == '':
|
||||
if webhook_url is not None:
|
||||
data['url'] = webhook_url
|
||||
if certificate:
|
||||
data['certificate'] = certificate
|
||||
|
|
|
@ -27,7 +27,7 @@ from .choseninlineresulthandler import ChosenInlineResultHandler
|
|||
from .commandhandler import CommandHandler
|
||||
from .handler import Handler
|
||||
from .inlinequeryhandler import InlineQueryHandler
|
||||
from .messagehandler import MessageHandler
|
||||
from .messagehandler import MessageHandler, Filters
|
||||
from .regexhandler import RegexHandler
|
||||
from .stringcommandhandler import StringCommandHandler
|
||||
from .stringregexhandler import StringRegexHandler
|
||||
|
@ -35,5 +35,5 @@ from .typehandler import TypeHandler
|
|||
|
||||
__all__ = ('Dispatcher', 'JobQueue', 'Updater', 'CallbackQueryHandler',
|
||||
'ChosenInlineResultHandler', 'CommandHandler', 'Handler',
|
||||
'InlineQueryHandler', 'MessageHandler', 'RegexHandler',
|
||||
'InlineQueryHandler', 'MessageHandler', 'Filters', 'RegexHandler',
|
||||
'StringCommandHandler', 'StringRegexHandler', 'TypeHandler')
|
||||
|
|
|
@ -24,11 +24,7 @@ from functools import wraps
|
|||
from threading import Thread, BoundedSemaphore, Lock, Event, current_thread
|
||||
from time import sleep
|
||||
|
||||
# Adjust for differences in Python versions
|
||||
try:
|
||||
from queue import Empty # flake8: noqa
|
||||
except ImportError:
|
||||
from Queue import Empty # flake8: noqa
|
||||
from queue import Empty
|
||||
|
||||
from telegram import (TelegramError, NullHandler)
|
||||
from telegram.ext.handler import Handler
|
||||
|
@ -100,6 +96,9 @@ class Dispatcher(object):
|
|||
self.update_queue = update_queue
|
||||
|
||||
self.handlers = {}
|
||||
""":type: dict[int, list[Handler]"""
|
||||
self.groups = []
|
||||
""":type: list[int]"""
|
||||
self.error_handlers = []
|
||||
|
||||
self.logger = logging.getLogger(__name__)
|
||||
|
@ -174,8 +173,8 @@ class Dispatcher(object):
|
|||
self.dispatchError(None, update)
|
||||
|
||||
else:
|
||||
for group in self.handlers.values():
|
||||
for handler in group:
|
||||
for group in self.groups:
|
||||
for handler in self.handlers[group]:
|
||||
try:
|
||||
if handler.checkUpdate(update):
|
||||
handler.handleUpdate(update, self)
|
||||
|
@ -188,7 +187,7 @@ class Dispatcher(object):
|
|||
|
||||
try:
|
||||
self.dispatchError(update, te)
|
||||
except:
|
||||
except Exception:
|
||||
self.logger.exception(
|
||||
'An uncaught error was raised while '
|
||||
'handling the error')
|
||||
|
@ -196,7 +195,7 @@ class Dispatcher(object):
|
|||
break
|
||||
|
||||
# Errors should not stop the thread
|
||||
except:
|
||||
except Exception:
|
||||
self.logger.exception(
|
||||
'An uncaught error was raised while '
|
||||
'processing the update')
|
||||
|
@ -204,25 +203,39 @@ class Dispatcher(object):
|
|||
|
||||
def addHandler(self, handler, group=DEFAULT_GROUP):
|
||||
"""
|
||||
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.
|
||||
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[object]): The group identifier. Default is 0
|
||||
group (Optional[int]): The group identifier. Default is 0
|
||||
"""
|
||||
|
||||
if not isinstance(handler, Handler):
|
||||
raise TypeError('Handler is no instance of telegram.ext.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)
|
||||
|
||||
|
@ -236,6 +249,9 @@ class Dispatcher(object):
|
|||
"""
|
||||
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 addErrorHandler(self, callback):
|
||||
"""
|
||||
|
|
|
@ -1,23 +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 filters used by the MessageHandler class """
|
||||
|
||||
TEXT, AUDIO, DOCUMENT, PHOTO, STICKER, VIDEO, VOICE, CONTACT, LOCATION, \
|
||||
VENUE, STATUS_UPDATE = range(11)
|
|
@ -46,6 +46,9 @@ class Handler(object):
|
|||
|
||||
Args:
|
||||
update (object): The update to be tested
|
||||
|
||||
Returns:
|
||||
bool
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
|
|
|
@ -22,11 +22,7 @@
|
|||
import logging
|
||||
import time
|
||||
from threading import Thread, Lock
|
||||
|
||||
try:
|
||||
from queue import PriorityQueue
|
||||
except ImportError:
|
||||
from Queue import PriorityQueue
|
||||
from queue import PriorityQueue
|
||||
|
||||
|
||||
class JobQueue(object):
|
||||
|
|
|
@ -22,7 +22,71 @@
|
|||
from .handler import Handler
|
||||
from telegram import Update
|
||||
|
||||
from .filters import * # flake8: noqa
|
||||
|
||||
class Filters(object):
|
||||
"""
|
||||
Convenient namespace (class) & methods for the filter funcs of the
|
||||
MessageHandler class.
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def text(update):
|
||||
return update.message.text and not update.message.text.startswith('/')
|
||||
|
||||
@staticmethod
|
||||
def command(update):
|
||||
return update.message.text and update.message.text.startswith('/')
|
||||
|
||||
@staticmethod
|
||||
def audio(update):
|
||||
return bool(update.message.audio)
|
||||
|
||||
@staticmethod
|
||||
def document(update):
|
||||
return bool(update.message.document)
|
||||
|
||||
@staticmethod
|
||||
def photo(update):
|
||||
return bool(update.message.photo)
|
||||
|
||||
@staticmethod
|
||||
def sticker(update):
|
||||
return bool(update.message.sticker)
|
||||
|
||||
@staticmethod
|
||||
def video(update):
|
||||
return bool(update.message.video)
|
||||
|
||||
@staticmethod
|
||||
def voice(update):
|
||||
return bool(update.message.voice)
|
||||
|
||||
@staticmethod
|
||||
def contact(update):
|
||||
return bool(update.message.contact)
|
||||
|
||||
@staticmethod
|
||||
def location(update):
|
||||
return bool(update.message.location)
|
||||
|
||||
@staticmethod
|
||||
def venue(update):
|
||||
return bool(update.message.venue)
|
||||
|
||||
@staticmethod
|
||||
def status_update(update):
|
||||
return bool(
|
||||
update.message.new_chat_member or
|
||||
update.message.left_chat_member or
|
||||
update.message.new_chat_title or
|
||||
update.message.new_chat_photo or
|
||||
update.message.delete_chat_photo or
|
||||
update.message.group_chat_created or
|
||||
update.message.supergroup_chat_created or
|
||||
update.message.channel_chat_created or
|
||||
update.message.migrate_to_chat_id or
|
||||
update.message.migrate_from_chat_id or
|
||||
update.message.pinned_message)
|
||||
|
||||
|
||||
class MessageHandler(Handler):
|
||||
|
@ -32,8 +96,10 @@ class MessageHandler(Handler):
|
|||
updates.
|
||||
|
||||
Args:
|
||||
filters (list): A list of filters defined in ``telegram.ext.filters``.
|
||||
All messages that match at least one of those filters will be
|
||||
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
|
||||
|
@ -49,36 +115,14 @@ class MessageHandler(Handler):
|
|||
self.filters = filters
|
||||
|
||||
def checkUpdate(self, update):
|
||||
filters = self.filters
|
||||
if isinstance(update, Update) and update.message:
|
||||
message = update.message
|
||||
return (not filters or # If filters is empty, accept all messages
|
||||
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)
|
||||
)
|
||||
if not self.filters:
|
||||
res = True
|
||||
else:
|
||||
res = any(func(update) for func in self.filters)
|
||||
else:
|
||||
return False
|
||||
res = False
|
||||
return res
|
||||
|
||||
def handleUpdate(self, update, dispatcher):
|
||||
optional_args = self.collectOptionalArgs(dispatcher)
|
||||
|
|
|
@ -21,6 +21,8 @@
|
|||
|
||||
import re
|
||||
|
||||
from future.utils import string_types
|
||||
|
||||
from .handler import Handler
|
||||
from telegram import Update
|
||||
|
||||
|
@ -52,7 +54,7 @@ class RegexHandler(Handler):
|
|||
pass_groupdict=False, pass_update_queue=False):
|
||||
super(RegexHandler, self).__init__(callback, pass_update_queue)
|
||||
|
||||
if isinstance(pattern, str):
|
||||
if isinstance(pattern, string_types):
|
||||
pattern = re.compile(pattern)
|
||||
|
||||
self.pattern = pattern
|
||||
|
|
|
@ -21,6 +21,8 @@
|
|||
|
||||
import re
|
||||
|
||||
from future.utils import string_types
|
||||
|
||||
from .handler import Handler
|
||||
|
||||
|
||||
|
@ -51,7 +53,7 @@ class StringRegexHandler(Handler):
|
|||
pass_groupdict=False, pass_update_queue=False):
|
||||
super(StringRegexHandler, self).__init__(callback, pass_update_queue)
|
||||
|
||||
if isinstance(pattern, str):
|
||||
if isinstance(pattern, string_types):
|
||||
pattern = re.compile(pattern)
|
||||
|
||||
self.pattern = pattern
|
||||
|
@ -59,11 +61,8 @@ class StringRegexHandler(Handler):
|
|||
self.pass_groupdict = pass_groupdict
|
||||
|
||||
def checkUpdate(self, update):
|
||||
if isinstance(update, str):
|
||||
match = re.match(self.pattern, update)
|
||||
return bool(match)
|
||||
else:
|
||||
return False
|
||||
return isinstance(update, string_types) and bool(
|
||||
re.match(self.pattern, update))
|
||||
|
||||
def handleUpdate(self, update, dispatcher):
|
||||
optional_args = self.collectOptionalArgs(dispatcher)
|
||||
|
|
|
@ -28,12 +28,7 @@ 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 queue import Queue
|
||||
|
||||
from telegram import Bot, TelegramError, NullHandler
|
||||
from telegram.ext import dispatcher, Dispatcher, JobQueue
|
||||
|
|
|
@ -21,24 +21,6 @@
|
|||
|
||||
from telegram.error import InvalidToken
|
||||
|
||||
try:
|
||||
type(basestring)
|
||||
except NameError:
|
||||
basestring = str
|
||||
|
||||
|
||||
def validate_string(arg, name):
|
||||
"""
|
||||
Validate a string argument. Raises a ValueError if `arg` is neither an
|
||||
instance of basestring (Python 2) or str (Python 3) nor None.
|
||||
|
||||
Args:
|
||||
arg (basestring): The value to be tested
|
||||
name (str): The name of the argument, for the error message
|
||||
"""
|
||||
if not isinstance(arg, basestring) and arg is not None:
|
||||
raise ValueError(name + ' is not a string')
|
||||
|
||||
|
||||
def validate_token(token):
|
||||
"""a very basic validation on token"""
|
||||
|
|
|
@ -144,8 +144,8 @@ class UpdaterTest(BaseTest, unittest.TestCase):
|
|||
def test_addRemoveTelegramMessageHandler(self):
|
||||
self._setup_updater('Test')
|
||||
d = self.updater.dispatcher
|
||||
from telegram.ext import filters
|
||||
handler = MessageHandler([filters.TEXT], self.telegramHandlerTest)
|
||||
from telegram.ext import Filters
|
||||
handler = MessageHandler([Filters.text], self.telegramHandlerTest)
|
||||
d.addHandler(handler)
|
||||
self.updater.start_polling(0.01)
|
||||
sleep(.1)
|
||||
|
|
Loading…
Reference in a new issue