Skip to content

Instantly share code, notes, and snippets.

@AutonomousCat
Last active June 24, 2023 08:16
Show Gist options
  • Save AutonomousCat/54830965d9a06bba9fa8058cf6ed5a70 to your computer and use it in GitHub Desktop.
Save AutonomousCat/54830965d9a06bba9fa8058cf6ed5a70 to your computer and use it in GitHub Desktop.
A cross-platform Python (3.7+) script that uses ADB to automatically pull Aegis Authenticator backups from your Android device. Designed to run in the background, so you can set it up once and forget about it.
from os import path, listdir
from time import sleep
from subprocess import check_output, Popen, PIPE, CalledProcessError
import logging
logging.basicConfig(
level=logging.INFO,
format='[%(asctime)s][%(levelname)s] %(message)s',
datefmt='%m-%d %H:%M',
handlers=[logging.StreamHandler(), logging.FileHandler("backup.log")]
)
log = logging.getLogger(__name__)
config = {
'source_dir': '/storage/self/primary/Aegis/', # use ADB shell to examine your device's directory structure
'destination_dir': 'C:/Users/me/Documents/Aegis-Backup/',
'check_interval': 15 * 60 # 15 minutes
}
class AegisBackup:
def __init__(self, **kwargs):
self.__dict__.update(kwargs)
self.adb_pull = None
def backup(self):
try:
source_files = check_output(['adb', 'shell', f'ls -t {self.source_dir}aegis-backup-*.json'], text=True).split()
log.debug(f'Output of "adb shell ls -t": {", ".join(source_files)}')
if len(source_files) > 0:
latest_source_file = path.split(source_files[0])[-1]
if latest_source_file not in listdir(self.destination_dir):
self.adb_pull = Popen(['adb', 'pull', f'{self.source_dir}{latest_source_file}', self.destination_dir], stdout=PIPE, stderr=PIPE, text=True)
adb_pull_output, adb_pull_errors = self.adb_pull.communicate()
if self.adb_pull.returncode == 0:
log.info(f'New backup file pulled: "{latest_source_file}"')
log.debug(f'Output of "adb pull": {adb_pull_output.strip()}')
else:
log.error(f'Backup failed with error: {adb_pull_errors.strip()}')
raise CalledProcessError(self.adb_pull.returncode, self.adb_pull.args, output=adb_pull_output, stderr=adb_pull_errors)
else:
log.debug(f'Source file "{latest_source_file}" is already in "{self.destination_dir}"')
else:
log.debug(f'No files found in: "{self.source_dir}"')
except CalledProcessError as e:
log.error(f'Backup failed with error: {e.output.strip()}')
self.adb_pull = None
def run(self):
log.info('Started.')
try:
while True:
self.backup()
sleep(self.check_interval)
except KeyboardInterrupt:
log.info('Stopping. Please wait for any remaining "adb pull" operation to complete.')
while self.adb_pull and not self.adb_pull.poll():
time.sleep(1)
log.debug('Stopped.')
if __name__ == '__main__':
backup = AegisBackup(**config)
backup.run()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment