diff --git a/core/clients/signal.py b/core/clients/signal.py index 22730e7..752c686 100644 --- a/core/clients/signal.py +++ b/core/clients/signal.py @@ -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( diff --git a/core/clients/whatsapp.py b/core/clients/whatsapp.py index 78ecb3b..5045f2a 100644 --- a/core/clients/whatsapp.py +++ b/core/clients/whatsapp.py @@ -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}") diff --git a/core/clients/xmpp.py b/core/clients/xmpp.py index 26aa089..597443e 100644 --- a/core/clients/xmpp.py +++ b/core/clients/xmpp.py @@ -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 [] diff --git a/core/messaging/history.py b/core/messaging/history.py index 186a936..8f4c7f3 100644 --- a/core/messaging/history.py +++ b/core/messaging/history.py @@ -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, diff --git a/core/modules/router.py b/core/modules/router.py index b81cd42..dbe088b 100644 --- a/core/modules/router.py +++ b/core/modules/router.py @@ -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): diff --git a/core/realtime/compose_ws.py b/core/realtime/compose_ws.py index 9d47fec..440174f 100644 --- a/core/realtime/compose_ws.py +++ b/core/realtime/compose_ws.py @@ -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 diff --git a/core/util/logs.py b/core/util/logs.py index 26cc230..6b89f75 100644 --- a/core/util/logs.py +++ b/core/util/logs.py @@ -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 diff --git a/core/views/compose.py b/core/views/compose.py index 55c9d35..60cb3d6 100644 --- a/core/views/compose.py +++ b/core/views/compose.py @@ -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: diff --git a/core/views/osint.py b/core/views/osint.py index 809e80d..839e6d1 100644 --- a/core/views/osint.py +++ b/core/views/osint.py @@ -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": []} diff --git a/docker/watch_and_restart.py b/docker/watch_and_restart.py index abdd340..b4296f8 100644 --- a/docker/watch_and_restart.py +++ b/docker/watch_and_restart.py @@ -25,9 +25,7 @@ The watcher ignores `__pycache__`, `.pyc` files and `.git` paths. """ import os import subprocess -import sys import time -from pathlib import Path from watchdog.events import FileSystemEventHandler from watchdog.observers import Observer