Account actions, session, examples and readme
This commit is contained in:
parent
75b10e6e8c
commit
03ca4098bc
8 changed files with 155 additions and 62 deletions
57
README.md
57
README.md
|
@ -1,42 +1,37 @@
|
|||
# Python Aternos API
|
||||
An unofficial Aternos API written in Python.
|
||||
It uses requests, cloudscraper and lxml to parse data from [aternos.org](https://aternos.org/).
|
||||
> Note for vim: if you have a problem like `IndentationError: unindent does not match any outer indentation level`, try out `retab`.
|
||||
It uses [aternos](https://aternos.org/)' private API and html parsing.
|
||||
|
||||
## Using
|
||||
First you need to install the module:
|
||||
## Installation
|
||||
```bash
|
||||
pip install python-aternos
|
||||
```
|
||||
> Note for Windows users:
|
||||
Install `lxml` package from [here](https://www.lfd.uci.edu/~gohlke/pythonlibs/#lxml) if you have a problem with it,
|
||||
and then execute `pip install --no-deps python-aternos`
|
||||
|
||||
To use Aternos API in your Python script, import it and
|
||||
login with your username and password (or MD5 hash of password).
|
||||
> Note: Logging in with Google or Facebook account is not supported yet.
|
||||
## Usage
|
||||
To use Aternos API in your Python script, import it
|
||||
and login with your username and password/MD5.
|
||||
|
||||
Then get the servers list using the `servers` field.
|
||||
Then request the servers list using `list_servers()`.
|
||||
You can start/stop your Aternos server now, calling `start()` or `stop()`.
|
||||
|
||||
Here is an example how to use the Aternos API:
|
||||
Here is an example how to use the API:
|
||||
```python
|
||||
# Import
|
||||
from python_aternos import Client
|
||||
|
||||
# Log in
|
||||
#aternos = Client('USERNAME', password='PASSWORD')
|
||||
aternos = Client('example', password='test123')
|
||||
aternos = Client.from_credentials('example', 'test123')
|
||||
# ----OR----
|
||||
# password is the 1st parameter,
|
||||
# so you don't have to specify its name
|
||||
aternos = Client('example', 'test123')
|
||||
# ----OR----
|
||||
#aternos = Client('USERNAME', md5='HASHED_PASSWORD')
|
||||
aternos = Client('example', md5='cc03e747a6afbbcbf8be7668acfebee5')
|
||||
aternos = Client.from_hashed('example', 'cc03e747a6afbbcbf8be7668acfebee5')
|
||||
|
||||
# Returns AternosServer list
|
||||
atservers = aternos.servers
|
||||
servs = aternos.list_servers()
|
||||
|
||||
# If you have only one server, get it by the 0 index
|
||||
myserv = atservers[0]
|
||||
# Get the first server by the 0 index
|
||||
myserv = servs[0]
|
||||
|
||||
# Start
|
||||
myserv.start()
|
||||
|
@ -45,7 +40,7 @@ myserv.stop()
|
|||
|
||||
# You can also find server by IP
|
||||
testserv = None
|
||||
for serv in atservers:
|
||||
for serv in servs:
|
||||
if serv.address == 'test.aternos.org':
|
||||
testserv = serv
|
||||
if testserv != None:
|
||||
|
@ -55,21 +50,11 @@ if testserv != None:
|
|||
# Starts server
|
||||
testserv.start()
|
||||
```
|
||||
You can find full documentation on the [Project Wiki](https://github.com/DarkCat09/python-aternos/wiki).
|
||||
~~You can find full documentation on the [Project Wiki](https://github.com/DarkCat09/python-aternos/wiki).~~
|
||||
|
||||
## [More examples](/examples)
|
||||
|
||||
## Changelog
|
||||
<!--
|
||||
* v0.1 - the first release.
|
||||
* v0.2 - fixed import problem.
|
||||
* v0.3 - implemented files API, added typization.
|
||||
* v0.4 - implemented configuration API, some bugfixes.
|
||||
* v0.5 - the API was updated corresponding to new Aternos security methods.
|
||||
Huge thanks to [lusm554](https://github.com/lusm554).
|
||||
* v0.6 - implementation of Google Drive backups API is planned.
|
||||
* v0.7 - full implementation of config API is planned.
|
||||
* v0.8 - shared access API and permission management is planned.
|
||||
* v0.9.x - a long debugging before stable release, SemVer version code.
|
||||
-->
|
||||
|Version|Description|
|
||||
|:-----:|:-----------|
|
||||
|v0.1|The first release.|
|
||||
|
@ -77,7 +62,7 @@ You can find full documentation on the [Project Wiki](https://github.com/DarkCat
|
|||
|v0.3|Implemented files API, added typization.|
|
||||
|v0.4|Implemented configuration API, some bugfixes.|
|
||||
|v0.5|The API was updated corresponding to new Aternos security methods. Huge thanks to [lusm554](https://github.com/lusm554).|
|
||||
|v0.6|Preventing detecting automated access is planned.|
|
||||
|v0.6|Code refactoring, unit-tests, websocket API and session saving to prevent detecting automation access.|
|
||||
|v0.7|Full implementation of config API and Google Drive backups is planned.|
|
||||
|v0.8|Shared access API and permission management is planned.|
|
||||
|v0.9.x|A long debugging before stable release, SemVer version code.|
|
||||
|
@ -99,4 +84,4 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
```
|
||||
You **don't** need to attribute me, if you are just using this module installed from PIP.
|
||||
You **don't** need to attribute me, if you are just using this module installed from PIP or wheel.
|
||||
|
|
38
examples/files_example.py
Normal file
38
examples/files_example.py
Normal file
|
@ -0,0 +1,38 @@
|
|||
from getpass import getpass
|
||||
from python_aternos import Client, atserver
|
||||
|
||||
user = input('Username: ')
|
||||
pswd = getpass('Password: ')
|
||||
aternos = Client.from_credentials(user, pswd)
|
||||
|
||||
s = aternos.list_servers()[0]
|
||||
files = s.files()
|
||||
|
||||
while True:
|
||||
|
||||
cmd = input('> ').strip().lower()
|
||||
|
||||
if cmd == 'help':
|
||||
print(
|
||||
'''Commands list:
|
||||
help - show this message
|
||||
quit - exit from the script
|
||||
world - download the world
|
||||
list [path] - show directory (or root) contents'''
|
||||
)
|
||||
|
||||
if cmd == 'quit':
|
||||
break
|
||||
|
||||
if cmd.startswith('list'):
|
||||
path = cmd.removeprefix('list').strip()
|
||||
directory = files.listdir(path)
|
||||
|
||||
print(path, 'contains:')
|
||||
for file in directory:
|
||||
print('\t' + file.name)
|
||||
|
||||
if cmd == 'world':
|
||||
file = files.get_file('/world')
|
||||
with open('world.zip', 'wb') as f:
|
||||
f.write(file.get_content())
|
18
examples/info_example.py
Normal file
18
examples/info_example.py
Normal file
|
@ -0,0 +1,18 @@
|
|||
from getpass import getpass
|
||||
from python_aternos import Client, atserver
|
||||
|
||||
user = input('Username: ')
|
||||
pswd = getpass('Password: ')
|
||||
aternos = Client.from_credentials(user, pswd)
|
||||
|
||||
srvs = aternos.list_servers()
|
||||
for srv in srvs:
|
||||
print('*** ' + srv.domain + ' ***')
|
||||
print(srv.motd)
|
||||
print('*** Status:', srv.status)
|
||||
print('*** Full address:', srv.address)
|
||||
print('*** Port:', srv.port)
|
||||
print('*** Name:', srv.subdomain)
|
||||
print('*** Minecraft:', srv.software, srv.version)
|
||||
print('*** IsBedrock:', srv.edition == atserver.Edition.bedrock)
|
||||
print('*** IsJava:', srv.edition == atserver.Edition.java)
|
|
@ -5,7 +5,7 @@ user = input('Username: ')
|
|||
pswd = getpass('Password: ')
|
||||
aternos = Client.from_credentials(user, pswd)
|
||||
|
||||
srvs = aternos.servers
|
||||
srvs = aternos.list_servers()
|
||||
print(srvs)
|
||||
|
||||
s = srvs[0]
|
||||
|
|
15
examples/websocket_example.py
Normal file
15
examples/websocket_example.py
Normal file
|
@ -0,0 +1,15 @@
|
|||
from getpass import getpass
|
||||
from python_aternos import Client, atwss
|
||||
|
||||
user = input('Username: ')
|
||||
pswd = getpass('Password: ')
|
||||
aternos = Client.from_credentials(user, pswd)
|
||||
|
||||
s = aternos.list_servers()[0]
|
||||
socket = s.wss()
|
||||
|
||||
@socket.wssreceiver(atwss.Streams.console)
|
||||
def console(msg):
|
||||
print('Received: ' + msg)
|
||||
|
||||
s.start()
|
|
@ -1,3 +1,5 @@
|
|||
import os
|
||||
import re
|
||||
import hashlib
|
||||
import lxml.html
|
||||
from typing import List
|
||||
|
@ -28,11 +30,10 @@ class Client:
|
|||
|
||||
loginreq = atconn.request_cloudflare(
|
||||
f'https://aternos.org/panel/ajax/account/login.php',
|
||||
'POST', data=credentials,
|
||||
sendtoken=True
|
||||
'POST', data=credentials, sendtoken=True
|
||||
)
|
||||
|
||||
if loginreq.cookies.get('ATERNOS_SESSION', None) == None:
|
||||
if 'ATERNOS_SESSION' not in loginreq.cookies:
|
||||
raise CredentialsError(
|
||||
'Check your username and password'
|
||||
)
|
||||
|
@ -42,9 +43,7 @@ class Client:
|
|||
@classmethod
|
||||
def from_credentials(cls, username:str, password:str):
|
||||
|
||||
pswd_bytes = password.encode('utf-8')
|
||||
md5 = hashlib.md5(pswd_bytes).hexdigest().lower()
|
||||
|
||||
md5 = Client.md5encode(password)
|
||||
return cls.from_hashed(username, md5)
|
||||
|
||||
@classmethod
|
||||
|
@ -57,17 +56,28 @@ class Client:
|
|||
|
||||
return cls(atconn)
|
||||
|
||||
@classmethod
|
||||
def restore_session(cls, file:str='~/.aternos'):
|
||||
|
||||
file = os.path.expanduser(file)
|
||||
with open(file, 'rt') as f:
|
||||
session = f.read().strip()
|
||||
return cls.from_session(session)
|
||||
|
||||
@staticmethod
|
||||
def google() -> str:
|
||||
def md5encode(passwd:str) -> str:
|
||||
|
||||
atconn = AternosConnect()
|
||||
auth = atconn.request_cloudflare(
|
||||
'https://aternos.org/auth/google-login',
|
||||
'GET', redirect=False
|
||||
)
|
||||
return auth.headers['Location']
|
||||
encoded = hashlib.md5(passwd.encode('utf-8'))
|
||||
return encoded.hexdigest().lower()
|
||||
|
||||
def save_session(self, file:str='~/.aternos') -> None:
|
||||
|
||||
file = os.path.expanduser(file)
|
||||
with open(file, 'wt') as f:
|
||||
f.write(self.atconn.atsession)
|
||||
|
||||
def list_servers(self) -> List[AternosServer]:
|
||||
|
||||
def list_servers(self) -> List[atserver.AternosServer]:
|
||||
serverspage = self.atconn.request_cloudflare(
|
||||
'https://aternos.org/servers/', 'GET'
|
||||
)
|
||||
|
@ -80,3 +90,37 @@ class Client:
|
|||
servers.append(AternosServer(servid, self.atconn))
|
||||
|
||||
return servers
|
||||
|
||||
def get_server(self, servid:str) -> AternosServer:
|
||||
|
||||
return AternosServer(servid, self.atconn)
|
||||
|
||||
def change_username(self, value:str) -> None:
|
||||
|
||||
self.atconn.request_cloudflare(
|
||||
'https://aternos.org/panel/ajax/account/username.php',
|
||||
'POST', data={'username': value}
|
||||
)
|
||||
|
||||
def change_email(self, value:str) -> None:
|
||||
|
||||
email = re.compile(r'^[A-Za-z0-9\-_+.]+@[A-Za-z0-9\-_+.]+\.[A-Za-z0-9\-]+$|^$')
|
||||
if not email.match(value):
|
||||
raise ValueError('Invalid e-mail!')
|
||||
|
||||
self.atconn.request_cloudflare(
|
||||
'https://aternos.org/panel/ajax/account/email.php',
|
||||
'POST', data={'email': value}
|
||||
)
|
||||
|
||||
def change_password(self, old:str, new:str) -> None:
|
||||
|
||||
old = Client.md5encode(old)
|
||||
new = Client.md5encode(new)
|
||||
self.atconn.request_cloudflare(
|
||||
'https://aternos.org/panel/ajax/account/password.php',
|
||||
'POST', data={
|
||||
'oldpassword': old,
|
||||
'newpassword': new
|
||||
}
|
||||
)
|
||||
|
|
|
@ -138,7 +138,4 @@ class AternosConnect:
|
|||
f'{method} completed with {req.status_code} status'
|
||||
)
|
||||
|
||||
with open('debug.html', 'wb') as f:
|
||||
f.write(req.content)
|
||||
|
||||
return req
|
||||
|
|
|
@ -59,16 +59,12 @@ class AternosFile:
|
|||
def get_text(self) -> str:
|
||||
|
||||
editor = self.atserv.atserver_request(
|
||||
f'https://aternos.org/files/{self._full}', 'GET'
|
||||
f'https://aternos.org/files/{self._full.lstrip("/")}', 'GET'
|
||||
)
|
||||
edittree = lxml.html.fromstring(editor.content)
|
||||
|
||||
editlines = edittree.xpath('//div[@class="ace_line"]')
|
||||
rawlines = []
|
||||
|
||||
for line in editlines:
|
||||
rawlines.append(line.text)
|
||||
return rawlines
|
||||
editblock = edittree.xpath('//div[@id="editor"]')[0]
|
||||
return editblock.text_content()
|
||||
|
||||
def set_text(self, value:str) -> None:
|
||||
|
||||
|
|
Reference in a new issue