Skip to content
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
ether/__pycache__/
crypto/__pycache__/
Empty file added client/__init__.py
Empty file.
56 changes: 56 additions & 0 deletions client/client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
from abc import ABC, abstractmethod

from models.identity import Identity


class Client(ABC):
"""
Abstract class to define the basics of a Dioxane Client
"""

# Client information, doesn't interfer with Eth3r
name: str
version: str = "0.1"

ether_version: int = 0x0001 # Which version of Eth3r it implements
identity: Identity # Key used by the client

def __init__(self, identity: Identity, name: str = "DIOXANE"):
"""
Instantiate a client
:param identity: Key and KeyId of the client
:param name: Name of the client
"""
self.identity = identity
self.name = name

@abstractmethod
def receive_pck(self, pck: bytearray):
"""
Process packet recieve from the server
:param pck: Packet
"""
pass

@abstractmethod
def send_pck(self, pck: bytearray):
"""
Send Packet to server
:param pck: bytes to send
"""
pass

def _exit(self, code: int = 0):
"""
Close the client
:param code: error code
"""
exit(code)

@abstractmethod
def command(self, text: str):
"""
Execute a command
:param text: Command
"""
pass
19 changes: 19 additions & 0 deletions client/display_client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from abc import ABC

from client.client import Client
from display.pad_curses import PadCurses
from display.simple_curses import SimpleCurses
from models.identity import Identity


class DisplayClient(Client, ABC):
display: SimpleCurses

def __init__(self, identity: Identity, name: str = "DIOXANE"):
super().__init__(identity, name)

self.display = PadCurses(self)

def _exit(self, code: int = 0):
self.display.exit()
super()._exit(code)
19 changes: 19 additions & 0 deletions client/io_client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from abc import ABC
from threading import Thread

from client.display_client import DisplayClient
from client.simple_client.input import Input
from models.identity import Identity


class IoClient(DisplayClient, ABC):
input: Input
intputThread: Thread

def __init__(self, identity: Identity, name: str = "DIOXANE"):
super().__init__(identity, name)

# Start the input thread
self.input = Input(self)
self.intputThread = Thread(target=self.input.start_listening)
self.intputThread.start()
Empty file.
51 changes: 51 additions & 0 deletions client/simple_client/identity_manager.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
from exceptions.exceptions import UnknownRecipient
from misc.hex_enumerator import bytes_to_str
from models.identity import Identity


class IdentityManager:

contactsByName: dict[str, str] # Map contacts name to their key_id
contactsByKey: dict[str, Identity] # Map key_id to Identity

def __init__(self):
self.contactsByName = {}
self.contactsByKey = {}

def getIdentity(self, identity: str | bytearray) -> Identity:
"""
Fetch the contact list for an Identity
:param identity: Name or key_id of the identity
:return: Identity
"""
key_id: str = ""

if type(identity) is str:
# Trying by name
for name in self.contactsByName.keys():
if identity in name:
return self.contactsByKey[self.contactsByName[name]]

if identity[0:2] == "0x":
key_id = identity[2:]

if type(identity) is bytearray:
key_id = bytes_to_str(identity)[2:]

# Trying by key_id
try:
if key_id in self.contactsByKey:
return self.contactsByKey[key_id]
except ValueError:
pass

raise UnknownRecipient(identity)

def registerIdentity(self, identity: Identity):
"""
Insert an Identity in the contact list
:param identity: Identity to register
"""
key_id: str = bytes_to_str(identity.key_id)[2:]
self.contactsByKey[key_id] = identity
self.contactsByName[identity.name] = key_id
75 changes: 75 additions & 0 deletions client/simple_client/input.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import curses

from client.display_client import DisplayClient


class Input:
client: DisplayClient
listening: bool
prompt: str
prompt_cursor: int

def __init__(self, client):
self.valid_char = list('/. ')
self.client = client
self._reset_prompt()

def _reset_prompt(self):
self.prompt = ""
self.prompt_cursor = 0
self.prompt_updated()

def start_listening(self):
self.listening = True
self.prompt = ""
while self.listening:
char: int = self.client.display.input_windows.getch()

if char == -1:
pass
else:
if char == 0xa: # Enter key
try:
self.client.command(self.prompt)
except Exception as e:
self.client.display.display_log(str(e))
finally:
self._reset_prompt()

elif char == curses.KEY_LEFT:
self.prompt_cursor -= 1
if self.prompt_cursor < 0:
self.prompt_cursor = 0

elif char == curses.KEY_RIGHT:
self.prompt_cursor += 1
if self.prompt_cursor > len(self.prompt):
self.prompt_cursor = len(self.prompt)

elif char == curses.KEY_UP:
self.client.display.scroll(-1)

elif char == curses.KEY_DOWN:
self.client.display.scroll(1)

elif char == curses.KEY_SR:
self.client.display.scroll(-1)

elif char == curses.KEY_SF:
self.client.display.scroll(1)

elif char == 8: # Del key
self.prompt = self.prompt[:self.prompt_cursor-1] + self.prompt[self.prompt_cursor:]
self.prompt_cursor -= 1

elif char == curses.KEY_DC:
self.prompt = self.prompt[:self.prompt_cursor] + self.prompt[self.prompt_cursor+1:]

elif chr(char).isalnum() or chr(char) in self.valid_char:
self.prompt = self.prompt[:self.prompt_cursor] + chr(char) + self.prompt[self.prompt_cursor:]
self.prompt_cursor += 1

self.prompt_updated()

def prompt_updated(self):
self.client.display.update_prompt(self.prompt, self.prompt_cursor)
43 changes: 43 additions & 0 deletions client/simple_client/packet_generator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
from client.client import Client
from models.identity import Identity
from models.packet import Packet, PacketCode
from models.room_message import RoomMessage


class PacketGenerator:
client: Client

def __init__(self, client: Client):
self.client = client

def get_hey_packet(self) -> Packet:
"""
Build a Hey packet to connect the client to the server
:return: Hey Packet
"""
return Packet(PacketCode.HEY, [self.client.ether_version.to_bytes(2, 'big')])

def get_send_key_packet(self) -> Packet:
"""
Build a Key Packet
:return: Key Packet
"""
return Packet(PacketCode.SEND_KEY, self.client.identity.get_key_pair())

# noinspection PyMethodMayBeStatic
def get_send_message_packet(self, message: RoomMessage) -> Packet:
"""
Build a Message Packet
:param message: Message to send
:return: Message Send Packet
"""
return Packet(PacketCode.MESSAGE_SEND, message.room.room_id.get_key_id_pair().append(message.payload))

# noinspection PyMethodMayBeStatic
def get_knock_packet(self, recipient: Identity) -> Packet:
"""
Build a Kncok Packet
:param recipient: Client to knock
:return: Knock Packet
"""
return Packet(PacketCode.KNOCK_SEND, recipient.get_key_id_pair())
Loading