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
2022-10-31 17:27:21 +04:00

334 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