from urllib.parse import urlencode import orjson import requests from django.conf import settings from django.shortcuts import render from django.urls import reverse from django.views import View from mixins.views import ObjectList, ObjectRead from core.clients import transport from core.models import Chat, PersonIdentifier from core.views.manage.permissions import SuperUserRequiredMixin class CustomObjectRead(ObjectRead): def post(self, request, *args, **kwargs): self.request = request return super().get(request, *args, **kwargs) class Signal(SuperUserRequiredMixin, View): template_name = "pages/signal.html" service = "signal" page_title = "Signal" accounts_url_name = "signal_accounts" def get(self, request): return render( request, self.template_name, { "service": self.service, "service_label": self.page_title, "accounts_url_name": self.accounts_url_name, }, ) class SignalAccounts(SuperUserRequiredMixin, ObjectList): list_template = "partials/signal-accounts.html" service = "signal" context_object_name_singular = "Signal Account" context_object_name = "Signal Accounts" list_url_name = "signal_accounts" list_url_args = ["type"] def _normalize_accounts(self, rows): out = [] for item in rows or []: if isinstance(item, dict): value = ( item.get("number") or item.get("id") or item.get("jid") or item.get("account") ) if value: out.append(str(value)) elif item: out.append(str(item)) return out def _service_context(self, service, label, add_url_name, show_contact_actions): return { "service": service, "service_label": label, "account_add_url_name": add_url_name, "show_contact_actions": show_contact_actions, "contacts_url_name": f"{service}_contacts", "chats_url_name": f"{service}_chats", "endpoint_base": str( getattr(settings, "SIGNAL_HTTP_URL", "http://signal:8080") ).rstrip("/") if service == "signal" else "", "service_warning": transport.get_service_warning(service), } def get_queryset(self, **kwargs): self.extra_context = self._service_context( service="signal", label="Signal", add_url_name="signal_account_add", show_contact_actions=True, ) return self._normalize_accounts(transport.list_accounts("signal")) class SignalContactsList(SuperUserRequiredMixin, ObjectList): list_template = "partials/signal-contacts-list.html" context_object_name_singular = "Signal Contact" context_object_name = "Signal Contacts" list_url_name = "signal_contacts" list_url_args = ["type", "pk"] def get_queryset(self, *args, **kwargs): # url = signal:8080/v1/accounts # /v1/configuration/{number}/settings # /v1/identities/{number} # /v1/contacts/{number} # response = requests.get( # f"http://signal:8080/v1/configuration/{self.kwargs['pk']}/settings" # ) # config = orjson.loads(response.text) base = getattr(settings, "SIGNAL_HTTP_URL", "http://signal:8080").rstrip("/") response = requests.get(f"{base}/v1/identities/{self.kwargs['pk']}") identities = orjson.loads(response.text) response = requests.get(f"{base}/v1/contacts/{self.kwargs['pk']}") contacts = orjson.loads(response.text) # add identities to contacts for contact in contacts: for identity in identities: if contact["number"] == identity["number"]: contact["identity"] = identity obj = { # "identity": identity, "contacts": contacts, } self.extra_context = {"pretty": list(obj.keys())} return obj class SignalChatsList(SuperUserRequiredMixin, ObjectList): list_template = "partials/signal-chats-list.html" context_object_name_singular = "Signal Chat" context_object_name = "Signal Chats" list_url_name = "signal_chats" list_url_args = ["type", "pk"] def get_queryset(self, *args, **kwargs): pk = self.kwargs.get("pk", "") chats = list(Chat.objects.filter(account=pk)) rows = [] for chat in chats: identifier_candidates = [ str(chat.source_uuid or "").strip(), str(chat.source_number or "").strip(), ] identifier_candidates = [value for value in identifier_candidates if value] person_identifier = None if identifier_candidates: person_identifier = ( PersonIdentifier.objects.filter( user=self.request.user, service="signal", identifier__in=identifier_candidates, ) .select_related("person") .first() ) identifier_value = ( person_identifier.identifier if person_identifier else "" ) or (chat.source_uuid or chat.source_number or "") service = "signal" compose_page_url = "" compose_widget_url = "" if identifier_value: query = f"service={service}&identifier={identifier_value}" if person_identifier: query += f"&person={person_identifier.person_id}" compose_page_url = f"{reverse('compose_page')}?{query}" compose_widget_url = f"{reverse('compose_widget')}?{query}" if person_identifier: ai_url = ( f"{reverse('ai_workspace')}?person={person_identifier.person_id}" ) else: ai_url = reverse("ai_workspace") rows.append( { "chat": chat, "compose_page_url": compose_page_url, "compose_widget_url": compose_widget_url, "ai_url": ai_url, "person_name": ( person_identifier.person.name if person_identifier else "" ), "manual_icon_class": "fa-solid fa-paper-plane", "can_compose": bool(compose_page_url), "match_url": ( f"{reverse('compose_contact_match')}?" f"{urlencode({'service': 'signal', 'identifier': identifier_value})}" if identifier_value else reverse("compose_contact_match") ), } ) return rows class SignalMessagesList(SuperUserRequiredMixin, ObjectList): ... class SignalAccountAdd(SuperUserRequiredMixin, CustomObjectRead): detail_template = "partials/signal-account-add.html" service = "signal" context_object_name_singular = "Add Account" context_object_name = "Add Account" detail_url_name = "signal_account_add" detail_url_args = ["type", "device"] page_title = None def get_object(self, **kwargs): form_args = self.request.POST.dict() device_name = form_args["device"] image_bytes = transport.get_link_qr(self.service, device_name) base64_image = transport.image_bytes_to_base64(image_bytes) return base64_image