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/atfile.py
DarkCat09 4892430f19 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

351 lines
8.5 KiB
Python

"""File info object used by `python_aternos.atfm`"""
import enum
from typing import Union
from typing import TYPE_CHECKING
import lxml.html
from .aterrors import FileError
if TYPE_CHECKING:
from .atserver import AternosServer
class FileType(enum.IntEnum):
"""File or dierctory"""
file = 0
directory = 1
dir = 1
class AternosFile:
"""File class which contains info
about its path, type and size"""
def __init__(
self,
atserv: 'AternosServer',
path: str, rmable: bool,
dlable: bool, editable: bool,
ftype: FileType = FileType.file,
size: Union[int, float] = 0) -> None:
"""File class which contains info
about its path, type and size
Args:
atserv (python_aternos.atserver.AternosServer):
atserver.AternosServer instance
path (str): Absolute path to the file
rmable (bool): Is the file deleteable (removeable)
dlable (bool): Is the file downloadable
ftype (python_aternos.atfile.FileType): File or directory
size (Union[int,float], optional): File size
"""
path = path.lstrip('/')
path = '/' + path
self.atserv = atserv
self._path = path
self._name = path[path.rfind('/') + 1:]
self._dirname = path[:path.rfind('/')]
self._deleteable = rmable
self._downloadable = dlable
self._editable = editable
self._ftype = ftype
self._size = float(size)
def create(
self,
name: str,
ftype: FileType = FileType.file) -> None:
"""Creates a file or a directory inside this one
Args:
name (str): Filename
ftype (FileType, optional): File type
Raises:
RuntimeWarning: Messages about probabilty of FileError
(if `self` file object is not a directory)
FileError: If Aternos denied file creation
"""
if self.is_file:
raise RuntimeWarning(
'Creating files only available '
'inside directories'
)
name = name.strip().replace('/', '_')
req = self.atserv.atserver_request(
'https://aternos.org/panel/ajax/files/create.php',
'POST', data={
'file': f'{self._path}/{name}',
'type': 'file'
if ftype == FileType.file
else 'directory'
}
)
if req.content == b'{"success":false}':
raise FileError('Unable to create a file')
def delete(self) -> None:
"""Deletes the file
Raises:
RuntimeWarning: Message about probability of FileError
FileError: If deleting this file is disallowed by Aternos
"""
if not self._deleteable:
raise RuntimeWarning(
'The file seems to be protected (undeleteable). '
'Always check it before calling delete()'
)
req = self.atserv.atserver_request(
'https://aternos.org/panel/ajax/delete.php',
'POST', data={'file': self._path},
sendtoken=True
)
if req.content == b'{"success":false}':
raise FileError('Unable to delete the file')
def get_content(self) -> bytes:
"""Requests file content in bytes (downloads it)
Raises:
RuntimeWarning: Message about probability of FileError
FileError: If downloading this file is disallowed by Aternos
Returns:
File content
"""
if not self._downloadable:
raise RuntimeWarning(
'The file seems to be undownloadable. '
'Always check it before calling get_content()'
)
file = self.atserv.atserver_request(
'https://aternos.org/panel/ajax/files/download.php',
'GET', params={
'file': self._path
}
)
if file.content == b'{"success":false}':
raise FileError(
'Unable to download the file. '
'Try to get text'
)
return file.content
def set_content(self, value: bytes) -> None:
"""Modifies file content
Args:
value (bytes): New content
Raises:
FileError: If Aternos denied file saving
"""
req = self.atserv.atserver_request(
'https://aternos.org/panel/ajax/save.php',
'POST', data={
'file': self._path,
'content': value
}, sendtoken=True
)
if req.content == b'{"success":false}':
raise FileError('Unable to save the file')
def get_text(self) -> str:
"""Requests editing the file as a text
Raises:
RuntimeWarning: Message about probability of FileError
FileError: If unable to parse text from response
Returns:
File text content
"""
if not self._editable:
raise RuntimeWarning(
'The file seems to be uneditable. '
'Always check it before calling get_text()'
)
if self.is_dir:
raise RuntimeWarning(
'Use get_content() to download '
'a directory as a ZIP file!'
)
filepath = self._path.lstrip("/")
editor = self.atserv.atserver_request(
f'https://aternos.org/files/{filepath}', 'GET'
)
edittree = lxml.html.fromstring(editor.content)
editblock = edittree.xpath('//div[@id="editor"]')
if len(editblock) < 1:
raise FileError(
'Unable to open editor. '
'Try to get file content'
)
return editblock[0].text_content()
def set_text(self, value: str) -> None:
"""Modifies the file content,
but unlike `set_content` takes
a string as an argument
Args:
value (str): New content
"""
self.set_content(value.encode('utf-8'))
@property
def path(self) -> str:
"""Abslute path to the file
without leading slash
including filename
Returns:
Full path to the file
"""
return self._path
@property
def name(self) -> str:
"""Filename with extension
Returns:
Filename
"""
return self._name
@property
def dirname(self) -> str:
"""Full path to the directory
which contains the file
without leading slash.
Empty path means root (`/`)
Returns:
Path to the directory
"""
return self._dirname
@property
def deleteable(self) -> bool:
"""True if the file can be deleted,
otherwise False
Returns:
Can the file be deleted
"""
return self._deleteable
@property
def downloadable(self) -> bool:
"""True if the file can be downloaded,
otherwise False
Returns:
Can the file be downloaded
"""
return self._downloadable
@property
def editable(self) -> bool:
"""True if the file can be
opened in Aternos editor,
otherwise False
Returns:
Can the file be edited
"""
return self._editable
@property
def ftype(self) -> FileType:
"""File object type: file or directory
Returns:
File type
"""
return self._ftype
@property
def is_dir(self) -> bool:
"""Check if the file object is a directory
Returns:
True if it is a directory, otherwise False
"""
return self._ftype == FileType.dir
@property
def is_file(self) -> bool:
"""Check if the file object is not a directory
Returns:
True if it is a file, otherwise False
"""
return self._ftype == FileType.file
@property
def size(self) -> float:
"""File size in bytes
Returns:
File size
"""
return self._size