comparison libervia/backend/plugins/plugin_xep_0054.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
76 class XEP_0054(object): 76 class XEP_0054(object):
77 77
78 def __init__(self, host): 78 def __init__(self, host):
79 log.info(_("Plugin XEP_0054 initialization")) 79 log.info(_("Plugin XEP_0054 initialization"))
80 self.host = host 80 self.host = host
81 self._i = host.plugins['IDENTITY'] 81 self._i = host.plugins["IDENTITY"]
82 self._i.register(IMPORT_NAME, 'avatar', self.get_avatar, self.set_avatar) 82 self._i.register(IMPORT_NAME, "avatar", self.get_avatar, self.set_avatar)
83 self._i.register(IMPORT_NAME, 'nicknames', self.get_nicknames, self.set_nicknames) 83 self._i.register(IMPORT_NAME, "nicknames", self.get_nicknames, self.set_nicknames)
84 host.trigger.add("presence_available", self.presence_available_trigger) 84 host.trigger.add("presence_available", self.presence_available_trigger)
85 85
86 def get_handler(self, client): 86 def get_handler(self, client):
87 return XEP_0054_handler(self) 87 return XEP_0054_handler(self)
88 88
89 def presence_available_trigger(self, presence_elt, client): 89 def presence_available_trigger(self, presence_elt, client):
90 try: 90 try:
91 avatar_hash = client._xep_0054_avatar_hashes[client.jid.userhost()] 91 avatar_hash = client._xep_0054_avatar_hashes[client.jid.userhost()]
92 except KeyError: 92 except KeyError:
93 log.info( 93 log.info(_("No avatar in cache for {profile}").format(profile=client.profile))
94 _("No avatar in cache for {profile}")
95 .format(profile=client.profile))
96 return True 94 return True
97 x_elt = domish.Element((NS_VCARD_UPDATE, "x")) 95 x_elt = domish.Element((NS_VCARD_UPDATE, "x"))
98 x_elt.addElement("photo", content=avatar_hash) 96 x_elt.addElement("photo", content=avatar_hash)
99 presence_elt.addChild(x_elt) 97 presence_elt.addChild(x_elt)
100 return True 98 return True
101 99
102 async def profile_connecting(self, client): 100 async def profile_connecting(self, client):
103 client._xep_0054_avatar_hashes = persistent.PersistentDict( 101 client._xep_0054_avatar_hashes = persistent.PersistentDict(
104 NS_VCARD, client.profile) 102 NS_VCARD, client.profile
103 )
105 await client._xep_0054_avatar_hashes.load() 104 await client._xep_0054_avatar_hashes.load()
106 105
107 def save_photo(self, client, photo_elt, entity): 106 def save_photo(self, client, photo_elt, entity):
108 """Parse a <PHOTO> photo_elt and save the picture""" 107 """Parse a <PHOTO> photo_elt and save the picture"""
109 # XXX: this method is launched in a separate thread 108 # XXX: this method is launched in a separate thread
134 del buf 133 del buf
135 134
136 if mime_type is None: 135 if mime_type is None:
137 log.debug( 136 log.debug(
138 f"no media type found specified for {entity}'s avatar, trying to " 137 f"no media type found specified for {entity}'s avatar, trying to "
139 f"guess") 138 f"guess"
139 )
140 140
141 try: 141 try:
142 mime_type = image.guess_type(io.BytesIO(decoded)) 142 mime_type = image.guess_type(io.BytesIO(decoded))
143 except IOError as e: 143 except IOError as e:
144 log.warning(f"Can't open avatar buffer: {e}") 144 log.warning(f"Can't open avatar buffer: {e}")
166 if elem.name == "FN": 166 if elem.name == "FN":
167 vcard_dict["fullname"] = str(elem) 167 vcard_dict["fullname"] = str(elem)
168 elif elem.name == "NICKNAME": 168 elif elem.name == "NICKNAME":
169 nickname = vcard_dict["nickname"] = str(elem) 169 nickname = vcard_dict["nickname"] = str(elem)
170 await self._i.update( 170 await self._i.update(
171 client, 171 client, IMPORT_NAME, "nicknames", [nickname], entity_jid
172 IMPORT_NAME,
173 "nicknames",
174 [nickname],
175 entity_jid
176 ) 172 )
177 elif elem.name == "URL": 173 elif elem.name == "URL":
178 vcard_dict["website"] = str(elem) 174 vcard_dict["website"] = str(elem)
179 elif elem.name == "EMAIL": 175 elif elem.name == "EMAIL":
180 vcard_dict["email"] = str(elem) 176 vcard_dict["email"] = str(elem)
194 avatar_hash = None 190 avatar_hash = None
195 else: 191 else:
196 vcard_dict["avatar"] = avatar_hash 192 vcard_dict["avatar"] = avatar_hash
197 if avatar_hash is not None: 193 if avatar_hash is not None:
198 await client._xep_0054_avatar_hashes.aset( 194 await client._xep_0054_avatar_hashes.aset(
199 entity_jid.full(), avatar_hash) 195 entity_jid.full(), avatar_hash
196 )
200 197
201 if avatar_hash: 198 if avatar_hash:
202 avatar_cache = self.host.common_cache.get_metadata(avatar_hash) 199 avatar_cache = self.host.common_cache.get_metadata(avatar_hash)
203 await self._i.update( 200 await self._i.update(
204 client, 201 client,
205 IMPORT_NAME, 202 IMPORT_NAME,
206 "avatar", 203 "avatar",
207 { 204 {
208 'path': avatar_cache['path'], 205 "path": avatar_cache["path"],
209 'filename': avatar_cache['filename'], 206 "filename": avatar_cache["filename"],
210 'media_type': avatar_cache['mime_type'], 207 "media_type": avatar_cache["mime_type"],
211 'cache_uid': avatar_hash 208 "cache_uid": avatar_hash,
212 }, 209 },
213 entity_jid 210 entity_jid,
214 ) 211 )
215 else: 212 else:
216 await self._i.update( 213 await self._i.update(
217 client, IMPORT_NAME, "avatar", None, entity_jid) 214 client, IMPORT_NAME, "avatar", None, entity_jid
215 )
218 else: 216 else:
219 log.debug("FIXME: [{}] VCard_elt tag is not managed yet".format(elem.name)) 217 log.debug(
218 "FIXME: [{}] VCard_elt tag is not managed yet".format(elem.name)
219 )
220 220
221 return vcard_dict 221 return vcard_dict
222 222
223 async def get_vcard_element(self, client, entity_jid): 223 async def get_vcard_element(self, client, entity_jid):
224 """Retrieve domish.Element of a VCard 224 """Retrieve domish.Element of a VCard
232 iq_elt.addElement("vCard", NS_VCARD) 232 iq_elt.addElement("vCard", NS_VCARD)
233 iq_ret_elt = await iq_elt.send(entity_jid.full()) 233 iq_ret_elt = await iq_elt.send(entity_jid.full())
234 try: 234 try:
235 return next(iq_ret_elt.elements(NS_VCARD, "vCard")) 235 return next(iq_ret_elt.elements(NS_VCARD, "vCard"))
236 except StopIteration: 236 except StopIteration:
237 log.warning(_( 237 log.warning(
238 "vCard element not found for {entity_jid}: {xml}" 238 _("vCard element not found for {entity_jid}: {xml}").format(
239 ).format(entity_jid=entity_jid, xml=iq_ret_elt.toXml())) 239 entity_jid=entity_jid, xml=iq_ret_elt.toXml()
240 )
241 )
240 raise exceptions.DataError(f"no vCard element found for {entity_jid}") 242 raise exceptions.DataError(f"no vCard element found for {entity_jid}")
241 243
242 async def update_vcard_elt(self, client, entity_jid, to_replace): 244 async def update_vcard_elt(self, client, entity_jid, to_replace):
243 """Create a vcard element to replace some metadata 245 """Create a vcard element to replace some metadata
244 246
277 try: 279 try:
278 vcard_elt = await self.get_vcard_element(client, entity_jid) 280 vcard_elt = await self.get_vcard_element(client, entity_jid)
279 except exceptions.DataError: 281 except exceptions.DataError:
280 self._i.update(client, IMPORT_NAME, "avatar", None, entity_jid) 282 self._i.update(client, IMPORT_NAME, "avatar", None, entity_jid)
281 except Exception as e: 283 except Exception as e:
282 log.warning(_( 284 log.warning(
283 "Can't get vCard for {entity_jid}: {e}" 285 _("Can't get vCard for {entity_jid}: {e}").format(
284 ).format(entity_jid=entity_jid, e=e)) 286 entity_jid=entity_jid, e=e
287 )
288 )
285 else: 289 else:
286 log.debug(_("VCard found")) 290 log.debug(_("VCard found"))
287 return await self.v_card_2_dict(client, vcard_elt, entity_jid) 291 return await self.v_card_2_dict(client, vcard_elt, entity_jid)
288 292
289 async def get_avatar( 293 async def get_avatar(
290 self, 294 self, client: SatXMPPEntity, entity_jid: jid.JID
291 client: SatXMPPEntity, 295 ) -> Optional[dict]:
292 entity_jid: jid.JID
293 ) -> Optional[dict]:
294 """Get avatar data 296 """Get avatar data
295 297
296 @param entity: entity to get avatar from 298 @param entity: entity to get avatar from
297 @return: avatar metadata, or None if no avatar has been found 299 @return: avatar metadata, or None if no avatar has been found
298 """ 300 """
302 if vcard is None: 304 if vcard is None:
303 return None 305 return None
304 try: 306 try:
305 avatar_hash = hashes_cache[entity_jid.full()] 307 avatar_hash = hashes_cache[entity_jid.full()]
306 except KeyError: 308 except KeyError:
307 if 'avatar' in vcard: 309 if "avatar" in vcard:
308 raise exceptions.InternalError( 310 raise exceptions.InternalError(
309 "No avatar hash while avatar is found in vcard") 311 "No avatar hash while avatar is found in vcard"
312 )
310 return None 313 return None
311 314
312 if not avatar_hash: 315 if not avatar_hash:
313 return None 316 return None
314 317
315 avatar_cache = self.host.common_cache.get_metadata(avatar_hash) 318 avatar_cache = self.host.common_cache.get_metadata(avatar_hash)
316 return self._i.avatar_build_metadata( 319 return self._i.avatar_build_metadata(
317 avatar_cache['path'], avatar_cache['mime_type'], avatar_hash) 320 avatar_cache["path"], avatar_cache["mime_type"], avatar_hash
321 )
318 322
319 async def set_avatar(self, client, avatar_data, entity): 323 async def set_avatar(self, client, avatar_data, entity):
320 """Set avatar of the profile 324 """Set avatar of the profile
321 325
322 @param avatar_data(dict): data of the image to use as avatar, as built by 326 @param avatar_data(dict): data of the image to use as avatar, as built by
323 IDENTITY plugin. 327 IDENTITY plugin.
324 @param entity(jid.JID): entity whose avatar must be changed 328 @param entity(jid.JID): entity whose avatar must be changed
325 """ 329 """
326 vcard_elt = await self.update_vcard_elt(client, entity, ['PHOTO']) 330 vcard_elt = await self.update_vcard_elt(client, entity, ["PHOTO"])
327 331
328 iq_elt = client.IQ() 332 iq_elt = client.IQ()
329 iq_elt.addChild(vcard_elt) 333 iq_elt.addChild(vcard_elt)
330 # metadata with encoded image are now filled at the right size/format 334 # metadata with encoded image are now filled at the right size/format
331 photo_elt = vcard_elt.addElement("PHOTO") 335 photo_elt = vcard_elt.addElement("PHOTO")
343 @param entity(jid.JID): entity to get nick from 347 @param entity(jid.JID): entity to get nick from
344 @return(list[str]): nicknames found 348 @return(list[str]): nicknames found
345 """ 349 """
346 vcard_data = await self.get_card(client, entity) 350 vcard_data = await self.get_card(client, entity)
347 try: 351 try:
348 return [vcard_data['nickname']] 352 return [vcard_data["nickname"]]
349 except (KeyError, TypeError): 353 except (KeyError, TypeError):
350 return [] 354 return []
351 355
352 async def set_nicknames(self, client, nicknames, entity): 356 async def set_nicknames(self, client, nicknames, entity):
353 """Update our vCard and set a nickname 357 """Update our vCard and set a nickname
355 @param nicknames(list[str]): new nicknames to use 359 @param nicknames(list[str]): new nicknames to use
356 only first item of this list will be used here 360 only first item of this list will be used here
357 """ 361 """
358 nick = nicknames[0].strip() 362 nick = nicknames[0].strip()
359 363
360 vcard_elt = await self.update_vcard_elt(client, entity, ['NICKNAME']) 364 vcard_elt = await self.update_vcard_elt(client, entity, ["NICKNAME"])
361 365
362 if nick: 366 if nick:
363 vcard_elt.addElement((NS_VCARD, "NICKNAME"), content=nick) 367 vcard_elt.addElement((NS_VCARD, "NICKNAME"), content=nick)
364 iq_elt = client.IQ() 368 iq_elt = client.IQ()
365 iq_elt.addChild(vcard_elt) 369 iq_elt.addChild(vcard_elt)
388 Check for avatar information, and get VCard if needed 392 Check for avatar information, and get VCard if needed
389 @param presence(domish.Element): <presence/> stanza 393 @param presence(domish.Element): <presence/> stanza
390 """ 394 """
391 client = self.parent 395 client = self.parent
392 entity_jid = self.plugin_parent._i.get_identity_jid( 396 entity_jid = self.plugin_parent._i.get_identity_jid(
393 client, jid.JID(presence["from"])) 397 client, jid.JID(presence["from"])
398 )
394 399
395 try: 400 try:
396 x_elt = next(presence.elements(NS_VCARD_UPDATE, "x")) 401 x_elt = next(presence.elements(NS_VCARD_UPDATE, "x"))
397 except StopIteration: 402 except StopIteration:
398 return 403 return
432 437
433 await hashes_cache.aset(entity_jid.full(), given_hash) 438 await hashes_cache.aset(entity_jid.full(), given_hash)
434 439
435 if not given_hash: 440 if not given_hash:
436 await self.plugin_parent._i.update( 441 await self.plugin_parent._i.update(
437 client, IMPORT_NAME, "avatar", None, entity_jid) 442 client, IMPORT_NAME, "avatar", None, entity_jid
443 )
438 # the avatar has been removed, no need to go further 444 # the avatar has been removed, no need to go further
439 return 445 return
440 446
441 avatar_cache = self.host.common_cache.get_metadata(given_hash) 447 avatar_cache = self.host.common_cache.get_metadata(given_hash)
442 if avatar_cache is not None: 448 if avatar_cache is not None:
443 log.debug( 449 log.debug(
444 f"New avatar found for [{entity_jid}], it's already in cache, we use it" 450 f"New avatar found for [{entity_jid}], it's already in cache, we use it"
445 ) 451 )
446 await self.plugin_parent._i.update( 452 await self.plugin_parent._i.update(
447 client, 453 client,
448 IMPORT_NAME, "avatar", 454 IMPORT_NAME,
455 "avatar",
449 { 456 {
450 'path': avatar_cache['path'], 457 "path": avatar_cache["path"],
451 'filename': avatar_cache['filename'], 458 "filename": avatar_cache["filename"],
452 'media_type': avatar_cache['mime_type'], 459 "media_type": avatar_cache["mime_type"],
453 'cache_uid': given_hash, 460 "cache_uid": given_hash,
454 }, 461 },
455 entity_jid 462 entity_jid,
456 ) 463 )
457 else: 464 else:
458 log.debug( 465 log.debug("New avatar found for [{entity_jid}], requesting vcard")
459 "New avatar found for [{entity_jid}], requesting vcard"
460 )
461 vcard = await self.plugin_parent.get_card(client, entity_jid) 466 vcard = await self.plugin_parent.get_card(client, entity_jid)
462 if vcard is None: 467 if vcard is None:
463 log.warning(f"Unexpected empty vCard for {entity_jid}") 468 log.warning(f"Unexpected empty vCard for {entity_jid}")
464 return 469 return
465 computed_hash = client._xep_0054_avatar_hashes[entity_jid.full()] 470 computed_hash = client._xep_0054_avatar_hashes[entity_jid.full()]