diff --git a/DMBotNetwork/side/side_client.py b/DMBotNetwork/side/side_client.py index 54564d7..d1d04ca 100644 --- a/DMBotNetwork/side/side_client.py +++ b/DMBotNetwork/side/side_client.py @@ -17,9 +17,10 @@ class Client: _ear_task: Optional[asyncio.Task] = None # lol _viva_alp: bool = True - _login: str = None - _password: str = None + _login: Optional[str] = None + _password: Optional[str] = None _content_path: Optional[Path] = None + _temp_fold: Optional[Path] = None _server_name: Optional[str] = None _is_connected: bool = False @@ -71,11 +72,15 @@ async def _call_method( logger.error(f"Error calling method '{method_name}' in {cls.__name__}: {e}") @classmethod - async def connect(cls, host, port) -> None: + async def connect(cls, host: str, port: int) -> None: + if not all((cls._login, cls._password, cls._content_path)): + logger.warning("Login, password or content_path not set, abort connect") + return + cls._reader, cls._writer = await asyncio.open_connection(host, port) cls._is_connected = True - cls._ear_task = asyncio.create_task(cls._ear) + cls._ear_task = asyncio.create_task(cls._ear()) @classmethod def is_connected(cls) -> bool: @@ -89,6 +94,24 @@ def get_auth_lp(cls) -> bool: def set_auth_lp(cls, value: bool) -> None: cls._viva_alp = value + @classmethod + def get_login(cls) -> Optional[str]: + return cls._login + + @classmethod + def set_login(cls, value: str) -> None: + cls._login = value + + @classmethod + def set_password(cls, value: str) -> None: + cls._password = value + + @classmethod + def set_up_content_path(cls, value: Path | str) -> None: + cls._content_path = Path(value) + cls._temp_fold = cls._content_path / "temp" + cls._temp_fold.mkdir(exist_ok=True) + @classmethod async def disconnect(cls) -> None: cls._is_connected = False @@ -144,33 +167,44 @@ async def _ear(cls) -> None: cls._server_name = receive_packet.get( "server_name", "Not_Set_Server_Name" ) + Path(cls._content_path / cls._server_name).mkdir(exist_ok=True) # type: ignore await cls._auth() elif code == NetCode.REQ_FILE_DOWNLOAD: - await cls._download_file(receive_packet) + cls._download_file(receive_packet) + + elif code == NetCode.END_FILE_DOWNLOAD: + cls._move_file(receive_packet) else: logger.error("Unknown 'code' type from server") @classmethod - async def send_packet(self, code: NCAnyType, **kwargs: Any) -> None: + async def req_net(cls, type: str, **kwargs: Any) -> None: + await cls.send_packet(NetCode.REQ_NET.value, type=type, **kwargs) + + @classmethod + async def send_packet(cls, code: NCAnyType, **kwargs: Any) -> None: payload = {"code": code, **kwargs} - await self.send_raw(msgpack.packb(payload)) + await cls._send_raw(msgpack.packb(payload)) # type: ignore @classmethod - async def send_raw(self, data: bytes) -> None: - if self._writer is None: + async def _send_raw(cls, data: bytes) -> None: + if cls._writer is None: raise ValueError("StreamWriter is not set") - self._writer.write(len(data).to_bytes(4, byteorder="big")) - await self._writer.drain() + cls._writer.write(len(data).to_bytes(4, byteorder="big")) + await cls._writer.drain() - self._writer.write(data) - await self._writer.drain() + cls._writer.write(data) + await cls._writer.drain() @classmethod async def _receive_packet(cls) -> Any: + if not cls._reader: + return + data_size_bytes = await cls._reader.readexactly(4) data_size = int.from_bytes(data_size_bytes, "big") @@ -181,16 +215,16 @@ async def _receive_packet(cls) -> Any: def _log(cls, code: NCLogType, receive_packet: dict) -> None: msg = receive_packet.get("message", "Not set") - if code == NetCode.REQ_LOG_DEBUG: + if code == NetCode.REQ_LOG_DEBUG.value: logger.debug(msg) - elif code == NetCode.REQ_LOG_INFO: + elif code == NetCode.REQ_LOG_INFO.value: logger.info(msg) - elif code == NetCode.REQ_LOG_WARNING: + elif code == NetCode.REQ_LOG_WARNING.value: logger.warning(msg) - elif code == NetCode.REQ_LOG_ERROR: + elif code == NetCode.REQ_LOG_ERROR.value: logger.error(msg) else: @@ -200,16 +234,16 @@ def _log(cls, code: NCLogType, receive_packet: dict) -> None: async def _auth(cls) -> None: if cls._viva_alp: await cls.send_packet( - NetCode.ANSWER_AUTH_ALP, login=cls._login, password=cls._password + NetCode.ANSWER_AUTH_ALP.value, login=cls._login, password=cls._password ) else: await cls.send_packet( - NetCode.ANSWER_AUTH_REG, login=cls._login, password=cls._password + NetCode.ANSWER_AUTH_REG.value, login=cls._login, password=cls._password ) @classmethod - async def _download_file(cls, receive_packet: dict) -> None: + def _download_file(cls, receive_packet: dict) -> None: try: file_name = receive_packet.get("file_name", None) chunk = receive_packet.get("chunk", None) @@ -217,10 +251,21 @@ async def _download_file(cls, receive_packet: dict) -> None: if not chunk or file_name: return - file_path: Path = cls._content_path / cls._server_name / file_name + file_path: Path = cls._content_path / cls._temp_fold / file_name # type: ignore - with file_path.open("wb") as file: + with file_path.open("wb+") as file: file.write(chunk) except Exception as e: - await logger.error(f"Error receiving file: {e}") + logger.error(f"Error receiving file: {e}") + + @classmethod + def _move_file(cls, receive_packet: dict) -> None: + file_name = receive_packet.get("file_name") + if not file_name: + return + + file_path: Path = cls._content_path / cls._temp_fold / file_name # type: ignore + if file_path.exists(): + path_serve = cls._content_path / cls._server_name / file_name # type: ignore + file_path.rename(path_serve) diff --git a/DMBotNetwork/side/side_server.py b/DMBotNetwork/side/side_server.py index 683072a..cb82d7e 100644 --- a/DMBotNetwork/side/side_server.py +++ b/DMBotNetwork/side/side_server.py @@ -3,7 +3,7 @@ import logging from asyncio import StreamReader, StreamWriter from pathlib import Path -from typing import Any, Dict, Optional, Set, get_type_hints +from typing import Any, Dict, Optional, get_type_hints from ..units import ClientUnit from ..utils import NetCode, ServerDB @@ -15,11 +15,10 @@ class Server: _network_methods: Dict[str, Any] = {} _connections: Dict[str, ClientUnit] = {} - _one_time_token: Set[str] = {} _allow_registration: bool = True _timeout: float = 30.0 - _server_name: str = None + _server_name: Optional[str] = None _is_online: bool = False _main_server: Optional[asyncio.AbstractServer] = None @@ -75,7 +74,7 @@ async def start( cls, host: str = "localhost", main_port: int = 5000, - db_file_path: Path | str = None, + db_file_path: Path | str = "", base_owner_password: str = "owner_password", timeout: float = 30.0, allow_registration: bool = True, @@ -94,6 +93,7 @@ async def start( cls._allow_registration = allow_registration cls._timeout = timeout + cls._server_name = server_name cls._main_server = await asyncio.start_server( cls._main_client_handler, host, main_port @@ -209,7 +209,7 @@ async def _main_client_handler( # Auth @classmethod async def _auth(cls, cl_unit: ClientUnit) -> None: - await cl_unit.send_packet(NetCode.REQ_AUTH, server_name=cls._server_name) + await cl_unit.send_packet(NetCode.REQ_AUTH.value, server_name=cls._server_name) receive_packet = await asyncio.wait_for(cl_unit._receive_packet(), cls._timeout) if not isinstance(receive_packet, dict): diff --git a/DMBotNetwork/units/client_unit.py b/DMBotNetwork/units/client_unit.py index 4544d9a..f0667c4 100644 --- a/DMBotNetwork/units/client_unit.py +++ b/DMBotNetwork/units/client_unit.py @@ -40,31 +40,31 @@ def writer(self) -> StreamWriter: return self._writer @property - def reader(self) -> StreamWriter: + def reader(self) -> StreamReader: return self._reader # Net async def req_net(self, type: str, **kwargs: Any) -> None: - await self.send_packet(NetCode.REQ_NET, type=type, **kwargs) + await self.send_packet(NetCode.REQ_NET.value, type=type, **kwargs) # Logs async def log_debug(self, message: str) -> None: - await self.send_packet(NetCode.REQ_LOG_DEBUG, message=message) + await self.send_packet(NetCode.REQ_LOG_DEBUG.value, message=message) async def log_info(self, message: str) -> None: - await self.send_packet(NetCode.REQ_LOG_INFO, message=message) + await self.send_packet(NetCode.REQ_LOG_INFO.value, message=message) async def log_warning(self, message: str) -> None: - await self.send_packet(NetCode.REQ_LOG_WARNING, message=message) + await self.send_packet(NetCode.REQ_LOG_WARNING.value, message=message) async def log_error(self, message: str) -> None: - await self.send_packet(NetCode.REQ_LOG_ERROR, message=message) + await self.send_packet(NetCode.REQ_LOG_ERROR.value, message=message) # Send async def send_packet(self, code: NCAnyType, **kwargs: Any) -> None: payload = {"code": code, **kwargs} - await self.send_raw(msgpack.packb(payload)) + await self.send_raw(msgpack.packb(payload)) # type: ignore async def send_raw(self, data: bytes) -> None: if self._writer is None: @@ -88,10 +88,13 @@ async def send_file( while True: chunk = file.read(chunk_size) if not chunk: + await self.send_packet( + NetCode.END_FILE_DOWNLOAD.value, file_name=file_name + ) break await self.send_packet( - NetCode.REQ_FILE_DOWNLOAD, file_name=file_name, chunk=chunk + NetCode.REQ_FILE_DOWNLOAD.value, file_name=file_name, chunk=chunk ) except Exception as e: diff --git a/DMBotNetwork/utils/net_code_encode.py b/DMBotNetwork/utils/net_code_encode.py index 8401d8a..f10cd1c 100644 --- a/DMBotNetwork/utils/net_code_encode.py +++ b/DMBotNetwork/utils/net_code_encode.py @@ -3,19 +3,19 @@ class NetCode(Enum): - REQ_AUTH: int = 10 # Запрос аунтификации - ANSWER_AUTH_ALP: int = 11 # Ответ запроса аунтификации через логин пароль - ANSWER_AUTH_REG: int = 12 # Ответ запроса регестрации + REQ_AUTH = 10 # Запрос аунтификации + ANSWER_AUTH_ALP = 11 # Ответ запроса аунтификации через логин пароль + ANSWER_AUTH_REG = 12 # Ответ запроса регестрации - REQ_NET: int = 20 # Запрос net метода клиента - REQ_FILE_DOWNLOAD: int = 21 - END_FILE_DOWNLOAD: int = 22 + REQ_NET = 20 # Запрос net метода клиента + REQ_FILE_DOWNLOAD = 21 + END_FILE_DOWNLOAD = 22 # Лог ивенты - REQ_LOG_DEBUG: int = 30 - REQ_LOG_INFO: int = 31 - REQ_LOG_WARNING: int = 32 - REQ_LOG_ERROR: int = 33 + REQ_LOG_DEBUG = 30 + REQ_LOG_INFO = 31 + REQ_LOG_WARNING = 32 + REQ_LOG_ERROR = 33 NCReqType = Literal[10] diff --git a/DMBotNetwork/utils/server_db.py b/DMBotNetwork/utils/server_db.py index f5c29af..abfce5a 100644 --- a/DMBotNetwork/utils/server_db.py +++ b/DMBotNetwork/utils/server_db.py @@ -30,7 +30,7 @@ def set_base_access(cls, value: Dict[str, bool]) -> None: cls._base_access = value @classmethod - def get_db_path(cls) -> Path: + def get_db_path(cls) -> Optional[Path]: return cls._db_path @classmethod @@ -79,6 +79,10 @@ async def _init_db(cls) -> None: @classmethod async def _init_exist_user(cls) -> None: + if not cls._connection: + logger.error("Connection not set") + return + try: async with cls._connection.execute("SELECT username FROM users") as cursor: rows = await cursor.fetchall() @@ -90,6 +94,10 @@ async def _init_exist_user(cls) -> None: @classmethod async def _init_owner(cls) -> None: + if not cls._connection: + logger.error("Connection not set") + return + try: if "owner" not in cls._exist_user: owner_password_hashed = await cls._hash_password( @@ -135,6 +143,10 @@ async def _hash_password(cls, password: str) -> bytes: # Управление пользователями @classmethod async def login_user(cls, login: str, password: str) -> Optional[str]: + if not cls._connection: + logger.error("Connection not set") + return + if login not in cls._exist_user: raise ValueError(f"User '{login}' not found.") @@ -159,6 +171,10 @@ async def login_user(cls, login: str, password: str) -> Optional[str]: async def add_user( cls, username: str, password: str, access: Optional[Dict[str, bool]] = None ) -> None: + if not cls._connection: + logger.error("Connection not set") + return + if username in cls._exist_user: raise ValueError(f"User '{username}' already exists.") @@ -184,6 +200,10 @@ async def add_user( @classmethod async def delete_user(cls, username: str) -> None: + if not cls._connection: + logger.error("Connection not set") + return + if username not in cls._exist_user: return @@ -202,6 +222,10 @@ async def delete_user(cls, username: str) -> None: @classmethod async def change_user_password(cls, username: str, new_password: str) -> None: + if not cls._connection: + logger.error("Connection not set") + return + if username not in cls._exist_user: return @@ -221,8 +245,12 @@ async def change_user_password(cls, username: str, new_password: str) -> None: async def change_user_access( cls, username: str, new_access: Optional[Dict[str, bool]] = None ) -> bool: + if not cls._connection: + logger.error("Connection not set") + return False + if username not in cls._exist_user: - return + return False if username == "owner": new_access = {"full_access": True} @@ -247,6 +275,10 @@ async def change_user_access( @classmethod async def get_access(cls, username: str) -> Optional[Dict[str, bool]]: + if not cls._connection: + logger.error("Connection not set") + return None + if username not in cls._exist_user: return None