comparison 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
comparison
equal deleted inserted replaced
3757:5bda9d2e8b35 3758:b7cef1b24f83
270 ) 270 )
271 host.bridge.addMethod( 271 host.bridge.addMethod(
272 "psSubscriptionsGet", 272 "psSubscriptionsGet",
273 ".plugin", 273 ".plugin",
274 in_sign="sss", 274 in_sign="sss",
275 out_sign="aa{ss}", 275 out_sign="s",
276 method=self._subscriptions, 276 method=self._subscriptions,
277 async_=True, 277 async_=True,
278 ) 278 )
279 host.bridge.addMethod( 279 host.bridge.addMethod(
280 "psSubscribeToMany", 280 "psSubscribeToMany",
1202 sub_jid or client.jid.userhostJID(), 1202 sub_jid or client.jid.userhostJID(),
1203 subscriptionIdentifier, 1203 subscriptionIdentifier,
1204 sender, 1204 sender,
1205 ) 1205 )
1206 1206
1207 def _subscriptions(self, service, nodeIdentifier="", profile_key=C.PROF_KEY_NONE): 1207 @utils.ensure_deferred
1208 async def _subscriptions(
1209 self,
1210 service="",
1211 nodeIdentifier="",
1212 profile_key=C.PROF_KEY_NONE
1213 ) -> str:
1208 client = self.host.getClient(profile_key) 1214 client = self.host.getClient(profile_key)
1209 service = None if not service else jid.JID(service) 1215 service = None if not service else jid.JID(service)
1210 1216 subs = await self.subscriptions(client, service, nodeIdentifier or None)
1211 def gotSubscriptions(subscriptions): 1217 return data_format.serialise(subs)
1212 # we replace pubsub.Subscription instance by dict that we can serialize 1218
1213 for idx, sub in enumerate(subscriptions): 1219 async def subscriptions(
1214 sub_dict = { 1220 self,
1215 "node": sub.nodeIdentifier, 1221 client: SatXMPPEntity,
1216 "subscriber": sub.subscriber.full(), 1222 service: Optional[jid.JID] = None,
1217 "state": sub.state, 1223 node: Optional[str] = None
1218 } 1224 ) -> List[Dict[str, Union[str, bool]]]:
1219 if sub.subscriptionIdentifier is not None: 1225 """Retrieve subscriptions from a service
1220 sub_dict["id"] = sub.subscriptionIdentifier
1221 subscriptions[idx] = sub_dict
1222
1223 return subscriptions
1224
1225 d = self.subscriptions(client, service, nodeIdentifier or None)
1226 d.addCallback(gotSubscriptions)
1227 return d
1228
1229 def subscriptions(self, client, service, nodeIdentifier=None):
1230 """retrieve subscriptions from a service
1231 1226
1232 @param service(jid.JID): PubSub service 1227 @param service(jid.JID): PubSub service
1233 @param nodeIdentifier(unicode, None): node to check 1228 @param nodeIdentifier(unicode, None): node to check
1234 None to get all subscriptions 1229 None to get all subscriptions
1235 """ 1230 """
1236 return client.pubsub_client.subscriptions(service, nodeIdentifier) 1231 cont, ret = await self.host.trigger.asyncReturnPoint(
1232 "XEP-0060_subscriptions", client, service, node
1233 )
1234 if not cont:
1235 return ret
1236 subs = await client.pubsub_client.subscriptions(service, node)
1237 ret = []
1238 for sub in subs:
1239 sub_dict = {
1240 "service": service.host if service else client.jid.host,
1241 "node": sub.nodeIdentifier,
1242 "subscriber": sub.subscriber.full(),
1243 "state": sub.state,
1244 }
1245 if sub.subscriptionIdentifier is not None:
1246 sub_dict["id"] = sub.subscriptionIdentifier
1247 ret.append(sub_dict)
1248 return ret
1237 1249
1238 ## misc tools ## 1250 ## misc tools ##
1239 1251
1240 def getNodeURI(self, service, node, item=None): 1252 def getNodeURI(self, service, node, item=None):
1241 """Return XMPP URI of a PubSub node 1253 """Return XMPP URI of a PubSub node
1323 for success, result in results 1335 for success, result in results
1324 ] 1336 ]
1325 1337
1326 # subscribe # 1338 # subscribe #
1327 1339
1328 def _getNodeSubscriptions(self, service_s, nodeIdentifier, profile_key): 1340 @utils.ensure_deferred
1329 client = self.host.getClient(profile_key) 1341 async def _getNodeSubscriptions(
1330 d = self.getNodeSubscriptions( 1342 self,
1331 client, jid.JID(service_s) if service_s else None, nodeIdentifier 1343 service: str,
1332 ) 1344 node: str,
1333 d.addCallback( 1345 profile_key: str
1334 lambda subscriptions: {j.full(): a for j, a in subscriptions.items()} 1346 ) -> Dict[str, str]:
1335 ) 1347 client = self.host.getClient(profile_key)
1336 return d 1348 subs = await self.getNodeSubscriptions(
1337 1349 client, jid.JID(service) if service else None, node
1338 def getNodeSubscriptions(self, client, service, nodeIdentifier): 1350 )
1351 return {j.full(): a for j, a in subs.items()}
1352
1353 async def getNodeSubscriptions(
1354 self,
1355 client: SatXMPPEntity,
1356 service: Optional[jid.JID],
1357 nodeIdentifier: str
1358 ) -> Dict[jid.JID, str]:
1339 """Retrieve subscriptions to a node 1359 """Retrieve subscriptions to a node
1340 1360
1341 @param nodeIdentifier(unicode): node to get subscriptions from 1361 @param nodeIdentifier(unicode): node to get subscriptions from
1342 """ 1362 """
1343 if not nodeIdentifier: 1363 if not nodeIdentifier:
1344 raise exceptions.DataError("node identifier can't be empty") 1364 raise exceptions.DataError("node identifier can't be empty")
1345 request = pubsub.PubSubRequest("subscriptionsGet") 1365 request = pubsub.PubSubRequest("subscriptionsGet")
1346 request.recipient = service 1366 request.recipient = service
1347 request.nodeIdentifier = nodeIdentifier 1367 request.nodeIdentifier = nodeIdentifier
1348 1368
1349 def cb(iq_elt): 1369 iq_elt = await request.send(client.xmlstream)
1350 try: 1370 try:
1351 subscriptions_elt = next( 1371 subscriptions_elt = next(
1352 iq_elt.pubsub.elements((pubsub.NS_PUBSUB, "subscriptions")) 1372 iq_elt.pubsub.elements((pubsub.NS_PUBSUB, "subscriptions"))
1373 )
1374 except StopIteration:
1375 raise ValueError(
1376 _("Invalid result: missing <subscriptions> element: {}").format(
1377 iq_elt.toXml
1353 ) 1378 )
1354 except StopIteration: 1379 )
1355 raise ValueError( 1380 except AttributeError as e:
1356 _("Invalid result: missing <subscriptions> element: {}").format( 1381 raise ValueError(_("Invalid result: {}").format(e))
1357 iq_elt.toXml 1382 try:
1358 ) 1383 return {
1384 jid.JID(s["jid"]): s["subscription"]
1385 for s in subscriptions_elt.elements(
1386 (pubsub.NS_PUBSUB, "subscription")
1359 ) 1387 )
1360 except AttributeError as e: 1388 }
1361 raise ValueError(_("Invalid result: {}").format(e)) 1389 except KeyError:
1362 try: 1390 raise ValueError(
1363 return { 1391 _("Invalid result: bad <subscription> element: {}").format(
1364 jid.JID(s["jid"]): s["subscription"] 1392 iq_elt.toXml
1365 for s in subscriptions_elt.elements(
1366 (pubsub.NS_PUBSUB, "subscription")
1367 )
1368 }
1369 except KeyError:
1370 raise ValueError(
1371 _("Invalid result: bad <subscription> element: {}").format(
1372 iq_elt.toXml
1373 )
1374 ) 1393 )
1375 1394 )
1376 d = request.send(client.xmlstream)
1377 d.addCallback(cb)
1378 return d
1379 1395
1380 def _setNodeSubscriptions( 1396 def _setNodeSubscriptions(
1381 self, service_s, nodeIdentifier, subscriptions, profile_key=C.PROF_KEY_NONE 1397 self, service_s, nodeIdentifier, subscriptions, profile_key=C.PROF_KEY_NONE
1382 ): 1398 ):
1383 client = self.host.getClient(profile_key) 1399 client = self.host.getClient(profile_key)