Lightweight containerized prosody tooling + moved auth scripts + xmpp reconnect/auth stabilization
This commit is contained in:
@@ -30,6 +30,7 @@ from core.assist.engine import process_inbound_assist
|
||||
from core.commands.base import CommandContext
|
||||
from core.commands.engine import process_inbound_message
|
||||
from core.commands.policies import ensure_variant_policies_for_profile
|
||||
from core.events.ledger import append_event_sync
|
||||
from core.messaging import ai as ai_runner
|
||||
from core.messaging import history
|
||||
from core.messaging import media_bridge
|
||||
@@ -53,6 +54,7 @@ from core.models import (
|
||||
from core.presence import get_settings as get_availability_settings
|
||||
from core.presence import spans_for_range
|
||||
from core.realtime.typing_state import get_person_typing_state
|
||||
from core.transports.capabilities import supports, unsupported_reason
|
||||
from core.translation.engine import process_inbound_translation
|
||||
from core.views.workspace import (
|
||||
INSIGHT_METRICS,
|
||||
@@ -516,7 +518,8 @@ def _serialize_message(msg: Message) -> dict:
|
||||
emoji = str(item.get("emoji") or "").strip()
|
||||
if not emoji:
|
||||
continue
|
||||
actor = str(item.get("actor") or "").strip()
|
||||
# Keep actor/source normalization stable to avoid duplicate/hiding issues.
|
||||
actor = str(item.get("actor") or "").strip().lower()
|
||||
source = str(item.get("source_service") or "").strip().lower()
|
||||
key = (emoji, actor, source)
|
||||
if key in seen_reactions:
|
||||
@@ -1811,6 +1814,7 @@ def _reaction_actor_key(user_id, service: str) -> str:
|
||||
|
||||
def _resolve_reaction_target(message: Message, service: str, channel_identifier: str) -> dict:
|
||||
service_key = _default_service(service)
|
||||
message_source_service = str(getattr(message, "source_service", "") or "").strip().lower()
|
||||
source_message_id = str(getattr(message, "source_message_id", "") or "").strip()
|
||||
sender_uuid = str(getattr(message, "sender_uuid", "") or "").strip()
|
||||
source_chat_id = str(getattr(message, "source_chat_id", "") or "").strip()
|
||||
@@ -1819,23 +1823,23 @@ def _resolve_reaction_target(message: Message, service: str, channel_identifier:
|
||||
|
||||
if service_key == "signal":
|
||||
target_ts = 0
|
||||
if source_message_id.isdigit():
|
||||
if message_source_service == "signal" and source_message_id.isdigit():
|
||||
target_ts = int(source_message_id)
|
||||
bridge_ref = _latest_signal_bridge_ref(message)
|
||||
if not target_ts:
|
||||
bridge_ref = _latest_signal_bridge_ref(message)
|
||||
upstream_id = str(bridge_ref.get("upstream_message_id") or "").strip()
|
||||
if upstream_id.isdigit():
|
||||
target_ts = int(upstream_id)
|
||||
if not target_ts:
|
||||
target_ts = int(bridge_ref.get("upstream_ts") or 0)
|
||||
if not target_ts:
|
||||
# Local web messages are only reactable once bridge refs exist.
|
||||
if not target_ts and message_source_service == "signal":
|
||||
target_ts = delivered_ts or local_ts
|
||||
if target_ts <= 0:
|
||||
return {"error": "signal_target_unresolvable"}
|
||||
|
||||
target_author = sender_uuid
|
||||
if not target_author:
|
||||
bridge_ref = _latest_signal_bridge_ref(message)
|
||||
target_author = str(bridge_ref.get("upstream_author") or "").strip()
|
||||
if (
|
||||
str(getattr(message, "custom_author", "") or "").strip().upper()
|
||||
@@ -1856,10 +1860,10 @@ def _resolve_reaction_target(message: Message, service: str, channel_identifier:
|
||||
}
|
||||
|
||||
if service_key == "whatsapp":
|
||||
target_message_id = source_message_id
|
||||
target_message_id = source_message_id if message_source_service == "whatsapp" else ""
|
||||
target_ts = delivered_ts or local_ts
|
||||
bridge_ref = _latest_whatsapp_bridge_ref(message)
|
||||
if not target_message_id:
|
||||
bridge_ref = _latest_whatsapp_bridge_ref(message)
|
||||
target_message_id = str(bridge_ref.get("upstream_message_id") or "").strip()
|
||||
if not target_ts:
|
||||
target_ts = int(bridge_ref.get("upstream_ts") or 0)
|
||||
@@ -4357,7 +4361,7 @@ class ComposeEngageSend(LoginRequiredMixin, View):
|
||||
identifier=base["person_identifier"],
|
||||
)
|
||||
ts_value = int(ts) if str(ts).isdigit() else int(time.time() * 1000)
|
||||
Message.objects.create(
|
||||
created = Message.objects.create(
|
||||
user=request.user,
|
||||
session=session,
|
||||
sender_uuid="",
|
||||
@@ -4366,6 +4370,23 @@ class ComposeEngageSend(LoginRequiredMixin, View):
|
||||
delivered_ts=ts_value if str(ts).isdigit() else None,
|
||||
custom_author="USER",
|
||||
)
|
||||
try:
|
||||
append_event_sync(
|
||||
user=request.user,
|
||||
session=session,
|
||||
ts=ts_value,
|
||||
event_type="message_created",
|
||||
direction="out",
|
||||
actor_identifier="USER",
|
||||
origin_transport="web",
|
||||
origin_message_id=str(created.id),
|
||||
origin_chat_id=str(base["identifier"] or ""),
|
||||
payload={"message_id": str(created.id), "text": outbound},
|
||||
raw_payload={},
|
||||
trace_id="",
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return JsonResponse({"ok": True, "message": "Shared engage sent."})
|
||||
|
||||
@@ -4519,6 +4540,23 @@ class ComposeSend(LoginRequiredMixin, View):
|
||||
),
|
||||
message_meta={},
|
||||
)
|
||||
try:
|
||||
append_event_sync(
|
||||
user=request.user,
|
||||
session=session,
|
||||
ts=int(ts),
|
||||
event_type="message_created",
|
||||
direction="out",
|
||||
actor_identifier="USER",
|
||||
origin_transport="web",
|
||||
origin_message_id=str(created_message.id),
|
||||
origin_chat_id=str(base["identifier"] or ""),
|
||||
payload={"message_id": str(created_message.id), "text": text},
|
||||
raw_payload={},
|
||||
trace_id="",
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
command_id = transport.enqueue_runtime_command(
|
||||
base["service"],
|
||||
"send_message_raw",
|
||||
@@ -4591,6 +4629,23 @@ class ComposeSend(LoginRequiredMixin, View):
|
||||
reply_source_message_id=str(reply_to.id) if reply_to is not None else None,
|
||||
message_meta={},
|
||||
)
|
||||
try:
|
||||
append_event_sync(
|
||||
user=request.user,
|
||||
session=session,
|
||||
ts=msg_ts,
|
||||
event_type="message_created",
|
||||
direction="out",
|
||||
actor_identifier="USER",
|
||||
origin_transport="web",
|
||||
origin_message_id=str(created_message.id),
|
||||
origin_chat_id=str(base["identifier"] or ""),
|
||||
payload={"message_id": str(created_message.id), "text": text},
|
||||
raw_payload={},
|
||||
trace_id="",
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
if created_message is not None:
|
||||
async_to_sync(process_inbound_message)(
|
||||
CommandContext(
|
||||
@@ -4643,6 +4698,14 @@ class ComposeReact(LoginRequiredMixin, View):
|
||||
service_key = _default_service(service)
|
||||
if service_key not in {"signal", "whatsapp"}:
|
||||
return JsonResponse({"ok": False, "error": "service_not_supported"})
|
||||
if bool(getattr(settings, "CAPABILITY_ENFORCEMENT_ENABLED", True)) and not supports(service_key, "reactions"):
|
||||
return JsonResponse(
|
||||
{
|
||||
"ok": False,
|
||||
"error": "unsupported_action",
|
||||
"reason": unsupported_reason(service_key, "reactions"),
|
||||
}
|
||||
)
|
||||
if not identifier and person is None:
|
||||
return JsonResponse({"ok": False, "error": "missing_scope"})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user