Code refactoring. Add config for customize colors, add services, fixed bugs & glitches

This commit is contained in:
mirai 2023-03-08 18:30:26 +03:00
parent b763787633
commit 0f066d367f
6 changed files with 149 additions and 46 deletions

3
.gitignore vendored
View File

@ -7,4 +7,5 @@ commiter.py
__pycache__
*.pyc
.idea
*.pem
*.pem
__pycache__

View File

@ -9,6 +9,10 @@
![Alt Text](example.gif)
# How it works?
All you need it's to run web-server and connect to them via client
# Server run
## Linux
@ -38,7 +42,7 @@ pip install -r requirements.txt
```
```
sanic server.app -H 0.0.0.0 -p <port>
sanic server.server.app -H 0.0.0.0 -p <port>
```
# Client run
@ -69,7 +73,7 @@ pip install -r requirements.txt
```
```
python client.py
python client/client.py
```
## How crypting works?

View File

@ -6,6 +6,9 @@ from colorama import init
from colorama import Fore
from websocket import create_connection
from core.crypto import RSAService
from config import (
COLORS
)
init()
@ -25,13 +28,21 @@ class Client(RSAService):
self.info_url = f"{self.base_url}/update"
self.key_url = f"{self.base_url}/get_key"
self.ws_url = f"ws://{self.server}:{self.port}"
self.close_response = str({
"action": "close",
"username": self.username
})
def __get_os(self) -> str:
""" checking what kind of platform you need
"""
if "Linux" in str(platform.platform()):
return "Linux"
return "Windows"
def send_info(self):
""" sending message to websocket
"""
ws = create_connection(f"{self.ws_url}/talk")
while True:
try:
@ -41,20 +52,26 @@ class Client(RSAService):
"text": self._encrypt(message),
"username": self.username
})
ws.send(payload=socket_message.encode())
ws.send(
payload=socket_message.encode()
)
except KeyboardInterrupt:
ws.send(self.close_response)
ws.close()
quit()
except Exception as exc:
ws.send(self.close_response)
ws.close()
print("Something went wrong! ", exc)
quit()
def print_message(self, message: str) -> str:
def __print_message(self, message: str) -> str:
""" generating string with message in required format
"""
message = message.split(":")
if message[0] == self.username:
return Fore.MAGENTA + message[0] + ": " + message[1] + Fore.WHITE
return message[0] + ": " + message[1] + Fore.WHITE
return COLORS["my_username_color"] + message[0] + ": " + message[1] + COLORS["text_color"]
return message[0] + ": " + message[1] + COLORS["text_color"]
def __clear_console(self):
# For windows clear command its cls
@ -64,31 +81,54 @@ class Client(RSAService):
else:
os.system("cls")
def __print_ip(
self,
ip: str
) -> str:
return f"IP: " + COLORS["ip_color"] + ip + COLORS["text_color"]
def __print_username(
self,
username: str
) -> str:
return f"USERNAME: " + COLORS["ip_color"] + username + COLORS["username_color"]
def __print_chat(self, response: list[str]) -> str:
for i, msg in enumerate(response["messages"]):
actual_message = self._decrypt(msg)
if i == 0:
for user in response["users_in_chat"]:
print(self.__print_ip(user.split(",")[0]))
print(self.__print_username(user.split(",")[1]))
print(f"\n{self.__print_message(actual_message)}")
else:
print(f"{self.__print_message(actual_message)}")
def update_info(self):
""" connecting to websocket,
wating for updates,
updating every 0.05 seconds
"""
ws = create_connection(f"{self.ws_url}/update")
last_try = None
while True:
try:
time.sleep(0.05)
r = eval(ws.recv())
if last_try == r:
response = eval(ws.recv())
if last_try == response:
continue
last_try = r
last_try = response
self.__clear_console()
if len(last_try['status']) > 0:
for i, msg in enumerate(last_try["status"]):
actual_message = self._decrypt(msg)
if i == 0:
for user in last_try["users_in_chat"]:
print("IP:", Fore.MAGENTA + user.split(",")[0] + Fore.WHITE)
print("USERNAME: ", Fore.GREEN + user.split(",")[1] + Fore.WHITE)
print(f"\n{self.print_message(actual_message)}")
else:
print(f"{self.print_message(actual_message)}")
if len(last_try["messages"]) > 0:
self.__print_chat(
response = last_try
)
except KeyboardInterrupt:
ws.send(self.close_response)
ws.close()
quit()
except Exception as exc:
ws.send(self.close_response)
ws.close()
print("Something went wrong! ", exc)
quit()

8
client/config.py Normal file
View File

@ -0,0 +1,8 @@
from colorama import Fore
COLORS = {
"text_color": Fore.WHITE,
"my_username_color": Fore.MAGENTA,
"ip_color": Fore.MAGENTA,
"username_color": Fore.GREEN
}

View File

@ -1,53 +1,68 @@
import rsa
import asyncio
from server.models import Message
import rsa
from cryptography.fernet import Fernet
from sanic.response import HTTPResponse
from sanic import Sanic, Request, response, Websocket
from server.models import Message
from server.services import (
_get_bytes_and_serialize,
_check_ws_for_close_status,
_generate_new_message,
_generate_update_payload
)
app = Sanic("app")
app.config.OAS = False
# Message structure is:
# [username: message, ...]
actual_messages: list[Message] = []
MESSAGES_MEMORY_DB: list[Message] = []
# Users structure is
# {Ip, Username: Public key}
users: dict[str, str] = {}
key = Fernet.generate_key()
USERS: dict[str, str] = {}
PUBLIC_KEY = Fernet.generate_key()
@app.websocket("/talk")
async def talking(request: Request, ws: Websocket) -> HTTPResponse:
async def talk_ws_view(request: Request, ws: Websocket) -> HTTPResponse:
while True:
data: str = await ws.recv()
serialized_message: dict = eval(data)
new_message = Message(
message=serialized_message.get("text")
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")
)
MESSAGES_MEMORY_DB.append(new_message)
await ws.send(
str({"status": "ok"})
)
actual_messages.append(new_message)
await ws.send("{'status': 'ok'}")
await asyncio.sleep(0.2)
@app.websocket("/update")
async def talking(request: Request, ws: Websocket) -> HTTPResponse:
async def update_ws_view(request: Request, ws: Websocket) -> HTTPResponse:
while True:
payload = str({
"status": [i.message for i in actual_messages],
"users_in_chat": list(users.keys())
})
payload = await _generate_update_payload(
MESSAGES_MEMORY_DB,
USERS
)
await ws.send(payload.encode())
await asyncio.sleep(0.2)
@app.route('/get_key', methods=['GET', 'POST'])
async def get_key(request: Request) -> HTTPResponse:
pubkey = rsa.PublicKey.load_pkcs1(request.form.get('pubkey'))
data = rsa.encrypt(key, pubkey)
if request.ip not in users:
users[f"{request.ip}, {request.form.get('username')}"] = key
return response.raw(data)
async def get_key_view(request: Request) -> HTTPResponse:
public_key = rsa.PublicKey.load_pkcs1(request.form.get('pubkey'))
encrypted_data = rsa.encrypt(PUBLIC_KEY, public_key)
if request.ip not in USERS:
USERS[f"{request.ip}, {request.form.get('username')}"] = PUBLIC_KEY
return response.raw(encrypted_data)

35
server/services.py Normal file
View File

@ -0,0 +1,35 @@
from sanic import Sanic, Request, response, Websocket
from server.models import Message
async def _get_bytes_and_serialize(
ws: Websocket
) -> dict:
return eval(await ws.recv())
async def _check_ws_for_close_status(
response: dict,
ws: Websocket
) -> None:
if "action" in response.keys():
if response["action"] == "close":
await ws.close()
async def _generate_new_message(
message: str
) -> Message:
return Message(message = message)
async def _generate_update_payload(
memory_msgs: list[str],
users_structure: dict
) -> str:
return str({
"messages": [i.message for i in memory_msgs],
"users_in_chat": list(users_structure.keys())
})