Reformat and remove stale debugging

This commit is contained in:
2026-02-16 19:50:17 +00:00
parent 658ab10647
commit f3ced1bfd8
10 changed files with 122 additions and 105 deletions

View File

@@ -621,6 +621,7 @@ class SignalClient(ClientBase):
async def _drain_runtime_commands(self):
"""Process queued runtime commands (e.g., web UI sends via composite router)."""
from core.clients import transport
# Process a small burst each loop to keep sends responsive.
for _ in range(5):
command = transport.pop_runtime_command(self.service)
@@ -631,6 +632,7 @@ class SignalClient(ClientBase):
async def _execute_runtime_command(self, command):
"""Execute a single runtime command like send_message_raw."""
from core.clients import transport
command_id = str((command or {}).get("id") or "").strip()
action = str((command or {}).get("action") or "").strip()
payload = dict((command or {}).get("payload") or {})
@@ -672,7 +674,9 @@ class SignalClient(ClientBase):
return
if action == "notify_xmpp_sent":
person_identifier_id = str(payload.get("person_identifier_id") or "").strip()
person_identifier_id = str(
payload.get("person_identifier_id") or ""
).strip()
text = str(payload.get("text") or "")
if not person_identifier_id:
transport.set_runtime_command_result(
@@ -683,7 +687,9 @@ class SignalClient(ClientBase):
return
try:
identifier = await sync_to_async(
lambda: PersonIdentifier.objects.filter(id=person_identifier_id).select_related("user", "person").first()
lambda: PersonIdentifier.objects.filter(id=person_identifier_id)
.select_related("user", "person")
.first()
)()
if identifier is None:
transport.set_runtime_command_result(

View File

@@ -685,7 +685,7 @@ class WhatsAppClient(ClientBase):
payload = dict((command or {}).get("payload") or {})
if not command_id:
return
self.log.info(
self.log.debug(
"whatsapp runtime command start: id=%s action=%s",
command_id,
action,
@@ -718,7 +718,7 @@ class WhatsAppClient(ClientBase):
else int(time.time() * 1000),
},
)
self.log.info(
self.log.debug(
"whatsapp runtime command ok: id=%s action=%s",
command_id,
action,
@@ -800,7 +800,9 @@ class WhatsAppClient(ClientBase):
return
if action == "notify_xmpp_sent":
person_identifier_id = str(payload.get("person_identifier_id") or "").strip()
person_identifier_id = str(
payload.get("person_identifier_id") or ""
).strip()
text = str(payload.get("text") or "")
if not person_identifier_id:
transport.set_runtime_command_result(
@@ -811,7 +813,9 @@ class WhatsAppClient(ClientBase):
return
try:
identifier = await sync_to_async(
lambda: PersonIdentifier.objects.filter(id=person_identifier_id).select_related("user", "person").first()
lambda: PersonIdentifier.objects.filter(id=person_identifier_id)
.select_related("user", "person")
.first()
)()
if identifier is None:
transport.set_runtime_command_result(
@@ -1424,13 +1428,13 @@ class WhatsAppClient(ClientBase):
# Read contact-like rows directly from the session sqlite DB instead.
contacts, source, lid_map = await self._sync_contacts_from_sqlite()
if not contacts:
self.log.info("whatsapp contacts sync empty (%s)", source or "unknown")
self.log.debug("whatsapp contacts sync empty (%s)", source or "unknown")
self._publish_state(
last_event="contacts_sync_empty",
contacts_source=source or "unknown",
)
return
self.log.info(
self.log.debug(
"whatsapp contacts synced: count=%s source=%s",
len(contacts),
source or "unknown",
@@ -2600,7 +2604,11 @@ class WhatsAppClient(ClientBase):
)
if is_transient and attempt < 1:
# If runtime rejected string target, try to build protobuf JID for retry.
if jid_obj is None and self._build_jid is not None and "@" in jid_str:
if (
jid_obj is None
and self._build_jid is not None
and "@" in jid_str
):
local_part, server_part = jid_str.split("@", 1)
try:
maybe_retry_jid = self._build_jid(local_part, server_part)
@@ -2740,10 +2748,12 @@ class WhatsAppClient(ClientBase):
async def send_message_to_contact(self, contact_jid: str, text: str) -> bool:
"""Send a text message to a WhatsApp contact."""
try:
jid = build_jid(contact_jid.split("@")[0], contact_jid.split("@")[1])
jid = self._to_jid(contact_jid)
if not jid or self._client is None:
return False
# neonize.send_message() accepts either a Message protobuf or a plain string
# If passing a string, it auto-converts to Message(conversation=text)
response = self.client.send_message(jid, text)
response = self._client.send_message(jid, text)
return response is not None
except Exception as e:
self.log.error(f"Failed to send WhatsApp message: {e}")
@@ -2753,8 +2763,10 @@ class WhatsAppClient(ClientBase):
async def send_structured_message(self, contact_jid: str, message: Message) -> bool:
"""Send a structured Message protobuf to a WhatsApp contact."""
try:
jid = build_jid(contact_jid.split("@")[0], contact_jid.split("@")[1])
response = self.client.send_message(jid, message)
jid = self._to_jid(contact_jid)
if not jid or self._client is None:
return False
response = self._client.send_message(jid, message)
return response is not None
except Exception as e:
self.log.error(f"Failed to send structured WhatsApp message: {e}")

View File

@@ -155,15 +155,15 @@ class XMPPComponent(ComponentXMPP):
try:
# Lookup user in Django
self.log.info(f"User {sender_username}")
self.log.debug("Resolving XMPP sender user=%s", sender_username)
user = User.objects.get(username=sender_username)
# Find Person object with name=person_name.lower()
self.log.info(f"Name {person_name.title()}")
self.log.debug("Resolving XMPP recipient person=%s", person_name.title())
person = Person.objects.get(user=user, name=person_name.title())
# Ensure a PersonIdentifier exists for this user, person, and service
self.log.info(f"Identifier {service}")
self.log.debug("Resolving XMPP identifier service=%s", service)
identifier = PersonIdentifier.objects.get(
user=user, person=person, service=service
)
@@ -580,13 +580,13 @@ class XMPPComponent(ComponentXMPP):
iq["roster"]["items"] = {jid: {"name": name or jid}}
iq.send()
self.log.info(f"Updated roster: Added {jid} ({name})")
self.log.debug("Updated roster: added %s (%s)", jid, name)
def on_chatstate_active(self, msg):
"""
Handle when a user is actively engaged in the chat.
"""
self.log.info(f"Chat state: Active from {msg['from']}.")
self.log.debug("Chat state active from %s", msg["from"])
self.get_identifier(msg)
@@ -594,7 +594,7 @@ class XMPPComponent(ComponentXMPP):
"""
Handle when a user is typing a message.
"""
self.log.info(f"Chat state: Composing from {msg['from']}.")
self.log.debug("Chat state composing from %s", msg["from"])
identifier = self.get_identifier(msg)
if identifier:
@@ -609,7 +609,7 @@ class XMPPComponent(ComponentXMPP):
"""
Handle when a user has paused typing.
"""
self.log.info(f"Chat state: Paused from {msg['from']}.")
self.log.debug("Chat state paused from %s", msg["from"])
identifier = self.get_identifier(msg)
if identifier:
@@ -624,7 +624,7 @@ class XMPPComponent(ComponentXMPP):
"""
Handle when a user is inactive in the chat.
"""
self.log.info(f"Chat state: Inactive from {msg['from']}.")
self.log.debug("Chat state inactive from %s", msg["from"])
self.get_identifier(msg)
@@ -632,7 +632,7 @@ class XMPPComponent(ComponentXMPP):
"""
Handle when a user has left the chat.
"""
self.log.info(f"Chat state: Gone from {msg['from']}.")
self.log.debug("Chat state gone from %s", msg["from"])
self.get_identifier(msg)
@@ -640,37 +640,37 @@ class XMPPComponent(ComponentXMPP):
"""
Handle when a user becomes available.
"""
self.log.info(f"Presence available from {pres['from']}")
self.log.debug("Presence available from %s", pres["from"])
def on_presence_dnd(self, pres):
"""
Handle when a user sets 'Do Not Disturb' status.
"""
self.log.info(f"User {pres['from']} is now in 'Do Not Disturb' mode.")
self.log.debug("Presence dnd from %s", pres["from"])
def on_presence_xa(self, pres):
"""
Handle when a user sets 'Extended Away' status.
"""
self.log.info(f"User {pres['from']} is now 'Extended Away'.")
self.log.debug("Presence extended-away from %s", pres["from"])
def on_presence_chat(self, pres):
"""
Handle when a user is actively available for chat.
"""
self.log.info(f"User {pres['from']} is now available for chat.")
self.log.debug("Presence chat-available from %s", pres["from"])
def on_presence_away(self, pres):
"""
Handle when a user sets 'Away' status.
"""
self.log.info(f"User {pres['from']} is now 'Away'.")
self.log.debug("Presence away from %s", pres["from"])
def on_presence_unavailable(self, pres):
"""
Handle when a user goes offline or unavailable.
"""
self.log.info(f"User {pres['from']} is now unavailable.")
self.log.debug("Presence unavailable from %s", pres["from"])
def on_presence_subscribe(self, pres):
"""
@@ -681,7 +681,7 @@ class XMPPComponent(ComponentXMPP):
sender_jid = str(pres["from"]).split("/")[0] # Bare JID (user@domain)
recipient_jid = str(pres["to"]).split("/")[0]
self.log.info(
self.log.debug(
f"Received subscription request from {sender_jid} to {recipient_jid}"
)
@@ -699,36 +699,36 @@ class XMPPComponent(ComponentXMPP):
service = None
# Lookup user in Django
self.log.info(f"User {user_username}")
self.log.debug("Resolving subscription user=%s", user_username)
user = User.objects.get(username=user_username)
# Find Person object with name=person_name.lower()
self.log.info(f"Name {person_name.title()}")
self.log.debug("Resolving subscription person=%s", person_name.title())
person = Person.objects.get(user=user, name=person_name.title())
# Ensure a PersonIdentifier exists for this user, person, and service
self.log.info(f"Identifier {service}")
self.log.debug("Resolving subscription identifier service=%s", service)
PersonIdentifier.objects.get(user=user, person=person, service=service)
component_jid = f"{person_name.lower()}|{service}@{self.boundjid.bare}"
# Accept the subscription
self.send_presence(ptype="subscribed", pto=sender_jid, pfrom=component_jid)
self.log.info(
self.log.debug(
f"Accepted subscription from {sender_jid}, sent from {component_jid}"
)
# Send a presence request **from the recipient to the sender** (ASKS THEM TO ACCEPT BACK)
# self.send_presence(ptype="subscribe", pto=sender_jid, pfrom=component_jid)
# self.log.info(f"Sent presence subscription request from {component_jid} to {sender_jid}")
# Add sender to roster
# self.update_roster(sender_jid, name=sender_jid.split("@")[0])
# self.log.info(f"Added {sender_jid} to roster.")
# Send presence update to sender **from the correct JID**
self.send_presence(ptype="available", pto=sender_jid, pfrom=component_jid)
self.log.info(f"Sent presence update from {component_jid} to {sender_jid}")
self.log.debug(
"Sent presence update from %s to %s", component_jid, sender_jid
)
except (User.DoesNotExist, Person.DoesNotExist, PersonIdentifier.DoesNotExist):
# If any lookup fails, reject the subscription
@@ -744,25 +744,25 @@ class XMPPComponent(ComponentXMPP):
"""
Handle successful subscription confirmations.
"""
self.log.info(f"Subscription to {pres['from']} was accepted.")
self.log.debug("Subscription to %s accepted", pres["from"])
def on_presence_unsubscribe(self, pres):
"""
Handle when a user unsubscribes from presence updates.
"""
self.log.info(f"User {pres['from']} has unsubscribed from presence updates.")
self.log.debug("Presence unsubscribe from %s", pres["from"])
def on_presence_unsubscribed(self, pres):
"""
Handle when a user's unsubscription request is confirmed.
"""
self.log.info(f"Unsubscription from {pres['from']} confirmed.")
self.log.debug("Presence unsubscribed confirmation from %s", pres["from"])
def on_roster_subscription_request(self, pres):
"""
Handle roster subscription requests.
"""
self.log.info(f"New roster subscription request from {pres['from']}.")
self.log.debug("Roster subscription request from %s", pres["from"])
async def session_start(self, *args):
self.log.info("XMPP session started")
@@ -793,8 +793,6 @@ class XMPPComponent(ComponentXMPP):
# self.log.error("No XEP-0363 upload service found.")
# return None
# self.log.info(f"Upload service: {upload_service}")
upload_service_jid = str(
getattr(settings, "XMPP_UPLOAD_SERVICE", "")
or getattr(settings, "XMPP_UPLOAD_JID", "")
@@ -851,8 +849,6 @@ class XMPPComponent(ComponentXMPP):
def sym(value):
msg.reply(f"[>] {value}").send()
# self.log.info(f"Received message: {msg}")
# Extract sender JID (full format: user@domain/resource)
sender_jid = str(msg["from"])
@@ -878,7 +874,9 @@ class XMPPComponent(ComponentXMPP):
body = msg["body"] if msg["body"] else ""
attachments = []
self.log.info(f"Full XMPP Message: {ET.tostring(msg.xml, encoding='unicode')}")
self.log.debug(
"Received XMPP stanza: %s", ET.tostring(msg.xml, encoding="unicode")
)
# Extract attachments from standard XMPP payloads.
for att in msg.xml.findall(".//{urn:xmpp:attachments}attachment"):
@@ -933,7 +931,7 @@ class XMPPComponent(ComponentXMPP):
if attachment_urls:
body = "\n".join(attachment_urls)
self.log.info(f"Extracted {len(attachments)} attachments from XMPP message.")
self.log.debug("Extracted %s attachments from XMPP message", len(attachments))
# Log extracted information with variable name annotations
log_message = (
f"Sender JID: {sender_jid}, Sender Username: {sender_username}, Sender Domain: {sender_domain}, "
@@ -941,7 +939,7 @@ class XMPPComponent(ComponentXMPP):
f"Recipient JID: {recipient_jid}, Recipient Username: {recipient_username}, Recipient Domain: {recipient_domain}, "
f"Body: {body or '[No Body]'}"
)
self.log.info(log_message)
self.log.debug(log_message)
# Ensure recipient domain matches our configured component
expected_domain = settings.XMPP_JID # 'jews.zm.is' in your config
@@ -959,14 +957,14 @@ class XMPPComponent(ComponentXMPP):
return
if recipient_jid == settings.XMPP_JID:
self.log.info("Message to JID")
self.log.debug("Handling command message sent to gateway JID")
if body.startswith("."):
# Messaging the gateway directly
if body == ".contacts":
# Lookup Person objects linked to sender
persons = Person.objects.filter(user=sender_user)
if not persons.exists():
self.log.info(f"No contacts found for {sender_username}")
self.log.debug("No contacts found for %s", sender_username)
sym("No contacts found.")
return
@@ -989,7 +987,7 @@ class XMPPComponent(ComponentXMPP):
else:
sym("No such command")
else:
self.log.info("Other message")
self.log.debug("Handling routed message to contact")
if "|" in recipient_username:
recipient_name, recipient_service = recipient_username.split("|")
@@ -1029,7 +1027,7 @@ class XMPPComponent(ComponentXMPP):
identifier=identifier,
user=identifier.user,
)
self.log.info(f"Component history store message {body}")
self.log.debug("Storing outbound XMPP message in history")
await history.store_message(
session=session,
sender="XMPP",
@@ -1037,7 +1035,7 @@ class XMPPComponent(ComponentXMPP):
ts=int(now().timestamp() * 1000),
outgoing=True,
)
self.log.info("Stored a message sent from XMPP in the history.")
self.log.debug("Stored outbound XMPP message in history")
manipulations = Manipulation.objects.filter(
group__people=identifier.person,
@@ -1045,7 +1043,7 @@ class XMPPComponent(ComponentXMPP):
mode="mutate",
enabled=True,
)
self.log.info(f"MANIP11 {manipulations}")
self.log.debug("Found %s active manipulations", manipulations.count())
if not manipulations:
await self.ur.stopped_typing(
"xmpp",
@@ -1056,7 +1054,7 @@ class XMPPComponent(ComponentXMPP):
body,
attachments,
)
self.log.info("Message sent unaltered")
self.log.debug("Message sent unaltered")
return
manip = manipulations.first()
@@ -1068,9 +1066,9 @@ class XMPPComponent(ComponentXMPP):
manip,
chat_history,
)
self.log.info("Running XMPP context prompt")
self.log.debug("Running XMPP context prompt")
result = await ai.run_prompt(prompt, manip.ai)
self.log.info(f"RESULT {result}")
self.log.debug("Generated mutated response for XMPP message")
await history.store_own_message(
session=session,
text=result,
@@ -1085,7 +1083,7 @@ class XMPPComponent(ComponentXMPP):
result,
attachments,
)
self.log.info("Message sent with modifications")
self.log.debug("Message sent with modifications")
async def request_upload_slots(self, recipient_jid, attachments):
"""Requests upload slots for multiple attachments concurrently."""
@@ -1120,8 +1118,8 @@ class XMPPComponent(ComponentXMPP):
f"Upload failed: {response.status} {await response.text()}"
)
return None
self.log.info(
f"Successfully uploaded {att['filename']} to {upload_url}"
self.log.debug(
"Successfully uploaded %s to %s", att["filename"], upload_url
)
# Send XMPP message immediately after successful upload
@@ -1148,7 +1146,7 @@ class XMPPComponent(ComponentXMPP):
url_element.text = attachment_url
msg.xml.append(oob_element)
self.log.info(f"Sending XMPP message: {msg.xml}")
self.log.debug("Sending XMPP message: %s", msg.xml)
msg.send()
async def send_chat_state(self, recipient_jid, sender_jid, started):
@@ -1158,7 +1156,7 @@ class XMPPComponent(ComponentXMPP):
msg.xml.append(
ET.Element(f"{{http://jabber.org/protocol/chatstates}}{state_tag}")
)
self.log.info(
self.log.debug(
"Sending XMPP chat-state %s: %s -> %s",
state_tag,
sender_jid,
@@ -1193,7 +1191,7 @@ class XMPPComponent(ComponentXMPP):
# Step 2: Request upload slots concurrently
valid_uploads = await self.request_upload_slots(recipient_jid, attachments)
self.log.info("Got upload slots")
self.log.debug("Got upload slots")
if not valid_uploads:
self.log.warning("No valid upload slots obtained.")
return []

View File

@@ -145,7 +145,7 @@ async def get_chat_session(user, identifier):
async def store_message(session, sender, text, ts, outgoing=False):
log.info(f"STORE MESSAGE {text}")
log.debug("Storing message for session=%s outgoing=%s", session.id, outgoing)
msg = await sync_to_async(Message.objects.create)(
user=session.user,
session=session,
@@ -160,7 +160,7 @@ async def store_message(session, sender, text, ts, outgoing=False):
async def store_own_message(session, text, ts, manip=None, queue=False):
log.info(f"STORE OWN MESSAGE {text}")
log.debug("Storing own message for session=%s queue=%s", session.id, queue)
cast = {
"user": session.user,
"session": session,

View File

@@ -71,7 +71,7 @@ class UnifiedRouter(object):
self._typing_stop_tasks[key] = self.loop.create_task(_timer())
def _start(self):
print("UR _start")
self.log.info("Starting unified router clients")
self.xmpp.start()
self.signal.start()
self.whatsapp.start()
@@ -81,7 +81,7 @@ class UnifiedRouter(object):
try:
# self.xmpp.client.client.process()
# self.xmpp.start()
print("IN RUN BEFORE START")
self.log.debug("Router run loop initializing")
self._start()
self.loop.run_forever()
except (KeyboardInterrupt, SystemExit):

View File

@@ -71,9 +71,7 @@ def _load_since(user_id, service, identifier, person_id, after_ts, limit):
qs = base_queryset.order_by("ts")
seed_previous = None
if after_ts > 0:
seed_previous = (
base_queryset.filter(ts__lte=after_ts).order_by("-ts").first()
)
seed_previous = base_queryset.filter(ts__lte=after_ts).order_by("-ts").first()
# Use a small rolling window to capture late/out-of-order timestamps.
# Frontend dedupes by message id, so repeated rows are ignored.
window_start = max(0, int(after_ts) - 5 * 60 * 1000)
@@ -84,8 +82,7 @@ def _load_since(user_id, service, identifier, person_id, after_ts, limit):
"session",
"session__identifier",
"session__identifier__person",
)
.order_by("-ts")[: max(10, min(limit, 200))]
).order_by("-ts")[: max(10, min(limit, 200))]
)
rows_desc.reverse()
rows = rows_desc

View File

@@ -1,9 +1,15 @@
# Other library imports
import logging
import os
log = logging.getLogger("util")
debug = True
debug = str(os.getenv("GIA_DEBUG_LOGS", "0")).strip().lower() in {
"1",
"true",
"yes",
"on",
}
# Color definitions
BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = range(8)
@@ -49,20 +55,26 @@ def get_logger(name):
color_formatter = ColoredFormatter(COLOR_FORMAT)
# formatter = logging.Formatter(
# Why is this so complicated?
ch = logging.StreamHandler()
ch.setLevel(logging.INFO)
# ch.setFormatter(formatter)
ch.setFormatter(color_formatter)
# Define the logger on the base class
log = logging.getLogger(name)
log.setLevel(logging.INFO)
if debug:
log.setLevel(logging.DEBUG)
ch.setLevel(logging.DEBUG)
# Add the handler and stop it being silly and printing everything twice
log.addHandler(ch)
# Add exactly one stream handler per logger to avoid duplicate lines.
existing_handler = None
for handler in log.handlers:
if getattr(handler, "_gia_logger_handler", False):
existing_handler = handler
break
if existing_handler is None:
ch = logging.StreamHandler()
ch._gia_logger_handler = True
ch.setFormatter(color_formatter)
log.addHandler(ch)
existing_handler = ch
existing_handler.setLevel(logging.DEBUG if debug else logging.INFO)
log.propagate = False
return log

View File

@@ -2758,7 +2758,9 @@ class ComposeCommandResult(LoginRequiredMixin, View):
request,
"partials/compose-send-status.html",
{
"notice_message": str(timeout_result.get("error") or "Send failed."),
"notice_message": str(
timeout_result.get("error") or "Send failed."
),
"notice_level": "danger",
},
)
@@ -3323,12 +3325,12 @@ class ComposeSend(LoginRequiredMixin, View):
log_prefix = (
f"[ComposeSend] service={base['service']} identifier={base['identifier']}"
)
logger.info(f"{log_prefix} text_len={len(text)} attempting send")
logger.debug(f"{log_prefix} text_len={len(text)} attempting send")
# If runtime is out-of-process, enqueue command and return immediately (non-blocking).
# Expose command id for cancellation so the client can cancel or poll later.
runtime_client = transport.get_runtime_client(base["service"]) or None
logger.info(
logger.debug(
f"{log_prefix} runtime_client={type(runtime_client).__name__ if runtime_client else 'None (queued)'}"
)
ts = None
@@ -3338,7 +3340,9 @@ class ComposeSend(LoginRequiredMixin, View):
runtime_state = transport.get_runtime_state("whatsapp")
last_seen = int(runtime_state.get("runtime_seen_at") or 0)
is_connected = bool(runtime_state.get("connected"))
pair_status = str(runtime_state.get("pair_status") or "").strip().lower()
pair_status = (
str(runtime_state.get("pair_status") or "").strip().lower()
)
now_s = int(time.time())
# Runtime may process sends even when `connected` lags false briefly;
# heartbeat freshness is the reliable signal for queue availability.
@@ -3358,15 +3362,12 @@ class ComposeSend(LoginRequiredMixin, View):
level="warning",
panel_id=panel_id,
)
logger.info(f"{log_prefix} enqueuing runtime command (out-of-process)")
command_id = transport.enqueue_runtime_command(
base["service"],
"send_message_raw",
{"recipient": base["identifier"], "text": text, "attachments": []},
)
logger.info(
f"{log_prefix} command_id={command_id} enqueued, returning immediately"
)
logger.debug(f"{log_prefix} command_id={command_id} enqueued")
# attach command id to request so _response can include it in HX-Trigger
request._compose_command_id = command_id
# Do NOT wait here — return immediately so the UI doesn't block.
@@ -3374,14 +3375,12 @@ class ComposeSend(LoginRequiredMixin, View):
ts = int(time.time() * 1000)
else:
# In-process runtime can perform the send synchronously and return a timestamp.
logger.info(f"{log_prefix} calling in-process send_message_raw (blocking)")
ts = async_to_sync(transport.send_message_raw)(
base["service"],
base["identifier"],
text=text,
attachments=[],
)
logger.info(f"{log_prefix} in-process send returned ts={ts}")
# For queued sends we set `ts` to a local timestamp; for in-process sends ts may be False.
if not ts:
return self._response(
@@ -3397,13 +3396,12 @@ class ComposeSend(LoginRequiredMixin, View):
user=request.user,
identifier=base["person_identifier"],
)
logger.info(f"{log_prefix} session_id={session.id}")
# For in-process sends (Signal, etc), ts is a timestamp or True.
# For queued sends (WhatsApp/UR), ts is a local timestamp.
# Set delivered_ts only if we got a real timestamp OR if it's an in-process sync send.
msg_ts = int(ts) if str(ts).isdigit() else int(time.time() * 1000)
delivered_ts = msg_ts if runtime_client is not None else None
msg = Message.objects.create(
Message.objects.create(
user=request.user,
session=session,
sender_uuid="",
@@ -3412,9 +3410,6 @@ class ComposeSend(LoginRequiredMixin, View):
delivered_ts=delivered_ts,
custom_author="USER",
)
logger.info(
f"{log_prefix} created message id={msg.id} ts={msg_ts} delivered_ts={delivered_ts} custom_author=USER"
)
# Notify XMPP clients from runtime so cross-platform sends appear there too.
if base["service"] in {"signal", "whatsapp"}:
try:

View File

@@ -695,21 +695,21 @@ class OSINTSearch(LoginRequiredMixin, View):
def _field_options(self, model_cls: type[models.Model]) -> list[dict[str, str]]:
options = []
for field in model_cls._meta.get_fields():
for model_field in model_cls._meta.get_fields():
# Skip reverse/accessor relations (e.g. ManyToManyRel) that are not
# directly searchable as user-facing fields in this selector.
if field.auto_created and not field.concrete:
if model_field.auto_created and not model_field.concrete:
continue
if field.name == "user":
if model_field.name == "user":
continue
label = getattr(
field,
model_field,
"verbose_name",
str(field.name).replace("_", " "),
str(model_field.name).replace("_", " "),
)
options.append(
{
"value": field.name,
"value": model_field.name,
"label": str(label).title(),
}
)
@@ -892,7 +892,6 @@ class OSINTSearch(LoginRequiredMixin, View):
object_list: list[Any],
request_type: str,
) -> list[dict[str, Any]]:
context_type = _context_type(request_type)
rows = []
for item in object_list:
row = {"id": str(item.pk), "cells": [], "actions": []}