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

Created util.py to clean up __init__.py and apihelper.py and updated README accordingly

Fixed failing send_document_by_id and send_photo_by_id
This commit is contained in:
pieter 2015-08-31 11:46:18 +02:00
parent 6f34a22c4b
commit 3c8faa155f
6 changed files with 161 additions and 171 deletions

View File

@ -282,7 +282,7 @@ To enable this behaviour, create an instance of AsyncTeleBot instead of TeleBot.
```python ```python
tb = telebot.AsyncTeleBot("TOKEN") tb = telebot.AsyncTeleBot("TOKEN")
``` ```
Now, every function that calls the Telegram API is executed in a separate Thread. The functions are modified to return an AsyncTask instance (defined in \__init__.py). Using AsyncTeleBot allows you to do the following: Now, every function that calls the Telegram API is executed in a separate Thread. The functions are modified to return an AsyncTask instance (defined in util.py). Using AsyncTeleBot allows you to do the following:
```python ```python
import telebot import telebot
@ -300,12 +300,12 @@ result = task.wait() # Get the result of the execution
### Sending large text messages ### Sending large text messages
Sometimes you must send messages that exceed 5000 characters. The Telegram API can not handle that many characters in one request, so we need to split the message in multiples. Here is how to do that using the API: Sometimes you must send messages that exceed 5000 characters. The Telegram API can not handle that many characters in one request, so we need to split the message in multiples. Here is how to do that using the API:
```python ```python
from telebot import apihelper from telebot import util
large_text = open("large_text.txt", "rb").read() large_text = open("large_text.txt", "rb").read()
# Split the text each 3000 characters. # Split the text each 3000 characters.
# split_string returns a list with the splitted text. # split_string returns a list with the splitted text.
splitted_text = apihelper.split_string(large_text, 3000) splitted_text = util.split_string(large_text, 3000)
for text in splitted_text: for text in splitted_text:
tb.send_message(chat_id, text) tb.send_message(chat_id, text)
``` ```

View File

@ -2,18 +2,14 @@
from __future__ import print_function from __future__ import print_function
import threading import threading
# Python3 queue support.
try:
import Queue
except ImportError:
import queue as Queue
import time import time
import logging import logging
logging.basicConfig() logging.basicConfig()
logger = logging.getLogger('Telebot') logger = logging.getLogger('Telebot')
import re import re
from telebot import apihelper, types
from telebot import apihelper, types, util
""" """
Module : telebot Module : telebot
@ -21,48 +17,6 @@ Module : telebot
API_URL = r"https://api.telegram.org/" API_URL = r"https://api.telegram.org/"
class ThreadPool:
class WorkerThread(threading.Thread):
count = 0
def __init__(self, queue):
threading.Thread.__init__(self, name="WorkerThread{0}".format(self.__class__.count + 1))
self.__class__.count += 1
self.queue = queue
self.daemon = True
self._running = True
self.start()
def run(self):
while self._running:
try:
task, args, kwargs = self.queue.get()
task(*args, **kwargs)
except Queue.Empty:
time.sleep(0)
pass
def stop(self):
self._running = False
def __init__(self, num_threads=4):
self.tasks = Queue.Queue()
self.workers = [self.WorkerThread(self.tasks) for _ in range(num_threads)]
self.num_threads = num_threads
def put(self, func, *args, **kwargs):
self.tasks.put((func, args, kwargs))
def close(self):
for worker in self.workers:
worker.stop()
for worker in self.workers:
worker.join()
class TeleBot: class TeleBot:
""" This is TeleBot Class """ This is TeleBot Class
Methods: Methods:
@ -82,7 +36,6 @@ class TeleBot:
def __init__(self, token, create_threads=True, num_threads=4): def __init__(self, token, create_threads=True, num_threads=4):
""" """
:param token: bot API token :param token: bot API token
:param create_threads: Create thread for message handler :param create_threads: Create thread for message handler
:param num_threads: Number of worker in thread pool. :param num_threads: Number of worker in thread pool.
@ -104,7 +57,7 @@ class TeleBot:
self.message_handlers = [] self.message_handlers = []
if self.__create_threads: if self.__create_threads:
self.worker_pool = ThreadPool(num_threads) self.worker_pool = util.ThreadPool(num_threads)
def get_update(self): def get_update(self):
""" """
@ -158,7 +111,6 @@ class TeleBot:
if block: if block:
self.__stop_polling.wait() self.__stop_polling.wait()
def __polling(self, none_stop, interval): def __polling(self, none_stop, interval):
logger.info('TeleBot: Started polling.') logger.info('TeleBot: Started polling.')
@ -249,7 +201,7 @@ class TeleBot:
:param duration:Duration of the audio in seconds :param duration:Duration of the audio in seconds
:param performer:Performer :param performer:Performer
:param title:Track name :param title:Track name
:param reply_to_message_id:If the message is a reply, ID of the original messag :param reply_to_message_id:If the message is a reply, ID of the original message
:param reply_markup: :param reply_markup:
:return: Message :return: Message
""" """
@ -438,7 +390,7 @@ class TeleBot:
if message.content_type not in message_handler['content_types']: if message.content_type not in message_handler['content_types']:
return False return False
if 'commands' in message_handler and message.content_type == 'text': if 'commands' in message_handler and message.content_type == 'text':
return apihelper.extract_command(message.text) in message_handler['commands'] return util.extract_command(message.text) in message_handler['commands']
if 'regexp' in message_handler and message.content_type == 'text' and re.search(message_handler['regexp'], if 'regexp' in message_handler and message.content_type == 'text' and re.search(message_handler['regexp'],
message.text): message.text):
return True return True
@ -452,93 +404,55 @@ class TeleBot:
if self._test_message_handler(message_handler, message): if self._test_message_handler(message_handler, message):
if self.__create_threads: if self.__create_threads:
self.worker_pool.put(message_handler['function'], message) self.worker_pool.put(message_handler['function'], message)
# t = threading.Thread(target=message_handler['function'], args=(message,))
# t.start()
else: else:
message_handler['function'](message) message_handler['function'](message)
break break
class AsyncTask:
def __init__(self, target, *args, **kwargs):
self.target = target
self.args = args
self.kwargs = kwargs
self.done = False
self.thread = threading.Thread(target=self._run)
self.thread.start()
def _run(self):
try:
self.result = self.target(*self.args, **self.kwargs)
except Exception as e:
self.result = e
self.done = True
def wait(self):
if not self.done:
self.thread.join()
if isinstance(self.result, Exception):
raise self.result
else:
return self.result
def async():
def decorator(fn):
def wrapper(*args, **kwargs):
return AsyncTask(fn, *args, **kwargs)
return wrapper
return decorator
class AsyncTeleBot(TeleBot): class AsyncTeleBot(TeleBot):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
TeleBot.__init__(self, *args, **kwargs) TeleBot.__init__(self, *args, **kwargs)
@async() @util.async()
def get_me(self): def get_me(self):
return TeleBot.get_me(self) return TeleBot.get_me(self)
@async() @util.async()
def get_user_profile_photos(self, *args, **kwargs): def get_user_profile_photos(self, *args, **kwargs):
return TeleBot.get_user_profile_photos(self, *args, **kwargs) return TeleBot.get_user_profile_photos(self, *args, **kwargs)
@async() @util.async()
def send_message(self, *args, **kwargs): def send_message(self, *args, **kwargs):
return TeleBot.send_message(self, *args, **kwargs) return TeleBot.send_message(self, *args, **kwargs)
@async() @util.async()
def forward_message(self, *args, **kwargs): def forward_message(self, *args, **kwargs):
return TeleBot.forward_message(self, *args, **kwargs) return TeleBot.forward_message(self, *args, **kwargs)
@async() @util.async()
def send_photo(self, *args, **kwargs): def send_photo(self, *args, **kwargs):
return TeleBot.send_photo(self, *args, **kwargs) return TeleBot.send_photo(self, *args, **kwargs)
@async() @util.async()
def send_audio(self, *args, **kwargs): def send_audio(self, *args, **kwargs):
return TeleBot.send_audio(self, *args, **kwargs) return TeleBot.send_audio(self, *args, **kwargs)
@async() @util.async()
def send_document(self, *args, **kwargs): def send_document(self, *args, **kwargs):
return TeleBot.send_document(self, *args, **kwargs) return TeleBot.send_document(self, *args, **kwargs)
@async() @util.async()
def send_sticker(self, *args, **kwargs): def send_sticker(self, *args, **kwargs):
return TeleBot.send_sticker(self, *args, **kwargs) return TeleBot.send_sticker(self, *args, **kwargs)
@async() @util.async()
def send_video(self, *args, **kwargs): def send_video(self, *args, **kwargs):
return TeleBot.send_video(self, *args, **kwargs) return TeleBot.send_video(self, *args, **kwargs)
@async() @util.async()
def send_location(self, *args, **kwargs): def send_location(self, *args, **kwargs):
return TeleBot.send_location(self, *args, **kwargs) return TeleBot.send_location(self, *args, **kwargs)
@async() @util.async()
def send_chat_action(self, *args, **kwargs): def send_chat_action(self, *args, **kwargs):
return TeleBot.send_chat_action(self, *args, **kwargs) return TeleBot.send_chat_action(self, *args, **kwargs)

View File

@ -1,9 +1,9 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import requests import requests
from six import string_types
import telebot import telebot
from telebot import types from telebot import types
from telebot import util
logger = telebot.logger logger = telebot.logger
@ -115,7 +115,7 @@ def send_photo(token, chat_id, photo, caption=None, reply_to_message_id=None, re
method_url = r'sendPhoto' method_url = r'sendPhoto'
payload = {'chat_id': chat_id} payload = {'chat_id': chat_id}
files = None files = None
if not is_string(photo): if not util.is_string(photo):
files = {'photo': photo} files = {'photo': photo}
else: else:
payload['photo'] = photo payload['photo'] = photo
@ -148,7 +148,7 @@ def send_video(token, chat_id, data, duration=None, caption=None, reply_to_messa
method_url = r'sendVideo' method_url = r'sendVideo'
payload = {'chat_id': chat_id} payload = {'chat_id': chat_id}
files = None files = None
if not is_string(data): if not util.is_string(data):
files = {'video': data} files = {'video': data}
else: else:
payload['video'] = data payload['video'] = data
@ -167,7 +167,7 @@ def send_voice(token, chat_id, voice, duration=None, reply_to_message_id=None, r
method_url = r'sendVoice' method_url = r'sendVoice'
payload = {'chat_id': chat_id} payload = {'chat_id': chat_id}
files = None files = None
if not is_string(voice): if not util.is_string(voice):
files = {'voice': voice} files = {'voice': voice}
else: else:
payload['voice'] = voice payload['voice'] = voice
@ -185,7 +185,7 @@ def send_audio(token, chat_id, audio, duration=None, performer=None, title=None,
method_url = r'sendAudio' method_url = r'sendAudio'
payload = {'chat_id': chat_id} payload = {'chat_id': chat_id}
files = None files = None
if not is_string(audio): if not util.is_string(audio):
files = {'audio': audio} files = {'audio': audio}
else: else:
payload['audio'] = audio payload['audio'] = audio
@ -206,7 +206,7 @@ def send_data(token, chat_id, data, data_type, reply_to_message_id=None, reply_m
method_url = get_method_by_type(data_type) method_url = get_method_by_type(data_type)
payload = {'chat_id': chat_id} payload = {'chat_id': chat_id}
files = None files = None
if not is_string(data): if not util.is_string(data):
files = {data_type: data} files = {data_type: data}
else: else:
payload[data_type] = data payload[data_type] = data
@ -229,49 +229,6 @@ def _convert_markup(markup):
return markup.to_json() return markup.to_json()
return markup return markup
def is_string(var):
return isinstance(var, string_types)
def is_command(text):
"""
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('/')
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('/help@BotName'): '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 (according to is_command), else None.
"""
return text.split()[0].split('@')[0][1:] if is_command(text) else None
def split_string(text, chars_per_string):
"""
Splits one string into multiple strings, with a maximum amount of `chars_per_string` characters per string.
This is very useful for splitting one giant message into multiples.
:param text: The text to split
:param chars_per_string: The number of characters per line the text is split into.
:return: The splitted text as a list of strings.
"""
return [text[i:i + chars_per_string] for i in range(0, len(text), chars_per_string)]
class ApiException(Exception): class ApiException(Exception):
""" """
This class represents an Exception thrown when a call to the Telegram API fails. This class represents an Exception thrown when a call to the Telegram API fails.

View File

@ -28,7 +28,6 @@ class JsonSerializable:
Subclasses of this class are guaranteed to be able to be converted to JSON format. Subclasses of this class are guaranteed to be able to be converted to JSON format.
All subclasses of this class must override to_json. All subclasses of this class must override to_json.
""" """
def to_json(self): def to_json(self):
""" """
Returns a JSON string representation of this class. Returns a JSON string representation of this class.
@ -44,7 +43,6 @@ class JsonDeserializable:
Subclasses of this class are guaranteed to be able to be created from a json-style dict or json formatted string. Subclasses of this class are guaranteed to be able to be created from a json-style dict or json formatted string.
All subclasses of this class must override de_json. All subclasses of this class must override de_json.
""" """
@classmethod @classmethod
def de_json(cls, json_type): def de_json(cls, json_type):
""" """

126
telebot/util.py Normal file
View File

@ -0,0 +1,126 @@
# -*- coding: utf-8 -*-
import threading
from six import string_types
# Python3 queue support.
try:
import Queue
except ImportError:
import queue as Queue
class ThreadPool:
class WorkerThread(threading.Thread):
count = 0
def __init__(self, queue):
threading.Thread.__init__(self, name="WorkerThread{0}".format(self.__class__.count + 1))
self.__class__.count += 1
self.queue = queue
self.daemon = True
self._running = True
self.start()
def run(self):
while self._running:
try:
task, args, kwargs = self.queue.get(block=True, timeout=.01)
task(*args, **kwargs)
except Queue.Empty:
pass
def stop(self):
self._running = False
def __init__(self, num_threads=4):
self.tasks = Queue.Queue()
self.workers = [self.WorkerThread(self.tasks) for _ in range(num_threads)]
self.num_threads = num_threads
def put(self, func, *args, **kwargs):
self.tasks.put((func, args, kwargs))
def close(self):
for worker in self.workers:
worker.stop()
for worker in self.workers:
worker.join()
class AsyncTask:
def __init__(self, target, *args, **kwargs):
self.target = target
self.args = args
self.kwargs = kwargs
self.done = False
self.thread = threading.Thread(target=self._run)
self.thread.start()
def _run(self):
try:
self.result = self.target(*self.args, **self.kwargs)
except Exception as e:
self.result = e
self.done = True
def wait(self):
if not self.done:
self.thread.join()
if isinstance(self.result, Exception):
raise self.result
else:
return self.result
def async():
def decorator(fn):
def wrapper(*args, **kwargs):
return AsyncTask(fn, *args, **kwargs)
return wrapper
return decorator
def is_string(var):
return isinstance(var, string_types)
def is_command(text):
"""
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('/')
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('/help@BotName'): '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 (according to is_command), else None.
"""
return text.split()[0].split('@')[0][1:] if is_command(text) else None
def split_string(text, chars_per_string):
"""
Splits one string into multiple strings, with a maximum amount of `chars_per_string` characters per string.
This is very useful for splitting one giant message into multiples.
:param text: The text to split
:param chars_per_string: The number of characters per line the text is split into.
:return: The splitted text as a list of strings.
"""
return [text[i:i + chars_per_string] for i in range(0, len(text), chars_per_string)]

View File

@ -9,6 +9,7 @@ import os
import telebot import telebot
from telebot import types from telebot import types
from telebot import apihelper from telebot import apihelper
from telebot import util
should_skip = 'TOKEN' and 'CHAT_ID' not in os.environ should_skip = 'TOKEN' and 'CHAT_ID' not in os.environ
@ -67,18 +68,15 @@ class TestTeleBot:
time.sleep(1) time.sleep(1)
assert not msg.text == 'got' assert not msg.text == 'got'
def test_send_file_by_id(self):
file_id = 'BQADBQADjAIAAsYifgbvqwq1he9REAI'
tb = telebot.TeleBot(TOKEN)
ret_msg = tb.send_document(CHAT_ID, file_id)
assert ret_msg.message_id
def test_send_file(self): def test_send_file(self):
file_data = open('../examples/detailed_example/kitten.jpg', 'rb') file_data = open('../examples/detailed_example/kitten.jpg', 'rb')
tb = telebot.TeleBot(TOKEN) tb = telebot.TeleBot(TOKEN)
ret_msg = tb.send_document(CHAT_ID, file_data) ret_msg = tb.send_document(CHAT_ID, file_data)
assert ret_msg.message_id assert ret_msg.message_id
ret_msg = tb.send_document(CHAT_ID, ret_msg.document.file_id)
assert ret_msg.message_id
def test_send_video(self): def test_send_video(self):
file_data = open('./test_data/test_video.mp4', 'rb') file_data = open('./test_data/test_video.mp4', 'rb')
tb = telebot.TeleBot(TOKEN) tb = telebot.TeleBot(TOKEN)
@ -100,18 +98,15 @@ class TestTeleBot:
print(e) print(e)
assert True assert True
def test_send_photo_by_id(self):
photo_id = 'AgADBQADTKgxG8YifgbcWQAB7Da9yYIx1rEyAAT-HYJ3CrJEqdA2AQABAg'
tb = telebot.TeleBot(TOKEN)
ret_msg = tb.send_photo(CHAT_ID, photo_id)
assert ret_msg.message_id
def test_send_photo(self): def test_send_photo(self):
file_data = open('../examples/detailed_example/kitten.jpg', 'rb') file_data = open('../examples/detailed_example/kitten.jpg', 'rb')
tb = telebot.TeleBot(TOKEN) tb = telebot.TeleBot(TOKEN)
ret_msg = tb.send_photo(CHAT_ID, file_data) ret_msg = tb.send_photo(CHAT_ID, file_data)
assert ret_msg.message_id assert ret_msg.message_id
ret_msg = tb.send_photo(CHAT_ID, ret_msg.photo[0].file_id)
assert ret_msg.message_id
def test_send_audio(self): def test_send_audio(self):
file_data = open('./test_data/record.mp3', 'rb') file_data = open('./test_data/record.mp3', 'rb')
tb = telebot.TeleBot(TOKEN) tb = telebot.TeleBot(TOKEN)
@ -174,12 +169,12 @@ class TestTeleBot:
def test_is_string_unicode(self): def test_is_string_unicode(self):
s1 = u'string' s1 = u'string'
assert apihelper.is_string(s1) assert util.is_string(s1)
def test_is_string_string(self): def test_is_string_string(self):
s1 = 'string' s1 = 'string'
assert apihelper.is_string(s1) assert util.is_string(s1)
def test_not_string(self): def test_not_string(self):
i1 = 10 i1 = 10
assert not apihelper.is_string(i1) assert not util.is_string(i1)