334 lines
8.5 KiB
Python
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
|