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/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,27 @@ 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
|
+
),
|
150
175
|
]
|
151
176
|
|
152
177
|
ROSTER_GROUP: str = "slidge"
|
@@ -211,7 +236,6 @@ class BaseGateway(
|
|
211
236
|
is_group = False
|
212
237
|
_can_send_carbon = False
|
213
238
|
store: SlidgeStore
|
214
|
-
avatar_pk: int
|
215
239
|
|
216
240
|
AVATAR_ID_TYPE: Callable[[str], Any] = str
|
217
241
|
"""
|
@@ -253,8 +277,9 @@ class BaseGateway(
|
|
253
277
|
"""
|
254
278
|
|
255
279
|
http: aiohttp.ClientSession
|
280
|
+
avatar: CachedAvatar | None = None
|
256
281
|
|
257
|
-
def __init__(self):
|
282
|
+
def __init__(self) -> None:
|
258
283
|
if config.COMPONENT_NAME:
|
259
284
|
self.COMPONENT_NAME = config.COMPONENT_NAME
|
260
285
|
if config.WELCOME_MESSAGE:
|
@@ -298,18 +323,13 @@ class BaseGateway(
|
|
298
323
|
self.jid_validator: re.Pattern = re.compile(config.USER_JID_VALIDATOR)
|
299
324
|
self.qr_pending_registrations = dict[str, asyncio.Future[Optional[dict]]]()
|
300
325
|
|
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
|
326
|
+
self.__setup_legacy_module_subclasses()
|
307
327
|
|
308
328
|
self.get_session_from_stanza: Callable[
|
309
329
|
[Union[Message, Presence, Iq]], BaseSession
|
310
|
-
] = self.
|
330
|
+
] = self._session_cls.from_stanza
|
311
331
|
self.get_session_from_user: Callable[[GatewayUser], BaseSession] = (
|
312
|
-
self.
|
332
|
+
self._session_cls.from_user
|
313
333
|
)
|
314
334
|
|
315
335
|
self.register_plugins()
|
@@ -349,13 +369,41 @@ class BaseGateway(
|
|
349
369
|
|
350
370
|
MessageMixin.__init__(self) # ComponentXMPP does not call super().__init__()
|
351
371
|
|
352
|
-
|
372
|
+
def __setup_legacy_module_subclasses(self):
|
373
|
+
from ..contact.roster import LegacyRoster
|
374
|
+
from ..group.bookmarks import LegacyBookmarks
|
375
|
+
from ..group.room import LegacyMUC, LegacyParticipant
|
376
|
+
|
377
|
+
session_cls: Type[BaseSession] = cast(
|
378
|
+
Type[BaseSession], BaseSession.get_unique_subclass()
|
379
|
+
)
|
380
|
+
contact_cls = LegacyContact.get_self_or_unique_subclass()
|
381
|
+
muc_cls = LegacyMUC.get_self_or_unique_subclass()
|
382
|
+
participant_cls = LegacyParticipant.get_self_or_unique_subclass()
|
383
|
+
bookmarks_cls = LegacyBookmarks.get_self_or_unique_subclass()
|
384
|
+
roster_cls = LegacyRoster.get_self_or_unique_subclass()
|
385
|
+
|
386
|
+
session_cls.xmpp = self # type:ignore[attr-defined]
|
387
|
+
contact_cls.xmpp = self # type:ignore[attr-defined]
|
388
|
+
muc_cls.xmpp = self # type:ignore[attr-defined]
|
389
|
+
|
390
|
+
self._session_cls = session_cls
|
391
|
+
session_cls._bookmarks_cls = bookmarks_cls # type:ignore[misc,assignment]
|
392
|
+
session_cls._roster_cls = roster_cls # type:ignore[misc,assignment]
|
393
|
+
LegacyRoster._contact_cls = contact_cls # type:ignore[misc]
|
394
|
+
LegacyBookmarks._muc_cls = muc_cls # type:ignore[misc]
|
395
|
+
LegacyMUC._participant_cls = participant_cls # type:ignore[misc]
|
396
|
+
|
397
|
+
async def kill_session(self, jid: JID) -> None:
|
398
|
+
await self._session_cls.kill_by_jid(jid)
|
399
|
+
|
400
|
+
async def __set_http(self) -> None:
|
353
401
|
self.http = aiohttp.ClientSession()
|
354
402
|
if getattr(self, "_test_mode", False):
|
355
403
|
return
|
356
404
|
avatar_cache.http = self.http
|
357
405
|
|
358
|
-
def __register_commands(self):
|
406
|
+
def __register_commands(self) -> None:
|
359
407
|
for cls in Command.subclasses:
|
360
408
|
if any(x is NotImplemented for x in [cls.CHAT_COMMAND, cls.NODE, cls.NAME]):
|
361
409
|
log.debug("Not adding command '%s' because it looks abstract", cls)
|
@@ -370,7 +418,7 @@ class BaseGateway(
|
|
370
418
|
self.__adhoc_handler.register(c)
|
371
419
|
self.__chat_commands_handler.register(c)
|
372
420
|
|
373
|
-
def __exception_handler(self, loop: asyncio.AbstractEventLoop, context):
|
421
|
+
def __exception_handler(self, loop: asyncio.AbstractEventLoop, context) -> None:
|
374
422
|
"""
|
375
423
|
Called when a task created by loop.create_task() raises an Exception
|
376
424
|
|
@@ -390,7 +438,7 @@ class BaseGateway(
|
|
390
438
|
self.has_crashed = True
|
391
439
|
loop.stop()
|
392
440
|
|
393
|
-
def __register_slixmpp_events(self):
|
441
|
+
def __register_slixmpp_events(self) -> None:
|
394
442
|
self.del_event_handler("presence_subscribe", self._handle_subscribe)
|
395
443
|
self.del_event_handler("presence_unsubscribe", self._handle_unsubscribe)
|
396
444
|
self.del_event_handler("presence_subscribed", self._handle_subscribed)
|
@@ -403,16 +451,32 @@ class BaseGateway(
|
|
403
451
|
self.add_event_handler("disconnected", self.connect)
|
404
452
|
|
405
453
|
def __register_slixmpp_api(self) -> None:
|
406
|
-
|
407
|
-
|
408
|
-
|
454
|
+
def with_session(func, commit: bool = True):
|
455
|
+
def wrapped(*a, **kw):
|
456
|
+
with self.store.session() as orm:
|
457
|
+
res = func(orm, *a, **kw)
|
458
|
+
if commit:
|
459
|
+
orm.commit()
|
460
|
+
return res
|
461
|
+
|
462
|
+
return wrapped
|
463
|
+
|
464
|
+
self.plugin["xep_0231"].api.register(
|
465
|
+
with_session(self.store.bob.get_bob, False), "get_bob"
|
466
|
+
)
|
467
|
+
self.plugin["xep_0231"].api.register(
|
468
|
+
with_session(self.store.bob.set_bob), "set_bob"
|
469
|
+
)
|
470
|
+
self.plugin["xep_0231"].api.register(
|
471
|
+
with_session(self.store.bob.del_bob), "del_bob"
|
472
|
+
)
|
409
473
|
|
410
474
|
@property # type: ignore
|
411
475
|
def jid(self):
|
412
476
|
# Override to avoid slixmpp deprecation warnings.
|
413
477
|
return self.boundjid
|
414
478
|
|
415
|
-
async def __on_session_start(self, event):
|
479
|
+
async def __on_session_start(self, event) -> None:
|
416
480
|
log.debug("Gateway session start: %s", event)
|
417
481
|
|
418
482
|
# prevents XMPP clients from considering the gateway as an HTTP upload
|
@@ -421,47 +485,48 @@ class BaseGateway(
|
|
421
485
|
await self.plugin["xep_0115"].update_caps(jid=self.boundjid)
|
422
486
|
|
423
487
|
if self.COMPONENT_AVATAR is not None:
|
488
|
+
log.debug("Setting gateway avatar…")
|
489
|
+
avatar = convert_avatar(self.COMPONENT_AVATAR, "!!---slidge---special---")
|
490
|
+
assert avatar is not None
|
424
491
|
try:
|
425
|
-
cached_avatar = await avatar_cache.convert_or_get(
|
492
|
+
cached_avatar = await avatar_cache.convert_or_get(avatar)
|
426
493
|
except Exception as e:
|
427
494
|
log.exception("Could not set the component avatar.", exc_info=e)
|
428
495
|
cached_avatar = None
|
429
496
|
else:
|
430
497
|
assert cached_avatar is not None
|
431
|
-
self.
|
498
|
+
self.avatar = cached_avatar
|
432
499
|
else:
|
433
500
|
cached_avatar = None
|
434
501
|
|
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
|
-
)
|
502
|
+
with self.store.session() as orm:
|
503
|
+
for user in orm.query(GatewayUser).all():
|
504
|
+
# TODO: before this, we should check if the user has removed us from their roster
|
505
|
+
# while we were offline and trigger unregister from there. Presence probe does not seem
|
506
|
+
# to work in this case, there must be another way. privileged entity could be used
|
507
|
+
# as last resort.
|
508
|
+
try:
|
509
|
+
await self["xep_0100"].add_component_to_roster(user.jid)
|
510
|
+
await self.__add_component_to_mds_whitelist(user.jid)
|
511
|
+
except (IqError, IqTimeout) as e:
|
512
|
+
# TODO: remove the user when this happens? or at least
|
513
|
+
# this can happen when the user has unsubscribed from the XMPP server
|
514
|
+
log.warning(
|
515
|
+
"Error with user %s, not logging them automatically",
|
516
|
+
user,
|
517
|
+
exc_info=e,
|
518
|
+
)
|
519
|
+
continue
|
520
|
+
session = self._session_cls.from_user(user)
|
521
|
+
session.create_task(self.login_wrap(session))
|
522
|
+
if cached_avatar is not None:
|
523
|
+
await self.pubsub.broadcast_avatar(
|
524
|
+
self.boundjid.bare, session.user_jid, cached_avatar
|
525
|
+
)
|
461
526
|
|
462
527
|
log.info("Slidge has successfully started")
|
463
528
|
|
464
|
-
async def __add_component_to_mds_whitelist(self, user_jid: JID):
|
529
|
+
async def __add_component_to_mds_whitelist(self, user_jid: JID) -> None:
|
465
530
|
# Uses privileged entity to add ourselves to the whitelist of the PEP
|
466
531
|
# MDS node so we receive MDS events
|
467
532
|
iq_creation = Iq(sto=user_jid.bare, sfrom=user_jid, stype="set")
|
@@ -515,7 +580,7 @@ class BaseGateway(
|
|
515
580
|
)
|
516
581
|
|
517
582
|
@timeit
|
518
|
-
async def login_wrap(self, session: "BaseSession"):
|
583
|
+
async def login_wrap(self, session: "BaseSession") -> None:
|
519
584
|
session.send_gateway_status("Logging in…", show="dnd")
|
520
585
|
session.is_logging_in = True
|
521
586
|
try:
|
@@ -534,7 +599,8 @@ class BaseGateway(
|
|
534
599
|
log.info("Login success for %s", session.user_jid)
|
535
600
|
session.logged = True
|
536
601
|
session.send_gateway_status("Syncing contacts…", show="dnd")
|
537
|
-
|
602
|
+
with self.store.session() as orm:
|
603
|
+
await session.contacts._fill(orm)
|
538
604
|
if not (r := session.contacts.ready).done():
|
539
605
|
r.set_result(True)
|
540
606
|
if self.GROUPS:
|
@@ -542,10 +608,7 @@ class BaseGateway(
|
|
542
608
|
await session.bookmarks.fill()
|
543
609
|
if not (r := session.bookmarks.ready).done():
|
544
610
|
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")
|
611
|
+
self.send_presence(pto=session.user.jid.bare, ptype="probe")
|
549
612
|
if status is None:
|
550
613
|
session.send_gateway_status("Logged in", show="chat")
|
551
614
|
else:
|
@@ -553,9 +616,12 @@ class BaseGateway(
|
|
553
616
|
if session.user.preferences.get("sync_avatar", False):
|
554
617
|
session.create_task(self.fetch_user_avatar(session))
|
555
618
|
else:
|
556
|
-
self.
|
619
|
+
with self.store.session(expire_on_commit=False) as orm:
|
620
|
+
session.user.avatar_hash = None
|
621
|
+
orm.add(session.user)
|
622
|
+
orm.commit()
|
557
623
|
|
558
|
-
async def fetch_user_avatar(self, session: BaseSession):
|
624
|
+
async def fetch_user_avatar(self, session: BaseSession) -> None:
|
559
625
|
try:
|
560
626
|
iq = await self.xmpp.plugin["xep_0060"].get_items(
|
561
627
|
session.user_jid.bare,
|
@@ -573,7 +639,10 @@ class BaseGateway(
|
|
573
639
|
except NotImplementedError:
|
574
640
|
pass
|
575
641
|
else:
|
576
|
-
self.
|
642
|
+
with self.store.session(expire_on_commit=False) as orm:
|
643
|
+
session.user.avatar_hash = None
|
644
|
+
orm.add(session.user)
|
645
|
+
orm.commit()
|
577
646
|
return
|
578
647
|
await self.__dispatcher.on_avatar_metadata_info(
|
579
648
|
session, iq["pubsub"]["items"]["item"]["avatar_metadata"]["info"]
|
@@ -622,11 +691,11 @@ class BaseGateway(
|
|
622
691
|
|
623
692
|
def get_session_from_jid(self, j: JID):
|
624
693
|
try:
|
625
|
-
return self.
|
694
|
+
return self._session_cls.from_jid(j)
|
626
695
|
except XMPPError:
|
627
696
|
pass
|
628
697
|
|
629
|
-
def exception(self, exception: Exception):
|
698
|
+
def exception(self, exception: Exception) -> None:
|
630
699
|
# """
|
631
700
|
# Called when a task created by slixmpp's internal (eg, on slix events) raises an Exception.
|
632
701
|
#
|
@@ -656,18 +725,26 @@ class BaseGateway(
|
|
656
725
|
self.loop.stop()
|
657
726
|
exit(1)
|
658
727
|
|
659
|
-
def re_login(self, session: "BaseSession"):
|
660
|
-
async def w():
|
728
|
+
def re_login(self, session: "BaseSession") -> None:
|
729
|
+
async def w() -> None:
|
661
730
|
session.cancel_all_tasks()
|
662
|
-
|
731
|
+
try:
|
732
|
+
await session.logout()
|
733
|
+
except NotImplementedError:
|
734
|
+
pass
|
663
735
|
await self.login_wrap(session)
|
664
736
|
|
665
737
|
session.create_task(w())
|
666
738
|
|
667
|
-
async def make_registration_form(
|
739
|
+
async def make_registration_form(
|
740
|
+
self, _jid: JID, _node: str, _ifrom: JID, iq: Iq
|
741
|
+
) -> Form:
|
668
742
|
self.raise_if_not_allowed_jid(iq.get_from())
|
669
743
|
reg = iq["register"]
|
670
|
-
|
744
|
+
with self.store.session() as orm:
|
745
|
+
user = (
|
746
|
+
orm.query(GatewayUser).filter_by(jid=iq.get_from().bare).one_or_none()
|
747
|
+
)
|
671
748
|
log.debug("User found: %s", user)
|
672
749
|
|
673
750
|
form = reg["form"]
|
@@ -715,7 +792,7 @@ class BaseGateway(
|
|
715
792
|
|
716
793
|
async def user_prevalidate(
|
717
794
|
self, ifrom: JID, form_dict: dict[str, Optional[str]]
|
718
|
-
) ->
|
795
|
+
) -> JSONSerializable | None:
|
719
796
|
# Pre validate a registration form using the content of self.REGISTRATION_FIELDS
|
720
797
|
# before passing it to the plugin custom validation logic.
|
721
798
|
for field in self.REGISTRATION_FIELDS:
|
@@ -726,7 +803,7 @@ class BaseGateway(
|
|
726
803
|
|
727
804
|
async def validate(
|
728
805
|
self, user_jid: JID, registration_form: dict[str, Optional[str]]
|
729
|
-
) ->
|
806
|
+
) -> JSONSerializable | None:
|
730
807
|
"""
|
731
808
|
Validate a user's initial registration form.
|
732
809
|
|
@@ -758,7 +835,7 @@ class BaseGateway(
|
|
758
835
|
|
759
836
|
async def validate_two_factor_code(
|
760
837
|
self, user: GatewayUser, code: str
|
761
|
-
) ->
|
838
|
+
) -> JSONSerializable | None:
|
762
839
|
"""
|
763
840
|
Called when the user enters their 2FA code.
|
764
841
|
|
@@ -798,8 +875,8 @@ class BaseGateway(
|
|
798
875
|
self,
|
799
876
|
user_bare_jid: str,
|
800
877
|
exception: Optional[Exception] = None,
|
801
|
-
legacy_data: Optional[
|
802
|
-
):
|
878
|
+
legacy_data: Optional[JSONSerializable] = None,
|
879
|
+
) -> None:
|
803
880
|
"""
|
804
881
|
This method is meant to be called to finalize QR code-based registration
|
805
882
|
flows, once the legacy service confirms the QR flashing.
|
@@ -820,16 +897,16 @@ class BaseGateway(
|
|
820
897
|
else:
|
821
898
|
fut.set_exception(exception)
|
822
899
|
|
823
|
-
async def unregister_user(self, user: GatewayUser):
|
900
|
+
async def unregister_user(self, user: GatewayUser) -> None:
|
824
901
|
self.send_presence(
|
825
902
|
pshow="dnd",
|
826
903
|
pstatus="You unregistered from this gateway.",
|
827
904
|
pto=user.jid,
|
828
905
|
)
|
829
906
|
await self.xmpp.plugin["xep_0077"].api["user_remove"](None, None, user.jid)
|
830
|
-
await self.xmpp.
|
907
|
+
await self.xmpp._session_cls.kill_by_jid(user.jid)
|
831
908
|
|
832
|
-
async def unregister(self, user: GatewayUser):
|
909
|
+
async def unregister(self, user: GatewayUser) -> None:
|
833
910
|
"""
|
834
911
|
Optionally override this if you need to clean additional
|
835
912
|
stuff after a user has been removed from the persistent user store.
|
@@ -845,7 +922,11 @@ class BaseGateway(
|
|
845
922
|
pass
|
846
923
|
|
847
924
|
async def input(
|
848
|
-
self,
|
925
|
+
self,
|
926
|
+
jid: JID,
|
927
|
+
text: str | None = None,
|
928
|
+
mtype: MessageTypes = "chat",
|
929
|
+
**input_kwargs: Any,
|
849
930
|
) -> str:
|
850
931
|
"""
|
851
932
|
Request arbitrary user input using a simple chat message, and await the result.
|
@@ -858,9 +939,11 @@ class BaseGateway(
|
|
858
939
|
:param mtype: Message type
|
859
940
|
:return: The user's reply
|
860
941
|
"""
|
861
|
-
return await self.__chat_commands_handler.input(
|
942
|
+
return await self.__chat_commands_handler.input(
|
943
|
+
jid, text, mtype=mtype, **input_kwargs
|
944
|
+
)
|
862
945
|
|
863
|
-
async def send_qr(self, text: str, **msg_kwargs):
|
946
|
+
async def send_qr(self, text: str, **msg_kwargs: Any) -> None:
|
864
947
|
"""
|
865
948
|
Sends a QR Code to a JID
|
866
949
|
|
@@ -877,9 +960,9 @@ class BaseGateway(
|
|
877
960
|
suffix=".png", delete=config.NO_UPLOAD_METHOD != "move"
|
878
961
|
) as f:
|
879
962
|
qr.save(f.name)
|
880
|
-
await self.send_file(f.name, **msg_kwargs)
|
963
|
+
await self.send_file(Path(f.name), **msg_kwargs)
|
881
964
|
|
882
|
-
def shutdown(self) -> list[asyncio.Task]:
|
965
|
+
def shutdown(self) -> list[asyncio.Task[None]]:
|
883
966
|
# """
|
884
967
|
# Called by the slidge entrypoint on normal exit.
|
885
968
|
#
|
@@ -889,9 +972,10 @@ class BaseGateway(
|
|
889
972
|
# """
|
890
973
|
log.debug("Shutting down")
|
891
974
|
tasks = []
|
892
|
-
|
893
|
-
|
894
|
-
|
975
|
+
with self.store.session() as orm:
|
976
|
+
for user in orm.query(GatewayUser).all():
|
977
|
+
tasks.append(self._session_cls.from_jid(user.jid).shutdown())
|
978
|
+
self.send_presence(ptype="unavailable", pto=user.jid)
|
895
979
|
return tasks
|
896
980
|
|
897
981
|
|
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")
|