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__ __pycache__
*.pyc *.pyc
.idea .idea
*.pem *.pem
__pycache__

View File

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

View File

@ -6,6 +6,9 @@ from colorama import init
from colorama import Fore from colorama import Fore
from websocket import create_connection from websocket import create_connection
from core.crypto import RSAService from core.crypto import RSAService
from config import (
COLORS
)
init() init()
@ -25,13 +28,21 @@ class Client(RSAService):
self.info_url = f"{self.base_url}/update" self.info_url = f"{self.base_url}/update"
self.key_url = f"{self.base_url}/get_key" self.key_url = f"{self.base_url}/get_key"
self.ws_url = f"ws://{self.server}:{self.port}" self.ws_url = f"ws://{self.server}:{self.port}"
self.close_response = str({
"action": "close",
"username": self.username
})
def __get_os(self) -> str: def __get_os(self) -> str:
""" checking what kind of platform you need
"""
if "Linux" in str(platform.platform()): if "Linux" in str(platform.platform()):
return "Linux" return "Linux"
return "Windows" return "Windows"
def send_info(self): def send_info(self):
""" sending message to websocket
"""
ws = create_connection(f"{self.ws_url}/talk") ws = create_connection(f"{self.ws_url}/talk")
while True: while True:
try: try:
@ -41,20 +52,26 @@ class Client(RSAService):
"text": self._encrypt(message), "text": self._encrypt(message),
"username": self.username "username": self.username
}) })
ws.send(payload=socket_message.encode()) ws.send(
payload=socket_message.encode()
)
except KeyboardInterrupt: except KeyboardInterrupt:
ws.send(self.close_response)
ws.close() ws.close()
quit() quit()
except Exception as exc: except Exception as exc:
ws.send(self.close_response)
ws.close() ws.close()
print("Something went wrong! ", exc) print("Something went wrong! ", exc)
quit() 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(":") message = message.split(":")
if message[0] == self.username: if message[0] == self.username:
return Fore.MAGENTA + message[0] + ": " + message[1] + Fore.WHITE return COLORS["my_username_color"] + message[0] + ": " + message[1] + COLORS["text_color"]
return message[0] + ": " + message[1] + Fore.WHITE return message[0] + ": " + message[1] + COLORS["text_color"]
def __clear_console(self): def __clear_console(self):
# For windows clear command its cls # For windows clear command its cls
@ -64,31 +81,54 @@ class Client(RSAService):
else: else:
os.system("cls") 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): def update_info(self):
""" connecting to websocket,
wating for updates,
updating every 0.05 seconds
"""
ws = create_connection(f"{self.ws_url}/update") ws = create_connection(f"{self.ws_url}/update")
last_try = None last_try = None
while True: while True:
try: try:
time.sleep(0.05) time.sleep(0.05)
r = eval(ws.recv()) response = eval(ws.recv())
if last_try == r: if last_try == response:
continue continue
last_try = r last_try = response
self.__clear_console() self.__clear_console()
if len(last_try['status']) > 0: if len(last_try["messages"]) > 0:
for i, msg in enumerate(last_try["status"]): self.__print_chat(
actual_message = self._decrypt(msg) response = last_try
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)}")
except KeyboardInterrupt: except KeyboardInterrupt:
ws.send(self.close_response)
ws.close() ws.close()
quit() quit()
except Exception as exc: except Exception as exc:
ws.send(self.close_response)
ws.close() ws.close()
print("Something went wrong! ", exc) print("Something went wrong! ", exc)
quit() 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 import asyncio
from server.models import Message
import rsa
from cryptography.fernet import Fernet from cryptography.fernet import Fernet
from sanic.response import HTTPResponse from sanic.response import HTTPResponse
from sanic import Sanic, Request, response, Websocket 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 = Sanic("app")
app.config.OAS = False app.config.OAS = False
# Message structure is: # Message structure is:
# [username: message, ...] # [username: message, ...]
actual_messages: list[Message] = [] MESSAGES_MEMORY_DB: list[Message] = []
# Users structure is # Users structure is
# {Ip, Username: Public key} # {Ip, Username: Public key}
users: dict[str, str] = {} USERS: dict[str, str] = {}
key = Fernet.generate_key() PUBLIC_KEY = Fernet.generate_key()
@app.websocket("/talk") @app.websocket("/talk")
async def talking(request: Request, ws: Websocket) -> HTTPResponse: async def talk_ws_view(request: Request, ws: Websocket) -> HTTPResponse:
while True: while True:
data: str = await ws.recv() serialized_message: dict = await _get_bytes_and_serialize(ws)
serialized_message: dict = eval(data) await _check_ws_for_close_status(
new_message = Message( serialized_message,
message=serialized_message.get("text") 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) await asyncio.sleep(0.2)
@app.websocket("/update") @app.websocket("/update")
async def talking(request: Request, ws: Websocket) -> HTTPResponse: async def update_ws_view(request: Request, ws: Websocket) -> HTTPResponse:
while True: while True:
payload = str({ payload = await _generate_update_payload(
"status": [i.message for i in actual_messages], MESSAGES_MEMORY_DB,
"users_in_chat": list(users.keys()) USERS
}) )
await ws.send(payload.encode()) await ws.send(payload.encode())
await asyncio.sleep(0.2) await asyncio.sleep(0.2)
@app.route('/get_key', methods=['GET', 'POST']) @app.route('/get_key', methods=['GET', 'POST'])
async def get_key(request: Request) -> HTTPResponse: async def get_key_view(request: Request) -> HTTPResponse:
public_key = rsa.PublicKey.load_pkcs1(request.form.get('pubkey'))
pubkey = rsa.PublicKey.load_pkcs1(request.form.get('pubkey')) encrypted_data = rsa.encrypt(PUBLIC_KEY, public_key)
data = rsa.encrypt(key, pubkey) if request.ip not in USERS:
USERS[f"{request.ip}, {request.form.get('username')}"] = PUBLIC_KEY
if request.ip not in users: return response.raw(encrypted_data)
users[f"{request.ip}, {request.form.get('username')}"] = key
return response.raw(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())
})