slidge 0.1.3__py3-none-any.whl → 0.2.0a1__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 +3 -5
- slidge/__main__.py +2 -196
- slidge/__version__.py +5 -0
- slidge/command/adhoc.py +8 -1
- slidge/command/admin.py +6 -7
- slidge/command/base.py +1 -2
- slidge/command/register.py +32 -16
- slidge/command/user.py +85 -6
- slidge/contact/contact.py +165 -49
- slidge/contact/roster.py +122 -47
- slidge/core/config.py +14 -11
- slidge/core/gateway/base.py +148 -36
- slidge/core/gateway/caps.py +7 -5
- slidge/core/gateway/disco.py +2 -4
- slidge/core/gateway/mam.py +1 -4
- slidge/core/gateway/muc_admin.py +1 -1
- slidge/core/gateway/ping.py +2 -3
- slidge/core/gateway/presence.py +1 -1
- slidge/core/gateway/registration.py +32 -21
- slidge/core/gateway/search.py +3 -5
- slidge/core/gateway/session_dispatcher.py +120 -57
- slidge/core/gateway/vcard_temp.py +7 -5
- slidge/core/mixins/__init__.py +11 -1
- slidge/core/mixins/attachment.py +32 -14
- slidge/core/mixins/avatar.py +90 -25
- slidge/core/mixins/base.py +8 -2
- slidge/core/mixins/db.py +18 -0
- slidge/core/mixins/disco.py +0 -10
- slidge/core/mixins/message.py +18 -8
- slidge/core/mixins/message_maker.py +17 -9
- slidge/core/mixins/presence.py +17 -4
- slidge/core/pubsub.py +54 -220
- slidge/core/session.py +69 -34
- slidge/db/__init__.py +4 -0
- slidge/db/alembic/env.py +64 -0
- slidge/db/alembic/script.py.mako +26 -0
- slidge/db/alembic/versions/09f27f098baa_add_missing_attributes_in_room.py +36 -0
- slidge/db/alembic/versions/2461390c0af2_store_contacts_caps_verstring_in_db.py +36 -0
- slidge/db/alembic/versions/29f5280c61aa_store_subject_setter_in_room.py +37 -0
- slidge/db/alembic/versions/2b1f45ab7379_store_room_subject_setter_by_nickname.py +41 -0
- slidge/db/alembic/versions/82a4af84b679_add_muc_history_filled.py +48 -0
- slidge/db/alembic/versions/8d2ced764698_rely_on_db_to_store_contacts_rooms_and_.py +133 -0
- slidge/db/alembic/versions/aa9d82a7f6ef_db_creation.py +85 -0
- slidge/db/alembic/versions/b33993e87db3_move_everything_to_persistent_db.py +214 -0
- slidge/db/alembic/versions/b64b1a793483_add_source_and_legacy_id_for_archived_.py +48 -0
- slidge/db/alembic/versions/c4a8ec35a0e8_per_room_user_nick.py +34 -0
- slidge/db/alembic/versions/e91195719c2c_store_users_avatars_persistently.py +26 -0
- slidge/db/avatar.py +235 -0
- slidge/db/meta.py +65 -0
- slidge/db/models.py +375 -0
- slidge/db/store.py +1078 -0
- slidge/group/archive.py +58 -14
- slidge/group/bookmarks.py +72 -57
- slidge/group/participant.py +87 -28
- slidge/group/room.py +369 -211
- slidge/main.py +201 -0
- slidge/migration.py +30 -0
- slidge/slixfix/__init__.py +35 -2
- slidge/slixfix/roster.py +11 -4
- slidge/slixfix/xep_0292/vcard4.py +3 -0
- slidge/util/archive_msg.py +2 -1
- slidge/util/db.py +1 -47
- slidge/util/test.py +71 -4
- slidge/util/types.py +29 -4
- slidge/util/util.py +22 -0
- {slidge-0.1.3.dist-info → slidge-0.2.0a1.dist-info}/METADATA +4 -4
- slidge-0.2.0a1.dist-info/RECORD +114 -0
- slidge/core/cache.py +0 -183
- slidge/util/schema.sql +0 -126
- slidge/util/sql.py +0 -508
- slidge-0.1.3.dist-info/RECORD +0 -96
- {slidge-0.1.3.dist-info → slidge-0.2.0a1.dist-info}/LICENSE +0 -0
- {slidge-0.1.3.dist-info → slidge-0.2.0a1.dist-info}/WHEEL +0 -0
- {slidge-0.1.3.dist-info → slidge-0.2.0a1.dist-info}/entry_points.txt +0 -0
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,
|
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,
|
@@ -24,12 +20,8 @@ from slixmpp.plugins.xep_0172 import UserNick
|
|
24
20
|
from slixmpp.plugins.xep_0292.stanza import VCard4
|
25
21
|
from slixmpp.types import JidStr, OptJidStr
|
26
22
|
|
27
|
-
from ..
|
28
|
-
from ..
|
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
|
23
|
+
from ..db.avatar import CachedAvatar, avatar_cache
|
24
|
+
from ..db.store import ContactStore, SlidgeStore
|
33
25
|
from .mixins.lock import NamedLockMixin
|
34
26
|
|
35
27
|
if TYPE_CHECKING:
|
@@ -39,19 +31,15 @@ VCARD4_NAMESPACE = "urn:xmpp:vcard4"
|
|
39
31
|
|
40
32
|
|
41
33
|
class PepItem:
|
42
|
-
|
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
|
34
|
+
pass
|
48
35
|
|
49
36
|
|
50
37
|
class PepAvatar(PepItem):
|
51
|
-
|
38
|
+
store: SlidgeStore
|
39
|
+
|
40
|
+
def __init__(self):
|
52
41
|
self.metadata: Optional[AvatarMetadata] = None
|
53
42
|
self.id: Optional[str] = None
|
54
|
-
self.jid = jid
|
55
43
|
self._avatar_data_path: Optional[Path] = None
|
56
44
|
|
57
45
|
@property
|
@@ -62,63 +50,7 @@ class PepAvatar(PepItem):
|
|
62
50
|
data.set_value(self._avatar_data_path.read_bytes())
|
63
51
|
return data
|
64
52
|
|
65
|
-
|
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):
|
53
|
+
def set_avatar_from_cache(self, cached_avatar: CachedAvatar):
|
122
54
|
metadata = AvatarMetadata()
|
123
55
|
self.id = cached_avatar.hash
|
124
56
|
metadata.add_info(
|
@@ -131,31 +63,10 @@ class PepAvatar(PepItem):
|
|
131
63
|
self.metadata = metadata
|
132
64
|
self._avatar_data_path = cached_avatar.path
|
133
65
|
|
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
66
|
|
158
67
|
class PepNick(PepItem):
|
68
|
+
contact_store: ContactStore
|
69
|
+
|
159
70
|
def __init__(self, nick: Optional[str] = None):
|
160
71
|
nickname = UserNick()
|
161
72
|
if nick is not None:
|
@@ -163,20 +74,6 @@ class PepNick(PepItem):
|
|
163
74
|
self.nick = nickname
|
164
75
|
self.__nick_str = nick
|
165
76
|
|
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
77
|
|
181
78
|
class PubSubComponent(NamedLockMixin, BasePlugin):
|
182
79
|
xmpp: "BaseGateway"
|
@@ -186,7 +83,6 @@ class PubSubComponent(NamedLockMixin, BasePlugin):
|
|
186
83
|
dependencies = {
|
187
84
|
"xep_0030",
|
188
85
|
"xep_0060",
|
189
|
-
# "xep_0084",
|
190
86
|
"xep_0115",
|
191
87
|
"xep_0163",
|
192
88
|
}
|
@@ -276,7 +172,7 @@ class PubSubComponent(NamedLockMixin, BasePlugin):
|
|
276
172
|
else:
|
277
173
|
if pep_avatar.metadata is None:
|
278
174
|
raise XMPPError("internal-server-error", "Avatar but no metadata?")
|
279
|
-
await self.
|
175
|
+
await self.__broadcast(
|
280
176
|
data=pep_avatar.metadata,
|
281
177
|
from_=p.get_to(),
|
282
178
|
to=from_,
|
@@ -288,7 +184,7 @@ class PubSubComponent(NamedLockMixin, BasePlugin):
|
|
288
184
|
except XMPPError:
|
289
185
|
pass
|
290
186
|
else:
|
291
|
-
await self.
|
187
|
+
await self.__broadcast(data=pep_nick.nick, from_=p.get_to(), to=from_)
|
292
188
|
|
293
189
|
if VCARD4_NAMESPACE + "+notify" in features:
|
294
190
|
await self.broadcast_vcard_event(p.get_to(), to=from_)
|
@@ -303,7 +199,7 @@ class PubSubComponent(NamedLockMixin, BasePlugin):
|
|
303
199
|
# but movim expects it to be here, and I guess
|
304
200
|
|
305
201
|
log.debug("Broadcast vcard4 event: %s", vcard)
|
306
|
-
await self.
|
202
|
+
await self.__broadcast(
|
307
203
|
data=vcard,
|
308
204
|
from_=JID(from_).bare,
|
309
205
|
to=to,
|
@@ -311,26 +207,34 @@ class PubSubComponent(NamedLockMixin, BasePlugin):
|
|
311
207
|
node=VCARD4_NAMESPACE,
|
312
208
|
)
|
313
209
|
|
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
210
|
async def _get_authorized_avatar(self, stanza: Union[Iq, Presence]) -> PepAvatar:
|
330
|
-
|
211
|
+
if stanza.get_to() == self.xmpp.boundjid.bare:
|
212
|
+
item = PepAvatar()
|
213
|
+
item.set_avatar_from_cache(avatar_cache.get_by_pk(self.xmpp.avatar_pk))
|
214
|
+
return item
|
215
|
+
|
216
|
+
session = self.xmpp.get_session_from_stanza(stanza)
|
217
|
+
entity = await session.get_contact_or_group_or_participant(stanza.get_to())
|
218
|
+
|
219
|
+
item = PepAvatar()
|
220
|
+
avatar_id = entity.avatar_id
|
221
|
+
if avatar_id is not None:
|
222
|
+
stored = avatar_cache.get(str(avatar_id))
|
223
|
+
assert stored is not None
|
224
|
+
item.set_avatar_from_cache(stored)
|
225
|
+
return item
|
331
226
|
|
332
227
|
async def _get_authorized_nick(self, stanza: Union[Iq, Presence]) -> PepNick:
|
333
|
-
|
228
|
+
if stanza.get_to() == self.xmpp.boundjid.bare:
|
229
|
+
return PepNick(self.xmpp.COMPONENT_NAME)
|
230
|
+
|
231
|
+
session = self.xmpp.get_session_from_stanza(stanza)
|
232
|
+
entity = await session.contacts.by_jid(stanza.get_to())
|
233
|
+
|
234
|
+
if entity.name is not None:
|
235
|
+
return PepNick(entity.name)
|
236
|
+
else:
|
237
|
+
return PepNick()
|
334
238
|
|
335
239
|
async def _get_avatar_data(self, iq: Iq):
|
336
240
|
pep_avatar = await self._get_authorized_avatar(iq)
|
@@ -372,10 +276,6 @@ class PubSubComponent(NamedLockMixin, BasePlugin):
|
|
372
276
|
raise XMPPError("item-not-found")
|
373
277
|
self._reply_with_payload(iq, vcard, "current", VCARD4_NAMESPACE)
|
374
278
|
|
375
|
-
@staticmethod
|
376
|
-
def get_avatar(jid: JID):
|
377
|
-
return PepAvatar.from_db(jid)
|
378
|
-
|
379
279
|
@staticmethod
|
380
280
|
def _reply_with_payload(
|
381
281
|
iq: Iq,
|
@@ -394,7 +294,7 @@ class PubSubComponent(NamedLockMixin, BasePlugin):
|
|
394
294
|
result["pubsub"]["items"].append(item)
|
395
295
|
result.send()
|
396
296
|
|
397
|
-
async def
|
297
|
+
async def __broadcast(self, data, from_: JidStr, to: OptJidStr = None, **kwargs):
|
398
298
|
from_ = JID(from_)
|
399
299
|
if from_ != self.xmpp.boundjid.bare and to is not None:
|
400
300
|
to = JID(to)
|
@@ -402,12 +302,6 @@ class PubSubComponent(NamedLockMixin, BasePlugin):
|
|
402
302
|
if session is None:
|
403
303
|
return
|
404
304
|
await session.ready
|
405
|
-
try:
|
406
|
-
entity = await session.get_contact_or_group_or_participant(from_)
|
407
|
-
except ContactIsUser:
|
408
|
-
return
|
409
|
-
if isinstance(entity, LegacyContact) and not entity.is_friend:
|
410
|
-
return
|
411
305
|
|
412
306
|
item = EventItem()
|
413
307
|
if data:
|
@@ -428,97 +322,37 @@ class PubSubComponent(NamedLockMixin, BasePlugin):
|
|
428
322
|
msg.append(event)
|
429
323
|
|
430
324
|
if to is None:
|
431
|
-
for u in
|
325
|
+
for u in self.xmpp.store.users.get_all():
|
432
326
|
new_msg = copy(msg)
|
433
|
-
new_msg.set_to(u.
|
327
|
+
new_msg.set_to(u.jid.bare)
|
434
328
|
new_msg.send()
|
435
329
|
else:
|
436
330
|
msg.set_to(to)
|
437
331
|
msg.send()
|
438
332
|
|
439
|
-
async def
|
440
|
-
self,
|
441
|
-
|
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))
|
333
|
+
async def broadcast_avatar(
|
334
|
+
self, from_: JidStr, to: JidStr, cached_avatar: Optional[CachedAvatar]
|
335
|
+
) -> None:
|
483
336
|
if cached_avatar is None:
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
337
|
+
await self.__broadcast(AvatarMetadata(), from_, to)
|
338
|
+
else:
|
339
|
+
pep_avatar = PepAvatar()
|
340
|
+
pep_avatar.set_avatar_from_cache(cached_avatar)
|
341
|
+
assert pep_avatar.metadata is not None
|
342
|
+
await self.__broadcast(
|
343
|
+
pep_avatar.metadata, from_, to, id=pep_avatar.metadata["info"]["id"]
|
489
344
|
)
|
490
|
-
return
|
491
|
-
self.xmpp.loop.create_task(
|
492
|
-
self.set_avatar(jid, cached_avatar.path, broadcast_to, uid, broadcast)
|
493
|
-
)
|
494
345
|
|
495
|
-
def
|
346
|
+
def broadcast_nick(
|
496
347
|
self,
|
497
|
-
|
348
|
+
user_jid: JID,
|
498
349
|
jid: JidStr,
|
499
350
|
nick: Optional[str] = None,
|
500
351
|
):
|
501
352
|
jid = JID(jid)
|
502
353
|
nickname = PepNick(nick)
|
503
|
-
nickname.to_db(jid, user)
|
504
354
|
log.debug("New nickname: %s", nickname.nick)
|
505
|
-
self.xmpp.loop.create_task(self.
|
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)
|
355
|
+
self.xmpp.loop.create_task(self.__broadcast(nickname.nick, jid, user_jid.bare))
|
522
356
|
|
523
357
|
|
524
358
|
log = logging.getLogger(__name__)
|
slidge/core/session.py
CHANGED
@@ -18,11 +18,10 @@ from slixmpp.types import PresenceShows
|
|
18
18
|
|
19
19
|
from ..command import SearchResult
|
20
20
|
from ..contact import LegacyContact, LegacyRoster
|
21
|
+
from ..db.models import GatewayUser
|
21
22
|
from ..group.bookmarks import LegacyBookmarks
|
22
23
|
from ..group.room import LegacyMUC
|
23
24
|
from ..util import ABCSubclassableOnceAtMost
|
24
|
-
from ..util.db import GatewayUser, user_store
|
25
|
-
from ..util.sql import SQLBiDict
|
26
25
|
from ..util.types import (
|
27
26
|
LegacyGroupIdType,
|
28
27
|
LegacyMessageType,
|
@@ -92,16 +91,10 @@ class BaseSession(
|
|
92
91
|
"""
|
93
92
|
|
94
93
|
def __init__(self, user: GatewayUser):
|
95
|
-
self.log = logging.getLogger(user.
|
94
|
+
self.log = logging.getLogger(user.jid.bare)
|
96
95
|
|
97
|
-
self.
|
98
|
-
self.
|
99
|
-
"session_message_sent", "legacy_id", "xmpp_id", self.user
|
100
|
-
)
|
101
|
-
# message ids (*not* stanza-ids), needed for last msg correction
|
102
|
-
self.muc_sent_msg_ids = SQLBiDict[LegacyMessageType, str](
|
103
|
-
"session_message_sent_muc", "legacy_id", "xmpp_id", self.user
|
104
|
-
)
|
96
|
+
self.user_jid = user.jid
|
97
|
+
self.user_pk = user.id
|
105
98
|
|
106
99
|
self.ignore_messages = set[str]()
|
107
100
|
|
@@ -115,26 +108,26 @@ class BaseSession(
|
|
115
108
|
|
116
109
|
self.http = self.xmpp.http
|
117
110
|
|
118
|
-
self.threads = SQLBiDict[str, LegacyThreadType]( # type:ignore
|
119
|
-
"session_thread_sent_muc", "legacy_id", "xmpp_id", self.user
|
120
|
-
)
|
121
111
|
self.thread_creation_lock = asyncio.Lock()
|
122
112
|
|
123
113
|
self.__cached_presence: Optional[CachedPresence] = None
|
124
114
|
|
125
|
-
self.avatar_hash: Optional[str] = None
|
126
|
-
|
127
115
|
self.__tasks = set[asyncio.Task]()
|
128
116
|
|
117
|
+
@property
|
118
|
+
def user(self) -> GatewayUser:
|
119
|
+
return self.xmpp.store.users.get(self.user_jid) # type:ignore
|
120
|
+
|
129
121
|
def __remove_task(self, fut):
|
130
122
|
self.log.debug("Removing fut %s", fut)
|
131
123
|
self.__tasks.remove(fut)
|
132
124
|
|
133
|
-
def create_task(self, coro) ->
|
125
|
+
def create_task(self, coro) -> asyncio.Task:
|
134
126
|
task = self.xmpp.loop.create_task(coro)
|
135
127
|
self.__tasks.add(task)
|
136
128
|
self.log.debug("Creating task %s", task)
|
137
129
|
task.add_done_callback(lambda _: self.__remove_task(task))
|
130
|
+
return task
|
138
131
|
|
139
132
|
def cancel_all_tasks(self):
|
140
133
|
for task in self.__tasks:
|
@@ -488,6 +481,17 @@ class BaseSession(
|
|
488
481
|
"""
|
489
482
|
await muc.on_set_affiliation(contact, "member", reason, None)
|
490
483
|
|
484
|
+
async def on_leave_group(self, muc_legacy_id: LegacyGroupIdType):
|
485
|
+
"""
|
486
|
+
Triggered when the user leaves a group via the dedicated slidge command
|
487
|
+
or the :xep:`0077` ``<remove />`` mechanism.
|
488
|
+
|
489
|
+
This should be interpreted as definitely leaving the group.
|
490
|
+
|
491
|
+
:param muc_legacy_id: The legacy ID of the group to leave
|
492
|
+
"""
|
493
|
+
raise NotImplementedError
|
494
|
+
|
491
495
|
def __reset_ready(self):
|
492
496
|
self.ready = self.xmpp.loop.create_future()
|
493
497
|
|
@@ -507,7 +511,7 @@ class BaseSession(
|
|
507
511
|
self.ready.set_result(True)
|
508
512
|
|
509
513
|
def __repr__(self):
|
510
|
-
return f"<Session of {self.
|
514
|
+
return f"<Session of {self.user_jid}>"
|
511
515
|
|
512
516
|
def shutdown(self) -> asyncio.Task:
|
513
517
|
for c in self.contacts:
|
@@ -571,9 +575,9 @@ class BaseSession(
|
|
571
575
|
log.debug("user not found", stack_info=True)
|
572
576
|
raise XMPPError(text="User not found", condition="subscription-required")
|
573
577
|
|
574
|
-
session = _sessions.get(user)
|
578
|
+
session = _sessions.get(user.jid.bare)
|
575
579
|
if session is None:
|
576
|
-
_sessions[user] = session = cls(user)
|
580
|
+
_sessions[user.jid.bare] = session = cls(user)
|
577
581
|
return session
|
578
582
|
|
579
583
|
@classmethod
|
@@ -590,7 +594,7 @@ class BaseSession(
|
|
590
594
|
# :param s:
|
591
595
|
# :return:
|
592
596
|
# """
|
593
|
-
return cls.
|
597
|
+
return cls.from_jid(s.get_from())
|
594
598
|
|
595
599
|
@classmethod
|
596
600
|
def from_jid(cls, jid: JID) -> "BaseSession":
|
@@ -602,7 +606,11 @@ class BaseSession(
|
|
602
606
|
# :param jid:
|
603
607
|
# :return:
|
604
608
|
# """
|
605
|
-
|
609
|
+
session = _sessions.get(jid.bare)
|
610
|
+
if session is not None:
|
611
|
+
return session
|
612
|
+
user = cls.xmpp.store.users.get(jid)
|
613
|
+
return cls._from_user_or_none(user)
|
606
614
|
|
607
615
|
@classmethod
|
608
616
|
async def kill_by_jid(cls, jid: JID):
|
@@ -615,16 +623,21 @@ class BaseSession(
|
|
615
623
|
# :return:
|
616
624
|
# """
|
617
625
|
log.debug("Killing session of %s", jid)
|
618
|
-
for
|
619
|
-
if
|
626
|
+
for user_jid, session in _sessions.items():
|
627
|
+
if user_jid == jid.bare:
|
620
628
|
break
|
621
629
|
else:
|
622
630
|
log.debug("Did not find a session for %s", jid)
|
623
631
|
return
|
624
632
|
for c in session.contacts:
|
625
633
|
c.unsubscribe()
|
634
|
+
user = cls.xmpp.store.users.get(jid)
|
635
|
+
if user is None:
|
636
|
+
log.warning("User not found during unregistration")
|
637
|
+
return
|
626
638
|
await cls.xmpp.unregister(user)
|
627
|
-
|
639
|
+
cls.xmpp.store.users.delete(user.jid)
|
640
|
+
del _sessions[user.jid.bare]
|
628
641
|
del user
|
629
642
|
del session
|
630
643
|
|
@@ -649,7 +662,7 @@ class BaseSession(
|
|
649
662
|
"""
|
650
663
|
self.__cached_presence = CachedPresence(status, show, kwargs)
|
651
664
|
self.xmpp.send_presence(
|
652
|
-
pto=self.
|
665
|
+
pto=self.user_jid.bare, pstatus=status, pshow=show, **kwargs
|
653
666
|
)
|
654
667
|
|
655
668
|
def send_cached_presence(self, to: JID):
|
@@ -671,7 +684,7 @@ class BaseSession(
|
|
671
684
|
|
672
685
|
:param text: A text
|
673
686
|
"""
|
674
|
-
self.xmpp.send_text(text, mto=self.
|
687
|
+
self.xmpp.send_text(text, mto=self.user_jid, **msg_kwargs)
|
675
688
|
|
676
689
|
def send_gateway_invite(
|
677
690
|
self,
|
@@ -686,7 +699,7 @@ class BaseSession(
|
|
686
699
|
:param reason:
|
687
700
|
:param password:
|
688
701
|
"""
|
689
|
-
self.xmpp.invite_to(muc, reason=reason, password=password, mto=self.
|
702
|
+
self.xmpp.invite_to(muc, reason=reason, password=password, mto=self.user_jid)
|
690
703
|
|
691
704
|
async def input(self, text: str, **msg_kwargs):
|
692
705
|
"""
|
@@ -698,7 +711,7 @@ class BaseSession(
|
|
698
711
|
:param msg_kwargs: Extra attributes
|
699
712
|
:return:
|
700
713
|
"""
|
701
|
-
return await self.xmpp.input(self.
|
714
|
+
return await self.xmpp.input(self.user_jid, text, **msg_kwargs)
|
702
715
|
|
703
716
|
async def send_qr(self, text: str):
|
704
717
|
"""
|
@@ -707,7 +720,7 @@ class BaseSession(
|
|
707
720
|
|
708
721
|
:param text: Text to encode as a QR code
|
709
722
|
"""
|
710
|
-
await self.xmpp.send_qr(text, mto=self.
|
723
|
+
await self.xmpp.send_qr(text, mto=self.user_jid)
|
711
724
|
|
712
725
|
def re_login(self):
|
713
726
|
# Logout then re-login
|
@@ -715,14 +728,17 @@ class BaseSession(
|
|
715
728
|
# No reason to override this
|
716
729
|
self.xmpp.re_login(self)
|
717
730
|
|
718
|
-
async def get_contact_or_group_or_participant(self, jid: JID):
|
731
|
+
async def get_contact_or_group_or_participant(self, jid: JID, create=True):
|
719
732
|
if jid.bare in (contacts := self.contacts.known_contacts(only_friends=False)):
|
720
733
|
return contacts[jid.bare]
|
721
|
-
if
|
722
|
-
return await self.__get_muc_or_participant(
|
734
|
+
if (muc := self.bookmarks.by_jid_only_if_exists(JID(jid.bare))) is not None:
|
735
|
+
return await self.__get_muc_or_participant(muc, jid)
|
723
736
|
else:
|
724
737
|
muc = None
|
725
738
|
|
739
|
+
if not create:
|
740
|
+
return None
|
741
|
+
|
726
742
|
try:
|
727
743
|
return await self.contacts.by_jid(jid)
|
728
744
|
except XMPPError:
|
@@ -763,6 +779,25 @@ class BaseSession(
|
|
763
779
|
"Legacy session is not fully initialized, retry later",
|
764
780
|
)
|
765
781
|
|
782
|
+
def legacy_module_data_update(self, data: dict):
|
783
|
+
with self.xmpp.store.session():
|
784
|
+
user = self.user
|
785
|
+
user.legacy_module_data.update(data)
|
786
|
+
self.xmpp.store.users.update(user)
|
787
|
+
|
788
|
+
def legacy_module_data_set(self, data: dict):
|
789
|
+
with self.xmpp.store.session():
|
790
|
+
user = self.user
|
791
|
+
user.legacy_module_data = data
|
792
|
+
self.xmpp.store.users.update(user)
|
793
|
+
|
794
|
+
def legacy_module_data_clear(self):
|
795
|
+
with self.xmpp.store.session():
|
796
|
+
user = self.user
|
797
|
+
user.legacy_module_data.clear()
|
798
|
+
self.xmpp.store.users.update(user)
|
799
|
+
|
766
800
|
|
767
|
-
|
801
|
+
# keys = user.jid.bare
|
802
|
+
_sessions: dict[str, BaseSession] = {}
|
768
803
|
log = logging.getLogger(__name__)
|
slidge/db/__init__.py
ADDED