This repository has been archived on 2024-07-30. You can view files and clone it, but cannot push or open issues or pull requests.
python-aternos/python_aternos/atserver.py

420 lines
9.2 KiB
Python

"""Aternos Minecraft server"""
import re
import enum
from typing import List
from .atselenium import SeleniumHelper
from .atconnect import AJAX_URL
from .atstubs import PlayersList
from .atstubs import Lists
from .atstubs import FileManager
from .atstubs import AternosConfig
from .aterrors import ServerStartError
from dataclasses import dataclass
SERVER_URL = f'{AJAX_URL}/server'
status_re = re.compile(
r'<script>\s*var lastStatus\s*?=\s*?(\{.+?\});?\s*<\/script>'
)
class Edition(enum.IntEnum):
"""Server edition type enum (java, bedrock)"""
java = 0
bedrock = 1
class Status(enum.IntEnum):
"""Server numeric status enum.
It is highly recommended to use
`AternosServer.status` instead of
`AternosServer.status_num`"""
off = 0
on = 1
starting = 2
shutdown = 3
loading = 6
error = 7
preparing = 10
confirm = 10
@dataclass
class PartialServerInfo:
se: SeleniumHelper
id: str
name: str = ''
software: str = ''
status: str = ''
players: int = 0
def use(self) -> None:
self.se.set_cookie('ATERNOS_SERVER', self.id)
self.se.load_page('/server')
@dataclass
class AternosServer:
"""Class for controlling your Aternos Minecraft server"""
def start(
self,
headstart: bool = False,
access_credits: bool = False,
accepteula: bool = True) -> None:
"""Starts a server
Args:
headstart (bool, optional): Start a server in
the headstart mode which allows
you to skip all queue
access_credits (bool, optional):
Some new parameter in Aternos API,
I don't know what it is
accepteula (bool, optional):
Automatically accept the Mojang EULA
Raises:
ServerStartError: When Aternos
is unable to start the server
"""
startreq = self.atserver_request(
f'{SERVER_URL}/start',
'GET', params={
'headstart': int(headstart),
'access-credits': int(access_credits),
},
sendtoken=True,
)
startresult = startreq.json()
if startresult['success']:
return
error = startresult['error']
if error == 'eula' and accepteula:
self.eula()
self.start(accepteula=False)
return
raise ServerStartError(error)
def confirm(self) -> None:
"""Confirms server launching"""
self.atserver_request(
f'{SERVER_URL}/confirm',
'GET', sendtoken=True,
)
def stop(self) -> None:
"""Stops the server"""
self.atserver_request(
f'{SERVER_URL}/stop',
'GET', sendtoken=True,
)
def cancel(self) -> None:
"""Cancels server launching"""
self.atserver_request(
f'{SERVER_URL}/cancel',
'GET', sendtoken=True,
)
def restart(self) -> None:
"""Restarts the server"""
self.atserver_request(
f'{SERVER_URL}/restart',
'GET', sendtoken=True,
)
def eula(self) -> None:
"""Sends a request to accept the Mojang EULA"""
self.atserver_request(
f'{SERVER_URL}/accept-eula',
'GET', sendtoken=True,
)
def files(self) -> FileManager:
"""Returns FileManager instance
for file operations
Returns:
FileManager object
"""
return FileManager(self)
def config(self) -> AternosConfig:
"""Returns AternosConfig instance
for editing server settings
Returns:
AternosConfig object
"""
return AternosConfig(self)
def players(self, lst: Lists) -> PlayersList:
"""Returns PlayersList instance
for managing operators, whitelist
and banned players lists
Args:
lst (Lists): Players list type,
must be the atplayers.Lists enum value
Returns:
PlayersList object
"""
return PlayersList(lst, self)
def set_subdomain(self, value: str) -> None:
"""Set a new subdomain for your server
(the part before `.aternos.me`)
Args:
value (str): Subdomain
"""
self.atserver_request(
f'{SERVER_URL}/options/set-subdomain',
'GET', params={'subdomain': value},
sendtoken=True,
)
def set_motd(self, value: str) -> None:
"""Set new Message of the Day
(shown below the name in the Minecraft servers list).
Formatting with "paragraph sign + code" is supported,
see https://minecraft.tools/color-code.php
Args:
value (str): MOTD
"""
self.atserver_request(
f'{SERVER_URL}/options/set-motd',
'POST', data={'motd': value},
sendtoken=True,
)
@property
def subdomain(self) -> str:
"""Get the server subdomain
(the part before `.aternos.me`)
Returns:
Subdomain
"""
atdomain = self.domain
return atdomain[:atdomain.find('.')]
@property
def motd(self) -> str:
"""Get the server message of the day
(shown below its name in Minecraft servers list)
Returns:
MOTD
"""
return self._info['motd']
@property
def address(self) -> str:
"""Full server address
including domain and port
Returns:
Server address
"""
return f'{self.domain}:{self.port}'
@property
def domain(self) -> str:
"""Server domain (e.g. `test.aternos.me`).
In other words, address without port number
Returns:
Domain
"""
return self._info['ip']
@property
def port(self) -> int:
"""Server port number
Returns:
Port
"""
return self._info['port']
@property
def edition(self) -> Edition:
"""Server software edition: Java or Bedrock
Returns:
Software edition
"""
soft_type = self._info['bedrock']
return Edition(soft_type)
@property
def is_java(self) -> bool:
"""Check if server software is Java Edition
Returns:
Is it Minecraft JE
"""
return not self._info['bedrock']
@property
def is_bedrock(self) -> bool:
"""Check if server software is Bedrock Edition
Returns:
Is it Minecraft BE
"""
return bool(self._info['bedrock'])
@property
def software(self) -> str:
"""Server software name (e.g. `Vanilla`)
Returns:
Software name
"""
return self._info['software']
@property
def version(self) -> str:
"""Server software version (1.16.5)
Returns:
Software version
"""
return self._info['version']
@property
def css_class(self) -> str:
"""CSS class for the server status element
on official web site: offline, online, loading, etc.
See https://aternos.dc09.ru/howto/server/#server-info
In most cases you need `AternosServer.status` instead of this
Returns:
CSS class
"""
return self._info['class']
@property
def status(self) -> str:
"""Server status string
(offline, loading, preparing)
Returns:
Status string
"""
return self._info['lang']
@property
def status_num(self) -> Status:
"""Server numeric status.
It is highly recommended to use
status string instead of a number
Returns:
Status code
"""
return Status(self._info['status'])
@property
def players_list(self) -> List[str]:
"""List of connected players' nicknames
Returns:
Connected players
"""
return self._info['playerlist']
@property
def players_count(self) -> int:
"""How many players are connected
Returns:
Connected players count
"""
return int(self._info['players'])
@property
def slots(self) -> int:
"""Server slots, how many
players **can** connect
Returns:
Slots count
"""
return int(self._info['slots'])
@property
def ram(self) -> int:
"""Server used RAM in MB
Returns:
Used RAM
"""
return int(self._info['ram'])
@property
def countdown(self) -> int:
"""Server stop countdown
in seconds
Returns:
Stop countdown
"""
value = self._info['countdown']
return int(value or -1)