WebSocket, code improvements, API update
This commit is contained in:
parent
ca9cda3b51
commit
6993aadf03
9 changed files with 284 additions and 170 deletions
|
@ -1,4 +1,6 @@
|
||||||
Client - C, Server - S.
|
Client - C, Server - S.
|
||||||
|
Note: "stream"/"type" means the type of received data:
|
||||||
|
a new console line, an updated RAM or TPS value...
|
||||||
|
|
||||||
C>
|
C>
|
||||||
GET wss://aternos.org/hermes/
|
GET wss://aternos.org/hermes/
|
||||||
|
@ -29,11 +31,10 @@ C> {"stream":"console","type":"command","data":"list"}
|
||||||
S> {"stream":"console","type":"line","data":"list\r"}
|
S> {"stream":"console","type":"line","data":"list\r"}
|
||||||
S> {"stream":"console","type":"line","data":"[23:28:28] [Server thread/INFO] [minecraft/DedicatedServer]: There are §r0§r/§r8§r players online:§r\r"}
|
S> {"stream":"console","type":"line","data":"[23:28:28] [Server thread/INFO] [minecraft/DedicatedServer]: There are §r0§r/§r8§r players online:§r\r"}
|
||||||
S> {"stream":"console","type":"line","data":"[23:28:28] [Server thread/INFO] [minecraft/DedicatedServer]: \r"}
|
S> {"stream":"console","type":"line","data":"[23:28:28] [Server thread/INFO] [minecraft/DedicatedServer]: \r"}
|
||||||
# AdBlock detected
|
# ???
|
||||||
C> {"type":"❤"}
|
C> {"type":"❤"}
|
||||||
|
|
||||||
*** Page was refreshed
|
*** Page was refreshed
|
||||||
(What's the difference between stream:heap and stream:console?)
|
|
||||||
|
|
||||||
C>
|
C>
|
||||||
GET wss://aternos.org/hermes/
|
GET wss://aternos.org/hermes/
|
||||||
|
@ -69,5 +70,32 @@ S> {"type":"status","message":"{\"brand\":\"aternos\",\"status\":5,\"change\":16
|
||||||
S> {"type":"status","message":"{\"brand\":\"aternos\",\"status\":0,\"change\":1640978991,\"slots\":8,\"problems\":0,\"players\":0,\"playerlist\":[],\"message\":{\"text\":\"\",\"class\":\"blue\"},\"dynip\":null,\"bedrock\":false,\"host\":\"\",\"port\":61370,\"headstarts\":null,\"ram\":0,\"lang\":\"offline\",\"label\":\"\\u041e\\u0444\\u0444\\u043b\\u0430\\u0439\\u043d\",\"class\":\"offline\",\"countdown\":null,\"queue\":null,\"id\":\"cDRCfaQjQDaABPKL\",\"name\":\"inc09\",\"software\":\"Forge\",\"softwareId\":\"p71sAEKNbhea4UEm\",\"type\":\"forge\",\"version\":\"1.12.2 (14.23.5.2860)\",\"deprecated\":false,\"ip\":\"inc09.aternos.me\",\"displayAddress\":\"inc09.aternos.me:61370\",\"motd\":\"IndustrialCraft 2\",\"icon\":\"fa-stop-circle\",\"dns\":{\"type\":\"DEFAULT\",\"domains\":[\"inc09.aternos.me\"],\"host\":null,\"port\":null}}"}
|
S> {"type":"status","message":"{\"brand\":\"aternos\",\"status\":0,\"change\":1640978991,\"slots\":8,\"problems\":0,\"players\":0,\"playerlist\":[],\"message\":{\"text\":\"\",\"class\":\"blue\"},\"dynip\":null,\"bedrock\":false,\"host\":\"\",\"port\":61370,\"headstarts\":null,\"ram\":0,\"lang\":\"offline\",\"label\":\"\\u041e\\u0444\\u0444\\u043b\\u0430\\u0439\\u043d\",\"class\":\"offline\",\"countdown\":null,\"queue\":null,\"id\":\"cDRCfaQjQDaABPKL\",\"name\":\"inc09\",\"software\":\"Forge\",\"softwareId\":\"p71sAEKNbhea4UEm\",\"type\":\"forge\",\"version\":\"1.12.2 (14.23.5.2860)\",\"deprecated\":false,\"ip\":\"inc09.aternos.me\",\"displayAddress\":\"inc09.aternos.me:61370\",\"motd\":\"IndustrialCraft 2\",\"icon\":\"fa-stop-circle\",\"dns\":{\"type\":\"DEFAULT\",\"domains\":[\"inc09.aternos.me\"],\"host\":null,\"port\":null}}"}
|
||||||
# Backup (why is this info here?)
|
# Backup (why is this info here?)
|
||||||
S> {"type":"backup_progress","message":"{\"id\":\"\",\"progress\":0,\"action\":\"reset\",\"auto\":false,\"done\":false}"}
|
S> {"type":"backup_progress","message":"{\"id\":\"\",\"progress\":0,\"action\":\"reset\",\"auto\":false,\"done\":false}"}
|
||||||
# AdBlock detected
|
# ???
|
||||||
C> {"type":"❤"}
|
C> {"type":"❤"}
|
||||||
|
|
||||||
|
*** Queue example
|
||||||
|
S> {"type":"queue_reduced","message":"{\"queue\":0,\"total\":49,\"maxtime\":3}"}
|
||||||
|
S> {"type":"queue_reduced","message":"{\"queue\":0,\"total\":49,\"maxtime\":2}"}
|
||||||
|
|
||||||
|
# From server.js:
|
||||||
|
# var percentage = lastStatus.queue.position / queue.total * 100;
|
||||||
|
if (queue.maxtime) {
|
||||||
|
let minutes = Math.round((queue.maxtime - ((Date.now() / 1000) - lastStatus.queue.jointime)) / 60);
|
||||||
|
if (minutes < 1 || lastStatus.queue.pending === "ready") {
|
||||||
|
minutes = 1;
|
||||||
|
}
|
||||||
|
if (minutes > lastStatus.queue.minutes) {
|
||||||
|
minutes = lastStatus.queue.minutes;
|
||||||
|
}
|
||||||
|
if (lastStatus.queue.minutes - minutes <= 3) {
|
||||||
|
lastStatus.queue.minutes = minutes;
|
||||||
|
}
|
||||||
|
lastStatus.queue.time = "ca. " + lastStatus.queue.minutes + " min";
|
||||||
|
}
|
||||||
|
|
||||||
|
*** type=tick
|
||||||
|
S> {"stream":"tick","type":"tick","data":{"averageTickTime":8.526042}}
|
||||||
|
S> {"stream":"tick","type":"tick","data":{"averageTickTime":1.4948028}}
|
||||||
|
|
||||||
|
# From legilimens.js:
|
||||||
|
# let tps = Math.round(Math.min(1000 / data.averageTickTime, 20) * 10) / 10;
|
||||||
|
|
|
@ -5,7 +5,8 @@ from typing import Optional, Union, List
|
||||||
from . import atserver
|
from . import atserver
|
||||||
from . import atconnect
|
from . import atconnect
|
||||||
from . import aterrors
|
from . import aterrors
|
||||||
from . import client_secrets
|
|
||||||
|
from .aterrors import AternosCredentialsError
|
||||||
|
|
||||||
class Client:
|
class Client:
|
||||||
|
|
||||||
|
@ -13,48 +14,58 @@ class Client:
|
||||||
|
|
||||||
self.atconn = atconn
|
self.atconn = atconn
|
||||||
|
|
||||||
# if google:
|
|
||||||
# flow = Flow.from_client_config\
|
|
||||||
# (
|
|
||||||
# json.loads(
|
|
||||||
# base64.standard_base64decode(client_secrets.CSJSON)
|
|
||||||
# ),
|
|
||||||
# scopes=['openid', 'email']
|
|
||||||
# )
|
|
||||||
# # localhost:8764
|
|
||||||
# flow.run_local_server(port=8764, open_browser=False)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_hashed(cls, username:str, md5:str):
|
def from_hashed(cls, username:str, md5:str):
|
||||||
|
|
||||||
atconn = atconnect.AternosConnect()
|
atconn = atconnect.AternosConnect()
|
||||||
token = atconn.parse_token()
|
atconn.parse_token()
|
||||||
sec = atconn.generate_sec()
|
atconn.generate_sec()
|
||||||
|
|
||||||
loginreq = self.atconn.request_cloudflare(
|
credentials = {
|
||||||
|
'user': username,
|
||||||
|
'password': md5
|
||||||
|
}
|
||||||
|
|
||||||
|
loginreq = atconn.request_cloudflare(
|
||||||
f'https://aternos.org/panel/ajax/account/login.php',
|
f'https://aternos.org/panel/ajax/account/login.php',
|
||||||
atconnect.REQPOST, data=self.credentials,
|
atconnect.REQPOST, data=credentials,
|
||||||
sendtoken=True
|
sendtoken=True
|
||||||
)
|
)
|
||||||
|
|
||||||
if loginreq.cookies.get('ATERNOS_SESSION', None) == None:
|
if loginreq.cookies.get('ATERNOS_SESSION', None) == None:
|
||||||
raise aterrors.AternosCredentialsError(
|
raise AternosCredentialsError(
|
||||||
'Check your username and password'
|
'Check your username and password'
|
||||||
)
|
)
|
||||||
|
|
||||||
cls(atconn)
|
return cls(atconn)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_credentials(cls, username:str, password:str):
|
def from_credentials(cls, username:str, password:str):
|
||||||
cls.from_hashed(
|
|
||||||
username,
|
pswd_bytes = password.encode('utf-8')
|
||||||
hashlib.md5(password.encode('utf-8'))\
|
md5 = hashlib.md5(pswd_bytes).hexdigest().lower()
|
||||||
.hexdigest().lower()
|
|
||||||
)
|
return cls.from_hashed(username, md5)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def with_google(cls):
|
def from_session(cls, session:str):
|
||||||
pass
|
|
||||||
|
atconn = atconnect.AternosConnect()
|
||||||
|
atconn.session.cookies.set('ATERNOS_SESSION', session)
|
||||||
|
atconn.parse_token()
|
||||||
|
atconn.generate_sec()
|
||||||
|
|
||||||
|
return cls(atconn)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def google() -> str:
|
||||||
|
|
||||||
|
atconn = atconnect.AternosConnect()
|
||||||
|
auth = atconn.request_cloudflare(
|
||||||
|
'https://aternos.org/auth/google-login',
|
||||||
|
atconnect.REQGET, redirect=False
|
||||||
|
)
|
||||||
|
return auth.headers['Location']
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def servers(self) -> List[atserver.AternosServer]:
|
def servers(self) -> List[atserver.AternosServer]:
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import enum
|
||||||
import re
|
import re
|
||||||
import lxml.html
|
import lxml.html
|
||||||
from typing import Any, Dict, List, Optional
|
from typing import Any, Dict, List, Optional
|
||||||
|
@ -8,68 +9,75 @@ from . import atconnect
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from atserver import AternosServer
|
from atserver import AternosServer
|
||||||
|
|
||||||
OPT_PLAYERS = 'max-players'
|
class ServerOpts(enum.Enum):
|
||||||
OPT_GAMEMODE = 'gamemode'
|
players = 'max-players'
|
||||||
OPT_DIFFICULTY = 'difficulty'
|
gm = 'gamemode'
|
||||||
OPT_WHITELIST = 'white-list'
|
difficulty = 'difficulty'
|
||||||
OPT_ONLINE = 'online-mode'
|
whl = 'white-list'
|
||||||
OPT_PVP = 'pvp'
|
online = 'online-mode'
|
||||||
OPT_CMDBLOCK = 'enable-command-block'
|
pvp = 'pvp'
|
||||||
OPT_FLIGHT = 'allow-flight'
|
cmdblock = 'enable-command-block'
|
||||||
OPT_ANIMALS = 'spawn-animals'
|
flight = 'allow-flight'
|
||||||
OPT_MONSTERS = 'spawn-monsters'
|
animals = 'spawn-animals'
|
||||||
OPT_VILLAGERS = 'spawn-npcs'
|
monsters = 'spawn-monsters'
|
||||||
OPT_NETHER = 'allow-nether'
|
villagers = 'spawn-npcs'
|
||||||
OPT_FORCEGM = 'force-gamemode'
|
nether = 'allow-nether'
|
||||||
OPT_SPAWNLOCK = 'spawn-protection'
|
forcegm = 'force-gamemode'
|
||||||
OPT_CHEATS = 'allow-cheats'
|
spawnlock = 'spawn-protection'
|
||||||
OPT_RESOURCEPACK = 'resource-pack'
|
cmds = 'allow-cheats'
|
||||||
|
pack = 'resource-pack'
|
||||||
|
|
||||||
DAT_PREFIX = 'Data:'
|
DAT_PREFIX = 'Data:'
|
||||||
DAT_SEED = 'RandomSeed'
|
|
||||||
DAT_HARDCORE = 'hardcore'
|
class WorldOpts(enum.Enum):
|
||||||
DAT_DIFFICULTY = 'Difficulty'
|
seed = 'randomseed'
|
||||||
|
hardcore = 'hardcore'
|
||||||
|
difficulty = 'difficulty'
|
||||||
|
|
||||||
DAT_GR_PREFIX = 'Data:GameRules:'
|
DAT_GR_PREFIX = 'Data:GameRules:'
|
||||||
DAT_GR_ADVS = 'announceAdvancements'
|
|
||||||
DAT_GR_CMDOUT = 'commandBlockOutput'
|
class WorldRules(enum.Enum):
|
||||||
DAT_GR_ELYTRA = 'disableElytraMovementCheck'
|
advs = 'announceadvancements'
|
||||||
DAT_GR_DAYLIGHT = 'doDaylightCycle'
|
cmdout = 'commandblockoutput'
|
||||||
DAT_GR_ENTDROPS = 'doEntityDrops'
|
elytra = 'disableelytramovementcheck'
|
||||||
DAT_GR_FIRETICK = 'doFireTick'
|
daynight = 'dodaylightcycle'
|
||||||
DAT_GR_LIMITCRAFT = 'doLimitedCrafting'
|
entdrop = 'doentitydrops'
|
||||||
DAT_GR_MOBLOOT = 'doMobLoot'
|
fire = 'dofiretick'
|
||||||
DAT_GR_MOBS = 'doMobSpawning'
|
limitcraft = 'dolimitedcrafting'
|
||||||
DAT_GR_TILEDROPS = 'doTileDrops'
|
mobloot = 'domobloot'
|
||||||
DAT_GR_WEATHER = 'doWeatherCycle'
|
mobs = 'domobspawning'
|
||||||
DAT_GR_KEEPINV = 'keepInventory'
|
blockdrop = 'dotiledrops'
|
||||||
DAT_GR_DEATHMSG = 'showDeathMessages'
|
weather = 'doweathercycle'
|
||||||
DAT_GR_ADMINCMDLOG = 'logAdminCommands'
|
keepinv = 'keepinventory'
|
||||||
DAT_GR_CMDLEN = 'maxCommandChainLength'
|
deathmsg = 'showdeathmessages'
|
||||||
DAT_GR_ENTCRAM = 'maxEntityCramming'
|
admincmdlog = 'logadmincommands'
|
||||||
DAT_GR_MOBGRIEF = 'mobGriefing'
|
cmdlen = 'maxcommandchainlength'
|
||||||
DAT_GR_REGEN = 'naturalRegeneration'
|
entcram = 'maxentitycramming'
|
||||||
DAT_GR_RNDTICK = 'randomTickSpeed'
|
mobgrief = 'mobgriefing'
|
||||||
DAT_GR_SPAWNRADIUS = 'spawnRadius'
|
regen = 'naturalregeneration'
|
||||||
DAT_GR_REDUCEDF3 = 'reducedDebugInfo'
|
rndtick = 'randomtickspeed'
|
||||||
DAT_GR_SPECTCHUNK = 'spectatorsGenerateChunks'
|
spawnradius = 'spawnradius'
|
||||||
DAT_GR_CMDFB = 'sendCommandFeedback'
|
reducedf3 = 'reduceddebuginfo'
|
||||||
|
spectchunkgen = 'spectatorsgeneratechunks'
|
||||||
|
cmdfb = 'sendcommandfeedback'
|
||||||
|
|
||||||
DAT_TYPE_WORLD = 0
|
DAT_TYPE_WORLD = 0
|
||||||
DAT_TYPE_GR = 1
|
DAT_TYPE_GR = 1
|
||||||
|
|
||||||
GM_SURVIVAL = 0
|
class Gamemode(enum.IntEnum):
|
||||||
GM_CREATIVE = 1
|
survival = 0
|
||||||
GM_ADVENTURE = 2
|
creative = 1
|
||||||
GM_SPECTATOR = 3
|
adventure = 2
|
||||||
|
spectator = 3
|
||||||
|
|
||||||
DF_PEACEFUL = 0
|
class Difficulty(enum.IntEnum):
|
||||||
DF_EASY = 1
|
peaceful = 0
|
||||||
DF_NORMAL = 2
|
easy = 1
|
||||||
DF_HARD = 3
|
normal = 2
|
||||||
|
hard = 3
|
||||||
|
|
||||||
JAVA_JDK = 'openjdk:{}'
|
JDK = 'openjdk:{}'
|
||||||
JAVA_OPENJ9 = 'adoptopenjdk:{}-jre-openj9-bionic'
|
OJ9 = 'adoptopenjdk:{}-jre-openj9-bionic'
|
||||||
|
|
||||||
FLAG_PROP_TYPE = 1
|
FLAG_PROP_TYPE = 1
|
||||||
|
|
||||||
|
@ -81,6 +89,7 @@ class AternosConfig:
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def timezone(self) -> str:
|
def timezone(self) -> str:
|
||||||
|
|
||||||
optreq = self.atserv.atserver_request(
|
optreq = self.atserv.atserver_request(
|
||||||
'https://aternos.org/options',
|
'https://aternos.org/options',
|
||||||
atconnect.REQGET
|
atconnect.REQGET
|
||||||
|
@ -93,6 +102,7 @@ class AternosConfig:
|
||||||
|
|
||||||
@timezone.setter
|
@timezone.setter
|
||||||
def timezone(self, value:str) -> None:
|
def timezone(self, value:str) -> None:
|
||||||
|
|
||||||
matches_tz = re.search(r'(?:^[A-Z]\w+\/[A-Z]\w+$)|^UTC$', value)
|
matches_tz = re.search(r'(?:^[A-Z]\w+\/[A-Z]\w+$)|^UTC$', value)
|
||||||
if matches_tz == None:
|
if matches_tz == None:
|
||||||
raise AttributeError('Timezone must match zoneinfo format: Area/Location')
|
raise AttributeError('Timezone must match zoneinfo format: Area/Location')
|
||||||
|
@ -105,6 +115,7 @@ class AternosConfig:
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def java_version(self) -> str:
|
def java_version(self) -> str:
|
||||||
|
|
||||||
optreq = self.atserv.atserver_request(
|
optreq = self.atserv.atserver_request(
|
||||||
'https://aternos.org/options',
|
'https://aternos.org/options',
|
||||||
atconnect.REQGET
|
atconnect.REQGET
|
||||||
|
@ -117,9 +128,9 @@ class AternosConfig:
|
||||||
|
|
||||||
@java_version.setter
|
@java_version.setter
|
||||||
def java_version(self, value:str) -> None:
|
def java_version(self, value:str) -> None:
|
||||||
matches_jdkver = re.search(r'^(?:adopt)*openjdk:(\d+)(?:-jre-openj9-bionic)*$', value)
|
matches_jdkver = re.search(r'^(?:adopt)?openjdk:(\d+)(?:-jre-openj9-bionic)?$', value)
|
||||||
if matches_jdkver == None:
|
if matches_jdkver == None:
|
||||||
raise AttributeError('Java image version must match "[adopt]openjdk:%d[-jre-openj9-bionic]" format')
|
raise AttributeError('Incorrect Java image version format!')
|
||||||
|
|
||||||
self.atserv.atserver_request(
|
self.atserv.atserver_request(
|
||||||
'https://aternos.org/panel/ajax/image.php',
|
'https://aternos.org/panel/ajax/image.php',
|
||||||
|
@ -149,6 +160,7 @@ class AternosConfig:
|
||||||
def set_world_prop(
|
def set_world_prop(
|
||||||
self, option:str, value:Any,
|
self, option:str, value:Any,
|
||||||
proptype:int, world:str='world') -> None:
|
proptype:int, world:str='world') -> None:
|
||||||
|
|
||||||
prefix = DAT_PREFIX
|
prefix = DAT_PREFIX
|
||||||
if proptype == DAT_TYPE_GR:
|
if proptype == DAT_TYPE_GR:
|
||||||
prefix = DAT_GR_PREFIX
|
prefix = DAT_GR_PREFIX
|
||||||
|
@ -162,6 +174,7 @@ class AternosConfig:
|
||||||
def get_world_props(
|
def get_world_props(
|
||||||
self, world:str='world',
|
self, world:str='world',
|
||||||
flags:int=FLAG_PROP_TYPE) -> Dict[str,Any]:
|
flags:int=FLAG_PROP_TYPE) -> Dict[str,Any]:
|
||||||
|
|
||||||
self.__get_all_props(
|
self.__get_all_props(
|
||||||
f'https://aternos.org/files/{world}/level.dat',
|
f'https://aternos.org/files/{world}/level.dat',
|
||||||
flags, [DAT_PREFIX, DAT_GR_PREFIX]
|
flags, [DAT_PREFIX, DAT_GR_PREFIX]
|
||||||
|
|
|
@ -36,6 +36,8 @@ class AternosConnect:
|
||||||
js_code = re.findall(r'\(\(\)(.*?)\)\(\);', text)
|
js_code = re.findall(r'\(\(\)(.*?)\)\(\);', text)
|
||||||
token_func = js_code[1] if len(js_code) > 1 else js_code[0]
|
token_func = js_code[1] if len(js_code) > 1 else js_code[0]
|
||||||
|
|
||||||
|
print('*** Function:', token_func)
|
||||||
|
|
||||||
ctx = atjsparse.exec(token_func)
|
ctx = atjsparse.exec(token_func)
|
||||||
self.token = ctx.window['AJAX_TOKEN']
|
self.token = ctx.window['AJAX_TOKEN']
|
||||||
|
|
||||||
|
@ -64,95 +66,86 @@ class AternosConnect:
|
||||||
for i in range(randlen+1):
|
for i in range(randlen+1):
|
||||||
rand_arr.append('')
|
rand_arr.append('')
|
||||||
|
|
||||||
rand_alphanum = \
|
rand = random.random()
|
||||||
self.convert_num(random.random(),36) + \
|
rand_alphanum = self.convert_num(rand, 36) + ('0' * 17)
|
||||||
'00000000000000000'
|
|
||||||
return (rand_alphanum[2:18].join(rand_arr)[:randlen])
|
|
||||||
|
|
||||||
def convert_num(self, num:Union[int,float], base:int) -> str:
|
return (rand_alphanum[:18].join(rand_arr)[:randlen])
|
||||||
|
|
||||||
|
def convert_num(
|
||||||
|
self, num:Union[int,float,str],
|
||||||
|
base:int, frombase:int=10) -> str:
|
||||||
|
|
||||||
|
if isinstance(num, str):
|
||||||
|
num = int(num, frombase)
|
||||||
|
|
||||||
|
if isinstance(num, float):
|
||||||
|
sliced = str(num)[2:]
|
||||||
|
num = int(sliced)
|
||||||
|
|
||||||
|
symbols = '0123456789abcdefghijklmnopqrstuvwxyz'
|
||||||
|
basesym = symbols[:base]
|
||||||
result = ''
|
result = ''
|
||||||
while num > 0:
|
while num > 0:
|
||||||
result = str(num % base) + result
|
rem = num % base
|
||||||
|
result = str(basesym[rem]) + result
|
||||||
num //= base
|
num //= base
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def request_cloudflare(
|
def request_cloudflare(
|
||||||
self, url:str, method:int,
|
self, url:str, method:int, retries:int=10,
|
||||||
retries:int=10,
|
params:Optional[dict]=None, data:Optional[dict]=None,
|
||||||
params:Optional[dict]=None,
|
headers:Optional[dict]=None, reqcookies:Optional[dict]=None,
|
||||||
data:Optional[dict]=None,
|
sendtoken:bool=False, redirect:bool=True) -> Response:
|
||||||
headers:Optional[dict]=None,
|
|
||||||
reqcookies:Optional[dict]=None,
|
|
||||||
sendtoken:bool=False) -> Response:
|
|
||||||
|
|
||||||
cftitle = '<title>Please Wait... | Cloudflare</title>'
|
cftitle = '<title>Please Wait... | Cloudflare</title>'
|
||||||
|
|
||||||
if sendtoken:
|
if params == None:
|
||||||
url += f'?TOKEN={self.token}&SEC={self.sec}'
|
params = {}
|
||||||
|
|
||||||
if headers == None:
|
if headers == None:
|
||||||
headers = {}
|
headers = {}
|
||||||
headers['User-Agent'] = REQUA
|
headers['User-Agent'] = REQUA
|
||||||
|
|
||||||
|
if sendtoken:
|
||||||
|
url += f'?TOKEN={self.token}&SEC={self.sec}'
|
||||||
|
|
||||||
try:
|
try:
|
||||||
cookies = self.session.cookies
|
cookies = self.session.cookies
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
cookies = None
|
cookies = None
|
||||||
|
|
||||||
self.session = CloudScraper()
|
|
||||||
if cookies != None:
|
|
||||||
self.session.cookies = cookies
|
|
||||||
|
|
||||||
if method == REQPOST:
|
|
||||||
req = self.session.post(
|
|
||||||
url,
|
|
||||||
data=data,
|
|
||||||
headers=headers,
|
|
||||||
cookies=reqcookies
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
req = self.session.get(
|
|
||||||
url,
|
|
||||||
params=params,
|
|
||||||
headers=headers,
|
|
||||||
cookies=reqcookies
|
|
||||||
)
|
|
||||||
|
|
||||||
countdown = retries
|
countdown = retries
|
||||||
while cftitle in req.text \
|
while True:
|
||||||
and (countdown > 0):
|
|
||||||
|
|
||||||
self.session = CloudScraper()
|
self.session = CloudScraper()
|
||||||
if cookies != None:
|
if cookies != None:
|
||||||
self.session.cookies = cookies
|
self.session.cookies = cookies
|
||||||
|
|
||||||
if reqcookies != None:
|
if reqcookies != None:
|
||||||
for cookiekey in reqcookies:
|
for cookiekey in reqcookies:
|
||||||
self.session.cookies.set(cookiekey, reqcookies[cookiekey])
|
self.session.cookies.set(cookiekey, reqcookies[cookiekey])
|
||||||
|
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
|
|
||||||
if method == REQPOST:
|
if method == REQPOST:
|
||||||
req = self.session.post(
|
req = self.session.post(
|
||||||
url,
|
url, data=data, params=params,
|
||||||
data=data,
|
headers=headers, cookies=reqcookies,
|
||||||
headers=headers,
|
allow_redirects=redirect
|
||||||
cookies=reqcookies
|
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
req = self.session.get(
|
req = self.session.get(
|
||||||
url,
|
url, params=params,
|
||||||
params=params,
|
headers=headers, cookies=reqcookies,
|
||||||
headers=headers,
|
allow_redirects=redirect
|
||||||
cookies=reqcookies
|
)
|
||||||
|
|
||||||
|
if not cftitle in req.text:
|
||||||
|
break
|
||||||
|
if not countdown > 0:
|
||||||
|
raise aterrors.CloudflareError(
|
||||||
|
'The retries limit has been reached'
|
||||||
)
|
)
|
||||||
countdown -= 1
|
countdown -= 1
|
||||||
|
|
||||||
print(sendtoken)
|
|
||||||
try:
|
|
||||||
print(self.sec, self.token)
|
|
||||||
except AttributeError:
|
|
||||||
pass
|
|
||||||
print(req.status_code)
|
|
||||||
print(req.cookies)
|
|
||||||
print(req.url, '', sep='\n---')
|
|
||||||
return req
|
return req
|
||||||
|
|
|
@ -1,15 +1,14 @@
|
||||||
class AternosError(Exception):
|
class AternosError(Exception):
|
||||||
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class AternosCredentialsError(AternosError):
|
class AternosCredentialsError(AternosError):
|
||||||
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class AternosServerStartError(AternosError):
|
class AternosServerStartError(AternosError):
|
||||||
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class AternosIOError(AternosError):
|
class AternosIOError(AternosError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class CloudflareError(AternosError):
|
||||||
pass
|
pass
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
|
import enum
|
||||||
import re
|
import re
|
||||||
import json
|
import json
|
||||||
import lxml.html
|
import lxml.html
|
||||||
|
import websockets
|
||||||
from requests import Response
|
from requests import Response
|
||||||
from typing import Optional, Dict
|
from typing import Optional, Dict
|
||||||
|
|
||||||
|
@ -10,26 +12,33 @@ from . import atfm
|
||||||
from . import atconf
|
from . import atconf
|
||||||
from . import atplayers
|
from . import atplayers
|
||||||
|
|
||||||
SOFTWARE_JAVA = 0
|
JAVA = 0
|
||||||
SOFTWARE_BEDROCK = 1
|
BEDROCK = 1
|
||||||
|
|
||||||
PLAYERS_ALLOWED = 'whitelist'
|
class Lists(enum.Enum):
|
||||||
PLAYERS_OPS = 'ops'
|
whl = 'whitelist'
|
||||||
PLAYERS_BANNED = 'banned-players'
|
ops = 'ops'
|
||||||
PLAYERS_IPS = 'banned-ips'
|
ban = 'banned-players'
|
||||||
|
ips = 'banned-ips'
|
||||||
|
|
||||||
STATUS_OFFLINE = 0
|
class Status(enum.IntEnum):
|
||||||
STATUS_ONLINE = 1
|
off = 0
|
||||||
STATUS_LOADING = 2
|
on = 1
|
||||||
STATUS_SHUTDOWN = 3
|
loading = 2
|
||||||
STATUS_ERROR = 7
|
shutdown = 3
|
||||||
|
error = 7
|
||||||
|
|
||||||
class AternosServer:
|
class AternosServer:
|
||||||
|
|
||||||
def __init__(self, servid:str, atconn:atconnect.AternosConnect) -> None:
|
def __init__(
|
||||||
|
self, servid:str,
|
||||||
|
atconn:atconnect.AternosConnect,
|
||||||
|
savelog:bool=True) -> None:
|
||||||
|
|
||||||
self.servid = servid
|
self.servid = servid
|
||||||
self.atconn = atconn
|
self.atconn = atconn
|
||||||
|
self.savelog = savelog
|
||||||
|
self.log = []
|
||||||
|
|
||||||
servreq = self.atserver_request(
|
servreq = self.atserver_request(
|
||||||
'https://aternos.org/server',
|
'https://aternos.org/server',
|
||||||
|
@ -43,36 +52,91 @@ class AternosServer:
|
||||||
servtree.head.text_content()
|
servtree.head.text_content()
|
||||||
)[1]
|
)[1]
|
||||||
)
|
)
|
||||||
|
self._ram = 0
|
||||||
|
self._tps = 0
|
||||||
|
|
||||||
self.atconn.parse_token(servreq.content)
|
self.atconn.parse_token(servreq.content)
|
||||||
self.atconn.generate_sec()
|
self.atconn.generate_sec()
|
||||||
|
|
||||||
def start(self, accepteula:bool=True) -> None:
|
async def wss(self):
|
||||||
|
|
||||||
|
session = self.atconn.session.cookies['ATERNOS_SESSION']
|
||||||
|
headers = [
|
||||||
|
('User-Agent', atconnect.REQUA),
|
||||||
|
('Cookie',
|
||||||
|
f'ATERNOS_SESSION={session}; ' + \
|
||||||
|
f'ATERNOS_SERVER={self.servid}')
|
||||||
|
]
|
||||||
|
|
||||||
|
async with websockets.connect(
|
||||||
|
'wss://aternos.org/hermes',
|
||||||
|
extra_headers=headers
|
||||||
|
) as websocket:
|
||||||
|
while True:
|
||||||
|
msg = await websocket.recv()
|
||||||
|
r = json.loads(msg)
|
||||||
|
|
||||||
|
if r['type'] == 'line' \
|
||||||
|
and r['stream'] == 'console'\
|
||||||
|
and self.savelog:
|
||||||
|
self.log.append(r['data'])
|
||||||
|
|
||||||
|
if r['type'] == 'heap':
|
||||||
|
self._ram = r['data']['usage']
|
||||||
|
|
||||||
|
if r['type'] == 'tick':
|
||||||
|
aver = 1000 / r['data']['averageTickTime']
|
||||||
|
self._tps = 20 if aver > 20 else aver
|
||||||
|
|
||||||
|
if r['type'] == 'status':
|
||||||
|
self._info = json.loads(r['message'])
|
||||||
|
|
||||||
|
def start(self, headstart:bool=False, accepteula:bool=True) -> None:
|
||||||
|
|
||||||
startreq = self.atserver_request(
|
startreq = self.atserver_request(
|
||||||
'https://aternos.org/panel/ajax/start.php',
|
'https://aternos.org/panel/ajax/start.php',
|
||||||
atconnect.REQGET, sendtoken=True
|
atconnect.REQGET, params={'headstart': int(headstart)},
|
||||||
|
sendtoken=True
|
||||||
)
|
)
|
||||||
startresult = startreq.json()
|
startresult = startreq.json()
|
||||||
|
|
||||||
if startresult['success']:
|
if startresult['success']:
|
||||||
return
|
return
|
||||||
|
|
||||||
error = startresult['error']
|
error = startresult['error']
|
||||||
|
|
||||||
if error == 'eula' and accepteula:
|
if error == 'eula' and accepteula:
|
||||||
self.eula()
|
self.eula()
|
||||||
self.start(accepteula=False)
|
self.start(accepteula=False)
|
||||||
|
|
||||||
elif error == 'eula':
|
elif error == 'eula':
|
||||||
raise aterrors.AternosServerStartError(
|
raise aterrors.AternosServerStartError(
|
||||||
'EULA was not accepted. Use start(accepteula=True)'
|
'EULA was not accepted. Use start(accepteula=True)'
|
||||||
)
|
)
|
||||||
|
|
||||||
elif error == 'already':
|
elif error == 'already':
|
||||||
raise aterrors.AternosServerStartError(
|
raise aterrors.AternosServerStartError(
|
||||||
'Server is already running'
|
'Server is already running'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
elif error == 'wrongversion':
|
||||||
|
raise aterrors.AternosServerStartError(
|
||||||
|
'Incorrect software version installed'
|
||||||
|
)
|
||||||
|
|
||||||
|
elif error == 'file':
|
||||||
|
raise aterrors.AternosServerStartError(
|
||||||
|
'File server is unavailbale, view status.aternos.gmbh'
|
||||||
|
)
|
||||||
|
|
||||||
|
elif error == 'size':
|
||||||
|
raise aterrors.AternosServerStartError(
|
||||||
|
f'Available storage size is 4GB, ' + \
|
||||||
|
f'your server used: {startresult["size"]}'
|
||||||
|
)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
raise aterrors.AternosServerStartError(
|
raise aterrors.AternosServerStartError(
|
||||||
f'Unable to start server. Code: {error}'
|
f'Unable to start server, code: {error}'
|
||||||
)
|
)
|
||||||
|
|
||||||
def confirm(self) -> None:
|
def confirm(self) -> None:
|
||||||
|
@ -120,6 +184,14 @@ class AternosServer:
|
||||||
|
|
||||||
def players(self, lst:str) -> atplayers.AternosPlayersList:
|
def players(self, lst:str) -> atplayers.AternosPlayersList:
|
||||||
|
|
||||||
|
correct = False
|
||||||
|
for lsttype in Lists:
|
||||||
|
if lsttype.value == lst:
|
||||||
|
correct = True
|
||||||
|
|
||||||
|
if not correct:
|
||||||
|
raise AttributeError('Incorrect players list type! Use Lists enum')
|
||||||
|
|
||||||
return atplayers.AternosPlayersList(lst, self)
|
return atplayers.AternosPlayersList(lst, self)
|
||||||
|
|
||||||
def atserver_request(
|
def atserver_request(
|
||||||
|
@ -129,7 +201,6 @@ class AternosServer:
|
||||||
headers:Optional[dict]=None,
|
headers:Optional[dict]=None,
|
||||||
sendtoken:bool=False) -> Response:
|
sendtoken:bool=False) -> Response:
|
||||||
|
|
||||||
print(sendtoken)
|
|
||||||
return self.atconn.request_cloudflare(
|
return self.atconn.request_cloudflare(
|
||||||
url=url, method=method,
|
url=url, method=method,
|
||||||
params=params, data=data,
|
params=params, data=data,
|
||||||
|
@ -149,7 +220,8 @@ class AternosServer:
|
||||||
def subdomain(self, value:str) -> None:
|
def subdomain(self, value:str) -> None:
|
||||||
self.atserver_request(
|
self.atserver_request(
|
||||||
'https://aternos.org/panel/ajax/options/subdomain.php',
|
'https://aternos.org/panel/ajax/options/subdomain.php',
|
||||||
atconnect.REQGET, params={'subdomain': value}
|
atconnect.REQGET, params={'subdomain': value},
|
||||||
|
sendtoken=True
|
||||||
)
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -179,10 +251,7 @@ class AternosServer:
|
||||||
@property
|
@property
|
||||||
def edition(self) -> int:
|
def edition(self) -> int:
|
||||||
soft_type = self._info['bedrock']
|
soft_type = self._info['bedrock']
|
||||||
if soft_type == True:
|
return int(soft_type)
|
||||||
return SOFTWARE_BEDROCK
|
|
||||||
else:
|
|
||||||
return SOFTWARE_JAVA
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def software(self) -> str:
|
def software(self) -> str:
|
||||||
|
@ -198,4 +267,4 @@ class AternosServer:
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def ram(self) -> int:
|
def ram(self) -> int:
|
||||||
return int(self._info['ram'])
|
return self._ram
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
CSJSON = 'eyJpbnN0YWxsZWQiOnsiY2xpZW50X2lkIjoiNjU4MTQyNjkxNjQxLXFnZ21sdGNmdXY0a2I1a2NwczdoZjBiaGlvN2Q0dnUzLmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tIiwicHJvamVjdF9pZCI6InB5dGhvbi1hdGVybm9zIiwiYXV0aF91cmkiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20vby9vYXV0aDIvYXV0aCIsInRva2VuX3VyaSI6Imh0dHBzOi8vb2F1dGgyLmdvb2dsZWFwaXMuY29tL3Rva2VuIiwiYXV0aF9wcm92aWRlcl94NTA5X2NlcnRfdXJsIjoiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vb2F1dGgyL3YxL2NlcnRzIiwiY2xpZW50X3NlY3JldCI6IkdPQ1NQWC1XUUJsMjIzOTZxcHVLMDJIVXVpWUhhUXNmbC03IiwicmVkaXJlY3RfdXJpcyI6WyJ1cm46aWV0Zjp3ZzpvYXV0aDoyLjA6b29iIiwiaHR0cDovL2xvY2FsaG9zdCJdfX0='
|
|
|
@ -1,4 +1,5 @@
|
||||||
lxml==4.6.2
|
lxml==4.6.2
|
||||||
requests==2.25.1
|
requests==2.25.1
|
||||||
cloudscraper==1.2.58
|
cloudscraper==1.2.58
|
||||||
Js2Py==0.71
|
js2py==0.71
|
||||||
|
websockets==10.1
|
||||||
|
|
13
setup.py
13
setup.py
|
@ -3,9 +3,14 @@ import setuptools
|
||||||
with open('README.md', 'rt') as readme:
|
with open('README.md', 'rt') as readme:
|
||||||
long_description = readme.read()
|
long_description = readme.read()
|
||||||
|
|
||||||
|
with open('requirements.txt', 'rt') as f:
|
||||||
|
requires = f.readlines()
|
||||||
|
for i, r in enumerate(requires):
|
||||||
|
requires[i] = r.strip('\r\n')
|
||||||
|
|
||||||
setuptools.setup(
|
setuptools.setup(
|
||||||
name='python-aternos',
|
name='python-aternos',
|
||||||
version='0.5',
|
version='0.6',
|
||||||
author='Chechkenev Andrey (@DarkCat09)',
|
author='Chechkenev Andrey (@DarkCat09)',
|
||||||
author_email='aacd0709@mail.ru',
|
author_email='aacd0709@mail.ru',
|
||||||
description='An unofficial Aternos API',
|
description='An unofficial Aternos API',
|
||||||
|
@ -21,11 +26,7 @@ setuptools.setup(
|
||||||
'License :: OSI Approved :: Apache Software License',
|
'License :: OSI Approved :: Apache Software License',
|
||||||
'Operating System :: OS Independent'
|
'Operating System :: OS Independent'
|
||||||
],
|
],
|
||||||
install_requires=[
|
install_requires=requires,
|
||||||
'lxml==4.6.2',
|
|
||||||
'requests==2.25.1',
|
|
||||||
'cloudscraper==1.2.58'
|
|
||||||
],
|
|
||||||
packages=['python_aternos'],
|
packages=['python_aternos'],
|
||||||
python_requires=">=3.6",
|
python_requires=">=3.6",
|
||||||
)
|
)
|
||||||
|
|
Reference in a new issue