Message sending and receiving appears provisionally working. Job system next... :D

This commit is contained in:
Michael Woods
2025-01-11 16:46:10 -05:00
parent ad446ecf0c
commit f95ef946f0
4 changed files with 82 additions and 22 deletions

View File

@@ -165,7 +165,7 @@ class Message:
def payload(self): def payload(self):
if 'd' in self.data: if 'd' in self.data:
pl = self.data['d'] pl = self.data['d']
if type(pl) in (dict, str, bytes): if type(pl) in (dict, str, bytes, list):
return pl return pl
else: else:
return str(pl) return str(pl)
@@ -173,11 +173,15 @@ class Message:
return "" return ""
@payload.setter @payload.setter
def payload(self, payload: Union[str, bytes, dict]): def payload(self, payload: Union[str, bytes, dict, list]):
if type(payload) in (str, bytes, dict): logging.debug(f"Setting a message payload: {type(payload)}: {payload}")
if type(payload) in (str, bytes, dict, list):
logging.debug(f"Payload type is {type(payload)}, conversion to string unnecessary")
self.data['d'] = payload self.data['d'] = payload
else: else:
logging.debug("payload type is not in (str, bytes, dict, list); converting to string")
self.data['d'] = str(payload) self.data['d'] = str(payload)
logging.debug(f"Final payload is: {type(payload)}: {payload}")
@classmethod @classmethod
def partial_unpack(cls, msg: dict) -> Self: def partial_unpack(cls, msg: dict) -> Self:

View File

@@ -36,6 +36,7 @@ class Server:
self.zeo_addr = None self.zeo_addr = None
self.zeo_stop = None self.zeo_stop = None
self.zeo = zeo self.zeo = zeo
self.started = False
if data_dir: if data_dir:
data_path = Path(data_dir) data_path = Path(data_dir)
else: else:
@@ -170,6 +171,13 @@ class Server:
def register_path_handler(self, path_root: str, fn: Callable): def register_path_handler(self, path_root: str, fn: Callable):
self.handlers[path_root.strip().lower()] = fn self.handlers[path_root.strip().lower()] = fn
def server_worker(self):
"""When called, do things. Should get called every so often."""
if not self.started:
return
# Add things to do here:
pass
def start_db(self): def start_db(self):
if not self.zeo: if not self.zeo:
self.storage = ZODB.FileStorage.FileStorage(self.data_file) self.storage = ZODB.FileStorage.FileStorage(self.data_file)
@@ -192,6 +200,11 @@ class Server:
self.start_db() self.start_db()
self.app.start(self.pe_server, self.pe_port) self.app.start(self.pe_server, self.pe_port)
self.app.register_callsigns(self.callsign) self.app.register_callsigns(self.callsign)
self.started = True
while self.started:
self.server_worker()
time.sleep(5)
def exit_gracefully(self, signum, frame): def exit_gracefully(self, signum, frame):
self.stop() self.stop()

View File

@@ -120,7 +120,7 @@ class MessageAlreadySentError(Exception):
pass pass
class Message(persistent.Persistent): class Message(persistent.Persistent):
def __init__(self, text: str, msg_to: Optional[Iterable[str],str]= None, msg_from: Optional[str] = None, def __init__(self, text: str, msg_to: Optional[Iterable[str]]= None, msg_from: Optional[str] = None,
attachments: Optional[Iterable[Attachment]] = None): attachments: Optional[Iterable[Attachment]] = None):
self.retrieved = False self.retrieved = False
self.sent_at = datetime.datetime.now(datetime.UTC) self.sent_at = datetime.datetime.now(datetime.UTC)
@@ -166,7 +166,7 @@ class Message(persistent.Persistent):
"attachments": attachments, "attachments": attachments,
"to": self.msg_to, "to": self.msg_to,
"from": self.msg_from, "from": self.msg_from,
"id": self.msg_id, "id": str(self.msg_id),
"sent_at": self.sent_at.isoformat(), "sent_at": self.sent_at.isoformat(),
"text": "" "text": ""
} }
@@ -175,6 +175,10 @@ class Message(persistent.Persistent):
return d return d
@classmethod
def from_dict(cls, data: dict) -> Self:
return Message(data['text'],msg_to=data.get('to'), attachments=data.get("attachments"))
def send(self, db: ZODB.DB) -> tuple: def send(self, db: ZODB.DB) -> tuple:
if self.msg_delivered: if self.msg_delivered:
raise MessageAlreadySentError("Cannot send a private message that has already been sent.") raise MessageAlreadySentError("Cannot send a private message that has already been sent.")
@@ -221,6 +225,7 @@ DisplayOptions = namedtuple('DisplayOptions', ['get_text', 'limit', 'sort_by', '
'get_attachments', 'sent_received_all']) 'get_attachments', 'sent_received_all'])
def parse_display_options(req: Request) -> DisplayOptions: def parse_display_options(req: Request) -> DisplayOptions:
logging.debug(f"Parsing request vars for message get: {req.vars}")
sent_received_all = "received" sent_received_all = "received"
d = req.vars.get("source") d = req.vars.get("source")
if type(d) is str: if type(d) is str:
@@ -234,7 +239,7 @@ def parse_display_options(req: Request) -> DisplayOptions:
try: try:
limit = int(limit) limit = int(limit)
except: except:
limit = 10 limit = None
d = req.vars.get('fetch_text') d = req.vars.get('fetch_text')
if type(d) is str: if type(d) is str:
@@ -245,12 +250,15 @@ def parse_display_options(req: Request) -> DisplayOptions:
get_text = True get_text = True
d = req.vars.get('fetch_attachments') d = req.vars.get('fetch_attachments')
logging.debug(f"Parsing fetch_attachment var: {d}")
if type(d) is str: if type(d) is str:
d.lower().strip() d.lower().strip()
if d in yes_values: if d in yes_values:
logging.debug("fetch_attachment is yes")
get_attachments = True get_attachments = True
else: else:
get_attachments = False get_attachments = False
logging.debug("fetch_attachment is no")
r = req.vars.get('reverse') r = req.vars.get('reverse')
if type(r) is str: if type(r) is str:
@@ -274,9 +282,9 @@ def parse_display_options(req: Request) -> DisplayOptions:
if type(s) is str: if type(s) is str:
s = s.lower() s = s.lower()
if s: if s:
search = str(s) search = str(s).lower()
return DisplayOptions(get_text, limit, sort_by, reverse, search, get_attachments, sent_receive_all) return DisplayOptions(get_text, limit, sort_by, reverse, search, get_attachments, sent_received_all)
def handle_message_get(req: Request, conn: PacketServerConnection, db: ZODB.DB): def handle_message_get(req: Request, conn: PacketServerConnection, db: ZODB.DB):
@@ -285,21 +293,52 @@ def handle_message_get(req: Request, conn: PacketServerConnection, db: ZODB.DB):
msg_return = [] msg_return = []
with db.transaction() as db: with db.transaction() as db:
mailbox_create(username, db.root()) mailbox_create(username, db.root())
mb = db.root.messages['username'] mb = db.root.messages[username]
messages = [] if opts.search:
if opts.reverse: messages = [msg for msg in mb if (opts.search in msg.text.lower()) or (opts.search in msg.msg_to[0].lower())
for i in range(1,len(mb)+1): or (opts.search in msg.msg_from.lower())]
messages.append(mb[len(mb) - 1])
else: else:
for i in range(0,len(mb)): messages = [msg for msg in mb]
messages.append(mb[i])
for msg in messages:
# do other filtering.
if opts.sort_by == "from":
messages.sort(key=lambda x: x.msg_from, reverse=opts.reverse)
elif opts.sort_by == "to":
messages.sort(key=lambda x: x.msg_to, reverse=opts.reverse)
else:
messages.sort(key=lambda x: x.sent_at, reverse=opts.reverse)
for i in range(0, len(messages)):
if opts.limit and (len(msg_return) >= opts.limit):
break
msg = messages[i]
msg.retrieved = True
msg_return.append(msg.to_dict(get_text=opts.get_text, get_attachments=opts.get_attachments))
def object_root_handler(req: Request, conn: PacketServerConnection, db: ZODB.DB): response = Response.blank()
response.status_code = 200
response.payload = msg_return
send_response(conn, response, req)
def handle_message_post(req: Request, conn: PacketServerConnection, db: ZODB.DB):
username = ax25.Address(conn.remote_callsign).call.upper().strip()
try:
msg = Message.from_dict(req.payload)
except:
send_blank_response(conn, req, status_code=400)
logging.warning(f"User '{username}' attempted to post message with invalid payload: {req.payload}")
return
msg.msg_from = username
try:
send_counter, failed = msg.send(db)
except:
send_blank_response(conn, req, status_code=500)
logging.error(f"Error while attempting to send message:\n{format_exc()}")
return
send_blank_response(conn, req, status_code=201, payload={"successes": send_counter, "failed": failed})
def message_root_handler(req: Request, conn: PacketServerConnection, db: ZODB.DB):
logging.debug(f"{req} being processed by user_root_handler") logging.debug(f"{req} being processed by user_root_handler")
if not user_authorized(conn, db): if not user_authorized(conn, db):
logging.debug(f"user {conn.remote_callsign} not authorized") logging.debug(f"user {conn.remote_callsign} not authorized")
@@ -308,6 +347,8 @@ def object_root_handler(req: Request, conn: PacketServerConnection, db: ZODB.DB)
logging.debug("user is authorized") logging.debug("user is authorized")
if req.method is Request.Method.GET: if req.method is Request.Method.GET:
handle_message_get(req, conn, db) handle_message_get(req, conn, db)
if req.method is Request.Method.POST:
handle_message_post(req, conn, db)
else: else:
send_blank_response(conn, req, status_code=404) send_blank_response(conn, req, status_code=404)

View File

@@ -5,6 +5,7 @@ from packetserver.common import Message, Request, Response, PacketServerConnecti
from .bulletin import bulletin_root_handler from .bulletin import bulletin_root_handler
from .users import user_root_handler, user_authorized from .users import user_root_handler, user_authorized
from .objects import object_root_handler from .objects import object_root_handler
from .messages import message_root_handler
import logging import logging
from typing import Union from typing import Union
import ZODB import ZODB
@@ -50,7 +51,8 @@ standard_handlers = {
"": root_root_handler, "": root_root_handler,
"bulletin": bulletin_root_handler, "bulletin": bulletin_root_handler,
"user": user_root_handler, "user": user_root_handler,
"object": object_root_handler "object": object_root_handler,
"message": message_root_handler
} }