def wait_for_upload(tg_client, msg, net_speed): """ This function blocks current thread until a specified file has finished uploading. """ state = msg["sending_state"]["@type"] doc = msg["content"]["document"]["document"] while doc and state == "messageSendingStatePending": left = doc["size"] - doc["remote"]["uploaded_size"] time.sleep(ceil(left / net_speed)) task = tg_client.call_method("getMessage", { "chat_id": msg["chat_id"], "message_id": msg["id"] }) task.wait() if task.error_info is not None or not task.update: get_logger().error("Error waiting for file %s", task.error_info) break msg = task.update doc = msg["content"]["document"]["document"] state = msg["sending_state"]["@type"]
def search(tg_client, chat_id, _): """ This function searches for uploaded file using the RegEx provide by the user. """ search_reg = freetext( "Enter the file path to browse for ( RegEx supported )") print("Searching...") files = [] file_names = ["Select All"] try: for (msg_id, file_id, caption) in get_messages(tg_client, chat_id): if re_search(search_reg, caption): files.append((msg_id, file_id, caption)) file_names.append(caption) except re_error as re_er: get_logger().warning("Error searching %s", re_er) if len(files) == 0: return freetext("No files matched your browse") choice = long_choice("Select files", file_names, False) if file_names[0] == choice: use_files(files, tg_client, chat_id) else: use_files(files[file_names.index(choice)], tg_client, chat_id)
def restore(tg_client_client, chat_id): """ This function starts the restore process. """ errors = "" try: (restored, failed, errors) = download_files(tg_client_client, chat_id) print("\n\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n") print(f"{restored} files restored to ~/Restored") print(f"{failed} failed \n") print("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n") except KeyboardInterrupt: failed = 0 print("\nRestoration paused.") try: rmtree(MESGS_DIR) except FileNotFoundError: get_logger().error("Messages directory not found.") if failed > 0 and input( "Do you want to see the error log (y/N) ? : ").lower() == "y": print(errors) input("Press enter to continue.")
def change_folder(_): """ This function allows user to change backup folder. """ file_log = get_logger() try: with open(DATA_FILE, "rb") as db_file: db_dict = pickle.load(db_file) except FileNotFoundError: file_log.warning("No backup folders to change") db_dict = {} if not confirm(f"Currently {','.join(db_dict['back_up_folders'])} are backed-up. Do you want to change this?"): return db_dict["back_up_folders"] = get_folders() with open(DATA_FILE, "wb") as db_file: pickle.dump(db_dict, db_file) file_log.info("Backup Folders changed.") input("Backup Folders changed. Press any enter to continue.")
def logout(tg_client): """ This function logs the user out. """ file_log = get_logger() if not confirm("Are you sure you want to Logout?"): return task = tg_client.call_method("logOut", {}) task.wait() if task.error_info: print("Oops something went wrong") file_log.error(task.error_info) return try: rmtree(GRAMUP_DIR) except FileNotFoundError: file_log.warning("Cache already cleared") file_log.info("Logged out") input("Logged out. Press enter to continue.") sys.exit(0)
def use_files(files, tg_client, chat_id): options = ["View", "Delete", "Go Back"] functions = [show_file, delete_files] while True: try: chose = choose("What do you want to do?", options) if chose == options[2]: break if functions[options.index(chose)](tg_client, chat_id, files): break except (ValueError, IndexError) as v_er: get_logger().warning("Error reading index %s", v_er) print(f"Please enter number between 1 and {len(files)}")
def delete_files(tg_client, chat_id, files): """ This function deletes the file with id file_id. """ if len(files) == 0: return False file_log = get_logger() question = "\n".join( [f" {caption_text}" for (_, _, caption_text) in files]) question += "\n\nAre you sure you want to delete these files?" if choose(question, ["Yes", "No"]) == "No": return False task = tg_client.call_method( "deleteMessages", { "chat_id": chat_id, "message_ids": [msg_id for (msg_id, _, _) in files], "revoke": True }) print("Deleting... Please wait.") task.wait() if task.error_info: file_log.error("Error showing file %s", task.error_info) freetext("Oops... Something went wrong.") return False freetext("Files deleted.") return True
def show_file(tg_client, chat_id, files): """ This function downloads and displays the file with id file_id. """ for (msg_id, file_id, caption) in files: task = download_file( tg_client, file_id if file_id else get_file_id(tg_client, chat_id, msg_id)) if task.error_info is None: temp_file = join(gettempdir(), basename(caption)) move(task.update["local"]["path"], temp_file) webbrowser.open(f"file://{temp_file}", new=2) else: get_logger().error("Error showing file %s", task.error_info) freetext("Oops... Something went wrong.")
def download_files(tg_client, chat_id): """ This function downloads and moves files to the appropriate directories in RE_FOLDER """ print("Getting file list...") files = get_messages(tg_client, chat_id) restored, failed, total = (0, 0, len(files)) errors, file_log = "", get_logger() file_log.info("%s files to restore", total) print("Restoring files\nPress ctrl+c to save progress and stop.\n") if total <= 0: return 0, 0, "" print_progress_bar(0, total) for (msg_id, file_id, path) in files: if isfile(join(RE_FOLDER, path)): restored += 1 print_progress_bar(restored + failed, total, "", suffix=f"{restored + failed} of {total} done") continue task = download_file( tg_client, file_id if file_id else get_file_id(tg_client, chat_id, msg_id)) if not (path and dirname(path)): path = join(OTHER_FOLDER, str(file_id)) if task.error_info is None: makedirs(dirname(join(RE_FOLDER, path)), exist_ok=True) copyfile(task.update["local"]["path"], join(RE_FOLDER, path)) restored += 1 else: file_log.error("Error restoring file %s", task.error_info) errors += str(task.error_info) + "\n" failed += 1 print_progress_bar(restored + failed, total, "", suffix=f"{restored + failed} of {total} done") return restored, failed, errors
def clear_cache(_): """ This function clears all local caches. """ if not confirm("Are you sure you want to clear all cache?"): return file_log = get_logger() try: rmtree(CACHE_DIR) except FileNotFoundError: file_log.warning("Cache already cleared") file_log.info("Cache cleared") input("Cache cleared. Press any enter to continue.")
def change_chat(tg_client): """ This function allows user to change backup folder. """ file_log = get_logger() try: with open(DATA_FILE, "rb") as db_file: db_dict = pickle.load(db_file) except FileNotFoundError: file_log.warning("No backup folders to change") db_dict = {} if not confirm("If you change chat you will not be able to access previously backed-up files without changing back." "Are you sure you want to change?"): return get_chat_id(tg_client, db_dict["phone_number"], db_dict["back_up_folders"]) input("backup chat changed. Press any enter to continue.")
def client_ready(tg_client, chat_id, bup_folders): """ This function is called once required data is loaded and Telegram client is initialized. """ if not (tg_client or chat_id or bup_folders): sys.exit(3) file_log = get_logger() file_log.info("Client ready.") options = ["Backup", "Restore", "Browse", "Settings", "Help", "Quit"] try: while True: print_banner() choice = choose("What do you want to do?", options) if choice == options[0]: backup(tg_client, chat_id, bup_folders) elif choice == options[1]: restore(tg_client, chat_id) elif choice == options[2]: browse(tg_client, chat_id, bup_folders) elif choice == options[3]: settings(tg_client) elif choice == options[4]: gramupHelp() else: break except KeyboardInterrupt: print_banner() file_log.warning("Keyboard interrupt received.") file_log.info("End of execution.") sys.exit(0)
def backup(tg_client, chat_id, back_up_folders): """ This function starts the backup process. """ async_result = ThreadPool(processes=1).apply_async( lambda: speedtest.Speedtest().upload() / 8, ()) file_log = get_logger() print("\nGetting list of uploaded files") old_files = get_uploaded_files(tg_client, chat_id, back_up_folders) file_log.info("Found %s files already uploaded", len(old_files)) new_files = [] print("Getting list of files to upload") for folder in back_up_folders: new_files.extend(get_new_files(folder, old_files)) file_log.info("Found %s new files to upload", len(new_files)) if len(new_files) == 0: return show_results(0, 0, "") total_files = len(new_files) net_speed = async_result.get() (done, failed, errors) = (0, 0, "") file_log.info("Measured internet speed to be %s Bps", net_speed) print_progress_bar(0, total_files) tg_client.send_message( chat_id=chat_id, text= f"Backup started on {datetime.today().strftime('%Y-%m-%d %I:%M %p')}") tg_client.send_message( chat_id=chat_id, text=f"Backing up {total_files} files @ {net_speed / 1000000} MBps.") for (new_file, folder) in new_files: task = send_file(tg_client, chat_id, new_file, folder) if task.error_info is None: wait_for_upload(tg_client, task.update, net_speed) done += 1 else: failed += 1 errors += str(task.error_info) + "\n\n" file_log.error("Error uploading %s %s", new_file, task.error_info) print_progress_bar(done + failed, total_files, "", suffix=f"{done + failed} of {total_files} done") tg_client.send_message( chat_id=chat_id, text=f"Backup ended on {datetime.today().strftime('%Y-%m-%d %I:%M %p')}" ).wait() return show_results(done, failed, errors)