diff sat/plugins/plugin_comp_ap_gateway/http_server.py @ 3821:0b1c30ff2cbb

component AP: XMPP identity => AP actor data converstion: XMPP identity data (coming from various XEPs, notably XEP-0292 (vCard4) and XEP-0084 (User Avatar)) are now used to complete actor data. Avatar files can now be delivered with the `avatar` type in URL. rel 368
author Goffi <goffi@goffi.org>
date Wed, 29 Jun 2022 12:06:21 +0200
parents 2032826cfbcf
children 81c79b7cafa7
line wrap: on
line diff
--- a/sat/plugins/plugin_comp_ap_gateway/http_server.py	Wed Jun 29 11:59:07 2022 +0200
+++ b/sat/plugins/plugin_comp_ap_gateway/http_server.py	Wed Jun 29 12:06:21 2022 +0200
@@ -17,14 +17,17 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 import time
+import html
 from typing import Optional, Dict, List, Any
 import json
 from urllib import parse
 from collections import deque
 import unicodedata
+from pathlib import Path
 from pprint import pformat
 
 from twisted.web import http, resource as web_resource, server
+from twisted.web import static
 from twisted.python import failure
 from twisted.internet import reactor, defer
 from twisted.words.protocols.jabber import jid, error
@@ -300,7 +303,10 @@
         # we have to use AP account as preferredUsername because it is used to retrieve
         # actor handle (see https://socialhub.activitypub.rocks/t/how-to-retrieve-user-server-tld-handle-from-actors-url/2196)
         preferred_username = ap_account.split("@", 1)[0]
-        return {
+
+        identity_data = await self.apg._i.getIdentity(self.apg.client, account_jid)
+
+        actor_data = {
             "@context": [
                 "https://www.w3.org/ns/activitystreams",
                 "https://w3id.org/security/v1"
@@ -323,6 +329,28 @@
             },
         }
 
+        if identity_data.get("nicknames"):
+            actor_data["name"] = identity_data["nicknames"][0]
+        if identity_data.get("description"):
+            # description is plain text while summary expects HTML
+            actor_data["summary"] = html.escape(identity_data["description"])
+        if identity_data.get("avatar"):
+            avatar_data = identity_data["avatar"]
+            try:
+                filename = avatar_data["filename"]
+                media_type = avatar_data["media_type"]
+            except KeyError:
+                log.error(f"incomplete avatar data: {identity_data!r}")
+            else:
+                avatar_url = self.apg.buildAPURL("avatar", filename)
+                actor_data["icon"] = {
+                    "type": "Image",
+                    "url": avatar_url,
+                    "mediaType": media_type
+                }
+
+        return actor_data
+
     def getCanonicalURL(self, request: "HTTPRequest") -> str:
         return parse.urljoin(
             f"https://{self.apg.public_url}",
@@ -618,6 +646,12 @@
             ret_data = await self.APInboxRequest(
                 request, None, None, None, ap_url, signing_actor
             )
+        elif request_type == "avatar":
+            if len(extra_args) != 1:
+                raise exceptions.DataError("avatar argument expected in URL")
+            avatar_filename = extra_args[0]
+            avatar_path = self.apg.host.common_cache.getPath(avatar_filename)
+            return static.File(str(avatar_path)).render(request)
         else:
             if len(extra_args) > 1:
                 log.warning(f"unexpected extra arguments: {extra_args!r}")