slidge 0.1.3__py3-none-any.whl → 0.2.0a1__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- 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