Skip to content

Instantly share code, notes, and snippets.

@Amperthorpe
Last active April 12, 2019 22:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Amperthorpe/ea526d0cbebe87004599d9af9b8da277 to your computer and use it in GitHub Desktop.
Save Amperthorpe/ea526d0cbebe87004599d9af9b8da277 to your computer and use it in GitHub Desktop.
Python script to handle ror2mm:// links, such as the Install buttons on Thunderstore.io.
#! /home/alleluid/.pyenv/ror2mm_venv/bin/python
"""
USE AT YOUR OWN RISK, IT WILL PROBABLY BREAK STUFF
Requires 'requests' and Python 3.6+
This script downloads the zip file to a temp location, then extracts
To use in Firefox, go to "about:config" and add a new boolean named "network.protocol-handler.expose.ror2mm" and set it to false.
When you next click on a ror2mm:// link it will ask what program to use, select this script.
For other browsers look up "<browser> custom protocol handler"
Made by Alleluid
"""
import sys
import os
import requests
import zipfile
import shutil
# Change this
USER_NAME = "alleluid"
# This should be the standard place for the game install on linux, change if needed. It auto escapes spaces.
bie_folder = f"/home/{USER_NAME}/.steam/steam/steamapps/common/Risk of Rain 2/BepInEx/"
zip_folder = "/tmp/" # It leaves the zips here if that matters to you.
os.chdir(zip_folder)
def main():
print("Started")
ror2mm_url: str = sys.argv[1]
print(ror2mm_url)
if not ror2mm_url.startswith("ror2mm://"):
print("Didn't start with ror2mm://")
exit(1)
invalid_url = ror2mm_url[9:]
print(f"Stripped: {invalid_url}")
split_url = invalid_url.split("/")
print(f"Split: {split_url}")
# assigns named vars for each section of the URL. The first is always "thunderstore.io", and at the end it leaves an empty string.
_ts, author, mod_name, version, *_ = split_url
final_url = f"https://thunderstore.io/package/download/{author}/{mod_name}/{version}/"
print(f"Final: {final_url}")
response = requests.get(final_url, allow_redirects=True, stream=True)
zip_file_name = f"{mod_name}.{version}.zip"
print(f"Saving in: {zip_folder}{zip_file_name}")
with open(zip_file_name, 'wb') as f:
for chunk in response.iter_content(chunk_size=512):
if chunk:
f.write(chunk)
zip_ref = zipfile.ZipFile(zip_folder+zip_file_name, 'r')
# Handle loose .dlls and .cfgs that might be at root level
for path in zip_ref.namelist():
split_path = path.split("/")
if split_path[0].endswith(".dll"):
# TODO: This doesn't work unless the terminal pops up
# in_folder = input(".dll file found in root. It probably should go in plugins, type the name of the folder if not: ")
#if not in_folder:
in_folder = "plugins"
zip_ref.extract(path, bie_folder+in_folder+"/")
elif split_path[0].endswith(".cfg"):
zip_ref.extract(path, bie_folder+"config/")
# Extract all files, and move possible roots to the right spots. Then delete the temp directory.
zip_ref.extractall()
possible_roots = ["BepInEx", "plugins", "config", "core", "monomod", "patchers"]
for root in possible_roots:
try:
copyTree(root, bie_folder if root == "BepInEx" else bie_folder+root)
shutil.rmtree(root)
except FileNotFoundError:
pass
# Yeah I copied this verbaitm from SO, who doesn't?
def forceMergeFlatDir(srcDir, dstDir):
if not os.path.exists(dstDir):
os.makedirs(dstDir)
for item in os.listdir(srcDir):
srcFile = os.path.join(srcDir, item)
dstFile = os.path.join(dstDir, item)
forceCopyFile(srcFile, dstFile)
def forceCopyFile (sfile, dfile):
if os.path.isfile(sfile):
shutil.copy2(sfile, dfile)
def isAFlatDir(sDir):
for item in os.listdir(sDir):
sItem = os.path.join(sDir, item)
if os.path.isdir(sItem):
return False
return True
def copyTree(src, dst):
for item in os.listdir(src):
s = os.path.join(src, item)
d = os.path.join(dst, item)
if os.path.isfile(s):
if not os.path.exists(dst):
os.makedirs(dst)
forceCopyFile(s,d)
if os.path.isdir(s):
isRecursive = not isAFlatDir(s)
if isRecursive:
copyTree(s, d)
else:
forceMergeFlatDir(s, d)
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment