pytrainer: unpin python 3.10
[NixPkgs.git] / pkgs / games / minecraft-servers / update.py
blobf272c8b71a84532a96336eee143598a1c87fab38
1 #!/usr/bin/env nix-shell
2 #!nix-shell -i python3 -p python3Packages.requests python3Packages.dataclasses-json
4 import json
5 from dataclasses import dataclass, field
6 from datetime import datetime
7 from pathlib import Path
8 from typing import Any, Dict, List, Optional
10 import requests
11 from dataclasses_json import DataClassJsonMixin, LetterCase, config
12 from marshmallow import fields
15 @dataclass
16 class Download(DataClassJsonMixin):
17 sha1: str
18 size: int
19 url: str
22 @dataclass
23 class Version(DataClassJsonMixin):
24 id: str
25 type: str
26 url: str
27 time: datetime = field(
28 metadata=config(
29 encoder=datetime.isoformat,
30 decoder=datetime.fromisoformat,
31 mm_field=fields.DateTime(format="iso"),
34 release_time: datetime = field(
35 metadata=config(
36 encoder=datetime.isoformat,
37 decoder=datetime.fromisoformat,
38 mm_field=fields.DateTime(format="iso"),
39 letter_case=LetterCase.CAMEL,
43 def get_manifest(self) -> Any:
44 """Return the version's manifest."""
45 response = requests.get(self.url)
46 response.raise_for_status()
47 return response.json()
49 def get_downloads(self) -> Dict[str, Download]:
50 """
51 Return all downloadable files from the version's manifest, in Download
52 objects.
53 """
54 return {
55 download_name: Download.from_dict(download_info)
56 for download_name, download_info in self.get_manifest()["downloads"].items()
59 def get_java_version(self) -> Any:
60 """
61 Return the java version specified in a version's manifest, if it is
62 present. Versions <= 1.6 do not specify this.
63 """
64 return self.get_manifest().get("javaVersion", {}).get("majorVersion", None)
66 def get_server(self) -> Optional[Download]:
67 """
68 If the version has a server download available, return the Download
69 object for the server download. If the version does not have a server
70 download avilable, return None.
71 """
72 downloads = self.get_downloads()
73 if "server" in downloads:
74 return downloads["server"]
75 return None
78 def get_versions() -> List[Version]:
79 """Return a list of Version objects for all available versions."""
80 response = requests.get(
81 "https://launchermeta.mojang.com/mc/game/version_manifest.json"
83 response.raise_for_status()
84 data = response.json()
85 return [Version.from_dict(version) for version in data["versions"]]
88 def get_major_release(version_id: str) -> str:
89 """
90 Return the major release for a version. The major release for 1.17 and
91 1.17.1 is 1.17.
92 """
93 if not len(version_id.split(".")) >= 2:
94 raise ValueError(f"version not in expected format: '{version_id}'")
95 return ".".join(version_id.split(".")[:2])
98 def group_major_releases(releases: List[Version]) -> Dict[str, List[Version]]:
99 """
100 Return a dictionary containing each version grouped by each major release.
101 The key "1.17" contains a list with two Version objects, one for "1.17"
102 and another for "1.17.1".
104 groups: Dict[str, List[Version]] = {}
105 for release in releases:
106 major_release = get_major_release(release.id)
107 if major_release not in groups:
108 groups[major_release] = []
109 groups[major_release].append(release)
110 return groups
113 def get_latest_major_releases(releases: List[Version]) -> Dict[str, Version]:
115 Return a dictionary containing the latest version for each major release.
116 The latest major release for 1.16 is 1.16.5, so the key "1.16" contains a
117 Version object for 1.16.5.
119 return {
120 major_release: max(
121 (release for release in releases if get_major_release(release.id) == major_release),
122 key=lambda x: tuple(map(int, x.id.split('.'))),
124 for major_release in group_major_releases(releases)
128 def generate() -> Dict[str, Dict[str, str]]:
130 Return a dictionary containing the latest url, sha1 and version for each major
131 release.
133 versions = get_versions()
134 releases = list(
135 filter(lambda version: version.type == "release", versions)
136 ) # remove snapshots and betas
137 latest_major_releases = get_latest_major_releases(releases)
139 servers = {
140 version: Download.schema().dump(download_info) # Download -> dict
141 for version, download_info in {
142 version: value.get_server()
143 for version, value in latest_major_releases.items()
144 }.items()
145 if download_info is not None # versions < 1.2 do not have a server
147 for server in servers.values():
148 del server["size"] # don't need it
150 for version, server in servers.items():
151 server["version"] = latest_major_releases[version].id
152 server["javaVersion"] = latest_major_releases[version].get_java_version()
153 return servers
156 if __name__ == "__main__":
157 with open(Path(__file__).parent / "versions.json", "w") as file:
158 json.dump(generate(), file, indent=2)
159 file.write("\n")