Lightweight containerized prosody tooling + moved auth scripts + xmpp reconnect/auth stabilization
This commit is contained in:
@@ -2,7 +2,9 @@ from asgiref.sync import sync_to_async
|
||||
from django.conf import settings
|
||||
import uuid
|
||||
|
||||
from core.events.ledger import append_event
|
||||
from core.messaging.utils import messages_to_string
|
||||
from core.observability.tracing import ensure_trace_id
|
||||
from core.models import ChatSession, Message, QueuedMessage
|
||||
from core.util import logs
|
||||
|
||||
@@ -158,6 +160,8 @@ async def store_message(
|
||||
reply_source_service="",
|
||||
reply_source_message_id="",
|
||||
message_meta=None,
|
||||
trace_id="",
|
||||
raw_payload=None,
|
||||
):
|
||||
log.debug("Storing message for session=%s outgoing=%s", session.id, outgoing)
|
||||
msg = await sync_to_async(Message.objects.create)(
|
||||
@@ -176,6 +180,29 @@ async def store_message(
|
||||
reply_source_message_id=str(reply_source_message_id or "").strip() or None,
|
||||
message_meta=dict(message_meta or {}),
|
||||
)
|
||||
try:
|
||||
await append_event(
|
||||
user=session.user,
|
||||
session=session,
|
||||
ts=int(ts),
|
||||
event_type="message_created",
|
||||
direction="out" if bool(outgoing) else "in",
|
||||
actor_identifier=str(sender or ""),
|
||||
origin_transport=str(source_service or ""),
|
||||
origin_message_id=str(source_message_id or ""),
|
||||
origin_chat_id=str(source_chat_id or ""),
|
||||
payload={
|
||||
"message_id": str(msg.id),
|
||||
"text": str(text or ""),
|
||||
"reply_source_service": str(reply_source_service or ""),
|
||||
"reply_source_message_id": str(reply_source_message_id or ""),
|
||||
"outgoing": bool(outgoing),
|
||||
},
|
||||
raw_payload=dict(raw_payload or {}),
|
||||
trace_id=ensure_trace_id(trace_id, message_meta or {}),
|
||||
)
|
||||
except Exception as exc:
|
||||
log.warning("Event ledger append failed for message=%s: %s", msg.id, exc)
|
||||
|
||||
return msg
|
||||
|
||||
@@ -193,6 +220,8 @@ async def store_own_message(
|
||||
reply_source_service="",
|
||||
reply_source_message_id="",
|
||||
message_meta=None,
|
||||
trace_id="",
|
||||
raw_payload=None,
|
||||
):
|
||||
log.debug("Storing own message for session=%s queue=%s", session.id, queue)
|
||||
cast = {
|
||||
@@ -219,6 +248,30 @@ async def store_own_message(
|
||||
msg = await sync_to_async(msg_object.objects.create)(
|
||||
**cast,
|
||||
)
|
||||
if msg_object is Message:
|
||||
try:
|
||||
await append_event(
|
||||
user=session.user,
|
||||
session=session,
|
||||
ts=int(ts),
|
||||
event_type="message_created",
|
||||
direction="out",
|
||||
actor_identifier="BOT",
|
||||
origin_transport=str(source_service or ""),
|
||||
origin_message_id=str(source_message_id or ""),
|
||||
origin_chat_id=str(source_chat_id or ""),
|
||||
payload={
|
||||
"message_id": str(msg.id),
|
||||
"text": str(text or ""),
|
||||
"queued": bool(queue),
|
||||
"reply_source_service": str(reply_source_service or ""),
|
||||
"reply_source_message_id": str(reply_source_message_id or ""),
|
||||
},
|
||||
raw_payload=dict(raw_payload or {}),
|
||||
trace_id=ensure_trace_id(trace_id, message_meta or {}),
|
||||
)
|
||||
except Exception as exc:
|
||||
log.warning("Event ledger append failed for own message=%s: %s", msg.id, exc)
|
||||
|
||||
return msg
|
||||
|
||||
@@ -235,6 +288,7 @@ async def apply_read_receipts(
|
||||
source_service="signal",
|
||||
read_by_identifier="",
|
||||
payload=None,
|
||||
trace_id="",
|
||||
):
|
||||
"""
|
||||
Persist delivery/read metadata for one identifier's messages.
|
||||
@@ -283,6 +337,33 @@ async def apply_read_receipts(
|
||||
if dirty:
|
||||
await sync_to_async(message.save)(update_fields=dirty)
|
||||
updated += 1
|
||||
try:
|
||||
await append_event(
|
||||
user=user,
|
||||
session=message.session,
|
||||
ts=int(read_at or message.ts or 0),
|
||||
event_type="read_receipt",
|
||||
direction="system",
|
||||
actor_identifier=str(read_by_identifier or ""),
|
||||
origin_transport=str(source_service or ""),
|
||||
origin_message_id=str(message.source_message_id or message.id),
|
||||
origin_chat_id=str(message.source_chat_id or ""),
|
||||
payload={
|
||||
"message_id": str(message.id),
|
||||
"message_ts": int(message.ts or 0),
|
||||
"read_ts": int(read_at or 0),
|
||||
"read_by_identifier": str(read_by_identifier or ""),
|
||||
"timestamps": [int(v) for v in ts_values],
|
||||
},
|
||||
raw_payload=dict(payload or {}),
|
||||
trace_id=ensure_trace_id(trace_id, payload or {}),
|
||||
)
|
||||
except Exception as exc:
|
||||
log.warning(
|
||||
"Event ledger append failed for read receipt message=%s: %s",
|
||||
message.id,
|
||||
exc,
|
||||
)
|
||||
return updated
|
||||
|
||||
|
||||
@@ -297,6 +378,8 @@ async def apply_reaction(
|
||||
actor="",
|
||||
remove=False,
|
||||
payload=None,
|
||||
trace_id="",
|
||||
target_author="",
|
||||
):
|
||||
log.debug(
|
||||
"reaction-bridge history-apply start user=%s person_identifier=%s target_message_id=%s target_ts=%s source=%s actor=%s remove=%s emoji=%s",
|
||||
@@ -315,6 +398,8 @@ async def apply_reaction(
|
||||
).select_related("session")
|
||||
|
||||
target = None
|
||||
match_strategy = "none"
|
||||
target_author_value = str(target_author or "").strip()
|
||||
target_uuid = str(target_message_id or "").strip()
|
||||
if target_uuid:
|
||||
is_uuid = True
|
||||
@@ -326,12 +411,16 @@ async def apply_reaction(
|
||||
target = await sync_to_async(
|
||||
lambda: queryset.filter(id=target_uuid).order_by("-ts").first()
|
||||
)()
|
||||
if target is not None:
|
||||
match_strategy = "local_message_id"
|
||||
if target is None:
|
||||
target = await sync_to_async(
|
||||
lambda: queryset.filter(source_message_id=target_uuid)
|
||||
.order_by("-ts")
|
||||
.first()
|
||||
)()
|
||||
if target is not None:
|
||||
match_strategy = "source_message_id"
|
||||
|
||||
if target is None:
|
||||
try:
|
||||
@@ -339,11 +428,64 @@ async def apply_reaction(
|
||||
except Exception:
|
||||
ts_value = 0
|
||||
if ts_value > 0:
|
||||
# Signal reactions target source timestamp; prefer deterministic exact matches.
|
||||
exact_candidates = await sync_to_async(list)(
|
||||
queryset.filter(source_message_id=str(ts_value)).order_by("-ts")[:20]
|
||||
)
|
||||
if target_author_value and exact_candidates:
|
||||
filtered = [
|
||||
row
|
||||
for row in exact_candidates
|
||||
if str(row.sender_uuid or "").strip() == target_author_value
|
||||
]
|
||||
if filtered:
|
||||
exact_candidates = filtered
|
||||
if exact_candidates:
|
||||
target = exact_candidates[0]
|
||||
match_strategy = "exact_source_message_id_ts"
|
||||
log.debug(
|
||||
"reaction-bridge history-apply exact-source-ts target_ts=%s picked_message_id=%s candidates=%s",
|
||||
ts_value,
|
||||
str(target.id),
|
||||
len(exact_candidates),
|
||||
)
|
||||
|
||||
if target is None and ts_value > 0:
|
||||
strict_ts_rows = await sync_to_async(list)(
|
||||
queryset.filter(ts=ts_value).order_by("-id")[:20]
|
||||
)
|
||||
if target_author_value and strict_ts_rows:
|
||||
filtered = [
|
||||
row
|
||||
for row in strict_ts_rows
|
||||
if str(row.sender_uuid or "").strip() == target_author_value
|
||||
]
|
||||
if filtered:
|
||||
strict_ts_rows = filtered
|
||||
if strict_ts_rows:
|
||||
target = strict_ts_rows[0]
|
||||
match_strategy = "strict_ts_match"
|
||||
log.debug(
|
||||
"reaction-bridge history-apply strict-ts target_ts=%s picked_message_id=%s candidates=%s",
|
||||
ts_value,
|
||||
str(target.id),
|
||||
len(strict_ts_rows),
|
||||
)
|
||||
|
||||
if target is None and ts_value > 0:
|
||||
lower = ts_value - 10_000
|
||||
upper = ts_value + 10_000
|
||||
window_rows = await sync_to_async(list)(
|
||||
queryset.filter(ts__gte=lower, ts__lte=upper).order_by("ts")[:200]
|
||||
)
|
||||
if target_author_value and window_rows:
|
||||
author_rows = [
|
||||
row
|
||||
for row in window_rows
|
||||
if str(row.sender_uuid or "").strip() == target_author_value
|
||||
]
|
||||
if author_rows:
|
||||
window_rows = author_rows
|
||||
if window_rows:
|
||||
target = min(
|
||||
window_rows,
|
||||
@@ -359,6 +501,7 @@ async def apply_reaction(
|
||||
int(target.ts or 0),
|
||||
len(window_rows),
|
||||
)
|
||||
match_strategy = "nearest_ts_window"
|
||||
|
||||
if target is None:
|
||||
log.warning(
|
||||
@@ -371,10 +514,13 @@ async def apply_reaction(
|
||||
return None
|
||||
|
||||
reactions = list((target.receipt_payload or {}).get("reactions") or [])
|
||||
normalized_source = str(source_service or "").strip().lower()
|
||||
normalized_actor = str(actor or "").strip()
|
||||
normalized_emoji = str(emoji or "").strip()
|
||||
reaction_key = (
|
||||
str(source_service or "").strip().lower(),
|
||||
str(actor or "").strip(),
|
||||
str(emoji or "").strip(),
|
||||
normalized_source,
|
||||
normalized_actor,
|
||||
normalized_emoji,
|
||||
)
|
||||
|
||||
merged = []
|
||||
@@ -386,31 +532,94 @@ async def apply_reaction(
|
||||
str(row.get("actor") or "").strip(),
|
||||
str(row.get("emoji") or "").strip(),
|
||||
)
|
||||
if not row_key[2] and bool(row.get("removed")):
|
||||
# Keep malformed remove rows out of active reaction set.
|
||||
continue
|
||||
if row_key == reaction_key:
|
||||
row["removed"] = bool(remove)
|
||||
row["updated_at"] = int(target_ts or target.ts or 0)
|
||||
row["payload"] = dict(payload or {})
|
||||
row["match_strategy"] = match_strategy
|
||||
merged.append(row)
|
||||
replaced = True
|
||||
continue
|
||||
merged.append(row)
|
||||
|
||||
if not replaced:
|
||||
if not replaced and (normalized_emoji or not bool(remove)):
|
||||
merged.append(
|
||||
{
|
||||
"emoji": str(emoji or ""),
|
||||
"source_service": str(source_service or ""),
|
||||
"actor": str(actor or ""),
|
||||
"emoji": normalized_emoji,
|
||||
"source_service": normalized_source,
|
||||
"actor": normalized_actor,
|
||||
"removed": bool(remove),
|
||||
"updated_at": int(target_ts or target.ts or 0),
|
||||
"payload": dict(payload or {}),
|
||||
"match_strategy": match_strategy,
|
||||
}
|
||||
)
|
||||
elif not replaced and bool(remove):
|
||||
receipt_payload = dict(target.receipt_payload or {})
|
||||
reaction_events = list(receipt_payload.get("reaction_events") or [])
|
||||
reaction_events.append(
|
||||
{
|
||||
"emoji": normalized_emoji,
|
||||
"source_service": normalized_source,
|
||||
"actor": normalized_actor,
|
||||
"removed": True,
|
||||
"updated_at": int(target_ts or target.ts or 0),
|
||||
"payload": dict(payload or {}),
|
||||
"match_strategy": match_strategy,
|
||||
"skip_reason": "remove_without_emoji_or_match",
|
||||
}
|
||||
)
|
||||
if len(reaction_events) > 200:
|
||||
reaction_events = reaction_events[-200:]
|
||||
receipt_payload["reaction_events"] = reaction_events
|
||||
target.receipt_payload = receipt_payload
|
||||
await sync_to_async(target.save)(update_fields=["receipt_payload"])
|
||||
log.debug(
|
||||
"reaction-bridge history-apply remove-without-match message_id=%s strategy=%s",
|
||||
str(target.id),
|
||||
match_strategy,
|
||||
)
|
||||
return target
|
||||
|
||||
receipt_payload = dict(target.receipt_payload or {})
|
||||
receipt_payload["reactions"] = merged
|
||||
if match_strategy:
|
||||
receipt_payload["reaction_last_match_strategy"] = str(match_strategy)
|
||||
target.receipt_payload = receipt_payload
|
||||
await sync_to_async(target.save)(update_fields=["receipt_payload"])
|
||||
try:
|
||||
await append_event(
|
||||
user=user,
|
||||
session=target.session,
|
||||
ts=int(target_ts or target.ts or 0),
|
||||
event_type="reaction_removed" if bool(remove) else "reaction_added",
|
||||
direction="system",
|
||||
actor_identifier=str(actor or ""),
|
||||
origin_transport=str(source_service or ""),
|
||||
origin_message_id=str(target.source_message_id or target.id),
|
||||
origin_chat_id=str(target.source_chat_id or ""),
|
||||
payload={
|
||||
"message_id": str(target.id),
|
||||
"target_message_id": str(target_message_id or target.id),
|
||||
"target_ts": int(target_ts or target.ts or 0),
|
||||
"emoji": str(emoji or ""),
|
||||
"remove": bool(remove),
|
||||
"source_service": normalized_source,
|
||||
"actor": normalized_actor,
|
||||
"match_strategy": match_strategy,
|
||||
},
|
||||
raw_payload=dict(payload or {}),
|
||||
trace_id=ensure_trace_id(trace_id, payload or {}),
|
||||
)
|
||||
except Exception as exc:
|
||||
log.warning(
|
||||
"Event ledger append failed for reaction on message=%s: %s",
|
||||
target.id,
|
||||
exc,
|
||||
)
|
||||
log.debug(
|
||||
"reaction-bridge history-apply ok message_id=%s reactions=%s",
|
||||
str(target.id),
|
||||
|
||||
Reference in New Issue
Block a user