1
0
mirror of https://github.com/eternnoir/pyTelegramBotAPI.git synced 2023-08-10 21:12:57 +03:00

Extended the message_handler decorator.

TeleBot now makes use of the API's getUpdates timeout optional parameter instead of time.sleep. This yields quicker response times.
This commit is contained in:
pieter 2015-07-02 22:32:18 +02:00
parent 144fa64604
commit 111ebf5c16
2 changed files with 78 additions and 39 deletions

View File

@ -41,7 +41,7 @@ class TeleBot:
self.last_update_id = 0 self.last_update_id = 0
self.commands = [] self.message_handlers = []
def get_update(self): def get_update(self):
""" """
@ -49,7 +49,7 @@ class TeleBot:
Registered listeners and applicable message handlers will be notified when a new message arrives. Registered listeners and applicable message handlers will be notified when a new message arrives.
:raises ApiException when a call has failed. :raises ApiException when a call has failed.
""" """
updates = apihelper.get_updates(self.token, offset=(self.last_update_id + 1)) updates = apihelper.get_updates(self.token, offset=(self.last_update_id + 1), timeout=20)
new_messages = [] new_messages = []
for update in updates: for update in updates:
if update['update_id'] > self.last_update_id: if update['update_id'] > self.last_update_id:
@ -66,31 +66,33 @@ class TeleBot:
t = threading.Thread(target=listener, args=new_messages) t = threading.Thread(target=listener, args=new_messages)
t.start() t.start()
def polling(self, interval=3): def polling(self):
""" """
This function creates a new Thread that calls an internal __polling function.
This allows the bot to retrieve Updates automagically and notify listeners and message handlers accordingly.
Do not call this function more than once!
Always get updates. Always get updates.
:param interval: interval secs. :param interval: interval secs.
:return: :return:
""" """
self.interval = interval
# clear thread.
self.__stop_polling = True
time.sleep(interval + 1)
self.__stop_polling = False self.__stop_polling = False
self.polling_thread = threading.Thread(target=self.__polling, args=()) self.polling_thread = threading.Thread(target=self.__polling, args=())
self.polling_thread.daemon = True self.polling_thread.daemon = True
self.polling_thread.start() self.polling_thread.start()
def __polling(self): def __polling(self):
print('telegram bot start polling') print('TeleBot: Started polling.')
while not self.__stop_polling: while not self.__stop_polling:
try: try:
self.get_update() self.get_update()
except Exception as e: except Exception as e:
print("TeleBot: Exception occurred. Stopping.")
self.__stop_polling = True
print(e) print(e)
time.sleep(self.interval)
print('telegram bot stop polling') print('TeleBot: Stopped polling.')
def stop_polling(self): def stop_polling(self):
self.__stop_polling = True self.__stop_polling = True
@ -109,7 +111,7 @@ class TeleBot:
:param user_id: :param user_id:
:param offset: :param offset:
:param limit: :param limit:
:return: :return: API reply.
""" """
result = apihelper.get_user_profile_photos(self.token, user_id, offset, limit) result = apihelper.get_user_profile_photos(self.token, user_id, offset, limit)
return types.UserProfilePhotos.de_json(result) return types.UserProfilePhotos.de_json(result)
@ -122,7 +124,7 @@ class TeleBot:
:param disable_web_page_preview: :param disable_web_page_preview:
:param reply_to_message_id: :param reply_to_message_id:
:param reply_markup: :param reply_markup:
:return: :return: API reply.
""" """
return apihelper.send_message(self.token, chat_id, text, disable_web_page_preview, reply_to_message_id, return apihelper.send_message(self.token, chat_id, text, disable_web_page_preview, reply_to_message_id,
reply_markup) reply_markup)
@ -133,7 +135,7 @@ class TeleBot:
:param chat_id: which chat to forward :param chat_id: which chat to forward
:param from_chat_id: which chat message from :param from_chat_id: which chat message from
:param message_id: message id :param message_id: message id
:return: :return: API reply.
""" """
return apihelper.forward_message(self.token, chat_id, from_chat_id, message_id) return apihelper.forward_message(self.token, chat_id, from_chat_id, message_id)
@ -145,7 +147,7 @@ class TeleBot:
:param caption: :param caption:
:param reply_to_message_id: :param reply_to_message_id:
:param reply_markup: :param reply_markup:
:return: :return: API reply.
""" """
return apihelper.send_photo(self.token, chat_id, photo, caption, reply_to_message_id, reply_markup) return apihelper.send_photo(self.token, chat_id, photo, caption, reply_to_message_id, reply_markup)
@ -157,7 +159,7 @@ class TeleBot:
:param data: :param data:
:param reply_to_message_id: :param reply_to_message_id:
:param reply_markup: :param reply_markup:
:return: :return: API reply.
""" """
return apihelper.send_data(self.token, chat_id, data, 'audio', reply_to_message_id, reply_markup) return apihelper.send_data(self.token, chat_id, data, 'audio', reply_to_message_id, reply_markup)
@ -168,7 +170,7 @@ class TeleBot:
:param data: :param data:
:param reply_to_message_id: :param reply_to_message_id:
:param reply_markup: :param reply_markup:
:return: :return: API reply.
""" """
return apihelper.send_data(self.token, chat_id, data, 'document', reply_to_message_id, reply_markup) return apihelper.send_data(self.token, chat_id, data, 'document', reply_to_message_id, reply_markup)
@ -179,7 +181,7 @@ class TeleBot:
:param data: :param data:
:param reply_to_message_id: :param reply_to_message_id:
:param reply_markup: :param reply_markup:
:return: :return: API reply.
""" """
return apihelper.send_data(self.token, chat_id, data, 'sticker', reply_to_message_id, reply_markup) return apihelper.send_data(self.token, chat_id, data, 'sticker', reply_to_message_id, reply_markup)
@ -190,7 +192,7 @@ class TeleBot:
:param data: :param data:
:param reply_to_message_id: :param reply_to_message_id:
:param reply_markup: :param reply_markup:
:return: :return: API reply.
""" """
return apihelper.send_data(self.token, chat_id, data, 'video', reply_to_message_id, reply_markup) return apihelper.send_data(self.token, chat_id, data, 'video', reply_to_message_id, reply_markup)
@ -202,7 +204,7 @@ class TeleBot:
:param longitude: :param longitude:
:param reply_to_message_id: :param reply_to_message_id:
:param reply_markup: :param reply_markup:
:return: :return: API reply.
""" """
return apihelper.send_location(self.token, chat_id, latitude, longitude, reply_to_message_id, reply_markup) return apihelper.send_location(self.token, chat_id, latitude, longitude, reply_to_message_id, reply_markup)
@ -212,13 +214,13 @@ class TeleBot:
The status is set for 5 seconds or less (when a message arrives from your bot, Telegram clients clear The status is set for 5 seconds or less (when a message arrives from your bot, Telegram clients clear
its typing status). its typing status).
:param chat_id: :param chat_id:
:param action: string . typing,upload_photo,record_video,upload_video,record_audio,upload_audio,upload_document, :param action: One of the following strings: 'typing', 'upload_photo', 'record_video', 'upload_video',
find_location. 'record_audio', 'upload_audio', 'upload_document', 'find_location'.
:return: :return: API reply.
""" """
return apihelper.send_chat_action(self.token, chat_id, action) return apihelper.send_chat_action(self.token, chat_id, action)
def message_handler(self, regexp=None, func=None, content_types=['text']): def message_handler(self, commands=None, regexp=None, func=None, content_types=['text']):
""" """
Message handler decorator. Message handler decorator.
This decorator can be used to decorate functions that must handle certain types of messages. This decorator can be used to decorate functions that must handle certain types of messages.
@ -246,27 +248,60 @@ class TeleBot:
:param regexp: Optional regular expression. :param regexp: Optional regular expression.
:param func: Optional lambda function. The lambda receives the message to test as the first parameter. It must return True if the command should handle the message. :param func: Optional lambda function. The lambda receives the message to test as the first parameter. It must return True if the command should handle the message.
:param content_types: This commands' supported content types. Must be a list. Defaults to ['text']. :param content_types: This commands' supported content types. Must be a list. Defaults to ['text'].
:return:
""" """
def decorator(fn): def decorator(fn):
self.commands.append([fn, regexp, func, content_types]) func_dict = {'function': fn, 'content_types': content_types}
if regexp:
func_dict['regexp'] = regexp if 'text' in content_types else None
if func:
func_dict['lambda'] = func
if commands:
func_dict['commands'] = commands if 'text' in content_types else None
self.message_handlers.append(func_dict)
return fn return fn
return decorator return decorator
@staticmethod @staticmethod
def _test_command(command, message): def is_command(text):
if message.content_type not in command[3]: """
Checks if `text` is a command. Telegram chat commands start with the '/' character.
:param text: Text to check.
:return: True if `text` is a command, else False.
"""
return text.startswith('/')
@staticmethod
def extract_command(text):
"""
Extracts the command from `text` (minus the '/') if `text` is a command (see is_command).
If `text` is not a command, this function returns None.
Examples:
extract_command('/help'): 'help'
extract_command('/search black eyed peas'): 'search'
extract_command('Good day to you'): None
:param text: String to extract the command from
:return: the command if `text` is a command, else None.
"""
return text.split()[0][1:] if TeleBot.is_command(text) else None
@staticmethod
def _test_message_handler(message_handler, message):
if message.content_type not in message_handler['content_types']:
return False return False
if command[1] is not None and message.content_type == 'text' and re.search(command[1], message.text): if 'commands' in message_handler and message.content_type == 'text':
return True return TeleBot.extract_command(message.text) in message_handler['commands']
if command[2] is not None: if 'regexp' in message_handler and message.content_type == 'text' and re.search(message_handler['regexp'], message.text):
return command[2](message) return False
if 'lambda' in message_handler:
return message_handler['lambda'](message)
return False return False
def _notify_command_handlers(self, new_messages): def _notify_command_handlers(self, new_messages):
for message in new_messages: for message in new_messages:
for command in self.commands: for message_handler in self.message_handlers:
if self._test_command(command, message): if self._test_message_handler(message_handler, message):
t = threading.Thread(target=command[0], args=(message,)) t = threading.Thread(target=message_handler['function'], args=(message,))
t.start() t.start()
break break

View File

@ -56,12 +56,16 @@ def send_message(token, chat_id, text, disable_web_page_preview=None, reply_to_m
return _make_request(token, method_url, params=payload) return _make_request(token, method_url, params=payload)
def get_updates(token, offset=None): def get_updates(token, offset=None, limit=None, timeout=None):
method_url = r'getUpdates' method_url = r'getUpdates'
if offset is not None: payload = {}
return _make_request(token, method_url, params={'offset': offset}) if offset:
else: payload['offset'] = offset
return _make_request(token, method_url) if limit:
payload['limit'] = limit
if timeout:
payload['timeout'] = timeout
return _make_request(token, method_url, params=payload)
def get_user_profile_photos(token, user_id, offset=None, limit=None): def get_user_profile_photos(token, user_id, offset=None, limit=None):