new: Clean up + Notifications (partial)

This commit is contained in:
Sayantan Santra 2023-08-09 19:12:04 -05:00
parent a9fe4bf646
commit b5fc2e43d3
Signed by: SinTan1729
GPG key ID: EB3E68BFBA25C85F
5 changed files with 193 additions and 86 deletions

View file

@ -4,13 +4,14 @@ import json
from packaging.version import Version from packaging.version import Version
import requests as req import requests as req
from bs4 import BeautifulSoup as bs from bs4 import BeautifulSoup as bs
from Cleanup import clean_exit
# Determine the best version available to download # Determine the best version available to download
def apkpure_best_match(version, soup): def apkpure_best_match(version, soup):
try: try:
vers_list = [Version(x['data-dt-version']) for x in soup.css.select(f"a[data-dt-apkid^=\"b/APK/\"]")] vers_list = [Version(x['data-dt-version']) for x in soup.css.select(f"a[data-dt-apkid^=\"b/APK/\"]")]
except: 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': if version != '0':
vers_list = filter(lambda x: x <= Version(version), vers_list) 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: try:
ver_code = soup.css.select(f"a[data-dt-version=\"{version}\"][data-dt-apkid^=\"b/APK/\"]")[0]['data-dt-versioncode'] ver_code = soup.css.select(f"a[data-dt-version=\"{version}\"][data-dt-apkid^=\"b/APK/\"]")[0]['data-dt-versioncode']
except: 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 = session.get(f"https://d.apkpure.com/b/APK/{apk}?versionCode={ver_code}", stream=True)
res.raise_for_status() 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 # 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...') print('Downloading required apk files from APKPure...')
# Get latest patches using the ReVanced API # Get latest patches using the ReVanced API
try: try:
patches = req.get('https://releases.revanced.app/patches').json() patches = req.get('https://releases.revanced.app/patches').json()
except req.exceptions.RequestException as e: except req.exceptions.RequestException as e:
sys.exit(e) clean_exit(e, appstate)
session = req.Session() session = req.Session()
session.headers.update({'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/116.0'}) 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'] pretty_name = build_config[app]['pretty_name']
apkpure_appname = build_config[app]['apkpure_appname'] apkpure_appname = build_config[app]['apkpure_appname']
except: 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}...") print(f"Checking {pretty_name}...")
try: 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) apkpure_dl(apk, apkpure_appname, str(required_ver), hard_version, session, present_vers, flag)
present_vers.update({apk: str(required_ver)}) present_vers.update({apk: str(required_ver)})
return present_vers
appstate['present_vers'] = present_vers
return appstate

42
Cleanup.py Normal file
View file

@ -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)

View file

@ -3,9 +3,13 @@ import sys
import configparser as cp import configparser as cp
import json import json
import subprocess import subprocess
from Cleanup import clean_exit
# Build the revanced apps # 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 = cp.ConfigParser()
chosen_patches.read('chosen_patches.toml') chosen_patches.read('chosen_patches.toml')
@ -55,7 +59,7 @@ def build_apps(build_config, flag):
apkpure_appname = build_config[app]['apkpure_appname'] apkpure_appname = build_config[app]['apkpure_appname']
output_name = build_config[app]['output_name'] output_name = build_config[app]['output_name']
except: 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" cmd += f" -a {apk}.apk -o {output_name}.apk"
@ -64,15 +68,13 @@ def build_apps(build_config, flag):
else: else:
print(f"Building {pretty_name} (nonroot)...") print(f"Building {pretty_name} (nonroot)...")
# if os.system(cmd) != 0:
# sys.exit('There was an error while building!')
try: try:
output = subprocess.check_output(cmd, shell=True, stderr=subprocess.STDOUT, text=True) output = subprocess.run(cmd, shell=True)
except Exception as e: 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: try:
os.rename(output_name+'.apk', output_name+'.apk') # TODO: Add timestamp here os.rename(output_name+'.apk', output_name+'.apk') # TODO: Add timestamp here
except FileNotFoundError: 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)

77
Notifications.py Normal file
View file

@ -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)

View file

@ -9,29 +9,29 @@ from packaging.version import Version
from APKPure_dl import * from APKPure_dl import *
from JAVABuilder import * from JAVABuilder import *
from datetime import datetime from datetime import datetime
from Notifications import send_notif
from Cleanup import *
# TODO: Logging # TODO: Logging
# TODO: Notifications # 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: Run post_script (preferably in any language)
# TODO: README # TODO: README
# TODO: PATCHES_GUIDE.md (maybe delete it?)
# Update the ReVanced tools, if needed # Update the ReVanced tools, if needed
def update_tools(): def update_tools(appstate):
for item in ['revanced-cli', 'revanced-integrations', 'revanced-patches']: for item in ['revanced-cli', 'revanced-integrations', 'revanced-patches']:
*_, tool = filter(lambda x: x['repository'] == 'revanced/'+item, tools) # Get the last result *_, tool = filter(lambda x: x['repository'] == 'revanced/'+item, tools) # Get the last result
latest_ver = Version(tool['version']) latest_ver = Version(tool['version'])
try: try:
present_ver = Version(present_vers[item]) present_ver = Version(appstate['present_vers'][item])
except KeyError: except KeyError:
present_ver = Version('0') present_ver = Version('0')
output_file = item+os.path.splitext(tool['name'])[1] output_file = item+os.path.splitext(tool['name'])[1]
if flag == 'force' or not os.path.isfile(output_file) or present_ver < latest_ver: if flag == 'force' or not os.path.isfile(output_file) or present_ver < latest_ver:
global up_to_date appstate['up-to-date'] = False
up_to_date = False
print(f"{item} has an update ({str(present_ver)} -> {str(latest_ver)})") print(f"{item} has an update ({str(present_ver)} -> {str(latest_ver)})")
if flag != 'checkonly': if flag != 'checkonly':
print(f"Downloading {output_file}...") print(f"Downloading {output_file}...")
@ -40,25 +40,26 @@ def update_tools():
with open(output_file, 'wb') as f: with open(output_file, 'wb') as f:
for chunk in res.iter_content(chunk_size=8192): for chunk in res.iter_content(chunk_size=8192):
f.write(chunk) f.write(chunk)
present_vers.update({item: str(latest_ver)}) appstate['present_vers'].update({item: str(latest_ver)})
print("Done!") print("Done!")
return appstate
# Update microG, if needed # Update microG, if needed
def update_microg(): def update_microg(appstate):
try: try:
data = req.get('https://api.github.com/repos/inotia00/VancedMicroG/releases/latest').json()['tag_name'] data = req.get('https://api.github.com/repos/inotia00/VancedMicroG/releases/latest').json()['tag_name']
latest_ver = Version(data) latest_ver = Version(data)
except req.exceptions.RequestException as e: except req.exceptions.RequestException as e:
sys.exit(e) clean_exit(e, appstate)
try: try:
present_ver = Version(present_vers['VancedMicroG']) present_ver = Version(appstate['present_vers']['VancedMicroG'])
except KeyError: except KeyError:
present_ver = Version('0') present_ver = Version('0')
if flag == 'force' or not os.path.isfile('microg.apk') or present_ver < latest_ver: if flag == 'force' or not os.path.isfile('microg.apk') or present_ver < latest_ver:
global up_to_date appstate['up-to-date'] = False
up_to_date = False
print(f"Vanced microG has an update ({str(present_ver)} -> {str(latest_ver)})") print(f"Vanced microG has an update ({str(present_ver)} -> {str(latest_ver)})")
if flag != 'checkonly': if flag != 'checkonly':
print(f"Downloading vanced-microg.apk...") print(f"Downloading vanced-microg.apk...")
@ -67,105 +68,83 @@ def update_microg():
with open('microg.apk', 'wb') as f: with open('microg.apk', 'wb') as f:
for chunk in res.iter_content(chunk_size=8192): for chunk in res.iter_content(chunk_size=8192):
f.write(chunk) f.write(chunk)
present_vers.update({'VancedMicroG': str(latest_ver)}) appstate['present_vers'].update({'VancedMicroG': str(latest_ver)})
print("Done!") print("Done!")
# Move apps to proper location return appstate
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()
# ------------------------------ # ------------------------------
# The main function starts here # The main function starts here
# ------------------------------ # ------------------------------
# Create a dict for storing important data
appstate = {}
# Get a timestamp # Get a timestamp
time = datetime.now() 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(f"Started building ReVanced apps at {time.strftime('%d %B, %Y %H:%M:%S')}")
print('----------------------------------------------------------------------')
# Read configs # Read configs
try: try:
os.chdir(sys.argv[1]) os.chdir(sys.argv[1])
except IndexError: except IndexError:
sys.exit('Please provide a working directory as argument!') clean_exit('Please provide a working directory as argument!', appstate)
except FileNotFoundError: except FileNotFoundError:
sys.exit('Invalid working directory provided!') clean_exit('Invalid working directory provided!', appstate)
try: try:
flag = sys.argv[2] flag = sys.argv[2]
except: except:
flag = None flag = None
appstate['flag'] = flag
try: try:
build_config=cp.ConfigParser() appstate['build_config']=cp.ConfigParser()
build_config.read_file(open('build_config.toml', 'r')) appstate['build_config'].read_file(open('build_config.toml', 'r'))
except FileNotFoundError: 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() appstate['notification_config'] = cp.ConfigParser()
appstate['notification_config'].read('notification_config.toml')
notification_config = cp.ConfigParser()
notification_config.read('notification_config.toml')
# Pull the latest information using the ReVanced API # Pull the latest information using the ReVanced API
try: try:
tools = req.get('https://releases.revanced.app/tools').json()['tools'] tools = req.get('https://releases.revanced.app/tools').json()['tools']
except req.exceptions.RequestException as e: except req.exceptions.RequestException as e:
sys.exit(e) clean_exit(e, appstate)
global present_vers
try: try:
with open('versions.json', 'r') as f: with open('versions.json', 'r') as f:
present_vers = json.load(f) appstate['present_vers'] = json.load(f)
except: except:
# We'll treat empty as 0 later # We'll treat empty as 0 later
present_vers = json.loads('{}') appstate['present_vers'] = json.loads('{}')
global up_to_date
up_to_date = True
appstate['up-to-date'] = True
# send_notif(appstate, error=False) # <,,,,,,,,<,,,,,,,,,,,,,
if flag != 'buildonly': if flag != 'buildonly':
update_tools() appstate = update_tools(appstate)
update_microg() appstate = update_microg(appstate)
if not up_to_date or flag == 'force': if not appstate['up-to-date'] or flag == 'force':
present_vers = get_apks(present_vers, build_config, flag) appstate = get_apks(appstate)
if (flag != 'checkonly' and not up_to_date) or flag in ['force', 'buildonly']: if (flag != 'checkonly' and not appstate['up-to-date']) or flag in ['force', 'buildonly']:
build_apps(build_config, flag) build_apps(appstate)
move_apps() move_apps(appstate)
# Update version numbers in the versions.json file # 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.') print('There\'s nothing to do.')
elif flag not in ['checkonly', 'buildonly']: elif flag != ['checkonly']:
with open('versions.json', 'w') as f: send_notif(appstate)
json.dump(present_vers, f, indent=4) 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)