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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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__)