Account actions, session, examples and readme

This commit is contained in:
DarkCat09 2022-04-01 17:00:02 +04:00
parent 75b10e6e8c
commit 03ca4098bc
8 changed files with 155 additions and 62 deletions

View file

@ -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
View 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
View 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)

View file

@ -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]

View 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()

View file

@ -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
@ -56,18 +55,29 @@ class Client:
atconn.generate_sec()
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
}
)

View file

@ -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

View file

@ -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: