def receive_custom_amount(update, context): _ = set_lang(update, context) if _(CUSTOM_MSG) in update.effective_message.reply_to_message.text: try: amount = round(float(update.effective_message.text)) if amount <= 0: raise ValueError send_payment_invoice(update, context, amount=amount) except ValueError: _ = set_lang(update, context) update.effective_message.reply_text( _('The amount you sent is invalid, try again. {}').format( _(CUSTOM_MSG)), reply_markup=ForceReply())
def send_payment_invoice( update: Update, context: CallbackContext, query: CallbackQuery, ): if query is None: message = update.effective_message label = message.text else: message = query.message label = query.data _ = set_lang(update, context) chat_id = message.chat_id title = _("Support PDF Bot") description = _("Say thanks to PDF Bot and help keep it running") price = PAYMENT_DICT[label] prices = [LabeledPrice(re.sub(r"\s\(.*", "", label), price * 100)] context.bot.send_invoice( chat_id, title, description, PAYMENT_PAYLOAD, STRIPE_TOKEN, CURRENCY, prices, max_tip_amount=1000, suggested_tip_amounts=[100, 300, 500, 1000], )
def check_photo(update: Update, context: CallbackContext) -> int: message = update.effective_message message.chat.send_action(ChatAction.TYPING) photo_file = check_photo_file(update, context) user_id = message.from_user.id photo_locks[user_id].acquire() if photo_file is None: if not context.user_data[PHOTO_IDS]: result = ask_first_photo(update, context) else: result = ask_next_photo(update, context) else: _ = set_lang(update, context) try: file_name = photo_file.file_name except AttributeError: file_name = _("File name unavailable") context.user_data[PHOTO_IDS].append(photo_file.file_id) context.user_data[PHOTO_NAMES].append(file_name) result = ask_next_photo(update, context) photo_locks[user_id].release() return result
def process_photo_task(update, context): """ Receive the task and perform the task on the photo Args: update: the update object context: the context object Returns: The variable indicating the conversation has ended """ if not check_user_data(update, context, PHOTO_ID): return ConversationHandler.END _ = set_lang(update, context) user_data = context.user_data file_id = user_data[PHOTO_ID] if update.effective_message.text == _(BEAUTIFY): process_photo(update, context, [file_id], is_beautify=True) else: process_photo(update, context, [file_id], is_beautify=False) if user_data[PHOTO_ID] == file_id: del user_data[PHOTO_ID] return ConversationHandler.END
def send_result_photos(update, context, dir_name, task): _ = set_lang(update, context) message = update.effective_message if message.text == _(PHOTOS): for photo_name in sorted(os.listdir(dir_name)): photo_path = os.path.join(dir_name, photo_name) if os.path.getsize(photo_path) <= MAX_FILESIZE_UPLOAD: try: message.chat.send_action(ChatAction.UPLOAD_PHOTO) message.reply_photo(open(photo_path, "rb")) except BadRequest: message.chat.send_action(ChatAction.UPLOAD_DOCUMENT) message.reply_document(open(photo_path, "rb")) message.reply_text( _("See above for all your photos"), reply_markup=get_support_markup(update, context), ) update_stats(update, task) else: # Compress the directory of photos shutil.make_archive(dir_name, "zip", dir_name) # Send result file send_result_file(update, context, f"{dir_name}.zip", task)
def get_pdf_photos(update, context): if not check_user_data(update, context, PDF_INFO): return ConversationHandler.END _ = set_lang(update, context) update.effective_message.reply_text( _("Extracting all the photos in your PDF file"), reply_markup=ReplyKeyboardRemove(), ) with tempfile.NamedTemporaryFile() as tf: user_data = context.user_data file_id, file_name = user_data[PDF_INFO] pdf_file = context.bot.get_file(file_id) pdf_file.download(custom_path=tf.name) with tempfile.TemporaryDirectory() as tmp_dir_name: dir_name = os.path.join(tmp_dir_name, "Photos_In_PDF") os.mkdir(dir_name) if not write_photos_in_pdf(tf.name, dir_name, file_name): update.effective_message.reply_text( _("Something went wrong, try again")) else: if not os.listdir(dir_name): update.effective_message.reply_text( _("I couldn't find any photos in your PDF file")) else: send_result_photos(update, context, dir_name, "get_photos") # Clean up memory if user_data[PDF_INFO] == file_id: del user_data[PDF_INFO] return ConversationHandler.END
def ask_split_range(update, context): _ = set_lang(update, context) update.effective_message.reply_text( _( "Send me the range of pages that you'll like to keep\n\n" "<b>General usage</b>\n" "<code>: all pages</code>\n" "<code>22 just the 23rd page</code>\n" "<code>0:3 the first three pages</code>\n" "<code>:3 the first three pages</code>\n" "<code>5: from the 6th page onwards</code>\n" "<code>-1 last page only</code>\n" "<code>:-1 all pages but the last page</code>\n" "<code>-2 second last page only</code>\n" "<code>-2: last two pages</code>\n" "<code>-3:-1 third and second last pages only</code>\n\n" "<b>Advanced usage</b>\n" "<code>::2 pages 0 2 4 ... to the end</code>\n" "<code>1:10:2 pages 1 3 5 7 9</code>\n" "<code>::-1 all pages in reversed order</code>\n" "<code>3:0:-1 pages 3 2 1 but not 0</code>\n" "<code>2::-1 pages 2 1 0</code>" ), parse_mode=ParseMode.HTML, reply_markup=get_back_markup(update, context), ) return WAIT_SPLIT_RANGE
def url_to_pdf(update: Update, context: CallbackContext): _ = set_lang(update, context) message = update.effective_message url = message.text user_data = context.user_data if user_data is not None and URLS in user_data and url in user_data[URLS]: message.reply_text( _("You've sent me this web page already and I'm still converting it") ) else: message.reply_text(_("Converting your web page into a PDF file")) if URLS in user_data: user_data[URLS].add(url) else: user_data[URLS] = {url} with tempfile.TemporaryDirectory() as dir_name: out_fn = os.path.join(dir_name, f"{urlparse(url).netloc}.pdf") try: HTML(url=url).write_pdf(out_fn) send_result_file(update, context, out_fn, "url") except URLFetchingError: message.reply_text(_("Unable to reach your web page")) user_data[URLS].remove(url)
def add_ocr_to_pdf(update, context): if not check_user_data(update, context, PDF_INFO): return ConversationHandler.END _ = set_lang(update, context) update.effective_message.reply_text( _("Adding an OCR text layer to your PDF file"), reply_markup=ReplyKeyboardRemove(), ) with tempfile.NamedTemporaryFile() as tf: user_data = context.user_data file_id, file_name = user_data[PDF_INFO] pdf_file = context.bot.get_file(file_id) pdf_file.download(custom_path=tf.name) with tempfile.TemporaryDirectory() as dir_name: out_fn = os.path.join(dir_name, f"OCR_{os.path.splitext(file_name)[0]}.pdf") try: # logging.getLogger("ocrmypdf").setLevel(logging.WARNING) ocrmypdf.ocr(tf.name, out_fn, deskew=True, progress_bar=False) send_result_file(update, context, out_fn, "ocr") except PriorOcrFoundError: update.effective_message.reply_text( _("Your PDF file already has a text layer")) # Clean up memory if user_data[PDF_INFO] == file_id: del user_data[PDF_INFO] return ConversationHandler.END
def check_pdf(update, context, send_msg=True): """ Validate the PDF file Args: update: the update object context: the context object send_msg: the bool indicating to send a message or not Returns: The variable indicating the validation result """ pdf_status = PDF_OK message = update.effective_message pdf_file = message.document _ = set_lang(update, context) if not pdf_file.mime_type.endswith("pdf"): pdf_status = PDF_INVALID_FORMAT if send_msg: message.reply_text( _("The file you sent is not a PDF file, try again")) elif pdf_file.file_size >= MAX_FILESIZE_DOWNLOAD: pdf_status = PDF_TOO_LARGE if send_msg: message.reply_text( _("The PDF file you sent is too large for me to download\n\n" "I've cancelled your action")) return pdf_status
def send_pdf_text(update, context, pdf_texts, is_file, out_fn): _ = set_lang(update, context) message = update.effective_message if pdf_texts: if is_file: with open(out_fn, "w") as f: f.write("\n".join(pdf_texts)) send_result_file(update, context, out_fn, "get_text") else: msg_text = "" for pdf_text in pdf_texts: if len(msg_text) + len(pdf_text) + 1 > MAX_MESSAGE_LENGTH: message.reply_text(msg_text.strip()) msg_text = "" msg_text += f" {pdf_text}" if msg_text: message.reply_text(msg_text.strip()) message.reply_text( _("*See above for all the text in your PDF file*"), parse_mode=ParseMode.MARKDOWN, ) else: message.reply_text(_("I couldn't find any text in your PDF file"))
def get_pdf_text(update, context, is_file): if not check_user_data(update, context, PDF_INFO): return ConversationHandler.END _ = set_lang(update, context) update.effective_message.reply_text( _("Extracting text from your PDF file"), reply_markup=ReplyKeyboardRemove() ) with tempfile.NamedTemporaryFile() as tf: user_data = context.user_data file_id, file_name = user_data[PDF_INFO] pdf_file = context.bot.get_file(file_id) pdf_file.download(custom_path=tf.name) with tempfile.TemporaryDirectory() as dir_name: tmp_text = tempfile.TemporaryFile() with open(tf.name, "rb") as f: extract_text_to_fp(f, tmp_text) tmp_text.seek(0) pdf_texts = textwrap.wrap(tmp_text.read().decode("utf-8").strip()) out_fn = os.path.join(dir_name, f"{os.path.splitext(file_name)[0]}.txt") send_pdf_text(update, context, pdf_texts, is_file, out_fn) # Clean up memory if user_data[PDF_INFO] == file_id: del user_data[PDF_INFO] return ConversationHandler.END
def check_user_data( update: Update, context: CallbackContext, key: str, lock: Lock = None ) -> bool: """ Check if the specified key exists in user_data Args: update: the update object context: the context object key: the string of key Returns: The boolean indicating if the key exists or not """ data_ok = True if lock is not None: lock.acquire() if key not in context.user_data: data_ok = False _ = set_lang(update, context) update.effective_message.reply_text(_("Something went wrong, start over again")) if lock is not None: lock.release() return data_ok
def cancel(update, context): _ = set_lang(update, context) update.effective_message.reply_text( _("Action cancelled"), reply_markup=ReplyKeyboardRemove() ) return ConversationHandler.END
def check_text(update: Update, context: CallbackContext) -> int: update.effective_message.chat.send_action(ChatAction.TYPING) _ = set_lang(update, context) if update.effective_message.text == _(CANCEL): return cancel(update, context) else: return receive_feedback(update, context)
def check_doc_task(update, context): _ = set_lang(update, context) text = update.effective_message.text if text == _(CROP): return ask_crop_type(update, context) elif text == _(DECRYPT): return ask_decrypt_pw(update, context) elif text == _(ENCRYPT): return ask_encrypt_pw(update, context) elif text in [_(EXTRACT_PHOTO), _(TO_PHOTO)]: return ask_photo_results_type(update, context) elif text == _(PREVIEW): return get_pdf_preview(update, context) elif text == _(RENAME): return ask_pdf_new_name(update, context) elif text == _(ROTATE): return ask_rotate_degree(update, context) elif text in [_(SCALE)]: return ask_scale_type(update, context) elif text == _(SPLIT): return ask_split_range(update, context) elif text == _(EXTRACT_TEXT): return ask_text_type(update, context) elif text == OCR: return add_ocr_to_pdf(update, context) elif text == COMPRESS: return compress_pdf(update, context) elif text == _(CANCEL): return cancel_without_async(update, context)
def send_support_options(update: Update, context: CallbackContext, query: CallbackQuery = None): update.effective_message.reply_chat_action(ChatAction.TYPING) _ = set_lang(update, context, query) keyboard = [ [ InlineKeyboardButton(_(THANKS), callback_data=THANKS), InlineKeyboardButton(_(COFFEE), callback_data=COFFEE), ], [ InlineKeyboardButton(_(BEER), callback_data=BEER), InlineKeyboardButton(_(MEAL), callback_data=MEAL), ], [InlineKeyboardButton(_("Developer"), "https://t.me/Amani_m_h_d")], ] reply_markup = InlineKeyboardMarkup(keyboard) text = _("Select how you want to support PDF Bot") if query is None: user_id = update.effective_message.from_user.id else: user_id = query.from_user.id context.bot.send_message(user_id, text, reply_markup=reply_markup)
def send_payment_invoice(update, context, query=None, amount=None): if query is None: message = update.effective_message label = message.text else: message = query.message label = query.data _ = set_lang(update, context) chat_id = message.chat_id title = _("Support PDF Bot") description = _("Say thanks to PDF Bot and help keep it running") if amount is None: price = PAYMENT_DICT[label] else: label = CUSTOM price = amount prices = [LabeledPrice(re.sub(r"\s\(.*", "", label), price * 100)] context.bot.send_invoice( chat_id, title, description, PAYMENT_PAYLOAD, STRIPE_TOKEN, PAYMENT_PARA, CURRENCY, prices, )
def compare_pdf(update, context): _ = set_lang(update, context) message = update.effective_message message.reply_text(_("Comparing your PDF files"), reply_markup=ReplyKeyboardRemove()) with tempfile.NamedTemporaryFile() as tf1, tempfile.NamedTemporaryFile( ) as tf2: # Download PDF files user_data = context.user_data first_file_id = user_data[COMPARE_ID] first_file = context.bot.get_file(first_file_id) first_file.download(custom_path=tf1.name) second_file = message.document.get_file() second_file.download(custom_path=tf2.name) try: with tempfile.TemporaryDirectory() as dir_name: out_fn = os.path.join(dir_name, "Differences.png") pdf_diff.main(files=[tf1.name, tf2.name], out_file=out_fn) send_result_file(update, context, out_fn, "compare") except NoDifferenceError: message.reply_text( _("There are no differences in text between your PDF files")) # Clean up memory and files if user_data[COMPARE_ID] == first_file_id: del user_data[COMPARE_ID] return ConversationHandler.END
def send_support_options(update, context, query=None): _ = set_lang(update, context, query) keyboard = [ [ InlineKeyboardButton(_(THANKS), callback_data=THANKS), InlineKeyboardButton(_(COFFEE), callback_data=COFFEE), ], [ InlineKeyboardButton(_(BEER), callback_data=BEER), InlineKeyboardButton(_(MEAL), callback_data=MEAL), ], [InlineKeyboardButton(_(CUSTOM), callback_data=CUSTOM)], [ InlineKeyboardButton(_("Help translate PDF Bot"), "https://crwd.in/telegram-pdf-bot") ], ] reply_markup = InlineKeyboardMarkup(keyboard) text = _("Select how you want to support PDF Bot") if query is None: user_id = update.effective_message.from_user.id else: user_id = query.from_user.id context.bot.send_message(user_id, text, reply_markup=reply_markup)
def ask_scale_value(update, context, ask_percent=True): _ = set_lang(update, context) message = update.effective_message reply_markup = get_back_markup(update, context) if message.text == _(BY_PERCENT) or ask_percent: message.reply_text( _("Send me the scaling factors for the horizontal and vertical axes\n\n" "2 will double the axis and 0.5 will halve the axis\n\n" "*Example: 2 0.5* (this will double the horizontal axis and halve the vertical axis)" ), reply_markup=reply_markup, parse_mode=ParseMode.MARKDOWN, ) return WAIT_SCALE_PERCENT else: message.reply_text( _("Send me the width and height\n\n" "*Example: 150 200* (this will set the width to 150 and height to 200)" ), reply_markup=reply_markup, parse_mode=ParseMode.MARKDOWN, ) return WAIT_SCALE_DIMENSION
def ask_crop_value(update, context): _ = set_lang(update, context) message = update.effective_message reply_markup = ReplyKeyboardMarkup([[_(BACK)]], one_time_keyboard=True, resize_keyboard=True) if message.text == _(BY_PERCENT): message.reply_text( _("Send me a number between {} and {}. This is the percentage of margin space to " "retain between the content in your PDF file and the page"). format(MIN_PERCENT, MAX_PERCENT), reply_markup=reply_markup, ) return WAIT_CROP_PERCENT else: message.reply_text( _("Send me a number that you'll like to adjust the margin size. " "Positive numbers will decrease the margin size and negative numbers will increase it" ), reply_markup=reply_markup, ) return WAIT_CROP_OFFSET
def crop_pdf(update, context, percent=None, offset=None): _ = set_lang(update, context) update.effective_message.reply_text(_("Cropping your PDF file"), reply_markup=ReplyKeyboardRemove()) with tempfile.NamedTemporaryFile(suffix=".pdf") as tf: user_data = context.user_data file_id, file_name = user_data[PDF_INFO] pdf_file = context.bot.get_file(file_id) pdf_file.download(custom_path=tf.name) with tempfile.TemporaryDirectory() as dir_name: out_fn = os.path.join(dir_name, f"Cropped_{file_name}") command = f'pdf-crop-margins -o "{out_fn}" "{tf.name}"' if percent is not None: command += f" -p {percent}" else: command += f" -a {offset}" if run_cmd(command): send_result_file(update, context, out_fn, "crop") else: update.effective_message.reply_text( _("Something went wrong, try again")) # Clean up memory if user_data[PDF_INFO] == file_id: del user_data[PDF_INFO] return ConversationHandler.END
def precheckout_check(update, context): _ = set_lang(update, context) query = update.pre_checkout_query if query.invoice_payload != PAYMENT_PAYLOAD: query.answer(ok=False, error_message=_("Something went wrong")) else: query.answer(ok=True)
def check_crop_task(update, context): _ = set_lang(update, context) text = update.effective_message.text if text in [_(BY_PERCENT), _(BY_SIZE)]: return ask_crop_value(update, context) elif text == _(BACK): return ask_doc_task(update, context)
def check_text(update, context): _ = set_lang(update, context) text = update.effective_message.text if text == _(BACK): return ask_first_doc(update, context) elif text == _(CANCEL): return cancel(update, context)
def check_text(update, context): _ = set_lang(update, context) text = update.effective_message.text if text == _(BACK): return ask_src_doc(update, context) elif text == _(CANCEL): return cancel_without_async(update, context)
def check_crop_size(update, context): _ = set_lang(update, context) message = update.effective_message if message.text == _(BACK): return ask_crop_type(update, context) try: offset = float(update.effective_message.text) except ValueError: _ = set_lang(update, context) update.effective_message.reply_text( _("The number is invalid, try again")) return WAIT_CROP_OFFSET return crop_pdf(update, context, offset=offset)
def check_to_photos_task(update, context): _ = set_lang(update, context) text = update.effective_message.text if text in [_(PHOTOS), _(COMPRESSED)]: return pdf_to_photos(update, context) elif text == _(BACK): return ask_doc_task(update, context)
def check_scale_task(update, context): _ = set_lang(update, context) text = update.effective_message.text if text in [_(BY_PERCENT), _(TO_DIMENSIONS)]: return ask_scale_value(update, context) elif text == _(BACK): return ask_doc_task(update, context)