slidge 0.2.12__py3-none-any.whl → 0.3.0__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 +123 -210
- slidge/contact/roster.py +108 -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 +120 -93
- 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 +26 -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 +177 -87
- slidge/core/mixins/__init__.py +1 -11
- slidge/core/mixins/attachment.py +200 -147
- slidge/core/mixins/avatar.py +105 -177
- slidge/core/mixins/base.py +3 -1
- slidge/core/mixins/db.py +50 -2
- slidge/core/mixins/disco.py +1 -1
- slidge/core/mixins/message.py +19 -17
- slidge/core/mixins/message_maker.py +29 -15
- slidge/core/mixins/message_text.py +67 -30
- slidge/core/mixins/presence.py +94 -37
- slidge/core/pubsub.py +42 -47
- slidge/core/session.py +95 -60
- slidge/db/alembic/versions/cef02a8b1451_initial_schema.py +361 -0
- slidge/db/avatar.py +150 -119
- slidge/db/meta.py +33 -22
- slidge/db/models.py +69 -117
- slidge/db/store.py +414 -1094
- slidge/group/archive.py +65 -55
- slidge/group/bookmarks.py +96 -59
- slidge/group/participant.py +150 -144
- slidge/group/room.py +345 -327
- slidge/main.py +34 -22
- slidge/migration.py +17 -29
- 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 +12 -2
- slidge/util/archive_msg.py +11 -5
- slidge/util/conf.py +27 -21
- 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 +24 -18
- slidge/util/util.py +26 -22
- {slidge-0.2.12.dist-info → slidge-0.3.0.dist-info}/METADATA +1 -1
- slidge-0.3.0.dist-info/RECORD +95 -0
- {slidge-0.2.12.dist-info → slidge-0.3.0.dist-info}/WHEEL +1 -1
- slidge/db/alembic/versions/04cf35e3cf85_add_participant_nickname_no_illegal.py +0 -33
- slidge/db/alembic/versions/09f27f098baa_add_missing_attributes_in_room.py +0 -36
- slidge/db/alembic/versions/15b0bd83407a_remove_bogus_unique_constraints_on_room_.py +0 -85
- slidge/db/alembic/versions/2461390c0af2_store_contacts_caps_verstring_in_db.py +0 -36
- slidge/db/alembic/versions/29f5280c61aa_store_subject_setter_in_room.py +0 -37
- slidge/db/alembic/versions/2b1f45ab7379_store_room_subject_setter_by_nickname.py +0 -41
- slidge/db/alembic/versions/3071e0fa69d4_add_contact_client_type.py +0 -52
- slidge/db/alembic/versions/45c24cc73c91_add_bob.py +0 -42
- slidge/db/alembic/versions/5bd48bfdffa2_lift_room_legacy_id_constraint.py +0 -61
- slidge/db/alembic/versions/82a4af84b679_add_muc_history_filled.py +0 -48
- slidge/db/alembic/versions/8b993243a536_add_vcard_content_to_contact_table.py +0 -43
- slidge/db/alembic/versions/8d2ced764698_rely_on_db_to_store_contacts_rooms_and_.py +0 -139
- slidge/db/alembic/versions/aa9d82a7f6ef_db_creation.py +0 -50
- slidge/db/alembic/versions/abba1ae0edb3_store_avatar_legacy_id_in_the_contact_.py +0 -79
- slidge/db/alembic/versions/b33993e87db3_move_everything_to_persistent_db.py +0 -214
- slidge/db/alembic/versions/b64b1a793483_add_source_and_legacy_id_for_archived_.py +0 -52
- slidge/db/alembic/versions/c4a8ec35a0e8_per_room_user_nick.py +0 -34
- slidge/db/alembic/versions/e91195719c2c_store_users_avatars_persistently.py +0 -26
- slidge-0.2.12.dist-info/RECORD +0 -112
- {slidge-0.2.12.dist-info → slidge-0.3.0.dist-info}/entry_points.txt +0 -0
- {slidge-0.2.12.dist-info → slidge-0.3.0.dist-info}/licenses/LICENSE +0 -0
- {slidge-0.2.12.dist-info → slidge-0.3.0.dist-info}/top_level.txt +0 -0
slidge/core/gateway.py
CHANGED
@@ -8,17 +8,19 @@ import re
|
|
8
8
|
import tempfile
|
9
9
|
from copy import copy
|
10
10
|
from datetime import datetime
|
11
|
-
from
|
11
|
+
from pathlib import Path
|
12
|
+
from typing import TYPE_CHECKING, Any, Callable, Optional, Sequence, Type, Union, cast
|
12
13
|
|
13
14
|
import aiohttp
|
14
15
|
import qrcode
|
15
16
|
from slixmpp import JID, ComponentXMPP, Iq, Message, Presence
|
16
17
|
from slixmpp.exceptions import IqError, IqTimeout, XMPPError
|
18
|
+
from slixmpp.plugins.xep_0004 import Form
|
17
19
|
from slixmpp.plugins.xep_0060.stanza import OwnerAffiliation
|
18
20
|
from slixmpp.types import MessageTypes
|
19
21
|
from slixmpp.xmlstream.xmlstream import NotConnectedError
|
20
22
|
|
21
|
-
from slidge import command # noqa: F401
|
23
|
+
from slidge import LegacyContact, command # noqa: F401
|
22
24
|
from slidge.command.adhoc import AdhocProvider
|
23
25
|
from slidge.command.admin import Exec
|
24
26
|
from slidge.command.base import Command, FormField
|
@@ -27,15 +29,17 @@ from slidge.command.register import RegistrationType
|
|
27
29
|
from slidge.core import config
|
28
30
|
from slidge.core.dispatcher.session_dispatcher import SessionDispatcher
|
29
31
|
from slidge.core.mixins import MessageMixin
|
32
|
+
from slidge.core.mixins.avatar import convert_avatar
|
30
33
|
from slidge.core.pubsub import PubSubComponent
|
31
34
|
from slidge.core.session import BaseSession
|
32
35
|
from slidge.db import GatewayUser, SlidgeStore
|
33
|
-
from slidge.db.avatar import avatar_cache
|
36
|
+
from slidge.db.avatar import CachedAvatar, avatar_cache
|
37
|
+
from slidge.db.meta import JSONSerializable
|
34
38
|
from slidge.slixfix import PrivilegedIqError
|
35
39
|
from slidge.slixfix.delivery_receipt import DeliveryReceipt
|
36
40
|
from slidge.slixfix.roster import RosterBackend
|
37
41
|
from slidge.util import ABCSubclassableOnceAtMost
|
38
|
-
from slidge.util.types import
|
42
|
+
from slidge.util.types import Avatar, MessageOrPresenceTypeVar
|
39
43
|
from slidge.util.util import timeit
|
40
44
|
|
41
45
|
if TYPE_CHECKING:
|
@@ -95,7 +99,7 @@ class BaseGateway(
|
|
95
99
|
"""Name of the component, as seen in service discovery by XMPP clients"""
|
96
100
|
COMPONENT_TYPE: str = ""
|
97
101
|
"""Type of the gateway, should follow https://xmpp.org/registrar/disco-categories.html"""
|
98
|
-
COMPONENT_AVATAR: Optional[
|
102
|
+
COMPONENT_AVATAR: Optional[Avatar | Path | str] = None
|
99
103
|
"""
|
100
104
|
Path, bytes or URL used by the component as an avatar.
|
101
105
|
"""
|
@@ -147,6 +151,34 @@ class BaseGateway(
|
|
147
151
|
required=True,
|
148
152
|
type="boolean",
|
149
153
|
),
|
154
|
+
FormField(
|
155
|
+
var="always_invite_when_adding_bookmarks",
|
156
|
+
label="Send an invitation to join MUCs after adding them to the bookmarks.",
|
157
|
+
value="true",
|
158
|
+
required=True,
|
159
|
+
type="boolean",
|
160
|
+
),
|
161
|
+
FormField(
|
162
|
+
var="last_seen_fallback",
|
163
|
+
label="Use contact presence status message to show when they were last seen.",
|
164
|
+
value="true",
|
165
|
+
required=True,
|
166
|
+
type="boolean",
|
167
|
+
),
|
168
|
+
FormField(
|
169
|
+
var="roster_push",
|
170
|
+
label="Add contacts to your roster.",
|
171
|
+
value="true",
|
172
|
+
required=True,
|
173
|
+
type="boolean",
|
174
|
+
),
|
175
|
+
FormField(
|
176
|
+
var="reaction_fallback",
|
177
|
+
label="Receive fallback messages for reactions (for legacy XMPP clients)",
|
178
|
+
value="false",
|
179
|
+
required=True,
|
180
|
+
type="boolean",
|
181
|
+
),
|
150
182
|
]
|
151
183
|
|
152
184
|
ROSTER_GROUP: str = "slidge"
|
@@ -211,7 +243,6 @@ class BaseGateway(
|
|
211
243
|
is_group = False
|
212
244
|
_can_send_carbon = False
|
213
245
|
store: SlidgeStore
|
214
|
-
avatar_pk: int
|
215
246
|
|
216
247
|
AVATAR_ID_TYPE: Callable[[str], Any] = str
|
217
248
|
"""
|
@@ -253,8 +284,9 @@ class BaseGateway(
|
|
253
284
|
"""
|
254
285
|
|
255
286
|
http: aiohttp.ClientSession
|
287
|
+
avatar: CachedAvatar | None = None
|
256
288
|
|
257
|
-
def __init__(self):
|
289
|
+
def __init__(self) -> None:
|
258
290
|
if config.COMPONENT_NAME:
|
259
291
|
self.COMPONENT_NAME = config.COMPONENT_NAME
|
260
292
|
if config.WELCOME_MESSAGE:
|
@@ -298,18 +330,13 @@ class BaseGateway(
|
|
298
330
|
self.jid_validator: re.Pattern = re.compile(config.USER_JID_VALIDATOR)
|
299
331
|
self.qr_pending_registrations = dict[str, asyncio.Future[Optional[dict]]]()
|
300
332
|
|
301
|
-
self.
|
302
|
-
self.session_cls.xmpp = self
|
303
|
-
|
304
|
-
from ..group.room import LegacyMUC
|
305
|
-
|
306
|
-
LegacyMUC.get_self_or_unique_subclass().xmpp = self
|
333
|
+
self.__setup_legacy_module_subclasses()
|
307
334
|
|
308
335
|
self.get_session_from_stanza: Callable[
|
309
336
|
[Union[Message, Presence, Iq]], BaseSession
|
310
|
-
] = self.
|
337
|
+
] = self._session_cls.from_stanza
|
311
338
|
self.get_session_from_user: Callable[[GatewayUser], BaseSession] = (
|
312
|
-
self.
|
339
|
+
self._session_cls.from_user
|
313
340
|
)
|
314
341
|
|
315
342
|
self.register_plugins()
|
@@ -349,13 +376,41 @@ class BaseGateway(
|
|
349
376
|
|
350
377
|
MessageMixin.__init__(self) # ComponentXMPP does not call super().__init__()
|
351
378
|
|
352
|
-
|
379
|
+
def __setup_legacy_module_subclasses(self):
|
380
|
+
from ..contact.roster import LegacyRoster
|
381
|
+
from ..group.bookmarks import LegacyBookmarks
|
382
|
+
from ..group.room import LegacyMUC, LegacyParticipant
|
383
|
+
|
384
|
+
session_cls: Type[BaseSession] = cast(
|
385
|
+
Type[BaseSession], BaseSession.get_unique_subclass()
|
386
|
+
)
|
387
|
+
contact_cls = LegacyContact.get_self_or_unique_subclass()
|
388
|
+
muc_cls = LegacyMUC.get_self_or_unique_subclass()
|
389
|
+
participant_cls = LegacyParticipant.get_self_or_unique_subclass()
|
390
|
+
bookmarks_cls = LegacyBookmarks.get_self_or_unique_subclass()
|
391
|
+
roster_cls = LegacyRoster.get_self_or_unique_subclass()
|
392
|
+
|
393
|
+
session_cls.xmpp = self # type:ignore[attr-defined]
|
394
|
+
contact_cls.xmpp = self # type:ignore[attr-defined]
|
395
|
+
muc_cls.xmpp = self # type:ignore[attr-defined]
|
396
|
+
|
397
|
+
self._session_cls = session_cls
|
398
|
+
session_cls._bookmarks_cls = bookmarks_cls # type:ignore[misc,assignment]
|
399
|
+
session_cls._roster_cls = roster_cls # type:ignore[misc,assignment]
|
400
|
+
LegacyRoster._contact_cls = contact_cls # type:ignore[misc]
|
401
|
+
LegacyBookmarks._muc_cls = muc_cls # type:ignore[misc]
|
402
|
+
LegacyMUC._participant_cls = participant_cls # type:ignore[misc]
|
403
|
+
|
404
|
+
async def kill_session(self, jid: JID) -> None:
|
405
|
+
await self._session_cls.kill_by_jid(jid)
|
406
|
+
|
407
|
+
async def __set_http(self) -> None:
|
353
408
|
self.http = aiohttp.ClientSession()
|
354
409
|
if getattr(self, "_test_mode", False):
|
355
410
|
return
|
356
411
|
avatar_cache.http = self.http
|
357
412
|
|
358
|
-
def __register_commands(self):
|
413
|
+
def __register_commands(self) -> None:
|
359
414
|
for cls in Command.subclasses:
|
360
415
|
if any(x is NotImplemented for x in [cls.CHAT_COMMAND, cls.NODE, cls.NAME]):
|
361
416
|
log.debug("Not adding command '%s' because it looks abstract", cls)
|
@@ -370,7 +425,7 @@ class BaseGateway(
|
|
370
425
|
self.__adhoc_handler.register(c)
|
371
426
|
self.__chat_commands_handler.register(c)
|
372
427
|
|
373
|
-
def __exception_handler(self, loop: asyncio.AbstractEventLoop, context):
|
428
|
+
def __exception_handler(self, loop: asyncio.AbstractEventLoop, context) -> None:
|
374
429
|
"""
|
375
430
|
Called when a task created by loop.create_task() raises an Exception
|
376
431
|
|
@@ -390,7 +445,7 @@ class BaseGateway(
|
|
390
445
|
self.has_crashed = True
|
391
446
|
loop.stop()
|
392
447
|
|
393
|
-
def __register_slixmpp_events(self):
|
448
|
+
def __register_slixmpp_events(self) -> None:
|
394
449
|
self.del_event_handler("presence_subscribe", self._handle_subscribe)
|
395
450
|
self.del_event_handler("presence_unsubscribe", self._handle_unsubscribe)
|
396
451
|
self.del_event_handler("presence_subscribed", self._handle_subscribed)
|
@@ -403,16 +458,32 @@ class BaseGateway(
|
|
403
458
|
self.add_event_handler("disconnected", self.connect)
|
404
459
|
|
405
460
|
def __register_slixmpp_api(self) -> None:
|
406
|
-
|
407
|
-
|
408
|
-
|
461
|
+
def with_session(func, commit: bool = True):
|
462
|
+
def wrapped(*a, **kw):
|
463
|
+
with self.store.session() as orm:
|
464
|
+
res = func(orm, *a, **kw)
|
465
|
+
if commit:
|
466
|
+
orm.commit()
|
467
|
+
return res
|
468
|
+
|
469
|
+
return wrapped
|
470
|
+
|
471
|
+
self.plugin["xep_0231"].api.register(
|
472
|
+
with_session(self.store.bob.get_bob, False), "get_bob"
|
473
|
+
)
|
474
|
+
self.plugin["xep_0231"].api.register(
|
475
|
+
with_session(self.store.bob.set_bob), "set_bob"
|
476
|
+
)
|
477
|
+
self.plugin["xep_0231"].api.register(
|
478
|
+
with_session(self.store.bob.del_bob), "del_bob"
|
479
|
+
)
|
409
480
|
|
410
481
|
@property # type: ignore
|
411
|
-
def jid(self):
|
482
|
+
def jid(self): # type:ignore[override]
|
412
483
|
# Override to avoid slixmpp deprecation warnings.
|
413
484
|
return self.boundjid
|
414
485
|
|
415
|
-
async def __on_session_start(self, event):
|
486
|
+
async def __on_session_start(self, event) -> None:
|
416
487
|
log.debug("Gateway session start: %s", event)
|
417
488
|
|
418
489
|
# prevents XMPP clients from considering the gateway as an HTTP upload
|
@@ -421,47 +492,48 @@ class BaseGateway(
|
|
421
492
|
await self.plugin["xep_0115"].update_caps(jid=self.boundjid)
|
422
493
|
|
423
494
|
if self.COMPONENT_AVATAR is not None:
|
495
|
+
log.debug("Setting gateway avatar…")
|
496
|
+
avatar = convert_avatar(self.COMPONENT_AVATAR, "!!---slidge---special---")
|
497
|
+
assert avatar is not None
|
424
498
|
try:
|
425
|
-
cached_avatar = await avatar_cache.convert_or_get(
|
499
|
+
cached_avatar = await avatar_cache.convert_or_get(avatar)
|
426
500
|
except Exception as e:
|
427
501
|
log.exception("Could not set the component avatar.", exc_info=e)
|
428
502
|
cached_avatar = None
|
429
503
|
else:
|
430
504
|
assert cached_avatar is not None
|
431
|
-
self.
|
505
|
+
self.avatar = cached_avatar
|
432
506
|
else:
|
433
507
|
cached_avatar = None
|
434
508
|
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
self.boundjid.bare, session.user_jid, cached_avatar
|
460
|
-
)
|
509
|
+
with self.store.session() as orm:
|
510
|
+
for user in orm.query(GatewayUser).all():
|
511
|
+
# TODO: before this, we should check if the user has removed us from their roster
|
512
|
+
# while we were offline and trigger unregister from there. Presence probe does not seem
|
513
|
+
# to work in this case, there must be another way. privileged entity could be used
|
514
|
+
# as last resort.
|
515
|
+
try:
|
516
|
+
await self["xep_0100"].add_component_to_roster(user.jid)
|
517
|
+
await self.__add_component_to_mds_whitelist(user.jid)
|
518
|
+
except (IqError, IqTimeout) as e:
|
519
|
+
# TODO: remove the user when this happens? or at least
|
520
|
+
# this can happen when the user has unsubscribed from the XMPP server
|
521
|
+
log.warning(
|
522
|
+
"Error with user %s, not logging them automatically",
|
523
|
+
user,
|
524
|
+
exc_info=e,
|
525
|
+
)
|
526
|
+
continue
|
527
|
+
session = self._session_cls.from_user(user)
|
528
|
+
session.create_task(self.login_wrap(session))
|
529
|
+
if cached_avatar is not None:
|
530
|
+
await self.pubsub.broadcast_avatar(
|
531
|
+
self.boundjid.bare, session.user_jid, cached_avatar
|
532
|
+
)
|
461
533
|
|
462
534
|
log.info("Slidge has successfully started")
|
463
535
|
|
464
|
-
async def __add_component_to_mds_whitelist(self, user_jid: JID):
|
536
|
+
async def __add_component_to_mds_whitelist(self, user_jid: JID) -> None:
|
465
537
|
# Uses privileged entity to add ourselves to the whitelist of the PEP
|
466
538
|
# MDS node so we receive MDS events
|
467
539
|
iq_creation = Iq(sto=user_jid.bare, sfrom=user_jid, stype="set")
|
@@ -515,7 +587,7 @@ class BaseGateway(
|
|
515
587
|
)
|
516
588
|
|
517
589
|
@timeit
|
518
|
-
async def login_wrap(self, session: "BaseSession"):
|
590
|
+
async def login_wrap(self, session: "BaseSession") -> None:
|
519
591
|
session.send_gateway_status("Logging in…", show="dnd")
|
520
592
|
session.is_logging_in = True
|
521
593
|
try:
|
@@ -534,7 +606,8 @@ class BaseGateway(
|
|
534
606
|
log.info("Login success for %s", session.user_jid)
|
535
607
|
session.logged = True
|
536
608
|
session.send_gateway_status("Syncing contacts…", show="dnd")
|
537
|
-
|
609
|
+
with self.store.session() as orm:
|
610
|
+
await session.contacts._fill(orm)
|
538
611
|
if not (r := session.contacts.ready).done():
|
539
612
|
r.set_result(True)
|
540
613
|
if self.GROUPS:
|
@@ -542,10 +615,7 @@ class BaseGateway(
|
|
542
615
|
await session.bookmarks.fill()
|
543
616
|
if not (r := session.bookmarks.ready).done():
|
544
617
|
r.set_result(True)
|
545
|
-
|
546
|
-
# we need to receive presences directed at the contacts, in
|
547
|
-
# order to send pubsub events for their +notify features
|
548
|
-
self.send_presence(pfrom=c.jid, pto=session.user_jid.bare, ptype="probe")
|
618
|
+
self.send_presence(pto=session.user.jid.bare, ptype="probe")
|
549
619
|
if status is None:
|
550
620
|
session.send_gateway_status("Logged in", show="chat")
|
551
621
|
else:
|
@@ -553,9 +623,12 @@ class BaseGateway(
|
|
553
623
|
if session.user.preferences.get("sync_avatar", False):
|
554
624
|
session.create_task(self.fetch_user_avatar(session))
|
555
625
|
else:
|
556
|
-
self.
|
626
|
+
with self.store.session(expire_on_commit=False) as orm:
|
627
|
+
session.user.avatar_hash = None
|
628
|
+
orm.add(session.user)
|
629
|
+
orm.commit()
|
557
630
|
|
558
|
-
async def fetch_user_avatar(self, session: BaseSession):
|
631
|
+
async def fetch_user_avatar(self, session: BaseSession) -> None:
|
559
632
|
try:
|
560
633
|
iq = await self.xmpp.plugin["xep_0060"].get_items(
|
561
634
|
session.user_jid.bare,
|
@@ -573,7 +646,10 @@ class BaseGateway(
|
|
573
646
|
except NotImplementedError:
|
574
647
|
pass
|
575
648
|
else:
|
576
|
-
self.
|
649
|
+
with self.store.session(expire_on_commit=False) as orm:
|
650
|
+
session.user.avatar_hash = None
|
651
|
+
orm.add(session.user)
|
652
|
+
orm.commit()
|
577
653
|
return
|
578
654
|
await self.__dispatcher.on_avatar_metadata_info(
|
579
655
|
session, iq["pubsub"]["items"]["item"]["avatar_metadata"]["info"]
|
@@ -622,11 +698,11 @@ class BaseGateway(
|
|
622
698
|
|
623
699
|
def get_session_from_jid(self, j: JID):
|
624
700
|
try:
|
625
|
-
return self.
|
701
|
+
return self._session_cls.from_jid(j)
|
626
702
|
except XMPPError:
|
627
703
|
pass
|
628
704
|
|
629
|
-
def exception(self, exception: Exception):
|
705
|
+
def exception(self, exception: Exception) -> None:
|
630
706
|
# """
|
631
707
|
# Called when a task created by slixmpp's internal (eg, on slix events) raises an Exception.
|
632
708
|
#
|
@@ -656,18 +732,26 @@ class BaseGateway(
|
|
656
732
|
self.loop.stop()
|
657
733
|
exit(1)
|
658
734
|
|
659
|
-
def re_login(self, session: "BaseSession"):
|
660
|
-
async def w():
|
735
|
+
def re_login(self, session: "BaseSession") -> None:
|
736
|
+
async def w() -> None:
|
661
737
|
session.cancel_all_tasks()
|
662
|
-
|
738
|
+
try:
|
739
|
+
await session.logout()
|
740
|
+
except NotImplementedError:
|
741
|
+
pass
|
663
742
|
await self.login_wrap(session)
|
664
743
|
|
665
744
|
session.create_task(w())
|
666
745
|
|
667
|
-
async def make_registration_form(
|
746
|
+
async def make_registration_form(
|
747
|
+
self, _jid: JID, _node: str, _ifrom: JID, iq: Iq
|
748
|
+
) -> Form:
|
668
749
|
self.raise_if_not_allowed_jid(iq.get_from())
|
669
750
|
reg = iq["register"]
|
670
|
-
|
751
|
+
with self.store.session() as orm:
|
752
|
+
user = (
|
753
|
+
orm.query(GatewayUser).filter_by(jid=iq.get_from().bare).one_or_none()
|
754
|
+
)
|
671
755
|
log.debug("User found: %s", user)
|
672
756
|
|
673
757
|
form = reg["form"]
|
@@ -715,7 +799,7 @@ class BaseGateway(
|
|
715
799
|
|
716
800
|
async def user_prevalidate(
|
717
801
|
self, ifrom: JID, form_dict: dict[str, Optional[str]]
|
718
|
-
) ->
|
802
|
+
) -> JSONSerializable | None:
|
719
803
|
# Pre validate a registration form using the content of self.REGISTRATION_FIELDS
|
720
804
|
# before passing it to the plugin custom validation logic.
|
721
805
|
for field in self.REGISTRATION_FIELDS:
|
@@ -726,7 +810,7 @@ class BaseGateway(
|
|
726
810
|
|
727
811
|
async def validate(
|
728
812
|
self, user_jid: JID, registration_form: dict[str, Optional[str]]
|
729
|
-
) ->
|
813
|
+
) -> JSONSerializable | None:
|
730
814
|
"""
|
731
815
|
Validate a user's initial registration form.
|
732
816
|
|
@@ -758,7 +842,7 @@ class BaseGateway(
|
|
758
842
|
|
759
843
|
async def validate_two_factor_code(
|
760
844
|
self, user: GatewayUser, code: str
|
761
|
-
) ->
|
845
|
+
) -> JSONSerializable | None:
|
762
846
|
"""
|
763
847
|
Called when the user enters their 2FA code.
|
764
848
|
|
@@ -798,8 +882,8 @@ class BaseGateway(
|
|
798
882
|
self,
|
799
883
|
user_bare_jid: str,
|
800
884
|
exception: Optional[Exception] = None,
|
801
|
-
legacy_data: Optional[
|
802
|
-
):
|
885
|
+
legacy_data: Optional[JSONSerializable] = None,
|
886
|
+
) -> None:
|
803
887
|
"""
|
804
888
|
This method is meant to be called to finalize QR code-based registration
|
805
889
|
flows, once the legacy service confirms the QR flashing.
|
@@ -820,32 +904,35 @@ class BaseGateway(
|
|
820
904
|
else:
|
821
905
|
fut.set_exception(exception)
|
822
906
|
|
823
|
-
async def unregister_user(self, user: GatewayUser):
|
907
|
+
async def unregister_user(self, user: GatewayUser) -> None:
|
824
908
|
self.send_presence(
|
825
909
|
pshow="dnd",
|
826
910
|
pstatus="You unregistered from this gateway.",
|
827
911
|
pto=user.jid,
|
828
912
|
)
|
829
913
|
await self.xmpp.plugin["xep_0077"].api["user_remove"](None, None, user.jid)
|
830
|
-
await self.xmpp.
|
914
|
+
await self.xmpp._session_cls.kill_by_jid(user.jid)
|
831
915
|
|
832
|
-
async def unregister(self,
|
916
|
+
async def unregister(self, session: BaseSession) -> None:
|
833
917
|
"""
|
834
918
|
Optionally override this if you need to clean additional
|
835
919
|
stuff after a user has been removed from the persistent user store.
|
836
920
|
|
837
921
|
By default, this just calls :meth:`BaseSession.logout`.
|
838
922
|
|
839
|
-
:param user
|
923
|
+
:param session: The session of the user who just unregistered
|
840
924
|
"""
|
841
|
-
session = self.get_session_from_user(user)
|
842
925
|
try:
|
843
926
|
await session.logout()
|
844
927
|
except NotImplementedError:
|
845
928
|
pass
|
846
929
|
|
847
930
|
async def input(
|
848
|
-
self,
|
931
|
+
self,
|
932
|
+
jid: JID,
|
933
|
+
text: str | None = None,
|
934
|
+
mtype: MessageTypes = "chat",
|
935
|
+
**input_kwargs: Any,
|
849
936
|
) -> str:
|
850
937
|
"""
|
851
938
|
Request arbitrary user input using a simple chat message, and await the result.
|
@@ -858,9 +945,11 @@ class BaseGateway(
|
|
858
945
|
:param mtype: Message type
|
859
946
|
:return: The user's reply
|
860
947
|
"""
|
861
|
-
return await self.__chat_commands_handler.input(
|
948
|
+
return await self.__chat_commands_handler.input(
|
949
|
+
jid, text, mtype=mtype, **input_kwargs
|
950
|
+
)
|
862
951
|
|
863
|
-
async def send_qr(self, text: str, **msg_kwargs):
|
952
|
+
async def send_qr(self, text: str, **msg_kwargs: Any) -> None:
|
864
953
|
"""
|
865
954
|
Sends a QR Code to a JID
|
866
955
|
|
@@ -877,9 +966,9 @@ class BaseGateway(
|
|
877
966
|
suffix=".png", delete=config.NO_UPLOAD_METHOD != "move"
|
878
967
|
) as f:
|
879
968
|
qr.save(f.name)
|
880
|
-
await self.send_file(f.name, **msg_kwargs)
|
969
|
+
await self.send_file(Path(f.name), **msg_kwargs)
|
881
970
|
|
882
|
-
def shutdown(self) -> list[asyncio.Task]:
|
971
|
+
def shutdown(self) -> list[asyncio.Task[None]]:
|
883
972
|
# """
|
884
973
|
# Called by the slidge entrypoint on normal exit.
|
885
974
|
#
|
@@ -889,9 +978,10 @@ class BaseGateway(
|
|
889
978
|
# """
|
890
979
|
log.debug("Shutting down")
|
891
980
|
tasks = []
|
892
|
-
|
893
|
-
|
894
|
-
|
981
|
+
with self.store.session() as orm:
|
982
|
+
for user in orm.query(GatewayUser).all():
|
983
|
+
tasks.append(self._session_cls.from_jid(user.jid).shutdown())
|
984
|
+
self.send_presence(ptype="unavailable", pto=user.jid)
|
895
985
|
return tasks
|
896
986
|
|
897
987
|
|
slidge/core/mixins/__init__.py
CHANGED
@@ -2,8 +2,6 @@
|
|
2
2
|
Mixins
|
3
3
|
"""
|
4
4
|
|
5
|
-
from typing import Optional
|
6
|
-
|
7
5
|
from .avatar import AvatarMixin
|
8
6
|
from .disco import ChatterDiscoMixin
|
9
7
|
from .message import MessageCarbonMixin, MessageMixin
|
@@ -18,12 +16,4 @@ class FullCarbonMixin(ChatterDiscoMixin, MessageCarbonMixin, PresenceMixin):
|
|
18
16
|
pass
|
19
17
|
|
20
18
|
|
21
|
-
|
22
|
-
def serialize_extra_attributes(self) -> Optional[dict]:
|
23
|
-
return None
|
24
|
-
|
25
|
-
def deserialize_extra_attributes(self, data: dict) -> None:
|
26
|
-
pass
|
27
|
-
|
28
|
-
|
29
|
-
__all__ = ("AvatarMixin", "FullCarbonMixin", "StoredAttributeMixin")
|
19
|
+
__all__ = ("AvatarMixin", "FullCarbonMixin")
|