diff --git a/constants.py b/constants.py index fd2ab3b..3ec19ec 100644 --- a/constants.py +++ b/constants.py @@ -29,7 +29,6 @@ class Requests: The Requests class provides a universal naming for the types of requests that can be made and received. """ REQUEST_NICK = 'REQUEST_NICK' # Send the server the client's nickname - REFRESH_CONNECTIONS_LIST = 'REFRESH_CLIENT_LIST' # Send the client all connections to the server and their information GET_MESSAGE_HISTORY = 'GET_MESSAGE_HISTORY' # Send the client a detailed list of all messages sent up to a certain point. diff --git a/server/__init__.py b/server/__init__.py index 2d8cee0..0fa6163 100644 --- a/server/__init__.py +++ b/server/__init__.py @@ -1,4 +1,3 @@ import logging -logging.basicConfig(level=logging.DEBUG, - format='[%(asctime)s] [%(levelname)s] [%(threadName)s] %(message)s') +logging.basicConfig(format='[%(asctime)s] [%(levelname)s] [%(threadName)s] %(message)s') diff --git a/server/commands.py b/server/commands.py index b669b2d..955349e 100644 --- a/server/commands.py +++ b/server/commands.py @@ -11,6 +11,7 @@ if TYPE_CHECKING: from server.handler import Client logger = logging.getLogger('commands') +logger.setLevel(logging.DEBUG) class CommandHandler: @@ -24,10 +25,13 @@ class CommandHandler: self.client = client self.aliases = {} self.commands = {} - self.__install_command(self.reroll, 'Reroll', 'reroll', 'Change your color to a random color.', aliases=['newcolor']) - self.__install_command(self.help, 'Help', 'help', 'Get info on a given commands functionality and more.', aliases=['about', 'doc']) + self.__install_command(self.reroll, 'Reroll', 'reroll', 'Change your color to a random color.', + aliases=['newcolor']) + self.__install_command(self.help, 'Help', 'help', 'Get info on a given commands functionality and more.', + aliases=['about', 'doc']) - def __install_command(self, func: Callable, name: str = None, command_name: str = None, description: str = '', aliases: List[str] = None): + def __install_command(self, func: Callable, name: str = None, command_name: str = None, description: str = '', + aliases: List[str] = None): if aliases is None: aliases = [] @@ -75,21 +79,24 @@ class CommandHandler: Randomly change the client's color to a different color. """ i = 0 - newColor = self.client.color + new_color = self.client.color choices = constants.Colors.has_contrast(float(minimum_contrast)) - while i < 50 and newColor == self.client.color: - newColor = random.choice(choices) + while i < 50 and new_color == self.client.color: + new_color = random.choice(choices) - self.client.color = newColor - contrast = round(constants.Colors.WHITE.contrast_ratio(newColor), 1) - return f'Changed your color to {newColor.name} ({newColor.hex}/{contrast})' + self.client.color = new_color + contrast = round(constants.Colors.WHITE.contrast_ratio(new_color), 1) + return f'Changed your color to {new_color.name} ({new_color.hex}/{contrast})' - def help(self, command: str) -> Optional[str]: + def help(self, command: str = None) -> Optional[str]: """ Print information about a given command :return: """ + if command is None: + return f"'help' requires 1 argument (command: str)." + if command in self.aliases.keys(): command = self.aliases[command] diff --git a/server/db.py b/server/db.py index 6bbf865..5b4a6cd 100644 --- a/server/db.py +++ b/server/db.py @@ -6,6 +6,8 @@ from typing import List import constants logger = logging.getLogger('database') +logger.setLevel(logging.DEBUG) + lock = threading.Lock() @@ -20,6 +22,9 @@ class Database(object): return self.__isClosed def close(self) -> None: + """ + Closes the database connection and record it's connection status. + """ if self.__isClosed: logger.warning(f'Database connection is already closed.', exc_info=True) else: @@ -30,7 +35,9 @@ class Database(object): with lock: cur = self.conn.cursor() try: + # check if the table exists cur.execute('''SELECT name FROM sqlite_master WHERE type='table' AND name='?';''', 'message') + # if it doesn't exist, create the table and report it if cur.fetchone() is None: self.conn.execute('''CREATE TABLE message (id INTEGER PRIMARY KEY, @@ -55,14 +62,15 @@ class Database(object): :return: The unique integer primary key chosen for the message, i.e. it's ID. """ with lock: - cur = self.conn.cursor() - try: - cur.execute('''INSERT INTO message (nickname, connection_hash, color, message, timestamp) - VALUES (?, ?, ?, ?, ?)''', [nickname, user_hash, color, message, timestamp]) - logger.debug(f'Message {cur.lastrowid} recorded.') - return cur.lastrowid - finally: - cur.close() + with self.conn: + cur = self.conn.cursor() + try: + cur.execute('''INSERT INTO message (nickname, connection_hash, color, message, timestamp) + VALUES (?, ?, ?, ?, ?)''', [nickname, user_hash, color, message, timestamp]) + logger.debug(f'Message {cur.lastrowid} recorded.') + return cur.lastrowid + finally: + cur.close() def get_messages(self, columns: List[str] = None): with lock: diff --git a/server/handler.py b/server/handler.py index 69a21fa..94ec882 100644 --- a/server/handler.py +++ b/server/handler.py @@ -13,9 +13,9 @@ import helpers from exceptions import DataReceptionException from server import db from server.commands import CommandHandler -from server.db import Database logger = logging.getLogger('handler') +logger.setLevel(logging.DEBUG) class BaseClient(object): @@ -23,12 +23,12 @@ class BaseClient(object): def __init__(self, conn: socket.socket, all_clients: List['Client'], address) -> None: self.conn, self.all_clients, self.address = conn, all_clients, address - self.db: Optional[Database] = None + self.db: Optional[db.Database] = None def connect_database(self): if self.db is None: logger.debug('Connecting client to database.') - self.db = Database() + self.db = db.Database() def send(self, message: bytes) -> None: """Sends a pre-encoded message to this client.""" @@ -89,7 +89,7 @@ class Client(BaseClient): def connect_database(self) -> None: if self.db is None: logger.debug(f'Connecting Client({self.id[:8]}) to the database.') - self.db = Database() + self.db = db.Database() def request_nickname(self) -> None: """Send a request for the client's nickname information.""" @@ -149,13 +149,19 @@ class Client(BaseClient): else: logger.info(f'{self.nickname} changed their name to {nickname}') self.nickname = nickname + # New nickname has to be sent to all clients + for client in self.all_clients: + client.send_connections_list() def close(self) -> None: logger.info(f'Client {self.id} closed. ({self.nickname})') self.conn.close() # Close socket connection self.all_clients.remove(self) # Remove the user from the global client list self.broadcast_message(f'{self.nickname} left!') # Now we can broadcast it's exit message - self.db.conn.close() # Close database connection + # Inform all clients of the disconnected client + for client in self.all_clients: + client.send_connections_list() + self.db.close() # Close database connection def handle(self) -> None: self.connect_database() @@ -164,8 +170,6 @@ class Client(BaseClient): data = self.receive() if data['type'] == constants.Types.REQUEST: - if data['request'] == constants.Requests.REFRESH_CONNECTIONS_LIST: - self.send_connections_list() if data['request'] == constants.Requests.GET_MESSAGE_HISTORY: self.send_message_history( limit=data.get('limit', 50), time_limit=data.get('time_limit', 60 * 30) diff --git a/server/main.py b/server/main.py index f637270..ebaa1e2 100644 --- a/server/main.py +++ b/server/main.py @@ -12,6 +12,7 @@ server.bind((host, port)) server.listen() logger = logging.getLogger('server') +logger.setLevel(logging.DEBUG) clients = [] @@ -28,6 +29,10 @@ def receive(): clients.append(client) client.request_nickname() + # Inform all clients of new client, give new client connections list + for client in clients: + client.send_connections_list() + # Start Handling Thread For Client thread = threading.Thread(target=client.handle, name=client.id[:8]) thread.start()