diff --git a/python_aternos/__init__.py b/python_aternos/__init__.py
index 0b99400..2805a67 100644
--- a/python_aternos/__init__.py
+++ b/python_aternos/__init__.py
@@ -22,7 +22,7 @@ class Client:
self.atconn = atconnect.AternosConnect()
- self.token = self.atconn.get_token()
+ self.token = self.atconn.parse_token()
self.sec = self.atconn.generate_sec()
self.credentials = {
@@ -33,7 +33,7 @@ class Client:
loginreq = self.atconn.request_cloudflare(
f'https://aternos.org/panel/ajax/account/login.php?' + \
f'SEC={self.sec}&TOKEN={self.token}',
- self.atconn.REQPOST, data=self.credentials
+ atconnect.REQPOST, data=self.credentials
)
if loginreq.cookies.get('ATERNOS_SESSION', None) == None:
@@ -41,11 +41,11 @@ class Client:
'Check your username and password'
)
- def get_servers(self):
-
+ @property
+ def servers(self):
serverspage = self.atconn.request_cloudflare(
'https://aternos.org/servers/',
- self.atconn.REQGET
+ atconnect.REQGET
)
serverstree = lxml.html.fromstring(serverspage.content)
serverslist = serverstree.xpath('//div[@class="servers"]/div')
diff --git a/python_aternos/atconnect.py b/python_aternos/atconnect.py
index c13515f..38cf54b 100644
--- a/python_aternos/atconnect.py
+++ b/python_aternos/atconnect.py
@@ -2,26 +2,27 @@ import re
import time
import random
import lxml.html
+from requests import Response
from cloudscraper import CloudScraper
+from typing import Optional, Union
from . import aterrors
+REQGET = 0
+REQPOST = 1
+REQUA = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:68.0) Gecko/20100101 Goanna/4.8 Firefox/68.0 PaleMoon/29.4.0.2'
+
class AternosConnect:
- REQGET = 0
- REQPOST = 1
- REQUA = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:68.0) Gecko/20100101 Goanna/4.8 Firefox/68.0 PaleMoon/29.4.0.2'
-
- def __init__(self):
+ def __init__(self) -> None:
pass
- def get_token(self, response=None):
+ def parse_token(self, response:Optional[Union[str,bytes]]=None) -> str:
if response == None:
loginpage = self.request_cloudflare(
- f'https://aternos.org/go/',
- self.REQGET
+ f'https://aternos.org/go/', REQGET
).content
pagetree = lxml.html.fromstring(loginpage)
else:
@@ -40,7 +41,7 @@ class AternosConnect:
return self.token
- def generate_sec(self):
+ def generate_sec(self) -> str:
randkey = self.generate_aternos_rand()
randval = self.generate_aternos_rand()
@@ -52,7 +53,7 @@ class AternosConnect:
return self.sec
- def generate_aternos_rand(self, randlen=16):
+ def generate_aternos_rand(self, randlen:int=16) -> str:
rand_arr = []
for i in range(randlen+1):
@@ -63,7 +64,7 @@ class AternosConnect:
'00000000000000000'
return (rand_alphanum[2:18].join(rand_arr)[:randlen])
- def convert_num(self, num, base):
+ def convert_num(self, num:Union[int,float], base:int) -> str:
result = ''
while num > 0:
@@ -72,9 +73,13 @@ class AternosConnect:
return result
def request_cloudflare(
- self, url, method, retries=10,
- params=None, data=None, headers=None,
- reqcookies=None, sendtoken=False):
+ self, url:str, method:int,
+ retries:int=10,
+ params:Optional[dict]=None,
+ data:Optional[dict]=None,
+ headers:Optional[dict]=None,
+ reqcookies:Optional[dict]=None,
+ sendtoken:bool=False) -> Response:
cftitle = '
Please Wait... | Cloudflare'
@@ -86,7 +91,7 @@ class AternosConnect:
if headers == None:
headers = {}
- headers['User-Agent'] = self.REQUA
+ headers['User-Agent'] = REQUA
try:
cookies = self.session.cookies
@@ -97,7 +102,7 @@ class AternosConnect:
if cookies != None:
self.session.cookies = cookies
- if method == self.REQPOST:
+ if method == REQPOST:
req = self.session.post(
url,
data=data,
@@ -124,7 +129,7 @@ class AternosConnect:
self.session.cookies.set(cookiekey, reqcookies[cookiekey])
time.sleep(1)
- if method == self.REQPOST:
+ if method == REQPOST:
req = self.session.post(
url,
data=data,
@@ -141,7 +146,3 @@ class AternosConnect:
countdown -= 1
return req
-
- def get_session(self):
-
- return self.session
diff --git a/python_aternos/atfile.py b/python_aternos/atfile.py
new file mode 100644
index 0000000..15fd8b6
--- /dev/null
+++ b/python_aternos/atfile.py
@@ -0,0 +1,61 @@
+import lxml.html
+from typing import Union
+
+from . import atserver
+from . import atconnect
+
+FTYPE_FILE = 0
+FTYPE_DIR = 1
+
+class AternosFile:
+
+ def __init__(atserv:atserver.AternosServer, path:str, name:str, ftype:int=FTYPE_FILE, size:Union[]=0):
+
+ self.atserv = atserv
+ self._name = name
+ self._ftype = ftype
+ self._size = size
+
+ def delete(self):
+
+ self.atserv.atserver_request(
+ 'https://aternos.org/panel/ajax/delete.php',
+ atconnect.REQPOST, data={'file': self._name},
+ sendtoken=True
+ )
+
+ @property
+ def text(self):
+ editor = self.atserv.atserver_request(
+ f'https://aternos.org/files/{self._name}',
+ atconnect.REQGET
+ )
+ edittree = lxml.html.fromstring(editor.content)
+
+ editfield = edittree.xpath('//div[@class="ace_layer ace_text-layer"]')[0]
+ editlines = editfield.xpath('/div[@class="ace_line"]')
+ rawlines = []
+
+ for line in editlines:
+ rawlines.append(line.text)
+ return rawlines
+
+ @text.setter
+ def text(self, value):
+ self.atserv.atserver_request(
+ f'https://aternos.org/panel/ajax/save.php',
+ atconnect.REQPOST, data={'content': value},
+ sendtoken=True
+ )
+
+ @property
+ def name(self):
+ return self._name
+
+ @property
+ def ftype(self):
+ return self._ftype
+
+ @property
+ def size(self):
+ return self._size
diff --git a/python_aternos/atfm.py b/python_aternos/atfm.py
new file mode 100644
index 0000000..4fa3497
--- /dev/null
+++ b/python_aternos/atfm.py
@@ -0,0 +1,101 @@
+import lxml.html
+from typing import Optional, Union, List
+
+from . import atserver
+from . import atconnect
+from . import atfile
+
+class AternosFileManager:
+
+ def __init__(atserv:atserver.AternosServer) -> None:
+
+ self.atserv = atserv
+
+ def listdir(self, path:str='') -> List[atfile.AternosFile]:
+
+ filesreq = self.atserv.atserver_request(
+ f'https://aternos.org/files/{path}',
+ atconnect.REQGET
+ )
+ filestree = lxml.html.fromstring(filesreq.content)
+ fileslist = filestree.xpath(
+ '//div[@class="files"]/div[@class="directory dropzone"]' + \
+ '/div[@class="file clickable"]'
+ )
+
+ files = []
+ for f in fileslist:
+
+ ftype_raw = f.xpath('/@data-type')
+ ftype = atfile.FTYPE_FILE \
+ if ftype_raw == 'file' \
+ else atfile.FTYPE_DIR
+
+ fsize_raw = f.xpath('/div[@class="filesize"]')
+ fsize = 0
+ if len(fsize_raw) > 0:
+
+ fsize_text = fsize_raw[0].text.strip()
+ fsize_num = fsize_text[:fsize_text.rfind(' ')]
+ fsize_msr = fsize_text[fsize_text.rfind(' ')+1:]
+
+ fsize = convert_size(fsize_num, fsize_msr)
+
+ fullpath = f.xpath('/@data-path')[0]
+ filepath = fullpath[:fullpath.rfind('/')]
+ filename = fullpath[fullpath.rfind('/'):]
+ files.append(
+ atfile.AternosFile(
+ self.atserv,
+ filepath, filename,
+ ftype, fsize
+ )
+ )
+
+ return files
+
+ def convert_size(self, num:Union[int,float], measure:str) -> float:
+
+ measure_match = {
+ 'B': 1,
+ 'kB': 1000,
+ 'MB': 1000000,
+ 'GB': 1000000000
+ }
+ try:
+ result = num * measure_match[measure]
+ except KeyError:
+ result = -1
+ return result
+
+ def get_file(self, path:str) -> Union[atfile.AternosFile,None]:
+
+ filepath = path[:path.rfind('/')]
+ filename = path[path.rfind('/'):]
+
+ filedir = listdir(filepath)
+ for file in filedir:
+ if file.name == filename:
+ return file
+
+ return None
+
+ def dl_file(self, path:str) -> bytes:
+
+ file = self.atserv.atserver_request(
+ f'https://aternos.org/panel/ajax/files/download.php?' + \
+ f'file={path.replace('/','%2F')}',
+ atconnect.REQGET
+ )
+
+ return file.content
+
+ def dl_world(self, world:str='world') -> bytes:
+
+ world = self.atserv.atserver_request(
+ f'https://aternos.org/panel/ajax/worlds/download.php?' + \
+ f'world={world.replace('/','%2F')}',
+ atconnect.REQGET
+ )
+
+ return world.content
diff --git a/python_aternos/atserver.py b/python_aternos/atserver.py
index 701d0a1..99628c6 100644
--- a/python_aternos/atserver.py
+++ b/python_aternos/atserver.py
@@ -1,45 +1,41 @@
+import re
+import json
import lxml.html
+from requests import Response
+from typing import Optional, Dict
+from . import atconnect
from . import aterrors
-from . import atfiles
+from . import atfm
class AternosServer:
- def __init__(self, servid, atconn):
+ def __init__(self, servid:str, atconn:atconnect.AternosConnect) -> None:
self.servid = servid
self.atconn = atconn
servreq = self.atserver_request(
'https://aternos.org/server',
- self.atconn.REQGET
+ atconnect.REQGET
)
servtree = lxml.html.fromstring(servreq.content)
- servinfo = servtree.xpath(
- '//div[@class="server-bottom-info server-info"]' + \
- '/div[@class="server-info-container"]' + \
- '/div[@class="server-info-box"]' + \
- '/div[@class="server-info-box-body"]' + \
- '/div[@class="server-info-box-value"]/span'
+ self._info = json.loads(
+ re.search(
+ r'var\s*lastStatus\s*=\s*({.*})',
+ servtree.head.text
+ )[1]
)
- fullip = servinfo[0].text
- self._address = fullip
- self._domain = fullip[:fullip.rfind(':')]
- self._port = fullip[fullip.rfind(':')+1:]
-
- self._software = servinfo[1].text
- self._version = servinfo[2].text
-
- self.atconn.get_token(servreq.content)
+ self.atconn.parse_token(servreq.content)
self.atconn.generate_sec()
- def start(self, accepteula=True):
+ def start(self, accepteula:bool=True) -> None:
startreq = self.atserver_request(
'https://aternos.org/panel/ajax/start.php',
- self.atconn.REQGET, sendtoken=True
+ atconnect.REQGET, sendtoken=True
)
startresult = startreq.json()
@@ -49,6 +45,7 @@ class AternosServer:
error = startresult['error']
if error == 'eula' and accepteula:
self.eula()
+ self.start(accepteula=False)
elif error == 'eula':
raise aterrors.AternosServerStartError(
'EULA was not accepted. Use start(accepteula=True)'
@@ -62,48 +59,51 @@ class AternosServer:
f'Unable to start server. Code: {error}'
)
- def confirm(self):
+ def confirm(self) -> None:
self.atserver_request(
'https://aternos.org/panel/ajax/confirm.php',
- self.atconn.REQGET, sendtoken=True
+ atconnect.REQGET, sendtoken=True
)
- def stop(self):
+ def stop(self) -> None:
self.atserver_request(
'https://aternos.org/panel/ajax/stop.php',
- self.atconn.REQGET, sendtoken=True
+ atconnect.REQGET, sendtoken=True
)
- def cancel(self):
+ def cancel(self) -> None:
self.atserver_request(
'https://aternos.org/panel/ajax/cancel.php',
- self.atconn.REQGET, sendtoken=True
+ atconnect.REQGET, sendtoken=True
)
- def restart(self):
+ def restart(self) -> None:
self.atserver_request(
'https://aternos.org/panel/ajax/restart.php',
- self.atconn.REQGET, sendtoken=True
+ atconnect.REQGET, sendtoken=True
)
- def eula(self):
+ def eula(self) -> None:
self.atserver_request(
'https://aternos.org/panel/ajax/eula.php',
- self.atconn.REQGET, sendtoken=True
+ atconnect.REQGET, sendtoken=True
)
- def files(self):
+ def files(self) -> atfm.AternosFileManager:
- return AternosFileManager(self)
+ return atfm.AternosFileManager(self)
def atserver_request(
- self, url, method, params=None,
- data=None, headers=None, sendtoken=False):
+ self, url:str, method:int,
+ params:Optional[dict]=None,
+ data:Optional[dict]=None,
+ headers:Optional[dict]=None,
+ sendtoken:bool=False) -> Response:
return self.atconn.request_cloudflare(
url=url, method=method,
@@ -116,21 +116,25 @@ class AternosServer:
)
@property
- def address(self):
- return self._address
-
- @property
- def domain(self):
- return self._domain
+ def info(self) -> dict:
+ return self._info
@property
- def port(self):
- return self._port
-
- @property
- def software(self):
- return self._software
+ def address(self) -> str:
+ return f'{self.domain}:{self.port}'
@property
- def version(self):
- return self._version
+ def domain(self) -> str:
+ return self._info['displayAddress']
+
+ @property
+ def port(self) -> int:
+ return self._info['port']
+
+ @property
+ def software(self) -> str:
+ return self._info['software']
+
+ @property
+ def version(self) -> str:
+ return self._info['version']