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.
- slidge/__init__.py +5 -2
- slidge/command/adhoc.py +9 -3
- slidge/command/admin.py +16 -12
- slidge/command/base.py +16 -12
- slidge/command/chat_command.py +25 -16
- slidge/command/user.py +7 -8
- slidge/contact/contact.py +119 -209
- slidge/contact/roster.py +106 -105
- slidge/core/config.py +2 -43
- slidge/core/dispatcher/caps.py +9 -2
- slidge/core/dispatcher/disco.py +13 -3
- slidge/core/dispatcher/message/__init__.py +1 -1
- slidge/core/dispatcher/message/chat_state.py +17 -8
- slidge/core/dispatcher/message/marker.py +7 -5
- slidge/core/dispatcher/message/message.py +117 -92
- slidge/core/dispatcher/muc/__init__.py +1 -1
- slidge/core/dispatcher/muc/admin.py +4 -4
- slidge/core/dispatcher/muc/mam.py +10 -6
- slidge/core/dispatcher/muc/misc.py +4 -2
- slidge/core/dispatcher/muc/owner.py +5 -3
- slidge/core/dispatcher/muc/ping.py +3 -1
- slidge/core/dispatcher/presence.py +21 -15
- slidge/core/dispatcher/registration.py +20 -12
- slidge/core/dispatcher/search.py +7 -3
- slidge/core/dispatcher/session_dispatcher.py +13 -5
- slidge/core/dispatcher/util.py +37 -27
- slidge/core/dispatcher/vcard.py +7 -4
- slidge/core/gateway.py +168 -84
- slidge/core/mixins/__init__.py +1 -11
- slidge/core/mixins/attachment.py +163 -148
- slidge/core/mixins/avatar.py +100 -177
- slidge/core/mixins/db.py +50 -2
- slidge/core/mixins/message.py +19 -17
- slidge/core/mixins/message_maker.py +29 -15
- slidge/core/mixins/message_text.py +38 -30
- slidge/core/mixins/presence.py +91 -35
- slidge/core/pubsub.py +42 -47
- slidge/core/session.py +88 -57
- slidge/db/alembic/versions/0337c90c0b96_unify_legacy_xmpp_id_mappings.py +183 -0
- slidge/db/alembic/versions/4dbd23a3f868_new_avatar_store.py +56 -0
- slidge/db/alembic/versions/54ce3cde350c_use_hash_for_avatar_filenames.py +50 -0
- slidge/db/alembic/versions/58b98dacf819_refactor.py +118 -0
- slidge/db/alembic/versions/75a62b74b239_ditch_hats_table.py +74 -0
- slidge/db/avatar.py +150 -119
- slidge/db/meta.py +33 -22
- slidge/db/models.py +68 -117
- slidge/db/store.py +412 -1094
- slidge/group/archive.py +61 -54
- slidge/group/bookmarks.py +74 -55
- slidge/group/participant.py +135 -142
- slidge/group/room.py +315 -312
- slidge/main.py +28 -18
- slidge/migration.py +2 -12
- slidge/slixfix/__init__.py +20 -4
- slidge/slixfix/delivery_receipt.py +6 -4
- slidge/slixfix/link_preview/link_preview.py +1 -1
- slidge/slixfix/link_preview/stanza.py +1 -1
- slidge/slixfix/roster.py +5 -7
- slidge/slixfix/xep_0077/register.py +8 -8
- slidge/slixfix/xep_0077/stanza.py +7 -7
- slidge/slixfix/xep_0100/gateway.py +12 -13
- slidge/slixfix/xep_0153/vcard_avatar.py +1 -1
- slidge/slixfix/xep_0292/vcard4.py +1 -1
- slidge/util/archive_msg.py +11 -5
- slidge/util/conf.py +23 -20
- slidge/util/jid_escaping.py +1 -1
- slidge/{core/mixins → util}/lock.py +6 -6
- slidge/util/test.py +30 -29
- slidge/util/types.py +22 -18
- slidge/util/util.py +19 -22
- {slidge-0.2.12.dist-info → slidge-0.3.0a0.dist-info}/METADATA +1 -1
- slidge-0.3.0a0.dist-info/RECORD +117 -0
- {slidge-0.2.12.dist-info → slidge-0.3.0a0.dist-info}/WHEEL +1 -1
- slidge-0.2.12.dist-info/RECORD +0 -112
- {slidge-0.2.12.dist-info → slidge-0.3.0a0.dist-info}/entry_points.txt +0 -0
- {slidge-0.2.12.dist-info → slidge-0.3.0a0.dist-info}/licenses/LICENSE +0 -0
- {slidge-0.2.12.dist-info → slidge-0.3.0a0.dist-info}/top_level.txt +0 -0
slidge/contact/contact.py
CHANGED
@@ -2,20 +2,19 @@ import datetime
|
|
2
2
|
import logging
|
3
3
|
import warnings
|
4
4
|
from datetime import date
|
5
|
-
from typing import TYPE_CHECKING, Generic, Iterable,
|
5
|
+
from typing import TYPE_CHECKING, Generic, Iterable, Iterator, Optional, Union
|
6
6
|
from xml.etree import ElementTree as ET
|
7
7
|
|
8
8
|
from slixmpp import JID, Message, Presence
|
9
9
|
from slixmpp.exceptions import IqError, IqTimeout
|
10
10
|
from slixmpp.plugins.xep_0292.stanza import VCard4
|
11
11
|
from slixmpp.types import MessageTypes
|
12
|
+
from sqlalchemy.exc import IntegrityError
|
12
13
|
|
13
|
-
from ..core import
|
14
|
-
from ..core.mixins import AvatarMixin, FullCarbonMixin, StoredAttributeMixin
|
15
|
-
from ..core.mixins.db import UpdateInfoMixin
|
14
|
+
from ..core.mixins import AvatarMixin, FullCarbonMixin
|
16
15
|
from ..core.mixins.disco import ContactAccountDiscoMixin
|
17
16
|
from ..core.mixins.recipient import ReactionRecipientMixin, ThreadRecipientMixin
|
18
|
-
from ..db.models import Contact
|
17
|
+
from ..db.models import Contact, ContactSent, Participant
|
19
18
|
from ..util import SubclassableOnce
|
20
19
|
from ..util.types import ClientType, LegacyUserIdType, MessageOrPresenceTypeVar
|
21
20
|
|
@@ -26,13 +25,11 @@ if TYPE_CHECKING:
|
|
26
25
|
|
27
26
|
class LegacyContact(
|
28
27
|
Generic[LegacyUserIdType],
|
29
|
-
StoredAttributeMixin,
|
30
28
|
AvatarMixin,
|
31
29
|
ContactAccountDiscoMixin,
|
32
30
|
FullCarbonMixin,
|
33
31
|
ReactionRecipientMixin,
|
34
32
|
ThreadRecipientMixin,
|
35
|
-
UpdateInfoMixin,
|
36
33
|
metaclass=SubclassableOnce,
|
37
34
|
):
|
38
35
|
"""
|
@@ -85,104 +82,68 @@ class LegacyContact(
|
|
85
82
|
STRIP_SHORT_DELAY = True
|
86
83
|
_NON_FRIEND_PRESENCES_FILTER = {"subscribe", "unsubscribed"}
|
87
84
|
|
88
|
-
_avatar_bare_jid = True
|
89
|
-
|
90
85
|
INVITATION_RECIPIENT = True
|
91
86
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
jid_username: str,
|
97
|
-
):
|
98
|
-
"""
|
99
|
-
:param session: The session this contact is part of
|
100
|
-
:param legacy_id: The contact's legacy ID
|
101
|
-
:param jid_username: User part of this contact's 'puppet' JID.
|
102
|
-
NB: case-insensitive, and some special characters are not allowed
|
103
|
-
"""
|
104
|
-
super().__init__()
|
87
|
+
stored: Contact
|
88
|
+
model: Contact
|
89
|
+
|
90
|
+
def __init__(self, session: "BaseSession", stored: Contact) -> None:
|
105
91
|
self.session = session
|
106
|
-
self.
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
:term:`XMPP Entity`.
|
111
|
-
|
112
|
-
Controlling what values are valid and how they are translated from a
|
113
|
-
:term:`JID Local Part` is done in :meth:`.jid_username_to_legacy_id`.
|
114
|
-
Reciprocally, in :meth:`legacy_id_to_jid_username` the inverse
|
115
|
-
transformation is defined.
|
116
|
-
"""
|
117
|
-
self.jid_username = jid_username
|
92
|
+
self.xmpp = session.xmpp
|
93
|
+
self.stored = stored
|
94
|
+
self._set_logger()
|
95
|
+
super().__init__()
|
118
96
|
|
119
|
-
|
97
|
+
@property
|
98
|
+
def jid(self):
|
99
|
+
jid = JID(self.stored.jid)
|
100
|
+
jid.resource = self.RESOURCE
|
101
|
+
return jid
|
120
102
|
|
121
|
-
|
122
|
-
|
123
|
-
self.
|
124
|
-
|
125
|
-
|
126
|
-
self.
|
127
|
-
self._added_to_roster = False
|
128
|
-
self._caps_ver: str | None = None
|
129
|
-
self._vcard_fetched = False
|
130
|
-
self._vcard: str | None = None
|
131
|
-
self._client_type: ClientType = "pc"
|
132
|
-
|
133
|
-
async def get_vcard(self, fetch=True) -> VCard4 | None:
|
134
|
-
if fetch and not self._vcard_fetched:
|
103
|
+
@property
|
104
|
+
def legacy_id(self):
|
105
|
+
return self.xmpp.LEGACY_CONTACT_ID_TYPE(self.stored.legacy_id)
|
106
|
+
|
107
|
+
async def get_vcard(self, fetch: bool = True) -> VCard4 | None:
|
108
|
+
if fetch and not self.stored.vcard_fetched:
|
135
109
|
await self.fetch_vcard()
|
136
|
-
if self.
|
110
|
+
if self.stored.vcard is None:
|
137
111
|
return None
|
138
112
|
|
139
|
-
return VCard4(xml=ET.fromstring(self.
|
113
|
+
return VCard4(xml=ET.fromstring(self.stored.vcard))
|
140
114
|
|
141
115
|
@property
|
142
|
-
def is_friend(self):
|
143
|
-
return self.
|
116
|
+
def is_friend(self) -> bool:
|
117
|
+
return self.stored.is_friend
|
144
118
|
|
145
119
|
@is_friend.setter
|
146
|
-
def is_friend(self, value: bool):
|
147
|
-
if value == self.
|
120
|
+
def is_friend(self, value: bool) -> None:
|
121
|
+
if value == self.is_friend:
|
148
122
|
return
|
149
|
-
self.
|
150
|
-
|
151
|
-
return
|
152
|
-
self.__ensure_pk()
|
153
|
-
assert self.contact_pk is not None
|
154
|
-
self.xmpp.store.contacts.set_friend(self.contact_pk, value)
|
123
|
+
self.stored.is_friend = value
|
124
|
+
self.commit()
|
155
125
|
|
156
126
|
@property
|
157
|
-
def added_to_roster(self):
|
158
|
-
return self.
|
127
|
+
def added_to_roster(self) -> bool:
|
128
|
+
return self.stored.added_to_roster
|
159
129
|
|
160
130
|
@added_to_roster.setter
|
161
|
-
def added_to_roster(self, value: bool):
|
162
|
-
if value == self.
|
163
|
-
return
|
164
|
-
self._added_to_roster = value
|
165
|
-
if self._updating_info:
|
166
|
-
return
|
167
|
-
if self.contact_pk is None:
|
168
|
-
# during LegacyRoster.fill()
|
131
|
+
def added_to_roster(self, value: bool) -> None:
|
132
|
+
if value == self.added_to_roster:
|
169
133
|
return
|
170
|
-
self.
|
134
|
+
self.stored.added_to_roster = value
|
135
|
+
self.commit()
|
171
136
|
|
172
137
|
@property
|
173
|
-
def participants(self) ->
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
self.session, stored, contact=self
|
183
|
-
)
|
184
|
-
for stored in self.xmpp.store.participants.get_for_contact(self.contact_pk)
|
185
|
-
]
|
138
|
+
def participants(self) -> Iterator["LegacyParticipant"]:
|
139
|
+
with self.xmpp.store.session() as orm:
|
140
|
+
self.stored = orm.merge(self.stored)
|
141
|
+
participants = self.stored.participants
|
142
|
+
for p in participants:
|
143
|
+
with self.xmpp.store.session() as orm:
|
144
|
+
orm.add(p)
|
145
|
+
muc = self.session.bookmarks.from_store(p.room)
|
146
|
+
yield muc.participant_from_store(p, contact=self)
|
186
147
|
|
187
148
|
@property
|
188
149
|
def user_jid(self):
|
@@ -190,7 +151,7 @@ class LegacyContact(
|
|
190
151
|
|
191
152
|
@property # type:ignore
|
192
153
|
def DISCO_TYPE(self) -> ClientType:
|
193
|
-
return self.
|
154
|
+
return self.client_type
|
194
155
|
|
195
156
|
@DISCO_TYPE.setter
|
196
157
|
def DISCO_TYPE(self, value: ClientType) -> None:
|
@@ -203,47 +164,27 @@ class LegacyContact(
|
|
203
164
|
|
204
165
|
Default is "pc".
|
205
166
|
"""
|
206
|
-
return self.
|
167
|
+
return self.stored.client_type
|
207
168
|
|
208
169
|
@client_type.setter
|
209
170
|
def client_type(self, value: ClientType) -> None:
|
210
|
-
self.
|
211
|
-
if self._updating_info:
|
171
|
+
if self.stored.client_type == value:
|
212
172
|
return
|
213
|
-
self.
|
214
|
-
|
215
|
-
self.xmpp.store.contacts.set_client_type(self.contact_pk, value)
|
216
|
-
|
217
|
-
def _set_logger_name(self):
|
218
|
-
self.log.name = f"{self.user_jid.bare}:contact:{self}"
|
173
|
+
self.stored.client_type = value
|
174
|
+
self.commit()
|
219
175
|
|
220
|
-
def
|
221
|
-
|
176
|
+
def _set_logger(self) -> None:
|
177
|
+
self.log = logging.getLogger(f"{self.user_jid.bare}:contact:{self}")
|
222
178
|
|
223
|
-
def
|
224
|
-
|
225
|
-
return
|
226
|
-
# This happens for legacy modules that don't follow the Roster.fill /
|
227
|
-
# populate contact attributes in Contact.update_info() method.
|
228
|
-
# This results in (even) less optimised SQL writes and read, but
|
229
|
-
# we allow it because it fits some legacy network libs better.
|
230
|
-
with self.xmpp.store.session() as orm:
|
231
|
-
orm.commit()
|
232
|
-
stored = self.xmpp.store.contacts.get_by_legacy_id(
|
233
|
-
self.user_pk, str(self.legacy_id)
|
234
|
-
)
|
235
|
-
if stored is None:
|
236
|
-
self.contact_pk = self.xmpp.store.contacts.update(self, commit=True)
|
237
|
-
else:
|
238
|
-
self.contact_pk = stored.id
|
239
|
-
assert self.contact_pk is not None
|
179
|
+
def __repr__(self) -> str:
|
180
|
+
return f"<Contact #{self.stored.id} '{self.name}' ({self.legacy_id} - {self.jid.user})'>"
|
240
181
|
|
241
|
-
def __get_subscription_string(self):
|
182
|
+
def __get_subscription_string(self) -> str:
|
242
183
|
if self.is_friend:
|
243
184
|
return "both"
|
244
185
|
return "none"
|
245
186
|
|
246
|
-
def __propagate_to_participants(self, stanza: Presence):
|
187
|
+
def __propagate_to_participants(self, stanza: Presence) -> None:
|
247
188
|
if not self.PROPAGATE_PRESENCE_TO_GROUPS:
|
248
189
|
return
|
249
190
|
|
@@ -274,7 +215,11 @@ class LegacyContact(
|
|
274
215
|
func(**kw)
|
275
216
|
|
276
217
|
def _send(
|
277
|
-
self,
|
218
|
+
self,
|
219
|
+
stanza: MessageOrPresenceTypeVar,
|
220
|
+
carbon: bool = False,
|
221
|
+
nick: bool = False,
|
222
|
+
**send_kwargs,
|
278
223
|
) -> MessageOrPresenceTypeVar:
|
279
224
|
if carbon and isinstance(stanza, Message):
|
280
225
|
stanza["to"] = self.jid.bare
|
@@ -299,9 +244,15 @@ class LegacyContact(
|
|
299
244
|
and self.xmpp.MARK_ALL_MESSAGES
|
300
245
|
and is_markable(stanza)
|
301
246
|
):
|
302
|
-
|
303
|
-
|
304
|
-
|
247
|
+
try:
|
248
|
+
with self.xmpp.store.session(expire_on_commit=False) as orm:
|
249
|
+
self.stored = orm.merge(self.stored)
|
250
|
+
new = ContactSent(contact=self.stored, msg_id=stanza["id"])
|
251
|
+
orm.add(new)
|
252
|
+
self.stored.sent_order.append(new)
|
253
|
+
orm.commit()
|
254
|
+
except IntegrityError:
|
255
|
+
self.log.warning("Contact has already sent message %s", stanza["id"])
|
305
256
|
stanza["to"] = self.user_jid
|
306
257
|
stanza.send()
|
307
258
|
return stanza
|
@@ -320,50 +271,42 @@ class LegacyContact(
|
|
320
271
|
:param horizon_xmpp_id: The latest message
|
321
272
|
:return: A list of XMPP ids or None if horizon_xmpp_id was not found
|
322
273
|
"""
|
323
|
-
self.
|
324
|
-
|
325
|
-
|
274
|
+
with self.xmpp.store.session() as orm:
|
275
|
+
assert self.stored.id is not None
|
276
|
+
ids = self.xmpp.store.contacts.pop_sent_up_to(
|
277
|
+
orm, self.stored.id, horizon_xmpp_id
|
278
|
+
)
|
279
|
+
orm.commit()
|
280
|
+
return ids
|
326
281
|
|
327
282
|
@property
|
328
|
-
def name(self):
|
283
|
+
def name(self) -> str:
|
329
284
|
"""
|
330
285
|
Friendly name of the contact, as it should appear in the user's roster
|
331
286
|
"""
|
332
|
-
return self.
|
287
|
+
return self.stored.nick or ""
|
333
288
|
|
334
289
|
@name.setter
|
335
|
-
def name(self, n: Optional[str]):
|
336
|
-
if self.
|
290
|
+
def name(self, n: Optional[str]) -> None:
|
291
|
+
if self.stored.nick == n:
|
337
292
|
return
|
338
|
-
self.
|
339
|
-
self.
|
293
|
+
self.stored.nick = n
|
294
|
+
self._set_logger()
|
340
295
|
if self.is_friend and self.added_to_roster:
|
341
296
|
self.xmpp.pubsub.broadcast_nick(
|
342
297
|
user_jid=self.user_jid, jid=self.jid.bare, nick=n
|
343
298
|
)
|
344
|
-
|
345
|
-
# means we're in update_info(), so no participants, and no need
|
346
|
-
# to write to DB now, it will be called in Roster.__finish_init_contact
|
347
|
-
return
|
299
|
+
self.commit()
|
348
300
|
for p in self.participants:
|
349
301
|
p.nickname = n
|
350
|
-
self.__ensure_pk()
|
351
|
-
assert self.contact_pk is not None
|
352
|
-
self.xmpp.store.contacts.update_nick(self.contact_pk, n)
|
353
302
|
|
354
|
-
def
|
355
|
-
if self.
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
assert self.contact_pk is not None
|
362
|
-
self.xmpp.store.contacts.set_avatar(
|
363
|
-
self.contact_pk,
|
364
|
-
self._avatar_pk,
|
365
|
-
None if self.avatar_id is None else str(self.avatar_id),
|
366
|
-
)
|
303
|
+
def _post_avatar_update(self, cached_avatar) -> None:
|
304
|
+
if self.is_friend and self.added_to_roster:
|
305
|
+
self.session.create_task(
|
306
|
+
self.session.xmpp.pubsub.broadcast_avatar(
|
307
|
+
self.jid.bare, self.session.user_jid, cached_avatar
|
308
|
+
)
|
309
|
+
)
|
367
310
|
for p in self.participants:
|
368
311
|
self.log.debug("Propagating new avatar to %s", p.muc)
|
369
312
|
p.send_last_presence(force=True, no_cache_online=True)
|
@@ -382,7 +325,7 @@ class LegacyContact(
|
|
382
325
|
email: Optional[str] = None,
|
383
326
|
country: Optional[str] = None,
|
384
327
|
locality: Optional[str] = None,
|
385
|
-
):
|
328
|
+
) -> None:
|
386
329
|
vcard = VCard4()
|
387
330
|
vcard.add_impp(f"xmpp:{self.jid.bare}")
|
388
331
|
|
@@ -415,17 +358,13 @@ class LegacyContact(
|
|
415
358
|
elif country:
|
416
359
|
vcard.add_address(country, locality)
|
417
360
|
|
418
|
-
self.
|
419
|
-
self.
|
361
|
+
self.stored.vcard = str(vcard)
|
362
|
+
self.stored.vcard_fetched = True
|
420
363
|
self.session.create_task(
|
421
364
|
self.xmpp.pubsub.broadcast_vcard_event(self.jid, self.user_jid, vcard)
|
422
365
|
)
|
423
366
|
|
424
|
-
|
425
|
-
return
|
426
|
-
|
427
|
-
assert self.contact_pk is not None
|
428
|
-
self.xmpp.store.contacts.set_vcard(self.contact_pk, self._vcard)
|
367
|
+
self.commit()
|
429
368
|
|
430
369
|
def get_roster_item(self):
|
431
370
|
item = {
|
@@ -436,7 +375,7 @@ class LegacyContact(
|
|
436
375
|
item["name"] = n
|
437
376
|
return {self.jid.bare: item}
|
438
377
|
|
439
|
-
async def add_to_roster(self, force=False):
|
378
|
+
async def add_to_roster(self, force: bool = False) -> None:
|
440
379
|
"""
|
441
380
|
Add this contact to the user roster using :xep:`0356`
|
442
381
|
|
@@ -444,7 +383,7 @@ class LegacyContact(
|
|
444
383
|
"""
|
445
384
|
if self.added_to_roster and not force:
|
446
385
|
return
|
447
|
-
if
|
386
|
+
if not self.session.user.preferences.get("roster_push", True):
|
448
387
|
log.debug("Roster push request by plugin ignored (--no-roster-push)")
|
449
388
|
return
|
450
389
|
try:
|
@@ -452,16 +391,16 @@ class LegacyContact(
|
|
452
391
|
jid=self.user_jid, roster_items=self.get_roster_item()
|
453
392
|
)
|
454
393
|
except PermissionError:
|
394
|
+
from slidge import __version__
|
395
|
+
|
455
396
|
warnings.warn(
|
456
|
-
"Slidge does not have
|
457
|
-
"
|
458
|
-
|
397
|
+
"Slidge does not have the privilege to manage rosters. See "
|
398
|
+
f"https://slidge.im/docs/slidge/{__version__}/admin/privilege.html"
|
399
|
+
)
|
400
|
+
self.send_friend_request(
|
401
|
+
f"I'm already your friend on {self.xmpp.COMPONENT_TYPE}, but "
|
402
|
+
"slidge is not allowed to manage your roster."
|
459
403
|
)
|
460
|
-
if config.ROSTER_PUSH_PRESENCE_SUBSCRIPTION_REQUEST_FALLBACK:
|
461
|
-
self.send_friend_request(
|
462
|
-
f"I'm already your friend on {self.xmpp.COMPONENT_TYPE}, but "
|
463
|
-
"slidge is not allowed to manage your roster."
|
464
|
-
)
|
465
404
|
return
|
466
405
|
except (IqError, IqTimeout) as e:
|
467
406
|
self.log.warning("Could not add to roster", exc_info=e)
|
@@ -471,7 +410,7 @@ class LegacyContact(
|
|
471
410
|
self.added_to_roster = True
|
472
411
|
self.send_last_presence()
|
473
412
|
|
474
|
-
async def __broadcast_pubsub_items(self):
|
413
|
+
async def __broadcast_pubsub_items(self) -> None:
|
475
414
|
if not self.is_friend:
|
476
415
|
return
|
477
416
|
if not self.added_to_roster:
|
@@ -490,11 +429,11 @@ class LegacyContact(
|
|
490
429
|
nick,
|
491
430
|
)
|
492
431
|
|
493
|
-
def send_friend_request(self, text: Optional[str] = None):
|
432
|
+
def send_friend_request(self, text: Optional[str] = None) -> None:
|
494
433
|
presence = self._make_presence(ptype="subscribe", pstatus=text, bare=True)
|
495
434
|
self._send(presence, nick=True)
|
496
435
|
|
497
|
-
async def accept_friend_request(self, text: Optional[str] = None):
|
436
|
+
async def accept_friend_request(self, text: Optional[str] = None) -> None:
|
498
437
|
"""
|
499
438
|
Call this to signify that this Contact has accepted to be a friend
|
500
439
|
of the user.
|
@@ -503,7 +442,6 @@ class LegacyContact(
|
|
503
442
|
"""
|
504
443
|
self.is_friend = True
|
505
444
|
self.added_to_roster = True
|
506
|
-
self.__ensure_pk()
|
507
445
|
self.log.debug("Accepting friend request")
|
508
446
|
presence = self._make_presence(ptype="subscribed", pstatus=text, bare=True)
|
509
447
|
self._send(presence, nick=True)
|
@@ -511,7 +449,7 @@ class LegacyContact(
|
|
511
449
|
await self.__broadcast_pubsub_items()
|
512
450
|
self.log.debug("Accepted friend request")
|
513
451
|
|
514
|
-
def reject_friend_request(self, text: Optional[str] = None):
|
452
|
+
def reject_friend_request(self, text: Optional[str] = None) -> None:
|
515
453
|
"""
|
516
454
|
Call this to signify that this Contact has refused to be a contact
|
517
455
|
of the user (or that they don't want to be friends anymore)
|
@@ -523,7 +461,7 @@ class LegacyContact(
|
|
523
461
|
self._send(presence, nick=True)
|
524
462
|
self.is_friend = False
|
525
463
|
|
526
|
-
async def on_friend_request(self, text=""):
|
464
|
+
async def on_friend_request(self, text: str = "") -> None:
|
527
465
|
"""
|
528
466
|
Called when receiving a "subscribe" presence, ie, "I would like to add
|
529
467
|
you to my contacts/friends", from the user to this contact.
|
@@ -540,7 +478,7 @@ class LegacyContact(
|
|
540
478
|
"""
|
541
479
|
pass
|
542
480
|
|
543
|
-
async def on_friend_delete(self, text=""):
|
481
|
+
async def on_friend_delete(self, text: str = "") -> None:
|
544
482
|
"""
|
545
483
|
Called when receiving an "unsubscribed" presence, ie, "I would like to
|
546
484
|
remove you to my contacts/friends" or "I refuse your friend request"
|
@@ -551,7 +489,7 @@ class LegacyContact(
|
|
551
489
|
"""
|
552
490
|
pass
|
553
491
|
|
554
|
-
async def on_friend_accept(self):
|
492
|
+
async def on_friend_accept(self) -> None:
|
555
493
|
"""
|
556
494
|
Called when receiving a "subscribed" presence, ie, "I accept to be
|
557
495
|
your/confirm that you are my friend" from the user to this contact.
|
@@ -560,7 +498,7 @@ class LegacyContact(
|
|
560
498
|
"""
|
561
499
|
pass
|
562
500
|
|
563
|
-
def unsubscribe(self):
|
501
|
+
def unsubscribe(self) -> None:
|
564
502
|
"""
|
565
503
|
(internal use by slidge)
|
566
504
|
|
@@ -569,9 +507,9 @@ class LegacyContact(
|
|
569
507
|
their 'friends'".
|
570
508
|
"""
|
571
509
|
for ptype in "unsubscribe", "unsubscribed", "unavailable":
|
572
|
-
self.xmpp.send_presence(pfrom=self.jid, pto=self.user_jid.bare, ptype=ptype)
|
510
|
+
self.xmpp.send_presence(pfrom=self.jid, pto=self.user_jid.bare, ptype=ptype)
|
573
511
|
|
574
|
-
async def update_info(self):
|
512
|
+
async def update_info(self) -> None:
|
575
513
|
"""
|
576
514
|
Fetch information about this contact from the legacy network
|
577
515
|
|
@@ -587,7 +525,7 @@ class LegacyContact(
|
|
587
525
|
"""
|
588
526
|
pass
|
589
527
|
|
590
|
-
async def fetch_vcard(self):
|
528
|
+
async def fetch_vcard(self) -> None:
|
591
529
|
"""
|
592
530
|
It the legacy network doesn't like that you fetch too many profiles on startup,
|
593
531
|
it's also possible to fetch it here, which will be called when XMPP clients
|
@@ -606,40 +544,12 @@ class LegacyContact(
|
|
606
544
|
):
|
607
545
|
p = super()._make_presence(last_seen=last_seen, **presence_kwargs)
|
608
546
|
caps = self.xmpp.plugin["xep_0115"]
|
609
|
-
if p.get_from().resource and self.
|
547
|
+
if p.get_from().resource and self.stored.caps_ver:
|
610
548
|
p["caps"]["node"] = caps.caps_node
|
611
549
|
p["caps"]["hash"] = caps.hash
|
612
|
-
p["caps"]["ver"] = self.
|
550
|
+
p["caps"]["ver"] = self.stored.caps_ver
|
613
551
|
return p
|
614
552
|
|
615
|
-
@classmethod
|
616
|
-
def from_store(cls, session, stored: Contact, *args, **kwargs) -> Self:
|
617
|
-
contact = cls(
|
618
|
-
session,
|
619
|
-
cls.xmpp.LEGACY_CONTACT_ID_TYPE(stored.legacy_id),
|
620
|
-
stored.jid.user, # type: ignore
|
621
|
-
*args, # type: ignore
|
622
|
-
**kwargs, # type: ignore
|
623
|
-
)
|
624
|
-
contact.contact_pk = stored.id
|
625
|
-
contact._name = stored.nick
|
626
|
-
contact._is_friend = stored.is_friend
|
627
|
-
contact._added_to_roster = stored.added_to_roster
|
628
|
-
if (data := stored.extra_attributes) is not None:
|
629
|
-
contact.deserialize_extra_attributes(data)
|
630
|
-
contact._caps_ver = stored.caps_ver
|
631
|
-
contact._set_logger_name()
|
632
|
-
contact._AvatarMixin__avatar_unique_id = ( # type:ignore
|
633
|
-
None
|
634
|
-
if stored.avatar_legacy_id is None
|
635
|
-
else session.xmpp.AVATAR_ID_TYPE(stored.avatar_legacy_id)
|
636
|
-
)
|
637
|
-
contact._avatar_pk = stored.avatar_id
|
638
|
-
contact._vcard = stored.vcard
|
639
|
-
contact._vcard_fetched = stored.vcard_fetched
|
640
|
-
contact._client_type = stored.client_type
|
641
|
-
return contact
|
642
|
-
|
643
553
|
|
644
554
|
def is_markable(stanza: Union[Message, Presence]):
|
645
555
|
if isinstance(stanza, Presence):
|