Continue AI features and improve protocol support
This commit is contained in:
54
core/modules/mixed_protocol.py
Normal file
54
core/modules/mixed_protocol.py
Normal file
@@ -0,0 +1,54 @@
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Any
|
||||
|
||||
|
||||
@dataclass(slots=True)
|
||||
class UnifiedEvent:
|
||||
"""
|
||||
Normalized event envelope shared across protocol adapters.
|
||||
"""
|
||||
|
||||
service: str
|
||||
event_type: str
|
||||
identifier: str = ""
|
||||
text: str = ""
|
||||
ts: int | None = None
|
||||
message_timestamps: list[int] = field(default_factory=list)
|
||||
attachments: list[dict[str, Any]] = field(default_factory=list)
|
||||
payload: dict[str, Any] = field(default_factory=dict)
|
||||
|
||||
|
||||
def normalize_gateway_event(service: str, payload: dict[str, Any]) -> UnifiedEvent:
|
||||
event_type = str(payload.get("type") or "").strip().lower()
|
||||
message_timestamps = []
|
||||
raw_timestamps = payload.get("message_timestamps") or payload.get("timestamps") or []
|
||||
if isinstance(raw_timestamps, list):
|
||||
for item in raw_timestamps:
|
||||
try:
|
||||
message_timestamps.append(int(item))
|
||||
except Exception:
|
||||
continue
|
||||
elif raw_timestamps:
|
||||
try:
|
||||
message_timestamps = [int(raw_timestamps)]
|
||||
except Exception:
|
||||
message_timestamps = []
|
||||
|
||||
ts = payload.get("ts") or payload.get("timestamp")
|
||||
try:
|
||||
ts = int(ts) if ts is not None else None
|
||||
except Exception:
|
||||
ts = None
|
||||
|
||||
return UnifiedEvent(
|
||||
service=service,
|
||||
event_type=event_type,
|
||||
identifier=str(
|
||||
payload.get("identifier") or payload.get("source") or payload.get("from") or ""
|
||||
).strip(),
|
||||
text=str(payload.get("text") or ""),
|
||||
ts=ts,
|
||||
message_timestamps=message_timestamps,
|
||||
attachments=list(payload.get("attachments") or []),
|
||||
payload=dict(payload or {}),
|
||||
)
|
||||
@@ -1,5 +1,12 @@
|
||||
from asgiref.sync import sync_to_async
|
||||
|
||||
from core.clients import transport
|
||||
from core.clients.instagram import InstagramClient
|
||||
from core.clients.signal import SignalClient
|
||||
from core.clients.whatsapp import WhatsAppClient
|
||||
from core.clients.xmpp import XMPPClient
|
||||
from core.messaging import history
|
||||
from core.models import PersonIdentifier
|
||||
from core.util import logs
|
||||
|
||||
|
||||
@@ -16,11 +23,15 @@ class UnifiedRouter(object):
|
||||
|
||||
self.xmpp = XMPPClient(self, loop, "xmpp")
|
||||
self.signal = SignalClient(self, loop, "signal")
|
||||
self.whatsapp = WhatsAppClient(self, loop, "whatsapp")
|
||||
self.instagram = InstagramClient(self, loop, "instagram")
|
||||
|
||||
def _start(self):
|
||||
print("UR _start")
|
||||
self.xmpp.start()
|
||||
self.signal.start()
|
||||
self.whatsapp.start()
|
||||
self.instagram.start()
|
||||
|
||||
def run(self):
|
||||
try:
|
||||
@@ -37,14 +48,66 @@ class UnifiedRouter(object):
|
||||
async def message_received(self, protocol, *args, **kwargs):
|
||||
self.log.info(f"Message received ({protocol}) {args} {kwargs}")
|
||||
|
||||
async def _resolve_identifier_objects(self, protocol, identifier):
|
||||
if isinstance(identifier, PersonIdentifier):
|
||||
return [identifier]
|
||||
value = str(identifier or "").strip()
|
||||
if not value:
|
||||
return []
|
||||
return await sync_to_async(list)(
|
||||
PersonIdentifier.objects.filter(
|
||||
identifier=value,
|
||||
service=protocol,
|
||||
)
|
||||
)
|
||||
|
||||
async def message_read(self, protocol, *args, **kwargs):
|
||||
self.log.info(f"Message read ({protocol}) {args} {kwargs}")
|
||||
identifier = kwargs.get("identifier")
|
||||
timestamps = kwargs.get("message_timestamps") or []
|
||||
read_ts = kwargs.get("read_ts")
|
||||
payload = kwargs.get("payload") or {}
|
||||
read_by = kwargs.get("read_by") or ""
|
||||
|
||||
identifiers = await self._resolve_identifier_objects(protocol, identifier)
|
||||
for row in identifiers:
|
||||
await history.apply_read_receipts(
|
||||
user=row.user,
|
||||
identifier=row,
|
||||
message_timestamps=timestamps,
|
||||
read_ts=read_ts,
|
||||
source_service=protocol,
|
||||
read_by_identifier=read_by or row.identifier,
|
||||
payload=payload,
|
||||
)
|
||||
|
||||
async def started_typing(self, protocol, *args, **kwargs):
|
||||
self.log.info(f"Started typing ({protocol}) {args} {kwargs}")
|
||||
identifier = kwargs.get("identifier")
|
||||
identifiers = await self._resolve_identifier_objects(protocol, identifier)
|
||||
for src in identifiers:
|
||||
targets = await sync_to_async(list)(
|
||||
PersonIdentifier.objects.filter(
|
||||
user=src.user,
|
||||
person=src.person,
|
||||
).exclude(service=protocol)
|
||||
)
|
||||
for target in targets:
|
||||
await transport.start_typing(target.service, target.identifier)
|
||||
|
||||
async def stopped_typing(self, protocol, *args, **kwargs):
|
||||
self.log.info(f"Stopped typing ({protocol}) {args} {kwargs}")
|
||||
identifier = kwargs.get("identifier")
|
||||
identifiers = await self._resolve_identifier_objects(protocol, identifier)
|
||||
for src in identifiers:
|
||||
targets = await sync_to_async(list)(
|
||||
PersonIdentifier.objects.filter(
|
||||
user=src.user,
|
||||
person=src.person,
|
||||
).exclude(service=protocol)
|
||||
)
|
||||
for target in targets:
|
||||
await transport.stop_typing(target.service, target.identifier)
|
||||
|
||||
async def reacted(self, protocol, *args, **kwargs):
|
||||
self.log.info(f"Reacted ({protocol}) {args} {kwargs}")
|
||||
|
||||
Reference in New Issue
Block a user