#!/usr/bin/env nix-shell #! nix-shell -i python -p "python3.withPackages (ps: with ps; [ps.requests ])" import hashlib import base64 import json import requests class Version: def __init__(self, name: str): self.name: str = name self.hash: str | None = None self.build_number: int | None = None @property def full_name(self): v_name = f"{self.name}-{self.build_number}" # this will probably never happen because the download of a build with NoneType in URL would fail if not self.name or not self.build_number: print(f"Warning: version '{v_name}' contains NoneType!") return v_name class VersionManager: def __init__(self, base_url: str = "https://api.papermc.io/v2/projects/paper"): self.versions: list[Version] = [] self.base_url: str = base_url def fetch_versions(self, not_before_minor_version: int = 18): """ Fetch all versions after given minor release """ response = requests.get(self.base_url) try: response.raise_for_status() except requests.exceptions.HTTPError as e: print(e) return # we only want versions that are no pre-releases release_versions = filter( lambda v_name: 'pre' not in v_name, response.json()["versions"]) for version_name in release_versions: # split version string, convert to list ot int version_split = version_name.split(".") version_split = list(map(int, version_split)) # check if version is higher than 1. if (version_split[0] > 1) or (version_split[0] == 1 and version_split[1] >= not_before_minor_version): self.versions.append(Version(version_name)) def fetch_latest_version_builds(self): """ Set latest build number to each version """ for version in self.versions: url = f"{self.base_url}/versions/{version.name}" response = requests.get(url) # check that we've got a good response try: response.raise_for_status() except requests.exceptions.HTTPError as e: print(e) return # the highest build in response.json()['builds']: latest_build = response.json()['builds'][-1] version.build_number = latest_build def generate_version_hashes(self): """ Generate and set the hashes for all registered versions (versions will are downloaded to memory) """ for version in self.versions: url = f"{self.base_url}/versions/{version.name}/builds/{version.build_number}/downloads/paper-{version.full_name}.jar" version.hash = self.download_and_generate_sha256_hash(url) def versions_to_json(self): return json.dumps( {version.name: {'hash': version.hash, 'version': version.full_name} for version in self.versions}, indent=4 ) def write_versions(self, file_name: str): """ write all processed versions to json """ # save json to versions.json with open(file_name, 'w') as f: f.write(self.versions_to_json() + "\n") @staticmethod def download_and_generate_sha256_hash(url: str) -> str | None: """ Fetch the tarball from the given URL. Then generate a sha256 hash of the tarball. """ try: # Download the file from the URL response = requests.get(url) response.raise_for_status() except requests.exceptions.RequestException as e: print(f"Error: {e}") return None # Create a new SHA-256 hash object sha256_hash = hashlib.sha256() # Update the hash object with chunks of the downloaded content for byte_block in response.iter_content(4096): sha256_hash.update(byte_block) # Get the hexadecimal representation of the hash hash_value = sha256_hash.digest() # Encode the hash value in base64 base64_hash = base64.b64encode(hash_value).decode('utf-8') # Format it as "sha256-{base64_hash}" sri_representation = f"sha256-{base64_hash}" return sri_representation if __name__ == '__main__': version_manager = VersionManager() version_manager.fetch_versions() version_manager.fetch_latest_version_builds() version_manager.generate_version_hashes() version_manager.write_versions(file_name="versions.json")