Fully implement WhatsApp, Signal and XMPP multiplexing
This commit is contained in:
@@ -6,9 +6,13 @@ from urllib.parse import parse_qs
|
||||
from asgiref.sync import sync_to_async
|
||||
from django.core import signing
|
||||
|
||||
from core.models import ChatSession, Message, PersonIdentifier, WorkspaceConversation
|
||||
from core.models import Message, Person, PersonIdentifier, WorkspaceConversation
|
||||
from core.realtime.typing_state import get_person_typing_state
|
||||
from core.views.compose import COMPOSE_WS_TOKEN_SALT, _serialize_messages_with_artifacts
|
||||
from core.views.compose import (
|
||||
COMPOSE_WS_TOKEN_SALT,
|
||||
ComposeHistorySync,
|
||||
_serialize_messages_with_artifacts,
|
||||
)
|
||||
|
||||
|
||||
def _safe_int(value, default=0):
|
||||
@@ -19,77 +23,108 @@ def _safe_int(value, default=0):
|
||||
|
||||
|
||||
def _load_since(user_id, service, identifier, person_id, after_ts, limit):
|
||||
person = None
|
||||
person_identifier = None
|
||||
if person_id:
|
||||
resolved_person_id = _safe_int(person_id)
|
||||
|
||||
if resolved_person_id > 0:
|
||||
person = Person.objects.filter(id=resolved_person_id, user_id=user_id).first()
|
||||
|
||||
if person is not None:
|
||||
person_identifier = (
|
||||
PersonIdentifier.objects.filter(
|
||||
user_id=user_id,
|
||||
person_id=person_id,
|
||||
person_id=person.id,
|
||||
service=service,
|
||||
).first()
|
||||
or PersonIdentifier.objects.filter(
|
||||
user_id=user_id,
|
||||
person_id=person_id,
|
||||
person_id=person.id,
|
||||
).first()
|
||||
)
|
||||
if person_identifier is None and identifier:
|
||||
elif identifier:
|
||||
person_identifier = PersonIdentifier.objects.filter(
|
||||
user_id=user_id,
|
||||
service=service,
|
||||
identifier=identifier,
|
||||
).first()
|
||||
if person_identifier is None:
|
||||
return {"messages": [], "last_ts": after_ts, "person_id": 0}
|
||||
|
||||
session = ChatSession.objects.filter(
|
||||
user_id=user_id,
|
||||
identifier=person_identifier,
|
||||
).first()
|
||||
if session is None:
|
||||
session_ids = ComposeHistorySync._session_ids_for_scope(
|
||||
user=user_id,
|
||||
person=person,
|
||||
service=service,
|
||||
person_identifier=person_identifier,
|
||||
explicit_identifier=identifier,
|
||||
)
|
||||
|
||||
if not session_ids:
|
||||
return {
|
||||
"messages": [],
|
||||
"last_ts": after_ts,
|
||||
"person_id": int(person_identifier.person_id),
|
||||
"last_ts": int(after_ts or 0),
|
||||
"person_id": int(person.id) if person is not None else 0,
|
||||
}
|
||||
|
||||
qs = Message.objects.filter(user_id=user_id, session=session).order_by("ts")
|
||||
base_queryset = Message.objects.filter(
|
||||
user_id=user_id,
|
||||
session_id__in=session_ids,
|
||||
)
|
||||
qs = base_queryset.order_by("ts")
|
||||
seed_previous = None
|
||||
if after_ts > 0:
|
||||
seed_previous = (
|
||||
Message.objects.filter(
|
||||
user_id=user_id,
|
||||
session=session,
|
||||
ts__lte=after_ts,
|
||||
)
|
||||
.order_by("-ts")
|
||||
.first()
|
||||
base_queryset.filter(ts__lte=after_ts).order_by("-ts").first()
|
||||
)
|
||||
qs = qs.filter(ts__gt=after_ts)
|
||||
# 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)
|
||||
qs = qs.filter(ts__gte=window_start)
|
||||
|
||||
rows = list(qs[: max(10, min(limit, 200))])
|
||||
rows_desc = list(
|
||||
qs.select_related(
|
||||
"session",
|
||||
"session__identifier",
|
||||
"session__identifier__person",
|
||||
)
|
||||
.order_by("-ts")[: max(10, min(limit, 200))]
|
||||
)
|
||||
rows_desc.reverse()
|
||||
rows = rows_desc
|
||||
newest = (
|
||||
Message.objects.filter(user_id=user_id, session=session)
|
||||
Message.objects.filter(
|
||||
user_id=user_id,
|
||||
session_id__in=session_ids,
|
||||
)
|
||||
.order_by("-ts")
|
||||
.values_list("ts", flat=True)
|
||||
.first()
|
||||
)
|
||||
|
||||
conversation = (
|
||||
WorkspaceConversation.objects.filter(
|
||||
user_id=user_id,
|
||||
participants__id=person_identifier.person_id,
|
||||
)
|
||||
.order_by("-last_event_ts", "-created_at")
|
||||
.first()
|
||||
effective_person_id = (
|
||||
int(person.id)
|
||||
if person is not None
|
||||
else (int(person_identifier.person_id) if person_identifier is not None else 0)
|
||||
)
|
||||
counterpart_identifiers = {
|
||||
str(value or "").strip()
|
||||
for value in PersonIdentifier.objects.filter(
|
||||
user_id=user_id,
|
||||
person_id=person_identifier.person_id,
|
||||
).values_list("identifier", flat=True)
|
||||
if str(value or "").strip()
|
||||
}
|
||||
|
||||
conversation = None
|
||||
counterpart_identifiers = set()
|
||||
if effective_person_id > 0:
|
||||
conversation = (
|
||||
WorkspaceConversation.objects.filter(
|
||||
user_id=user_id,
|
||||
participants__id=effective_person_id,
|
||||
)
|
||||
.order_by("-last_event_ts", "-created_at")
|
||||
.first()
|
||||
)
|
||||
counterpart_identifiers = {
|
||||
str(value or "").strip()
|
||||
for value in PersonIdentifier.objects.filter(
|
||||
user_id=user_id,
|
||||
person_id=effective_person_id,
|
||||
).values_list("identifier", flat=True)
|
||||
if str(value or "").strip()
|
||||
}
|
||||
|
||||
return {
|
||||
"messages": _serialize_messages_with_artifacts(
|
||||
rows,
|
||||
@@ -98,7 +133,7 @@ def _load_since(user_id, service, identifier, person_id, after_ts, limit):
|
||||
seed_previous=seed_previous,
|
||||
),
|
||||
"last_ts": int(newest or after_ts or 0),
|
||||
"person_id": int(person_identifier.person_id),
|
||||
"person_id": int(effective_person_id),
|
||||
}
|
||||
|
||||
|
||||
@@ -133,6 +168,7 @@ async def compose_ws_application(scope, receive, send):
|
||||
last_ts = 0
|
||||
limit = 100
|
||||
last_typing_key = ""
|
||||
sent_message_ids = set()
|
||||
|
||||
while True:
|
||||
event = None
|
||||
@@ -159,7 +195,15 @@ async def compose_ws_application(scope, receive, send):
|
||||
after_ts=last_ts,
|
||||
limit=limit,
|
||||
)
|
||||
messages = payload.get("messages") or []
|
||||
raw_messages = payload.get("messages") or []
|
||||
messages = []
|
||||
for msg in raw_messages:
|
||||
message_id = str((msg or {}).get("id") or "").strip()
|
||||
if message_id and message_id in sent_message_ids:
|
||||
continue
|
||||
if message_id:
|
||||
sent_message_ids.add(message_id)
|
||||
messages.append(msg)
|
||||
latest = _safe_int(payload.get("last_ts"), last_ts)
|
||||
if resolved_person_id <= 0:
|
||||
resolved_person_id = _safe_int(payload.get("person_id"), 0)
|
||||
|
||||
Reference in New Issue
Block a user