|
| 1 | +# Classes and methods for interacting with turbowarp placeholder (https://share.turbowarp.org/) |
| 2 | +import re |
| 3 | +import json |
| 4 | +import bs4 |
| 5 | + |
| 6 | +from dataclasses import dataclass |
| 7 | +from typing_extensions import Optional |
| 8 | +from bs4 import BeautifulSoup |
| 9 | + |
| 10 | +from scratchattach.site import session |
| 11 | +from scratchattach.utils.requests import requests |
| 12 | +from scratchattach import editor |
| 13 | + |
| 14 | +@dataclass |
| 15 | +class PlaceholderProject: |
| 16 | + id: str |
| 17 | + |
| 18 | + title: Optional[str] = None |
| 19 | + description: Optional[str] = None |
| 20 | + md5exts_to_sha256: Optional[dict[str, str]] = None |
| 21 | + |
| 22 | + _session: Optional[session.Session] = None |
| 23 | + |
| 24 | + def get_json(self): |
| 25 | + with requests.no_error_handling(): |
| 26 | + return requests.get(f"https://share.turbowarp.org/api/projects/{self.id}").json() |
| 27 | + |
| 28 | + def update_by_html(self): |
| 29 | + """ |
| 30 | + Scrape JS to update the project. Hopefully this will not be necessary in the future, as it is very error-prone. |
| 31 | + """ |
| 32 | + |
| 33 | + with requests.no_error_handling(): |
| 34 | + resp = requests.get(f"https://share.turbowarp.org/projects/{self.id}") |
| 35 | + soup = BeautifulSoup(resp.text, "html.parser") |
| 36 | + |
| 37 | + for script in soup.find_all("script"): |
| 38 | + if not isinstance(script, bs4.element.Tag): |
| 39 | + continue |
| 40 | + |
| 41 | + if raw_data := re.search("const data = \\[.*\"data\":{metadata:{.*},md5extsToSha256:.*];", str(script.contents[0])): |
| 42 | + data = raw_data.group().removeprefix("const data = ").removesuffix(";") |
| 43 | + # this data is NOT json. Therefore, we can't just JSON.parse it. |
| 44 | + # it's actually native JavaScript, but we can extract the information in a really flimsy way using regex |
| 45 | + # This flimsy method would probably break with bad input, so maybe, instead, a request should be made to GarboMuffin. |
| 46 | + # If you want full JS support, use an automated browser. |
| 47 | + print(data) |
| 48 | + |
| 49 | + # get title |
| 50 | + pf, sf = 'metadata:{title:"', '",description' |
| 51 | + if group := re.search(pf+'.*?'+sf, data).group(): |
| 52 | + self.title = json.loads('"' + group.removeprefix(pf).removesuffix(sf) + '"') |
| 53 | + |
| 54 | + # Get description. It is probably very easy to break this regex |
| 55 | + pf, sf = 'description:"', '"},' |
| 56 | + if group := re.search(pf+'.*?'+sf, data).group(): |
| 57 | + self.description = json.loads('"' + group.removeprefix(pf).removesuffix(sf) + '"') |
| 58 | + |
| 59 | + pf, sf = 'md5extsToSha256:{', '},' |
| 60 | + if group := re.search(pf+'.*?'+sf, data).group(): |
| 61 | + self.md5exts_to_sha256 = json.loads('{' + group.removeprefix(pf).removesuffix(sf) + '}') |
| 62 | + |
| 63 | + break |
| 64 | + |
| 65 | + def get_project_body(self): |
| 66 | + data = self.get_json() |
| 67 | + body = editor.Project.from_json(data) |
| 68 | + |
| 69 | + for asset in body.assets: |
| 70 | + print(asset.md5ext) |
| 71 | + |
| 72 | + return body |
| 73 | + |
| 74 | + |
| 75 | +def get_placeholder_project(_id: str): |
| 76 | + return PlaceholderProject(_id) |
| 77 | + |
| 78 | +if __name__ == '__main__': |
| 79 | + p = get_placeholder_project("44c35afc-fe00-49d8-afe7-d71f4430c121") |
| 80 | + p.update_by_html() |
| 81 | + print(p) |
0 commit comments