Implement contact matching

This commit is contained in:
2026-02-15 23:48:32 +00:00
parent 10af1e4d6b
commit b1a53034d5
12 changed files with 667 additions and 46 deletions

View File

@@ -1,9 +1,12 @@
from django.shortcuts import render
from django.urls import reverse
from urllib.parse import urlencode
from django.views import View
from mixins.views import ObjectList, ObjectRead
from core.clients import transport
from core.models import PersonIdentifier
from core.views.compose import _compose_urls, _service_icon_class
from core.views.manage.permissions import SuperUserRequiredMixin
import time
@@ -56,12 +59,107 @@ class WhatsAppAccounts(SuperUserRequiredMixin, ObjectList):
"service": "whatsapp",
"service_label": "WhatsApp",
"account_add_url_name": "whatsapp_account_add",
"show_contact_actions": False,
"show_contact_actions": True,
"contacts_url_name": "whatsapp_contacts",
"chats_url_name": "whatsapp_chats",
"service_warning": transport.get_service_warning("whatsapp"),
}
return self._normalize_accounts(transport.list_accounts("whatsapp"))
class WhatsAppContactsList(SuperUserRequiredMixin, ObjectList):
list_template = "partials/whatsapp-contacts-list.html"
context_object_name_singular = "WhatsApp Contact"
context_object_name = "WhatsApp Contacts"
list_url_name = "whatsapp_contacts"
list_url_args = ["type", "pk"]
def get_queryset(self, *args, **kwargs):
state = transport.get_runtime_state("whatsapp")
runtime_contacts = state.get("contacts") or []
rows = []
seen = set()
for item in runtime_contacts:
if not isinstance(item, dict):
continue
identifier = str(
item.get("identifier") or item.get("jid") or item.get("chat") or ""
).strip()
if not identifier or identifier in seen:
continue
seen.add(identifier)
linked = (
PersonIdentifier.objects.filter(
user=self.request.user,
service="whatsapp",
identifier=identifier,
)
.select_related("person")
.first()
)
urls = _compose_urls(
"whatsapp",
identifier,
linked.person_id if linked else None,
)
rows.append(
{
"identifier": identifier,
"jid": str(item.get("jid") or ""),
"name": str(item.get("name") or item.get("chat") or ""),
"service_icon_class": _service_icon_class("whatsapp"),
"person_name": linked.person.name if linked else "",
"compose_page_url": urls["page_url"],
"compose_widget_url": urls["widget_url"],
"match_url": (
f"{reverse('compose_contact_match')}?"
f"{urlencode({'service': 'whatsapp', 'identifier': identifier})}"
),
}
)
# Include already-linked WhatsApp contacts not yet discovered by runtime.
linked_rows = (
PersonIdentifier.objects.filter(
user=self.request.user,
service="whatsapp",
)
.select_related("person")
.order_by("person__name", "identifier")
)
for row in linked_rows:
identifier = str(row.identifier or "").strip()
if not identifier or identifier in seen:
continue
seen.add(identifier)
urls = _compose_urls("whatsapp", identifier, row.person_id)
rows.append(
{
"identifier": identifier,
"jid": "",
"name": row.person.name,
"service_icon_class": _service_icon_class("whatsapp"),
"person_name": row.person.name,
"compose_page_url": urls["page_url"],
"compose_widget_url": urls["widget_url"],
"match_url": (
f"{reverse('compose_contact_match')}?"
f"{urlencode({'service': 'whatsapp', 'identifier': identifier})}"
),
}
)
return rows
class WhatsAppChatsList(WhatsAppContactsList):
list_template = "partials/whatsapp-chats-list.html"
context_object_name_singular = "WhatsApp Chat"
context_object_name = "WhatsApp Chats"
list_url_name = "whatsapp_chats"
class WhatsAppAccountAdd(SuperUserRequiredMixin, ObjectRead):
detail_template = "partials/whatsapp-account-add.html"
service = "whatsapp"