fix help command without argument, change from default DEBUG level to individually configured, comments/docs/formatting, improved imports, remove REFRESH CLIENT LIST request

This commit is contained in:
Xevion
2021-01-22 14:35:54 -06:00
parent e862540e14
commit 0dcfa234ad
6 changed files with 50 additions and 28 deletions

View File

@@ -29,7 +29,6 @@ class Requests:
The Requests class provides a universal naming for the types of requests that can be made and received. 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 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. GET_MESSAGE_HISTORY = 'GET_MESSAGE_HISTORY' # Send the client a detailed list of all messages sent up to a certain point.

View File

@@ -1,4 +1,3 @@
import logging import logging
logging.basicConfig(level=logging.DEBUG, logging.basicConfig(format='[%(asctime)s] [%(levelname)s] [%(threadName)s] %(message)s')
format='[%(asctime)s] [%(levelname)s] [%(threadName)s] %(message)s')

View File

@@ -11,6 +11,7 @@ if TYPE_CHECKING:
from server.handler import Client from server.handler import Client
logger = logging.getLogger('commands') logger = logging.getLogger('commands')
logger.setLevel(logging.DEBUG)
class CommandHandler: class CommandHandler:
@@ -24,10 +25,13 @@ class CommandHandler:
self.client = client self.client = client
self.aliases = {} self.aliases = {}
self.commands = {} self.commands = {}
self.__install_command(self.reroll, 'Reroll', 'reroll', 'Change your color to a random color.', aliases=['newcolor']) self.__install_command(self.reroll, 'Reroll', 'reroll', 'Change your color to a random color.',
self.__install_command(self.help, 'Help', 'help', 'Get info on a given commands functionality and more.', aliases=['about', 'doc']) 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: if aliases is None:
aliases = [] aliases = []
@@ -75,21 +79,24 @@ class CommandHandler:
Randomly change the client's color to a different color. Randomly change the client's color to a different color.
""" """
i = 0 i = 0
newColor = self.client.color new_color = self.client.color
choices = constants.Colors.has_contrast(float(minimum_contrast)) choices = constants.Colors.has_contrast(float(minimum_contrast))
while i < 50 and newColor == self.client.color: while i < 50 and new_color == self.client.color:
newColor = random.choice(choices) new_color = random.choice(choices)
self.client.color = newColor self.client.color = new_color
contrast = round(constants.Colors.WHITE.contrast_ratio(newColor), 1) contrast = round(constants.Colors.WHITE.contrast_ratio(new_color), 1)
return f'Changed your color to {newColor.name} ({newColor.hex}/{contrast})' 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 Print information about a given command
:return: :return:
""" """
if command is None:
return f"'help' requires 1 argument (command: str)."
if command in self.aliases.keys(): if command in self.aliases.keys():
command = self.aliases[command] command = self.aliases[command]

View File

@@ -6,6 +6,8 @@ from typing import List
import constants import constants
logger = logging.getLogger('database') logger = logging.getLogger('database')
logger.setLevel(logging.DEBUG)
lock = threading.Lock() lock = threading.Lock()
@@ -20,6 +22,9 @@ class Database(object):
return self.__isClosed return self.__isClosed
def close(self) -> None: def close(self) -> None:
"""
Closes the database connection and record it's connection status.
"""
if self.__isClosed: if self.__isClosed:
logger.warning(f'Database connection is already closed.', exc_info=True) logger.warning(f'Database connection is already closed.', exc_info=True)
else: else:
@@ -30,7 +35,9 @@ class Database(object):
with lock: with lock:
cur = self.conn.cursor() cur = self.conn.cursor()
try: try:
# check if the table exists
cur.execute('''SELECT name FROM sqlite_master WHERE type='table' AND name='?';''', 'message') 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: if cur.fetchone() is None:
self.conn.execute('''CREATE TABLE message self.conn.execute('''CREATE TABLE message
(id INTEGER PRIMARY KEY, (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. :return: The unique integer primary key chosen for the message, i.e. it's ID.
""" """
with lock: with lock:
cur = self.conn.cursor() with self.conn:
try: cur = self.conn.cursor()
cur.execute('''INSERT INTO message (nickname, connection_hash, color, message, timestamp) try:
VALUES (?, ?, ?, ?, ?)''', [nickname, user_hash, color, message, timestamp]) cur.execute('''INSERT INTO message (nickname, connection_hash, color, message, timestamp)
logger.debug(f'Message {cur.lastrowid} recorded.') VALUES (?, ?, ?, ?, ?)''', [nickname, user_hash, color, message, timestamp])
return cur.lastrowid logger.debug(f'Message {cur.lastrowid} recorded.')
finally: return cur.lastrowid
cur.close() finally:
cur.close()
def get_messages(self, columns: List[str] = None): def get_messages(self, columns: List[str] = None):
with lock: with lock:

View File

@@ -13,9 +13,9 @@ import helpers
from exceptions import DataReceptionException from exceptions import DataReceptionException
from server import db from server import db
from server.commands import CommandHandler from server.commands import CommandHandler
from server.db import Database
logger = logging.getLogger('handler') logger = logging.getLogger('handler')
logger.setLevel(logging.DEBUG)
class BaseClient(object): class BaseClient(object):
@@ -23,12 +23,12 @@ class BaseClient(object):
def __init__(self, conn: socket.socket, all_clients: List['Client'], address) -> None: def __init__(self, conn: socket.socket, all_clients: List['Client'], address) -> None:
self.conn, self.all_clients, self.address = conn, all_clients, address 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): def connect_database(self):
if self.db is None: if self.db is None:
logger.debug('Connecting client to database.') logger.debug('Connecting client to database.')
self.db = Database() self.db = db.Database()
def send(self, message: bytes) -> None: def send(self, message: bytes) -> None:
"""Sends a pre-encoded message to this client.""" """Sends a pre-encoded message to this client."""
@@ -89,7 +89,7 @@ class Client(BaseClient):
def connect_database(self) -> None: def connect_database(self) -> None:
if self.db is None: if self.db is None:
logger.debug(f'Connecting Client({self.id[:8]}) to the database.') logger.debug(f'Connecting Client({self.id[:8]}) to the database.')
self.db = Database() self.db = db.Database()
def request_nickname(self) -> None: def request_nickname(self) -> None:
"""Send a request for the client's nickname information.""" """Send a request for the client's nickname information."""
@@ -149,13 +149,19 @@ class Client(BaseClient):
else: else:
logger.info(f'{self.nickname} changed their name to {nickname}') logger.info(f'{self.nickname} changed their name to {nickname}')
self.nickname = 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: def close(self) -> None:
logger.info(f'Client {self.id} closed. ({self.nickname})') logger.info(f'Client {self.id} closed. ({self.nickname})')
self.conn.close() # Close socket connection self.conn.close() # Close socket connection
self.all_clients.remove(self) # Remove the user from the global client list 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.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: def handle(self) -> None:
self.connect_database() self.connect_database()
@@ -164,8 +170,6 @@ class Client(BaseClient):
data = self.receive() data = self.receive()
if data['type'] == constants.Types.REQUEST: 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: if data['request'] == constants.Requests.GET_MESSAGE_HISTORY:
self.send_message_history( self.send_message_history(
limit=data.get('limit', 50), time_limit=data.get('time_limit', 60 * 30) limit=data.get('limit', 50), time_limit=data.get('time_limit', 60 * 30)

View File

@@ -12,6 +12,7 @@ server.bind((host, port))
server.listen() server.listen()
logger = logging.getLogger('server') logger = logging.getLogger('server')
logger.setLevel(logging.DEBUG)
clients = [] clients = []
@@ -28,6 +29,10 @@ def receive():
clients.append(client) clients.append(client)
client.request_nickname() 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 # Start Handling Thread For Client
thread = threading.Thread(target=client.handle, name=client.id[:8]) thread = threading.Thread(target=client.handle, name=client.id[:8])
thread.start() thread.start()