Merge pull request #5 from anorak999/fix/new-change

This commit is contained in:
mirai 2025-11-07 16:45:09 +03:00 committed by GitHub
commit 945d7aeb62
9 changed files with 43 additions and 31 deletions

View File

@ -40,7 +40,7 @@ class Client(RSAService, RichClientRenderer):
return f"{self.ws_url}{path}"
def _connect_ws(self, path: str, retries: int = 5, backoff: float = 0.5):
last_exc = None
last_exc: Exception = ConnectionError("Failed to connect")
for attempt in range(retries):
try:
return create_connection(self._ws_full(path))

View File

@ -19,7 +19,7 @@ class CryptoService(ABC):
raise NotImplementedError("Need to implement generate keys method")
@abstractmethod
def _get_generated_keys(self) -> list[str]:
def _get_generated_keys(self) -> tuple:
raise NotImplementedError("Need to implement get generated keys method")
@abstractmethod

View File

@ -2,23 +2,33 @@ from abc import ABC, abstractmethod
class ClientRenderer(ABC):
# These attributes are expected to be provided by subclasses
# (typically via multiple inheritance with CryptoService)
username: str
@abstractmethod
def _decrypt(self, message: str) -> str:
"""Decrypt an encrypted message (provided by crypto mixin)."""
raise NotImplementedError("Need to implement _decrypt")
@abstractmethod
def print_message(self, message: str) -> str:
raise NotImplementedError("Need to implement print_message")
@abstractmethod
def clear_console(self, message: str) -> str:
def clear_console(self) -> None:
"""Clear the client console (platform-specific)."""
raise NotImplementedError("Need to implement clear_console")
@abstractmethod
def print_ip(self, url: str, username: str):
def print_ip(self, ip: str) -> str:
raise NotImplementedError("Need to implement print_ip")
@abstractmethod
def print_username(self):
def print_username(self, username: str) -> str:
raise NotImplementedError("Need to implement print_username")
@abstractmethod
def print_chat(self) -> list[str]:
def print_chat(self, response) -> None:
"""Render chat payload (response is expected to be a mapping with 'messages' and 'users_in_chat')."""
raise NotImplementedError("Need to implement print_chat")

View File

@ -28,7 +28,8 @@ class RSAService(CryptoService):
stream=True,
)
r.raise_for_status()
message = r.raw.read(999)
# read the full response content (server returns encrypted symmetric key)
message = r.content
self.symmetric_key = rsa.decrypt(message, self.private_key)
self.fernet = Fernet(self.symmetric_key)

View File

@ -21,10 +21,11 @@ class DefaultClientRenderer(ClientRenderer):
def print_message(self, message: str) -> str:
""" generating string with message in required format
"""
message = message.split(":")
if message[0] == self.username:
return COLORS["my_username_color"] + message[0] + ": " + message[1] + COLORS["text_color"]
return message[0] + ": " + message[1] + COLORS["text_color"]
# split only on the first ':' to keep message contents intact
parts = message.split(":", 1)
if parts[0] == self.username:
return COLORS["my_username_color"] + parts[0] + ": " + parts[1] + COLORS["text_color"]
return parts[0] + ": " + parts[1] + COLORS["text_color"]
def clear_console(self):
# For windows clear command its cls
@ -44,9 +45,10 @@ class DefaultClientRenderer(ClientRenderer):
self,
username: str
) -> str:
return f"USERNAME: " + COLORS["ip_color"] + username + COLORS["username_color"]
# Username label + colored username
return f"USERNAME: " + COLORS["username_color"] + username + COLORS["text_color"]
def print_chat(self, response: list[str]) -> str:
def print_chat(self, response) -> None:
for i, msg in enumerate(response["messages"]):
actual_message = self._decrypt(msg)
if i == 0:

View File

@ -25,16 +25,17 @@ class RichClientRenderer(ClientRenderer):
def print_message(self, message: str) -> Text:
""" generating string with message in required format
"""
message = message.split(":")
if message[0] == self.username:
# split only on the first ':' so message bodies containing ':' are preserved
parts = message.split(":", 1)
if parts[0] == self.username:
return \
Text(text=message[0], style="bold") + \
Text(text=parts[0], style="bold") + \
Text(text=": ", style="bold") + \
Text(text=message[1], style="underline")
Text(text=parts[1], style="underline")
return \
Text(text=message[0], style="bold") + \
Text(text=parts[0], style="bold") + \
Text(text=": ", style="bold") + \
Text(text=message[1], style="underline")
Text(text=parts[1], style="underline")
def clear_console(self):
# For windows clear command its cls
@ -56,7 +57,7 @@ class RichClientRenderer(ClientRenderer):
) -> str:
return username
def print_chat(self, response: list[str]) -> str:
def print_chat(self, response) -> None:
self.clear_console()
for i, msg in enumerate(response["messages"][-MESSAGES_TO_SHOW:]):
actual_message = self._decrypt(msg)

View File

@ -40,7 +40,10 @@ def attach_endpoints(app: Sanic):
while True:
serialized_message: dict = await _get_bytes_and_serialize(ws)
await _check_ws_for_close_status(serialized_message, ws)
new_message = await _generate_new_message(serialized_message.get("text"))
text = serialized_message.get("text")
if text is None:
continue
new_message = await _generate_new_message(text)
MESSAGES_MEMORY_DB.append(new_message)
await ws.send(str({"status": "ok"}))
await asyncio.sleep(0.2)

View File

@ -25,7 +25,7 @@ async def _generate_new_message(
async def _generate_update_payload(
memory_msgs: list[str],
memory_msgs: list[Message],
users_structure: dict
) -> str:
return str({

View File

@ -8,13 +8,8 @@ setuptools.setup(
version="1.1.22",
author="dinosaurtirex",
author_email="sneakybeaky18@gmail.com",
packages=[
"cmd_chat",
"cmd_chat/client",
"cmd_chat/client/core",
"cmd_chat/client/core/abs",
"cmd_chat/server"
],
# Use find_packages to correctly discover package names
packages=setuptools.find_packages(exclude=("tests", "docs")),
description="Secured console chat with RSA & Fernet",
long_description=description,
long_description_content_type="text/markdown",