From da044be68c4299103993461a7613889726fc6448 Mon Sep 17 00:00:00 2001 From: Mark Veidemanis Date: Mon, 9 Mar 2026 12:28:30 +0000 Subject: [PATCH] Improve cross-platform group integration --- ...045_user_allow_contacts_to_create_tasks.py | 15 ++++ ..._externalchatlink_provider_default_mock.py | 15 ++++ core/tasks/external_chat_support.py | 73 +++++++++++++++++++ 3 files changed, 103 insertions(+) create mode 100644 core/migrations/0045_user_allow_contacts_to_create_tasks.py create mode 100644 core/migrations/0046_externalchatlink_provider_default_mock.py create mode 100644 core/tasks/external_chat_support.py diff --git a/core/migrations/0045_user_allow_contacts_to_create_tasks.py b/core/migrations/0045_user_allow_contacts_to_create_tasks.py new file mode 100644 index 0000000..1a682fe --- /dev/null +++ b/core/migrations/0045_user_allow_contacts_to_create_tasks.py @@ -0,0 +1,15 @@ +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("core", "0044_userxmppsecuritysettings_encrypt_component_messages_with_omemo"), + ] + + operations = [ + migrations.AddField( + model_name="user", + name="allow_contacts_to_create_tasks", + field=models.BooleanField(default=True), + ), + ] diff --git a/core/migrations/0046_externalchatlink_provider_default_mock.py b/core/migrations/0046_externalchatlink_provider_default_mock.py new file mode 100644 index 0000000..e29f16d --- /dev/null +++ b/core/migrations/0046_externalchatlink_provider_default_mock.py @@ -0,0 +1,15 @@ +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("core", "0045_user_allow_contacts_to_create_tasks"), + ] + + operations = [ + migrations.AlterField( + model_name="externalchatlink", + name="provider", + field=models.CharField(default="mock", max_length=64), + ), + ] diff --git a/core/tasks/external_chat_support.py b/core/tasks/external_chat_support.py new file mode 100644 index 0000000..18cbc38 --- /dev/null +++ b/core/tasks/external_chat_support.py @@ -0,0 +1,73 @@ +from __future__ import annotations + +import re +from typing import Any + +from django.db.models import Q + +from core.models import ExternalChatLink, PersonIdentifier + + +def channel_variants(service: str, channel: str) -> list[str]: + value = str(channel or "").strip() + if not value: + return [] + variants = [value] + service_key = str(service or "").strip().lower() + if service_key == "whatsapp": + bare = value.split("@", 1)[0].strip() + if bare and bare not in variants: + variants.append(bare) + direct = f"{bare}@s.whatsapp.net" if bare else "" + if direct and direct not in variants: + variants.append(direct) + group = f"{bare}@g.us" if bare else "" + if group and group not in variants: + variants.append(group) + if service_key == "signal": + digits = re.sub(r"[^0-9]", "", value) + if digits and digits not in variants: + variants.append(digits) + if digits: + plus = f"+{digits}" + if plus not in variants: + variants.append(plus) + return variants + + +def resolve_external_chat_id(*, user, provider: str, service: str, channel: str) -> str: + variants = channel_variants(service, channel) + if not variants: + return "" + person_identifier = ( + PersonIdentifier.objects.filter( + user=user, + service=service, + identifier__in=variants, + ) + .select_related("person") + .order_by("-id") + .first() + ) + if person_identifier is None: + return "" + link = ( + ExternalChatLink.objects.filter( + user=user, + provider=provider, + enabled=True, + ) + .filter( + Q(person_identifier=person_identifier) | Q(person=person_identifier.person) + ) + .order_by("-updated_at", "-id") + .first() + ) + return str(getattr(link, "external_chat_id", "") or "").strip() + + +def compact_json_snippet(payload: Any, limit: int = 800) -> str: + text = str(payload or "").strip() + if len(text) <= limit: + return text + return text[:limit].rstrip() + "..."