comparison sat/plugins/plugin_pubsub_attachments.py @ 3869:c0bcbcf5b4b7

component AP gateway: handle `Like` and `Undo`/`Like` activities: rel 370
author Goffi <goffi@goffi.org>
date Thu, 21 Jul 2022 18:07:35 +0200
parents ac255a0fbd4c
children
comparison
equal deleted inserted replaced
3868:37d2c0282304 3869:c0bcbcf5b4b7
59 def __init__(self, host): 59 def __init__(self, host):
60 log.info(_("Pubsub Attachments plugin initialization")) 60 log.info(_("Pubsub Attachments plugin initialization"))
61 host.registerNamespace("pubsub-attachments", NS_PUBSUB_ATTACHMENTS) 61 host.registerNamespace("pubsub-attachments", NS_PUBSUB_ATTACHMENTS)
62 self.host = host 62 self.host = host
63 self._p = host.plugins["XEP-0060"] 63 self._p = host.plugins["XEP-0060"]
64 self.handlers = {} 64 self.handlers: Dict[Tuple[str, str], dict[str, Any]] = {}
65 host.trigger.add("XEP-0277_send", self.onMBSend) 65 host.trigger.add("XEP-0277_send", self.onMBSend)
66 self.registerAttachmentHandler( 66 self.registerAttachmentHandler(
67 "noticed", NS_PUBSUB_ATTACHMENTS, self.noticedGet, self.noticedSet 67 "noticed", NS_PUBSUB_ATTACHMENTS, self.noticedGet, self.noticedSet
68 ) 68 )
69 self.registerAttachmentHandler( 69 self.registerAttachmentHandler(
268 attachments_s: str, 268 attachments_s: str,
269 profile_key: str 269 profile_key: str
270 ) -> None: 270 ) -> None:
271 client = self.host.getClient(profile_key) 271 client = self.host.getClient(profile_key)
272 attachments = data_format.deserialise(attachments_s) or {} 272 attachments = data_format.deserialise(attachments_s) or {}
273 return defer.ensureDeferred(self.setAttachments( client, attachments)) 273 return defer.ensureDeferred(self.setAttachments(client, attachments))
274 274
275 async def setAttachments( 275 def applySetHandler(
276 self, 276 self,
277 client: SatXMPPEntity, 277 client: SatXMPPEntity,
278 data: Dict[str, Any] 278 attachments_data: dict,
279 ) -> None: 279 item_elt: Optional[domish.Element],
280 """Set or update attachments 280 handlers: Optional[List[Tuple[str, str]]] = None,
281 281 from_jid: Optional[jid.JID] = None,
282 Former <attachments> element will be retrieved and updated. Individual 282 ) -> domish.Element:
283 attachments replace or update their elements individually, according to the 283 """Apply all ``set`` callbacks to an attachments item
284 "operation" key. 284
285 285 @param attachments_data: data describing the attachments
286 "operation" key may be "update" or "replace", and defaults to update, it is only 286 ``extra`` key will be used, and created if not found
287 used in attachments where "update" makes sense (e.g. it's used for "reactions" 287 @param from_jid: jid of the author of the attachments
288 but not for "noticed"). 288 ``client.jid.userhostJID()`` will be used if not specified
289 289 @param item_elt: item containing an <attachments> element
290 @param data: microblog data data. Various keys (usually stored in 290 will be modified in place
291 data["extra"]) may be used depending on the attachments handlers 291 if None, a new element will be created
292 registered. The keys "service", "node" and "id" MUST be set. 292 @param handlers: list of (name, namespace) of handlers to use.
293 if None, all registered handlers will be used.
294 @return: updated item_elt if given, otherwise a new item_elt
293 """ 295 """
294 data.setdefault("extra", {}) 296 attachments_data.setdefault("extra", {})
295 try: 297 if item_elt is None:
296 service = jid.JID(data["service"]) 298 item_id = client.jid.userhost() if from_jid is None else from_jid.userhost()
297 node = data["node"] 299 item_elt = pubsub.Item(item_id)
298 item = data["id"]
299 except (KeyError, RuntimeError):
300 raise ValueError(
301 'data must have "service", "node" and "id" set'
302 )
303 attachment_node = self.getAttachmentNodeName(service, node, item)
304 items, __ = await self._p.getItems(
305 client, service, attachment_node, item_ids=[client.jid.userhost()]
306 )
307 if not items:
308 # the item doesn't exist, we create a new one
309 item_elt = pubsub.Item(client.jid.userhost())
310 item_elt.addElement((NS_PUBSUB_ATTACHMENTS, "attachments")) 300 item_elt.addElement((NS_PUBSUB_ATTACHMENTS, "attachments"))
311 else:
312 item_elt = items[0]
313 301
314 try: 302 try:
315 attachments_elt = next( 303 attachments_elt = next(
316 item_elt.elements(NS_PUBSUB_ATTACHMENTS, "attachments") 304 item_elt.elements(NS_PUBSUB_ATTACHMENTS, "attachments")
317 ) 305 )
319 log.warning( 307 log.warning(
320 f"no <attachments> element found, creating a new one: {item_elt.toXml()}" 308 f"no <attachments> element found, creating a new one: {item_elt.toXml()}"
321 ) 309 )
322 attachments_elt = item_elt.addElement((NS_PUBSUB_ATTACHMENTS, "attachments")) 310 attachments_elt = item_elt.addElement((NS_PUBSUB_ATTACHMENTS, "attachments"))
323 311
324 for (name, namespace), handler in self.handlers.items(): 312 if handlers is None:
313 handlers = list(self.handlers.keys())
314
315 for name, namespace in handlers:
316 try:
317 handler = self.handlers[(name, namespace)]
318 except KeyError:
319 log.error(
320 f"unregistered handler ({name!r}, {namespace!r}) is requested, "
321 "ignoring"
322 )
323 continue
325 try: 324 try:
326 former_elt = next(attachments_elt.elements(namespace, name)) 325 former_elt = next(attachments_elt.elements(namespace, name))
327 except StopIteration: 326 except StopIteration:
328 former_elt = None 327 former_elt = None
329 new_elt = handler["set"](client, data, former_elt) 328 new_elt = handler["set"](client, attachments_data, former_elt)
330 if new_elt != former_elt: 329 if new_elt != former_elt:
331 if former_elt is not None: 330 if former_elt is not None:
332 attachments_elt.children.remove(former_elt) 331 attachments_elt.children.remove(former_elt)
333 if new_elt is not None: 332 if new_elt is not None:
334 attachments_elt.addChild(new_elt) 333 attachments_elt.addChild(new_elt)
334 return item_elt
335
336 async def setAttachments(
337 self,
338 client: SatXMPPEntity,
339 attachments_data: Dict[str, Any]
340 ) -> None:
341 """Set or update attachments
342
343 Former <attachments> element will be retrieved and updated. Individual
344 attachments replace or update their elements individually, according to the
345 "operation" key.
346
347 "operation" key may be "update" or "replace", and defaults to update, it is only
348 used in attachments where "update" makes sense (e.g. it's used for "reactions"
349 but not for "noticed").
350
351 @param attachments_data: data describing attachments. Various keys (usually stored
352 in attachments_data["extra"]) may be used depending on the attachments
353 handlers registered. The keys "service", "node" and "id" MUST be set.
354 ``attachments_data`` is thought to be compatible with microblog data.
355
356 """
357 try:
358 service = jid.JID(attachments_data["service"])
359 node = attachments_data["node"]
360 item = attachments_data["id"]
361 except (KeyError, RuntimeError):
362 raise ValueError(
363 'data must have "service", "node" and "id" set'
364 )
365 attachment_node = self.getAttachmentNodeName(service, node, item)
366 try:
367 items, __ = await self._p.getItems(
368 client, service, attachment_node, item_ids=[client.jid.userhost()]
369 )
370 except exceptions.NotFound:
371 item_elt = None
372 else:
373 if not items:
374 item_elt = None
375 else:
376 item_elt = items[0]
377
378 item_elt = self.applySetHandler(
379 client,
380 attachments_data,
381 item_elt=item_elt,
382 )
383
335 try: 384 try:
336 await self._p.sendItems(client, service, attachment_node, [item_elt]) 385 await self._p.sendItems(client, service, attachment_node, [item_elt])
337 except error.StanzaError as e: 386 except error.StanzaError as e:
338 if e.condition == "item-not-found": 387 if e.condition == "item-not-found":
339 # the node doesn't exist, we can't publish attachments 388 # the node doesn't exist, we can't publish attachments