slidge 0.2.4__py3-none-any.whl → 0.2.6__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- slidge/__version__.py +1 -1
- slidge/command/adhoc.py +8 -0
- slidge/command/base.py +6 -2
- slidge/command/user.py +5 -0
- slidge/contact/contact.py +2 -8
- slidge/core/config.py +12 -5
- slidge/core/gateway.py +16 -4
- slidge/core/mixins/attachment.py +1 -0
- slidge/core/mixins/message.py +6 -9
- slidge/core/pubsub.py +2 -1
- slidge/core/session.py +11 -3
- slidge/db/alembic/versions/04cf35e3cf85_add_participant_nickname_no_illegal.py +33 -0
- slidge/db/alembic/versions/aa9d82a7f6ef_db_creation.py +0 -51
- slidge/db/avatar.py +20 -1
- slidge/db/models.py +1 -0
- slidge/db/store.py +5 -0
- slidge/group/participant.py +12 -1
- slidge/group/room.py +1 -1
- slidge/slixfix/__init__.py +0 -2
- slidge/slixfix/xep_0100/gateway.py +1 -4
- slidge/util/util.py +5 -1
- {slidge-0.2.4.dist-info → slidge-0.2.6.dist-info}/METADATA +15 -15
- {slidge-0.2.4.dist-info → slidge-0.2.6.dist-info}/RECORD +26 -30
- slidge/db/alembic/old_user_store.py +0 -183
- slidge/slixfix/xep_0356_old/__init__.py +0 -7
- slidge/slixfix/xep_0356_old/privilege.py +0 -167
- slidge/slixfix/xep_0356_old/stanza.py +0 -44
- slidge/util/db.py +0 -5
- {slidge-0.2.4.dist-info → slidge-0.2.6.dist-info}/WHEEL +0 -0
- {slidge-0.2.4.dist-info → slidge-0.2.6.dist-info}/entry_points.txt +0 -0
- {slidge-0.2.4.dist-info → slidge-0.2.6.dist-info}/top_level.txt +0 -0
slidge/__version__.py
CHANGED
slidge/command/adhoc.py
CHANGED
@@ -240,6 +240,14 @@ class AdhocProvider:
|
|
240
240
|
:param iq: the disco query IQ
|
241
241
|
:return: commands accessible to the given JID will be listed
|
242
242
|
"""
|
243
|
+
if not self.xmpp.jid_validator.match(str(jid)) and jid not in config.ADMINS:
|
244
|
+
raise XMPPError(
|
245
|
+
"forbidden",
|
246
|
+
"You are not authorized to execute adhoc commands on this gateway. "
|
247
|
+
"If this is unexpected, ask your administrator to verify that "
|
248
|
+
"'user-jid-validator' is correctly set in slidge's configuration.",
|
249
|
+
)
|
250
|
+
|
243
251
|
all_items = self.xmpp.plugin["xep_0030"].static.get_items(jid, node, None, None)
|
244
252
|
log.debug("Static items: %r", all_items)
|
245
253
|
if not all_items:
|
slidge/command/base.py
CHANGED
@@ -293,12 +293,16 @@ class FormField:
|
|
293
293
|
return value
|
294
294
|
|
295
295
|
def __validate_list_multi(self, value: list[str]) -> Union[list[str], list[JID]]:
|
296
|
+
# COMPAT: all the "if v" and "if not v" are workarounds for https://codeberg.org/slidge/slidge/issues/43
|
297
|
+
# They should be reverted once the bug is fixed upstream, cf https://soprani.ca/todo/390
|
296
298
|
for v in value:
|
297
299
|
if v not in self.__acceptable_options():
|
300
|
+
if not v:
|
301
|
+
continue
|
298
302
|
raise XMPPError("not-acceptable", f"Not a valid option: '{v}'")
|
299
303
|
if self.type == "list-multi":
|
300
|
-
return value
|
301
|
-
return [JID(v) for v in value]
|
304
|
+
return [v for v in value if v]
|
305
|
+
return [JID(v) for v in value if v]
|
302
306
|
|
303
307
|
def get_xml(self) -> SlixFormField:
|
304
308
|
"""
|
slidge/command/user.py
CHANGED
@@ -172,6 +172,9 @@ class Login(Command):
|
|
172
172
|
|
173
173
|
async def run(self, session: Optional[AnyBaseSession], _ifrom, *_):
|
174
174
|
assert session is not None
|
175
|
+
if session.is_logging_in:
|
176
|
+
raise XMPPError("bad-request", "You are already logging in.")
|
177
|
+
session.is_logging_in = True
|
175
178
|
try:
|
176
179
|
msg = await session.login()
|
177
180
|
except Exception as e:
|
@@ -179,6 +182,8 @@ class Login(Command):
|
|
179
182
|
raise XMPPError(
|
180
183
|
"internal-server-error", etype="wait", text=f"Could not login: {e}"
|
181
184
|
)
|
185
|
+
finally:
|
186
|
+
session.is_logging_in = False
|
182
187
|
session.logged = True
|
183
188
|
session.send_gateway_status(msg or "Re-connected", show="chat")
|
184
189
|
session.send_gateway_message(msg or "Re-connected")
|
slidge/contact/contact.py
CHANGED
@@ -448,13 +448,13 @@ class LegacyContact(
|
|
448
448
|
log.debug("Roster push request by plugin ignored (--no-roster-push)")
|
449
449
|
return
|
450
450
|
try:
|
451
|
-
await self.
|
451
|
+
await self.xmpp["xep_0356"].set_roster(
|
452
452
|
jid=self.user_jid, roster_items=self.get_roster_item()
|
453
453
|
)
|
454
454
|
except PermissionError:
|
455
455
|
warnings.warn(
|
456
456
|
"Slidge does not have privileges to add contacts to the roster. Refer"
|
457
|
-
" to https://slidge.
|
457
|
+
" to https://slidge.im/docs/slidge/main/admin/privilege.html for"
|
458
458
|
" more info."
|
459
459
|
)
|
460
460
|
if config.ROSTER_PUSH_PRESENCE_SUBSCRIPTION_REQUEST_FALLBACK:
|
@@ -490,12 +490,6 @@ class LegacyContact(
|
|
490
490
|
nick,
|
491
491
|
)
|
492
492
|
|
493
|
-
async def _set_roster(self, **kw):
|
494
|
-
try:
|
495
|
-
await self.xmpp["xep_0356"].set_roster(**kw)
|
496
|
-
except PermissionError:
|
497
|
-
await self.xmpp["xep_0356_old"].set_roster(**kw)
|
498
|
-
|
499
493
|
def send_friend_request(self, text: Optional[str] = None):
|
500
494
|
presence = self._make_presence(ptype="subscribe", pstatus=text, bare=True)
|
501
495
|
self._send(presence, nick=True)
|
slidge/core/config.py
CHANGED
@@ -68,7 +68,6 @@ USER_JID_VALIDATOR__DYNAMIC_DEFAULT = True
|
|
68
68
|
ADMINS: tuple[JIDType, ...] = ()
|
69
69
|
ADMINS__DOC = "JIDs of the gateway admins"
|
70
70
|
|
71
|
-
|
72
71
|
UPLOAD_SERVICE: Optional[str] = None
|
73
72
|
UPLOAD_SERVICE__DOC = (
|
74
73
|
"JID of an HTTP upload service the gateway can use. "
|
@@ -76,9 +75,6 @@ UPLOAD_SERVICE__DOC = (
|
|
76
75
|
"discovery."
|
77
76
|
)
|
78
77
|
|
79
|
-
SECRET_KEY: Optional[str] = None
|
80
|
-
SECRET_KEY__DOC = "Encryption for disk storage. Deprecated."
|
81
|
-
|
82
78
|
NO_ROSTER_PUSH = False
|
83
79
|
NO_ROSTER_PUSH__DOC = "Do not fill users' rosters with legacy contacts automatically"
|
84
80
|
|
@@ -187,7 +183,7 @@ MAM_MAX_DAYS__DOC = "Maximum number of days for group archive retention."
|
|
187
183
|
CORRECTION_EMPTY_BODY_AS_RETRACTION = True
|
188
184
|
CORRECTION_EMPTY_BODY_AS_RETRACTION__DOC = (
|
189
185
|
"Treat last message correction to empty message as a retraction. "
|
190
|
-
"(this is what cheogram
|
186
|
+
"(this is what cheogram does for retraction)"
|
191
187
|
)
|
192
188
|
|
193
189
|
ATTACHMENT_MAXIMUM_FILE_NAME_LENGTH = 200
|
@@ -220,3 +216,14 @@ STRIP_LEADING_EMOJI_ADHOC__DOC = (
|
|
220
216
|
"Strip the leading emoji in ad-hoc command names, if present, in case you "
|
221
217
|
"are a emoji-hater."
|
222
218
|
)
|
219
|
+
|
220
|
+
COMPONENT_NAME: Optional[str] = None
|
221
|
+
COMPONENT_NAME__DOC = (
|
222
|
+
"Overrides the default component name with a custom one. This is seen in service discovery and as the nickname "
|
223
|
+
"of the component in chat windows."
|
224
|
+
)
|
225
|
+
|
226
|
+
WELCOME_MESSAGE: Optional[str] = None
|
227
|
+
WELCOME_MESSAGE__DOC = (
|
228
|
+
"Overrides the default welcome message received by newly registered users."
|
229
|
+
)
|
slidge/core/gateway.py
CHANGED
@@ -254,6 +254,10 @@ class BaseGateway(
|
|
254
254
|
http: aiohttp.ClientSession
|
255
255
|
|
256
256
|
def __init__(self):
|
257
|
+
if config.COMPONENT_NAME:
|
258
|
+
self.COMPONENT_NAME = config.COMPONENT_NAME
|
259
|
+
if config.WELCOME_MESSAGE:
|
260
|
+
self.WELCOME_MESSAGE = config.WELCOME_MESSAGE
|
257
261
|
self.log = log
|
258
262
|
self.datetime_started = datetime.now()
|
259
263
|
self.xmpp = self # ugly hack to work with the BaseSender mixin :/
|
@@ -416,8 +420,14 @@ class BaseGateway(
|
|
416
420
|
await self.plugin["xep_0115"].update_caps(jid=self.boundjid)
|
417
421
|
|
418
422
|
if self.COMPONENT_AVATAR is not None:
|
419
|
-
|
420
|
-
|
423
|
+
try:
|
424
|
+
cached_avatar = await avatar_cache.convert_or_get(self.COMPONENT_AVATAR)
|
425
|
+
except Exception as e:
|
426
|
+
log.exception("Could not set the component avatar.", exc_info=e)
|
427
|
+
cached_avatar = None
|
428
|
+
else:
|
429
|
+
assert cached_avatar is not None
|
430
|
+
self.avatar_pk = cached_avatar.pk
|
421
431
|
else:
|
422
432
|
cached_avatar = None
|
423
433
|
|
@@ -505,6 +515,7 @@ class BaseGateway(
|
|
505
515
|
@timeit
|
506
516
|
async def login_wrap(self, session: "BaseSession"):
|
507
517
|
session.send_gateway_status("Logging in…", show="dnd")
|
518
|
+
session.is_logging_in = True
|
508
519
|
try:
|
509
520
|
status = await session.login()
|
510
521
|
except Exception as e:
|
@@ -798,7 +809,9 @@ class BaseGateway(
|
|
798
809
|
|
799
810
|
async def unregister_user(self, user: GatewayUser):
|
800
811
|
self.send_presence(
|
801
|
-
pshow="
|
812
|
+
pshow="dnd",
|
813
|
+
pstatus="You unregistered from this gateway.",
|
814
|
+
pto=user.jid,
|
802
815
|
)
|
803
816
|
await self.xmpp.plugin["xep_0077"].api["user_remove"](None, None, user.jid)
|
804
817
|
await self.xmpp.session_cls.kill_by_jid(user.jid)
|
@@ -903,7 +916,6 @@ SLIXMPP_PLUGINS = [
|
|
903
916
|
"xep_0333", # Chat markers
|
904
917
|
"xep_0334", # Message Processing Hints
|
905
918
|
"xep_0356", # Privileged Entity
|
906
|
-
"xep_0356_old", # Privileged Entity (old namespace)
|
907
919
|
"xep_0363", # HTTP file upload
|
908
920
|
"xep_0385", # Stateless in-line media sharing
|
909
921
|
"xep_0402", # PEP Native Bookmarks
|
slidge/core/mixins/attachment.py
CHANGED
slidge/core/mixins/message.py
CHANGED
@@ -177,15 +177,12 @@ class CarbonMessageMixin(ContentMessageMixin, MarkerMixin):
|
|
177
177
|
try:
|
178
178
|
self.xmpp["xep_0356"].send_privileged_message(msg)
|
179
179
|
except PermissionError:
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
" https://slidge.codeberg.page/docs/main/admin/privilege.html"
|
187
|
-
" for more info."
|
188
|
-
)
|
180
|
+
warnings.warn(
|
181
|
+
"Slidge does not have privileges to send message on behalf of"
|
182
|
+
" user.Refer to"
|
183
|
+
" https://slidge.im/docs/slidge/main/admin/privilege.html"
|
184
|
+
" for more info."
|
185
|
+
)
|
189
186
|
|
190
187
|
|
191
188
|
class InviteMixin(MessageMaker):
|
slidge/core/pubsub.py
CHANGED
@@ -207,7 +207,8 @@ class PubSubComponent(NamedLockMixin, BasePlugin):
|
|
207
207
|
) -> PepAvatar:
|
208
208
|
if stanza.get_to() == self.xmpp.boundjid.bare:
|
209
209
|
item = PepAvatar()
|
210
|
-
|
210
|
+
if hasattr(self.xmpp, "avatar_pk"):
|
211
|
+
item.set_avatar_from_cache(avatar_cache.get_by_pk(self.xmpp.avatar_pk))
|
211
212
|
return item
|
212
213
|
|
213
214
|
if contact is None:
|
slidge/core/session.py
CHANGED
@@ -33,7 +33,7 @@ from ..util.types import (
|
|
33
33
|
ResourceDict,
|
34
34
|
Sticker,
|
35
35
|
)
|
36
|
-
from ..util.util import deprecated
|
36
|
+
from ..util.util import deprecated, noop_coro
|
37
37
|
|
38
38
|
if TYPE_CHECKING:
|
39
39
|
from ..group.participant import LegacyParticipant
|
@@ -98,6 +98,7 @@ class BaseSession(
|
|
98
98
|
self.ignore_messages = set[str]()
|
99
99
|
|
100
100
|
self.contacts: LegacyRoster = LegacyRoster.get_self_or_unique_subclass()(self)
|
101
|
+
self.is_logging_in = False
|
101
102
|
self._logged = False
|
102
103
|
self.__reset_ready()
|
103
104
|
|
@@ -527,11 +528,13 @@ class BaseSession(
|
|
527
528
|
|
528
529
|
@logged.setter
|
529
530
|
def logged(self, v: bool):
|
531
|
+
self.is_logging_in = False
|
530
532
|
self._logged = v
|
531
533
|
if self.ready.done():
|
532
534
|
if v:
|
533
535
|
return
|
534
536
|
self.__reset_ready()
|
537
|
+
self.shutdown(logout=False)
|
535
538
|
else:
|
536
539
|
if v:
|
537
540
|
self.ready.set_result(True)
|
@@ -539,12 +542,15 @@ class BaseSession(
|
|
539
542
|
def __repr__(self):
|
540
543
|
return f"<Session of {self.user_jid}>"
|
541
544
|
|
542
|
-
def shutdown(self) -> asyncio.Task:
|
545
|
+
def shutdown(self, logout=True) -> asyncio.Task:
|
543
546
|
for c in self.contacts:
|
544
547
|
c.offline()
|
545
548
|
for m in self.bookmarks:
|
546
549
|
m.shutdown()
|
547
|
-
|
550
|
+
if logout:
|
551
|
+
return self.xmpp.loop.create_task(self.logout())
|
552
|
+
else:
|
553
|
+
return self.xmpp.loop.create_task(noop_coro())
|
548
554
|
|
549
555
|
@staticmethod
|
550
556
|
def legacy_to_xmpp_msg_id(legacy_msg_id: LegacyMessageType) -> str:
|
@@ -657,6 +663,8 @@ class BaseSession(
|
|
657
663
|
return
|
658
664
|
for c in session.contacts:
|
659
665
|
c.unsubscribe()
|
666
|
+
for m in session.bookmarks:
|
667
|
+
m.shutdown()
|
660
668
|
user = cls.xmpp.store.users.get(jid)
|
661
669
|
if user is None:
|
662
670
|
log.warning("User not found during unregistration")
|
@@ -0,0 +1,33 @@
|
|
1
|
+
"""Add Participant.nickname_no_illegal
|
2
|
+
|
3
|
+
Revision ID: 04cf35e3cf85
|
4
|
+
Revises: 15b0bd83407a
|
5
|
+
Create Date: 2025-02-22 06:57:45.491326
|
6
|
+
|
7
|
+
"""
|
8
|
+
|
9
|
+
from typing import Sequence, Union
|
10
|
+
|
11
|
+
import sqlalchemy as sa
|
12
|
+
from alembic import op
|
13
|
+
|
14
|
+
# revision identifiers, used by Alembic.
|
15
|
+
revision: str = "04cf35e3cf85"
|
16
|
+
down_revision: Union[str, None] = "15b0bd83407a"
|
17
|
+
branch_labels: Union[str, Sequence[str], None] = None
|
18
|
+
depends_on: Union[str, Sequence[str], None] = None
|
19
|
+
|
20
|
+
|
21
|
+
def upgrade() -> None:
|
22
|
+
# ### commands auto generated by Alembic - please adjust! ###
|
23
|
+
with op.batch_alter_table("participant", schema=None) as batch_op:
|
24
|
+
batch_op.add_column(
|
25
|
+
sa.Column("nickname_no_illegal", sa.String(), nullable=True)
|
26
|
+
)
|
27
|
+
|
28
|
+
# ### end Alembic commands ###
|
29
|
+
|
30
|
+
|
31
|
+
def downgrade() -> None:
|
32
|
+
with op.batch_alter_table("participant", schema=None) as batch_op:
|
33
|
+
batch_op.drop_column("nickname_no_illegal")
|
@@ -8,8 +8,6 @@ Create Date: 2024-04-17 20:57:01.357041
|
|
8
8
|
|
9
9
|
"""
|
10
10
|
|
11
|
-
import logging
|
12
|
-
from datetime import datetime
|
13
11
|
from typing import Sequence, Union
|
14
12
|
|
15
13
|
import sqlalchemy as sa
|
@@ -44,58 +42,9 @@ def upgrade() -> None:
|
|
44
42
|
sa.UniqueConstraint("jid"),
|
45
43
|
)
|
46
44
|
# ### end Alembic commands ###
|
47
|
-
try:
|
48
|
-
migrate_from_shelf(accounts)
|
49
|
-
except Exception:
|
50
|
-
downgrade()
|
51
|
-
raise
|
52
45
|
|
53
46
|
|
54
47
|
def downgrade() -> None:
|
55
48
|
# ### commands auto generated by Alembic - please adjust! ###
|
56
49
|
op.drop_table("user_account")
|
57
50
|
# ### end Alembic commands ###
|
58
|
-
|
59
|
-
|
60
|
-
def migrate_from_shelf(accounts: sa.Table) -> None:
|
61
|
-
from slidge import global_config
|
62
|
-
|
63
|
-
home = getattr(global_config, "HOME_DIR", None)
|
64
|
-
if home is None:
|
65
|
-
return
|
66
|
-
|
67
|
-
db_file = home / "slidge.db"
|
68
|
-
if not db_file.exists():
|
69
|
-
return
|
70
|
-
|
71
|
-
try:
|
72
|
-
from slidge.db.alembic.old_user_store import user_store
|
73
|
-
except ImportError:
|
74
|
-
return
|
75
|
-
|
76
|
-
user_store.set_file(db_file, global_config.SECRET_KEY)
|
77
|
-
|
78
|
-
try:
|
79
|
-
users = list(user_store.get_all())
|
80
|
-
except AttributeError:
|
81
|
-
return
|
82
|
-
logging.info("Migrating %s users from the deprecated user_store shelf", len(users))
|
83
|
-
op.bulk_insert(
|
84
|
-
accounts,
|
85
|
-
[
|
86
|
-
{
|
87
|
-
"jid": user.jid,
|
88
|
-
"registration_date": (
|
89
|
-
user.registration_date
|
90
|
-
if user.registration_date is not None
|
91
|
-
else datetime.now()
|
92
|
-
),
|
93
|
-
"legacy_module_data": user.registration_form,
|
94
|
-
"preferences": {},
|
95
|
-
}
|
96
|
-
for user in users
|
97
|
-
],
|
98
|
-
)
|
99
|
-
|
100
|
-
user_store.close()
|
101
|
-
db_file.unlink()
|
slidge/db/avatar.py
CHANGED
@@ -14,6 +14,7 @@ from multidict import CIMultiDictProxy
|
|
14
14
|
from PIL.Image import Image
|
15
15
|
from PIL.Image import open as open_image
|
16
16
|
from sqlalchemy import select
|
17
|
+
from sqlalchemy.orm.exc import DetachedInstanceError
|
17
18
|
|
18
19
|
from slidge.core import config
|
19
20
|
from slidge.db.models import Avatar
|
@@ -102,6 +103,7 @@ class AvatarCache:
|
|
102
103
|
if response.status == HTTPStatus.NOT_MODIFIED:
|
103
104
|
log.debug("Using avatar cache for %s", url)
|
104
105
|
raise NotModified
|
106
|
+
response.raise_for_status()
|
105
107
|
return (
|
106
108
|
open_image(io.BytesIO(await response.read())),
|
107
109
|
response.headers,
|
@@ -141,7 +143,24 @@ class AvatarCache:
|
|
141
143
|
)
|
142
144
|
except NotModified:
|
143
145
|
assert stored is not None
|
144
|
-
|
146
|
+
try:
|
147
|
+
return CachedAvatar.from_store(stored, self.dir)
|
148
|
+
except DetachedInstanceError:
|
149
|
+
# This is an awful hack to prevent errors on startup under certain conditions,
|
150
|
+
# because we basically misused SQLAlchemy pretty bad in slidge.db.store.EngineMixin.session().
|
151
|
+
# cf https://codeberg.org/slidge/slidge/issues/36
|
152
|
+
# and https://docs.sqlalchemy.org/en/20/orm/session_basics.html#session-faq-threadsafe
|
153
|
+
# It may be related to threads as we only have reports of this for slidge-whatsapp and skidge
|
154
|
+
# which are the only implementations that uses threads.
|
155
|
+
# In any case, a proper fix implies a major refactoring in which we spawn and close SQLAlchemy
|
156
|
+
# "ORM Session"s with a reasonable, well-thought lifetime, instead of how we do it now, where we
|
157
|
+
# basically just brute-forced our way into having something usable but with poor performance.
|
158
|
+
# Databases, asyncio, and concurrency in general are hard… :(
|
159
|
+
# Oh, and getting rid of the convoluted mess that this giant method is would also probably
|
160
|
+
# be a good idea.
|
161
|
+
stored = self.store.get_by_url(avatar)
|
162
|
+
assert stored is not None
|
163
|
+
return CachedAvatar.from_store(stored, self.dir)
|
145
164
|
else:
|
146
165
|
img = await self._get_image(avatar)
|
147
166
|
response_headers = None
|
slidge/db/models.py
CHANGED
@@ -388,6 +388,7 @@ class Participant(Base):
|
|
388
388
|
|
389
389
|
resource: Mapped[Optional[str]] = mapped_column(default=None)
|
390
390
|
nickname: Mapped[str] = mapped_column(nullable=True, default=None)
|
391
|
+
nickname_no_illegal: Mapped[str] = mapped_column(nullable=True, default=None)
|
391
392
|
|
392
393
|
hats: Mapped[list["Hat"]] = relationship(
|
393
394
|
secondary=participant_hats, back_populates="participants"
|
slidge/db/store.py
CHANGED
@@ -58,6 +58,8 @@ class EngineMixin:
|
|
58
58
|
def __init__(self, engine: Engine):
|
59
59
|
self._engine = engine
|
60
60
|
|
61
|
+
# TODO: we should not have a global Session object but instead build Sessions with different parameters
|
62
|
+
# depending on the context (startup, incoming XMPP event, incoming legacy event).
|
61
63
|
@contextmanager
|
62
64
|
def session(self, **session_kwargs) -> Iterator[Session]:
|
63
65
|
global _session
|
@@ -696,6 +698,8 @@ class MultiStore(EngineMixin):
|
|
696
698
|
).scalar()
|
697
699
|
if multi is None:
|
698
700
|
return []
|
701
|
+
if multi.legacy_ids_multi is None:
|
702
|
+
return []
|
699
703
|
return [m.xmpp_id for m in multi.legacy_ids_multi.xmpp_ids]
|
700
704
|
|
701
705
|
def set_xmpp_ids(
|
@@ -1070,6 +1074,7 @@ class ParticipantStore(EngineMixin):
|
|
1070
1074
|
.where(Participant.id == participant.pk)
|
1071
1075
|
.values(
|
1072
1076
|
resource=participant.jid.resource,
|
1077
|
+
nickname_no_illegal=participant._nickname_no_illegal,
|
1073
1078
|
affiliation=participant.affiliation,
|
1074
1079
|
role=participant.role,
|
1075
1080
|
presence_sent=participant._presence_sent, # type:ignore
|
slidge/group/participant.py
CHANGED
@@ -70,6 +70,8 @@ class LegacyParticipant(
|
|
70
70
|
is_system=False,
|
71
71
|
role: MucRole = "participant",
|
72
72
|
affiliation: MucAffiliation = "member",
|
73
|
+
resource: str | None = None,
|
74
|
+
nickname_no_illegal: str | None = None,
|
73
75
|
):
|
74
76
|
self.session = session = muc.session
|
75
77
|
self.xmpp = session.xmpp
|
@@ -83,7 +85,14 @@ class LegacyParticipant(
|
|
83
85
|
|
84
86
|
self._nickname = nickname
|
85
87
|
|
86
|
-
|
88
|
+
if resource is None:
|
89
|
+
self.__update_jid(nickname)
|
90
|
+
else:
|
91
|
+
assert nickname_no_illegal is not None
|
92
|
+
self._nickname_no_illegal = nickname_no_illegal
|
93
|
+
self.jid = JID(self.muc.jid)
|
94
|
+
self.jid.resource = resource
|
95
|
+
|
87
96
|
log.debug("Instantiation of: %r", self)
|
88
97
|
|
89
98
|
self.contact: Optional["LegacyContact"] = None
|
@@ -510,6 +519,8 @@ class LegacyParticipant(
|
|
510
519
|
stored.nickname,
|
511
520
|
role=stored.role,
|
512
521
|
affiliation=stored.affiliation,
|
522
|
+
resource=stored.resource,
|
523
|
+
nickname_no_illegal=stored.nickname_no_illegal,
|
513
524
|
)
|
514
525
|
part.pk = stored.id
|
515
526
|
if contact is not None:
|
slidge/group/room.py
CHANGED
@@ -423,7 +423,7 @@ class LegacyMUC(
|
|
423
423
|
|
424
424
|
if subject_setter == self._subject_setter:
|
425
425
|
return
|
426
|
-
assert isinstance(subject_setter, str)
|
426
|
+
assert isinstance(subject_setter, str | None)
|
427
427
|
self._subject_setter = subject_setter
|
428
428
|
if self._updating_info:
|
429
429
|
return
|
slidge/slixfix/__init__.py
CHANGED
@@ -20,7 +20,6 @@ from . import (
|
|
20
20
|
xep_0100,
|
21
21
|
xep_0153,
|
22
22
|
xep_0292,
|
23
|
-
xep_0356_old,
|
24
23
|
xep_0492,
|
25
24
|
)
|
26
25
|
|
@@ -148,7 +147,6 @@ slixmpp.plugins.PLUGINS.extend(
|
|
148
147
|
[
|
149
148
|
"link_preview",
|
150
149
|
"xep_0292_provider",
|
151
|
-
"xep_0356_old",
|
152
150
|
"xep_0492",
|
153
151
|
]
|
154
152
|
)
|
@@ -93,10 +93,7 @@ class XEP_0100(BasePlugin):
|
|
93
93
|
self.xmpp.send_presence(ptype="subscribe", pto=jid.bare)
|
94
94
|
|
95
95
|
async def _set_roster(self, jid, items):
|
96
|
-
|
97
|
-
await self.xmpp["xep_0356"].set_roster(jid=jid.bare, roster_items=items)
|
98
|
-
except PermissionError:
|
99
|
-
await self.xmpp["xep_0356_old"].set_roster(jid=jid.bare, roster_items=items)
|
96
|
+
await self.xmpp["xep_0356"].set_roster(jid=jid.bare, roster_items=items)
|
100
97
|
|
101
98
|
def on_presence_unsubscribe(self, p: Presence):
|
102
99
|
if p.get_to() == self.xmpp.boundjid.bare:
|
slidge/util/util.py
CHANGED
@@ -321,7 +321,7 @@ def timeit(func):
|
|
321
321
|
async def wrapped(self, *args, **kwargs):
|
322
322
|
start = time()
|
323
323
|
r = await func(self, *args, **kwargs)
|
324
|
-
self.log.
|
324
|
+
self.log.debug("%s took %s ms", func.__name__, round((time() - start) * 1000))
|
325
325
|
return r
|
326
326
|
|
327
327
|
return wrapped
|
@@ -336,3 +336,7 @@ def strip_leading_emoji(text: str) -> str:
|
|
336
336
|
if len(words) > 1 and emoji.purely_emoji(words[0]):
|
337
337
|
return " ".join(words[1:])
|
338
338
|
return text
|
339
|
+
|
340
|
+
|
341
|
+
async def noop_coro():
|
342
|
+
pass
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.2
|
2
2
|
Name: slidge
|
3
|
-
Version: 0.2.
|
3
|
+
Version: 0.2.6
|
4
4
|
Summary: XMPP bridging framework
|
5
5
|
Author-email: Nicolas Cedilnik <nicoco@nicoco.fr>
|
6
6
|
License: GNU AFFERO GENERAL PUBLIC LICENSE
|
@@ -669,22 +669,22 @@ Project-URL: Homepage, https://codeberg.org/slidge/
|
|
669
669
|
Project-URL: Issues, https://codeberg.org/slidge/slidge/issues
|
670
670
|
Project-URL: Repository, https://codeberg.org/slidge/slidge/
|
671
671
|
Project-URL: Chat room, https://conference.nicoco.fr:5281/muc_log/slidge/
|
672
|
-
Project-URL: Documentation, https://slidge.
|
672
|
+
Project-URL: Documentation, https://slidge.im/docs/slidge/main
|
673
|
+
Project-URL: changelog, https://codeberg.org/slidge/slidge/releases
|
673
674
|
Keywords: xmpp,gateway,bridge,instant messaging
|
674
675
|
Classifier: Topic :: Internet :: XMPP
|
675
676
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
676
677
|
Requires-Python: >=3.11
|
677
678
|
Description-Content-Type: text/markdown
|
678
|
-
Requires-Dist: aiohttp[speedups]
|
679
|
-
Requires-Dist: alembic
|
680
|
-
Requires-Dist: configargparse
|
679
|
+
Requires-Dist: aiohttp[speedups]<4,>=3.11.11
|
680
|
+
Requires-Dist: alembic<2,>=1.14.0
|
681
|
+
Requires-Dist: configargparse<2,>=1.7
|
681
682
|
Requires-Dist: defusedxml>=0.7.1
|
682
|
-
Requires-Dist:
|
683
|
-
Requires-Dist:
|
684
|
-
Requires-Dist:
|
685
|
-
Requires-Dist:
|
686
|
-
Requires-Dist:
|
687
|
-
Requires-Dist: sqlalchemy>=2.0.36
|
683
|
+
Requires-Dist: pillow<12,>=11.0.0
|
684
|
+
Requires-Dist: python-magic<0.5,>=0.4.27
|
685
|
+
Requires-Dist: qrcode<9,>=8.0
|
686
|
+
Requires-Dist: slixmpp<2,>=1.8.6
|
687
|
+
Requires-Dist: sqlalchemy<3,>=2
|
688
688
|
Requires-Dist: thumbhash>=0.1.2
|
689
689
|
|
690
690
|
![Slidge logo](https://codeberg.org/slidge/slidge/raw/branch/main/dev/assets/slidge-color-small.png)
|
@@ -693,14 +693,14 @@ Requires-Dist: thumbhash>=0.1.2
|
|
693
693
|
|
694
694
|
|
695
695
|
[![woodpecker CI status](https://ci.codeberg.org/api/badges/14027/status.svg)](https://ci.codeberg.org/repos/14027)
|
696
|
-
[![coverage](https://slidge.
|
696
|
+
[![coverage](https://slidge.im/coverage/main/coverage.svg)](https://slidge.im/coverage/main)
|
697
697
|
|
698
698
|
[![pypi version](https://badge.fury.io/py/slidge.svg)](https://pypi.org/project/slidge/)
|
699
699
|
[![debian unstable version](https://badges.debian.net/badges/debian/unstable/python3-slidge/version.svg)](https://packages.debian.org/unstable/python3-slidge)
|
700
700
|
|
701
701
|
Slidge is an XMPP (puppeteer) gateway library in python.
|
702
702
|
It makes
|
703
|
-
[writing gateways to other chat networks](https://slidge.im/
|
703
|
+
[writing gateways to other chat networks](https://slidge.im/docs/slidge/main/dev/tutorial.html)
|
704
704
|
(*legacy modules*) as frictionless as possible.
|
705
705
|
It supports fancy IM features, such as
|
706
706
|
[(emoji) reactions](https://xmpp.org/extensions/xep-0444.html),
|
@@ -751,7 +751,7 @@ class Session(BaseSession):
|
|
751
751
|
self.legacy_client.send_message(text=text, destination=chat.legacy_id)
|
752
752
|
```
|
753
753
|
|
754
|
-
There's more in [the tutorial](https://slidge.
|
754
|
+
There's more in [the tutorial](https://slidge.im/docs/slidge/main/dev/tutorial.html)!
|
755
755
|
|
756
756
|
Installation
|
757
757
|
------------
|
@@ -766,7 +766,7 @@ bundle.
|
|
766
766
|
Slidge is available on
|
767
767
|
[codeberg](https://codeberg.org/slidge/-/packages) (python packages and containers)
|
768
768
|
and [pypi](https://pypi.org/project/slidge/).
|
769
|
-
Refer to [the docs](https://slidge.
|
769
|
+
Refer to [the docs](https://slidge.im/docs/slidge/main/admin/install.html) for details.
|
770
770
|
|
771
771
|
About privacy
|
772
772
|
-------------
|
@@ -1,25 +1,25 @@
|
|
1
1
|
slidge/__init__.py,sha256=S0tUjqpZlzsr8G4Y_1Xt-KCYB07qaknTB0OwHU8k29U,1587
|
2
2
|
slidge/__main__.py,sha256=ydjUklOoavS4YlGfjRX_8BQN2DaSbaXPMi47RkOgcFI,37
|
3
|
-
slidge/__version__.py,sha256=
|
3
|
+
slidge/__version__.py,sha256=LukT7r82VsYYhRUhnpDW_6t1xNzuYY3v380h-wU8KCg,165
|
4
4
|
slidge/main.py,sha256=vMJzhvUxbeuIXuHxXXs6lm_ShBjXiS9B5Li5Ge4vWPo,6238
|
5
5
|
slidge/migration.py,sha256=4BJmPIRB56_WIhRTqBFIIBXuvnhhBjjOMl4CE7jY6oc,1541
|
6
6
|
slidge/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
7
7
|
slidge/command/__init__.py,sha256=UYf1mjCYbZ5G7PIgaFTWSQRAzEJkQ6dTH8Fu_e_XnO0,613
|
8
|
-
slidge/command/adhoc.py,sha256
|
8
|
+
slidge/command/adhoc.py,sha256=FEkE5UapIP_yrmrzGn5yIHCubOZijoI_muu1AtCqGOM,10472
|
9
9
|
slidge/command/admin.py,sha256=TYrzgCIhjcTIwl1IUaFlUd3D98SPyao10gB20zo8b3Q,6187
|
10
|
-
slidge/command/base.py,sha256=
|
10
|
+
slidge/command/base.py,sha256=EDcEl5dJcooSmLarXI2fmBq6QtU7h-7MOM3DDsxXmTU,13447
|
11
11
|
slidge/command/categories.py,sha256=vF0KGDV9sEn8TNkcMoDRw-u3gEyNHSXghOU2JRHQtKs,351
|
12
12
|
slidge/command/chat_command.py,sha256=z-4qp03rK7kCh3_kEozDViwkDg_hVjHvRCiYYJxedBQ,11153
|
13
13
|
slidge/command/register.py,sha256=BduDI31Kx8CbWWEdjybimTA5Wcfhn-Jkt8sSPsySCpo,6724
|
14
|
-
slidge/command/user.py,sha256=
|
14
|
+
slidge/command/user.py,sha256=fLh5d7XTSXicj6g0I80F5n6BFaA20PaYoXFkfDOR4zA,12303
|
15
15
|
slidge/contact/__init__.py,sha256=WMMaHk7UW7YT9EH2LtPdkU0bHQaOp4ikBhbBQskmoc8,191
|
16
|
-
slidge/contact/contact.py,sha256=
|
16
|
+
slidge/contact/contact.py,sha256=ITcjW_3VMdVEHQfqxscpIULnofZBJ841wvnYwOvk1Tc,22934
|
17
17
|
slidge/contact/roster.py,sha256=x3speGdHbZ-VTLoQLQW4s53rBeBvW87W8ZibCCZSLDA,10300
|
18
18
|
slidge/core/__init__.py,sha256=RG7Jj5JCJERjhqJ31lOLYV-7bH_oblClQD1KF9LsTXo,68
|
19
|
-
slidge/core/config.py,sha256=
|
20
|
-
slidge/core/gateway.py,sha256=
|
21
|
-
slidge/core/pubsub.py,sha256=
|
22
|
-
slidge/core/session.py,sha256=
|
19
|
+
slidge/core/config.py,sha256=OjJfpXJaDhMxRB-vYA0cqkSf0fwMt-HMThM8GS1htCg,7964
|
20
|
+
slidge/core/gateway.py,sha256=qdvLGh5GF823mACVw5j1WLYMzsSmeGpmQFuWvz5Z9AU,37028
|
21
|
+
slidge/core/pubsub.py,sha256=BoeYE__ptmRAn4x55Hn_6JWRA4nM-XJgDemG5Cy5kN4,11959
|
22
|
+
slidge/core/session.py,sha256=Y6psOKm_lv4q7yXARiLuijvSebuS64NjqSNF1WARtHM,28439
|
23
23
|
slidge/core/dispatcher/__init__.py,sha256=1EXcjXietUKlxEqdrCWCV3xZ3q_DSsjHoqWrPMbtYao,84
|
24
24
|
slidge/core/dispatcher/caps.py,sha256=vzCAXo_bhALuLEpJWtyJTzVfWx96g1AsWD8_wkoDl0Y,2028
|
25
25
|
slidge/core/dispatcher/disco.py,sha256=j56VY9NIFzwPEWFKQQZ7YIqS9GdD-ZaF_K8a2L-JvRk,2006
|
@@ -40,26 +40,26 @@ slidge/core/dispatcher/muc/misc.py,sha256=bHBjMC-Pu3jR5hAPGMzXf-C05UbACIwg38YbJU
|
|
40
40
|
slidge/core/dispatcher/muc/owner.py,sha256=1a6YV7b_mmi1jC6q1ko8weeL8imQA-s-hYGPLIHd10I,3308
|
41
41
|
slidge/core/dispatcher/muc/ping.py,sha256=lb1VQPhiUPZ19KhbofRXMVCcY6wwQ2w-asnqtANaAwA,1660
|
42
42
|
slidge/core/mixins/__init__.py,sha256=muReAzgvENgMvlfm0Fpe6BQFfm2EMjoDe9ZhGgo6Vig,627
|
43
|
-
slidge/core/mixins/attachment.py,sha256=
|
43
|
+
slidge/core/mixins/attachment.py,sha256=rZcipXiGgo-8klumBvfgrSsU_rmS6cl1zx-zev_FZRg,20174
|
44
44
|
slidge/core/mixins/avatar.py,sha256=BVOeH5j5tsPJ0IHUcB5ozpyh02TBPKiZMd9MYizDokA,8136
|
45
45
|
slidge/core/mixins/base.py,sha256=MOd-pas38_52VawQVlxWtBtmTKC6My9G0ZaCeQxOJbs,748
|
46
46
|
slidge/core/mixins/db.py,sha256=5Qpegd7D8e5TLXLLINYcf_DuVdN-7wNmsfztUuFYPcU,442
|
47
47
|
slidge/core/mixins/disco.py,sha256=jk3Z1B6zTuisHv8VKNRJodIo0ee5btYHh2ZrlflPj_Q,3670
|
48
48
|
slidge/core/mixins/lock.py,sha256=Vf1rrkbyNbSprr38WGfZiMgTB7AdbqH8ppFHY8N2yXE,975
|
49
|
-
slidge/core/mixins/message.py,sha256=
|
49
|
+
slidge/core/mixins/message.py,sha256=X8Ka8j0nOnBcecYE_YuK8_J7MeO5-R0TIZw4X8c7R3Y,7846
|
50
50
|
slidge/core/mixins/message_maker.py,sha256=TcCutHi0sIwL6beJNkN7XyR0aDIbA0xZyxd2Gc9ulG4,6022
|
51
51
|
slidge/core/mixins/message_text.py,sha256=pCY4tezEuwB2ZuUyUi72i4v9AJkxp_SWF1jrFsn94Ns,8096
|
52
52
|
slidge/core/mixins/presence.py,sha256=yywo6KAw8C7GaZSMrSMuioNfhW08MrnobHt8XbHd0q8,7891
|
53
53
|
slidge/core/mixins/recipient.py,sha256=b0uFnpym-hOFgYxGjXT1xQcZ4YRbDSBftPcNWLzSwEI,1336
|
54
54
|
slidge/db/__init__.py,sha256=EBDH1JSEhgqYcli2Bw11CRC749wJk8AOucgBzmhDSvU,105
|
55
|
-
slidge/db/avatar.py,sha256=
|
55
|
+
slidge/db/avatar.py,sha256=z5e72STv8PdN6zkNyKlLqF7NFxHwCa6IjwgFpzu5ghE,8033
|
56
56
|
slidge/db/meta.py,sha256=v1Jf-npZ28QwdGpsLQWLBHEbEP3-jnPrygRg05tJ_Iw,1831
|
57
|
-
slidge/db/models.py,sha256=
|
58
|
-
slidge/db/store.py,sha256=
|
57
|
+
slidge/db/models.py,sha256=MSVNW04x05qfxahvjCYRDFjfFP-XXp-lOHnK5IqFCXw,14046
|
58
|
+
slidge/db/store.py,sha256=ETk6oUUEz8-YUFGCh7nspSJH7dFOj8qeIWLQhX4nOH8,47051
|
59
59
|
slidge/db/alembic/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
60
60
|
slidge/db/alembic/env.py,sha256=hsBlRNs0zF5diSHGRSa8Fi3qRVQDA2rJdR41AEIdvxc,1642
|
61
|
-
slidge/db/alembic/old_user_store.py,sha256=zFOv0JEWQQK0_TMRlU4Z0G5Mc9pxvEErLyOzXmRAe5Q,5209
|
62
61
|
slidge/db/alembic/script.py.mako,sha256=MEqL-2qATlST9TAOeYgscMn1uy6HUS9NFvDgl93dMj8,635
|
62
|
+
slidge/db/alembic/versions/04cf35e3cf85_add_participant_nickname_no_illegal.py,sha256=Dwz_azOXr7Tsw7Wnj0L8mknITIPXO9ewEsRn169EUNA,904
|
63
63
|
slidge/db/alembic/versions/09f27f098baa_add_missing_attributes_in_room.py,sha256=mUL-0Io6ZPd_QbnKfwGYyjdMcM2uxQ0Wg72H23-2t_E,1033
|
64
64
|
slidge/db/alembic/versions/15b0bd83407a_remove_bogus_unique_constraints_on_room_.py,sha256=kzHuHGhzey5CY0p_OsKf5a-3zSk2649wqg2ToLiSD1I,2927
|
65
65
|
slidge/db/alembic/versions/2461390c0af2_store_contacts_caps_verstring_in_db.py,sha256=CLB-kOP9Rc0FJIKDLef912L5sYkjpTIPC8fhrIdrC7k,1084
|
@@ -71,7 +71,7 @@ slidge/db/alembic/versions/5bd48bfdffa2_lift_room_legacy_id_constraint.py,sha256
|
|
71
71
|
slidge/db/alembic/versions/82a4af84b679_add_muc_history_filled.py,sha256=g37po0ydp8ZmzJrE5oFV7GscnploxjCtPDpw28SqVGk,1429
|
72
72
|
slidge/db/alembic/versions/8b993243a536_add_vcard_content_to_contact_table.py,sha256=18tG8B03Kq8Qz_-mMd28Beed6jow8XNTtrz7gT5QY3g,1210
|
73
73
|
slidge/db/alembic/versions/8d2ced764698_rely_on_db_to_store_contacts_rooms_and_.py,sha256=ikoAlRV3_BJcDcFRANF-9HTB--0xpY0C5XdGuMuW9c0,4866
|
74
|
-
slidge/db/alembic/versions/aa9d82a7f6ef_db_creation.py,sha256=
|
74
|
+
slidge/db/alembic/versions/aa9d82a7f6ef_db_creation.py,sha256=eFlfn-LiDph05yyCc8gmtJwVKzgrSHwyWJ6nuVfEpQA,1391
|
75
75
|
slidge/db/alembic/versions/abba1ae0edb3_store_avatar_legacy_id_in_the_contact_.py,sha256=VprqEVHipYuM-ea-CIM4_ubOD5zJ9inLTbhXc869n3A,2779
|
76
76
|
slidge/db/alembic/versions/b33993e87db3_move_everything_to_persistent_db.py,sha256=2tiRxoC9PYOQn6XQrwK0JTEsb45Pzp2PsKoZSS4rcIA,7564
|
77
77
|
slidge/db/alembic/versions/b64b1a793483_add_source_and_legacy_id_for_archived_.py,sha256=r2sOgR5HcfueJyc3cWNDRmlZzdHOSX6nl2gef54wDbk,1559
|
@@ -80,9 +80,9 @@ slidge/db/alembic/versions/e91195719c2c_store_users_avatars_persistently.py,sha2
|
|
80
80
|
slidge/group/__init__.py,sha256=yFt7cHqeaKIMN6f9ZyhhspOcJJvBtLedGv-iICG7lto,258
|
81
81
|
slidge/group/archive.py,sha256=IPqklzo0UN3lPHckfsKW9c4nl3m_9XGY4u0eehrhe8k,5281
|
82
82
|
slidge/group/bookmarks.py,sha256=AvFL34bEX6n3OP1Np309T5hrLK9GnjkjdyLJ3uiLZyc,6616
|
83
|
-
slidge/group/participant.py,sha256=
|
84
|
-
slidge/group/room.py,sha256=
|
85
|
-
slidge/slixfix/__init__.py,sha256=
|
83
|
+
slidge/group/participant.py,sha256=cUuyJRGq8AIHrwtubje5cyb5hHY2hGLtGboBju4SI0c,17781
|
84
|
+
slidge/group/room.py,sha256=Ar7mSSZRqjDPaXtRuq1da-ZqdJ-fibVtEZDcfpZtpXY,46856
|
85
|
+
slidge/slixfix/__init__.py,sha256=Og6_EAuWst6paWmDiGqeqQH6Iof6V8Vkr5pyYisgjsw,4282
|
86
86
|
slidge/slixfix/delivery_receipt.py,sha256=3bWdZH3-X3CZJXmnI_TpjkTUUK-EY4Ktm78lW0-40fc,1366
|
87
87
|
slidge/slixfix/roster.py,sha256=KvDjh9q7pqaZf69H93okfib13cc95uVZUJ6rzpqmDaU,1704
|
88
88
|
slidge/slixfix/link_preview/__init__.py,sha256=TDPTSEH5FQxgGpQpQIde-D72AHg-6YVWG-tOj4KpKmU,290
|
@@ -92,27 +92,23 @@ slidge/slixfix/xep_0077/__init__.py,sha256=0lY1YXdgAsfrfxI_Woxaf1etHCJXe35Xtntq_
|
|
92
92
|
slidge/slixfix/xep_0077/register.py,sha256=6nwTfHNL7Z9-1wUhpAF743TNbjQLCMP7Rflkdad8d60,10431
|
93
93
|
slidge/slixfix/xep_0077/stanza.py,sha256=Lngly7F1ChCkNKn7yl1QmN838fO-KqkAhkazxzDsz80,2410
|
94
94
|
slidge/slixfix/xep_0100/__init__.py,sha256=AtEXDQOrEWodkN3fgKR0W3Ezsz_Zza6cgO5ZaZS-JOo,107
|
95
|
-
slidge/slixfix/xep_0100/gateway.py,sha256=
|
95
|
+
slidge/slixfix/xep_0100/gateway.py,sha256=aSR18605MWVqgucNd_zd1SdYlXry7k5uoqDzsvVqQyM,4363
|
96
96
|
slidge/slixfix/xep_0100/stanza.py,sha256=7vCzej9VFQupsTpGGl0cJWuGNH4I6oVcckBu_-fE55c,232
|
97
97
|
slidge/slixfix/xep_0153/__init__.py,sha256=hsEldnLuzvcp0NqSscxPV7FJl-6GFP372vlDg1G3S3I,283
|
98
98
|
slidge/slixfix/xep_0153/vcard_avatar.py,sha256=py-qzj1jmmzsM4GCTKLRW7cAdAmSVjodp6q0r5B0RqQ,458
|
99
99
|
slidge/slixfix/xep_0292/__init__.py,sha256=_MvS9wGra6ig3P_dPAVlCPDJkiOFvUWGjaRsHj1woUg,98
|
100
100
|
slidge/slixfix/xep_0292/vcard4.py,sha256=jL-TOW3eG2QXLduSLNq03L8HoUNmvy8kTZI5ojvo6GE,358
|
101
|
-
slidge/slixfix/xep_0356_old/__init__.py,sha256=3jGWJX2m5gWgDCxcVqCsCCVPRTcfmU96yenwvAJtOKE,180
|
102
|
-
slidge/slixfix/xep_0356_old/privilege.py,sha256=kcJzFbzhOHtQMtzOJpvvwm1pghSpealWnqhC0zc8dGo,5338
|
103
|
-
slidge/slixfix/xep_0356_old/stanza.py,sha256=i7aqcaTg6PBhVwbHToLtlrwxBj7uO-M7VrYSyElyEKI,1229
|
104
101
|
slidge/slixfix/xep_0492/__init__.py,sha256=kjWVeX3SG_2ohHx0fuMh1gmM2G57Bl6SRo7Mfv6sglA,161
|
105
102
|
slidge/slixfix/xep_0492/notify.py,sha256=8EPSdU3rTzWkHNm8oFr0tK2PmMJ6hBAIr88GoOmHTuQ,340
|
106
103
|
slidge/slixfix/xep_0492/stanza.py,sha256=gL0ixpV07Q9eqTXIOjdLVPtim45izuwaLk0iCoZ8e7c,2649
|
107
104
|
slidge/util/__init__.py,sha256=BELovoTMPcPPGz3D48esBr8A4BRRHXTvavfgnArBgEc,301
|
108
105
|
slidge/util/archive_msg.py,sha256=xXAR0BI5r3d6KKWjae9594izCOv6iI03z2WLuTecNw8,1724
|
109
106
|
slidge/util/conf.py,sha256=1j2OnOsCBar1tOObErhXR5RC3Vl3faliOZ1U8J3My58,6613
|
110
|
-
slidge/util/db.py,sha256=4LxZj8oBYgiSnyBUnF_ALjr0TblkfNQq_p28sCfkHMY,242
|
111
107
|
slidge/util/test.py,sha256=l1VHBsw5Uzk2t7wtkfb9kWvtehcYhw1t_d567JAJFKA,14135
|
112
108
|
slidge/util/types.py,sha256=R_xfS5mRL0XUJIoDpnaAkZlTOoLPerduXBFftaVwIAI,5489
|
113
|
-
slidge/util/util.py,sha256=
|
114
|
-
slidge-0.2.
|
115
|
-
slidge-0.2.
|
116
|
-
slidge-0.2.
|
117
|
-
slidge-0.2.
|
118
|
-
slidge-0.2.
|
109
|
+
slidge/util/util.py,sha256=4GdRZwHYUPkteEi8oux--qLwMiXnVLl_ojMDaKbEQCE,9629
|
110
|
+
slidge-0.2.6.dist-info/METADATA,sha256=ShWfovgJtP9yPJYaEZqHg0Rd62blhfP78go2AsRfeoM,44841
|
111
|
+
slidge-0.2.6.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
112
|
+
slidge-0.2.6.dist-info/entry_points.txt,sha256=py3_x834fFJ2TEzPd18Wt2DnysdAfuVqJ5zzBrXbAZs,44
|
113
|
+
slidge-0.2.6.dist-info/top_level.txt,sha256=2LRjDYHaGZ5ieCMF8xy58JIiabRMzX-MGMbCZwfE17c,7
|
114
|
+
slidge-0.2.6.dist-info/RECORD,,
|
@@ -1,183 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
This module covers a backend for storing user data persistently and managing a
|
3
|
-
pseudo-roster for the gateway component.
|
4
|
-
"""
|
5
|
-
|
6
|
-
import dataclasses
|
7
|
-
import datetime
|
8
|
-
import logging
|
9
|
-
import os.path
|
10
|
-
import shelve
|
11
|
-
from io import BytesIO
|
12
|
-
from os import PathLike
|
13
|
-
from typing import Iterable, Optional, Union
|
14
|
-
|
15
|
-
from pickle_secure import Pickler, Unpickler
|
16
|
-
from slixmpp import JID, Iq, Message, Presence
|
17
|
-
|
18
|
-
|
19
|
-
# noinspection PyUnresolvedReferences
|
20
|
-
class EncryptedShelf(shelve.DbfilenameShelf):
|
21
|
-
cache: dict
|
22
|
-
dict: dict
|
23
|
-
writeback: bool
|
24
|
-
keyencoding: str
|
25
|
-
_protocol: int
|
26
|
-
|
27
|
-
def __init__(
|
28
|
-
self, filename: PathLike, key: str, flag="c", protocol=None, writeback=False
|
29
|
-
):
|
30
|
-
super().__init__(str(filename), flag, protocol, writeback)
|
31
|
-
self.secret_key = key
|
32
|
-
|
33
|
-
def __getitem__(self, key):
|
34
|
-
try:
|
35
|
-
value = self.cache[key]
|
36
|
-
except KeyError:
|
37
|
-
f = BytesIO(self.dict[key.encode(self.keyencoding)])
|
38
|
-
value = Unpickler(f, key=self.secret_key).load() # type:ignore
|
39
|
-
if self.writeback:
|
40
|
-
self.cache[key] = value
|
41
|
-
return value
|
42
|
-
|
43
|
-
def __setitem__(self, key, value):
|
44
|
-
if self.writeback:
|
45
|
-
self.cache[key] = value
|
46
|
-
f = BytesIO()
|
47
|
-
p = Pickler(f, self._protocol, key=self.secret_key) # type:ignore
|
48
|
-
p.dump(value)
|
49
|
-
self.dict[key.encode(self.keyencoding)] = f.getvalue()
|
50
|
-
|
51
|
-
|
52
|
-
@dataclasses.dataclass
|
53
|
-
class GatewayUser:
|
54
|
-
"""
|
55
|
-
A gateway user
|
56
|
-
"""
|
57
|
-
|
58
|
-
bare_jid: str
|
59
|
-
"""Bare JID of the user"""
|
60
|
-
registration_form: dict[str, Optional[str]]
|
61
|
-
"""Content of the registration form, as a dict"""
|
62
|
-
plugin_data: Optional[dict] = None
|
63
|
-
registration_date: Optional[datetime.datetime] = None
|
64
|
-
|
65
|
-
def __hash__(self):
|
66
|
-
return hash(self.bare_jid)
|
67
|
-
|
68
|
-
def __repr__(self):
|
69
|
-
return f"<User {self.bare_jid}>"
|
70
|
-
|
71
|
-
def __post_init__(self):
|
72
|
-
if self.registration_date is None:
|
73
|
-
self.registration_date = datetime.datetime.now()
|
74
|
-
|
75
|
-
@property
|
76
|
-
def jid(self) -> JID:
|
77
|
-
"""
|
78
|
-
The user's (bare) JID
|
79
|
-
|
80
|
-
:return:
|
81
|
-
"""
|
82
|
-
return JID(self.bare_jid)
|
83
|
-
|
84
|
-
def get(self, field: str, default: str = "") -> Optional[str]:
|
85
|
-
# """
|
86
|
-
# Get fields from the registration form (required to comply with slixmpp backend protocol)
|
87
|
-
#
|
88
|
-
# :param field: Name of the field
|
89
|
-
# :param default: Default value to return if the field is not present
|
90
|
-
#
|
91
|
-
# :return: Value of the field
|
92
|
-
# """
|
93
|
-
return self.registration_form.get(field, default)
|
94
|
-
|
95
|
-
|
96
|
-
class UserStore:
|
97
|
-
"""
|
98
|
-
Basic user store implementation using shelve from the python standard library
|
99
|
-
|
100
|
-
Set_file must be called before it is usable
|
101
|
-
"""
|
102
|
-
|
103
|
-
def __init__(self):
|
104
|
-
self._users: shelve.Shelf[GatewayUser] = None # type: ignore
|
105
|
-
|
106
|
-
def set_file(self, filename: PathLike, secret_key: Optional[str] = None):
|
107
|
-
"""
|
108
|
-
Set the file to use to store user data
|
109
|
-
|
110
|
-
:param filename: Path to the shelf file
|
111
|
-
:param secret_key: Secret key to store files encrypted on disk
|
112
|
-
"""
|
113
|
-
if self._users is not None:
|
114
|
-
raise RuntimeError("Shelf file already set!")
|
115
|
-
if os.path.exists(filename):
|
116
|
-
log.info("Using existing slidge DB: %s", filename)
|
117
|
-
else:
|
118
|
-
log.info("Creating a new slidge DB: %s", filename)
|
119
|
-
if secret_key:
|
120
|
-
self._users = EncryptedShelf(filename, key=secret_key)
|
121
|
-
else:
|
122
|
-
self._users = shelve.open(str(filename))
|
123
|
-
log.info("Registered users in the DB: %s", list(self._users.keys()))
|
124
|
-
|
125
|
-
def get_all(self) -> Iterable[GatewayUser]:
|
126
|
-
"""
|
127
|
-
Get all users in the store
|
128
|
-
|
129
|
-
:return: An iterable of GatewayUsers
|
130
|
-
"""
|
131
|
-
return self._users.values()
|
132
|
-
|
133
|
-
def commit(self, user: GatewayUser):
|
134
|
-
self._users[user.jid.bare] = user
|
135
|
-
self._users.sync()
|
136
|
-
|
137
|
-
def get(self, _gateway_jid, _node, ifrom: JID, iq) -> Optional[GatewayUser]:
|
138
|
-
"""
|
139
|
-
Get a user from the store
|
140
|
-
|
141
|
-
NB: there is no reason to call this, it is used by SliXMPP internal API
|
142
|
-
|
143
|
-
:param _gateway_jid:
|
144
|
-
:param _node:
|
145
|
-
:param ifrom:
|
146
|
-
:param iq:
|
147
|
-
:return:
|
148
|
-
"""
|
149
|
-
if ifrom is None: # bug in SliXMPP's XEP_0100 plugin
|
150
|
-
ifrom = iq["from"]
|
151
|
-
log.debug("Getting user %s", ifrom.bare)
|
152
|
-
return self._users.get(ifrom.bare)
|
153
|
-
|
154
|
-
def get_by_jid(self, jid: JID) -> Optional[GatewayUser]:
|
155
|
-
"""
|
156
|
-
Convenience function to get a user from their JID.
|
157
|
-
|
158
|
-
:param jid: JID of the gateway user
|
159
|
-
:return:
|
160
|
-
"""
|
161
|
-
return self._users.get(jid.bare)
|
162
|
-
|
163
|
-
def get_by_stanza(self, s: Union[Presence, Message, Iq]) -> Optional[GatewayUser]:
|
164
|
-
"""
|
165
|
-
Convenience function to get a user from a stanza they sent.
|
166
|
-
|
167
|
-
:param s: A stanza sent by the gateway user
|
168
|
-
:return:
|
169
|
-
"""
|
170
|
-
return self.get_by_jid(s.get_from())
|
171
|
-
|
172
|
-
def close(self):
|
173
|
-
self._users.sync()
|
174
|
-
self._users.close()
|
175
|
-
|
176
|
-
|
177
|
-
user_store = UserStore()
|
178
|
-
"""
|
179
|
-
A persistent store for slidge users. Not public, but I didn't find how to hide
|
180
|
-
it from the docs!
|
181
|
-
"""
|
182
|
-
|
183
|
-
log = logging.getLogger(__name__)
|
@@ -1,167 +0,0 @@
|
|
1
|
-
import logging
|
2
|
-
import typing
|
3
|
-
from collections import defaultdict
|
4
|
-
|
5
|
-
from slixmpp import JID, Iq, Message
|
6
|
-
from slixmpp.plugins.base import BasePlugin
|
7
|
-
from slixmpp.plugins.xep_0356.permissions import (
|
8
|
-
MessagePermission,
|
9
|
-
Permissions,
|
10
|
-
RosterAccess,
|
11
|
-
)
|
12
|
-
from slixmpp.types import JidStr
|
13
|
-
from slixmpp.xmlstream import StanzaBase
|
14
|
-
from slixmpp.xmlstream.handler import Callback
|
15
|
-
from slixmpp.xmlstream.matcher import StanzaPath
|
16
|
-
|
17
|
-
from . import stanza
|
18
|
-
|
19
|
-
log = logging.getLogger(__name__)
|
20
|
-
|
21
|
-
|
22
|
-
# noinspection PyPep8Naming
|
23
|
-
class XEP_0356_OLD(BasePlugin):
|
24
|
-
"""
|
25
|
-
XEP-0356: Privileged Entity
|
26
|
-
|
27
|
-
Events:
|
28
|
-
|
29
|
-
::
|
30
|
-
|
31
|
-
privileges_advertised_old -- Received message/privilege from the server
|
32
|
-
"""
|
33
|
-
|
34
|
-
name = "xep_0356_old"
|
35
|
-
description = "XEP-0356: Privileged Entity (slidge - old namespace)"
|
36
|
-
dependencies = {"xep_0297"}
|
37
|
-
stanza = stanza
|
38
|
-
|
39
|
-
granted_privileges: defaultdict[JidStr, Permissions] = defaultdict(Permissions)
|
40
|
-
|
41
|
-
def plugin_init(self):
|
42
|
-
if not self.xmpp.is_component:
|
43
|
-
log.error("XEP 0356 is only available for components")
|
44
|
-
return
|
45
|
-
|
46
|
-
stanza.register()
|
47
|
-
|
48
|
-
self.xmpp.register_handler(
|
49
|
-
Callback(
|
50
|
-
"Privileges_old",
|
51
|
-
StanzaPath("message/privilege_old"),
|
52
|
-
self._handle_privilege,
|
53
|
-
)
|
54
|
-
)
|
55
|
-
|
56
|
-
def plugin_end(self):
|
57
|
-
self.xmpp.remove_handler("Privileges_old")
|
58
|
-
|
59
|
-
def _handle_privilege(self, msg: StanzaBase):
|
60
|
-
"""
|
61
|
-
Called when the XMPP server advertise the component's privileges.
|
62
|
-
|
63
|
-
Stores the privileges in this instance's granted_privileges attribute (a dict)
|
64
|
-
and raises the privileges_advertised event
|
65
|
-
"""
|
66
|
-
for perm in msg["privilege_old"]["perms"]:
|
67
|
-
setattr(
|
68
|
-
self.granted_privileges[msg.get_from()], perm["access"], perm["type"]
|
69
|
-
)
|
70
|
-
log.debug(f"Privileges (old): {self.granted_privileges}")
|
71
|
-
self.xmpp.event("privileges_advertised_old")
|
72
|
-
|
73
|
-
def send_privileged_message(self, msg: Message):
|
74
|
-
if (
|
75
|
-
self.granted_privileges[msg.get_from().domain].message
|
76
|
-
!= MessagePermission.OUTGOING
|
77
|
-
):
|
78
|
-
raise PermissionError(
|
79
|
-
"The server hasn't authorized us to send messages on behalf of other users"
|
80
|
-
)
|
81
|
-
else:
|
82
|
-
self._make_privileged_message(msg).send()
|
83
|
-
|
84
|
-
def _make_privileged_message(self, msg: Message):
|
85
|
-
server = msg.get_from().domain
|
86
|
-
wrapped = self.xmpp.make_message(mto=server, mfrom=self.xmpp.boundjid.bare)
|
87
|
-
wrapped["privilege_old"]["forwarded"].append(msg)
|
88
|
-
return wrapped
|
89
|
-
|
90
|
-
def _make_get_roster(self, jid: typing.Union[JID, str], **iq_kwargs):
|
91
|
-
return self.xmpp.make_iq_get(
|
92
|
-
queryxmlns="jabber:iq:roster",
|
93
|
-
ifrom=self.xmpp.boundjid.bare,
|
94
|
-
ito=jid,
|
95
|
-
**iq_kwargs,
|
96
|
-
)
|
97
|
-
|
98
|
-
def _make_set_roster(
|
99
|
-
self,
|
100
|
-
jid: typing.Union[JID, str],
|
101
|
-
roster_items: dict,
|
102
|
-
**iq_kwargs,
|
103
|
-
):
|
104
|
-
iq = self.xmpp.make_iq_set(
|
105
|
-
ifrom=self.xmpp.boundjid.bare,
|
106
|
-
ito=jid,
|
107
|
-
**iq_kwargs,
|
108
|
-
)
|
109
|
-
iq["roster"]["items"] = roster_items
|
110
|
-
return iq
|
111
|
-
|
112
|
-
async def get_roster(self, jid: typing.Union[JID, str], **send_kwargs) -> Iq:
|
113
|
-
"""
|
114
|
-
Return the roster of user on the server the component has privileged access to.
|
115
|
-
|
116
|
-
Raises ValueError if the server did not advertise the corresponding privileges
|
117
|
-
|
118
|
-
:param jid: user we want to fetch the roster from
|
119
|
-
"""
|
120
|
-
if isinstance(jid, str):
|
121
|
-
jid = JID(jid)
|
122
|
-
if self.granted_privileges[jid.domain].roster not in (
|
123
|
-
RosterAccess.GET,
|
124
|
-
RosterAccess.BOTH,
|
125
|
-
):
|
126
|
-
raise PermissionError(
|
127
|
-
"The server did not grant us privileges to get rosters"
|
128
|
-
)
|
129
|
-
else:
|
130
|
-
return await self._make_get_roster(jid).send(**send_kwargs)
|
131
|
-
|
132
|
-
async def set_roster(
|
133
|
-
self, jid: typing.Union[JID, str], roster_items: dict, **send_kwargs
|
134
|
-
) -> Iq:
|
135
|
-
"""
|
136
|
-
Return the roster of user on the server the component has privileged access to.
|
137
|
-
|
138
|
-
Raises ValueError if the server did not advertise the corresponding privileges
|
139
|
-
|
140
|
-
:param jid: user we want to add or modify roster items
|
141
|
-
:param roster_items: a dict containing the roster items' JIDs as keys and
|
142
|
-
nested dicts containing names, subscriptions and groups.
|
143
|
-
Example:
|
144
|
-
{
|
145
|
-
"friend1@example.com": {
|
146
|
-
"name": "Friend 1",
|
147
|
-
"subscription": "both",
|
148
|
-
"groups": ["group1", "group2"],
|
149
|
-
},
|
150
|
-
"friend2@example.com": {
|
151
|
-
"name": "Friend 2",
|
152
|
-
"subscription": "from",
|
153
|
-
"groups": ["group3"],
|
154
|
-
},
|
155
|
-
}
|
156
|
-
"""
|
157
|
-
if isinstance(jid, str):
|
158
|
-
jid = JID(jid)
|
159
|
-
if self.granted_privileges[jid.domain].roster not in (
|
160
|
-
RosterAccess.GET,
|
161
|
-
RosterAccess.BOTH,
|
162
|
-
):
|
163
|
-
raise PermissionError(
|
164
|
-
"The server did not grant us privileges to set rosters"
|
165
|
-
)
|
166
|
-
else:
|
167
|
-
return await self._make_set_roster(jid, roster_items).send(**send_kwargs)
|
@@ -1,44 +0,0 @@
|
|
1
|
-
from slixmpp.plugins.xep_0297 import Forwarded
|
2
|
-
from slixmpp.stanza import Message
|
3
|
-
from slixmpp.xmlstream import ElementBase, register_stanza_plugin
|
4
|
-
|
5
|
-
|
6
|
-
class PrivilegeOld(ElementBase):
|
7
|
-
namespace = "urn:xmpp:privilege:1"
|
8
|
-
name = "privilege"
|
9
|
-
plugin_attrib = "privilege_old"
|
10
|
-
|
11
|
-
def permission(self, access):
|
12
|
-
for perm in self["perms"]:
|
13
|
-
if perm["access"] == access:
|
14
|
-
return perm["type"]
|
15
|
-
|
16
|
-
def roster(self):
|
17
|
-
return self.permission("roster")
|
18
|
-
|
19
|
-
def message(self):
|
20
|
-
return self.permission("message")
|
21
|
-
|
22
|
-
def presence(self):
|
23
|
-
return self.permission("presence")
|
24
|
-
|
25
|
-
def add_perm(self, access, type):
|
26
|
-
# This should only be needed for servers, so maybe out of scope for slixmpp
|
27
|
-
perm = PermOld()
|
28
|
-
perm["type"] = type
|
29
|
-
perm["access"] = access
|
30
|
-
self.append(perm)
|
31
|
-
|
32
|
-
|
33
|
-
class PermOld(ElementBase):
|
34
|
-
namespace = "urn:xmpp:privilege:1"
|
35
|
-
name = "perm"
|
36
|
-
plugin_attrib = "perm"
|
37
|
-
plugin_multi_attrib = "perms"
|
38
|
-
interfaces = {"type", "access"}
|
39
|
-
|
40
|
-
|
41
|
-
def register():
|
42
|
-
register_stanza_plugin(Message, PrivilegeOld)
|
43
|
-
register_stanza_plugin(PrivilegeOld, Forwarded)
|
44
|
-
register_stanza_plugin(PrivilegeOld, PermOld, iterable=True)
|
slidge/util/db.py
DELETED
File without changes
|
File without changes
|
File without changes
|