From 06d3569fbd204a69570d97776dec7da13ec947ef Mon Sep 17 00:00:00 2001 From: Davte Date: Mon, 6 Apr 2020 22:20:17 +0200 Subject: [PATCH] IDE-guided adjustments --- davtelepot/__init__.py | 2 +- davtelepot/api.py | 35 ++++++++++++++++++----------------- davtelepot/bot.py | 24 ++++++++++++++++-------- davtelepot/database.py | 6 +++--- 4 files changed, 38 insertions(+), 29 deletions(-) diff --git a/davtelepot/__init__.py b/davtelepot/__init__.py index f7c24ca..8f61fad 100644 --- a/davtelepot/__init__.py +++ b/davtelepot/__init__.py @@ -14,7 +14,7 @@ __author__ = "Davide Testa" __email__ = "davide@davte.it" __credits__ = ["Marco Origlia", "Nick Lee @Nickoala"] __license__ = "GNU General Public License v3.0" -__version__ = "2.4.14" +__version__ = "2.4.15" __maintainer__ = "Davide Testa" __contact__ = "t.me/davte" diff --git a/davtelepot/api.py b/davtelepot/api.py index ea105bc..a8de1e7 100644 --- a/davtelepot/api.py +++ b/davtelepot/api.py @@ -18,7 +18,7 @@ from aiohttp import web class TelegramError(Exception): """Telegram API exceptions class.""" - def __init__(self, error_code=0, description=None, ok=False): + def __init__(self, error_code=0, description=None): """Get an error response and return corresponding Exception.""" self._code = error_code if description is None: @@ -38,7 +38,9 @@ class TelegramError(Exception): return f"Error {self.code}: {self._description}" -class TelegramBot(object): +# This class needs to mirror Telegram API, so camelCase method are needed +# noinspection PyPep8Naming +class TelegramBot: """Provide python method having the same signature as Telegram API methods. All mirrored methods are camelCase. @@ -65,12 +67,13 @@ class TelegramBot(object): self._token = token self.sessions = dict() self._flood_wait = 0 - self.last_sending_time = dict( - absolute=( + self.last_sending_time = { + 'absolute': ( datetime.datetime.now() - self.absolute_cooldown_timedelta - ) - ) + ), + 0: [] # Each `telegram_id` key has a list of `datetime.datetime` as value + } @property def token(self): @@ -139,14 +142,16 @@ class TelegramBot(object): return response['result'] @staticmethod - def adapt_parameters(parameters, exclude=[]): + def adapt_parameters(parameters, exclude=None): """Build a aiohttp.FormData object from given `paramters`. Exclude `self`, empty values and parameters in `exclude` list. Cast integers to string to avoid TypeError during json serialization. """ + if exclude is None: + exclude = [] exclude.append('self') - # quote_fields must be set to False, otherwise filenames cause troubles + # quote_fields must be set to False, otherwise some file names cause troubles data = aiohttp.FormData(quote_fields=False) for key, value in parameters.items(): if not (key in exclude or value is None): @@ -260,13 +265,17 @@ class TelegramBot(object): self.last_sending_time['absolute'] = now() return - async def api_request(self, method, parameters={}, exclude=[]): + async def api_request(self, method, parameters=None, exclude=None): """Return the result of a Telegram bot API request, or an Exception. Opened sessions will be used more than one time (if appropriate) and will be closed on `Bot.app.cleanup`. Result may be a Telegram API json response, None, or Exception. """ + if exclude is None: + exclude = [] + if parameters is None: + parameters = {} response_object = None session, session_must_be_closed = self.get_session(method) # Prevent Telegram flood control for all methodsd having a `chat_id` @@ -343,14 +352,6 @@ class TelegramBot(object): See https://core.telegram.org/bots/api#setwebhook for details. """ - if url is None: - url = self.webhook_url - if allowed_updates is None: - allowed_updates = self.allowed_updates - if max_connections is None: - max_connections = self.max_connections - if certificate is None: - certificate = self.certificate if type(certificate) is str: try: certificate = dict( diff --git a/davtelepot/bot.py b/davtelepot/bot.py index 7c5765b..da0f5cd 100644 --- a/davtelepot/bot.py +++ b/davtelepot/bot.py @@ -1,6 +1,6 @@ """Provide a simple Bot object, mirroring Telegram API methods. -camelCase methods mirror API directly, while snake_case ones act as middlewares +camelCase methods mirror API directly, while snake_case ones act as middleware someway. Usage @@ -58,6 +58,8 @@ from .utilities import ( logging.getLogger('aiohttp').setLevel(logging.WARNING) +# Some methods are not implemented yet: that's the reason behind the following statement +# noinspection PyUnusedLocal,PyMethodMayBeStatic,PyMethodMayBeStatic class Bot(TelegramBot, ObjectWithDatabase, MultiLanguageObject): """Simple Bot object, providing methods corresponding to Telegram bot API. @@ -579,7 +581,7 @@ class Bot(TelegramBot, ObjectWithDatabase, MultiLanguageObject): break if not results: results = self.default_inline_query_answer - if type(results) is dict and 'answer' in results: + if isinstance(results, dict) and 'answer' in results: if 'switch_pm_text' in results: switch_pm_text = results['switch_pm_text'] if 'switch_pm_parameter' in results: @@ -1010,6 +1012,7 @@ class Bot(TelegramBot, ObjectWithDatabase, MultiLanguageObject): "but this handler does nothing yet." ) + # noinspection SpellCheckingInspection @staticmethod def split_message_text(text, limit=None, parse_mode='HTML'): r"""Split text if it hits telegram limits for text messages. @@ -1076,7 +1079,7 @@ class Bot(TelegramBot, ObjectWithDatabase, MultiLanguageObject): method = self.send_voice if method is not None: return await method(update=update, *args, **kwargs) - raise Exception("Unsopported keyword arguments for `Bot().reply`.") + raise Exception("Unsupported keyword arguments for `Bot().reply`.") async def send_message(self, chat_id=None, text=None, parse_mode='HTML', @@ -1121,7 +1124,7 @@ class Bot(TelegramBot, ObjectWithDatabase, MultiLanguageObject): return parse_mode = str(parse_mode) if isinstance(text, dict) and chat_id > 0: - if user_record is None : + if user_record is None: user_record = self.db['users'].find_one(telegram_id=chat_id) text = self.get_message( update=update, @@ -1255,7 +1258,6 @@ class Bot(TelegramBot, ObjectWithDatabase, MultiLanguageObject): Set `disable_notification` to True to avoid disturbing recipient. Pass the `update` to be forwarded or its identifier (`from_chat_id` and `message_id`). - @type message_id: int """ if from_chat_id is None or message_id is None: message_identifier = self.get_message_identifier(update) @@ -1669,7 +1671,7 @@ class Bot(TelegramBot, ObjectWithDatabase, MultiLanguageObject): set to True, use file_id (it is faster and recommended). `document_path` may contain `{path}`: it will be replaced by `self.path`. - `document_name` diplayed to Telegram may differ from actual document + `document_name` displayed to Telegram may differ from actual document name if this parameter is set. """ already_sent = False @@ -1785,7 +1787,7 @@ class Bot(TelegramBot, ObjectWithDatabase, MultiLanguageObject): return sent_update async def download_file(self, file_id, - file_name=None, mime_type=None, path=None): + file_name=None, path=None): """Given a telegram `file_id`, download the related file. Telegram may not preserve the original file name and MIME type: the @@ -1998,7 +2000,7 @@ class Bot(TelegramBot, ObjectWithDatabase, MultiLanguageObject): Decorate command handlers like this: ``` - @bot.command('/mycommand', ['Button'], True, "My command", 'user') + @bot.command('/my_command', ['Button'], True, "My command", 'user') async def command_handler(bot, update, user_record): return "Result" ``` @@ -2573,6 +2575,12 @@ class Bot(TelegramBot, ObjectWithDatabase, MultiLanguageObject): return if allowed_updates is None: allowed_updates = [] + if certificate is None: + certificate = self.certificate + if max_connections is None: + max_connections = self.max_connections + if url is None: + url = self.webhook_url webhook_was_set = await self.setWebhook( url=url, certificate=certificate, max_connections=max_connections, allowed_updates=allowed_updates diff --git a/davtelepot/database.py b/davtelepot/database.py index a9e6f16..0e73dbb 100644 --- a/davtelepot/database.py +++ b/davtelepot/database.py @@ -22,7 +22,7 @@ class ObjectWithDatabase(object): ``` """ - def __init__(self, database_url=None): + def __init__(self, database_url: str = None): """Instantiate object and open connection with database.""" if database_url is None: database_url = 'database.db' @@ -41,12 +41,12 @@ class ObjectWithDatabase(object): logging.error(f"{e}") @property - def db_url(self): + def db_url(self) -> str: """Return complete path to database.""" return self._database_url @property - def db(self): + def db(self) -> dataset.Database: """Return the dataset.Database instance related to `self`.""" return self._database