From 64202e769969817ce29fe970903ffd3feb9f16e4 Mon Sep 17 00:00:00 2001 From: Victor Westerlund Date: Sun, 22 Nov 2020 20:29:52 +0100 Subject: [PATCH 1/3] Draft: Visibility Work in progress; Change visibility of cosmetics with 'Visibility()' Currently not working. Getting HTTP Conflict response from endpoint. Needs further investigation --- .gitignore | 3 ++- labylib/Cape.py | 65 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 264daca..b1687e0 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -*__pycache__ \ No newline at end of file +*__pycache__ +dev* \ No newline at end of file diff --git a/labylib/Cape.py b/labylib/Cape.py index fcac83d..e731760 100644 --- a/labylib/Cape.py +++ b/labylib/Cape.py @@ -92,6 +92,71 @@ class Texture: data = self.body ) + # Raise exception if request fails + # Use [3:5] to clean up junk chars from reponse body + if(str(request.text)[3:5] != "OK"): + raise RequestError(str(request.text)) + +class Visibility: + + endpoint = "https://www.labymod.net/api/change" + + def __init__(self,cookie,value): + self.validate(cookie,value) + + self.cookies = dict(PHPSESSID = cookie) + + self.headers = { + "accept": "*/*", + "accept-encoding": "gzip, deflate, br", + "accept-language": "en-US,en;q=0.9,sv;q=0.8", + "cache-control": "no-cache", + "dnt": "1", + "user-agent": "Mozilla/5.0 (Windows NT 5.1; rv:33.0) Gecko/20100101 Firefox/33.0", + "origin": "https://www.labymod.net", + "pragma": "no-cache", + "referer": "https://www.labymod.net/dashboard", + "sec-fetch-dest": "empty", + "sec-fetch-mode": "cors", + "sec-fetch-site": "same-origin", + "x-requested-with": "XMLHttpRequest", + "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8" + } + + self.body = "" + + # Payload + self.addEncodedFormData("type","switch") + self.addEncodedFormData("value",value) + self.addEncodedFormData("item",459595) + self.addEncodedFormData("site","control") + + # ----------------------------------- + + def validate(self,cookie,file): + return True + + # Add URLEncoded form data (x-www-form-urlencoded) + def addEncodedFormData(self,key,value): + body = "&" + + # Remove '&' delimiter for first item + if(self.body == ""): + body = "" + + body += f"{key}={value}" + + self.body += body + + # ----------------------------------- + + def update(self): + request = requests.post(Texture.endpoint, + headers = self.headers, + cookies = self.cookies, + data = self.body + ) + # Raise exception if request fails # Use [3:5] to clean up junk chars from reponse body if(str(request.text)[3:5] != "OK"): From 18b6ef34e40e97046a1bff4e842b72de0b7ea7b5 Mon Sep 17 00:00:00 2001 From: Victor Westerlund Date: Mon, 30 Nov 2020 01:18:45 +0100 Subject: [PATCH 2/3] Added new cosmetic 'Hat' and finished 'Cape' All customizable elements of 'Hat' and 'Cape' are now implemented. Values are now submitted directly from the 'update()' which means you can easily chain actions without having to create a new instance. Removed 'validate()' for now. Removed '__init__.py' from repo root as labylib should be installed as a Python package. --- __init__.py | 1 - labylib/Cape.py | 130 ++++++++++++++++++++++++++++++++++++++---------- labylib/Hat.py | 126 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 231 insertions(+), 26 deletions(-) delete mode 100644 __init__.py create mode 100644 labylib/Hat.py diff --git a/__init__.py b/__init__.py deleted file mode 100644 index d6beb08..0000000 --- a/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from labylib.labylib import Cape \ No newline at end of file diff --git a/labylib/Cape.py b/labylib/Cape.py index e731760..a96f5ec 100644 --- a/labylib/Cape.py +++ b/labylib/Cape.py @@ -2,15 +2,11 @@ import requests import hashlib import time -class RequestError(Exception): pass - class Texture: endpoint = "https://www.labymod.net/page/php/cape.php" - def __init__(self,cookie,img): - self.validate(cookie,img) - + def __init__(self,cookie): self.body = b"" # Initialize request body self.cookies = dict(PHPSESSID = cookie) self.boundary = self.boundary() @@ -33,13 +29,9 @@ class Texture: } self.appendBinaryFormData(b"cosmetic",b"cape") - self.appendBinaryFormData(b"file",self.bOpen(img)) # ----------------------------------- - def validate(self,cookie,file): - return True - # Generate boundary header from MD5-hash of current time def boundary(self): seed = str(time.time()) @@ -83,7 +75,9 @@ class Texture: # ----------------------------------- - def update(self): + def update(self,img): + self.appendBinaryFormData(b"file",self.bOpen(img)) + self.closeBinaryFormData() # Add final boundary header request = requests.post(Texture.endpoint, @@ -93,17 +87,98 @@ class Texture: ) # Raise exception if request fails - # Use [3:5] to clean up junk chars from reponse body - if(str(request.text)[3:5] != "OK"): - raise RequestError(str(request.text)) + request.raise_for_status() + +class Template: + + endpoint = "https://www.labymod.net/page/php/setCapeTpl.php" + templates = { + "labymod": "10_LABYMOD.png", + "minecon2011": "30_MINECON2011.png", + "minecon2012": "30_MINECON2012.png", + "minecon2013": "30_MINECON2013.png", + "minecon2015": "30_MINECON2015.png", + "minecon2016": "30_MINECON2016.png", + "minecon2019": "30_MINECON2019.png", + "prismarine": "30_PRISMARINE.png", + "christmas2010": "40_CHRISTMAS2010.png", + "cobalt": "40_COBALT.png", + "julianclark": "40_JULIANCLARK.png", + "mapmaker": "40_MAPMAKER.png", + "mojira": "40_MOJIRA.png", + "mrmessiah": "40_MRMESSIAH.png", + "newyear": "40_NEWYEAR.png", + "scrolls": "40_SCROLLS.png", + "translator": "40_TRANSLATOR.png", + "turtle": "40_TURTLE.png", + "winner": "40_WINNER.png" + } + + def __init__(self,cookie): + self.cookies = dict(PHPSESSID = cookie) + + self.headers = { + "accept": "*/*", + "accept-encoding": "gzip, deflate, br", + "accept-language": "en-US,en;q=0.9,sv;q=0.8", + "cache-control": "no-cache", + "dnt": "1", + "user-agent": "Mozilla/5.0 (Windows NT 5.1; rv:33.0) Gecko/20100101 Firefox/33.0", + "origin": "https://www.labymod.net", + "pragma": "no-cache", + "referer": "https://www.labymod.net/dashboard", + "sec-fetch-dest": "empty", + "sec-fetch-mode": "cors", + "sec-fetch-site": "same-origin", + "x-requested-with": "XMLHttpRequest", + "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8" + } + + self.body = "" + + # Payload + self.addEncodedFormData("cosmetic","cape") + + # ----------------------------------- + + # Add URLEncoded form data (x-www-form-urlencoded) + def addEncodedFormData(self,key,value): + body = "&" + + # Remove '&' delimiter for first item + if(self.body == ""): + body = "" + + body += f"{key}={value}" + + self.body += body + + # ----------------------------------- + + def update(self,value): + value = value.lower() + + if(value not in Template.templates): + raise ValueError(f"'{value}' is not a valid template.") + + texture = Template.templates[value] + + self.addEncodedFormData("cape",texture) + + request = requests.post(Template.endpoint, + headers = self.headers, + cookies = self.cookies, + data = self.body + ) + + # Raise exception if request fails + request.raise_for_status() class Visibility: endpoint = "https://www.labymod.net/api/change" - def __init__(self,cookie,value): - self.validate(cookie,value) - + def __init__(self,cookie): self.cookies = dict(PHPSESSID = cookie) self.headers = { @@ -127,15 +202,11 @@ class Visibility: # Payload self.addEncodedFormData("type","switch") - self.addEncodedFormData("value",value) self.addEncodedFormData("item",459595) self.addEncodedFormData("site","control") # ----------------------------------- - def validate(self,cookie,file): - return True - # Add URLEncoded form data (x-www-form-urlencoded) def addEncodedFormData(self,key,value): body = "&" @@ -150,14 +221,23 @@ class Visibility: # ----------------------------------- - def update(self): - request = requests.post(Texture.endpoint, + def update(self,value): + # Interpret strings + if(type(value) != int): + if(value == "show"): + value = 1 + elif(value == "hide"): + value = 0 + else: + raise ValueError(f"'{value}' is not a valid visibility state.") + + self.addEncodedFormData("value",value) + + request = requests.post(Visibility.endpoint, headers = self.headers, cookies = self.cookies, data = self.body ) # Raise exception if request fails - # Use [3:5] to clean up junk chars from reponse body - if(str(request.text)[3:5] != "OK"): - raise RequestError(str(request.text)) \ No newline at end of file + request.raise_for_status() \ No newline at end of file diff --git a/labylib/Hat.py b/labylib/Hat.py new file mode 100644 index 0000000..13df56d --- /dev/null +++ b/labylib/Hat.py @@ -0,0 +1,126 @@ +import requests + +class Texture: + + endpoint = "https://www.labymod.net/api/change" + + def __init__(self,cookie): + self.cookies = dict(PHPSESSID = cookie) + + self.headers = { + "accept": "*/*", + "accept-encoding": "gzip, deflate, br", + "accept-language": "en-US,en;q=0.9,sv;q=0.8", + "cache-control": "no-cache", + "dnt": "1", + "user-agent": "Mozilla/5.0 (Windows NT 5.1; rv:33.0) Gecko/20100101 Firefox/33.0", + "origin": "https://www.labymod.net", + "pragma": "no-cache", + "referer": "https://www.labymod.net/dashboard", + "sec-fetch-dest": "empty", + "sec-fetch-mode": "cors", + "sec-fetch-site": "same-origin", + "x-requested-with": "XMLHttpRequest", + "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8" + } + + self.body = "" + + # Payload + self.addEncodedFormData("type","country") + self.addEncodedFormData("item",459594) + self.addEncodedFormData("site","control") + + # ----------------------------------- + + # Add URLEncoded form data (x-www-form-urlencoded) + def addEncodedFormData(self,key,value): + body = "&" + + # Remove '&' delimiter for first item + if(self.body == ""): + body = "" + + body += f"{key}={value}" + + self.body += body + + # ----------------------------------- + + def update(self,value): + self.addEncodedFormData("value",value) + + request = requests.post(Texture.endpoint, + headers = self.headers, + cookies = self.cookies, + data = self.body + ) + + # Raise exception if request fails + request.raise_for_status() + +class Visibility: + + endpoint = "https://www.labymod.net/api/change" + + def __init__(self,cookie): + self.cookies = dict(PHPSESSID = cookie) + + self.headers = { + "accept": "*/*", + "accept-encoding": "gzip, deflate, br", + "accept-language": "en-US,en;q=0.9,sv;q=0.8", + "cache-control": "no-cache", + "dnt": "1", + "user-agent": "Mozilla/5.0 (Windows NT 5.1; rv:33.0) Gecko/20100101 Firefox/33.0", + "origin": "https://www.labymod.net", + "pragma": "no-cache", + "referer": "https://www.labymod.net/dashboard", + "sec-fetch-dest": "empty", + "sec-fetch-mode": "cors", + "sec-fetch-site": "same-origin", + "x-requested-with": "XMLHttpRequest", + "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8" + } + + self.body = "" + + # Payload + self.addEncodedFormData("type","switch") + self.addEncodedFormData("item",459594) + self.addEncodedFormData("site","control") + + # ----------------------------------- + + # Add URLEncoded form data (x-www-form-urlencoded) + def addEncodedFormData(self,key,value): + body = "&" + + # Remove '&' delimiter for first item + if(self.body == ""): + body = "" + + body += f"{key}={value}" + + self.body += body + + # ----------------------------------- + + def update(self,value): + # Interpret strings + if(type(value) != int): + if(value == "show"): + value = 1 + else: + value = 0 + + self.addEncodedFormData("value",value) + + request = requests.post(Visibility.endpoint, + headers = self.headers, + cookies = self.cookies, + data = self.body + ) + + # Raise exception if request fails + request.raise_for_status() \ No newline at end of file From 2be9c262525c1807f4a6016b58828e88f49d1dbe Mon Sep 17 00:00:00 2001 From: Victor Westerlund <35688133+VictorWesterlund@users.noreply.github.com> Date: Mon, 30 Nov 2020 03:45:37 +0100 Subject: [PATCH 3/3] Update README.md --- README.md | 103 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 58 insertions(+), 45 deletions(-) diff --git a/README.md b/README.md index 2a1501c..17185ce 100644 --- a/README.md +++ b/README.md @@ -15,61 +15,74 @@ _This program is offered as-is and might stop working at any time_ ## Installation 1. Download and install [Python 3](https://www.python.org/downloads/) for your architecture. -2. Clone this repo to your machine, or [download a zip](/VictorWesterlund/labylib/archive/master.zip) +2. Install the latest version of labylib with [`pip3`](https://pypi.org/project/labylib/) ```bash -$ git clone https://github.com/VictorWesterlund/labylib/ -$ gh repo clone VictorWesterlund/labylib +$ python3 -m pip install labylib +``` +*..or if that doesn't work* +```python +$ pip3 install labylib ``` ## Quickstart -**1. Import a cosmetics module from `labylib/`.** - -[A list of all modules and classes can be found here](https://github.com/VictorWesterlund/labylib/wiki/Module-reference-sheet) -```python -from labylib import Cape +1. Import a labylib Module from the [list of available modules](https://github.com/VictorWesterlund/labylib/wiki/labylib-Modules). +```python3 +from labylib import ``` -**2. Initialize a module class.** - -All labylib classes take a `PHPSESSID` as their first argument. - -_Example with `Cape` where a file-path is expected as a second argument:_ -```python -texture = Cape.Texture("","") # labylib = Cape.Texture("772nnas663jkc8ahbb2","/home/VicW/coolCape-2.png") +2. Each Module comes with a set of classes available to each cosmetic. Pick a class for your Module. (`Visibility`,`Texture` etc.) +3. Initialize the class by passing it a `PHPSESSID`
+[**Here's what it is and where to find it**](https://github.com/VictorWesterlund/labylib/wiki/Find-your-PHPSESSID) +```python3 +# Example +cape_vis = Cape.Visibility(PHPSESSID) +``` +4. Call `update()` with a value expected by the class. Just like Modules, the value expected depends on the class. +```python3 +# Example +cape_vis.update("show") ``` -**3. Submit a cosmetic update** -```python -texture.update() -``` -Python's [Built-in-exceptions](https://docs.python.org/3/library/exceptions.html#exception-hierarchy) are rasied as needed for missing texture files (`FileNotFoundError`) etc. If a request was sucuessfully sent to the Labymod endpoint, but it returned something falsey (not `OK`). A custom-defined `RequestError` exception will be raised; containing the response received from the endpoint. -```python -try: - texture.update() -except RequestError as error: - print("Caugh RequestError exception:" + error) -# "Caugh RequestError exception: Session expired." +# Advanced Usage +## Request headers, cookies and body +Each class instance can be modified before `update()` is called to make changes to the request headers, cookies etc. You can even add additional encoded form data to the request body if necessary. + +labylib uses [`Requests`](https://requests.readthedocs.io/en/master/) under the hood and request parameters like headers and cookies can be modified in accordance with `Request`'s conventions. +```python3 +# This will send add a "foo=bar" cookie and header with the request +cape_vis.cookies["foo"] = "bar" +cape_vis.headers["foo"] = "bar" + +cape_vis.update("show") ``` -## Advanced usage -### HTTP POST Headers -Request headers and cookies can be accessed and modified pre-submission by applying standard list modifications on the followin objects: `self.headers` and `self.cookies` -```python -texture = Cape.Texture("","") +### To append form data to the request body of an instance: -texture.headers["Origin"] = "https://example.com/" -texture.cookies["Foo"] = "Bar" - -labylib.update() +**For `x-www-form-urlencoded` requests:** Append form data with the `addEncodedFormData(key,value)` method: +```python3 +# This will add "foo=bar" to the URL encoded payload +cape_vis.addEncodedFormData("foo","bar") +cape_vis.update("show") ``` -### HTTP POST Body -Binary form-data can be appended by calling `self.appendBinaryFormData(name,payload)`. -```python -texture = Cape.Texture("","") -texture.appendBinaryFormData(b"foo",b"bar") -texture.appendBinaryFormData(b"file","/home/VicW/home/VicW/coolCape-2.png") # Note that 'payload' is a String in this case (as opposed to Binary) +**For `multipart/form-data` requests:** Append binary form data with the `addBinaryFormData(key,payload)` method: +```python3 +# This will create a new payload boundary containing "foo=bar" +cape_texture.addBinaryFormData(b"foor",b"bar") +cape_texture.update("show") ``` -Special form-data types ('names'): -|name|Description -|--|--| -|`'file'`| Submit cosmetic texture file as BLOB by passing `payload` a binary-encoded PNG.
_`self.bOpen()` can be used to 'open()' a file as binary string._ +You can also append `image/png` files by passing "file" as the `key` argument. You can either pass binary data directly as a BLOB to `payload` or use `bOpen()` to load an image from disk: +```python3 +# This will create a new payload boundary with a "Content-Type: image/png" header and BLOB body +cape_texture.addBinaryFormData(b"file",cape_vis.bOpen("~/someImage.png")) +cape_texture.update("~/myAwesomeTexture.png") +``` + +## Contribute + +If you find any bugs with- or would like to suggest features to labylib, please submit them under [Issues](https://github.com/VictorWesterlund/labylib/issues) + +Pull requests to labylib are highly encouraged! + +## License + +[GNU General Public License v3.0](https://github.com/VictorWesterlund/labylib/blob/master/LICENSE)