diff sat/plugins/plugin_comp_ap_gateway/__init__.py @ 3888:aa7197b67c26

component AP gateway: AP <=> XMPP reactions conversions: - Pubsub Attachments plugin has been renamed to XEP-0470 following publication - XEP-0470 has been updated to follow 0.2 changes - AP reactions (as implemented in Pleroma) are converted to XEP-0470 - XEP-0470 events are converted to AP reactions (again, using "EmojiReact" from Pleroma) - AP activities related to attachments (like/reactions) are cached in Libervia because it's not possible to retrieve them from Pleroma instances once they have been emitted (doing an HTTP get on their ID returns a 404). For now those cache are not flushed, this should be improved in the future. - `sharedInbox` is used when available. Pleroma returns a 500 HTTP error when ``to`` or ``cc`` are used in a direct inbox. - reactions and like are not currently used for direct messages, because they can't be emitted from Pleroma in this case, thus there is no point in implementing them for the moment. rel 371
author Goffi <goffi@goffi.org>
date Wed, 31 Aug 2022 17:07:03 +0200
parents 6da749bbf320
children 0aa7023dcd08
line wrap: on
line diff
--- a/sat/plugins/plugin_comp_ap_gateway/__init__.py	Wed Aug 31 17:07:03 2022 +0200
+++ b/sat/plugins/plugin_comp_ap_gateway/__init__.py	Wed Aug 31 17:07:03 2022 +0200
@@ -71,6 +71,7 @@
     TYPE_TOMBSTONE,
     TYPE_MENTION,
     TYPE_LIKE,
+    TYPE_REACTION,
     NS_AP,
     NS_AP_PUBLIC,
     PUBLIC_TUPLE
@@ -92,8 +93,8 @@
     C.PI_PROTOCOLS: [],
     C.PI_DEPENDENCIES: [
         "XEP-0054", "XEP-0060", "XEP-0084", "XEP-0106", "XEP-0277", "XEP-0292",
-        "XEP-0329", "XEP-0372", "XEP-0424", "XEP-0465", "PUBSUB_CACHE", "TEXT_SYNTAXES",
-        "IDENTITY", "PUBSUB_ATTACHMENTS"
+        "XEP-0329", "XEP-0372", "XEP-0424", "XEP-0465", "XEP-0470", "PUBSUB_CACHE",
+        "TEXT_SYNTAXES", "IDENTITY"
     ],
     C.PI_RECOMMENDATIONS: [],
     C.PI_MAIN: "APGateway",
@@ -132,7 +133,7 @@
         self._c = host.plugins["PUBSUB_CACHE"]
         self._t = host.plugins["TEXT_SYNTAXES"]
         self._i = host.plugins["IDENTITY"]
-        self._pa = host.plugins["PUBSUB_ATTACHMENTS"]
+        self._pa = host.plugins["XEP-0470"]
         self._p.addManagedNode(
             "",
             items_cb=self._itemsReceived,
@@ -335,7 +336,7 @@
         if resp.code >= 300:
             text = await resp.text()
             if resp.code == 404:
-                raise exceptions.NotFound()
+                raise exceptions.NotFound(f"Can't find resource at {url}")
             else:
                 msg = f"HTTP error {resp.code}: {text}"
                 raise exceptions.ExternalRequestError(msg)
@@ -1104,6 +1105,7 @@
         old_attachment_pubsub_items = await self.host.memory.storage.searchPubsubItems({
             "profiles": [self.client.profile],
             "services": [service],
+            "nodes": [node],
             "names": [item_elt["id"]]
         })
         if not old_attachment_pubsub_items:
@@ -1127,6 +1129,7 @@
             # no known element was present in attachments
             attachments = {}
 
+        # noticed
         if "noticed" in attachments:
             if not "noticed" in old_attachment:
                 # new "noticed" attachment, we translate to "Like" activity
@@ -1134,7 +1137,8 @@
                 like = self.createActivity(
                     TYPE_LIKE, publisher_actor_id, item_url, activity_id=activity_id
                 )
-                like["to"] = [NS_AP_PUBLIC]
+                like["to"] = [ap_account]
+                like["cc"] = [NS_AP_PUBLIC]
                 await self.signAndPost(inbox, publisher_actor_id, like)
         else:
             if "noticed" in old_attachment:
@@ -1143,10 +1147,36 @@
                 like = self.createActivity(
                     TYPE_LIKE, publisher_actor_id, item_url, activity_id=activity_id
                 )
-                like["to"] = [NS_AP_PUBLIC]
+                like["to"] = [ap_account]
+                like["cc"] = [NS_AP_PUBLIC]
                 undo = self.createActivity("Undo", publisher_actor_id, like)
                 await self.signAndPost(inbox, publisher_actor_id, undo)
 
+        # reactions
+        new_reactions = set(attachments.get("reactions", {}).get("reactions", []))
+        old_reactions = set(old_attachment.get("reactions", {}).get("reactions", []))
+        reactions_remove = old_reactions - new_reactions
+        reactions_add = new_reactions - old_reactions
+        for reactions, undo in ((reactions_remove, True), (reactions_add, False)):
+            for reaction in reactions:
+                activity_id = self.buildAPURL(
+                    "reaction", item_account, item_id, reaction.encode().hex()
+                )
+                reaction_activity = self.createActivity(
+                    TYPE_REACTION, publisher_actor_id, item_url,
+                    activity_id=activity_id
+                )
+                reaction_activity["content"] = reaction
+                reaction_activity["to"] = [ap_account]
+                reaction_activity["cc"] = [NS_AP_PUBLIC]
+                if undo:
+                    activy = self.createActivity(
+                        "Undo", publisher_actor_id, reaction_activity
+                    )
+                else:
+                    activy = reaction_activity
+                await self.signAndPost(inbox, publisher_actor_id, activy)
+
         if service.user and self.isVirtualJID(service):
             # the item is on a virtual service, we need to store it in cache
             log.debug("storing attachments item in cache")
@@ -1262,9 +1292,18 @@
         href = await self.getAPActorIdFromAccount(account)
         return await self.apGet(href)
 
-    async def getAPInboxFromId(self, actor_id: str) -> str:
-        """Retrieve inbox of an actor_id"""
+    async def getAPInboxFromId(self, actor_id: str, use_shared: bool = True) -> str:
+        """Retrieve inbox of an actor_id
+
+        @param use_shared: if True, and a shared inbox exists, it will be used instead of
+            the user inbox
+        """
         data = await self.getActorData(actor_id)
+        if use_shared:
+            try:
+                return data["endpoints"]["sharedInbox"]
+            except KeyError:
+                pass
         return data["inbox"]
 
     @async_lru(maxsize=LRU_MAX_SIZE)
@@ -2039,7 +2078,7 @@
 
         actor_account = self._e.unescape(mess_data["to"].user)
         actor_id = await self.getAPActorIdFromAccount(actor_account)
-        inbox = await self.getAPInboxFromId(actor_id)
+        inbox = await self.getAPInboxFromId(actor_id, use_shared=False)
 
         try:
             language, message = next(iter(mess_data["message"].items()))
@@ -2085,7 +2124,7 @@
             )
         ap_account = self._e.unescape(to_jid.user)
         actor_id = await self.getAPActorIdFromAccount(ap_account)
-        inbox = await self.getAPInboxFromId(actor_id)
+        inbox = await self.getAPInboxFromId(actor_id, use_shared=False)
         url_actor, ap_item = await self.apDeleteItem(
             from_jid.userhostJID(), None, fastened_elts.id, public=False
         )
@@ -2175,7 +2214,7 @@
             "name": ap_account,
         })
 
-        inbox = await self.getAPInboxFromId(actor_id)
+        inbox = await self.getAPInboxFromId(actor_id, use_shared=False)
 
         resp = await self.signAndPost(inbox, ap_item["actor"], ap_item)