slidge 0.1.2__py3-none-any.whl → 0.2.0a0__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. slidge/__init__.py +3 -5
  2. slidge/__main__.py +2 -196
  3. slidge/__version__.py +5 -0
  4. slidge/command/adhoc.py +8 -1
  5. slidge/command/admin.py +5 -6
  6. slidge/command/base.py +1 -2
  7. slidge/command/register.py +32 -16
  8. slidge/command/user.py +85 -5
  9. slidge/contact/contact.py +93 -31
  10. slidge/contact/roster.py +54 -39
  11. slidge/core/config.py +13 -7
  12. slidge/core/gateway/base.py +139 -34
  13. slidge/core/gateway/disco.py +2 -4
  14. slidge/core/gateway/mam.py +1 -4
  15. slidge/core/gateway/ping.py +2 -3
  16. slidge/core/gateway/presence.py +1 -1
  17. slidge/core/gateway/registration.py +32 -21
  18. slidge/core/gateway/search.py +3 -5
  19. slidge/core/gateway/session_dispatcher.py +109 -51
  20. slidge/core/gateway/vcard_temp.py +6 -4
  21. slidge/core/mixins/__init__.py +11 -1
  22. slidge/core/mixins/attachment.py +15 -10
  23. slidge/core/mixins/avatar.py +66 -18
  24. slidge/core/mixins/base.py +8 -2
  25. slidge/core/mixins/message.py +11 -7
  26. slidge/core/mixins/message_maker.py +17 -9
  27. slidge/core/mixins/presence.py +14 -4
  28. slidge/core/pubsub.py +54 -212
  29. slidge/core/session.py +65 -33
  30. slidge/db/__init__.py +4 -0
  31. slidge/db/alembic/env.py +64 -0
  32. slidge/db/alembic/script.py.mako +26 -0
  33. slidge/db/alembic/versions/09f27f098baa_add_missing_attributes_in_room.py +36 -0
  34. slidge/db/alembic/versions/29f5280c61aa_store_subject_setter_in_room.py +37 -0
  35. slidge/db/alembic/versions/8d2ced764698_rely_on_db_to_store_contacts_rooms_and_.py +133 -0
  36. slidge/db/alembic/versions/aa9d82a7f6ef_db_creation.py +76 -0
  37. slidge/db/alembic/versions/b33993e87db3_move_everything_to_persistent_db.py +214 -0
  38. slidge/db/alembic/versions/e91195719c2c_store_users_avatars_persistently.py +26 -0
  39. slidge/db/avatar.py +224 -0
  40. slidge/db/meta.py +65 -0
  41. slidge/db/models.py +365 -0
  42. slidge/db/store.py +976 -0
  43. slidge/group/archive.py +13 -14
  44. slidge/group/bookmarks.py +59 -56
  45. slidge/group/participant.py +81 -29
  46. slidge/group/room.py +242 -142
  47. slidge/main.py +201 -0
  48. slidge/migration.py +30 -0
  49. slidge/slixfix/__init__.py +35 -2
  50. slidge/slixfix/roster.py +11 -4
  51. slidge/slixfix/xep_0292/vcard4.py +1 -0
  52. slidge/util/db.py +1 -47
  53. slidge/util/test.py +21 -4
  54. slidge/util/types.py +24 -4
  55. {slidge-0.1.2.dist-info → slidge-0.2.0a0.dist-info}/METADATA +3 -1
  56. slidge-0.2.0a0.dist-info/RECORD +108 -0
  57. slidge/core/cache.py +0 -183
  58. slidge/util/schema.sql +0 -126
  59. slidge/util/sql.py +0 -508
  60. slidge-0.1.2.dist-info/RECORD +0 -96
  61. {slidge-0.1.2.dist-info → slidge-0.2.0a0.dist-info}/LICENSE +0 -0
  62. {slidge-0.1.2.dist-info → slidge-0.2.0a0.dist-info}/WHEEL +0 -0
  63. {slidge-0.1.2.dist-info → slidge-0.2.0a0.dist-info}/entry_points.txt +0 -0
@@ -111,7 +111,7 @@ class MarkerMixin(MessageMaker):
111
111
  self.xmpp.delivery_receipt.make_ack(
112
112
  self._legacy_to_xmpp(legacy_msg_id),
113
113
  mfrom=self.jid,
114
- mto=self.user.jid,
114
+ mto=self.user_jid,
115
115
  )
116
116
  )
117
117
  self._send(
@@ -144,7 +144,7 @@ class MarkerMixin(MessageMaker):
144
144
  # We'll see if we need to implement that later
145
145
  return
146
146
  xmpp_msg_id = self._legacy_to_xmpp(legacy_msg_id)
147
- iq = Iq(sto=self.user.bare_jid, sfrom=self.user.bare_jid, stype="set")
147
+ iq = Iq(sto=self.user_jid.bare, sfrom=self.user_jid.bare, stype="set")
148
148
  iq["pubsub"]["publish"]["node"] = self.xmpp["xep_0490"].stanza.NS
149
149
  iq["pubsub"]["publish"]["item"]["id"] = muc_jid
150
150
  displayed = self.xmpp["xep_0490"].stanza.Displayed()
@@ -169,8 +169,8 @@ class ContentMessageMixin(AttachmentMixin):
169
169
 
170
170
  def __replace_id(self, legacy_msg_id: LegacyMessageType):
171
171
  if self.mtype == "groupchat":
172
- return self.session.muc_sent_msg_ids.get(
173
- legacy_msg_id
172
+ return self.xmpp.store.sent.get_group_xmpp_id(
173
+ self.session.user_pk, str(legacy_msg_id)
174
174
  ) or self._legacy_to_xmpp(legacy_msg_id)
175
175
  else:
176
176
  return self._legacy_to_xmpp(legacy_msg_id)
@@ -215,14 +215,18 @@ class ContentMessageMixin(AttachmentMixin):
215
215
  but store it in the archive. Meant to be used during ``MUC.backfill()``
216
216
  """
217
217
  if carbon:
218
- if not correction and legacy_msg_id in self.session.sent:
218
+ if not correction and self.xmpp.store.sent.was_sent_by_user(
219
+ self.session.user_pk, str(legacy_msg_id)
220
+ ):
219
221
  log.warning(
220
222
  "Carbon message for a message an XMPP has sent? This is a bug! %s",
221
223
  legacy_msg_id,
222
224
  )
223
225
  return
224
- self.session.sent[legacy_msg_id] = self.session.legacy_to_xmpp_msg_id(
225
- legacy_msg_id
226
+ self.xmpp.store.sent.set_message(
227
+ self.session.user_pk,
228
+ str(legacy_msg_id),
229
+ self.session.legacy_to_xmpp_msg_id(legacy_msg_id),
226
230
  )
227
231
  hints = self.__default_hints(hints)
228
232
  msg = self._make_message(
@@ -7,8 +7,8 @@ from uuid import uuid4
7
7
  from slixmpp import Message
8
8
  from slixmpp.types import MessageTypes
9
9
 
10
+ from ...db.models import GatewayUser
10
11
  from ...slixfix.link_preview.stanza import LinkPreview as LinkPreviewStanza
11
- from ...util.db import GatewayUser
12
12
  from ...util.types import (
13
13
  ChatState,
14
14
  LegacyMessageType,
@@ -60,8 +60,9 @@ class MessageMaker(BaseSender):
60
60
  msg["body"] = body
61
61
  state = "active"
62
62
  if thread:
63
- known_threads = self.session.threads.inverse # type:ignore
64
- msg["thread"] = known_threads.get(thread) or str(thread)
63
+ msg["thread"] = self.xmpp.store.sent.get_legacy_thread(
64
+ self.user_pk, str(thread)
65
+ ) or str(thread)
65
66
  if state:
66
67
  msg["chat_state"] = state
67
68
  for hint in hints:
@@ -88,9 +89,9 @@ class MessageMaker(BaseSender):
88
89
  msg["stanza_id"]["by"] = self.muc.jid # type: ignore
89
90
 
90
91
  def _legacy_to_xmpp(self, legacy_id: LegacyMessageType):
91
- return self.session.sent.get(legacy_id) or self.session.legacy_to_xmpp_msg_id(
92
- legacy_id
93
- )
92
+ return self.xmpp.store.sent.get_xmpp_id(
93
+ self.session.user_pk, str(legacy_id)
94
+ ) or self.session.legacy_to_xmpp_msg_id(legacy_id)
94
95
 
95
96
  def _add_delay(self, msg: Message, when: Optional[datetime]):
96
97
  if when:
@@ -110,16 +111,23 @@ class MessageMaker(BaseSender):
110
111
  muc = getattr(self, "muc", None)
111
112
 
112
113
  if entity := reply_to.author:
113
- if isinstance(entity, GatewayUser):
114
+ if entity == "user" or isinstance(entity, GatewayUser):
115
+ if isinstance(entity, GatewayUser):
116
+ warnings.warn(
117
+ "Using a GatewayUser as the author of a "
118
+ "MessageReference is deprecated. Use the string 'user' "
119
+ "instead.",
120
+ DeprecationWarning,
121
+ )
114
122
  if muc:
115
123
  jid = copy(muc.jid)
116
124
  jid.resource = fallback_nick = muc.user_nick
117
125
  msg["reply"]["to"] = jid
118
126
  else:
119
- msg["reply"]["to"] = entity.jid
127
+ msg["reply"]["to"] = self.session.user_jid
120
128
  # TODO: here we should use preferably use the PEP nick of the user
121
129
  # (but it doesn't matter much)
122
- fallback_nick = entity.jid.local
130
+ fallback_nick = self.session.user_jid.local
123
131
  else:
124
132
  if muc:
125
133
  if hasattr(entity, "muc"):
@@ -5,7 +5,7 @@ from typing import Optional
5
5
 
6
6
  from slixmpp.types import PresenceShows, PresenceTypes
7
7
 
8
- from ...util.sql import CachedPresence, db
8
+ from ...util.types import CachedPresence
9
9
  from .. import config
10
10
  from .base import BaseSender
11
11
 
@@ -19,9 +19,12 @@ _FRIEND_REQUEST_PRESENCES = {"subscribe", "unsubscribe", "subscribed", "unsubscr
19
19
 
20
20
  class PresenceMixin(BaseSender):
21
21
  _ONLY_SEND_PRESENCE_CHANGES = False
22
+ contact_pk: Optional[int]
22
23
 
23
24
  def __init__(self, *a, **k):
24
25
  super().__init__(*a, **k)
26
+ # FIXME: this should not be an attribute of this mixin to allow garbage
27
+ # collection of instances
25
28
  self.__update_last_seen_fallback_task: Optional[Task] = None
26
29
 
27
30
  async def __update_last_seen_fallback(self):
@@ -29,10 +32,16 @@ class PresenceMixin(BaseSender):
29
32
  self.send_last_presence(force=True, no_cache_online=False)
30
33
 
31
34
  def _get_last_presence(self) -> Optional[CachedPresence]:
32
- return db.presence_get(self.jid, self.user)
35
+ # TODO: use contact PK instead of JID
36
+ if self.contact_pk is None:
37
+ return None
38
+ return self.xmpp.store.contacts.get_presence(self.contact_pk)
33
39
 
34
40
  def _store_last_presence(self, new: CachedPresence):
35
- return db.presence_store(self.jid, new, self.user)
41
+ # TODO: use contact PK instead of JID
42
+ if self.contact_pk is None:
43
+ return
44
+ self.xmpp.store.contacts.set_presence(self.contact_pk, new)
36
45
 
37
46
  def _make_presence(
38
47
  self,
@@ -55,7 +64,8 @@ class PresenceMixin(BaseSender):
55
64
  )
56
65
  if old != new:
57
66
  if hasattr(self, "muc") and ptype == "unavailable":
58
- db.presence_delete(self.jid, self.user)
67
+ if self.contact_pk is not None:
68
+ self.xmpp.store.contacts.reset_presence(self.contact_pk)
59
69
  else:
60
70
  self._store_last_presence(new)
61
71
  if old and not force and self._ONLY_SEND_PRESENCE_CHANGES:
slidge/core/pubsub.py CHANGED
@@ -1,12 +1,8 @@
1
- import hashlib
2
- import io
3
1
  import logging
4
2
  from copy import copy
5
3
  from pathlib import Path
6
- from typing import TYPE_CHECKING, Optional, Type, Union
4
+ from typing import TYPE_CHECKING, Optional, Union
7
5
 
8
- from PIL import Image, UnidentifiedImageError
9
- from PIL.Image import Image as PILImage
10
6
  from slixmpp import (
11
7
  JID,
12
8
  CoroutineCallback,
@@ -26,10 +22,8 @@ from slixmpp.types import JidStr, OptJidStr
26
22
 
27
23
  from ..contact.contact import LegacyContact
28
24
  from ..contact.roster import ContactIsUser
29
- from ..util.db import GatewayUser, user_store
30
- from ..util.sql import db
31
- from ..util.types import AvatarType, LegacyFileIdType, PepItemType
32
- from .cache import CachedAvatar, avatar_cache
25
+ from ..db.avatar import CachedAvatar, avatar_cache
26
+ from ..db.store import ContactStore, SlidgeStore
33
27
  from .mixins.lock import NamedLockMixin
34
28
 
35
29
  if TYPE_CHECKING:
@@ -39,19 +33,15 @@ VCARD4_NAMESPACE = "urn:xmpp:vcard4"
39
33
 
40
34
 
41
35
  class PepItem:
42
- @staticmethod
43
- def from_db(jid: JID, user: Optional[GatewayUser] = None) -> Optional["PepItem"]:
44
- raise NotImplementedError
45
-
46
- def to_db(self, jid: JID, user: Optional[GatewayUser] = None):
47
- raise NotImplementedError
36
+ pass
48
37
 
49
38
 
50
39
  class PepAvatar(PepItem):
51
- def __init__(self, jid: JID):
40
+ store: SlidgeStore
41
+
42
+ def __init__(self):
52
43
  self.metadata: Optional[AvatarMetadata] = None
53
44
  self.id: Optional[str] = None
54
- self.jid = jid
55
45
  self._avatar_data_path: Optional[Path] = None
56
46
 
57
47
  @property
@@ -62,63 +52,7 @@ class PepAvatar(PepItem):
62
52
  data.set_value(self._avatar_data_path.read_bytes())
63
53
  return data
64
54
 
65
- @staticmethod
66
- def _sha(b: bytes):
67
- return hashlib.sha1(b).hexdigest()
68
-
69
- def _get_weak_unique_id(self, avatar: AvatarType):
70
- if isinstance(avatar, str):
71
- return avatar # url
72
- elif isinstance(avatar, bytes):
73
- return self._sha(avatar)
74
- elif isinstance(avatar, Path):
75
- return self._sha(avatar.read_bytes())
76
-
77
- @staticmethod
78
- async def _get_image(avatar: AvatarType) -> PILImage:
79
- if isinstance(avatar, str):
80
- # async with aiohttp.ClientSession() as session:
81
- async with avatar_cache.http.get(avatar) as response:
82
- return Image.open(io.BytesIO(await response.read()))
83
- elif isinstance(avatar, bytes):
84
- return Image.open(io.BytesIO(avatar))
85
- elif isinstance(avatar, Path):
86
- return Image.open(avatar)
87
- else:
88
- raise TypeError("Avatar must be bytes, a Path or a str (URL)", avatar)
89
-
90
- async def set_avatar(
91
- self, avatar: AvatarType, unique_id: Optional[LegacyFileIdType] = None
92
- ):
93
- if unique_id is None:
94
- if isinstance(avatar, str):
95
- await self._set_avatar_from_url_alone(avatar)
96
- return
97
- unique_id = self._get_weak_unique_id(avatar)
98
-
99
- await self._set_avatar_from_unique_id(avatar, unique_id)
100
-
101
- async def _set_avatar_from_unique_id(
102
- self, avatar: AvatarType, unique_id: LegacyFileIdType
103
- ):
104
- cached_avatar = avatar_cache.get(unique_id)
105
- if cached_avatar:
106
- # this shouldn't be necessary but here to re-use avatars downloaded
107
- # before the change introducing the JID to unique ID mapping
108
- avatar_cache.store_jid(self.jid, unique_id)
109
- else:
110
- img = await self._get_image(avatar)
111
- cached_avatar = await avatar_cache.convert_and_store(
112
- img, unique_id, self.jid
113
- )
114
-
115
- self._set_avatar_from_cache(cached_avatar)
116
-
117
- async def _set_avatar_from_url_alone(self, url: str):
118
- cached_avatar = await avatar_cache.get_avatar_from_url_alone(url, self.jid)
119
- self._set_avatar_from_cache(cached_avatar)
120
-
121
- def _set_avatar_from_cache(self, cached_avatar: CachedAvatar):
55
+ def set_avatar_from_cache(self, cached_avatar: CachedAvatar):
122
56
  metadata = AvatarMetadata()
123
57
  self.id = cached_avatar.hash
124
58
  metadata.add_info(
@@ -131,31 +65,10 @@ class PepAvatar(PepItem):
131
65
  self.metadata = metadata
132
66
  self._avatar_data_path = cached_avatar.path
133
67
 
134
- @staticmethod
135
- def from_db(jid: JID, user: Optional[GatewayUser] = None) -> Optional["PepAvatar"]:
136
- cached_id = db.avatar_get(jid)
137
- if cached_id is None:
138
- return None
139
- item = PepAvatar(jid)
140
- cached_avatar = avatar_cache.get(cached_id)
141
- if cached_avatar is None:
142
- raise XMPPError("internal-server-error")
143
- item._set_avatar_from_cache(cached_avatar)
144
- return item
145
-
146
- def to_db(self, jid: JID, user=None):
147
- cached_id = avatar_cache.get_cached_id_for(jid)
148
- if cached_id is None:
149
- log.warning("Could not store avatar for %s", jid)
150
- return
151
- db.avatar_store(jid, cached_id)
152
-
153
- @staticmethod
154
- def remove_from_db(jid: JID):
155
- db.avatar_delete(jid)
156
-
157
68
 
158
69
  class PepNick(PepItem):
70
+ contact_store: ContactStore
71
+
159
72
  def __init__(self, nick: Optional[str] = None):
160
73
  nickname = UserNick()
161
74
  if nick is not None:
@@ -163,20 +76,6 @@ class PepNick(PepItem):
163
76
  self.nick = nickname
164
77
  self.__nick_str = nick
165
78
 
166
- @staticmethod
167
- def from_db(jid: JID, user: Optional[GatewayUser] = None) -> Optional["PepNick"]:
168
- if user is None:
169
- raise XMPPError("not-allowed")
170
- nick = db.nick_get(jid, user)
171
- if nick is None:
172
- return None
173
- return PepNick(nick)
174
-
175
- def to_db(self, jid: JID, user: Optional[GatewayUser] = None):
176
- if user is None:
177
- raise XMPPError("not-allowed")
178
- db.nick_store(jid, str(self.__nick_str), user)
179
-
180
79
 
181
80
  class PubSubComponent(NamedLockMixin, BasePlugin):
182
81
  xmpp: "BaseGateway"
@@ -186,7 +85,6 @@ class PubSubComponent(NamedLockMixin, BasePlugin):
186
85
  dependencies = {
187
86
  "xep_0030",
188
87
  "xep_0060",
189
- # "xep_0084",
190
88
  "xep_0115",
191
89
  "xep_0163",
192
90
  }
@@ -276,7 +174,7 @@ class PubSubComponent(NamedLockMixin, BasePlugin):
276
174
  else:
277
175
  if pep_avatar.metadata is None:
278
176
  raise XMPPError("internal-server-error", "Avatar but no metadata?")
279
- await self._broadcast(
177
+ await self.broadcast(
280
178
  data=pep_avatar.metadata,
281
179
  from_=p.get_to(),
282
180
  to=from_,
@@ -288,7 +186,7 @@ class PubSubComponent(NamedLockMixin, BasePlugin):
288
186
  except XMPPError:
289
187
  pass
290
188
  else:
291
- await self._broadcast(data=pep_nick.nick, from_=p.get_to(), to=from_)
189
+ await self.broadcast(data=pep_nick.nick, from_=p.get_to(), to=from_)
292
190
 
293
191
  if VCARD4_NAMESPACE + "+notify" in features:
294
192
  await self.broadcast_vcard_event(p.get_to(), to=from_)
@@ -303,7 +201,7 @@ class PubSubComponent(NamedLockMixin, BasePlugin):
303
201
  # but movim expects it to be here, and I guess
304
202
 
305
203
  log.debug("Broadcast vcard4 event: %s", vcard)
306
- await self._broadcast(
204
+ await self.broadcast(
307
205
  data=vcard,
308
206
  from_=JID(from_).bare,
309
207
  to=to,
@@ -311,26 +209,34 @@ class PubSubComponent(NamedLockMixin, BasePlugin):
311
209
  node=VCARD4_NAMESPACE,
312
210
  )
313
211
 
314
- async def _get_authorized_item(
315
- self, cls: Type[PepItemType], stanza: Union[Iq, Presence]
316
- ) -> PepItemType:
317
- sto = stanza.get_to()
318
- user = user_store.get_by_jid(stanza.get_from())
319
- item = cls.from_db(sto, user)
320
- if item is None:
321
- raise XMPPError("item-not-found")
322
-
323
- if sto != self.xmpp.boundjid.bare:
324
- session = self.xmpp.get_session_from_stanza(stanza)
325
- await session.contacts.by_jid(sto)
326
-
327
- return item # type:ignore
328
-
329
212
  async def _get_authorized_avatar(self, stanza: Union[Iq, Presence]) -> PepAvatar:
330
- return await self._get_authorized_item(PepAvatar, stanza)
213
+ if stanza.get_to() == self.xmpp.boundjid.bare:
214
+ item = PepAvatar()
215
+ item.set_avatar_from_cache(avatar_cache.get_by_pk(self.xmpp.avatar_pk))
216
+ return item
217
+
218
+ session = self.xmpp.get_session_from_stanza(stanza)
219
+ entity = await session.get_contact_or_group_or_participant(stanza.get_to())
220
+
221
+ item = PepAvatar()
222
+ avatar_id = entity.avatar_id
223
+ if avatar_id is not None:
224
+ stored = avatar_cache.get(str(avatar_id))
225
+ assert stored is not None
226
+ item.set_avatar_from_cache(stored)
227
+ return item
331
228
 
332
229
  async def _get_authorized_nick(self, stanza: Union[Iq, Presence]) -> PepNick:
333
- return await self._get_authorized_item(PepNick, stanza)
230
+ if stanza.get_to() == self.xmpp.boundjid.bare:
231
+ return PepNick(self.xmpp.COMPONENT_NAME)
232
+
233
+ session = self.xmpp.get_session_from_stanza(stanza)
234
+ entity = await session.contacts.by_jid(stanza.get_to())
235
+
236
+ if entity.name is not None:
237
+ return PepNick(entity.name)
238
+ else:
239
+ return PepNick()
334
240
 
335
241
  async def _get_avatar_data(self, iq: Iq):
336
242
  pep_avatar = await self._get_authorized_avatar(iq)
@@ -372,10 +278,6 @@ class PubSubComponent(NamedLockMixin, BasePlugin):
372
278
  raise XMPPError("item-not-found")
373
279
  self._reply_with_payload(iq, vcard, "current", VCARD4_NAMESPACE)
374
280
 
375
- @staticmethod
376
- def get_avatar(jid: JID):
377
- return PepAvatar.from_db(jid)
378
-
379
281
  @staticmethod
380
282
  def _reply_with_payload(
381
283
  iq: Iq,
@@ -394,7 +296,7 @@ class PubSubComponent(NamedLockMixin, BasePlugin):
394
296
  result["pubsub"]["items"].append(item)
395
297
  result.send()
396
298
 
397
- async def _broadcast(self, data, from_: JidStr, to: OptJidStr = None, **kwargs):
299
+ async def broadcast(self, data, from_: JidStr, to: OptJidStr = None, **kwargs):
398
300
  from_ = JID(from_)
399
301
  if from_ != self.xmpp.boundjid.bare and to is not None:
400
302
  to = JID(to)
@@ -428,97 +330,37 @@ class PubSubComponent(NamedLockMixin, BasePlugin):
428
330
  msg.append(event)
429
331
 
430
332
  if to is None:
431
- for u in user_store.get_all():
333
+ for u in self.xmpp.store.users.get_all():
432
334
  new_msg = copy(msg)
433
- new_msg.set_to(u.bare_jid)
335
+ new_msg.set_to(u.jid.bare)
434
336
  new_msg.send()
435
337
  else:
436
338
  msg.set_to(to)
437
339
  msg.send()
438
340
 
439
- async def set_avatar(
440
- self,
441
- jid: JidStr,
442
- avatar: Optional[AvatarType] = None,
443
- broadcast_to: OptJidStr = None,
444
- unique_id=None,
445
- broadcast=True,
446
- ):
447
- jid = JID(jid)
448
- if avatar is None:
449
- PepAvatar.remove_from_db(jid)
450
- await self._broadcast(AvatarMetadata(), jid, broadcast_to)
451
- avatar_cache.delete_jid(jid)
452
- else:
453
- pep_avatar = PepAvatar(jid)
454
- try:
455
- await pep_avatar.set_avatar(avatar, unique_id)
456
- except (UnidentifiedImageError, FileNotFoundError) as e:
457
- log.warning("Failed to set avatar for %s: %r", self, e)
458
- return
459
- pep_avatar.to_db(jid)
460
- if pep_avatar.metadata is None:
461
- raise RuntimeError
462
- if not broadcast:
463
- return
464
- await self._broadcast(
465
- pep_avatar.metadata,
466
- jid,
467
- broadcast_to,
468
- id=pep_avatar.metadata["info"]["id"],
469
- )
470
-
471
- async def set_avatar_from_cache(
472
- self, jid: JID, send_empty: bool, broadcast_to: OptJidStr = None, broadcast=True
473
- ):
474
- uid = avatar_cache.get_cached_id_for(jid)
475
- if uid is None:
476
- if not send_empty:
477
- return
478
- self.xmpp.loop.create_task(
479
- self.set_avatar(jid, None, broadcast_to, uid, broadcast)
480
- )
481
- return
482
- cached_avatar = avatar_cache.get(str(uid))
341
+ async def broadcast_avatar(
342
+ self, from_: JidStr, to: JidStr, cached_avatar: Optional[CachedAvatar]
343
+ ) -> None:
483
344
  if cached_avatar is None:
484
- # should not happen but well…
485
- log.warning(
486
- "Something is wrong with the avatar, %s won't have an "
487
- "avatar because avatar not found in cache",
488
- jid,
345
+ await self.broadcast(AvatarMetadata(), from_, to)
346
+ else:
347
+ pep_avatar = PepAvatar()
348
+ pep_avatar.set_avatar_from_cache(cached_avatar)
349
+ assert pep_avatar.metadata is not None
350
+ await self.broadcast(
351
+ pep_avatar.metadata, from_, to, id=pep_avatar.metadata["info"]["id"]
489
352
  )
490
- return
491
- self.xmpp.loop.create_task(
492
- self.set_avatar(jid, cached_avatar.path, broadcast_to, uid, broadcast)
493
- )
494
353
 
495
- def set_nick(
354
+ def broadcast_nick(
496
355
  self,
497
- user: GatewayUser,
356
+ user_jid: JID,
498
357
  jid: JidStr,
499
358
  nick: Optional[str] = None,
500
359
  ):
501
360
  jid = JID(jid)
502
361
  nickname = PepNick(nick)
503
- nickname.to_db(jid, user)
504
362
  log.debug("New nickname: %s", nickname.nick)
505
- self.xmpp.loop.create_task(self._broadcast(nickname.nick, jid, user.bare_jid))
506
-
507
- async def broadcast_all(self, from_: JID, to: JID):
508
- """
509
- Force push avatar and nick for a stored JID.
510
- """
511
- a = PepAvatar.from_db(from_)
512
- if a:
513
- if a.metadata:
514
- await self._broadcast(
515
- a.metadata, from_, to, id=a.metadata["info"]["id"]
516
- )
517
- else:
518
- log.warning("No metadata associated to this cached avatar?!")
519
- n = PepNick.from_db(from_, user_store.get_by_jid(to))
520
- if n:
521
- await self._broadcast(n.nick, from_, to)
363
+ self.xmpp.loop.create_task(self.broadcast(nickname.nick, jid, user_jid.bare))
522
364
 
523
365
 
524
366
  log = logging.getLogger(__name__)