slidge 0.2.12__py3-none-any.whl → 0.3.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 (77) hide show
  1. slidge/__init__.py +5 -2
  2. slidge/command/adhoc.py +9 -3
  3. slidge/command/admin.py +16 -12
  4. slidge/command/base.py +16 -12
  5. slidge/command/chat_command.py +25 -16
  6. slidge/command/user.py +7 -8
  7. slidge/contact/contact.py +119 -209
  8. slidge/contact/roster.py +106 -105
  9. slidge/core/config.py +2 -43
  10. slidge/core/dispatcher/caps.py +9 -2
  11. slidge/core/dispatcher/disco.py +13 -3
  12. slidge/core/dispatcher/message/__init__.py +1 -1
  13. slidge/core/dispatcher/message/chat_state.py +17 -8
  14. slidge/core/dispatcher/message/marker.py +7 -5
  15. slidge/core/dispatcher/message/message.py +117 -92
  16. slidge/core/dispatcher/muc/__init__.py +1 -1
  17. slidge/core/dispatcher/muc/admin.py +4 -4
  18. slidge/core/dispatcher/muc/mam.py +10 -6
  19. slidge/core/dispatcher/muc/misc.py +4 -2
  20. slidge/core/dispatcher/muc/owner.py +5 -3
  21. slidge/core/dispatcher/muc/ping.py +3 -1
  22. slidge/core/dispatcher/presence.py +21 -15
  23. slidge/core/dispatcher/registration.py +20 -12
  24. slidge/core/dispatcher/search.py +7 -3
  25. slidge/core/dispatcher/session_dispatcher.py +13 -5
  26. slidge/core/dispatcher/util.py +37 -27
  27. slidge/core/dispatcher/vcard.py +7 -4
  28. slidge/core/gateway.py +168 -84
  29. slidge/core/mixins/__init__.py +1 -11
  30. slidge/core/mixins/attachment.py +163 -148
  31. slidge/core/mixins/avatar.py +100 -177
  32. slidge/core/mixins/db.py +50 -2
  33. slidge/core/mixins/message.py +19 -17
  34. slidge/core/mixins/message_maker.py +29 -15
  35. slidge/core/mixins/message_text.py +38 -30
  36. slidge/core/mixins/presence.py +91 -35
  37. slidge/core/pubsub.py +42 -47
  38. slidge/core/session.py +88 -57
  39. slidge/db/alembic/versions/0337c90c0b96_unify_legacy_xmpp_id_mappings.py +183 -0
  40. slidge/db/alembic/versions/4dbd23a3f868_new_avatar_store.py +56 -0
  41. slidge/db/alembic/versions/54ce3cde350c_use_hash_for_avatar_filenames.py +50 -0
  42. slidge/db/alembic/versions/58b98dacf819_refactor.py +118 -0
  43. slidge/db/alembic/versions/75a62b74b239_ditch_hats_table.py +74 -0
  44. slidge/db/avatar.py +150 -119
  45. slidge/db/meta.py +33 -22
  46. slidge/db/models.py +68 -117
  47. slidge/db/store.py +412 -1094
  48. slidge/group/archive.py +61 -54
  49. slidge/group/bookmarks.py +74 -55
  50. slidge/group/participant.py +135 -142
  51. slidge/group/room.py +315 -312
  52. slidge/main.py +28 -18
  53. slidge/migration.py +2 -12
  54. slidge/slixfix/__init__.py +20 -4
  55. slidge/slixfix/delivery_receipt.py +6 -4
  56. slidge/slixfix/link_preview/link_preview.py +1 -1
  57. slidge/slixfix/link_preview/stanza.py +1 -1
  58. slidge/slixfix/roster.py +5 -7
  59. slidge/slixfix/xep_0077/register.py +8 -8
  60. slidge/slixfix/xep_0077/stanza.py +7 -7
  61. slidge/slixfix/xep_0100/gateway.py +12 -13
  62. slidge/slixfix/xep_0153/vcard_avatar.py +1 -1
  63. slidge/slixfix/xep_0292/vcard4.py +1 -1
  64. slidge/util/archive_msg.py +11 -5
  65. slidge/util/conf.py +23 -20
  66. slidge/util/jid_escaping.py +1 -1
  67. slidge/{core/mixins → util}/lock.py +6 -6
  68. slidge/util/test.py +30 -29
  69. slidge/util/types.py +22 -18
  70. slidge/util/util.py +19 -22
  71. {slidge-0.2.12.dist-info → slidge-0.3.0a0.dist-info}/METADATA +1 -1
  72. slidge-0.3.0a0.dist-info/RECORD +117 -0
  73. {slidge-0.2.12.dist-info → slidge-0.3.0a0.dist-info}/WHEEL +1 -1
  74. slidge-0.2.12.dist-info/RECORD +0 -112
  75. {slidge-0.2.12.dist-info → slidge-0.3.0a0.dist-info}/entry_points.txt +0 -0
  76. {slidge-0.2.12.dist-info → slidge-0.3.0a0.dist-info}/licenses/LICENSE +0 -0
  77. {slidge-0.2.12.dist-info → slidge-0.3.0a0.dist-info}/top_level.txt +0 -0
@@ -23,9 +23,13 @@ class TextMessageMixin(MessageMaker):
23
23
 
24
24
  def _replace_id(self, legacy_msg_id: LegacyMessageType):
25
25
  if self.mtype == "groupchat":
26
- return self.xmpp.store.sent.get_group_xmpp_id(
27
- self.session.user_pk, str(legacy_msg_id)
28
- ) or self._legacy_to_xmpp(legacy_msg_id)
26
+ with self.xmpp.store.session() as orm:
27
+ ids = self.xmpp.store.id_map.get_xmpp(
28
+ orm, self._recipient_pk(), str(legacy_msg_id), True
29
+ )
30
+ if ids:
31
+ return ids[0]
32
+ return self.session.legacy_to_xmpp_msg_id(legacy_msg_id)
29
33
  else:
30
34
  return self._legacy_to_xmpp(legacy_msg_id)
31
35
 
@@ -38,9 +42,9 @@ class TextMessageMixin(MessageMaker):
38
42
  reply_to: Optional[MessageReference] = None,
39
43
  thread: Optional[LegacyThreadType] = None,
40
44
  hints: Optional[Iterable[ProcessingHint]] = None,
41
- carbon=False,
42
- archive_only=False,
43
- correction=False,
45
+ carbon: bool = False,
46
+ archive_only: bool = False,
47
+ correction: bool = False,
44
48
  correction_event_id: Optional[LegacyMessageType] = None,
45
49
  link_previews: Optional[list[LinkPreview]] = None,
46
50
  **send_kwargs,
@@ -69,24 +73,28 @@ class TextMessageMixin(MessageMaker):
69
73
  but store it in the archive. Meant to be used during ``MUC.backfill()``
70
74
  """
71
75
  if carbon and not hasattr(self, "muc"):
72
- if not correction and self.xmpp.store.sent.was_sent_by_user(
73
- self.session.user_pk, str(legacy_msg_id)
74
- ):
75
- log.warning(
76
- "Carbon message for a message an XMPP has sent? This is a bug! %s",
77
- legacy_msg_id,
78
- )
79
- return
80
- if hasattr(self, "muc") and not self.is_user: # type:ignore
81
- log.warning(
82
- "send_text() called with carbon=True on a participant who is not the user",
83
- legacy_msg_id,
76
+ with self.xmpp.store.session() as orm:
77
+ if not correction and self.xmpp.store.id_map.was_sent_by_user(
78
+ orm, self._recipient_pk(), str(legacy_msg_id), self.is_group
79
+ ):
80
+ log.warning(
81
+ "Carbon message for a message an XMPP has sent? This is a bug! %s",
82
+ legacy_msg_id,
83
+ )
84
+ return
85
+ if hasattr(self, "muc") and not self.is_user: # type:ignore
86
+ log.warning(
87
+ "send_text() called with carbon=True on a participant who is not the user",
88
+ legacy_msg_id,
89
+ )
90
+ self.xmpp.store.id_map.set_msg(
91
+ orm,
92
+ self._recipient_pk(),
93
+ str(legacy_msg_id),
94
+ [self.session.legacy_to_xmpp_msg_id(legacy_msg_id)],
95
+ self.is_group,
84
96
  )
85
- self.xmpp.store.sent.set_message(
86
- self.session.user_pk,
87
- str(legacy_msg_id),
88
- self.session.legacy_to_xmpp_msg_id(legacy_msg_id),
89
- )
97
+ orm.commit()
90
98
  hints = self.__default_hints(hints)
91
99
  msg = self._make_message(
92
100
  mbody=body,
@@ -117,12 +125,12 @@ class TextMessageMixin(MessageMaker):
117
125
  reply_to: Optional[MessageReference] = None,
118
126
  thread: Optional[LegacyThreadType] = None,
119
127
  hints: Optional[Iterable[ProcessingHint]] = None,
120
- carbon=False,
121
- archive_only=False,
128
+ carbon: bool = False,
129
+ archive_only: bool = False,
122
130
  correction_event_id: Optional[LegacyMessageType] = None,
123
131
  link_previews: Optional[list[LinkPreview]] = None,
124
132
  **send_kwargs,
125
- ):
133
+ ) -> None:
126
134
  """
127
135
  Modify a message that was previously sent by this :term:`XMPP Entity`.
128
136
 
@@ -165,7 +173,7 @@ class TextMessageMixin(MessageMaker):
165
173
  emojis: Iterable[str] = (),
166
174
  thread: Optional[LegacyThreadType] = None,
167
175
  **kwargs,
168
- ):
176
+ ) -> None:
169
177
  """
170
178
  Send a reaction (:xep:`0444`) from this :term:`XMPP Entity`.
171
179
 
@@ -174,7 +182,7 @@ class TextMessageMixin(MessageMaker):
174
182
  :param thread:
175
183
  """
176
184
  msg = self._make_message(
177
- hints={"store"}, carbon=kwargs.get("carbon"), thread=thread
185
+ hints={"store"}, carbon=bool(kwargs.get("carbon")), thread=thread
178
186
  )
179
187
  xmpp_id = kwargs.pop("xmpp_id", None)
180
188
  if not xmpp_id:
@@ -187,7 +195,7 @@ class TextMessageMixin(MessageMaker):
187
195
  legacy_msg_id: LegacyMessageType,
188
196
  thread: Optional[LegacyThreadType] = None,
189
197
  **kwargs,
190
- ):
198
+ ) -> None:
191
199
  """
192
200
  Send a message retraction (:XEP:`0424`) from this :term:`XMPP Entity`.
193
201
 
@@ -198,7 +206,7 @@ class TextMessageMixin(MessageMaker):
198
206
  state=None,
199
207
  hints={"store"},
200
208
  mbody=f"/me retracted the message {legacy_msg_id}",
201
- carbon=kwargs.get("carbon"),
209
+ carbon=bool(kwargs.get("carbon")),
202
210
  thread=thread,
203
211
  )
204
212
  msg.enable("fallback")
@@ -1,13 +1,20 @@
1
1
  import re
2
2
  from asyncio import Task, sleep
3
3
  from datetime import datetime, timedelta, timezone
4
- from typing import Optional
4
+ from functools import partial
5
+ from typing import TYPE_CHECKING, Optional
5
6
 
6
7
  from slixmpp.types import PresenceShows, PresenceTypes
8
+ from sqlalchemy.exc import InvalidRequestError
9
+ from sqlalchemy.orm.exc import DetachedInstanceError
7
10
 
11
+ from ...db.models import Contact, Participant
8
12
  from ...util.types import CachedPresence
9
- from .. import config
10
13
  from .base import BaseSender
14
+ from .db import DBMixin
15
+
16
+ if TYPE_CHECKING:
17
+ from ..session import BaseSession
11
18
 
12
19
 
13
20
  class _NoChange(Exception):
@@ -15,43 +22,84 @@ class _NoChange(Exception):
15
22
 
16
23
 
17
24
  _FRIEND_REQUEST_PRESENCES = {"subscribe", "unsubscribe", "subscribed", "unsubscribed"}
25
+ _UPDATE_LAST_SEEN_FALLBACK_TASKS = dict[int, Task]()
26
+ _ONE_WEEK_SECONDS = 3600 * 24 * 7
27
+
28
+
29
+ async def _update_last_seen_fallback(session: "BaseSession", contact_pk: int) -> None:
30
+ await sleep(_ONE_WEEK_SECONDS)
31
+ with session.xmpp.store.session() as orm:
32
+ stored = orm.get(Contact, contact_pk)
33
+ if stored is None:
34
+ return
35
+ contact = session.contacts.from_store(stored)
36
+ contact.send_last_presence(force=True, no_cache_online=False)
18
37
 
19
38
 
20
- class PresenceMixin(BaseSender):
39
+ def _clear_last_seen_task(contact_pk: int, _task) -> None:
40
+ try:
41
+ del _UPDATE_LAST_SEEN_FALLBACK_TASKS[contact_pk]
42
+ except KeyError:
43
+ pass
44
+
45
+
46
+ class PresenceMixin(BaseSender, DBMixin):
21
47
  _ONLY_SEND_PRESENCE_CHANGES = False
22
- contact_pk: Optional[int] = None
23
48
 
24
- def __init__(self, *a, **k):
49
+ stored: Contact | Participant
50
+
51
+ def __init__(self, *a, **k) -> None:
25
52
  super().__init__(*a, **k)
26
- # FIXME: this should not be an attribute of this mixin to allow garbage
27
- # collection of instances
28
- self.__update_last_seen_fallback_task: Optional[Task] = None
29
53
  # this is only used when a presence is set during Contact.update_info(),
30
54
  # when the contact does not have a DB primary key yet, and is written
31
55
  # to DB at the end of update_info()
32
56
  self.cached_presence: Optional[CachedPresence] = None
33
57
 
34
- async def __update_last_seen_fallback(self):
35
- await sleep(3600 * 7)
36
- self.send_last_presence(force=True, no_cache_online=False)
58
+ def __stored(self) -> Contact | None:
59
+ if isinstance(self.stored, Contact):
60
+ return self.stored
61
+ else:
62
+ try:
63
+ return self.stored.contact
64
+ except DetachedInstanceError:
65
+ self.merge()
66
+ return self.stored.contact
67
+
68
+ @property
69
+ def __contact_pk(self) -> int | None:
70
+ stored = self.__stored()
71
+ return None if stored is None else stored.id
37
72
 
38
73
  def _get_last_presence(self) -> Optional[CachedPresence]:
39
- if self.contact_pk is None:
74
+ stored = self.__stored()
75
+ if stored is None or not stored.cached_presence:
40
76
  return None
41
- return self.xmpp.store.contacts.get_presence(self.contact_pk)
77
+ return CachedPresence(
78
+ None
79
+ if stored.last_seen is None
80
+ else stored.last_seen.replace(tzinfo=timezone.utc),
81
+ stored.ptype, # type:ignore
82
+ stored.pstatus,
83
+ stored.pshow, # type:ignore
84
+ )
42
85
 
43
- def _store_last_presence(self, new: CachedPresence):
44
- if self.contact_pk is None:
45
- self.cached_presence = new
46
- return
47
- self.xmpp.store.contacts.set_presence(self.contact_pk, new)
86
+ def _store_last_presence(self, new: CachedPresence) -> None:
87
+ stored = self.__stored()
88
+ if stored is not None:
89
+ stored.cached_presence = True
90
+ for k, v in new._asdict().items():
91
+ setattr(stored, k, v)
92
+ try:
93
+ self.commit()
94
+ except InvalidRequestError:
95
+ self.commit(merge=True)
48
96
 
49
97
  def _make_presence(
50
98
  self,
51
99
  *,
52
100
  last_seen: Optional[datetime] = None,
53
- force=False,
54
- bare=False,
101
+ force: bool = False,
102
+ bare: bool = False,
55
103
  ptype: Optional[PresenceTypes] = None,
56
104
  pstatus: Optional[str] = None,
57
105
  pshow: Optional[PresenceShows] = None,
@@ -67,8 +115,10 @@ class PresenceMixin(BaseSender):
67
115
  )
68
116
  if old != new:
69
117
  if hasattr(self, "muc") and ptype == "unavailable":
70
- if self.contact_pk is not None:
71
- self.xmpp.store.contacts.reset_presence(self.contact_pk)
118
+ stored = self.__stored()
119
+ if stored is not None:
120
+ stored.cached_presence = False
121
+ self.commit(merge=True)
72
122
  else:
73
123
  self._store_last_presence(new)
74
124
  if old and not force and self._ONLY_SEND_PRESENCE_CHANGES:
@@ -87,27 +137,33 @@ class PresenceMixin(BaseSender):
87
137
  )
88
138
  if last_seen:
89
139
  # it's ugly to check for the presence of this string, but a better fix is more work
90
- if config.LAST_SEEN_FALLBACK and not re.match(
140
+ if not re.match(
91
141
  ".*Last seen .*", p["status"]
92
- ):
142
+ ) and self.session.user.preferences.get("last_seen_fallback", True):
93
143
  last_seen_fallback, recent = get_last_seen_fallback(last_seen)
94
144
  if p["status"]:
95
145
  p["status"] = p["status"] + " -- " + last_seen_fallback
96
146
  else:
97
147
  p["status"] = last_seen_fallback
98
- if recent:
148
+ pk = self.__contact_pk
149
+ if recent and pk is not None:
99
150
  # if less than a week, we use sth like 'Last seen: Monday, 8:05",
100
151
  # but if lasts more than a week, this is not very informative, so
101
152
  # we need to force resend an updated presence status
102
- if self.__update_last_seen_fallback_task:
103
- self.__update_last_seen_fallback_task.cancel()
104
- self.__update_last_seen_fallback_task = self.xmpp.loop.create_task(
105
- self.__update_last_seen_fallback()
153
+ task = _UPDATE_LAST_SEEN_FALLBACK_TASKS.get(pk)
154
+ if task is not None:
155
+ task.cancel()
156
+ task = self.session.create_task(
157
+ _update_last_seen_fallback(self.session, pk)
106
158
  )
159
+ _UPDATE_LAST_SEEN_FALLBACK_TASKS[pk] = task
160
+ task.add_done_callback(partial(_clear_last_seen_task, pk))
107
161
  p["idle"]["since"] = last_seen
108
162
  return p
109
163
 
110
- def send_last_presence(self, force=False, no_cache_online=False):
164
+ def send_last_presence(
165
+ self, force: bool = False, no_cache_online: bool = False
166
+ ) -> None:
111
167
  if (cache := self._get_last_presence()) is None:
112
168
  if force:
113
169
  if no_cache_online:
@@ -129,7 +185,7 @@ class PresenceMixin(BaseSender):
129
185
  self,
130
186
  status: Optional[str] = None,
131
187
  last_seen: Optional[datetime] = None,
132
- ):
188
+ ) -> None:
133
189
  """
134
190
  Send an "online" presence from this contact to the user.
135
191
 
@@ -145,7 +201,7 @@ class PresenceMixin(BaseSender):
145
201
  self,
146
202
  status: Optional[str] = None,
147
203
  last_seen: Optional[datetime] = None,
148
- ):
204
+ ) -> None:
149
205
  """
150
206
  Send an "away" presence from this contact to the user.
151
207
 
@@ -166,7 +222,7 @@ class PresenceMixin(BaseSender):
166
222
  self,
167
223
  status: Optional[str] = None,
168
224
  last_seen: Optional[datetime] = None,
169
- ):
225
+ ) -> None:
170
226
  """
171
227
  Send an "extended away" presence from this contact to the user.
172
228
 
@@ -187,7 +243,7 @@ class PresenceMixin(BaseSender):
187
243
  self,
188
244
  status: Optional[str] = None,
189
245
  last_seen: Optional[datetime] = None,
190
- ):
246
+ ) -> None:
191
247
  """
192
248
  Send a "busy" (ie, "dnd") presence from this contact to the user,
193
249
 
@@ -205,7 +261,7 @@ class PresenceMixin(BaseSender):
205
261
  self,
206
262
  status: Optional[str] = None,
207
263
  last_seen: Optional[datetime] = None,
208
- ):
264
+ ) -> None:
209
265
  """
210
266
  Send an "offline" presence from this contact to the user.
211
267
 
slidge/core/pubsub.py CHANGED
@@ -21,8 +21,8 @@ from slixmpp.plugins.xep_0292.stanza import VCard4
21
21
  from slixmpp.types import JidStr, OptJidStr
22
22
 
23
23
  from ..db.avatar import CachedAvatar, avatar_cache
24
- from ..db.store import ContactStore, SlidgeStore
25
- from .mixins.lock import NamedLockMixin
24
+ from ..db.models import GatewayUser
25
+ from ..util.lock import NamedLockMixin
26
26
 
27
27
  if TYPE_CHECKING:
28
28
  from slidge.core.gateway import BaseGateway
@@ -32,14 +32,8 @@ if TYPE_CHECKING:
32
32
  VCARD4_NAMESPACE = "urn:xmpp:vcard4"
33
33
 
34
34
 
35
- class PepItem:
36
- pass
37
-
38
-
39
- class PepAvatar(PepItem):
40
- store: SlidgeStore
41
-
42
- def __init__(self):
35
+ class PepAvatar:
36
+ def __init__(self) -> None:
43
37
  self.metadata: Optional[AvatarMetadata] = None
44
38
  self.id: Optional[str] = None
45
39
  self._avatar_data_path: Optional[Path] = None
@@ -52,7 +46,7 @@ class PepAvatar(PepItem):
52
46
  data.set_value(self._avatar_data_path.read_bytes())
53
47
  return data
54
48
 
55
- def set_avatar_from_cache(self, cached_avatar: CachedAvatar):
49
+ def set_avatar_from_cache(self, cached_avatar: CachedAvatar) -> None:
56
50
  metadata = AvatarMetadata()
57
51
  self.id = cached_avatar.hash
58
52
  metadata.add_info(
@@ -66,17 +60,6 @@ class PepAvatar(PepItem):
66
60
  self._avatar_data_path = cached_avatar.path
67
61
 
68
62
 
69
- class PepNick(PepItem):
70
- contact_store: ContactStore
71
-
72
- def __init__(self, nick: Optional[str] = None):
73
- nickname = UserNick()
74
- if nick is not None:
75
- nickname["nick"] = nick
76
- self.nick = nickname
77
- self.__nick_str = nick
78
-
79
-
80
63
  class PubSubComponent(NamedLockMixin, BasePlugin):
81
64
  xmpp: "BaseGateway"
82
65
 
@@ -91,11 +74,11 @@ class PubSubComponent(NamedLockMixin, BasePlugin):
91
74
  default_config = {"component_name": None}
92
75
  component_name: str
93
76
 
94
- def __init__(self, *a, **kw):
77
+ def __init__(self, *a, **kw) -> None:
95
78
  super(PubSubComponent, self).__init__(*a, **kw)
96
79
  register_stanza_plugin(EventItem, UserNick)
97
80
 
98
- def plugin_init(self):
81
+ def plugin_init(self) -> None:
99
82
  self.xmpp.register_handler(
100
83
  CoroutineCallback(
101
84
  "pubsub_get_avatar_data",
@@ -144,7 +127,7 @@ class PubSubComponent(NamedLockMixin, BasePlugin):
144
127
 
145
128
  async def on_presence_available(
146
129
  self, p: Presence, contact: Optional["LegacyContact"]
147
- ):
130
+ ) -> None:
148
131
  if p.get_plugin("muc_join", check=True) is not None:
149
132
  log.debug("Ignoring MUC presence here")
150
133
  return
@@ -177,14 +160,16 @@ class PubSubComponent(NamedLockMixin, BasePlugin):
177
160
  except XMPPError:
178
161
  pass
179
162
  else:
180
- await self.__broadcast(data=pep_nick.nick, from_=p.get_to(), to=from_)
163
+ await self.__broadcast(data=pep_nick, from_=p.get_to(), to=from_)
181
164
 
182
165
  if contact is not None and VCARD4_NAMESPACE + "+notify" in features:
183
166
  await self.broadcast_vcard_event(
184
167
  p.get_to(), from_, await contact.get_vcard()
185
168
  )
186
169
 
187
- async def broadcast_vcard_event(self, from_: JID, to: JID, vcard: VCard4 | None):
170
+ async def broadcast_vcard_event(
171
+ self, from_: JID, to: JID, vcard: VCard4 | None
172
+ ) -> None:
188
173
  item = Item()
189
174
  item.namespace = VCARD4_NAMESPACE
190
175
  item["id"] = "current"
@@ -211,33 +196,33 @@ class PubSubComponent(NamedLockMixin, BasePlugin):
211
196
  ) -> PepAvatar:
212
197
  if stanza.get_to() == self.xmpp.boundjid.bare:
213
198
  item = PepAvatar()
214
- if hasattr(self.xmpp, "avatar_pk"):
215
- item.set_avatar_from_cache(avatar_cache.get_by_pk(self.xmpp.avatar_pk))
199
+ if self.xmpp.avatar is not None:
200
+ item.set_avatar_from_cache(self.xmpp.avatar)
216
201
  return item
217
202
 
218
203
  if contact is None:
219
204
  contact = await self.__get_contact(stanza)
220
205
 
221
206
  item = PepAvatar()
222
- if contact.avatar_pk is not None:
223
- stored = avatar_cache.get_by_pk(contact.avatar_pk)
207
+ if contact.stored.avatar is not None:
208
+ stored = avatar_cache.get(contact.stored.avatar)
224
209
  assert stored is not None
225
210
  item.set_avatar_from_cache(stored)
226
211
  return item
227
212
 
228
213
  async def _get_authorized_nick(
229
214
  self, stanza: Union[Iq, Presence], contact: Optional["LegacyContact"] = None
230
- ) -> PepNick:
215
+ ) -> UserNick:
231
216
  if stanza.get_to() == self.xmpp.boundjid.bare:
232
- return PepNick(self.xmpp.COMPONENT_NAME)
217
+ return get_user_nick(self.xmpp.COMPONENT_NAME)
233
218
 
234
219
  if contact is None:
235
220
  contact = await self.__get_contact(stanza)
236
221
 
237
222
  if contact.name is not None:
238
- return PepNick(contact.name)
223
+ return get_user_nick(contact.name)
239
224
  else:
240
- return PepNick()
225
+ return UserNick()
241
226
 
242
227
  def __reply_with(
243
228
  self, iq: Iq, content: AvatarData | AvatarMetadata | None, item_id: str | None
@@ -254,11 +239,11 @@ class PubSubComponent(NamedLockMixin, BasePlugin):
254
239
  else:
255
240
  raise XMPPError("item-not-found")
256
241
 
257
- async def _get_avatar_data(self, iq: Iq):
242
+ async def _get_avatar_data(self, iq: Iq) -> None:
258
243
  pep_avatar = await self._get_authorized_avatar(iq)
259
244
  self.__reply_with(iq, pep_avatar.data, pep_avatar.id)
260
245
 
261
- async def _get_avatar_metadata(self, iq: Iq):
246
+ async def _get_avatar_metadata(self, iq: Iq) -> None:
262
247
  pep_avatar = await self._get_authorized_avatar(iq)
263
248
  self.__reply_with(iq, pep_avatar.metadata, pep_avatar.id)
264
249
 
@@ -279,7 +264,7 @@ class PubSubComponent(NamedLockMixin, BasePlugin):
279
264
  payload: Optional[Union[AvatarMetadata, AvatarData, VCard4]],
280
265
  id_: Optional[str],
281
266
  namespace: Optional[str] = None,
282
- ):
267
+ ) -> None:
283
268
  result = iq.reply()
284
269
  item = Item()
285
270
  if payload:
@@ -291,7 +276,9 @@ class PubSubComponent(NamedLockMixin, BasePlugin):
291
276
  result["pubsub"]["items"].append(item)
292
277
  result.send()
293
278
 
294
- async def __broadcast(self, data, from_: JidStr, to: OptJidStr = None, **kwargs):
279
+ async def __broadcast(
280
+ self, data, from_: JidStr, to: OptJidStr = None, **kwargs
281
+ ) -> None:
295
282
  from_ = JID(from_)
296
283
  if from_ != self.xmpp.boundjid.bare and to is not None:
297
284
  to = JID(to)
@@ -319,10 +306,11 @@ class PubSubComponent(NamedLockMixin, BasePlugin):
319
306
  msg.append(event)
320
307
 
321
308
  if to is None:
322
- for u in self.xmpp.store.users.get_all():
323
- new_msg = copy(msg)
324
- new_msg.set_to(u.jid.bare)
325
- new_msg.send()
309
+ with self.xmpp.store.session() as orm:
310
+ for u in orm.query(GatewayUser).all():
311
+ new_msg = copy(msg)
312
+ new_msg.set_to(u.jid.bare)
313
+ new_msg.send()
326
314
  else:
327
315
  msg.set_to(to)
328
316
  msg.send()
@@ -345,11 +333,18 @@ class PubSubComponent(NamedLockMixin, BasePlugin):
345
333
  user_jid: JID,
346
334
  jid: JidStr,
347
335
  nick: Optional[str] = None,
348
- ):
336
+ ) -> None:
349
337
  jid = JID(jid)
350
- nickname = PepNick(nick)
351
- log.debug("New nickname: %s", nickname.nick)
352
- self.xmpp.loop.create_task(self.__broadcast(nickname.nick, jid, user_jid.bare))
338
+ nickname = get_user_nick(nick)
339
+ log.debug("New nickname: %s", nickname)
340
+ self.xmpp.loop.create_task(self.__broadcast(nickname, jid, user_jid.bare))
341
+
342
+
343
+ def get_user_nick(nick: Optional[str] = None) -> UserNick:
344
+ user_nick = UserNick()
345
+ if nick is not None:
346
+ user_nick["nick"] = nick
347
+ return user_nick
353
348
 
354
349
 
355
350
  log = logging.getLogger(__name__)