comparison libervia/backend/plugins/plugin_xep_0471.py @ 4270:0d7bb4df2343

Reformatted code base using black.
author Goffi <goffi@goffi.org>
date Wed, 19 Jun 2024 18:44:57 +0200
parents bc898879af34
children
comparison
equal deleted inserted replaced
4269:64a85ce8be70 4270:0d7bb4df2343
51 C.PI_IMPORT_NAME: "XEP-0471", 51 C.PI_IMPORT_NAME: "XEP-0471",
52 C.PI_TYPE: "XEP", 52 C.PI_TYPE: "XEP",
53 C.PI_MODES: C.PLUG_MODE_BOTH, 53 C.PI_MODES: C.PLUG_MODE_BOTH,
54 C.PI_PROTOCOLS: [], 54 C.PI_PROTOCOLS: [],
55 C.PI_DEPENDENCIES: [ 55 C.PI_DEPENDENCIES: [
56 "XEP-0060", "XEP-0080", "XEP-0447", "XEP-0470", # "INVITATION", "PUBSUB_INVITATION", 56 "XEP-0060",
57 # "LIST_INTEREST" 57 "XEP-0080",
58 "XEP-0447",
59 "XEP-0470", # "INVITATION", "PUBSUB_INVITATION",
60 # "LIST_INTEREST"
58 ], 61 ],
59 C.PI_RECOMMENDATIONS: ["XEP-0277", "EMAIL_INVITATION"], 62 C.PI_RECOMMENDATIONS: ["XEP-0277", "EMAIL_INVITATION"],
60 C.PI_MAIN: "XEP_0471", 63 C.PI_MAIN: "XEP_0471",
61 C.PI_HANDLER: "yes", 64 C.PI_HANDLER: "yes",
62 C.PI_DESCRIPTION: _("""Calendar Events"""), 65 C.PI_DESCRIPTION: _("""Calendar Events"""),
79 self._b = host.plugins.get("XEP-0277") 82 self._b = host.plugins.get("XEP-0277")
80 self._sfs = host.plugins["XEP-0447"] 83 self._sfs = host.plugins["XEP-0447"]
81 self._a = host.plugins["XEP-0470"] 84 self._a = host.plugins["XEP-0470"]
82 # self._i = host.plugins.get("EMAIL_INVITATION") 85 # self._i = host.plugins.get("EMAIL_INVITATION")
83 host.register_namespace("events", NS_EVENTS) 86 host.register_namespace("events", NS_EVENTS)
84 self._a.register_attachment_handler("rsvp", NS_EVENTS, self.rsvp_get, self.rsvp_set) 87 self._a.register_attachment_handler(
88 "rsvp", NS_EVENTS, self.rsvp_get, self.rsvp_set
89 )
85 # host.plugins["PUBSUB_INVITATION"].register(NS_EVENTS, self) 90 # host.plugins["PUBSUB_INVITATION"].register(NS_EVENTS, self)
86 host.bridge.add_method( 91 host.bridge.add_method(
87 "events_get", 92 "events_get",
88 ".plugin", 93 ".plugin",
89 in_sign="ssasss", 94 in_sign="ssasss",
302 307
303 # categories 308 # categories
304 309
305 for category_elt in event_elt.elements(NS_EVENTS, "category"): 310 for category_elt in event_elt.elements(NS_EVENTS, "category"):
306 try: 311 try:
307 category_data = { 312 category_data = {"term": category_elt["term"]}
308 "term": category_elt["term"]
309 }
310 except KeyError: 313 except KeyError:
311 log.warning( 314 log.warning(
312 "<category/> element is missing mandatory term: " 315 "<category/> element is missing mandatory term: "
313 f"{category_elt.toXml()}" 316 f"{category_elt.toXml()}"
314 ) 317 )
358 361
359 for name in ("invitees", "comments", "blog", "schedule"): 362 for name in ("invitees", "comments", "blog", "schedule"):
360 elt = next(event_elt.elements(NS_EVENTS, name), None) 363 elt = next(event_elt.elements(NS_EVENTS, name), None)
361 if elt is not None: 364 if elt is not None:
362 try: 365 try:
363 event_data[name] = { 366 event_data[name] = {"service": elt["jid"], "node": elt["node"]}
364 "service": elt["jid"],
365 "node": elt["node"]
366 }
367 except KeyError: 367 except KeyError:
368 log.warning(f"invalid {name} element: {elt.toXml()}") 368 log.warning(f"invalid {name} element: {elt.toXml()}")
369 369
370 # attachments 370 # attachments
371 371
372 attachments_elt = next(event_elt.elements(NS_EVENTS, "attachments"), None) 372 attachments_elt = next(event_elt.elements(NS_EVENTS, "attachments"), None)
373 if attachments_elt: 373 if attachments_elt:
374 attachments = event_data["attachments"] = [] 374 attachments = event_data["attachments"] = []
375 for file_sharing_elt in attachments_elt.elements( 375 for file_sharing_elt in attachments_elt.elements(
376 self._sfs.namespace, "file-sharing"): 376 self._sfs.namespace, "file-sharing"
377 ):
377 try: 378 try:
378 file_sharing_data = self._sfs.parse_file_sharing_elt(file_sharing_elt) 379 file_sharing_data = self._sfs.parse_file_sharing_elt(file_sharing_elt)
379 except Exception as e: 380 except Exception as e:
380 log.warning(f"invalid attachment: {e}\n{file_sharing_elt.toXml()}") 381 log.warning(f"invalid attachment: {e}\n{file_sharing_elt.toXml()}")
381 continue 382 continue
407 if external_elt: 408 if external_elt:
408 try: 409 try:
409 event_data["external"] = { 410 event_data["external"] = {
410 "jid": external_elt["jid"], 411 "jid": external_elt["jid"],
411 "node": external_elt["node"], 412 "node": external_elt["node"],
412 "item": external_elt["item"] 413 "item": external_elt["item"],
413 } 414 }
414 except KeyError: 415 except KeyError:
415 log.warning(f"invalid <external/> element: {external_elt.toXml()}") 416 log.warning(f"invalid <external/> element: {external_elt.toXml()}")
416 417
417 return event_data 418 return event_data
418 419
419 def _events_get( 420 def _events_get(
420 self, service: str, node: str, event_ids: List[str], extra: str, profile_key: str 421 self, service: str, node: str, event_ids: List[str], extra: str, profile_key: str
421 ): 422 ):
422 client = self.host.get_client(profile_key) 423 client = self.host.get_client(profile_key)
423 d = defer.ensureDeferred( 424 d = defer.ensureDeferred(
424 self.events_get( 425 self.events_get(
425 client, 426 client,
426 jid.JID(service) if service else None, 427 jid.JID(service) if service else None,
427 node if node else NS_EVENTS, 428 node if node else NS_EVENTS,
428 event_ids, 429 event_ids,
429 data_format.deserialise(extra) 430 data_format.deserialise(extra),
430 ) 431 )
431 ) 432 )
432 d.addCallback(data_format.serialise) 433 d.addCallback(data_format.serialise)
433 return d 434 return d
434 435
455 events = [] 456 events = []
456 for item in items: 457 for item in items:
457 try: 458 try:
458 events.append(self.event_elt_2_event_data((item))) 459 events.append(self.event_elt_2_event_data((item)))
459 except (ValueError, exceptions.NotFound): 460 except (ValueError, exceptions.NotFound):
460 log.warning( 461 log.warning(f"Can't parse event for item {item['id']}: {item.toXml()}")
461 f"Can't parse event for item {item['id']}: {item.toXml()}"
462 )
463 462
464 return events 463 return events
465 464
466 def _event_create( 465 def _event_create(
467 self, 466 self,
468 data_s: str, 467 data_s: str,
469 service: str, 468 service: str,
470 node: str, 469 node: str,
471 event_id: str = "", 470 event_id: str = "",
472 profile_key: str = C.PROF_KEY_NONE 471 profile_key: str = C.PROF_KEY_NONE,
473 ): 472 ):
474 client = self.host.get_client(profile_key) 473 client = self.host.get_client(profile_key)
475 return defer.ensureDeferred( 474 return defer.ensureDeferred(
476 self.event_create( 475 self.event_create(
477 client, 476 client,
478 data_format.deserialise(data_s), 477 data_format.deserialise(data_s),
479 jid.JID(service) if service else None, 478 jid.JID(service) if service else None,
480 node or None, 479 node or None,
481 event_id or None 480 event_id or None,
482 ) 481 )
483 ) 482 )
484 483
485 def event_data_2_event_elt(self, event_data: Dict[str, Any]) -> domish.Element: 484 def event_data_2_event_elt(self, event_data: Dict[str, Any]) -> domish.Element:
486 """Convert Event Data to corresponding Element 485 """Convert Event Data to corresponding Element
688 rsvp_elt["xml:lang"] = lang 687 rsvp_elt["xml:lang"] = lang
689 if not rsvp_data.get("no_default", False): 688 if not rsvp_data.get("no_default", False):
690 try: 689 try:
691 next(f for f in rsvp_data["fields"] if f["name"] == "attending") 690 next(f for f in rsvp_data["fields"] if f["name"] == "attending")
692 except StopIteration: 691 except StopIteration:
693 rsvp_data["fields"].append({ 692 rsvp_data["fields"].append(
694 "type": "list-single", 693 {
695 "name": "attending", 694 "type": "list-single",
696 "label": "Attending", 695 "name": "attending",
697 "options": [ 696 "label": "Attending",
698 {"label": "maybe", "value": "maybe"}, 697 "options": [
699 {"label": "yes", "value": "yes"}, 698 {"label": "maybe", "value": "maybe"},
700 {"label": "no", "value": "no"} 699 {"label": "yes", "value": "yes"},
701 ], 700 {"label": "no", "value": "no"},
702 "required": True 701 ],
703 }) 702 "required": True,
703 }
704 )
704 rsvp_data["namespace"] = NS_RSVP 705 rsvp_data["namespace"] = NS_RSVP
705 rsvp_form = xml_tools.data_dict_2_data_form(rsvp_data) 706 rsvp_form = xml_tools.data_dict_2_data_form(rsvp_data)
706 rsvp_elt.addChild(rsvp_form.toElement()) 707 rsvp_elt.addChild(rsvp_form.toElement())
707 708
708 for node_type in ("invitees", "comments", "blog", "schedule"): 709 for node_type in ("invitees", "comments", "blog", "schedule"):
726 self._sfs.get_file_sharing_elt(**attachment_data) 727 self._sfs.get_file_sharing_elt(**attachment_data)
727 ) 728 )
728 729
729 extra = event_data.get("extra") 730 extra = event_data.get("extra")
730 if extra: 731 if extra:
731 extra_form = data_form.Form( 732 extra_form = data_form.Form("result", formNamespace=NS_EXTRA)
732 "result",
733 formNamespace=NS_EXTRA
734 )
735 for node_type in ("website", "status"): 733 for node_type in ("website", "status"):
736 if node_type in extra: 734 if node_type in extra:
737 extra_form.addField( 735 extra_form.addField(
738 data_form.Field(var=node_type, value=extra[node_type]) 736 data_form.Field(var=node_type, value=extra[node_type])
739 ) 737 )
801 self, 799 self,
802 data_s: str, 800 data_s: str,
803 event_id: str, 801 event_id: str,
804 service: str, 802 service: str,
805 node: str, 803 node: str,
806 profile_key: str = C.PROF_KEY_NONE 804 profile_key: str = C.PROF_KEY_NONE,
807 ) -> None: 805 ) -> None:
808 client = self.host.get_client(profile_key) 806 client = self.host.get_client(profile_key)
809 defer.ensureDeferred( 807 defer.ensureDeferred(
810 self.event_modify( 808 self.event_modify(
811 client, 809 client,
844 attachments_elt: domish.Element, 842 attachments_elt: domish.Element,
845 data: Dict[str, Any], 843 data: Dict[str, Any],
846 ) -> None: 844 ) -> None:
847 """Get RSVP answers from attachments""" 845 """Get RSVP answers from attachments"""
848 try: 846 try:
849 rsvp_elt = next( 847 rsvp_elt = next(attachments_elt.elements(NS_EVENTS, "rsvp"))
850 attachments_elt.elements(NS_EVENTS, "rsvp")
851 )
852 except StopIteration: 848 except StopIteration:
853 pass 849 pass
854 else: 850 else:
855 rsvp_form = data_form.findForm(rsvp_elt, NS_RSVP) 851 rsvp_form = data_form.findForm(rsvp_elt, NS_RSVP)
856 if rsvp_form is not None: 852 if rsvp_form is not None:
859 855
860 def rsvp_set( 856 def rsvp_set(
861 self, 857 self,
862 client: SatXMPPEntity, 858 client: SatXMPPEntity,
863 data: Dict[str, Any], 859 data: Dict[str, Any],
864 former_elt: Optional[domish.Element] 860 former_elt: Optional[domish.Element],
865 ) -> Optional[domish.Element]: 861 ) -> Optional[domish.Element]:
866 """update the <reaction> attachment""" 862 """update the <reaction> attachment"""
867 rsvp_data = data["extra"].get("rsvp") 863 rsvp_data = data["extra"].get("rsvp")
868 if rsvp_data is None: 864 if rsvp_data is None:
869 return former_elt 865 return former_elt
870 elif rsvp_data: 866 elif rsvp_data:
871 rsvp_elt = domish.Element( 867 rsvp_elt = domish.Element(
872 (NS_EVENTS, "rsvp"), 868 (NS_EVENTS, "rsvp"), attribs={"timestamp": utils.xmpp_date()}
873 attribs = {
874 "timestamp": utils.xmpp_date()
875 }
876 ) 869 )
877 rsvp_form = data_form.Form("submit", formNamespace=NS_RSVP) 870 rsvp_form = data_form.Form("submit", formNamespace=NS_RSVP)
878 rsvp_form.makeFields(rsvp_data) 871 rsvp_form.makeFields(rsvp_data)
879 rsvp_elt.addChild(rsvp_form.toElement()) 872 rsvp_elt.addChild(rsvp_form.toElement())
880 return rsvp_elt 873 return rsvp_elt
886 service: str, 879 service: str,
887 node: str, 880 node: str,
888 item: str, 881 item: str,
889 invitees: List[str], 882 invitees: List[str],
890 extra: str, 883 extra: str,
891 profile_key: str 884 profile_key: str,
892 ) -> defer.Deferred: 885 ) -> defer.Deferred:
893 client = self.host.get_client(profile_key) 886 client = self.host.get_client(profile_key)
894 if invitees: 887 if invitees:
895 invitees_jid = [jid.JID(i) for i in invitees] 888 invitees_jid = [jid.JID(i) for i in invitees]
896 else: 889 else:
900 client, 893 client,
901 jid.JID(service) if service else None, 894 jid.JID(service) if service else None,
902 node or None, 895 node or None,
903 item, 896 item,
904 invitees_jid, 897 invitees_jid,
905 data_format.deserialise(extra) 898 data_format.deserialise(extra),
906 ) 899 )
907 ) 900 )
908 d.addCallback(lambda ret: data_format.serialise(ret)) 901 d.addCallback(lambda ret: data_format.serialise(ret))
909 return d 902 return d
910 903
943 ret[attachment["from"]] = rsvp 936 ret[attachment["from"]] = rsvp
944 937
945 return ret 938 return ret
946 939
947 def _event_invitee_set( 940 def _event_invitee_set(
948 self, 941 self, service: str, node: str, item: str, rsvp_s: str, profile_key: str
949 service: str,
950 node: str,
951 item: str,
952 rsvp_s: str,
953 profile_key: str
954 ): 942 ):
955 client = self.host.get_client(profile_key) 943 client = self.host.get_client(profile_key)
956 return defer.ensureDeferred( 944 return defer.ensureDeferred(
957 self.event_invitee_set( 945 self.event_invitee_set(
958 client, 946 client,
959 jid.JID(service) if service else None, 947 jid.JID(service) if service else None,
960 node or None, 948 node or None,
961 item, 949 item,
962 data_format.deserialise(rsvp_s) 950 data_format.deserialise(rsvp_s),
963 ) 951 )
964 ) 952 )
965 953
966 async def event_invitee_set( 954 async def event_invitee_set(
967 self, 955 self,
980 """ 968 """
981 if service is None: 969 if service is None:
982 service = client.jid.userhostJID() 970 service = client.jid.userhostJID()
983 if node is None: 971 if node is None:
984 node = NS_EVENTS 972 node = NS_EVENTS
985 await self._a.set_attachements(client, { 973 await self._a.set_attachements(
986 "service": service.full(), 974 client,
987 "node": node, 975 {
988 "id": item, 976 "service": service.full(),
989 "extra": {"rsvp": rsvp} 977 "node": node,
990 }) 978 "id": item,
979 "extra": {"rsvp": rsvp},
980 },
981 )
991 982
992 def _event_invitees_list(self, service, node, profile_key): 983 def _event_invitees_list(self, service, node, profile_key):
993 service = jid.JID(service) if service else None 984 service = jid.JID(service) if service else None
994 node = node if node else NS_EVENT 985 node = node if node else NS_EVENT
995 client = self.host.get_client(profile_key) 986 client = self.host.get_client(profile_key)
996 return defer.ensureDeferred( 987 return defer.ensureDeferred(self.event_invitees_list(client, service, node))
997 self.event_invitees_list(client, service, node)
998 )
999 988
1000 async def event_invitees_list(self, client, service, node): 989 async def event_invitees_list(self, client, service, node):
1001 """Retrieve attendance from event node 990 """Retrieve attendance from event node
1002 991
1003 @param service(unicode, None): PubSub service 992 @param service(unicode, None): PubSub service
1010 for item in items: 999 for item in items:
1011 try: 1000 try:
1012 event_elt = next(item.elements(NS_EVENT, "invitee")) 1001 event_elt = next(item.elements(NS_EVENT, "invitee"))
1013 except StopIteration: 1002 except StopIteration:
1014 # no item found, event data are not set yet 1003 # no item found, event data are not set yet
1015 log.warning(_( 1004 log.warning(
1016 "no data found for {item_id} (service: {service}, node: {node})" 1005 _(
1017 .format(item_id=item["id"], service=service, node=node))) 1006 "no data found for {item_id} (service: {service}, node: {node})".format(
1007 item_id=item["id"], service=service, node=node
1008 )
1009 )
1010 )
1018 else: 1011 else:
1019 data = {} 1012 data = {}
1020 for key in ("attend", "guests"): 1013 for key in ("attend", "guests"):
1021 try: 1014 try:
1022 data[key] = event_elt[key] 1015 data[key] = event_elt[key]
1030 client: SatXMPPEntity, 1023 client: SatXMPPEntity,
1031 invitee_jid: jid.JID, 1024 invitee_jid: jid.JID,
1032 service: jid.JID, 1025 service: jid.JID,
1033 node: str, 1026 node: str,
1034 item_id: Optional[str] = None, 1027 item_id: Optional[str] = None,
1035 name: str = '', 1028 name: str = "",
1036 extra: Optional[dict] = None, 1029 extra: Optional[dict] = None,
1037 ) -> None: 1030 ) -> None:
1038 if self._b is None: 1031 if self._b is None:
1039 raise exceptions.FeatureNotFound( 1032 raise exceptions.FeatureNotFound(
1040 _('"XEP-0277" (blog) plugin is needed for this feature') 1033 _('"XEP-0277" (blog) plugin is needed for this feature')
1064 try: 1057 try:
1065 comments_service = jid.JID(item["comments_service"]) 1058 comments_service = jid.JID(item["comments_service"])
1066 comments_node = item["comments_node"] 1059 comments_node = item["comments_node"]
1067 except KeyError: 1060 except KeyError:
1068 log.debug( 1061 log.debug(
1069 "no comment service set for item {item_id}".format( 1062 "no comment service set for item {item_id}".format(item_id=item["id"])
1070 item_id=item["id"]
1071 )
1072 ) 1063 )
1073 else: 1064 else:
1074 await self._p.set_node_affiliations( 1065 await self._p.set_node_affiliations(
1075 client, comments_service, comments_node, {invitee_jid: "publisher"} 1066 client, comments_service, comments_node, {invitee_jid: "publisher"}
1076 ) 1067 )
1079 def _invite(self, invitee_jid, service, node, item_id, profile): 1070 def _invite(self, invitee_jid, service, node, item_id, profile):
1080 return self.host.plugins["PUBSUB_INVITATION"]._send_pubsub_invitation( 1071 return self.host.plugins["PUBSUB_INVITATION"]._send_pubsub_invitation(
1081 invitee_jid, service, node, item_id or NS_EVENT, profile_key=profile 1072 invitee_jid, service, node, item_id or NS_EVENT, profile_key=profile
1082 ) 1073 )
1083 1074
1084 def _invite_by_email(self, service, node, id_=NS_EVENT, email="", emails_extra=None, 1075 def _invite_by_email(
1085 name="", host_name="", language="", url_template="", 1076 self,
1086 message_subject="", message_body="", 1077 service,
1087 profile_key=C.PROF_KEY_NONE): 1078 node,
1079 id_=NS_EVENT,
1080 email="",
1081 emails_extra=None,
1082 name="",
1083 host_name="",
1084 language="",
1085 url_template="",
1086 message_subject="",
1087 message_body="",
1088 profile_key=C.PROF_KEY_NONE,
1089 ):
1088 client = self.host.get_client(profile_key) 1090 client = self.host.get_client(profile_key)
1089 kwargs = { 1091 kwargs = {
1090 "profile": client.profile, 1092 "profile": client.profile,
1091 "emails_extra": [str(e) for e in emails_extra], 1093 "emails_extra": [str(e) for e in emails_extra],
1092 } 1094 }
1099 "message_subject", 1101 "message_subject",
1100 "message_body", 1102 "message_body",
1101 ): 1103 ):
1102 value = locals()[key] 1104 value = locals()[key]
1103 kwargs[key] = str(value) 1105 kwargs[key] = str(value)
1104 return defer.ensureDeferred(self.invite_by_email( 1106 return defer.ensureDeferred(
1105 client, jid.JID(service) if service else None, node, id_ or NS_EVENT, **kwargs 1107 self.invite_by_email(
1106 )) 1108 client,
1109 jid.JID(service) if service else None,
1110 node,
1111 id_ or NS_EVENT,
1112 **kwargs,
1113 )
1114 )
1107 1115
1108 async def invite_by_email(self, client, service, node, id_=NS_EVENT, **kwargs): 1116 async def invite_by_email(self, client, service, node, id_=NS_EVENT, **kwargs):
1109 """High level method to create an email invitation to an event 1117 """High level method to create an email invitation to an event
1110 1118
1111 @param service(unicode, None): PubSub service 1119 @param service(unicode, None): PubSub service
1137 name: str, 1145 name: str,
1138 extra: dict, 1146 extra: dict,
1139 service: jid.JID, 1147 service: jid.JID,
1140 node: str, 1148 node: str,
1141 item_id: Optional[str], 1149 item_id: Optional[str],
1142 item_elt: domish.Element 1150 item_elt: domish.Element,
1143 ) -> None: 1151 ) -> None:
1144 event_elt = item_elt.event 1152 event_elt = item_elt.event
1145 link_elt = event_elt.addElement("link") 1153 link_elt = event_elt.addElement("link")
1146 link_elt["service"] = service.full() 1154 link_elt["service"] = service.full()
1147 link_elt["node"] = node 1155 link_elt["node"] = node
1151 name = event_data["name"] 1159 name = event_data["name"]
1152 except KeyError: 1160 except KeyError:
1153 pass 1161 pass
1154 else: 1162 else:
1155 extra["name"] = name 1163 extra["name"] = name
1156 if 'image' in event_data: 1164 if "image" in event_data:
1157 extra["thumb_url"] = event_data['image'] 1165 extra["thumb_url"] = event_data["image"]
1158 extra["element"] = event_elt 1166 extra["element"] = event_elt
1159 1167
1160 1168
1161 @implementer(iwokkel.IDisco) 1169 @implementer(iwokkel.IDisco)
1162 class EventsHandler(XMPPHandler): 1170 class EventsHandler(XMPPHandler):