Merge branch 'master' into jobqueue

This commit is contained in:
Jannes Höke 2016-01-04 00:01:27 +01:00
commit aa125e59fb
9 changed files with 187 additions and 87 deletions

7
.gitignore vendored
View file

@ -61,3 +61,10 @@ target/
# Sublime Text 2
*.sublime*
# unitests files
telegram.mp3
telegram.mp4
telegram.ogg
telegram.png
telegram.webp

View file

@ -9,6 +9,7 @@ python:
install:
- pip install pylint flake8 coveralls
- pip install -r requirements.txt
- 'if [[ $TRAVIS_PYTHON_VERSION == 2.6 ]]; then pip install unittest2; fi'
script:
- nosetests --with-coverage --cover-package telegram/
- flake8 telegram

View file

@ -19,6 +19,7 @@ The following wonderful people contributed directly or indirectly to this projec
- `macrojames <https://github.com/macrojames>`_
- `naveenvhegde <https://github.com/naveenvhegde>`_
- `njittam <https://github.com/njittam>`_
- `Noam Meltzer <https://github.com/tsnoam>`_
- `Rahiel Kasim <https://github.com/rahiel>`_
- `sooyhwang <https://github.com/sooyhwang>`_
- `wjt <https://github.com/wjt>`_

View file

@ -52,7 +52,7 @@ class Bot(TelegramObject):
def __init__(self,
token,
base_url=None):
self.token = token
self.token = self._valid_token(token)
if base_url is None:
self.base_url = 'https://api.telegram.org/bot%s' % self.token
@ -744,3 +744,11 @@ class Bot(TelegramObject):
def __reduce__(self):
return (self.__class__, (self.token,
self.base_url.replace(self.token, '')))
@staticmethod
def _valid_token(token):
"""a very basic validation on token"""
left, sep, _right = token.partition(':')
if (not sep) or (not left.isdigit()) or (len(left) < 3):
raise TelegramError('Invalid token')
return token

View file

@ -18,7 +18,22 @@
"""This module contains a object that represents a Telegram Error"""
import re
def _lstrip_str(in_s, lstr):
"""
Args:
in_s (str): in string
lstr (str): substr to strip from left side
Returns:
str:
"""
if in_s.startswith(lstr):
res = in_s[len(lstr):]
else:
res = in_s
return res
class TelegramError(Exception):
@ -26,16 +41,20 @@ class TelegramError(Exception):
def __init__(self, message):
"""
Args:
message (str):
Returns:
str:
"""
super(TelegramError, self).__init__()
api_error = re.match(r'^Error: (?P<message>.*)', message)
if api_error:
self.message = api_error.group('message').capitalize()
else:
self.message = message
msg = _lstrip_str(message, 'Error: ')
msg = _lstrip_str(msg, '[Error]: ')
if msg != message:
# api_error - capitalize the msg...
msg = msg.capitalize()
self.message = msg
def __str__(self):
return '%s' % (self.message)

View file

@ -64,15 +64,26 @@ class Updater:
Attributes:
Args:
token (str): The bots token given by the @BotFather
token (Optional[str]): The bot's token given by the @BotFather
base_url (Optional[str]):
workers (Optional[int]): Amount of threads in the thread pool for
functions decorated with @run_async
bot (Optional[Bot]):
Raises:
ValueError: If both `token` and `bot` are passed or none of them.
"""
def __init__(self, token, base_url=None, workers=4):
def __init__(self, token=None, base_url=None, workers=4, bot=None):
if (token is None) and (bot is None):
raise ValueError('`token` or `bot` must be passed')
if (token is not None) and (bot is not None):
raise ValueError('`token` and `bot` are mutually exclusive')
self.bot = Bot(token, base_url)
if bot is not None:
self.bot = bot
else:
self.bot = Bot(token, base_url)
self.update_queue = Queue()
self.dispatcher = Dispatcher(self.bot, self.update_queue,
workers=workers)

View file

@ -19,6 +19,7 @@
"""This module contains methods to make POST and GET requests"""
import functools
import json
import socket
from ssl import SSLError
@ -45,7 +46,11 @@ def _parse(json_data):
Returns:
A JSON parsed as Python dict with results.
"""
data = json.loads(json_data.decode())
decoded_s = json_data.decode('utf-8')
try:
data = json.loads(decoded_s)
except ValueError:
raise TelegramError('Invalid server response')
if not data.get('ok') and data.get('description'):
return data['description']
@ -53,6 +58,34 @@ def _parse(json_data):
return data['result']
def _try_except_req(func):
"""Decorator for requests to handle known exceptions"""
@functools.wraps(func)
def decorator(*args, **kwargs):
try:
return func(*args, **kwargs)
except HTTPError as error:
if error.getcode() == 403:
raise TelegramError('Unauthorized')
if error.getcode() == 502:
raise TelegramError('Bad Gateway')
try:
message = _parse(error.read())
except ValueError:
message = 'Unknown HTTPError {0}'.format(error.getcode())
raise TelegramError(message)
except (SSLError, socket.timeout) as error:
if "operation timed out" in str(error):
raise TelegramError("Timed out")
raise TelegramError(str(error))
return decorator
@_try_except_req
def get(url):
"""Request an URL.
Args:
@ -67,6 +100,7 @@ def get(url):
return _parse(result)
@_try_except_req
def post(url,
data,
network_delay=2.):
@ -91,39 +125,22 @@ def post(url,
else:
timeout = None
try:
if InputFile.is_inputfile(data):
data = InputFile(data)
request = Request(url,
data=data.to_form(),
headers=data.headers)
else:
data = json.dumps(data)
request = Request(url,
data=data.encode(),
headers={'Content-Type': 'application/json'})
if InputFile.is_inputfile(data):
data = InputFile(data)
request = Request(url,
data=data.to_form(),
headers=data.headers)
else:
data = json.dumps(data)
request = Request(url,
data=data.encode(),
headers={'Content-Type': 'application/json'})
result = urlopen(request, timeout=timeout).read()
except HTTPError as error:
if error.getcode() == 403:
raise TelegramError('Unauthorized')
if error.getcode() == 502:
raise TelegramError('Bad Gateway')
try:
message = _parse(error.read())
except ValueError:
message = 'Unknown HTTPError'
raise TelegramError(message)
except (SSLError, socket.timeout) as error:
if "operation timed out" in str(error):
raise TelegramError("Timed out")
raise TelegramError(str(error))
result = urlopen(request, timeout=timeout).read()
return _parse(result)
@_try_except_req
def download(url,
filename):
"""Download a file by its URL.

View file

@ -20,9 +20,14 @@
"""This module contains a object that represents Tests for Telegram Bot"""
import os
import unittest
from datetime import datetime
import sys
if sys.version_info[0:2] == (2, 6):
import unittest2 as unittest
else:
import unittest
sys.path.append('.')
import telegram
@ -137,5 +142,33 @@ class BotTest(BaseTest, unittest.TestCase):
self.assertTrue(self.is_json(upf.to_json()))
self.assertEqual(upf.photos[0][0].file_size, 12421)
def _test_invalid_token(self, token):
print('Testing invalid token: {0}'.format(token))
self.assertRaisesRegexp(telegram.TelegramError, 'Invalid token', telegram.Bot, token)
def testInvalidToken1(self):
self._test_invalid_token('123')
def testInvalidToken2(self):
self._test_invalid_token('12a:')
def testInvalidToken3(self):
self._test_invalid_token('12:')
def testUnauthToken(self):
print('Testing unauthorized token')
with self.assertRaisesRegexp(telegram.TelegramError, 'Unauthorized'):
bot = telegram.Bot('1234:abcd1234')
bot.getMe()
def testInvalidSrvResp(self):
print('Testing invalid server response')
with self.assertRaisesRegexp(telegram.TelegramError, 'Invalid server response'):
# bypass the valid token check
bot_cls = type('bot_cls', (telegram.Bot, ), {'_valid_token': lambda self, token: token})
bot = bot_cls('12')
bot.getMe()
if __name__ == '__main__':
unittest.main()

View file

@ -22,7 +22,6 @@ This module contains a object that represents Tests for Updater, Dispatcher,
WebhookServer and WebhookHandler
"""
import logging
import unittest
import sys
import re
import os
@ -31,6 +30,11 @@ from random import randrange
from time import sleep
from datetime import datetime
if sys.version_info[0:2] == (2, 6):
import unittest2 as unittest
else:
import unittest
try:
from urllib2 import urlopen, Request
except ImportError:
@ -38,7 +42,7 @@ except ImportError:
sys.path.append('.')
from telegram import Update, Message, TelegramError, User, Chat, Updater
from telegram import Update, Message, TelegramError, User, Chat, Updater, Bot
from telegram.dispatcher import run_async
from tests.base import BaseTest
from threading import Lock, Thread
@ -61,14 +65,18 @@ class UpdaterTest(BaseTest, unittest.TestCase):
"""
def setUp(self):
self.updater = Updater('', workers=2)
self.updater = None
self.received_message = None
self.message_count = 0
self.lock = Lock()
def _setup_updater(self, *args, **kwargs):
bot = MockBot(*args, **kwargs)
self.updater = Updater(workers=2, bot=bot)
def tearDown(self):
self.updater.stop()
if self.updater is not None:
self.updater.stop()
def reset(self):
self.message_count = 0
@ -119,8 +127,7 @@ class UpdaterTest(BaseTest, unittest.TestCase):
def test_addRemoveTelegramMessageHandler(self):
print('Testing add/removeTelegramMessageHandler')
bot = MockBot('Test')
self.updater.bot = bot
self._setup_updater('Test')
d = self.updater.dispatcher
d.addTelegramMessageHandler(
self.telegramHandlerTest)
@ -132,13 +139,13 @@ class UpdaterTest(BaseTest, unittest.TestCase):
d.removeTelegramMessageHandler(self.telegramHandlerTest)
self.reset()
bot.send_messages = 1
self.updater.bot.send_messages = 1
sleep(.1)
self.assertTrue(None is self.received_message)
def test_addTelegramMessageHandlerMultipleMessages(self):
print('Testing addTelegramMessageHandler and send 100 messages...')
self.updater.bot = MockBot('Multiple', 100)
self._setup_updater('Multiple', 100)
self.updater.dispatcher.addTelegramMessageHandler(
self.telegramHandlerTest)
self.updater.start_polling(0.0)
@ -148,8 +155,7 @@ class UpdaterTest(BaseTest, unittest.TestCase):
def test_addRemoveTelegramRegexHandler(self):
print('Testing add/removeStringRegexHandler')
bot = MockBot('Test2')
self.updater.bot = bot
self._setup_updater('Test2')
d = self.updater.dispatcher
regobj = re.compile('Te.*')
self.updater.dispatcher.addTelegramRegexHandler(regobj,
@ -162,14 +168,13 @@ class UpdaterTest(BaseTest, unittest.TestCase):
d.removeTelegramRegexHandler(regobj, self.telegramHandlerTest)
self.reset()
bot.send_messages = 1
self.updater.bot.send_messages = 1
sleep(.1)
self.assertTrue(None is self.received_message)
def test_addRemoveTelegramCommandHandler(self):
print('Testing add/removeTelegramCommandHandler')
bot = MockBot('/test')
self.updater.bot = bot
self._setup_updater('/test')
d = self.updater.dispatcher
self.updater.dispatcher.addTelegramCommandHandler(
'test', self.telegramHandlerTest)
@ -181,14 +186,13 @@ class UpdaterTest(BaseTest, unittest.TestCase):
d.removeTelegramCommandHandler('test', self.telegramHandlerTest)
self.reset()
bot.send_messages = 1
self.updater.bot.send_messages = 1
sleep(.1)
self.assertTrue(None is self.received_message)
def test_addRemoveUnknownTelegramCommandHandler(self):
print('Testing add/removeUnknownTelegramCommandHandler')
bot = MockBot('/test2')
self.updater.bot = bot
self._setup_updater('/test2')
d = self.updater.dispatcher
self.updater.dispatcher.addUnknownTelegramCommandHandler(
self.telegramHandlerTest)
@ -200,14 +204,13 @@ class UpdaterTest(BaseTest, unittest.TestCase):
d.removeUnknownTelegramCommandHandler(self.telegramHandlerTest)
self.reset()
bot.send_messages = 1
self.updater.bot.send_messages = 1
sleep(.1)
self.assertTrue(None is self.received_message)
def test_addRemoveStringRegexHandler(self):
print('Testing add/removeStringRegexHandler')
bot = MockBot('', messages=0)
self.updater.bot = bot
self._setup_updater('', messages=0)
d = self.updater.dispatcher
d.addStringRegexHandler('Te.*', self.stringHandlerTest)
queue = self.updater.start_polling(0.01)
@ -225,8 +228,7 @@ class UpdaterTest(BaseTest, unittest.TestCase):
def test_addRemoveStringCommandHandler(self):
print('Testing add/removeStringCommandHandler')
bot = MockBot('', messages=0)
self.updater.bot = bot
self._setup_updater('', messages=0)
d = self.updater.dispatcher
d.addStringCommandHandler(
'test3', self.stringHandlerTest)
@ -246,8 +248,7 @@ class UpdaterTest(BaseTest, unittest.TestCase):
def test_addRemoveUnknownStringCommandHandler(self):
print('Testing add/removeUnknownStringCommandHandler')
bot = MockBot('/test')
self.updater.bot = bot
self._setup_updater('/test')
d = self.updater.dispatcher
d.addUnknownStringCommandHandler(
self.stringHandlerTest)
@ -260,14 +261,13 @@ class UpdaterTest(BaseTest, unittest.TestCase):
d.removeUnknownStringCommandHandler(self.stringHandlerTest)
self.reset()
bot.send_messages = 1
self.updater.bot.send_messages = 1
sleep(.1)
self.assertTrue(None is self.received_message)
def test_addRemoveErrorHandler(self):
print('Testing add/removeErrorHandler')
bot = MockBot('', messages=0)
self.updater.bot = bot
self._setup_updater('', messages=0)
d = self.updater.dispatcher
d.addErrorHandler(self.errorHandlerTest)
queue = self.updater.start_polling(0.01)
@ -286,8 +286,7 @@ class UpdaterTest(BaseTest, unittest.TestCase):
def test_errorInHandler(self):
print('Testing error in Handler')
bot = MockBot('', messages=0)
self.updater.bot = bot
self._setup_updater('', messages=0)
d = self.updater.dispatcher
d.addStringRegexHandler('.*',
self.errorRaisingHandlerTest)
@ -300,8 +299,7 @@ class UpdaterTest(BaseTest, unittest.TestCase):
def test_errorOnGetUpdates(self):
print('Testing error on getUpdates')
bot = MockBot('', raise_error=True)
self.updater.bot = bot
self._setup_updater('', raise_error=True)
d = self.updater.dispatcher
d.addErrorHandler(self.errorHandlerTest)
self.updater.start_polling(0.01)
@ -310,8 +308,7 @@ class UpdaterTest(BaseTest, unittest.TestCase):
def test_addRemoveTypeHandler(self):
print('Testing add/removeTypeHandler')
bot = MockBot('', messages=0)
self.updater.bot = bot
self._setup_updater('', messages=0)
d = self.updater.dispatcher
d.addTypeHandler(dict, self.stringHandlerTest)
queue = self.updater.start_polling(0.01)
@ -330,8 +327,7 @@ class UpdaterTest(BaseTest, unittest.TestCase):
def test_runAsync(self):
print('Testing @run_async')
bot = MockBot('Test5', messages=2)
self.updater.bot = bot
self._setup_updater('Test5', messages=2)
d = self.updater.dispatcher
d.addTelegramMessageHandler(
self.asyncHandlerTest)
@ -342,7 +338,7 @@ class UpdaterTest(BaseTest, unittest.TestCase):
def test_additionalArgs(self):
print('Testing additional arguments for handlers')
self.updater.bot = MockBot('', messages=0)
self._setup_updater('', messages=0)
self.updater.dispatcher.addStringCommandHandler(
'test5', self.additionalArgsTest)
@ -354,8 +350,7 @@ class UpdaterTest(BaseTest, unittest.TestCase):
def test_regexGroupHandler(self):
print('Testing optional groups and groupdict parameters')
bot = MockBot('', messages=0)
self.updater.bot = bot
self._setup_updater('', messages=0)
d = self.updater.dispatcher
d.addStringRegexHandler('^(This).*?(?P<testgroup>regex group).*',
self.regexGroupHandlerTest)
@ -368,8 +363,7 @@ class UpdaterTest(BaseTest, unittest.TestCase):
def test_runAsyncWithAdditionalArgs(self):
print('Testing @run_async with additional parameters')
bot = MockBot('Test6', messages=2)
self.updater.bot = bot
self._setup_updater('Test6', messages=2)
d = self.updater.dispatcher
d.addTelegramMessageHandler(
self.asyncAdditionalHandlerTest)
@ -380,8 +374,7 @@ class UpdaterTest(BaseTest, unittest.TestCase):
def test_webhook(self):
print('Testing Webhook')
bot = MockBot('', messages=0)
self.updater.bot = bot
self._setup_updater('', messages=0)
d = self.updater.dispatcher
d.addTelegramMessageHandler(
self.telegramHandlerTest)
@ -442,8 +435,7 @@ class UpdaterTest(BaseTest, unittest.TestCase):
def test_webhook_no_ssl(self):
print('Testing Webhook without SSL')
bot = MockBot('', messages=0)
self.updater.bot = bot
self._setup_updater('', messages=0)
d = self.updater.dispatcher
d.addTelegramMessageHandler(
self.telegramHandlerTest)
@ -485,7 +477,7 @@ class UpdaterTest(BaseTest, unittest.TestCase):
def test_idle(self):
print('Testing idle')
self.updater.bot = MockBot('Test6', messages=0)
self._setup_updater('Test6', messages=0)
self.updater.start_polling(poll_interval=0.01)
Thread(target=self.signalsender).start()
self.updater.idle()
@ -493,6 +485,17 @@ class UpdaterTest(BaseTest, unittest.TestCase):
sleep(1)
self.updater.running = False
def test_createBot(self):
updater = Updater('123:abcd')
self.assertIsNotNone(updater.bot)
def test_mutualExclusiveTokenBot(self):
bot = Bot('123:zyxw')
self.assertRaises(ValueError, Updater, token='123:abcd', bot=bot)
def test_noTokenOrBot(self):
self.assertRaises(ValueError, Updater)
class MockBot: