Mercurial > libervia-backend
comparison libervia/backend/plugins/plugin_xep_0470.py @ 4270:0d7bb4df2343
Reformatted code base using black.
author | Goffi <goffi@goffi.org> |
---|---|
date | Wed, 19 Jun 2024 18:44:57 +0200 |
parents | 2109d864a3e7 |
children |
comparison
equal
deleted
inserted
replaced
4269:64a85ce8be70 | 4270:0d7bb4df2343 |
---|---|
91 | 91 |
92 def register_attachment_handler( | 92 def register_attachment_handler( |
93 self, | 93 self, |
94 name: str, | 94 name: str, |
95 namespace: str, | 95 namespace: str, |
96 get_cb: Callable[ | 96 get_cb: Callable[[SatXMPPEntity, domish.Element, Dict[str, Any]], None], |
97 [SatXMPPEntity, domish.Element, Dict[str, Any]], | |
98 None], | |
99 set_cb: Callable[ | 97 set_cb: Callable[ |
100 [SatXMPPEntity, Dict[str, Any], Optional[domish.Element]], | 98 [SatXMPPEntity, Dict[str, Any], Optional[domish.Element]], |
101 Optional[domish.Element]], | 99 Optional[domish.Element], |
100 ], | |
102 ) -> None: | 101 ) -> None: |
103 """Register callbacks to handle an attachment | 102 """Register callbacks to handle an attachment |
104 | 103 |
105 @param name: name of the element | 104 @param name: name of the element |
106 @param namespace: namespace of the element | 105 @param namespace: namespace of the element |
118 key = (name, namespace) | 117 key = (name, namespace) |
119 if key in self.handlers: | 118 if key in self.handlers: |
120 raise exceptions.ConflictError( | 119 raise exceptions.ConflictError( |
121 f"({name}, {namespace}) attachment handlers are already registered" | 120 f"({name}, {namespace}) attachment handlers are already registered" |
122 ) | 121 ) |
123 self.handlers[(name, namespace)] = { | 122 self.handlers[(name, namespace)] = {"get": get_cb, "set": set_cb} |
124 "get": get_cb, | |
125 "set": set_cb | |
126 } | |
127 | 123 |
128 def get_attachment_node_name(self, service: jid.JID, node: str, item: str) -> str: | 124 def get_attachment_node_name(self, service: jid.JID, node: str, item: str) -> str: |
129 """Generate name to use for attachment node""" | 125 """Generate name to use for attachment node""" |
130 target_item_uri = uri.build_xmpp_uri( | 126 target_item_uri = uri.build_xmpp_uri( |
131 "pubsub", | 127 "pubsub", path=service.userhost(), node=node, item=item |
132 path=service.userhost(), | |
133 node=node, | |
134 item=item | |
135 ) | 128 ) |
136 return f"{NS_PUBSUB_ATTACHMENTS}/{target_item_uri}" | 129 return f"{NS_PUBSUB_ATTACHMENTS}/{target_item_uri}" |
137 | 130 |
138 def is_attachment_node(self, node: str) -> bool: | 131 def is_attachment_node(self, node: str) -> bool: |
139 """Return True if node name is an attachment node""" | 132 """Return True if node name is an attachment node""" |
160 self, | 153 self, |
161 client: SatXMPPEntity, | 154 client: SatXMPPEntity, |
162 service: jid.JID, | 155 service: jid.JID, |
163 node: str, | 156 node: str, |
164 item: domish.Element, | 157 item: domish.Element, |
165 data: dict | 158 data: dict, |
166 ) -> bool: | 159 ) -> bool: |
167 """trigger to create attachment node on each publication""" | 160 """trigger to create attachment node on each publication""" |
168 await self.create_attachments_node( | 161 await self.create_attachments_node( |
169 client, service, node, item["id"], autocreate=True | 162 client, service, node, item["id"], autocreate=True |
170 ) | 163 ) |
174 self, | 167 self, |
175 client: SatXMPPEntity, | 168 client: SatXMPPEntity, |
176 service: jid.JID, | 169 service: jid.JID, |
177 node: str, | 170 node: str, |
178 item_id: str, | 171 item_id: str, |
179 autocreate: bool = False | 172 autocreate: bool = False, |
180 ): | 173 ): |
181 """Create node for attachements if necessary | 174 """Create node for attachements if necessary |
182 | 175 |
183 @param service: service of target node | 176 @param service: service of target node |
184 @param node: node where target item is published | 177 @param node: node where target item is published |
188 try: | 181 try: |
189 node_config = await self._p.getConfiguration(client, service, node) | 182 node_config = await self._p.getConfiguration(client, service, node) |
190 except error.StanzaError as e: | 183 except error.StanzaError as e: |
191 if e.condition == "item-not-found" and autocreate: | 184 if e.condition == "item-not-found" and autocreate: |
192 # we auto-create the missing node | 185 # we auto-create the missing node |
193 await self._p.createNode( | 186 await self._p.createNode(client, service, node) |
194 client, service, node | |
195 ) | |
196 node_config = await self._p.getConfiguration(client, service, node) | 187 node_config = await self._p.getConfiguration(client, service, node) |
197 elif e.condition in ("forbidden", "feature-not-implemented"): | 188 elif e.condition in ("forbidden", "feature-not-implemented"): |
198 node_config = self._p.make_configuration_form({}) | 189 node_config = self._p.make_configuration_form({}) |
199 else: | 190 else: |
200 raise e | 191 raise e |
211 ) | 202 ) |
212 except Exception as e: | 203 except Exception as e: |
213 log.warning(f"Can't create attachment node {attachment_node}: {e}") | 204 log.warning(f"Can't create attachment node {attachment_node}: {e}") |
214 | 205 |
215 def items_2_attachment_data( | 206 def items_2_attachment_data( |
216 self, | 207 self, client: SatXMPPEntity, items: List[domish.Element] |
217 client: SatXMPPEntity, | |
218 items: List[domish.Element] | |
219 ) -> List[Dict[str, Any]]: | 208 ) -> List[Dict[str, Any]]: |
220 """Convert items from attachment node to attachment data""" | 209 """Convert items from attachment node to attachment data""" |
221 list_data = [] | 210 list_data = [] |
222 for item in items: | 211 for item in items: |
223 try: | 212 try: |
247 log.warning( | 236 log.warning( |
248 "item ID is not a JID, this is not compliant and is ignored: " | 237 "item ID is not a JID, this is not compliant and is ignored: " |
249 f"{item.toXml}" | 238 f"{item.toXml}" |
250 ) | 239 ) |
251 continue | 240 continue |
252 data = { | 241 data = {"from": item_id} |
253 "from": item_id | |
254 } | |
255 for handler in self.handlers.values(): | 242 for handler in self.handlers.values(): |
256 handler["get"](client, attachments_elt, data) | 243 handler["get"](client, attachments_elt, data) |
257 if len(data) > 1: | 244 if len(data) > 1: |
258 list_data.append(data) | 245 list_data.append(data) |
259 return list_data | 246 return list_data |
263 service_s: str, | 250 service_s: str, |
264 node: str, | 251 node: str, |
265 item: str, | 252 item: str, |
266 senders_s: List[str], | 253 senders_s: List[str], |
267 extra_s: str, | 254 extra_s: str, |
268 profile_key: str | 255 profile_key: str, |
269 ) -> defer.Deferred: | 256 ) -> defer.Deferred: |
270 client = self.host.get_client(profile_key) | 257 client = self.host.get_client(profile_key) |
271 extra = data_format.deserialise(extra_s) | 258 extra = data_format.deserialise(extra_s) |
272 senders = [jid.JID(s) for s in senders_s] | 259 senders = [jid.JID(s) for s in senders_s] |
273 d = defer.ensureDeferred( | 260 d = defer.ensureDeferred( |
274 self.get_attachments(client, jid.JID(service_s), node, item, senders) | 261 self.get_attachments(client, jid.JID(service_s), node, item, senders) |
275 ) | 262 ) |
276 d.addCallback( | 263 d.addCallback( |
277 lambda ret: | 264 lambda ret: (data_format.serialise(ret[0]), data_format.serialise(ret[1])) |
278 (data_format.serialise(ret[0]), | |
279 data_format.serialise(ret[1])) | |
280 ) | 265 ) |
281 return d | 266 return d |
282 | 267 |
283 async def get_attachments( | 268 async def get_attachments( |
284 self, | 269 self, |
285 client: SatXMPPEntity, | 270 client: SatXMPPEntity, |
286 service: jid.JID, | 271 service: jid.JID, |
287 node: str, | 272 node: str, |
288 item: str, | 273 item: str, |
289 senders: Optional[List[jid.JID]], | 274 senders: Optional[List[jid.JID]], |
290 extra: Optional[dict] = None | 275 extra: Optional[dict] = None, |
291 ) -> Tuple[List[Dict[str, Any]], dict]: | 276 ) -> Tuple[List[Dict[str, Any]], dict]: |
292 """Retrieve data attached to a pubsub item | 277 """Retrieve data attached to a pubsub item |
293 | 278 |
294 @param service: pubsub service where the node is | 279 @param service: pubsub service where the node is |
295 @param node: pubsub node containing the item | 280 @param node: pubsub node containing the item |
314 ) | 299 ) |
315 list_data = self.items_2_attachment_data(client, items) | 300 list_data = self.items_2_attachment_data(client, items) |
316 | 301 |
317 return list_data, metadata | 302 return list_data, metadata |
318 | 303 |
319 def _set( | 304 def _set(self, attachments_s: str, profile_key: str) -> None: |
320 self, | |
321 attachments_s: str, | |
322 profile_key: str | |
323 ) -> None: | |
324 client = self.host.get_client(profile_key) | 305 client = self.host.get_client(profile_key) |
325 attachments = data_format.deserialise(attachments_s) or {} | 306 attachments = data_format.deserialise(attachments_s) or {} |
326 return defer.ensureDeferred(self.set_attachements(client, attachments)) | 307 return defer.ensureDeferred(self.set_attachements(client, attachments)) |
327 | 308 |
328 async def apply_set_handler( | 309 async def apply_set_handler( |
329 self, | 310 self, |
330 client: SatXMPPEntity, | 311 client: SatXMPPEntity, |
387 if new_elt is not None: | 368 if new_elt is not None: |
388 attachments_elt.addChild(new_elt) | 369 attachments_elt.addChild(new_elt) |
389 return item_elt | 370 return item_elt |
390 | 371 |
391 async def set_attachements( | 372 async def set_attachements( |
392 self, | 373 self, client: SatXMPPEntity, attachments_data: Dict[str, Any] |
393 client: SatXMPPEntity, | |
394 attachments_data: Dict[str, Any] | |
395 ) -> None: | 374 ) -> None: |
396 """Set or update attachments | 375 """Set or update attachments |
397 | 376 |
398 Former <attachments> element will be retrieved and updated. Individual | 377 Former <attachments> element will be retrieved and updated. Individual |
399 attachments replace or update their elements individually, according to the | 378 attachments replace or update their elements individually, according to the |
412 try: | 391 try: |
413 service = jid.JID(attachments_data["service"]) | 392 service = jid.JID(attachments_data["service"]) |
414 node = attachments_data["node"] | 393 node = attachments_data["node"] |
415 item = attachments_data["id"] | 394 item = attachments_data["id"] |
416 except (KeyError, RuntimeError): | 395 except (KeyError, RuntimeError): |
417 raise ValueError( | 396 raise ValueError('data must have "service", "node" and "id" set') |
418 'data must have "service", "node" and "id" set' | |
419 ) | |
420 attachment_node = self.get_attachment_node_name(service, node, item) | 397 attachment_node = self.get_attachment_node_name(service, node, item) |
421 try: | 398 try: |
422 items, __ = await self._p.get_items( | 399 items, __ = await self._p.get_items( |
423 client, service, attachment_node, item_ids=[client.jid.userhost()] | 400 client, service, attachment_node, item_ids=[client.jid.userhost()] |
424 ) | 401 ) |
463 @param item: name of target item (used to get attachment node's name) | 440 @param item: name of target item (used to get attachment node's name) |
464 """ | 441 """ |
465 attachment_node = self.get_attachment_node_name(service, node, item) | 442 attachment_node = self.get_attachment_node_name(service, node, item) |
466 await self._p.subscribe(client, service, attachment_node) | 443 await self._p.subscribe(client, service, attachment_node) |
467 | 444 |
468 | |
469 def set_timestamp(self, attachment_elt: domish.Element, data: dict) -> None: | 445 def set_timestamp(self, attachment_elt: domish.Element, data: dict) -> None: |
470 """Check if a ``timestamp`` attribute is set, parse it, and fill data | 446 """Check if a ``timestamp`` attribute is set, parse it, and fill data |
471 | 447 |
472 @param attachments_elt: element where the ``timestamp`` attribute may be set | 448 @param attachments_elt: element where the ``timestamp`` attribute may be set |
473 @param data: data specific to the attachment (i.e. not the whole microblog data) | 449 @param data: data specific to the attachment (i.e. not the whole microblog data) |
487 client: SatXMPPEntity, | 463 client: SatXMPPEntity, |
488 attachments_elt: domish.Element, | 464 attachments_elt: domish.Element, |
489 data: Dict[str, Any], | 465 data: Dict[str, Any], |
490 ) -> None: | 466 ) -> None: |
491 try: | 467 try: |
492 noticed_elt = next( | 468 noticed_elt = next(attachments_elt.elements(NS_PUBSUB_ATTACHMENTS, "noticed")) |
493 attachments_elt.elements(NS_PUBSUB_ATTACHMENTS, "noticed") | |
494 ) | |
495 except StopIteration: | 469 except StopIteration: |
496 pass | 470 pass |
497 else: | 471 else: |
498 noticed_data = { | 472 noticed_data = {"noticed": True} |
499 "noticed": True | |
500 } | |
501 self.set_timestamp(noticed_elt, noticed_data) | 473 self.set_timestamp(noticed_elt, noticed_data) |
502 data["noticed"] = noticed_data | 474 data["noticed"] = noticed_data |
503 | 475 |
504 def noticed_set( | 476 def noticed_set( |
505 self, | 477 self, |
506 client: SatXMPPEntity, | 478 client: SatXMPPEntity, |
507 data: Dict[str, Any], | 479 data: Dict[str, Any], |
508 former_elt: Optional[domish.Element] | 480 former_elt: Optional[domish.Element], |
509 ) -> Optional[domish.Element]: | 481 ) -> Optional[domish.Element]: |
510 """add or remove a <noticed> attachment | 482 """add or remove a <noticed> attachment |
511 | 483 |
512 if data["noticed"] is True, element is added, if it's False, it's removed, and | 484 if data["noticed"] is True, element is added, if it's False, it's removed, and |
513 it's not present or None, the former element is kept. | 485 it's not present or None, the former element is kept. |
515 noticed = data["extra"].get("noticed") | 487 noticed = data["extra"].get("noticed") |
516 if noticed is None: | 488 if noticed is None: |
517 return former_elt | 489 return former_elt |
518 elif noticed: | 490 elif noticed: |
519 return domish.Element( | 491 return domish.Element( |
520 (NS_PUBSUB_ATTACHMENTS, "noticed"), | 492 (NS_PUBSUB_ATTACHMENTS, "noticed"), attribs={"timestamp": xmpp_date()} |
521 attribs = { | |
522 "timestamp": xmpp_date() | |
523 } | |
524 ) | 493 ) |
525 else: | 494 else: |
526 return None | 495 return None |
527 | 496 |
528 def reactions_get( | 497 def reactions_get( |
547 | 516 |
548 def reactions_set( | 517 def reactions_set( |
549 self, | 518 self, |
550 client: SatXMPPEntity, | 519 client: SatXMPPEntity, |
551 data: Dict[str, Any], | 520 data: Dict[str, Any], |
552 former_elt: Optional[domish.Element] | 521 former_elt: Optional[domish.Element], |
553 ) -> Optional[domish.Element]: | 522 ) -> Optional[domish.Element]: |
554 """update the <reaction> attachment""" | 523 """update the <reaction> attachment""" |
555 reactions_data = data["extra"].get("reactions") | 524 reactions_data = data["extra"].get("reactions") |
556 if reactions_data is None: | 525 if reactions_data is None: |
557 return former_elt | 526 return former_elt |
558 operation_type = reactions_data.get("operation", "update") | 527 operation_type = reactions_data.get("operation", "update") |
559 if operation_type == "update": | 528 if operation_type == "update": |
560 former_reactions = { | 529 former_reactions = ( |
561 str(r) for r in former_elt.elements(NS_PUBSUB_ATTACHMENTS, "reaction") | 530 {str(r) for r in former_elt.elements(NS_PUBSUB_ATTACHMENTS, "reaction")} |
562 } if former_elt is not None else set() | 531 if former_elt is not None |
532 else set() | |
533 ) | |
563 added_reactions = set(reactions_data.get("add") or []) | 534 added_reactions = set(reactions_data.get("add") or []) |
564 removed_reactions = set(reactions_data.get("remove") or []) | 535 removed_reactions = set(reactions_data.get("remove") or []) |
565 reactions = list((former_reactions | added_reactions) - removed_reactions) | 536 reactions = list((former_reactions | added_reactions) - removed_reactions) |
566 elif operation_type == "replace": | 537 elif operation_type == "replace": |
567 reactions = reactions_data.get("reactions") or [] | 538 reactions = reactions_data.get("reactions") or [] |
568 else: | 539 else: |
569 raise exceptions.DataError(f"invalid reaction operation: {operation_type!r}") | 540 raise exceptions.DataError(f"invalid reaction operation: {operation_type!r}") |
570 if reactions: | 541 if reactions: |
571 reactions_elt = domish.Element( | 542 reactions_elt = domish.Element( |
572 (NS_PUBSUB_ATTACHMENTS, "reactions"), | 543 (NS_PUBSUB_ATTACHMENTS, "reactions"), attribs={"timestamp": xmpp_date()} |
573 attribs = { | |
574 "timestamp": xmpp_date() | |
575 } | |
576 ) | 544 ) |
577 for reactions_data in reactions: | 545 for reactions_data in reactions: |
578 reactions_elt.addElement("reaction", content=reactions_data) | 546 reactions_elt.addElement("reaction", content=reactions_data) |
579 return reactions_elt | 547 return reactions_elt |
580 else: | 548 else: |