Continue AI features and improve protocol support

This commit is contained in:
2026-02-15 16:57:32 +00:00
parent ab91c7dba1
commit 9a70f86088
62 changed files with 5472 additions and 441 deletions

View File

@@ -1,5 +1,6 @@
import asyncio
import json
from urllib.parse import urlparse
import aiohttp
from asgiref.sync import sync_to_async
@@ -15,12 +16,19 @@ from core.util import logs
log = logs.get_logger("signalF")
if settings.DEBUG:
SIGNAL_HOST = "127.0.0.1"
_signal_http_url = getattr(settings, "SIGNAL_HTTP_URL", "").strip()
if _signal_http_url:
parsed = urlparse(
_signal_http_url if "://" in _signal_http_url else f"http://{_signal_http_url}"
)
SIGNAL_HOST = parsed.hostname or "signal"
SIGNAL_PORT = parsed.port or 8080
else:
SIGNAL_HOST = "signal"
SIGNAL_PORT = 8080
if settings.DEBUG:
SIGNAL_HOST = "127.0.0.1"
else:
SIGNAL_HOST = "signal"
SIGNAL_PORT = 8080
SIGNAL_URL = f"{SIGNAL_HOST}:{SIGNAL_PORT}"
@@ -103,6 +111,36 @@ def _extract_attachments(raw_payload):
return results
def _extract_receipt_timestamps(receipt_payload):
raw_ts = receipt_payload.get("timestamp")
if raw_ts is None:
raw_ts = receipt_payload.get("timestamps")
if isinstance(raw_ts, list):
out = []
for item in raw_ts:
try:
out.append(int(item))
except Exception:
continue
return out
if raw_ts is not None:
try:
return [int(raw_ts)]
except Exception:
return []
return []
def _typing_started(typing_payload):
action = str(typing_payload.get("action") or "").strip().lower()
if action in {"started", "start", "typing", "composing"}:
return True
explicit = typing_payload.get("isTyping")
if isinstance(explicit, bool):
return explicit
return True
class NewSignalBot(SignalBot):
def __init__(self, ur, service, config):
self.ur = ur
@@ -221,16 +259,53 @@ class HandleMessage(Command):
log.warning("No Signal identifier available for message routing.")
return
# Handle attachments across multiple Signal payload variants.
attachment_list = _extract_attachments(raw)
# Get users/person identifiers for this Signal sender/recipient.
# Resolve person identifiers once for this event.
identifiers = await sync_to_async(list)(
PersonIdentifier.objects.filter(
identifier=identifier_uuid,
service=self.service,
)
)
envelope = raw.get("envelope", {})
typing_payload = envelope.get("typingMessage")
if isinstance(typing_payload, dict):
for identifier in identifiers:
if _typing_started(typing_payload):
await self.ur.started_typing(
self.service,
identifier=identifier,
payload=typing_payload,
)
else:
await self.ur.stopped_typing(
self.service,
identifier=identifier,
payload=typing_payload,
)
return
receipt_payload = envelope.get("receiptMessage")
if isinstance(receipt_payload, dict):
read_timestamps = _extract_receipt_timestamps(receipt_payload)
read_ts = (
envelope.get("timestamp")
or envelope.get("serverReceivedTimestamp")
or c.message.timestamp
)
for identifier in identifiers:
await self.ur.message_read(
self.service,
identifier=identifier,
message_timestamps=read_timestamps,
read_ts=read_ts,
payload=receipt_payload,
read_by=source_uuid,
)
return
# Handle attachments across multiple Signal payload variants.
attachment_list = _extract_attachments(raw)
xmpp_attachments = []
# Asynchronously fetch all attachments