MkDocs, Readme, Files API, Automated session saving, v2.0.1
MkDocs: sphinx docstrings rewritten to google, improved config, written the major part of how-to.
Readme: centered title + logo, added badges, features list, updated changelog.
Improved Files API, added automatical session saving and restoring to Client.
Some changes in makefile and gitignore.
License Notice now refers to all contributors.
2022-08-26 16:14:07 +04:00
|
|
|
"""Connects to Aternos WebSocket API
|
2022-07-01 14:28:39 +04:00
|
|
|
for real-time information"""
|
|
|
|
|
2022-01-22 15:10:30 +04:00
|
|
|
import enum
|
|
|
|
import json
|
|
|
|
import asyncio
|
2022-04-06 11:44:14 +04:00
|
|
|
import logging
|
2022-08-22 09:55:08 +04:00
|
|
|
|
2022-07-01 14:28:39 +04:00
|
|
|
from typing import Union, Any
|
2022-08-22 09:55:08 +04:00
|
|
|
from typing import Tuple, Dict
|
2022-07-01 14:28:39 +04:00
|
|
|
from typing import Callable, Coroutine
|
2022-03-25 16:45:38 +04:00
|
|
|
from typing import TYPE_CHECKING
|
2022-01-22 15:10:30 +04:00
|
|
|
|
2022-08-22 09:55:08 +04:00
|
|
|
import websockets
|
|
|
|
|
2022-01-22 15:10:30 +04:00
|
|
|
from .atconnect import REQUA
|
2022-03-25 16:45:38 +04:00
|
|
|
if TYPE_CHECKING:
|
2022-06-23 15:13:56 +04:00
|
|
|
from .atserver import AternosServer
|
|
|
|
|
2022-07-01 14:28:39 +04:00
|
|
|
OneArgT = Callable[[Any], Coroutine[Any, Any, None]]
|
|
|
|
TwoArgT = Callable[[Any, Tuple[Any, ...]], Coroutine[Any, Any, None]]
|
|
|
|
FunctionT = Union[OneArgT, TwoArgT]
|
|
|
|
ArgsTuple = Tuple[FunctionT, Tuple[Any, ...]]
|
2022-07-01 09:36:52 +04:00
|
|
|
|
2022-01-22 15:10:30 +04:00
|
|
|
|
2022-04-06 11:44:14 +04:00
|
|
|
class Streams(enum.Enum):
|
|
|
|
|
2022-06-23 15:13:56 +04:00
|
|
|
"""WebSocket streams types"""
|
|
|
|
|
|
|
|
status = (0, None)
|
|
|
|
queue = (1, None)
|
|
|
|
console = (2, 'console')
|
|
|
|
ram = (3, 'heap')
|
|
|
|
tps = (4, 'tick')
|
2022-07-01 14:28:39 +04:00
|
|
|
none = (-1, None)
|
2022-06-17 12:30:58 +04:00
|
|
|
|
2022-06-23 15:13:56 +04:00
|
|
|
def __init__(self, num: int, stream: str) -> None:
|
MkDocs, Readme, Files API, Automated session saving, v2.0.1
MkDocs: sphinx docstrings rewritten to google, improved config, written the major part of how-to.
Readme: centered title + logo, added badges, features list, updated changelog.
Improved Files API, added automatical session saving and restoring to Client.
Some changes in makefile and gitignore.
License Notice now refers to all contributors.
2022-08-26 16:14:07 +04:00
|
|
|
|
2022-06-23 15:13:56 +04:00
|
|
|
self.num = num
|
|
|
|
self.stream = stream
|
2022-04-06 11:44:14 +04:00
|
|
|
|
2022-01-22 15:10:30 +04:00
|
|
|
|
|
|
|
class AternosWss:
|
|
|
|
|
MkDocs, Readme, Files API, Automated session saving, v2.0.1
MkDocs: sphinx docstrings rewritten to google, improved config, written the major part of how-to.
Readme: centered title + logo, added badges, features list, updated changelog.
Improved Files API, added automatical session saving and restoring to Client.
Some changes in makefile and gitignore.
License Notice now refers to all contributors.
2022-08-26 16:14:07 +04:00
|
|
|
"""Class for managing websocket connection"""
|
2022-06-23 15:13:56 +04:00
|
|
|
|
2022-07-01 14:28:39 +04:00
|
|
|
def __init__(
|
|
|
|
self,
|
|
|
|
atserv: 'AternosServer',
|
|
|
|
autoconfirm: bool = False) -> None:
|
2022-06-23 15:13:56 +04:00
|
|
|
|
MkDocs, Readme, Files API, Automated session saving, v2.0.1
MkDocs: sphinx docstrings rewritten to google, improved config, written the major part of how-to.
Readme: centered title + logo, added badges, features list, updated changelog.
Improved Files API, added automatical session saving and restoring to Client.
Some changes in makefile and gitignore.
License Notice now refers to all contributors.
2022-08-26 16:14:07 +04:00
|
|
|
"""Class for managing websocket connection
|
|
|
|
|
|
|
|
Args:
|
|
|
|
atserv (AternosServer):
|
|
|
|
atserver.AternosServer instance
|
|
|
|
autoconfirm (bool, optional):
|
|
|
|
Automatically start server status listener
|
|
|
|
when AternosWss connects to API to confirm
|
|
|
|
server launching
|
|
|
|
"""
|
|
|
|
|
2022-06-23 15:13:56 +04:00
|
|
|
self.atserv = atserv
|
|
|
|
self.servid = atserv.servid
|
2022-07-01 14:28:39 +04:00
|
|
|
|
2022-08-22 09:55:08 +04:00
|
|
|
cookies = atserv.atconn.session.cookies
|
|
|
|
self.session = cookies['ATERNOS_SESSION']
|
|
|
|
|
2022-07-01 14:28:39 +04:00
|
|
|
recvtype = Dict[Streams, ArgsTuple]
|
2022-07-01 09:36:52 +04:00
|
|
|
self.recv: recvtype = {}
|
2022-06-23 15:13:56 +04:00
|
|
|
self.autoconfirm = autoconfirm
|
|
|
|
self.confirmed = False
|
|
|
|
|
2022-07-01 14:28:39 +04:00
|
|
|
self.socket: Any
|
|
|
|
self.keep: asyncio.Task
|
|
|
|
self.msgs: asyncio.Task
|
|
|
|
|
2022-06-23 15:13:56 +04:00
|
|
|
async def confirm(self) -> None:
|
|
|
|
|
MkDocs, Readme, Files API, Automated session saving, v2.0.1
MkDocs: sphinx docstrings rewritten to google, improved config, written the major part of how-to.
Readme: centered title + logo, added badges, features list, updated changelog.
Improved Files API, added automatical session saving and restoring to Client.
Some changes in makefile and gitignore.
License Notice now refers to all contributors.
2022-08-26 16:14:07 +04:00
|
|
|
"""Simple way to call
|
|
|
|
`AternosServer.confirm`
|
|
|
|
from this class"""
|
2022-06-23 15:13:56 +04:00
|
|
|
|
|
|
|
self.atserv.confirm()
|
|
|
|
|
2022-07-01 14:28:39 +04:00
|
|
|
def wssreceiver(
|
|
|
|
self,
|
|
|
|
stream: Streams,
|
|
|
|
*args: Any) -> Callable[[FunctionT], Any]:
|
2022-06-23 15:13:56 +04:00
|
|
|
|
|
|
|
"""Decorator that marks your function as a stream receiver.
|
|
|
|
When websocket receives message from the specified stream,
|
|
|
|
it calls all listeners created with this decorator.
|
|
|
|
|
MkDocs, Readme, Files API, Automated session saving, v2.0.1
MkDocs: sphinx docstrings rewritten to google, improved config, written the major part of how-to.
Readme: centered title + logo, added badges, features list, updated changelog.
Improved Files API, added automatical session saving and restoring to Client.
Some changes in makefile and gitignore.
License Notice now refers to all contributors.
2022-08-26 16:14:07 +04:00
|
|
|
Args:
|
|
|
|
stream (Streams): Stream that your function should listen
|
|
|
|
*args (tuple, optional): Arguments which will be passed to your function
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
...
|
2022-06-23 15:13:56 +04:00
|
|
|
"""
|
|
|
|
|
2022-07-01 09:36:52 +04:00
|
|
|
def decorator(func: FunctionT) -> None:
|
2022-06-23 15:13:56 +04:00
|
|
|
self.recv[stream] = (func, args)
|
|
|
|
return decorator
|
|
|
|
|
|
|
|
async def connect(self) -> None:
|
|
|
|
|
MkDocs, Readme, Files API, Automated session saving, v2.0.1
MkDocs: sphinx docstrings rewritten to google, improved config, written the major part of how-to.
Readme: centered title + logo, added badges, features list, updated changelog.
Improved Files API, added automatical session saving and restoring to Client.
Some changes in makefile and gitignore.
License Notice now refers to all contributors.
2022-08-26 16:14:07 +04:00
|
|
|
"""Connects to the websocket server
|
|
|
|
and starts all stream listeners"""
|
2022-06-23 15:13:56 +04:00
|
|
|
|
|
|
|
headers = [
|
|
|
|
('Host', 'aternos.org'),
|
|
|
|
('User-Agent', REQUA),
|
|
|
|
(
|
|
|
|
'Cookie',
|
|
|
|
f'ATERNOS_SESSION={self.session}; '
|
|
|
|
f'ATERNOS_SERVER={self.servid}'
|
|
|
|
)
|
|
|
|
]
|
2022-07-01 14:28:39 +04:00
|
|
|
self.socket = await websockets.connect( # type: ignore
|
2022-06-23 15:13:56 +04:00
|
|
|
'wss://aternos.org/hermes/',
|
|
|
|
origin='https://aternos.org',
|
|
|
|
extra_headers=headers
|
|
|
|
)
|
|
|
|
|
|
|
|
@self.wssreceiver(Streams.status)
|
MkDocs, Readme, Files API, Automated session saving, v2.0.1
MkDocs: sphinx docstrings rewritten to google, improved config, written the major part of how-to.
Readme: centered title + logo, added badges, features list, updated changelog.
Improved Files API, added automatical session saving and restoring to Client.
Some changes in makefile and gitignore.
License Notice now refers to all contributors.
2022-08-26 16:14:07 +04:00
|
|
|
async def confirmfunc(msg: Dict[str, Any]) -> None:
|
2022-06-23 15:13:56 +04:00
|
|
|
|
MkDocs, Readme, Files API, Automated session saving, v2.0.1
MkDocs: sphinx docstrings rewritten to google, improved config, written the major part of how-to.
Readme: centered title + logo, added badges, features list, updated changelog.
Improved Files API, added automatical session saving and restoring to Client.
Some changes in makefile and gitignore.
License Notice now refers to all contributors.
2022-08-26 16:14:07 +04:00
|
|
|
"""Automatically confirm
|
|
|
|
Minecraft server launching
|
|
|
|
|
|
|
|
Args:
|
|
|
|
msg (Dict[str, Any]): Server info dictionary
|
|
|
|
"""
|
2022-06-23 15:13:56 +04:00
|
|
|
|
|
|
|
if not self.autoconfirm:
|
|
|
|
return
|
|
|
|
|
|
|
|
in_queue = (msg['class'] == 'queueing')
|
|
|
|
pending = (msg['queue']['pending'] == 'pending')
|
|
|
|
confirmation = in_queue and pending
|
|
|
|
|
|
|
|
if confirmation and not self.confirmed:
|
MkDocs, Readme, Files API, Automated session saving, v2.0.1
MkDocs: sphinx docstrings rewritten to google, improved config, written the major part of how-to.
Readme: centered title + logo, added badges, features list, updated changelog.
Improved Files API, added automatical session saving and restoring to Client.
Some changes in makefile and gitignore.
License Notice now refers to all contributors.
2022-08-26 16:14:07 +04:00
|
|
|
await self.confirm()
|
2022-06-23 15:13:56 +04:00
|
|
|
|
|
|
|
@self.wssreceiver(Streams.status)
|
MkDocs, Readme, Files API, Automated session saving, v2.0.1
MkDocs: sphinx docstrings rewritten to google, improved config, written the major part of how-to.
Readme: centered title + logo, added badges, features list, updated changelog.
Improved Files API, added automatical session saving and restoring to Client.
Some changes in makefile and gitignore.
License Notice now refers to all contributors.
2022-08-26 16:14:07 +04:00
|
|
|
async def streamsfunc(msg: Dict[str, Any]) -> None:
|
2022-06-23 15:13:56 +04:00
|
|
|
|
|
|
|
"""Automatically starts streams. Detailed description:
|
|
|
|
|
MkDocs, Readme, Files API, Automated session saving, v2.0.1
MkDocs: sphinx docstrings rewritten to google, improved config, written the major part of how-to.
Readme: centered title + logo, added badges, features list, updated changelog.
Improved Files API, added automatical session saving and restoring to Client.
Some changes in makefile and gitignore.
License Notice now refers to all contributors.
2022-08-26 16:14:07 +04:00
|
|
|
https://github.com/DarkCat09/python-aternos/issues/22#issuecomment-1146788496
|
2022-06-23 15:13:56 +04:00
|
|
|
According to the websocket messages from the web site,
|
|
|
|
Aternos can't receive any data from a stream (e.g. console) until
|
|
|
|
it requests this stream via the special message
|
|
|
|
to the websocket server: `{"stream":"console","type":"start"}`
|
|
|
|
on which the server responses with: `{"type":"connected"}`
|
|
|
|
Also, there are RAM (used heap) and TPS (ticks per second)
|
|
|
|
streams that must be enabled before trying to get information.
|
|
|
|
Enabling the stream for listening the server status is not needed,
|
|
|
|
these data is sent from API by default, so there's None value in
|
|
|
|
the second item of its stream type tuple
|
|
|
|
(`<Streams.status: (0, None)>`).
|
MkDocs, Readme, Files API, Automated session saving, v2.0.1
MkDocs: sphinx docstrings rewritten to google, improved config, written the major part of how-to.
Readme: centered title + logo, added badges, features list, updated changelog.
Improved Files API, added automatical session saving and restoring to Client.
Some changes in makefile and gitignore.
License Notice now refers to all contributors.
2022-08-26 16:14:07 +04:00
|
|
|
|
|
|
|
Args:
|
|
|
|
msg (Dict[str, Any]): Server info dictionary
|
2022-06-23 15:13:56 +04:00
|
|
|
"""
|
|
|
|
|
|
|
|
if msg['status'] == 2:
|
|
|
|
# Automatically start streams
|
|
|
|
for strm in self.recv:
|
|
|
|
|
|
|
|
if not isinstance(strm, Streams):
|
|
|
|
continue
|
|
|
|
|
|
|
|
if strm.stream:
|
MkDocs, Readme, Files API, Automated session saving, v2.0.1
MkDocs: sphinx docstrings rewritten to google, improved config, written the major part of how-to.
Readme: centered title + logo, added badges, features list, updated changelog.
Improved Files API, added automatical session saving and restoring to Client.
Some changes in makefile and gitignore.
License Notice now refers to all contributors.
2022-08-26 16:14:07 +04:00
|
|
|
logging.debug(f'Requesting {strm.stream} stream')
|
2022-06-23 15:13:56 +04:00
|
|
|
await self.send({
|
|
|
|
'stream': strm.stream,
|
|
|
|
'type': 'start'
|
|
|
|
})
|
|
|
|
|
|
|
|
await self.wssworker()
|
|
|
|
|
|
|
|
async def close(self) -> None:
|
|
|
|
|
|
|
|
"""Closes websocket connection and stops all listeners"""
|
|
|
|
|
|
|
|
self.keep.cancel()
|
|
|
|
self.msgs.cancel()
|
|
|
|
await self.socket.close()
|
|
|
|
del self.socket
|
|
|
|
|
|
|
|
async def send(self, obj: Union[Dict[str, Any], str]) -> None:
|
|
|
|
|
|
|
|
"""Sends a message to websocket server
|
|
|
|
|
MkDocs, Readme, Files API, Automated session saving, v2.0.1
MkDocs: sphinx docstrings rewritten to google, improved config, written the major part of how-to.
Readme: centered title + logo, added badges, features list, updated changelog.
Improved Files API, added automatical session saving and restoring to Client.
Some changes in makefile and gitignore.
License Notice now refers to all contributors.
2022-08-26 16:14:07 +04:00
|
|
|
Args:
|
|
|
|
obj (Union[Dict[str, Any],str]):
|
|
|
|
Message, may be a string or a dict
|
2022-06-23 15:13:56 +04:00
|
|
|
"""
|
|
|
|
|
|
|
|
if isinstance(obj, dict):
|
|
|
|
obj = json.dumps(obj)
|
|
|
|
|
|
|
|
await self.socket.send(obj)
|
|
|
|
|
|
|
|
async def wssworker(self) -> None:
|
|
|
|
|
|
|
|
"""Starts async tasks in background
|
|
|
|
for receiving websocket messages
|
|
|
|
and sending keepalive ping"""
|
|
|
|
|
|
|
|
self.keep = asyncio.create_task(self.keepalive())
|
|
|
|
self.msgs = asyncio.create_task(self.receiver())
|
|
|
|
|
|
|
|
async def keepalive(self) -> None:
|
|
|
|
|
|
|
|
"""Each 49 seconds sends keepalive ping to websocket server"""
|
|
|
|
|
|
|
|
try:
|
|
|
|
while True:
|
|
|
|
await asyncio.sleep(49)
|
|
|
|
await self.socket.send('{"type":"\u2764"}')
|
|
|
|
|
|
|
|
except asyncio.CancelledError:
|
|
|
|
pass
|
|
|
|
|
|
|
|
async def receiver(self) -> None:
|
|
|
|
|
|
|
|
"""Receives messages from websocket servers
|
|
|
|
and calls user's streams listeners"""
|
|
|
|
|
2022-07-01 14:28:39 +04:00
|
|
|
while True:
|
|
|
|
try:
|
2022-06-23 15:13:56 +04:00
|
|
|
data = await self.socket.recv()
|
|
|
|
obj = json.loads(data)
|
2022-07-01 14:28:39 +04:00
|
|
|
msgtype = Streams.none
|
2022-06-23 15:13:56 +04:00
|
|
|
|
|
|
|
if obj['type'] == 'line':
|
|
|
|
msgtype = Streams.console
|
|
|
|
msg = obj['data'].strip('\r\n ')
|
|
|
|
|
|
|
|
elif obj['type'] == 'heap':
|
|
|
|
msgtype = Streams.ram
|
|
|
|
msg = int(obj['data']['usage'])
|
|
|
|
|
|
|
|
elif obj['type'] == 'tick':
|
|
|
|
msgtype = Streams.tps
|
|
|
|
ticks = 1000 / obj['data']['averageTickTime']
|
|
|
|
msg = 20 if ticks > 20 else ticks
|
|
|
|
|
|
|
|
elif obj['type'] == 'status':
|
|
|
|
msgtype = Streams.status
|
|
|
|
msg = json.loads(obj['message'])
|
|
|
|
|
|
|
|
if msgtype in self.recv:
|
|
|
|
|
2022-07-01 14:28:39 +04:00
|
|
|
# function info tuple
|
|
|
|
func: ArgsTuple = self.recv[msgtype]
|
2022-06-23 15:13:56 +04:00
|
|
|
|
|
|
|
# if arguments is not empty
|
|
|
|
if func[1]:
|
|
|
|
# call the function with args
|
2022-07-01 14:28:39 +04:00
|
|
|
coro = func[0](msg, func[1]) # type: ignore
|
2022-06-23 15:13:56 +04:00
|
|
|
else:
|
2022-07-01 14:28:39 +04:00
|
|
|
# mypy error: Too few arguments
|
|
|
|
# looks like bug, so it is ignored
|
|
|
|
coro = func[0](msg) # type: ignore
|
2022-06-23 15:13:56 +04:00
|
|
|
# run
|
2022-07-01 14:28:39 +04:00
|
|
|
asyncio.create_task(coro)
|
2022-06-23 15:13:56 +04:00
|
|
|
|
2022-07-01 14:28:39 +04:00
|
|
|
except asyncio.CancelledError:
|
|
|
|
break
|