diff sat/plugins/plugin_xep_0060.py @ 3758:b7cef1b24f83

plugins XEP-0060, XEP-0376, XEP-0465, CLI: PAM + PSS implementation: - update psSubscriptionsGet to use serialised return value - implement XEP-0376 Pubsub Account Management - implement XEP-0465 Public Pubsub Subscriptions - CLI `pubsub` commands updated accordingly, and added `--public` flags to `subscribe`, `Subscriptions` and `node Subscriptions get` ⚠ `XEP-0465` is speculative, the XEP has been accepted by council but not published yet. As is should be the next one, and current latest one is `XEP-0464`, `XEP-0465` has been anticipated. rel 365
author Goffi <goffi@goffi.org>
date Fri, 13 May 2022 18:38:05 +0200
parents 5bda9d2e8b35
children c61233f51b86
line wrap: on
line diff
--- a/sat/plugins/plugin_xep_0060.py	Fri May 13 18:29:42 2022 +0200
+++ b/sat/plugins/plugin_xep_0060.py	Fri May 13 18:38:05 2022 +0200
@@ -272,7 +272,7 @@
             "psSubscriptionsGet",
             ".plugin",
             in_sign="sss",
-            out_sign="aa{ss}",
+            out_sign="s",
             method=self._subscriptions,
             async_=True,
         )
@@ -1204,36 +1204,48 @@
             sender,
         )
 
-    def _subscriptions(self, service, nodeIdentifier="", profile_key=C.PROF_KEY_NONE):
+    @utils.ensure_deferred
+    async def _subscriptions(
+        self,
+        service="",
+        nodeIdentifier="",
+        profile_key=C.PROF_KEY_NONE
+    ) -> str:
         client = self.host.getClient(profile_key)
         service = None if not service else jid.JID(service)
+        subs = await self.subscriptions(client, service, nodeIdentifier or None)
+        return data_format.serialise(subs)
 
-        def gotSubscriptions(subscriptions):
-            # we replace pubsub.Subscription instance by dict that we can serialize
-            for idx, sub in enumerate(subscriptions):
-                sub_dict = {
-                    "node": sub.nodeIdentifier,
-                    "subscriber": sub.subscriber.full(),
-                    "state": sub.state,
-                }
-                if sub.subscriptionIdentifier is not None:
-                    sub_dict["id"] = sub.subscriptionIdentifier
-                subscriptions[idx] = sub_dict
-
-            return subscriptions
-
-        d = self.subscriptions(client, service, nodeIdentifier or None)
-        d.addCallback(gotSubscriptions)
-        return d
-
-    def subscriptions(self, client, service, nodeIdentifier=None):
-        """retrieve subscriptions from a service
+    async def subscriptions(
+        self,
+        client: SatXMPPEntity,
+        service: Optional[jid.JID] = None,
+        node: Optional[str] = None
+    ) -> List[Dict[str, Union[str, bool]]]:
+        """Retrieve subscriptions from a service
 
         @param service(jid.JID): PubSub service
         @param nodeIdentifier(unicode, None): node to check
             None to get all subscriptions
         """
-        return client.pubsub_client.subscriptions(service, nodeIdentifier)
+        cont, ret = await self.host.trigger.asyncReturnPoint(
+            "XEP-0060_subscriptions", client, service, node
+        )
+        if not cont:
+            return ret
+        subs = await client.pubsub_client.subscriptions(service, node)
+        ret = []
+        for sub in subs:
+            sub_dict = {
+                "service": service.host if service else client.jid.host,
+                "node": sub.nodeIdentifier,
+                "subscriber": sub.subscriber.full(),
+                "state": sub.state,
+            }
+            if sub.subscriptionIdentifier is not None:
+                sub_dict["id"] = sub.subscriptionIdentifier
+            ret.append(sub_dict)
+        return ret
 
     ## misc tools ##
 
@@ -1325,17 +1337,25 @@
 
     # subscribe #
 
-    def _getNodeSubscriptions(self, service_s, nodeIdentifier, profile_key):
+    @utils.ensure_deferred
+    async def _getNodeSubscriptions(
+        self,
+        service: str,
+        node: str,
+        profile_key: str
+    ) -> Dict[str, str]:
         client = self.host.getClient(profile_key)
-        d = self.getNodeSubscriptions(
-            client, jid.JID(service_s) if service_s else None, nodeIdentifier
+        subs = await self.getNodeSubscriptions(
+            client, jid.JID(service) if service else None, node
         )
-        d.addCallback(
-            lambda subscriptions: {j.full(): a for j, a in subscriptions.items()}
-        )
-        return d
+        return {j.full(): a for j, a in subs.items()}
 
-    def getNodeSubscriptions(self, client, service, nodeIdentifier):
+    async def getNodeSubscriptions(
+        self,
+        client: SatXMPPEntity,
+        service: Optional[jid.JID],
+        nodeIdentifier: str
+    ) -> Dict[jid.JID, str]:
         """Retrieve subscriptions to a node
 
         @param nodeIdentifier(unicode): node to get subscriptions from
@@ -1346,36 +1366,32 @@
         request.recipient = service
         request.nodeIdentifier = nodeIdentifier
 
-        def cb(iq_elt):
-            try:
-                subscriptions_elt = next(
-                    iq_elt.pubsub.elements((pubsub.NS_PUBSUB, "subscriptions"))
-                )
-            except StopIteration:
-                raise ValueError(
-                    _("Invalid result: missing <subscriptions> element: {}").format(
-                        iq_elt.toXml
-                    )
+        iq_elt = await request.send(client.xmlstream)
+        try:
+            subscriptions_elt = next(
+                iq_elt.pubsub.elements((pubsub.NS_PUBSUB, "subscriptions"))
+            )
+        except StopIteration:
+            raise ValueError(
+                _("Invalid result: missing <subscriptions> element: {}").format(
+                    iq_elt.toXml
                 )
-            except AttributeError as e:
-                raise ValueError(_("Invalid result: {}").format(e))
-            try:
-                return {
-                    jid.JID(s["jid"]): s["subscription"]
-                    for s in subscriptions_elt.elements(
-                        (pubsub.NS_PUBSUB, "subscription")
-                    )
-                }
-            except KeyError:
-                raise ValueError(
-                    _("Invalid result: bad <subscription> element: {}").format(
-                        iq_elt.toXml
-                    )
+            )
+        except AttributeError as e:
+            raise ValueError(_("Invalid result: {}").format(e))
+        try:
+            return {
+                jid.JID(s["jid"]): s["subscription"]
+                for s in subscriptions_elt.elements(
+                    (pubsub.NS_PUBSUB, "subscription")
                 )
-
-        d = request.send(client.xmlstream)
-        d.addCallback(cb)
-        return d
+            }
+        except KeyError:
+            raise ValueError(
+                _("Invalid result: bad <subscription> element: {}").format(
+                    iq_elt.toXml
+                )
+            )
 
     def _setNodeSubscriptions(
         self, service_s, nodeIdentifier, subscriptions, profile_key=C.PROF_KEY_NONE