Mercurial > libervia-backend
comparison sat/plugins/plugin_xep_0470.py @ 4037:524856bd7b19
massive refactoring to switch from camelCase to snake_case:
historically, Libervia (SàT before) was using camelCase as allowed by PEP8 when using a
pre-PEP8 code, to use the same coding style as in Twisted.
However, snake_case is more readable and it's better to follow PEP8 best practices, so it
has been decided to move on full snake_case. Because Libervia has a huge codebase, this
ended with a ugly mix of camelCase and snake_case.
To fix that, this patch does a big refactoring by renaming every function and method
(including bridge) that are not coming from Twisted or Wokkel, to use fully snake_case.
This is a massive change, and may result in some bugs.
author | Goffi <goffi@goffi.org> |
---|---|
date | Sat, 08 Apr 2023 13:54:42 +0200 |
parents | 722c25818778 |
children |
comparison
equal
deleted
inserted
replaced
4036:c4464d7ae97b | 4037:524856bd7b19 |
---|---|
28 from sat.core.i18n import _ | 28 from sat.core.i18n import _ |
29 from sat.core.log import getLogger | 29 from sat.core.log import getLogger |
30 from sat.core.core_types import SatXMPPEntity | 30 from sat.core.core_types import SatXMPPEntity |
31 from sat.core import exceptions | 31 from sat.core import exceptions |
32 from sat.tools.common import uri, data_format, date_utils | 32 from sat.tools.common import uri, data_format, date_utils |
33 from sat.tools.utils import asDeferred, xmpp_date | 33 from sat.tools.utils import as_deferred, xmpp_date |
34 | 34 |
35 | 35 |
36 log = getLogger(__name__) | 36 log = getLogger(__name__) |
37 | 37 |
38 IMPORT_NAME = "XEP-0470" | 38 IMPORT_NAME = "XEP-0470" |
56 class PubsubAttachments: | 56 class PubsubAttachments: |
57 namespace = NS_PUBSUB_ATTACHMENTS | 57 namespace = NS_PUBSUB_ATTACHMENTS |
58 | 58 |
59 def __init__(self, host): | 59 def __init__(self, host): |
60 log.info(_("XEP-0470 (Pubsub Attachments) plugin initialization")) | 60 log.info(_("XEP-0470 (Pubsub Attachments) plugin initialization")) |
61 host.registerNamespace("pubsub-attachments", NS_PUBSUB_ATTACHMENTS) | 61 host.register_namespace("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: Dict[Tuple[str, str], dict[str, Any]] = {} | 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.on_mb_send) |
66 self.register_attachment_handler( | 66 self.register_attachment_handler( |
67 "noticed", NS_PUBSUB_ATTACHMENTS, self.noticedGet, self.noticedSet | 67 "noticed", NS_PUBSUB_ATTACHMENTS, self.noticed_get, self.noticed_set |
68 ) | 68 ) |
69 self.register_attachment_handler( | 69 self.register_attachment_handler( |
70 "reactions", NS_PUBSUB_ATTACHMENTS, self.reactionsGet, self.reactionsSet | 70 "reactions", NS_PUBSUB_ATTACHMENTS, self.reactions_get, self.reactions_set |
71 ) | 71 ) |
72 host.bridge.addMethod( | 72 host.bridge.add_method( |
73 "psAttachmentsGet", | 73 "ps_attachments_get", |
74 ".plugin", | 74 ".plugin", |
75 in_sign="sssasss", | 75 in_sign="sssasss", |
76 out_sign="(ss)", | 76 out_sign="(ss)", |
77 method=self._get, | 77 method=self._get, |
78 async_=True, | 78 async_=True, |
79 ) | 79 ) |
80 host.bridge.addMethod( | 80 host.bridge.add_method( |
81 "psAttachmentsSet", | 81 "ps_attachments_set", |
82 ".plugin", | 82 ".plugin", |
83 in_sign="ss", | 83 in_sign="ss", |
84 out_sign="", | 84 out_sign="", |
85 method=self._set, | 85 method=self._set, |
86 async_=True, | 86 async_=True, |
87 ) | 87 ) |
88 | 88 |
89 def getHandler(self, client): | 89 def get_handler(self, client): |
90 return PubsubAttachments_Handler() | 90 return PubsubAttachments_Handler() |
91 | 91 |
92 def register_attachment_handler( | 92 def register_attachment_handler( |
93 self, | 93 self, |
94 name: str, | 94 name: str, |
123 self.handlers[(name, namespace)] = { | 123 self.handlers[(name, namespace)] = { |
124 "get": get_cb, | 124 "get": get_cb, |
125 "set": set_cb | 125 "set": set_cb |
126 } | 126 } |
127 | 127 |
128 def getAttachmentNodeName(self, service: jid.JID, node: str, item: str) -> str: | 128 def get_attachment_node_name(self, service: jid.JID, node: str, item: str) -> str: |
129 """Generate name to use for attachment node""" | 129 """Generate name to use for attachment node""" |
130 target_item_uri = uri.buildXMPPUri( | 130 target_item_uri = uri.build_xmpp_uri( |
131 "pubsub", | 131 "pubsub", |
132 path=service.userhost(), | 132 path=service.userhost(), |
133 node=node, | 133 node=node, |
134 item=item | 134 item=item |
135 ) | 135 ) |
136 return f"{NS_PUBSUB_ATTACHMENTS}/{target_item_uri}" | 136 return f"{NS_PUBSUB_ATTACHMENTS}/{target_item_uri}" |
137 | 137 |
138 def isAttachmentNode(self, node: str) -> bool: | 138 def is_attachment_node(self, node: str) -> bool: |
139 """Return True if node name is an attachment node""" | 139 """Return True if node name is an attachment node""" |
140 return node.startswith(f"{NS_PUBSUB_ATTACHMENTS}/") | 140 return node.startswith(f"{NS_PUBSUB_ATTACHMENTS}/") |
141 | 141 |
142 def attachmentNode2Item(self, node: str) -> Tuple[jid.JID, str, str]: | 142 def attachment_node_2_item(self, node: str) -> Tuple[jid.JID, str, str]: |
143 """Retrieve service, node and item from attachement node's name""" | 143 """Retrieve service, node and item from attachement node's name""" |
144 if not self.isAttachmentNode(node): | 144 if not self.is_attachment_node(node): |
145 raise ValueError("this is not an attachment node!") | 145 raise ValueError("this is not an attachment node!") |
146 prefix_len = len(f"{NS_PUBSUB_ATTACHMENTS}/") | 146 prefix_len = len(f"{NS_PUBSUB_ATTACHMENTS}/") |
147 item_uri = node[prefix_len:] | 147 item_uri = node[prefix_len:] |
148 parsed_uri = uri.parseXMPPUri(item_uri) | 148 parsed_uri = uri.parse_xmpp_uri(item_uri) |
149 if parsed_uri["type"] != "pubsub": | 149 if parsed_uri["type"] != "pubsub": |
150 raise ValueError(f"unexpected URI type, it must be a pubsub URI: {item_uri}") | 150 raise ValueError(f"unexpected URI type, it must be a pubsub URI: {item_uri}") |
151 try: | 151 try: |
152 service = jid.JID(parsed_uri["path"]) | 152 service = jid.JID(parsed_uri["path"]) |
153 except RuntimeError: | 153 except RuntimeError: |
154 raise ValueError(f"invalid service in pubsub URI: {item_uri}") | 154 raise ValueError(f"invalid service in pubsub URI: {item_uri}") |
155 node = parsed_uri["node"] | 155 node = parsed_uri["node"] |
156 item = parsed_uri["item"] | 156 item = parsed_uri["item"] |
157 return (service, node, item) | 157 return (service, node, item) |
158 | 158 |
159 async def onMBSend( | 159 async def on_mb_send( |
160 self, | 160 self, |
161 client: SatXMPPEntity, | 161 client: SatXMPPEntity, |
162 service: jid.JID, | 162 service: jid.JID, |
163 node: str, | 163 node: str, |
164 item: domish.Element, | 164 item: domish.Element, |
201 try: | 201 try: |
202 # FIXME: check if this is the best publish_model option | 202 # FIXME: check if this is the best publish_model option |
203 node_config.fields["pubsub#publish_model"].value = "open" | 203 node_config.fields["pubsub#publish_model"].value = "open" |
204 except KeyError: | 204 except KeyError: |
205 log.warning("pubsub#publish_model field is missing") | 205 log.warning("pubsub#publish_model field is missing") |
206 attachment_node = self.getAttachmentNodeName(service, node, item_id) | 206 attachment_node = self.get_attachment_node_name(service, node, item_id) |
207 # we use the same options as target node | 207 # we use the same options as target node |
208 try: | 208 try: |
209 await self._p.createIfNewNode( | 209 await self._p.create_if_new_node( |
210 client, service, attachment_node, options=dict(node_config) | 210 client, service, attachment_node, options=dict(node_config) |
211 ) | 211 ) |
212 except Exception as e: | 212 except Exception as e: |
213 log.warning(f"Can't create attachment node {attachment_node}: {e}") | 213 log.warning(f"Can't create attachment node {attachment_node}: {e}") |
214 | 214 |
215 def items2attachmentData( | 215 def items_2_attachment_data( |
216 self, | 216 self, |
217 client: SatXMPPEntity, | 217 client: SatXMPPEntity, |
218 items: List[domish.Element] | 218 items: List[domish.Element] |
219 ) -> List[Dict[str, Any]]: | 219 ) -> List[Dict[str, Any]]: |
220 """Convert items from attachment node to attachment data""" | 220 """Convert items from attachment node to attachment data""" |
265 item: str, | 265 item: str, |
266 senders_s: List[str], | 266 senders_s: List[str], |
267 extra_s: str, | 267 extra_s: str, |
268 profile_key: str | 268 profile_key: str |
269 ) -> defer.Deferred: | 269 ) -> defer.Deferred: |
270 client = self.host.getClient(profile_key) | 270 client = self.host.get_client(profile_key) |
271 extra = data_format.deserialise(extra_s) | 271 extra = data_format.deserialise(extra_s) |
272 senders = [jid.JID(s) for s in senders_s] | 272 senders = [jid.JID(s) for s in senders_s] |
273 d = defer.ensureDeferred( | 273 d = defer.ensureDeferred( |
274 self.getAttachments(client, jid.JID(service_s), node, item, senders) | 274 self.get_attachments(client, jid.JID(service_s), node, item, senders) |
275 ) | 275 ) |
276 d.addCallback( | 276 d.addCallback( |
277 lambda ret: | 277 lambda ret: |
278 (data_format.serialise(ret[0]), | 278 (data_format.serialise(ret[0]), |
279 data_format.serialise(ret[1])) | 279 data_format.serialise(ret[1])) |
280 ) | 280 ) |
281 return d | 281 return d |
282 | 282 |
283 async def getAttachments( | 283 async def get_attachments( |
284 self, | 284 self, |
285 client: SatXMPPEntity, | 285 client: SatXMPPEntity, |
286 service: jid.JID, | 286 service: jid.JID, |
287 node: str, | 287 node: str, |
288 item: str, | 288 item: str, |
296 @param item: ID of the item for which attachments will be retrieved | 296 @param item: ID of the item for which attachments will be retrieved |
297 @param senders: bare JIDs of entities that are checked. Attachments from those | 297 @param senders: bare JIDs of entities that are checked. Attachments from those |
298 entities will be retrieved. | 298 entities will be retrieved. |
299 If None, attachments from all entities will be retrieved | 299 If None, attachments from all entities will be retrieved |
300 @param extra: extra data, will be used as ``extra`` argument when doing | 300 @param extra: extra data, will be used as ``extra`` argument when doing |
301 ``getItems`` call. | 301 ``get_items`` call. |
302 @return: A tuple with: | 302 @return: A tuple with: |
303 - the list of attachments data, one item per found sender. The attachments | 303 - the list of attachments data, one item per found sender. The attachments |
304 data are dict containing attachment, no ``extra`` field is used here | 304 data are dict containing attachment, no ``extra`` field is used here |
305 (contrarily to attachments data used with ``set_attachements``). | 305 (contrarily to attachments data used with ``set_attachements``). |
306 - metadata returned by the call to ``getItems`` | 306 - metadata returned by the call to ``get_items`` |
307 """ | 307 """ |
308 if extra is None: | 308 if extra is None: |
309 extra = {} | 309 extra = {} |
310 attachment_node = self.getAttachmentNodeName(service, node, item) | 310 attachment_node = self.get_attachment_node_name(service, node, item) |
311 item_ids = [e.userhost() for e in senders] if senders else None | 311 item_ids = [e.userhost() for e in senders] if senders else None |
312 items, metadata = await self._p.getItems( | 312 items, metadata = await self._p.get_items( |
313 client, service, attachment_node, item_ids=item_ids, extra=extra | 313 client, service, attachment_node, item_ids=item_ids, extra=extra |
314 ) | 314 ) |
315 list_data = self.items2attachmentData(client, items) | 315 list_data = self.items_2_attachment_data(client, items) |
316 | 316 |
317 return list_data, metadata | 317 return list_data, metadata |
318 | 318 |
319 def _set( | 319 def _set( |
320 self, | 320 self, |
321 attachments_s: str, | 321 attachments_s: str, |
322 profile_key: str | 322 profile_key: str |
323 ) -> None: | 323 ) -> None: |
324 client = self.host.getClient(profile_key) | 324 client = self.host.get_client(profile_key) |
325 attachments = data_format.deserialise(attachments_s) or {} | 325 attachments = data_format.deserialise(attachments_s) or {} |
326 return defer.ensureDeferred(self.set_attachements(client, attachments)) | 326 return defer.ensureDeferred(self.set_attachements(client, attachments)) |
327 | 327 |
328 async def apply_set_handler( | 328 async def apply_set_handler( |
329 self, | 329 self, |
376 continue | 376 continue |
377 try: | 377 try: |
378 former_elt = next(attachments_elt.elements(namespace, name)) | 378 former_elt = next(attachments_elt.elements(namespace, name)) |
379 except StopIteration: | 379 except StopIteration: |
380 former_elt = None | 380 former_elt = None |
381 new_elt = await asDeferred( | 381 new_elt = await as_deferred( |
382 handler["set"], client, attachments_data, former_elt | 382 handler["set"], client, attachments_data, former_elt |
383 ) | 383 ) |
384 if new_elt != former_elt: | 384 if new_elt != former_elt: |
385 if former_elt is not None: | 385 if former_elt is not None: |
386 attachments_elt.children.remove(former_elt) | 386 attachments_elt.children.remove(former_elt) |
415 item = attachments_data["id"] | 415 item = attachments_data["id"] |
416 except (KeyError, RuntimeError): | 416 except (KeyError, RuntimeError): |
417 raise ValueError( | 417 raise ValueError( |
418 'data must have "service", "node" and "id" set' | 418 'data must have "service", "node" and "id" set' |
419 ) | 419 ) |
420 attachment_node = self.getAttachmentNodeName(service, node, item) | 420 attachment_node = self.get_attachment_node_name(service, node, item) |
421 try: | 421 try: |
422 items, __ = await self._p.getItems( | 422 items, __ = await self._p.get_items( |
423 client, service, attachment_node, item_ids=[client.jid.userhost()] | 423 client, service, attachment_node, item_ids=[client.jid.userhost()] |
424 ) | 424 ) |
425 except exceptions.NotFound: | 425 except exceptions.NotFound: |
426 item_elt = None | 426 item_elt = None |
427 else: | 427 else: |
435 attachments_data, | 435 attachments_data, |
436 item_elt=item_elt, | 436 item_elt=item_elt, |
437 ) | 437 ) |
438 | 438 |
439 try: | 439 try: |
440 await self._p.sendItems(client, service, attachment_node, [item_elt]) | 440 await self._p.send_items(client, service, attachment_node, [item_elt]) |
441 except error.StanzaError as e: | 441 except error.StanzaError as e: |
442 if e.condition == "item-not-found": | 442 if e.condition == "item-not-found": |
443 # the node doesn't exist, we can't publish attachments | 443 # the node doesn't exist, we can't publish attachments |
444 log.warning( | 444 log.warning( |
445 f"no attachment node found at {service} on {node!r} for item " | 445 f"no attachment node found at {service} on {node!r} for item " |
460 | 460 |
461 @param service: service of target item (will also be used for attachment node) | 461 @param service: service of target item (will also be used for attachment node) |
462 @param node: node of target item (used to get attachment node's name) | 462 @param node: node of target item (used to get attachment node's name) |
463 @param item: name of target item (used to get attachment node's name) | 463 @param item: name of target item (used to get attachment node's name) |
464 """ | 464 """ |
465 attachment_node = self.getAttachmentNodeName(service, node, item) | 465 attachment_node = self.get_attachment_node_name(service, node, item) |
466 await self._p.subscribe(client, service, attachment_node) | 466 await self._p.subscribe(client, service, attachment_node) |
467 | 467 |
468 | 468 |
469 def setTimestamp(self, attachment_elt: domish.Element, data: dict) -> None: | 469 def set_timestamp(self, attachment_elt: domish.Element, data: dict) -> None: |
470 """Check if a ``timestamp`` attribute is set, parse it, and fill data | 470 """Check if a ``timestamp`` attribute is set, parse it, and fill data |
471 | 471 |
472 @param attachments_elt: element where the ``timestamp`` attribute may be set | 472 @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) | 473 @param data: data specific to the attachment (i.e. not the whole microblog data) |
474 ``timestamp`` field will be set there if timestamp exists and is parsable | 474 ``timestamp`` field will be set there if timestamp exists and is parsable |
480 except date_utils.ParserError: | 480 except date_utils.ParserError: |
481 log.warning(f"can't parse timestamp: {timestamp_raw}") | 481 log.warning(f"can't parse timestamp: {timestamp_raw}") |
482 else: | 482 else: |
483 data["timestamp"] = timestamp | 483 data["timestamp"] = timestamp |
484 | 484 |
485 def noticedGet( | 485 def noticed_get( |
486 self, | 486 self, |
487 client: SatXMPPEntity, | 487 client: SatXMPPEntity, |
488 attachments_elt: domish.Element, | 488 attachments_elt: domish.Element, |
489 data: Dict[str, Any], | 489 data: Dict[str, Any], |
490 ) -> None: | 490 ) -> None: |
496 pass | 496 pass |
497 else: | 497 else: |
498 noticed_data = { | 498 noticed_data = { |
499 "noticed": True | 499 "noticed": True |
500 } | 500 } |
501 self.setTimestamp(noticed_elt, noticed_data) | 501 self.set_timestamp(noticed_elt, noticed_data) |
502 data["noticed"] = noticed_data | 502 data["noticed"] = noticed_data |
503 | 503 |
504 def noticedSet( | 504 def noticed_set( |
505 self, | 505 self, |
506 client: SatXMPPEntity, | 506 client: SatXMPPEntity, |
507 data: Dict[str, Any], | 507 data: Dict[str, Any], |
508 former_elt: Optional[domish.Element] | 508 former_elt: Optional[domish.Element] |
509 ) -> Optional[domish.Element]: | 509 ) -> Optional[domish.Element]: |
523 } | 523 } |
524 ) | 524 ) |
525 else: | 525 else: |
526 return None | 526 return None |
527 | 527 |
528 def reactionsGet( | 528 def reactions_get( |
529 self, | 529 self, |
530 client: SatXMPPEntity, | 530 client: SatXMPPEntity, |
531 attachments_elt: domish.Element, | 531 attachments_elt: domish.Element, |
532 data: Dict[str, Any], | 532 data: Dict[str, Any], |
533 ) -> None: | 533 ) -> None: |
540 else: | 540 else: |
541 reactions_data = {"reactions": []} | 541 reactions_data = {"reactions": []} |
542 reactions = reactions_data["reactions"] | 542 reactions = reactions_data["reactions"] |
543 for reaction_elt in reactions_elt.elements(NS_PUBSUB_ATTACHMENTS, "reaction"): | 543 for reaction_elt in reactions_elt.elements(NS_PUBSUB_ATTACHMENTS, "reaction"): |
544 reactions.append(str(reaction_elt)) | 544 reactions.append(str(reaction_elt)) |
545 self.setTimestamp(reactions_elt, reactions_data) | 545 self.set_timestamp(reactions_elt, reactions_data) |
546 data["reactions"] = reactions_data | 546 data["reactions"] = reactions_data |
547 | 547 |
548 def reactionsSet( | 548 def reactions_set( |
549 self, | 549 self, |
550 client: SatXMPPEntity, | 550 client: SatXMPPEntity, |
551 data: Dict[str, Any], | 551 data: Dict[str, Any], |
552 former_elt: Optional[domish.Element] | 552 former_elt: Optional[domish.Element] |
553 ) -> Optional[domish.Element]: | 553 ) -> Optional[domish.Element]: |