Allow linking chats

This commit is contained in:
2026-02-19 17:13:34 +00:00
parent bac2841298
commit 0816687c71
15 changed files with 369 additions and 461 deletions

View File

@@ -11,7 +11,7 @@ from signalbot import Command, Context, SignalBot
from core.clients import ClientBase, signalapi
from core.messaging import ai, history, media_bridge, natural, replies, utils
from core.models import Chat, Manipulation, PersonIdentifier, QueuedMessage
from core.models import Chat, Manipulation, PersonIdentifier, PlatformChatLink, QueuedMessage
from core.util import logs
log = logs.get_logger("signalF")
@@ -263,6 +263,49 @@ class NewSignalBot(SignalBot):
await self._resolve_commands()
await self._produce_consume_messages()
async def _upsert_groups(self) -> None:
groups = getattr(self, "groups", None) or []
if not groups:
self.log.debug("[Signal] _upsert_groups: no groups to persist")
return
identifiers = await sync_to_async(list)(
PersonIdentifier.objects.filter(service="signal").select_related("user")
)
seen_user_ids: set = set()
users = []
for pi in identifiers:
if pi.user_id not in seen_user_ids:
seen_user_ids.add(pi.user_id)
users.append(pi.user)
if not users:
self.log.debug("[Signal] _upsert_groups: no PersonIdentifiers found — skipping")
return
for user in users:
for group in groups:
group_id = group.get("id") or ""
name = group.get("name") or group_id
if not group_id:
continue
await sync_to_async(PlatformChatLink.objects.update_or_create)(
user=user,
service="signal",
chat_identifier=group_id,
defaults={
"person": None,
"person_identifier": None,
"is_group": True,
"chat_name": name,
},
)
self.log.info("[Signal] upserted %d groups for %d users", len(groups), len(users))
async def _detect_groups(self):
await super()._detect_groups()
await self._upsert_groups()
def start(self):
"""Start bot without blocking the caller's event loop."""
task = self._event_loop.create_task(

View File

@@ -15,7 +15,7 @@ from django.core.cache import cache
from core.clients import ClientBase, transport
from core.messaging import history, media_bridge
from core.models import Message, PersonIdentifier
from core.models import Message, PersonIdentifier, PlatformChatLink
class WhatsAppClient(ClientBase):
@@ -1514,6 +1514,7 @@ class WhatsAppClient(ClientBase):
# Read contact-like rows directly from the session sqlite DB instead.
contacts, source, lid_map = await self._sync_contacts_from_sqlite()
groups, groups_source = await self._sync_groups_from_client()
await self._upsert_groups(groups)
now_ts = int(time.time())
if contacts:
@@ -1762,6 +1763,53 @@ class WhatsAppClient(ClientBase):
return await asyncio.to_thread(_extract)
async def _upsert_groups(self, groups: list) -> None:
if not groups:
self.log.debug("[WA] _upsert_groups: no groups to persist")
return
identifiers = await sync_to_async(list)(
PersonIdentifier.objects.filter(service="whatsapp").select_related("user")
)
seen_user_ids: set = set()
users = []
for pi in identifiers:
if pi.user_id not in seen_user_ids:
seen_user_ids.add(pi.user_id)
users.append(pi.user)
if not users:
self.log.debug("[WA] _upsert_groups: no PersonIdentifiers found — skipping")
return
upserted = 0
for user in users:
for group in groups:
identifier = group.get("identifier") or ""
name = group.get("name") or identifier
jid = group.get("jid") or ""
if "@newsletter" in jid or "@newsletter" in identifier:
continue
await sync_to_async(PlatformChatLink.objects.update_or_create)(
user=user,
service="whatsapp",
chat_identifier=identifier,
defaults={
"person": None,
"person_identifier": None,
"is_group": True,
"chat_name": name,
"chat_jid": jid,
},
)
upserted += 1
self.log.info(
"[WA] upserted %d group rows (%d groups × %d users)",
upserted,
len(groups),
len(users),
)
async def _sync_groups_from_client(self):
if self._client is None:
return [], "client_missing"