mirror of
https://github.com/Xevion/tcp-chat.git
synced 2025-12-07 13:16:47 -06:00
message history exchange implementation, include database message IDs with all messages sent
This commit is contained in:
@@ -16,11 +16,11 @@ class Types:
|
|||||||
A message could be a request, a posting of information, a message etc.
|
A message could be a request, a posting of information, a message etc.
|
||||||
The Types class provides a universal naming for the types of messages that will be exchanged.
|
The Types class provides a universal naming for the types of messages that will be exchanged.
|
||||||
"""
|
"""
|
||||||
SERVER_MESSAGE = 'SERVER_MESSAGE'
|
|
||||||
REQUEST = 'REQUEST'
|
REQUEST = 'REQUEST'
|
||||||
|
MESSAGE = 'MESSAGE'
|
||||||
NICKNAME = 'NICKNAME'
|
NICKNAME = 'NICKNAME'
|
||||||
USER_LIST = 'USER_LIST'
|
USER_LIST = 'USER_LIST'
|
||||||
MESSAGE = 'MESSAGE'
|
MESSAGE_HISTORY = 'MESSAGE_HISTORY'
|
||||||
|
|
||||||
|
|
||||||
class Requests:
|
class Requests:
|
||||||
@@ -30,7 +30,7 @@ class Requests:
|
|||||||
"""
|
"""
|
||||||
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
|
REFRESH_CONNECTIONS_LIST = 'REFRESH_CLIENT_LIST' # Send the client all connections to the server and their information
|
||||||
GET_HISTORY = 'GET_HISTORY' # Send a short history of the chat to the client
|
GET_MESSAGE_HISTORY = 'GET_MESSAGE_HISTORY' # Send the client a detailed list of all messages sent up to a certain point.
|
||||||
|
|
||||||
|
|
||||||
class Color():
|
class Color():
|
||||||
|
|||||||
24
helpers.py
24
helpers.py
@@ -1,5 +1,6 @@
|
|||||||
import json
|
import json
|
||||||
import time
|
import time
|
||||||
|
from typing import List, Tuple
|
||||||
|
|
||||||
import constants
|
import constants
|
||||||
|
|
||||||
@@ -22,14 +23,33 @@ def prepare_json(object) -> bytes:
|
|||||||
return prepare(json.dumps(object))
|
return prepare(json.dumps(object))
|
||||||
|
|
||||||
|
|
||||||
def prepare_message(nickname: str, message: str, color: str, msgtime: int = None):
|
def prepare_message(nickname: str, message: str, color: str, message_id: int, timestamp: int = None) -> bytes:
|
||||||
return prepare_json(
|
return prepare_json(
|
||||||
{
|
{
|
||||||
'type': constants.Types.MESSAGE,
|
'type': constants.Types.MESSAGE,
|
||||||
'nickname': nickname,
|
'nickname': nickname,
|
||||||
'content': message,
|
'content': message,
|
||||||
'color': color,
|
'color': color,
|
||||||
'time': msgtime or int(time.time())
|
'time': timestamp or int(time.time()),
|
||||||
|
'id': message_id,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def prepare_message_history(messages: List[Tuple[int, str, str, str, int]]) -> bytes:
|
||||||
|
return prepare_json(
|
||||||
|
{
|
||||||
|
'type': constants.Types.MESSAGE_HISTORY,
|
||||||
|
'messages': [
|
||||||
|
{
|
||||||
|
'type': constants.Types.MESSAGE,
|
||||||
|
'nickname': nickname,
|
||||||
|
'content': message,
|
||||||
|
'color': color,
|
||||||
|
'time': timestamp,
|
||||||
|
'id': message_id
|
||||||
|
} for message_id, nickname, color, message, timestamp in messages
|
||||||
|
]
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -29,14 +29,16 @@ class BaseClient(object):
|
|||||||
"""Sends a string message as the server to this client."""
|
"""Sends a string message as the server to this client."""
|
||||||
# db.add_message('Server', 'server', constants.Colors.BLACK.hex, message, int(time.time()))
|
# db.add_message('Server', 'server', constants.Colors.BLACK.hex, message, int(time.time()))
|
||||||
self.conn.send(helpers.prepare_message(
|
self.conn.send(helpers.prepare_message(
|
||||||
nickname='Server', message=message, color=constants.Colors.BLACK.hex
|
nickname='Server', message=message, color=constants.Colors.BLACK.hex, message_id=-1
|
||||||
))
|
))
|
||||||
|
|
||||||
def broadcast_message(self, message: str) -> None:
|
def broadcast_message(self, message: str) -> None:
|
||||||
"""Sends a string message to all connected clients as the Server."""
|
"""Sends a string message to all connected clients as the Server."""
|
||||||
db.add_message('Server', 'server', constants.Colors.BLACK.hex, message, int(time.time()))
|
timestamp = int(time.time())
|
||||||
|
message_id = db.add_message('Server', 'server', constants.Colors.BLACK.hex, message, timestamp)
|
||||||
prepared = helpers.prepare_message(
|
prepared = helpers.prepare_message(
|
||||||
nickname='Server', message=message, color=constants.Colors.BLACK.hex
|
nickname='Server', message=message, color=constants.Colors.BLACK.hex, message_id=message_id,
|
||||||
|
timestamp=timestamp
|
||||||
)
|
)
|
||||||
for client in self.all_clients:
|
for client in self.all_clients:
|
||||||
client.send(prepared)
|
client.send(prepared)
|
||||||
@@ -82,6 +84,25 @@ class Client(BaseClient):
|
|||||||
}
|
}
|
||||||
))
|
))
|
||||||
|
|
||||||
|
def send_message_history(self, limit: int, time_limit: int) -> None:
|
||||||
|
limit = min(100, max(0, limit))
|
||||||
|
time_limit = min(60 * 30, max(0, time_limit))
|
||||||
|
min_time = int(time.time()) - time_limit
|
||||||
|
|
||||||
|
cur = db.conn.cursor()
|
||||||
|
try:
|
||||||
|
cur.execute('''SELECT id, nickname, color, message, timestamp
|
||||||
|
FROM message
|
||||||
|
WHERE timestamp >= ?
|
||||||
|
ORDER BY timestamp
|
||||||
|
LIMIT ?''',
|
||||||
|
[min_time, limit])
|
||||||
|
|
||||||
|
messages = cur.fetchall()
|
||||||
|
self.send(helpers.prepare_message_history(messages))
|
||||||
|
finally:
|
||||||
|
cur.close()
|
||||||
|
|
||||||
def receive(self) -> Any:
|
def receive(self) -> Any:
|
||||||
length = int(self.conn.recv(constants.HEADER_LENGTH).decode('utf-8'))
|
length = int(self.conn.recv(constants.HEADER_LENGTH).decode('utf-8'))
|
||||||
logger.debug(f'Header received - Length {length}')
|
logger.debug(f'Header received - Length {length}')
|
||||||
@@ -106,18 +127,25 @@ class Client(BaseClient):
|
|||||||
if data['type'] == constants.Types.REQUEST:
|
if data['type'] == constants.Types.REQUEST:
|
||||||
if data['request'] == constants.Requests.REFRESH_CONNECTIONS_LIST:
|
if data['request'] == constants.Requests.REFRESH_CONNECTIONS_LIST:
|
||||||
self.send_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)
|
||||||
|
)
|
||||||
|
|
||||||
elif data['type'] == constants.Types.NICKNAME:
|
elif data['type'] == constants.Types.NICKNAME:
|
||||||
self.handle_nickname(data['nickname'])
|
self.handle_nickname(data['nickname'])
|
||||||
elif data['type'] == constants.Types.MESSAGE:
|
elif data['type'] == constants.Types.MESSAGE:
|
||||||
|
# Record the message in the DB.
|
||||||
|
message_id = db.add_message(self.nickname, self.id, self.color.hex, data['content'],
|
||||||
|
int(time.time()))
|
||||||
|
|
||||||
self.broadcast(helpers.prepare_message(
|
self.broadcast(helpers.prepare_message(
|
||||||
nickname=self.nickname,
|
nickname=self.nickname,
|
||||||
message=data['content'],
|
message=data['content'],
|
||||||
color=self.color.hex
|
color=self.color.hex,
|
||||||
|
message_id=message_id
|
||||||
))
|
))
|
||||||
|
|
||||||
# Record the message in the DB.
|
|
||||||
db.add_message(self.nickname, self.id, self.color.hex, data['content'], int(time.time()))
|
|
||||||
|
|
||||||
# Process commands
|
# Process commands
|
||||||
command = data['content'].strip()
|
command = data['content'].strip()
|
||||||
if command.startswith('/'):
|
if command.startswith('/'):
|
||||||
|
|||||||
Reference in New Issue
Block a user