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

Reformatted code base using black.
author Goffi <goffi@goffi.org>
date Wed, 19 Jun 2024 18:44:57 +0200
parents 4b842c1fb686
children
comparison
equal deleted inserted replaced
4269:64a85ce8be70 4270:0d7bb4df2343
36 36
37 PLUGIN_INFO = { 37 PLUGIN_INFO = {
38 C.PI_NAME: "Email Invitations", 38 C.PI_NAME: "Email Invitations",
39 C.PI_IMPORT_NAME: "EMAIL_INVITATION", 39 C.PI_IMPORT_NAME: "EMAIL_INVITATION",
40 C.PI_TYPE: C.PLUG_TYPE_MISC, 40 C.PI_TYPE: C.PLUG_TYPE_MISC,
41 C.PI_DEPENDENCIES: ['XEP-0077'], 41 C.PI_DEPENDENCIES: ["XEP-0077"],
42 C.PI_RECOMMENDATIONS: ["IDENTITY"], 42 C.PI_RECOMMENDATIONS: ["IDENTITY"],
43 C.PI_MAIN: "InvitationsPlugin", 43 C.PI_MAIN: "InvitationsPlugin",
44 C.PI_HANDLER: "no", 44 C.PI_HANDLER: "no",
45 C.PI_DESCRIPTION: _("""invitation of people without XMPP account""") 45 C.PI_DESCRIPTION: _("""invitation of people without XMPP account"""),
46 } 46 }
47 47
48 48
49 SUFFIX_MAX = 5 49 SUFFIX_MAX = 5
50 INVITEE_PROFILE_TPL = "guest@@{uuid}" 50 INVITEE_PROFILE_TPL = "guest@@{uuid}"
51 KEY_ID = 'id' 51 KEY_ID = "id"
52 KEY_JID = 'jid' 52 KEY_JID = "jid"
53 KEY_CREATED = 'created' 53 KEY_CREATED = "created"
54 KEY_LAST_CONNECTION = 'last_connection' 54 KEY_LAST_CONNECTION = "last_connection"
55 KEY_GUEST_PROFILE = 'guest_profile' 55 KEY_GUEST_PROFILE = "guest_profile"
56 KEY_PASSWORD = 'password' 56 KEY_PASSWORD = "password"
57 KEY_EMAILS_EXTRA = 'emails_extra' 57 KEY_EMAILS_EXTRA = "emails_extra"
58 EXTRA_RESERVED = {KEY_ID, KEY_JID, KEY_CREATED, 'jid_', 'jid', KEY_LAST_CONNECTION, 58 EXTRA_RESERVED = {
59 KEY_GUEST_PROFILE, KEY_PASSWORD, KEY_EMAILS_EXTRA} 59 KEY_ID,
60 KEY_JID,
61 KEY_CREATED,
62 "jid_",
63 "jid",
64 KEY_LAST_CONNECTION,
65 KEY_GUEST_PROFILE,
66 KEY_PASSWORD,
67 KEY_EMAILS_EXTRA,
68 }
60 DEFAULT_SUBJECT = D_("You have been invited by {host_name} to {app_name}") 69 DEFAULT_SUBJECT = D_("You have been invited by {host_name} to {app_name}")
61 DEFAULT_BODY = D_("""Hello {name}! 70 DEFAULT_BODY = D_(
71 """Hello {name}!
62 72
63 You have received an invitation from {host_name} to participate to "{app_name}". 73 You have received an invitation from {host_name} to participate to "{app_name}".
64 To join, you just have to click on the following URL: 74 To join, you just have to click on the following URL:
65 {url} 75 {url}
66 76
67 Please note that this URL should not be shared with anybody! 77 Please note that this URL should not be shared with anybody!
68 If you want more details on {app_name}, you can check {app_url}. 78 If you want more details on {app_name}, you can check {app_url}.
69 79
70 Welcome! 80 Welcome!
71 """) 81 """
82 )
72 83
73 84
74 class InvitationsPlugin(object): 85 class InvitationsPlugin(object):
75 86
76 def __init__(self, host): 87 def __init__(self, host):
77 log.info(_("plugin Invitations initialization")) 88 log.info(_("plugin Invitations initialization"))
78 self.host = host 89 self.host = host
79 self.invitations = persistent.LazyPersistentBinaryDict('invitations') 90 self.invitations = persistent.LazyPersistentBinaryDict("invitations")
80 host.bridge.add_method("invitation_create", ".plugin", in_sign='sasssssssssa{ss}s', 91 host.bridge.add_method(
81 out_sign='a{ss}', 92 "invitation_create",
82 method=self._create, 93 ".plugin",
83 async_=True) 94 in_sign="sasssssssssa{ss}s",
84 host.bridge.add_method("invitation_get", ".plugin", in_sign='s', out_sign='a{ss}', 95 out_sign="a{ss}",
85 method=self.get, 96 method=self._create,
86 async_=True) 97 async_=True,
87 host.bridge.add_method("invitation_delete", ".plugin", in_sign='s', out_sign='', 98 )
88 method=self._delete, 99 host.bridge.add_method(
89 async_=True) 100 "invitation_get",
90 host.bridge.add_method("invitation_modify", ".plugin", in_sign='sa{ss}b', 101 ".plugin",
91 out_sign='', 102 in_sign="s",
92 method=self._modify, 103 out_sign="a{ss}",
93 async_=True) 104 method=self.get,
94 host.bridge.add_method("invitation_list", ".plugin", in_sign='s', 105 async_=True,
95 out_sign='a{sa{ss}}', 106 )
96 method=self._list, 107 host.bridge.add_method(
97 async_=True) 108 "invitation_delete",
98 host.bridge.add_method("invitation_simple_create", ".plugin", in_sign='sssss', 109 ".plugin",
99 out_sign='a{ss}', 110 in_sign="s",
100 method=self._simple_create, 111 out_sign="",
101 async_=True) 112 method=self._delete,
113 async_=True,
114 )
115 host.bridge.add_method(
116 "invitation_modify",
117 ".plugin",
118 in_sign="sa{ss}b",
119 out_sign="",
120 method=self._modify,
121 async_=True,
122 )
123 host.bridge.add_method(
124 "invitation_list",
125 ".plugin",
126 in_sign="s",
127 out_sign="a{sa{ss}}",
128 method=self._list,
129 async_=True,
130 )
131 host.bridge.add_method(
132 "invitation_simple_create",
133 ".plugin",
134 in_sign="sssss",
135 out_sign="a{ss}",
136 method=self._simple_create,
137 async_=True,
138 )
102 139
103 def check_extra(self, extra): 140 def check_extra(self, extra):
104 if EXTRA_RESERVED.intersection(extra): 141 if EXTRA_RESERVED.intersection(extra):
105 raise ValueError( 142 raise ValueError(
106 _("You can't use following key(s) in extra, they are reserved: {}") 143 _(
107 .format(', '.join(EXTRA_RESERVED.intersection(extra)))) 144 "You can't use following key(s) in extra, they are reserved: {}"
108 145 ).format(", ".join(EXTRA_RESERVED.intersection(extra)))
109 def _create(self, email='', emails_extra=None, jid_='', password='', name='', 146 )
110 host_name='', language='', url_template='', message_subject='', 147
111 message_body='', extra=None, profile=''): 148 def _create(
149 self,
150 email="",
151 emails_extra=None,
152 jid_="",
153 password="",
154 name="",
155 host_name="",
156 language="",
157 url_template="",
158 message_subject="",
159 message_body="",
160 extra=None,
161 profile="",
162 ):
112 # XXX: we don't use **kwargs here to keep arguments name for introspection with 163 # XXX: we don't use **kwargs here to keep arguments name for introspection with
113 # D-Bus bridge 164 # D-Bus bridge
114 if emails_extra is None: 165 if emails_extra is None:
115 emails_extra = [] 166 emails_extra = []
116 167
117 if extra is None: 168 if extra is None:
118 extra = {} 169 extra = {}
119 else: 170 else:
120 extra = {str(k): str(v) for k,v in extra.items()} 171 extra = {str(k): str(v) for k, v in extra.items()}
121 172
122 kwargs = {"extra": extra, 173 kwargs = {"extra": extra, KEY_EMAILS_EXTRA: [str(e) for e in emails_extra]}
123 KEY_EMAILS_EXTRA: [str(e) for e in emails_extra]
124 }
125 174
126 # we need to be sure that values are unicode, else they won't be pickled correctly 175 # we need to be sure that values are unicode, else they won't be pickled correctly
127 # with D-Bus 176 # with D-Bus
128 for key in ("jid_", "password", "name", "host_name", "email", "language", 177 for key in (
129 "url_template", "message_subject", "message_body", "profile"): 178 "jid_",
179 "password",
180 "name",
181 "host_name",
182 "email",
183 "language",
184 "url_template",
185 "message_subject",
186 "message_body",
187 "profile",
188 ):
130 value = locals()[key] 189 value = locals()[key]
131 if value: 190 if value:
132 kwargs[key] = str(value) 191 kwargs[key] = str(value)
133 return defer.ensureDeferred(self.create(**kwargs)) 192 return defer.ensureDeferred(self.create(**kwargs))
134 193
150 if invitation.get("email") == email: 209 if invitation.get("email") == email:
151 invitation[KEY_ID] = id_ 210 invitation[KEY_ID] = id_
152 return invitation 211 return invitation
153 212
154 async def _create_account_and_profile( 213 async def _create_account_and_profile(
155 self, 214 self, id_: str, kwargs: dict, extra: dict
156 id_: str,
157 kwargs: dict,
158 extra: dict
159 ) -> None: 215 ) -> None:
160 """Create XMPP account and Libervia profile for guest""" 216 """Create XMPP account and Libervia profile for guest"""
161 ## XMPP account creation 217 ## XMPP account creation
162 password = kwargs.pop('password', None) 218 password = kwargs.pop("password", None)
163 if password is None: 219 if password is None:
164 password = utils.generate_password() 220 password = utils.generate_password()
165 assert password 221 assert password
166 # XXX: password is here saved in clear in database 222 # XXX: password is here saved in clear in database
167 # it is needed for invitation as the same password is used for profile 223 # it is needed for invitation as the same password is used for profile
168 # and SàT need to be able to automatically open the profile with the uuid 224 # and SàT need to be able to automatically open the profile with the uuid
169 # FIXME: we could add an extra encryption key which would be used with the 225 # FIXME: we could add an extra encryption key which would be used with the
170 # uuid when the invitee is connecting (e.g. with URL). This key would 226 # uuid when the invitee is connecting (e.g. with URL). This key would
171 # not be saved and could be used to encrypt profile password. 227 # not be saved and could be used to encrypt profile password.
172 extra[KEY_PASSWORD] = password 228 extra[KEY_PASSWORD] = password
173 229
174 jid_ = kwargs.pop('jid_', None) 230 jid_ = kwargs.pop("jid_", None)
175 if not jid_: 231 if not jid_:
176 domain = self.host.memory.config_get(None, 'xmpp_domain') 232 domain = self.host.memory.config_get(None, "xmpp_domain")
177 if not domain: 233 if not domain:
178 # TODO: fallback to profile's domain 234 # TODO: fallback to profile's domain
179 raise ValueError(_("You need to specify xmpp_domain in sat.conf")) 235 raise ValueError(_("You need to specify xmpp_domain in sat.conf"))
180 jid_ = "invitation-{uuid}@{domain}".format(uuid=shortuuid.uuid(), 236 jid_ = "invitation-{uuid}@{domain}".format(
181 domain=domain) 237 uuid=shortuuid.uuid(), domain=domain
238 )
182 jid_ = jid.JID(jid_) 239 jid_ = jid.JID(jid_)
183 extra[KEY_JID] = jid_.full() 240 extra[KEY_JID] = jid_.full()
184 241
185 if jid_.user: 242 if jid_.user:
186 # we don't register account if there is no user as anonymous login is then 243 # we don't register account if there is no user as anonymous login is then
187 # used 244 # used
188 try: 245 try:
189 await self.host.plugins['XEP-0077'].register_new_account(jid_, password) 246 await self.host.plugins["XEP-0077"].register_new_account(jid_, password)
190 except error.StanzaError as e: 247 except error.StanzaError as e:
191 prefix = jid_.user 248 prefix = jid_.user
192 idx = 0 249 idx = 0
193 while e.condition == 'conflict': 250 while e.condition == "conflict":
194 if idx >= SUFFIX_MAX: 251 if idx >= SUFFIX_MAX:
195 raise exceptions.ConflictError(_("Can't create XMPP account")) 252 raise exceptions.ConflictError(_("Can't create XMPP account"))
196 jid_.user = prefix + '_' + str(idx) 253 jid_.user = prefix + "_" + str(idx)
197 log.info(_("requested jid already exists, trying with {}".format( 254 log.info(
198 jid_.full()))) 255 _(
256 "requested jid already exists, trying with {}".format(
257 jid_.full()
258 )
259 )
260 )
199 try: 261 try:
200 await self.host.plugins['XEP-0077'].register_new_account( 262 await self.host.plugins["XEP-0077"].register_new_account(
201 jid_, 263 jid_, password
202 password
203 ) 264 )
204 except error.StanzaError: 265 except error.StanzaError:
205 idx += 1 266 idx += 1
206 else: 267 else:
207 break 268 break
208 if e.condition != 'conflict': 269 if e.condition != "conflict":
209 raise e 270 raise e
210 271
211 log.info(_("account {jid_} created").format(jid_=jid_.full())) 272 log.info(_("account {jid_} created").format(jid_=jid_.full()))
212 273
213 ## profile creation 274 ## profile creation
214 275
215 extra[KEY_GUEST_PROFILE] = guest_profile = INVITEE_PROFILE_TPL.format( 276 extra[KEY_GUEST_PROFILE] = guest_profile = INVITEE_PROFILE_TPL.format(uuid=id_)
216 uuid=id_
217 )
218 # profile creation should not fail as we generate unique name ourselves 277 # profile creation should not fail as we generate unique name ourselves
219 await self.host.memory.create_profile(guest_profile, password) 278 await self.host.memory.create_profile(guest_profile, password)
220 await self.host.memory.start_session(password, guest_profile) 279 await self.host.memory.start_session(password, guest_profile)
221 await self.host.memory.param_set("JabberID", jid_.full(), "Connection", 280 await self.host.memory.param_set(
222 profile_key=guest_profile) 281 "JabberID", jid_.full(), "Connection", profile_key=guest_profile
223 await self.host.memory.param_set("Password", password, "Connection", 282 )
224 profile_key=guest_profile) 283 await self.host.memory.param_set(
284 "Password", password, "Connection", profile_key=guest_profile
285 )
225 286
226 async def create(self, **kwargs): 287 async def create(self, **kwargs):
227 r"""Create an invitation 288 r"""Create an invitation
228 289
229 This will create an XMPP account and a profile, and use a UUID to retrieve them. 290 This will create an XMPP account and a profile, and use a UUID to retrieve them.
282 @return (dict[unicode, unicode]): dictionary with: 343 @return (dict[unicode, unicode]): dictionary with:
283 - UUID associated with the invitee (key: id) 344 - UUID associated with the invitee (key: id)
284 - filled extra dictionary, as saved in the databae 345 - filled extra dictionary, as saved in the databae
285 """ 346 """
286 ## initial checks 347 ## initial checks
287 extra = kwargs.pop('extra', {}) 348 extra = kwargs.pop("extra", {})
288 if set(kwargs).intersection(extra): 349 if set(kwargs).intersection(extra):
289 raise ValueError( 350 raise ValueError(
290 _("You can't use following key(s) in both args and extra: {}").format( 351 _("You can't use following key(s) in both args and extra: {}").format(
291 ', '.join(set(kwargs).intersection(extra)))) 352 ", ".join(set(kwargs).intersection(extra))
353 )
354 )
292 355
293 self.check_extra(extra) 356 self.check_extra(extra)
294 357
295 email = kwargs.pop('email', None) 358 email = kwargs.pop("email", None)
296 359
297 existing = await self.get_existing_invitation(email) 360 existing = await self.get_existing_invitation(email)
298 if existing is not None: 361 if existing is not None:
299 log.info(f"There is already an invitation for {email!r}") 362 log.info(f"There is already an invitation for {email!r}")
300 extra.update(existing) 363 extra.update(existing)
301 del extra[KEY_ID] 364 del extra[KEY_ID]
302 365
303 emails_extra = kwargs.pop('emails_extra', []) 366 emails_extra = kwargs.pop("emails_extra", [])
304 if not email and emails_extra: 367 if not email and emails_extra:
305 raise ValueError( 368 raise ValueError(
306 _('You need to provide a main email address before using emails_extra')) 369 _("You need to provide a main email address before using emails_extra")
307 370 )
308 if (email is not None 371
309 and not 'url_template' in kwargs 372 if (
310 and not 'message_body' in kwargs): 373 email is not None
374 and not "url_template" in kwargs
375 and not "message_body" in kwargs
376 ):
311 raise ValueError( 377 raise ValueError(
312 _("You need to provide url_template if you use default message body")) 378 _("You need to provide url_template if you use default message body")
379 )
313 380
314 ## uuid 381 ## uuid
315 log.info(_("creating an invitation")) 382 log.info(_("creating an invitation"))
316 id_ = existing[KEY_ID] if existing else str(shortuuid.uuid()) 383 id_ = existing[KEY_ID] if existing else str(shortuuid.uuid())
317 384
318 if existing is None: 385 if existing is None:
319 await self._create_account_and_profile(id_, kwargs, extra) 386 await self._create_account_and_profile(id_, kwargs, extra)
320 387
321 profile = kwargs.pop('profile', None) 388 profile = kwargs.pop("profile", None)
322 guest_profile = extra[KEY_GUEST_PROFILE] 389 guest_profile = extra[KEY_GUEST_PROFILE]
323 jid_ = jid.JID(extra[KEY_JID]) 390 jid_ = jid.JID(extra[KEY_JID])
324 391
325 ## identity 392 ## identity
326 name = kwargs.pop('name', None) 393 name = kwargs.pop("name", None)
327 password = extra[KEY_PASSWORD] 394 password = extra[KEY_PASSWORD]
328 if name is not None: 395 if name is not None:
329 extra['name'] = name 396 extra["name"] = name
330 try: 397 try:
331 id_plugin = self.host.plugins['IDENTITY'] 398 id_plugin = self.host.plugins["IDENTITY"]
332 except KeyError: 399 except KeyError:
333 pass 400 pass
334 else: 401 else:
335 await self.host.connect(guest_profile, password) 402 await self.host.connect(guest_profile, password)
336 guest_client = self.host.get_client(guest_profile) 403 guest_client = self.host.get_client(guest_profile)
337 await id_plugin.set_identity(guest_client, {'nicknames': [name]}) 404 await id_plugin.set_identity(guest_client, {"nicknames": [name]})
338 await self.host.disconnect(guest_profile) 405 await self.host.disconnect(guest_profile)
339 406
340 ## email 407 ## email
341 language = kwargs.pop('language', None) 408 language = kwargs.pop("language", None)
342 if language is not None: 409 if language is not None:
343 extra['language'] = language.strip() 410 extra["language"] = language.strip()
344 411
345 if email is not None: 412 if email is not None:
346 extra['email'] = email 413 extra["email"] = email
347 data_format.iter2dict(KEY_EMAILS_EXTRA, extra) 414 data_format.iter2dict(KEY_EMAILS_EXTRA, extra)
348 url_template = kwargs.pop('url_template', '') 415 url_template = kwargs.pop("url_template", "")
349 format_args = { 416 format_args = {"uuid": id_, "app_name": C.APP_NAME, "app_url": C.APP_URL}
350 'uuid': id_,
351 'app_name': C.APP_NAME,
352 'app_url': C.APP_URL}
353 417
354 if name is None: 418 if name is None:
355 format_args['name'] = email 419 format_args["name"] = email
356 else: 420 else:
357 format_args['name'] = name 421 format_args["name"] = name
358 422
359 if profile is None: 423 if profile is None:
360 format_args['profile'] = '' 424 format_args["profile"] = ""
361 else: 425 else:
362 format_args['profile'] = extra['profile'] = profile 426 format_args["profile"] = extra["profile"] = profile
363 427
364 host_name = kwargs.pop('host_name', None) 428 host_name = kwargs.pop("host_name", None)
365 if host_name is None: 429 if host_name is None:
366 format_args['host_name'] = profile or _("somebody") 430 format_args["host_name"] = profile or _("somebody")
367 else: 431 else:
368 format_args['host_name'] = extra['host_name'] = host_name 432 format_args["host_name"] = extra["host_name"] = host_name
369 433
370 invite_url = url_template.format(**format_args) 434 invite_url = url_template.format(**format_args)
371 format_args['url'] = invite_url 435 format_args["url"] = invite_url
372 436
373 await sat_email.send_email( 437 await sat_email.send_email(
374 self.host.memory.config, 438 self.host.memory.config,
375 [email] + emails_extra, 439 [email] + emails_extra,
376 (kwargs.pop('message_subject', None) or DEFAULT_SUBJECT).format( 440 (kwargs.pop("message_subject", None) or DEFAULT_SUBJECT).format(
377 **format_args), 441 **format_args
378 (kwargs.pop('message_body', None) or DEFAULT_BODY).format(**format_args), 442 ),
443 (kwargs.pop("message_body", None) or DEFAULT_BODY).format(**format_args),
379 ) 444 )
380 445
381 ## roster 446 ## roster
382 447
383 # we automatically add guest to host roster (if host is specified) 448 # we automatically add guest to host roster (if host is specified)
386 try: 451 try:
387 client = self.host.get_client(profile) 452 client = self.host.get_client(profile)
388 except Exception as e: 453 except Exception as e:
389 log.error(f"Can't get host profile: {profile}: {e}") 454 log.error(f"Can't get host profile: {profile}: {e}")
390 else: 455 else:
391 await self.host.contact_update(client, jid_, name, ['guests']) 456 await self.host.contact_update(client, jid_, name, ["guests"])
392 457
393 if kwargs: 458 if kwargs:
394 log.warning(_("Not all arguments have been consumed: {}").format(kwargs)) 459 log.warning(_("Not all arguments have been consumed: {}").format(kwargs))
395 460
396 ## extra data saving 461 ## extra data saving
408 url_template = str(url_template) 473 url_template = str(url_template)
409 extra = data_format.deserialise(extra_s) 474 extra = data_format.deserialise(extra_s)
410 d = defer.ensureDeferred( 475 d = defer.ensureDeferred(
411 self.simple_create(client, invitee_email, invitee_name, url_template, extra) 476 self.simple_create(client, invitee_email, invitee_name, url_template, extra)
412 ) 477 )
413 d.addCallback(lambda data: {k: str(v) for k,v in data.items()}) 478 d.addCallback(lambda data: {k: str(v) for k, v in data.items()})
414 return d 479 return d
415 480
416 async def simple_create( 481 async def simple_create(
417 self, client, invitee_email, invitee_name, url_template, extra): 482 self, client, invitee_email, invitee_name, url_template, extra
483 ):
418 """Simplified method to invite somebody by email""" 484 """Simplified method to invite somebody by email"""
419 return await self.create( 485 return await self.create(
420 name=invitee_name, 486 name=invitee_name,
421 email=invitee_email, 487 email=invitee_email,
422 url_template=url_template, 488 url_template=url_template,
437 503
438 async def delete(self, id_): 504 async def delete(self, id_):
439 """Delete an invitation data and associated XMPP account""" 505 """Delete an invitation data and associated XMPP account"""
440 log.info(f"deleting invitation {id_}") 506 log.info(f"deleting invitation {id_}")
441 data = await self.get(id_) 507 data = await self.get(id_)
442 guest_profile = data['guest_profile'] 508 guest_profile = data["guest_profile"]
443 password = data['password'] 509 password = data["password"]
444 try: 510 try:
445 await self.host.connect(guest_profile, password) 511 await self.host.connect(guest_profile, password)
446 guest_client = self.host.get_client(guest_profile) 512 guest_client = self.host.get_client(guest_profile)
447 # XXX: be extra careful to use guest_client and not client below, as this will 513 # XXX: be extra careful to use guest_client and not client below, as this will
448 # delete the associated XMPP account 514 # delete the associated XMPP account
449 log.debug("deleting XMPP account") 515 log.debug("deleting XMPP account")
450 await self.host.plugins['XEP-0077'].unregister(guest_client, None) 516 await self.host.plugins["XEP-0077"].unregister(guest_client, None)
451 except (error.StanzaError, sasl.SASLAuthError) as e: 517 except (error.StanzaError, sasl.SASLAuthError) as e:
452 log.warning( 518 log.warning(
453 f"Can't delete {guest_profile}'s XMPP account, maybe it as already been " 519 f"Can't delete {guest_profile}'s XMPP account, maybe it as already been "
454 f"deleted: {e}") 520 f"deleted: {e}"
521 )
455 try: 522 try:
456 await self.host.memory.profile_delete_async(guest_profile, True) 523 await self.host.memory.profile_delete_async(guest_profile, True)
457 except Exception as e: 524 except Exception as e:
458 log.warning(f"Can't delete guest profile {guest_profile}: {e}") 525 log.warning(f"Can't delete guest profile {guest_profile}: {e}")
459 log.debug("removing guest data") 526 log.debug("removing guest data")
460 await self.invitations.adel(id_) 527 await self.invitations.adel(id_)
461 log.info(f"{id_} invitation has been deleted") 528 log.info(f"{id_} invitation has been deleted")
462 529
463 def _modify(self, id_, new_extra, replace): 530 def _modify(self, id_, new_extra, replace):
464 return self.modify(id_, {str(k): str(v) for k,v in new_extra.items()}, 531 return self.modify(id_, {str(k): str(v) for k, v in new_extra.items()}, replace)
465 replace)
466 532
467 def modify(self, id_, new_extra, replace=False): 533 def modify(self, id_, new_extra, replace=False):
468 """Modify invitation data 534 """Modify invitation data
469 535
470 @param id_(unicode): UUID linked to an invitation 536 @param id_(unicode): UUID linked to an invitation
473 @param replace(bool): if True replace the data 539 @param replace(bool): if True replace the data
474 else update them 540 else update them
475 @raise KeyError: there is not invitation with this id_ 541 @raise KeyError: there is not invitation with this id_
476 """ 542 """
477 self.check_extra(new_extra) 543 self.check_extra(new_extra)
544
478 def got_current_data(current_data): 545 def got_current_data(current_data):
479 if replace: 546 if replace:
480 new_data = new_extra 547 new_data = new_extra
481 for k in EXTRA_RESERVED: 548 for k in EXTRA_RESERVED:
482 try: 549 try:
483 new_data[k] = current_data[k] 550 new_data[k] = current_data[k]
484 except KeyError: 551 except KeyError:
485 continue 552 continue
486 else: 553 else:
487 new_data = current_data 554 new_data = current_data
488 for k,v in new_extra.items(): 555 for k, v in new_extra.items():
489 if k in EXTRA_RESERVED: 556 if k in EXTRA_RESERVED:
490 log.warning(_("Skipping reserved key {key}").format(key=k)) 557 log.warning(_("Skipping reserved key {key}").format(key=k))
491 continue 558 continue
492 if v: 559 if v:
493 new_data[k] = v 560 new_data[k] = v
513 C.PROF_KEY_NONE: don't filter invitations 580 C.PROF_KEY_NONE: don't filter invitations
514 @return list(unicode): invitations uids 581 @return list(unicode): invitations uids
515 """ 582 """
516 invitations = await self.invitations.all() 583 invitations = await self.invitations.all()
517 if profile != C.PROF_KEY_NONE: 584 if profile != C.PROF_KEY_NONE:
518 invitations = {id_:data for id_, data in invitations.items() 585 invitations = {
519 if data.get('profile') == profile} 586 id_: data
587 for id_, data in invitations.items()
588 if data.get("profile") == profile
589 }
520 590
521 return invitations 591 return invitations