From b5fc2e43d375e12c3273d9c44f1b62941b07c8da Mon Sep 17 00:00:00 2001 From: SinTan1729 Date: Wed, 9 Aug 2023 19:12:04 -0500 Subject: [PATCH] new: Clean up + Notifications (partial) --- APKPure_dl.py | 19 ++++--- Cleanup.py | 42 +++++++++++++++ JAVABuilder.py | 16 +++--- Notifications.py | 77 ++++++++++++++++++++++++++++ ReVancedBuilder.py | 125 +++++++++++++++++++-------------------------- 5 files changed, 193 insertions(+), 86 deletions(-) create mode 100644 Cleanup.py create mode 100644 Notifications.py diff --git a/APKPure_dl.py b/APKPure_dl.py index ba44ebb..439197e 100644 --- a/APKPure_dl.py +++ b/APKPure_dl.py @@ -4,13 +4,14 @@ import json from packaging.version import Version import requests as req from bs4 import BeautifulSoup as bs +from Cleanup import clean_exit # Determine the best version available to download def apkpure_best_match(version, soup): try: vers_list = [Version(x['data-dt-version']) for x in soup.css.select(f"a[data-dt-apkid^=\"b/APK/\"]")] except: - sys.exit(f" There was some error getting list of versions of {apk}...") + clean_exit(f" There was some error getting list of versions of {apk}...", appstate) if version != '0': vers_list = filter(lambda x: x <= Version(version), vers_list) @@ -42,7 +43,7 @@ def apkpure_dl(apk, appname, version, hard_version, session, present_vers, flag) try: ver_code = soup.css.select(f"a[data-dt-version=\"{version}\"][data-dt-apkid^=\"b/APK/\"]")[0]['data-dt-versioncode'] except: - sys.exit(f" There was some error while downloading {apk}...") + clean_exit(f" There was some error while downloading {apk}...", appstate) res = session.get(f"https://d.apkpure.com/b/APK/{apk}?versionCode={ver_code}", stream=True) res.raise_for_status() @@ -54,14 +55,18 @@ def apkpure_dl(apk, appname, version, hard_version, session, present_vers, flag) # Download apk files, if needed -def get_apks(present_vers, build_config, flag): +def get_apks(appstate): + present_vers = appstate['present_vers'] + build_config = appstate['build_config'] + flag=appstate['flag'] + print('Downloading required apk files from APKPure...') # Get latest patches using the ReVanced API try: patches = req.get('https://releases.revanced.app/patches').json() except req.exceptions.RequestException as e: - sys.exit(e) + clean_exit(e, appstate) session = req.Session() session.headers.update({'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/116.0'}) @@ -76,7 +81,7 @@ def get_apks(present_vers, build_config, flag): pretty_name = build_config[app]['pretty_name'] apkpure_appname = build_config[app]['apkpure_appname'] except: - sys.exit(f"Invalid config for {app} in build_config.toml!") + clean_exit(f"Invalid config for {app} in build_config.toml!", appstate) print(f"Checking {pretty_name}...") try: @@ -103,4 +108,6 @@ def get_apks(present_vers, build_config, flag): apkpure_dl(apk, apkpure_appname, str(required_ver), hard_version, session, present_vers, flag) present_vers.update({apk: str(required_ver)}) - return present_vers \ No newline at end of file + + appstate['present_vers'] = present_vers + return appstate \ No newline at end of file diff --git a/Cleanup.py b/Cleanup.py new file mode 100644 index 0000000..e0d406d --- /dev/null +++ b/Cleanup.py @@ -0,0 +1,42 @@ +import os +import sys +from Notifications import send_notif + +# Move apps to proper location +def move_apps(appstate): + build_config = appstate['build_config'] + + try: + os.mkdir('archive') + except FileExistsError: + pass + + for app in build_config: + if not build_config[app].getboolean('build'): + continue + name = build_config[app]['output_name'] + final_name = f"{name}_{appstate['timestamp']}.apk" + + try: + os.rename(name+'.apk', 'archive/'+final_name) + except FileNotFoundError: + pass + # sys.exit('There was an error moving the final apk files!') + + files = [] + dir = os.scandir('archive') + for f in dir: + if name in f.name: + files.append(f) + files.sort(key=lambda f: f.stat().st_ctime) + files.reverse() + for f in files[3:]: + os.remove(f) + print('Deleted old build '+f.name) + dir.close() + +def clean_exit(msg, appstate, code=1): + send_notif(appstate, error=True) + if msg: + print(msg, file=sys.stderr) + exit(code) \ No newline at end of file diff --git a/JAVABuilder.py b/JAVABuilder.py index ce0b82b..6a6796a 100644 --- a/JAVABuilder.py +++ b/JAVABuilder.py @@ -3,9 +3,13 @@ import sys import configparser as cp import json import subprocess +from Cleanup import clean_exit # Build the revanced apps -def build_apps(build_config, flag): +def build_apps(appstate): + build_config = appstate['build_config'] + flag = appstate['flag'] + chosen_patches = cp.ConfigParser() chosen_patches.read('chosen_patches.toml') @@ -55,7 +59,7 @@ def build_apps(build_config, flag): apkpure_appname = build_config[app]['apkpure_appname'] output_name = build_config[app]['output_name'] except: - sys.exit(f"Invalid config for {app} in build_config.toml!") + clean_exit(f"Invalid config for {app} in build_config.toml!", appstate) cmd += f" -a {apk}.apk -o {output_name}.apk" @@ -64,15 +68,13 @@ def build_apps(build_config, flag): else: print(f"Building {pretty_name} (nonroot)...") - # if os.system(cmd) != 0: - # sys.exit('There was an error while building!') try: - output = subprocess.check_output(cmd, shell=True, stderr=subprocess.STDOUT, text=True) + output = subprocess.run(cmd, shell=True) except Exception as e: - sys.exit(f"There was an error while building! {e.output}") + clean_exit(f"There was an error while building {pretty_name}!", appstate) try: os.rename(output_name+'.apk', output_name+'.apk') # TODO: Add timestamp here except FileNotFoundError: - sys.exit(f"There was an error while building {pretty_name}!") + clean_exit(f"There was an error while building {pretty_name}!", appstate) \ No newline at end of file diff --git a/Notifications.py b/Notifications.py new file mode 100644 index 0000000..d11cce1 --- /dev/null +++ b/Notifications.py @@ -0,0 +1,77 @@ +import json +import re +import requests as req + +def send_notif(appstate, error=False): + timestamp = appstate['timestamp'] + if error: + msg = f"There was an error during build! Please check the logs.\nTimestamp: {timestamp}" + else: + notification_config = appstate['notification_config'] + build_config = appstate['build_config'] + present_vers = appstate['present_vers'] + flag = appstate['flag'] + + msg = json.dumps(present_vers, indent=0) + msg = re.sub('("|\{|\}|,)', '', msg).strip('\n') + + for app in build_config: + if not build_config[app].getboolean('build'): + continue + msg = msg.replace(build_config[app]['apk'], build_config[app]['pretty_name']) + + msg += '\nTimestamp: ' + timestamp + + config = appstate['notification_config'] + for entry in config: + if not config[entry].getboolean('enabled'): + continue + encoded_title = '⚙⚙⚙ ReVanced Build ⚙⚙⚙'.encode('utf-8') + + match entry: + case 'ntfy': + print('Sending notification through nfty.sh') + try: + url = config[entry]['url'] + topic = config[entry]['topic'] + except: + print('URL or TOPIC not provided!') + continue + headers = {'Icon': 'https://upload.wikimedia.org/wikipedia/commons/thumb/4/40/Revanced-logo-round.svg/240px-Revanced-logo-round.svg.png', + 'Title': encoded_title} + try: + req.post(f"{url}/{topic}", msg, headers=headers) + except Exception as e: + print('Failed!' + str(e)) + + case 'gotify': + print('Sending notification through nfty.sh') + try: + url = config[entry]['url'] + token = config[entry]['token'] + except: + print('URL or TOKEN not provided!') + continue + data = {'Title': encoded_title, 'message': msg, 'priority': '5'} + try: + req.post(f"{url}/message?token={token}", data) + except Exception as e: + print('Failed!' + str(e)) + + case 'telegram': + # TODO: Finish this! + print('Sending notification through telegram') + try: + chat = config[entry]['chat'] + token = config[entry]['token'] + except: + print('CHAT or TOKEN not provided!') + continue + data = {'Title': encoded_title, 'message': msg, 'priority': '5'} + try: + req.post(f"{url}/message?token={token}", data) + except Exception as e: + print('Failed!' + str(e)) + + case _: + print('Don\'t know how to send notifications to ' + entry) diff --git a/ReVancedBuilder.py b/ReVancedBuilder.py index 254ca87..6450231 100755 --- a/ReVancedBuilder.py +++ b/ReVancedBuilder.py @@ -9,29 +9,29 @@ from packaging.version import Version from APKPure_dl import * from JAVABuilder import * from datetime import datetime +from Notifications import send_notif +from Cleanup import * # TODO: Logging # TODO: Notifications -# TODO: Notification for errors (maybe by writing custom exit function? Exit function should also cleanup output files. printerr?) -# TODO: Moving files in proper locations # TODO: Run post_script (preferably in any language) # TODO: README +# TODO: PATCHES_GUIDE.md (maybe delete it?) # Update the ReVanced tools, if needed -def update_tools(): +def update_tools(appstate): for item in ['revanced-cli', 'revanced-integrations', 'revanced-patches']: *_, tool = filter(lambda x: x['repository'] == 'revanced/'+item, tools) # Get the last result latest_ver = Version(tool['version']) try: - present_ver = Version(present_vers[item]) + present_ver = Version(appstate['present_vers'][item]) except KeyError: present_ver = Version('0') output_file = item+os.path.splitext(tool['name'])[1] if flag == 'force' or not os.path.isfile(output_file) or present_ver < latest_ver: - global up_to_date - up_to_date = False + appstate['up-to-date'] = False print(f"{item} has an update ({str(present_ver)} -> {str(latest_ver)})") if flag != 'checkonly': print(f"Downloading {output_file}...") @@ -40,25 +40,26 @@ def update_tools(): with open(output_file, 'wb') as f: for chunk in res.iter_content(chunk_size=8192): f.write(chunk) - present_vers.update({item: str(latest_ver)}) + appstate['present_vers'].update({item: str(latest_ver)}) print("Done!") + return appstate + # Update microG, if needed -def update_microg(): +def update_microg(appstate): try: data = req.get('https://api.github.com/repos/inotia00/VancedMicroG/releases/latest').json()['tag_name'] latest_ver = Version(data) except req.exceptions.RequestException as e: - sys.exit(e) + clean_exit(e, appstate) try: - present_ver = Version(present_vers['VancedMicroG']) + present_ver = Version(appstate['present_vers']['VancedMicroG']) except KeyError: present_ver = Version('0') if flag == 'force' or not os.path.isfile('microg.apk') or present_ver < latest_ver: - global up_to_date - up_to_date = False + appstate['up-to-date'] = False print(f"Vanced microG has an update ({str(present_ver)} -> {str(latest_ver)})") if flag != 'checkonly': print(f"Downloading vanced-microg.apk...") @@ -67,105 +68,83 @@ def update_microg(): with open('microg.apk', 'wb') as f: for chunk in res.iter_content(chunk_size=8192): f.write(chunk) - present_vers.update({'VancedMicroG': str(latest_ver)}) + appstate['present_vers'].update({'VancedMicroG': str(latest_ver)}) print("Done!") -# Move apps to proper location -def move_apps(): - try: - os.mkdir('archive') - except FileExistsError: - pass - - for app in build_config: - if not build_config[app].getboolean('build'): - continue - name = build_config[app]['output_name'] - final_name = f"{name}_{timestamp}.apk" - - try: - os.rename(name+'.apk', 'archive/'+final_name) - except FileNotFoundError: - pass - # sys.exit('There was an error moving the final apk files!') - - files = [] - dir = os.scandir('archive') - for f in dir: - if name in f.name: - files.append(f) - files.sort(key=lambda f: f.stat().st_ctime) - files.reverse() - for f in files[3:]: - os.remove(f) - print('Deleted old build '+f.name) - dir.close() - - + return appstate # ------------------------------ # The main function starts here # ------------------------------ +# Create a dict for storing important data +appstate = {} + # Get a timestamp time = datetime.now() -timestamp = time.strftime('%Y%m%d%H%M%S') +appstate['timestamp'] = time.strftime('%Y%m%d%H%M%S') print(f"Started building ReVanced apps at {time.strftime('%d %B, %Y %H:%M:%S')}") +print('----------------------------------------------------------------------') # Read configs try: os.chdir(sys.argv[1]) except IndexError: - sys.exit('Please provide a working directory as argument!') + clean_exit('Please provide a working directory as argument!', appstate) except FileNotFoundError: - sys.exit('Invalid working directory provided!') + clean_exit('Invalid working directory provided!', appstate) try: flag = sys.argv[2] except: flag = None +appstate['flag'] = flag + try: - build_config=cp.ConfigParser() - build_config.read_file(open('build_config.toml', 'r')) + appstate['build_config']=cp.ConfigParser() + appstate['build_config'].read_file(open('build_config.toml', 'r')) except FileNotFoundError: - sys.exit('No build config provided, exiting. Please look at the GitHub page for more information:\n https://github.com/SinTan1729/ReVancedBuilder') + clean_exit('No build config provided, exiting. Please look at the GitHub page for more information:\n https://github.com/SinTan1729/ReVancedBuilder', appstate) -move_apps() - -notification_config = cp.ConfigParser() -notification_config.read('notification_config.toml') +appstate['notification_config'] = cp.ConfigParser() +appstate['notification_config'].read('notification_config.toml') # Pull the latest information using the ReVanced API try: tools = req.get('https://releases.revanced.app/tools').json()['tools'] except req.exceptions.RequestException as e: - sys.exit(e) + clean_exit(e, appstate) -global present_vers try: with open('versions.json', 'r') as f: - present_vers = json.load(f) + appstate['present_vers'] = json.load(f) except: # We'll treat empty as 0 later - present_vers = json.loads('{}') - -global up_to_date -up_to_date = True + appstate['present_vers'] = json.loads('{}') +appstate['up-to-date'] = True +# send_notif(appstate, error=False) # <,,,,,,,,<,,,,,,,,,,,,, if flag != 'buildonly': - update_tools() - update_microg() - if not up_to_date or flag == 'force': - present_vers = get_apks(present_vers, build_config, flag) + appstate = update_tools(appstate) + appstate = update_microg(appstate) + if not appstate['up-to-date'] or flag == 'force': + appstate = get_apks(appstate) -if (flag != 'checkonly' and not up_to_date) or flag in ['force', 'buildonly']: - build_apps(build_config, flag) - move_apps() +if (flag != 'checkonly' and not appstate['up-to-date']) or flag in ['force', 'buildonly']: + build_apps(appstate) + move_apps(appstate) # Update version numbers in the versions.json file -if up_to_date and flag != 'buildonly': +if appstate['up-to-date'] and flag != 'buildonly': print('There\'s nothing to do.') -elif flag not in ['checkonly', 'buildonly']: - with open('versions.json', 'w') as f: - json.dump(present_vers, f, indent=4) +elif flag != ['checkonly']: + send_notif(appstate) + try: + os.rename('versions.json', 'versions-old.json') + except FileNotFoundError: + pass + + if flag != 'buildonly': + with open('versions.json', 'w') as f: + json.dump(appstate['present_vers'], f, indent=4)