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

View File

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

View File

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

View File

@@ -145,7 +145,7 @@ async def get_chat_session(user, identifier):
async def store_message(session, sender, text, ts, outgoing=False): 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)( msg = await sync_to_async(Message.objects.create)(
user=session.user, user=session.user,
session=session, 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): 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 = { cast = {
"user": session.user, "user": session.user,
"session": session, "session": session,

View File

@@ -71,7 +71,7 @@ class UnifiedRouter(object):
self._typing_stop_tasks[key] = self.loop.create_task(_timer()) self._typing_stop_tasks[key] = self.loop.create_task(_timer())
def _start(self): def _start(self):
print("UR _start") self.log.info("Starting unified router clients")
self.xmpp.start() self.xmpp.start()
self.signal.start() self.signal.start()
self.whatsapp.start() self.whatsapp.start()
@@ -81,7 +81,7 @@ class UnifiedRouter(object):
try: try:
# self.xmpp.client.client.process() # self.xmpp.client.client.process()
# self.xmpp.start() # self.xmpp.start()
print("IN RUN BEFORE START") self.log.debug("Router run loop initializing")
self._start() self._start()
self.loop.run_forever() self.loop.run_forever()
except (KeyboardInterrupt, SystemExit): 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") qs = base_queryset.order_by("ts")
seed_previous = None seed_previous = None
if after_ts > 0: if after_ts > 0:
seed_previous = ( seed_previous = base_queryset.filter(ts__lte=after_ts).order_by("-ts").first()
base_queryset.filter(ts__lte=after_ts).order_by("-ts").first()
)
# Use a small rolling window to capture late/out-of-order timestamps. # Use a small rolling window to capture late/out-of-order timestamps.
# Frontend dedupes by message id, so repeated rows are ignored. # Frontend dedupes by message id, so repeated rows are ignored.
window_start = max(0, int(after_ts) - 5 * 60 * 1000) 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",
"session__identifier", "session__identifier",
"session__identifier__person", "session__identifier__person",
) ).order_by("-ts")[: max(10, min(limit, 200))]
.order_by("-ts")[: max(10, min(limit, 200))]
) )
rows_desc.reverse() rows_desc.reverse()
rows = rows_desc rows = rows_desc

View File

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

View File

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

View File

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

View File

@@ -25,9 +25,7 @@ The watcher ignores `__pycache__`, `.pyc` files and `.git` paths.
""" """
import os import os
import subprocess import subprocess
import sys
import time import time
from pathlib import Path
from watchdog.events import FileSystemEventHandler from watchdog.events import FileSystemEventHandler
from watchdog.observers import Observer from watchdog.observers import Observer