From 04827b46910f12b8a6b6a746e7abd42b07bf2cc2 Mon Sep 17 00:00:00 2001 From: Davte Date: Sun, 21 Jul 2019 17:32:27 +0200 Subject: [PATCH] davtelepot.bot.Bot().send_document method implemented It accepts document handlers, Telegram `document_id`s or file paths --- davtelepot/bot.py | 130 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 130 insertions(+) diff --git a/davtelepot/bot.py b/davtelepot/bot.py index ea4a9c6..6bcdda1 100644 --- a/davtelepot/bot.py +++ b/davtelepot/bot.py @@ -1196,6 +1196,136 @@ class Bot(TelegramBot, ObjectWithDatabase, MultiLanguageObject): ) return sent_update + async def send_document(self, chat_id=None, document=None, thumb=None, + caption=None, parse_mode=None, + disable_notification=None, + reply_to_message_id=None, reply_markup=None, + document_path=None, + document_name=None, + update=dict(), + reply_to_update=False, + send_default_keyboard=True, + use_stored_file_id=False): + """Send a document. + + This method wraps lower-level `TelegramBot.sendDocument` method. + Pass an `update` to extract `chat_id` and `message_id` from it. + Set `reply_to_update` = True to reply to `update['message_id']`. + Set `send_default_keyboard` = False to avoid sending default keyboard + as reply_markup (only those messages can be edited, which were + sent with no reply markup or with an inline keyboard). + If document was already sent by this bot and `use_stored_file_id` is + 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 + name if this parameter is set. + """ + already_sent = False + # This buffered_file trick is necessary for two reasons + # 1. File operations must be blocking, but sendDocument is a coroutine + # 2. A `with` statement is not possible here + # `buffered_file` must be closed at all costs! + buffered_file = None + if 'message' in update: + update = update['message'] + if chat_id is None and 'chat' in update: + chat_id = self.get_chat_id(update) + if reply_to_update and 'message_id' in update: + reply_to_message_id = update['message_id'] + if ( + send_default_keyboard + and reply_markup is None + and type(chat_id) is int + and chat_id > 0 + and caption != self.authorization_denied_message + ): + reply_markup = self.default_keyboard + if document_path is not None: + with self.db as db: + already_sent = db['sent_documents'].find_one( + path=document_path, + errors=False + ) + if already_sent and use_stored_file_id: + document = already_sent['file_id'] + already_sent = True + else: + already_sent = False + if not any( + [ + document_path.startswith(url_starter) + for url_starter in ('http', 'www',) + ] + ): # If `document_path` is not a url but a local file path + try: + with open( + document_path.format( + path=self.path + ), + 'rb' # Read bytes + ) as file_: + buffered_file = io.BytesIO(file_.read()) + buffered_file.name = ( + document_name + or file_.name + or 'Document' + ) + document = buffered_file + except FileNotFoundError: + document = None + buffered_file.close() + else: + use_stored_file_id = False + if document is None: + logging.error( + "`document` is None, `send_document` returning..." + ) + return + sent_update = None + try: + sent_update = await self.sendDocument( + chat_id=chat_id, + document=document, + thumb=thumb, + caption=caption, + parse_mode=parse_mode, + disable_notification=disable_notification, + reply_to_message_id=reply_to_message_id, + reply_markup=reply_markup + ) + if isinstance(sent_update, Exception): + raise Exception("sendDocument API call failed!") + except Exception as e: + logging.error(f"Error sending document\n{e}") + if already_sent: + with self.db as db: + db['sent_documents'].update( + dict( + path=document_path, + errors=True + ), + ['path'] + ) + finally: + buffered_file.close() + if ( + type(sent_update) is dict + and 'document' in sent_update + and 'file_id' in sent_update['document'] + and (not already_sent) + and use_stored_file_id + ): + with self.db as db: + db['sent_documents'].insert( + dict( + path=document_path, + file_id=sent_update['document']['file_id'], + errors=False + ) + ) + return sent_update + async def answer_inline_query(self, inline_query_id=None, results=[],