Improving the design of existing Telegram classes and adding docstrings

This commit is contained in:
Leandro Toledo 2015-08-28 12:19:30 -03:00
parent ce58f72566
commit b20f5af1e1
7 changed files with 242 additions and 61 deletions

View file

@ -16,6 +16,7 @@
# You should have received a copy of the GNU Lesser Public License # You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/]. # along with this program. If not, see [http://www.gnu.org/licenses/].
"""A library that provides a Python interface to the Telegram Bot API"""
__author__ = 'leandrotoledodesouza@gmail.com' __author__ = 'leandrotoledodesouza@gmail.com'
__version__ = '2.7.1' __version__ = '2.7.1'

View file

@ -16,13 +16,14 @@
# You should have received a copy of the GNU Lesser Public License # You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/]. # along with this program. If not, see [http://www.gnu.org/licenses/].
"""Base class for Telegram Objects"""
import json import json
from abc import ABCMeta, abstractmethod from abc import ABCMeta, abstractmethod
class TelegramObject(object): class TelegramObject(object):
"""Base class for most telegram object""" """Base class for most telegram objects"""
__metaclass__ = ABCMeta __metaclass__ = ABCMeta
@ -34,11 +35,26 @@ class TelegramObject(object):
@staticmethod @staticmethod
def de_json(data): def de_json(data):
"""
Args:
data (str):
Returns:
telegram.TelegramObject:
"""
raise NotImplementedError raise NotImplementedError
def to_json(self): def to_json(self):
"""
Returns:
str:
"""
return json.dumps(self.to_dict()) return json.dumps(self.to_dict())
@abstractmethod @abstractmethod
def to_dict(self): def to_dict(self):
"""
Returns:
dict:
"""
return None return None

View file

@ -1,4 +1,5 @@
#!/usr/bin/env python #!/usr/bin/env python
# pylint: disable=E0611,E0213,E1102,C0103,E1101,W0613,R0913,R0904
# #
# A library that provides a Python interface to the Telegram Bot API # A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015 Leandro Toledo de Souza <leandrotoeldodesouza@gmail.com> # Copyright (C) 2015 Leandro Toledo de Souza <leandrotoeldodesouza@gmail.com>
@ -16,6 +17,7 @@
# You should have received a copy of the GNU Lesser Public License # You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/]. # along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram Bot"""
import json import json
try: try:
@ -32,11 +34,27 @@ import logging
from telegram import (User, Message, Update, UserProfilePhotos, TelegramError, from telegram import (User, Message, Update, UserProfilePhotos, TelegramError,
ReplyMarkup, InputFile, TelegramObject, NullHandler) ReplyMarkup, InputFile, TelegramObject, NullHandler)
h = NullHandler() H = NullHandler()
logging.getLogger(__name__).addHandler(h) logging.getLogger(__name__).addHandler(H)
class Bot(TelegramObject): class Bot(TelegramObject):
"""This object represents a Telegram Bot.
Attributes:
id (int):
first_name (str):
last_name (str):
username (str):
name (str):
Args:
token (str):
**kwargs: Arbitrary keyword arguments.
Keyword Args:
base_url (Optional[str]):
"""
def __init__(self, def __init__(self,
token, token,
@ -50,11 +68,17 @@ class Bot(TelegramObject):
self.bot = None self.bot = None
self.log = logging.getLogger(__name__) self.logger = logging.getLogger(__name__)
def info(func): def info(func):
"""
bla
"""
@functools.wraps(func) @functools.wraps(func)
def decorator(self, *args, **kwargs): def decorator(self, *args, **kwargs):
"""
bla
"""
if not self.bot: if not self.bot:
self.getMe() self.getMe()
@ -65,36 +89,48 @@ class Bot(TelegramObject):
@property @property
@info @info
def id(self): def id(self):
"""int: """
return self.bot.id return self.bot.id
@property @property
@info @info
def first_name(self): def first_name(self):
"""str: """
return self.bot.first_name return self.bot.first_name
@property @property
@info @info
def last_name(self): def last_name(self):
"""str: """
return self.bot.last_name return self.bot.last_name
@property @property
@info @info
def username(self): def username(self):
"""str: """
return self.bot.username return self.bot.username
@property @property
def name(self): def name(self):
"""str: """
return '@%s' % self.username return '@%s' % self.username
def log(func): def log(func):
"""
Returns:
A telegram.Message instance representing the message posted.
"""
logger = logging.getLogger(func.__module__) logger = logging.getLogger(func.__module__)
@functools.wraps(func) @functools.wraps(func)
def decorator(self, *args, **kwargs): def decorator(self, *args, **kwargs):
logger.debug('Entering: %s' % func.__name__) """
decorator
"""
logger.debug('Entering: %s', func.__name__)
result = func(self, *args, **kwargs) result = func(self, *args, **kwargs)
logger.debug(result) logger.debug(result)
logger.debug('Exiting: %s' % func.__name__) logger.debug('Exiting: %s', func.__name__)
return result return result
return decorator return decorator
@ -105,6 +141,9 @@ class Bot(TelegramObject):
""" """
@functools.wraps(func) @functools.wraps(func)
def decorator(self, *args, **kwargs): def decorator(self, *args, **kwargs):
"""
decorator
"""
url, data = func(self, *args, **kwargs) url, data = func(self, *args, **kwargs)
if kwargs.get('reply_to_message_id'): if kwargs.get('reply_to_message_id'):
@ -118,8 +157,8 @@ class Bot(TelegramObject):
else: else:
data['reply_markup'] = reply_markup data['reply_markup'] = reply_markup
json_data = self._requestUrl(url, 'POST', data=data) json_data = Bot._requestUrl(url, 'POST', data=data)
data = self._parseAndCheckTelegram(json_data) data = Bot._parseAndCheckTelegram(json_data)
if data is True: if data is True:
return data return data
@ -150,8 +189,7 @@ class Bot(TelegramObject):
chat_id, chat_id,
text, text,
disable_web_page_preview=None, disable_web_page_preview=None,
reply_to_message_id=None, **kwargs):
reply_markup=None):
"""Use this method to send text messages. """Use this method to send text messages.
Args: Args:
@ -222,8 +260,7 @@ class Bot(TelegramObject):
chat_id, chat_id,
photo, photo,
caption=None, caption=None,
reply_to_message_id=None, **kwargs):
reply_markup=None):
"""Use this method to send photos. """Use this method to send photos.
Args: Args:
@ -265,8 +302,7 @@ class Bot(TelegramObject):
duration=None, duration=None,
performer=None, performer=None,
title=None, title=None,
reply_to_message_id=None, **kwargs):
reply_markup=None):
"""Use this method to send audio files, if you want Telegram clients to """Use this method to send audio files, if you want Telegram clients to
display them in the music player. Your audio must be in an .mp3 format. display them in the music player. Your audio must be in an .mp3 format.
On success, the sent Message is returned. Bots can currently send audio On success, the sent Message is returned. Bots can currently send audio
@ -321,8 +357,7 @@ class Bot(TelegramObject):
def sendDocument(self, def sendDocument(self,
chat_id, chat_id,
document, document,
reply_to_message_id=None, **kwargs):
reply_markup=None):
"""Use this method to send general files. """Use this method to send general files.
Args: Args:
@ -355,8 +390,7 @@ class Bot(TelegramObject):
def sendSticker(self, def sendSticker(self,
chat_id, chat_id,
sticker, sticker,
reply_to_message_id=None, **kwargs):
reply_markup=None):
"""Use this method to send .webp stickers. """Use this method to send .webp stickers.
Args: Args:
@ -391,8 +425,7 @@ class Bot(TelegramObject):
video, video,
duration=None, duration=None,
caption=None, caption=None,
reply_to_message_id=None, **kwargs):
reply_markup=None):
"""Use this method to send video files, Telegram clients support mp4 """Use this method to send video files, Telegram clients support mp4
videos (other formats may be sent as telegram.Document). videos (other formats may be sent as telegram.Document).
@ -437,8 +470,7 @@ class Bot(TelegramObject):
chat_id, chat_id,
voice, voice,
duration=None, duration=None,
reply_to_message_id=None, **kwargs):
reply_markup=None):
"""Use this method to send audio files, if you want Telegram clients to """Use this method to send audio files, if you want Telegram clients to
display the file as a playable voice message. For this to work, your display the file as a playable voice message. For this to work, your
audio must be in an .ogg file encoded with OPUS (other formats may be audio must be in an .ogg file encoded with OPUS (other formats may be
@ -482,8 +514,7 @@ class Bot(TelegramObject):
chat_id, chat_id,
latitude, latitude,
longitude, longitude,
reply_to_message_id=None, **kwargs):
reply_markup=None):
"""Use this method to send point on the map. """Use this method to send point on the map.
Args: Args:
@ -617,10 +648,10 @@ class Bot(TelegramObject):
data = self._parseAndCheckTelegram(json_data) data = self._parseAndCheckTelegram(json_data)
if data: if data:
self.log.info( self.logger.info(
'Getting updates: %s' % [u['update_id'] for u in data]) 'Getting updates: %s', [u['update_id'] for u in data])
else: else:
self.log.info('No new updates found.') self.logger.info('No new updates found.')
return [Update.de_json(x) for x in data] return [Update.de_json(x) for x in data]
@ -650,8 +681,8 @@ class Bot(TelegramObject):
return True return True
def _requestUrl(self, @staticmethod
url, def _requestUrl(url,
method, method,
data=None): data=None):
"""Request an URL. """Request an URL.
@ -688,12 +719,12 @@ class Bot(TelegramObject):
url, url,
urlencode(data).encode() urlencode(data).encode()
).read() ).read()
except IOError as e:
raise TelegramError(str(e))
except HTTPError as e: except HTTPError as e:
raise TelegramError(str(e)) raise TelegramError(str(e))
except URLError as e: except URLError as e:
raise TelegramError(str(e)) raise TelegramError(str(e))
except IOError as e:
raise TelegramError(str(e))
if method == 'GET': if method == 'GET':
try: try:
@ -701,8 +732,8 @@ class Bot(TelegramObject):
except URLError as e: except URLError as e:
raise TelegramError(str(e)) raise TelegramError(str(e))
def _parseAndCheckTelegram(self, @staticmethod
json_data): def _parseAndCheckTelegram(json_data):
"""Try and parse the JSON returned from Telegram and return an empty """Try and parse the JSON returned from Telegram and return an empty
dictionary if there is any error. dictionary if there is any error.
@ -725,7 +756,15 @@ class Bot(TelegramObject):
return data['result'] return data['result']
@staticmethod
def de_json(data):
pass
def to_dict(self): def to_dict(self):
"""
Returns:
dict:
"""
data = {'id': self.id, data = {'id': self.id,
'username': self.username, 'username': self.username,
'first_name': self.username} 'first_name': self.username}

View file

@ -16,24 +16,63 @@
# You should have received a copy of the GNU Lesser Public License # You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/]. # along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram ForceReply"""
from telegram import ReplyMarkup from telegram import ReplyMarkup
class ForceReply(ReplyMarkup): class ForceReply(ReplyMarkup):
"""This object represents a Telegram ForceReply.
Attributes:
force_reply (bool):
selective (bool):
Args:
force_reply (bool):
**kwargs: Arbitrary keyword arguments.
Keyword Args:
selective (Optional[bool]):
"""
def __init__(self, def __init__(self,
force_reply=True, force_reply=True,
selective=None): **kwargs):
self.force_reply = force_reply # Required
self.selective = selective self.force_reply = bool(force_reply)
# Optionals
self.selective = bool(kwargs.get('selective', False))
@staticmethod @staticmethod
def de_json(data): def de_json(data):
return ForceReply(force_reply=data.get('force_reply', None), """
selective=data.get('selective', None)) Args:
data (str):
Returns:
telegram.ForceReply:
"""
force_reply = dict()
# Required
force_reply['force_reply'] = data['force_reply']
# Optionals
force_reply['selective'] = data.get('selective', False)
return ForceReply(**force_reply)
def to_dict(self): def to_dict(self):
data = {'force_reply': self.force_reply} """
Returns:
dict:
"""
data = dict()
# Required
data['force_reply'] = self.force_reply
# Optionals
if self.selective: if self.selective:
data['selective'] = self.selective data['selective'] = self.selective
return data return data

View file

@ -16,24 +16,64 @@
# You should have received a copy of the GNU Lesser Public License # You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/]. # along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram
ReplyKeyboardHide"""
from telegram import ReplyMarkup from telegram import ReplyMarkup
class ReplyKeyboardHide(ReplyMarkup): class ReplyKeyboardHide(ReplyMarkup):
"""This object represents a Telegram ReplyKeyboardHide.
Attributes:
hide_keyboard (bool):
selective (bool):
Args:
hide_keyboard (bool):
**kwargs: Arbitrary keyword arguments.
Keyword Args:
selective (Optional[bool]):
"""
def __init__(self, def __init__(self,
hide_keyboard=True, hide_keyboard=True,
selective=None): **kwargs):
self.hide_keyboard = hide_keyboard # Required
self.selective = selective self.hide_keyboard = bool(hide_keyboard)
# Optionals
self.selective = bool(kwargs.get('selective', False))
@staticmethod @staticmethod
def de_json(data): def de_json(data):
return ReplyKeyboardHide(hide_keyboard=data.get('hide_keyboard', None), """
selective=data.get('selective', None)) Args:
data (str):
Returns:
telegram.ReplyKeyboardHide:
"""
rkh = dict()
# Required
rkh['hide_keyboard'] = data['hide_keyboard']
# Optionals
rkh['selective'] = data.get('selective', False)
return ReplyKeyboardHide(**rkh)
def to_dict(self): def to_dict(self):
data = {'hide_keyboard': self.hide_keyboard} """
Returns:
dict:
"""
data = dict()
# Required
data['hide_keyboard'] = self.hide_keyboard
# Optionals
if self.selective: if self.selective:
data['selective'] = self.selective data['selective'] = self.selective
return data return data

View file

@ -16,38 +16,76 @@
# You should have received a copy of the GNU Lesser Public License # You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/]. # along with this program. If not, see [http://www.gnu.org/licenses/].
"""This module contains a object that represents a Telegram
ReplyKeyboardMarkup"""
from telegram import ReplyMarkup from telegram import ReplyMarkup
class ReplyKeyboardMarkup(ReplyMarkup): class ReplyKeyboardMarkup(ReplyMarkup):
"""This object represents a Telegram ReplyKeyboardMarkup.
Attributes:
keyboard (List[List[str]]):
resize_keyboard (bool):
one_time_keyboard (bool):
selective (bool):
Args:
keyboard (List[List[str]]):
**kwargs: Arbitrary keyword arguments.
Keyword Args:
resize_keyboard (Optional[bool]):
one_time_keyboard (Optional[bool]):
selective (Optional[bool]):
"""
def __init__(self, def __init__(self,
keyboard, keyboard,
resize_keyboard=None, **kwargs):
one_time_keyboard=None, # Required
selective=None):
self.keyboard = keyboard self.keyboard = keyboard
self.resize_keyboard = resize_keyboard # Optionals
self.one_time_keyboard = one_time_keyboard self.resize_keyboard = bool(kwargs.get('resize_keyboard', False))
self.selective = selective self.one_time_keyboard = bool(kwargs.get('one_time_keyboard', False))
self.selective = bool(kwargs.get('selective', False))
@staticmethod @staticmethod
def de_json(data): def de_json(data):
return ReplyKeyboardMarkup(keyboard=data.get('keyboard', None), """
resize_keyboard=data.get( Args:
'resize_keyboard', None data (str):
),
one_time_keyboard=data.get( Returns:
'one_time_keyboard', None telegram.ReplyKeyboardMarkup:
), """
selective=data.get('selective', None)) rkm = dict()
# Required
rkm['keyboard'] = data['keyboard']
# Optionals
rkm['resize_keyboard'] = data.get('resize_keyboard', False)
rkm['one_time_keyboard'] = data.get('one_time_keyboard', False)
rkm['selective'] = data.get('selective', False)
return ReplyKeyboardMarkup(**rkm)
def to_dict(self): def to_dict(self):
data = {'keyboard': self.keyboard} """
Returns:
dict:
"""
data = dict()
# Required
data['keyboard'] = self.keyboard
# Optionals
if self.resize_keyboard: if self.resize_keyboard:
data['resize_keyboard'] = self.resize_keyboard data['resize_keyboard'] = self.resize_keyboard
if self.one_time_keyboard: if self.one_time_keyboard:
data['one_time_keyboard'] = self.one_time_keyboard data['one_time_keyboard'] = self.one_time_keyboard
if self.selective: if self.selective:
data['selective'] = self.selective data['selective'] = self.selective
return data return data

View file

@ -16,9 +16,17 @@
# You should have received a copy of the GNU Lesser Public License # You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/]. # along with this program. If not, see [http://www.gnu.org/licenses/].
"""Base class for Telegram ReplyMarkup Objects"""
from telegram import TelegramObject from telegram import TelegramObject
class ReplyMarkup(TelegramObject): class ReplyMarkup(TelegramObject):
pass """Base class for Telegram ReplyMarkup Objects"""
@staticmethod
def de_json(data):
pass
def to_dict(self):
pass