Another update to send.py to reflect recent changes.

This commit is contained in:
Michael Woods
2025-12-21 21:42:01 -05:00
parent ba2e42db2d
commit 2e3032265c

View File

@@ -5,28 +5,36 @@ from typing import List
from persistent.list import PersistentList from persistent.list import PersistentList
from persistent.mapping import PersistentMapping from persistent.mapping import PersistentMapping
from datetime import datetime from datetime import datetime
import uuid import transaction
from packetserver.http.dependencies import get_current_http_user from packetserver.http.dependencies import get_current_http_user
from packetserver.http.auth import HttpUser from packetserver.http.auth import HttpUser
from packetserver.server.messages import Message # core Message class from packetserver.server.messages import Message
from packetserver.common.util import is_valid_ax25_callsign
router = APIRouter(prefix="/api/v1", tags=["messages"]) router = APIRouter(prefix="/api/v1", tags=["messages"])
class SendMessageRequest(BaseModel): class SendMessageRequest(BaseModel):
to: List[str] = Field(..., description="List of recipient callsigns (uppercase) or ['ALL'] for bulletin") to: List[str] = Field(..., description="List of recipient callsigns or ['ALL'] for bulletin")
subject: str = Field("", description="Optional subject line")
text: str = Field(..., min_length=1, description="Message body text") text: str = Field(..., min_length=1, description="Message body text")
@validator("to") @validator("to")
def validate_to(cls, v): def validate_to(cls, v):
if not v: if not v:
raise ValueError("At least one recipient required") raise ValueError("At least one recipient required")
# Allow 'ALL' only as single bulletin
if len(v) > 1 and "ALL" in [x.upper() for x in v]: validated = []
for call in v:
call_upper = call.upper().strip()
if not is_valid_ax25_callsign(call_upper):
raise ValueError(f"Invalid AX.25 callsign: {call}")
validated.append(call_upper)
if len(validated) > 1 and "ALL" in validated:
raise ValueError("'ALL' can only be used alone for bulletins") raise ValueError("'ALL' can only be used alone for bulletins")
return [x.upper() for x in v]
return validated
@router.post("/messages") @router.post("/messages")
@@ -36,6 +44,8 @@ async def send_message(
): ):
from packetserver.runners.http_server import get_db_connection from packetserver.runners.http_server import get_db_connection
conn = get_db_connection() conn = get_db_connection()
root = conn.root()
if not current_user.is_rf_enabled(conn): if not current_user.is_rf_enabled(conn):
raise HTTPException( raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN, status_code=status.HTTP_403_FORBIDDEN,
@@ -44,35 +54,35 @@ async def send_message(
username = current_user.username username = current_user.username
from packetserver.runners.http_server import get_db_connection # Prepare recipients
conn = get_db_connection() to_list = payload.to
root = conn.root() to_tuple = tuple(to_list)
if "ALL" in to_list:
# Prepare recipients tuple
to_tuple = tuple(payload.to)
if "ALL" in payload.to:
to_tuple = ("ALL",) to_tuple = ("ALL",)
# Create new Message is_bulletin = "ALL" in to_list
recipients = to_list if not is_bulletin else list(root.get('users', {}).keys())
# Create message using only supported core params
new_msg = Message( new_msg = Message(
text=payload.text,
msg_from=username, msg_from=username,
msg_to=to_tuple, msg_to=to_tuple,
text=payload.text, attachments=()
attachments=() # empty for now
) )
# Deliver to all recipients (including sender for sent folder) # Deliver to recipients + always sender (sent folder)
recipients = payload.to if "ALL" not in payload.to else list(root.get('users', {}).keys()) messages_root = root.setdefault('messages', PersistentMapping())
delivered_to = set()
for recip in set(recipients + [username]): # always copy to sender for recip in set(recipients) | {username}:
if 'messages' not in root: mailbox = messages_root.setdefault(recip, PersistentList())
root['messages'] = PersistentMapping()
mailbox = root['messages'].setdefault(recip, persistent.list.PersistentList())
mailbox.append(new_msg) mailbox.append(new_msg)
mailbox._p_changed = True
delivered_to.add(recip)
# Persist messages_root._p_changed = True
conn.root()["messages"]._p_changed = True transaction.commit()
# Note: transaction.commit() not needed here—FastAPI/ZODB handles on response
return { return {
"status": "sent", "status": "sent",
@@ -80,5 +90,5 @@ async def send_message(
"from": username, "from": username,
"to": list(to_tuple), "to": list(to_tuple),
"sent_at": new_msg.sent_at.isoformat() + "Z", "sent_at": new_msg.sent_at.isoformat() + "Z",
"subject": payload.subject "recipients_delivered": len(delivered_to)
} }