diff --git a/.travis.yml b/.travis.yml index d69714ccf..c1a8cb8c0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,9 @@ language: python python: - - 2.7 + - "2.6" + - "2.7" + - "3.2" + - "3.3" + - "3.4" + - "nightly" script: make test diff --git a/telegram/bot.py b/telegram/bot.py index 32eae2e19..eec1c81db 100644 --- a/telegram/bot.py +++ b/telegram/bot.py @@ -4,8 +4,14 @@ """A library that provides a Python interface to the Telegram Bot API""" import json -import urllib -import urllib2 +try: + from urllib.parse import urlencode + from urllib.request import urlopen, Request + from urllib.error import HTTPError, URLError +except ImportError: + from urllib import urlencode + from urllib2 import urlopen, Request + from urllib2 import HTTPError, URLError import functools from telegram import (User, Message, Update, UserProfilePhotos, TelegramError, @@ -28,30 +34,22 @@ class Bot(object): try: bot = self.getMe() - self._id = bot.id - self._first_name = bot.first_name - self._last_name = bot.last_name - self._username = bot.username + self.id = bot.id + self.first_name = bot.first_name + self.last_name = bot.last_name + self.username = bot.username self.__auth = True except TelegramError: raise TelegramError({'message': 'Bad token'}) @property - def id(self): - return self._id - - @property - def first_name(self): - return self._first_name - - @property - def last_name(self): - return self._last_name - - @property - def username(self): - return self._username + def name(self): + if self.username: + return '@%s' % self.username + if self.last_name: + return '%s %s' % (self.first_name, self.last_name) + return self.first_name def clearCredentials(self): """Clear any credentials for this instance. @@ -65,7 +63,7 @@ class Bot(object): A telegram.User instance representing that bot if the credentials are valid, None otherwise. """ - url = '%s/getMe' % (self.base_url) + url = '%s/getMe' % self.base_url json_data = self._requestUrl(url, 'GET') data = self._parseAndCheckTelegram(json_data) @@ -141,7 +139,7 @@ class Bot(object): A telegram.Message instance representing the message posted. """ - url = '%s/sendMessage' % (self.base_url) + url = '%s/sendMessage' % self.base_url data = {'chat_id': chat_id, 'text': text} @@ -149,7 +147,7 @@ class Bot(object): if disable_web_page_preview: data['disable_web_page_preview'] = disable_web_page_preview - return (url, data) + return url, data @message @require_authentication @@ -172,7 +170,7 @@ class Bot(object): A telegram.Message instance representing the message forwarded. """ - url = '%s/forwardMessage' % (self.base_url) + url = '%s/forwardMessage' % self.base_url data = {} if chat_id: @@ -182,7 +180,7 @@ class Bot(object): if message_id: data['message_id'] = message_id - return (url, data) + return url, data @message @require_authentication @@ -215,7 +213,7 @@ class Bot(object): A telegram.Message instance representing the message posted. """ - url = '%s/sendPhoto' % (self.base_url) + url = '%s/sendPhoto' % self.base_url data = {'chat_id': chat_id, 'photo': photo} @@ -223,7 +221,7 @@ class Bot(object): if caption: data['caption'] = caption - return (url, data) + return url, data @message @require_authentication @@ -255,12 +253,12 @@ class Bot(object): A telegram.Message instance representing the message posted. """ - url = '%s/sendAudio' % (self.base_url) + url = '%s/sendAudio' % self.base_url data = {'chat_id': chat_id, 'audio': audio} - return (url, data) + return url, data @message @require_authentication @@ -289,12 +287,12 @@ class Bot(object): A telegram.Message instance representing the message posted. """ - url = '%s/sendDocument' % (self.base_url) + url = '%s/sendDocument' % self.base_url data = {'chat_id': chat_id, 'document': document} - return (url, data) + return url, data @message @require_authentication @@ -323,12 +321,12 @@ class Bot(object): A telegram.Message instance representing the message posted. """ - url = '%s/sendSticker' % (self.base_url) + url = '%s/sendSticker' % self.base_url data = {'chat_id': chat_id, 'sticker': sticker} - return (url, data) + return url, data @message @require_authentication @@ -358,12 +356,12 @@ class Bot(object): A telegram.Message instance representing the message posted. """ - url = '%s/sendVideo' % (self.base_url) + url = '%s/sendVideo' % self.base_url data = {'chat_id': chat_id, 'video': video} - return (url, data) + return url, data @message @require_authentication @@ -393,13 +391,13 @@ class Bot(object): A telegram.Message instance representing the message posted. """ - url = '%s/sendLocation' % (self.base_url) + url = '%s/sendLocation' % self.base_url data = {'chat_id': chat_id, 'latitude': latitude, 'longitude': longitude} - return (url, data) + return url, data @message @require_authentication @@ -425,12 +423,12 @@ class Bot(object): - ChatAction.FIND_LOCATION for location data. """ - url = '%s/sendChatAction' % (self.base_url) + url = '%s/sendChatAction' % self.base_url data = {'chat_id': chat_id, 'action': action} - return (url, data) + return url, data @require_authentication def getUserProfilePhotos(self, @@ -453,7 +451,7 @@ class Bot(object): Returns a telegram.UserProfilePhotos object. """ - url = '%s/getUserProfilePhotos' % (self.base_url) + url = '%s/getUserProfilePhotos' % self.base_url data = {'user_id': user_id} @@ -492,7 +490,7 @@ class Bot(object): A list of telegram.Update objects are returned. """ - url = '%s/getUpdates' % (self.base_url) + url = '%s/getUpdates' % self.base_url data = {} if offset: @@ -524,7 +522,7 @@ class Bot(object): Returns: True if successful else TelegramError was raised """ - url = '%s/setWebhook' % (self.base_url) + url = '%s/setWebhook' % self.base_url data = {'url': webhook_url} @@ -556,29 +554,29 @@ class Bot(object): if InputFile.is_inputfile(data): data = InputFile(data) - request = urllib2.Request( + request = Request( url, data=data.to_form(), headers=data.headers ) - return urllib2.urlopen(request).read() + return urlopen(request).read() else: - return urllib2.urlopen( + return urlopen( url, - urllib.urlencode(data) + urlencode(data).encode() ).read() except IOError as e: raise TelegramError(str(e)) - except urllib2.HTTPError as e: + except HTTPError as e: raise TelegramError(str(e)) - except urllib2.URLError as e: + except URLError as e: raise TelegramError(str(e)) if method == 'GET': try: - return urllib2.urlopen(url).read() - except urllib2.URLError as e: + return urlopen(url).read() + except URLError as e: raise TelegramError(str(e)) return 0 # if not a POST or GET request diff --git a/telegram/inputfile.py b/telegram/inputfile.py index 4fc8f15e5..33e6ca5a1 100644 --- a/telegram/inputfile.py +++ b/telegram/inputfile.py @@ -1,11 +1,16 @@ #!/usr/bin/env python - -import mimetools +try: + from email.generator import _make_boundary as choose_boundary + from urllib.request import urlopen + from io import BufferedReader as file +except ImportError: + from mimetools import choose_boundary + from urllib2 import urlopen import mimetypes import os import re -import urllib2 +import sys from .error import TelegramError @@ -18,7 +23,7 @@ class InputFile(object): def __init__(self, data): self.data = data - self.boundary = mimetools.choose_boundary() + self.boundary = choose_boundary() if 'audio' in data: self.input_name = 'audio' @@ -40,7 +45,7 @@ class InputFile(object): DEFAULT_MIME_TYPE if 'http' in self.input_file: - self.input_file_content = urllib2.urlopen(self.input_file).read() + self.input_file_content = urlopen(self.input_file).read() self.mimetype = InputFile.is_image(self.input_file_content) self.filename = self.mimetype.replace('/', '.') @@ -58,10 +63,10 @@ class InputFile(object): form_boundary = '--' + self.boundary # Add data fields - for name, value in self.data.iteritems(): + for name, value in self.data.items(): form.extend([ form_boundary, - str('Content-Disposition: form-data; name="%s"' % name), + 'Content-Disposition: form-data; name="%s"' % name, '', str(value) ]) @@ -69,9 +74,9 @@ class InputFile(object): # Add input_file to upload form.extend([ form_boundary, - str('Content-Disposition: form-data; name="%s"; filename="%s"' % ( + 'Content-Disposition: form-data; name="%s"; filename="%s"' % ( self.input_name, self.filename - )), + ), 'Content-Type: %s' % self.mimetype, '', self.input_file_content @@ -80,6 +85,19 @@ class InputFile(object): form.append('--' + self.boundary + '--') form.append('') + return self._parse(form) + + def _parse(self, form): + if sys.version_info > (3,): + # on Python 3 form needs to be byte encoded + encoded_form = [] + for item in form: + try: + encoded_form.append(item.encode()) + except AttributeError: + encoded_form.append(item) + + return b'\r\n'.join(encoded_form) return '\r\n'.join(form) @staticmethod @@ -96,14 +114,14 @@ class InputFile(object): try: header = stream[:10] - if re.match(r'GIF8', header): + if re.match(b'GIF8', header): return 'image/gif' - if re.match(r'\x89PNG', header): + if re.match(b'\x89PNG', header): return 'image/png' - if re.match(r'\xff\xd8\xff\xe0\x00\x10JFIF', header) or \ - re.match(r'\xff\xd8\xff\xe1(.*){2}Exif', header): + if re.match(b'\xff\xd8\xff\xe0\x00\x10JFIF', header) or \ + re.match(b'\xff\xd8\xff\xe1(.*){2}Exif', header): return 'image/jpeg' except IndexError as e: raise TelegramError(str(e)) @@ -122,7 +140,7 @@ class InputFile(object): """ if data: file_types = ['audio', 'document', 'photo', 'video'] - file_type = [i for i in data.keys() if i in file_types] + file_type = [i for i in list(data.keys()) if i in file_types] if file_type: file_content = data[file_type[0]] diff --git a/telegram/message.py b/telegram/message.py index 4bdb44a76..3668b20ce 100644 --- a/telegram/message.py +++ b/telegram/message.py @@ -83,7 +83,7 @@ class Message(object): reply_to_message = None if 'text' in data: - text = data['text'].encode('utf-8') + text = data['text'] else: text = None diff --git a/telegram/user.py b/telegram/user.py index 50f3e98ef..bcd1e4bb4 100644 --- a/telegram/user.py +++ b/telegram/user.py @@ -15,6 +15,14 @@ class User(object): self.last_name = last_name self.username = username + @property + def name(self): + if self.username: + return '@%s' % self.username + if self.last_name: + return '%s %s' % (self.first_name, self.last_name) + return self.first_name + @staticmethod def de_json(data): return User(id=data.get('id', None), diff --git a/tests/test_bot.py b/tests/test_bot.py index 270bea854..e4417b45a 100644 --- a/tests/test_bot.py +++ b/tests/test_bot.py @@ -1,6 +1,5 @@ #!/usr/bin/env python # encoding: utf-8 - import os import telegram import unittest @@ -27,7 +26,7 @@ class BotTest(unittest.TestCase): print('Testing sendMessage') message = self._bot.sendMessage(chat_id=12173560, text='Моё судно на воздушной подушке полно угрей') - self.assertEqual('Моё судно на воздушной подушке полно угрей', message.text) + self.assertEqual(u'Моё судно на воздушной подушке полно угрей', message.text) def testGetUpdates(self): '''Test the telegram.Bot getUpdates method'''