def download_file(item, local_file): if not (item and local_file): return False LOGGER.info(f"Downloading {local_file} ...") try: with item.open(stream=True) as response: with open(local_file, "wb") as file_out: copyfileobj(response.raw, file_out) item_modified_time = time.mktime(item.date_modified.timetuple()) os.utime(local_file, (item_modified_time, item_modified_time)) except (exceptions.ICloudPyAPIResponseException, FileNotFoundError, Exception) as e: LOGGER.error(f"Failed to download {local_file}: {str(e)}") return False return True
def get_username(config): username = None config_path = ["app", "credentials", "username"] if not traverse_config_path(config=config, config_path=config_path): LOGGER.error( f"username is missing in {config_path_to_string(config_path)}. Please set the username." ) else: username = get_config_value(config=config, config_path=config_path) username = username.strip() if len(username) == 0: username = None LOGGER.error( f"username is empty in {config_path_to_string(config_path)}.") return username
def _process_method(self, value: str, execute: bool = True) -> str: LOGGER.debug(f"Processando parse método {value}...") method_definition = value.split("@")[1] method_name = method_definition[0:method_definition.find("(")] if method_name not in AVAILABLE_METHODS: raise MethodNotImplementedException() if "user_input" == method_name and self.action_execution.arguments: return self.action_execution.arguments[self.index_method_executed] original_arguments = method_definition[method_definition.find("(") + 1:method_definition.find(")")] # refs.: https://stackoverflow.com/a/48838456/7973282 parsed_arguments = eval("dict({})".format(original_arguments)) parsed_arguments = { k: self._change_value(v) for k, v in parsed_arguments.items() } parsed_arguments["execute"] = execute parsed_arguments = ",".join([ f"{k}='{v}'" if isinstance(v, str) else f"{k}={v}" for k, v in parsed_arguments.items() ]) method_definition = f"{method_name}({parsed_arguments})" try: return eval(f"template_methods.{method_definition}") except TypeError as e: LOGGER.error(str(e)) raise InvalidTemplateException()
def send(config, last_send=None, dry_run=False): sent_on = None email = config_parser.get_smtp_email(config=config) to_email = config_parser.get_smtp_to_email(config=config) host = config_parser.get_smtp_host(config=config) port = config_parser.get_smtp_port(config=config) no_tls = config_parser.get_smtp_no_tls(config=config) password = config_parser.get_smtp_password(config=config) if last_send and last_send > datetime.datetime.now() - datetime.timedelta(hours=24): LOGGER.info("Throttling email to once a day") sent_on = last_send elif email and host and port: try: sent_on = datetime.datetime.now() if not dry_run: smtp = smtplib.SMTP(host, port) smtp.set_debuglevel(0) smtp.connect(host, port) if not no_tls: smtp.starttls() if password: smtp.login(email, password) msg = build_message(email) smtp.sendmail(from_addr=email, to_addrs=to_email, msg=msg.as_string()) smtp.quit() except (Exception) as e: sent_on = None LOGGER.error(f"Failed to send email: {str(e)}.") else: LOGGER.warning("Not sending 2FA notification because SMTP is not configured") return sent_on
def sync(): last_send = None enable_sync_drive = True enable_sync_photos = True drive_sync_interval = 0 photos_sync_interval = 0 sleep_for = 10 while True: config = read_config() username = config_parser.get_username(config=config) if username: try: if ENV_ICLOUD_PASSWORD_KEY in os.environ: password = os.environ.get(ENV_ICLOUD_PASSWORD_KEY) utils.store_password_in_keyring(username=username, password=password) else: password = utils.get_password_from_keyring( username=username) api = ICloudPyService( apple_id=username, password=password, cookie_directory=DEFAULT_COOKIE_DIRECTORY, ) if not api.requires_2sa: if "drive" in config and enable_sync_drive: sync_drive.sync_drive(config=config, drive=api.drive) drive_sync_interval = config_parser.get_drive_sync_interval( config=config) if "photos" in config and enable_sync_photos: sync_photos.sync_photos(config=config, photos=api.photos) photos_sync_interval = config_parser.get_photos_sync_interval( config=config) if "drive" not in config and "photos" not in config: LOGGER.warning( "Nothing to sync. Please add drive: and/or photos: section in config.yaml file." ) else: LOGGER.error("Error: 2FA is required. Please log in.") # Retry again sleep_for = config_parser.get_retry_login_interval( config=config) next_sync = ( datetime.datetime.now() + datetime.timedelta(seconds=sleep_for)).strftime("%c") LOGGER.info(f"Retrying login at {next_sync} ...") last_send = notify.send(config, last_send) sleep(sleep_for) continue except exceptions.ICloudPyNoStoredPasswordAvailableException: LOGGER.error( "Password is not stored in keyring. Please save the password in keyring." ) sleep_for = config_parser.get_retry_login_interval( config=config) next_sync = ( datetime.datetime.now() + datetime.timedelta(seconds=sleep_for)).strftime("%c") LOGGER.info(f"Retrying login at {next_sync} ...") last_send = notify.send(config, last_send) sleep(sleep_for) continue if "drive" not in config and "photos" in config: sleep_for = photos_sync_interval enable_sync_drive = False enable_sync_photos = True elif "drive" in config and "photos" not in config: sleep_for = drive_sync_interval enable_sync_drive = True enable_sync_photos = False elif ("drive" in config and "photos" in config and drive_sync_interval <= photos_sync_interval): sleep_for = photos_sync_interval - drive_sync_interval photos_sync_interval -= drive_sync_interval enable_sync_drive = True enable_sync_photos = False else: sleep_for = drive_sync_interval - photos_sync_interval drive_sync_interval -= photos_sync_interval enable_sync_drive = False enable_sync_photos = True next_sync = (datetime.datetime.now() + datetime.timedelta(seconds=sleep_for)).strftime("%c") LOGGER.info(f"Resyncing at {next_sync} ...") if (config_parser.get_drive_sync_interval(config=config) < 0 if "drive" in config else True and config_parser.get_photos_sync_interval( config=config) < 0 if "photos" in config else True): break sleep(sleep_for)