slidge 0.2.0a9__py3-none-any.whl → 0.2.0b0__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/__main__.py +2 -3
- slidge/__version__.py +1 -1
- slidge/command/adhoc.py +1 -1
- slidge/command/base.py +4 -4
- slidge/command/user.py +5 -1
- slidge/contact/roster.py +9 -0
- slidge/core/config.py +0 -3
- slidge/core/dispatcher/__init__.py +3 -0
- slidge/core/{gateway → dispatcher}/caps.py +6 -4
- slidge/core/{gateway → dispatcher}/disco.py +11 -17
- slidge/core/dispatcher/message/__init__.py +10 -0
- slidge/core/dispatcher/message/chat_state.py +40 -0
- slidge/core/dispatcher/message/marker.py +62 -0
- slidge/core/dispatcher/message/message.py +397 -0
- slidge/core/dispatcher/muc/__init__.py +12 -0
- slidge/core/dispatcher/muc/admin.py +98 -0
- slidge/core/{gateway → dispatcher/muc}/mam.py +26 -15
- slidge/core/dispatcher/muc/misc.py +121 -0
- slidge/core/dispatcher/muc/owner.py +96 -0
- slidge/core/{gateway → dispatcher/muc}/ping.py +10 -15
- slidge/core/dispatcher/presence.py +177 -0
- slidge/core/{gateway → dispatcher}/registration.py +23 -2
- slidge/core/{gateway → dispatcher}/search.py +9 -14
- slidge/core/dispatcher/session_dispatcher.py +84 -0
- slidge/core/dispatcher/util.py +174 -0
- slidge/core/{gateway/vcard_temp.py → dispatcher/vcard.py} +26 -12
- slidge/core/{gateway/base.py → gateway.py} +42 -137
- slidge/core/mixins/attachment.py +24 -8
- slidge/core/mixins/base.py +2 -2
- slidge/core/mixins/lock.py +10 -8
- slidge/core/mixins/message.py +9 -203
- slidge/core/mixins/message_text.py +211 -0
- slidge/core/pubsub.py +2 -1
- slidge/core/session.py +28 -2
- slidge/db/alembic/versions/15b0bd83407a_remove_bogus_unique_constraints_on_room_.py +83 -0
- slidge/db/alembic/versions/45c24cc73c91_add_bob.py +42 -0
- slidge/db/alembic/versions/5bd48bfdffa2_lift_room_legacy_id_constraint.py +12 -1
- slidge/db/models.py +16 -1
- slidge/db/store.py +144 -11
- slidge/group/bookmarks.py +23 -1
- slidge/group/participant.py +5 -5
- slidge/group/room.py +10 -1
- slidge/{core/gateway → slixfix}/delivery_receipt.py +1 -1
- slidge/util/test.py +9 -5
- slidge/util/types.py +6 -0
- slidge/util/util.py +5 -2
- {slidge-0.2.0a9.dist-info → slidge-0.2.0b0.dist-info}/METADATA +2 -1
- {slidge-0.2.0a9.dist-info → slidge-0.2.0b0.dist-info}/RECORD +51 -40
- slidge-0.2.0b0.dist-info/entry_points.txt +3 -0
- slidge/core/gateway/__init__.py +0 -3
- slidge/core/gateway/muc_admin.py +0 -35
- slidge/core/gateway/presence.py +0 -95
- slidge/core/gateway/session_dispatcher.py +0 -895
- slidge-0.2.0a9.dist-info/entry_points.txt +0 -3
- {slidge-0.2.0a9.dist-info → slidge-0.2.0b0.dist-info}/LICENSE +0 -0
- {slidge-0.2.0a9.dist-info → slidge-0.2.0b0.dist-info}/WHEEL +0 -0
slidge/db/models.py
CHANGED
@@ -156,7 +156,9 @@ class Contact(Base):
|
|
156
156
|
|
157
157
|
is_friend: Mapped[bool] = mapped_column(default=False)
|
158
158
|
added_to_roster: Mapped[bool] = mapped_column(default=False)
|
159
|
-
sent_order: Mapped[list["ContactSent"]] = relationship(
|
159
|
+
sent_order: Mapped[list["ContactSent"]] = relationship(
|
160
|
+
back_populates="contact", cascade="all, delete-orphan"
|
161
|
+
)
|
160
162
|
|
161
163
|
extra_attributes: Mapped[Optional[JSONSerializable]] = mapped_column(
|
162
164
|
default=None, nullable=True
|
@@ -386,3 +388,16 @@ class Participant(Base):
|
|
386
388
|
)
|
387
389
|
|
388
390
|
extra_attributes: Mapped[Optional[JSONSerializable]] = mapped_column(default=None)
|
391
|
+
|
392
|
+
|
393
|
+
class Bob(Base):
|
394
|
+
__tablename__ = "bob"
|
395
|
+
|
396
|
+
id: Mapped[int] = mapped_column(primary_key=True)
|
397
|
+
file_name: Mapped[str] = mapped_column(nullable=False)
|
398
|
+
|
399
|
+
sha_1: Mapped[str] = mapped_column(nullable=False, unique=True)
|
400
|
+
sha_256: Mapped[str] = mapped_column(nullable=False, unique=True)
|
401
|
+
sha_512: Mapped[str] = mapped_column(nullable=False, unique=True)
|
402
|
+
|
403
|
+
content_type: Mapped[Optional[str]] = mapped_column(nullable=False)
|
slidge/db/store.py
CHANGED
@@ -1,27 +1,33 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
|
+
import hashlib
|
3
4
|
import json
|
4
5
|
import logging
|
6
|
+
import uuid
|
5
7
|
from contextlib import contextmanager
|
6
8
|
from datetime import datetime, timedelta, timezone
|
9
|
+
from mimetypes import guess_extension
|
7
10
|
from typing import TYPE_CHECKING, Collection, Iterator, Optional, Type
|
8
11
|
|
9
12
|
from slixmpp import JID, Iq, Message, Presence
|
10
13
|
from slixmpp.exceptions import XMPPError
|
14
|
+
from slixmpp.plugins.xep_0231.stanza import BitsOfBinary
|
11
15
|
from sqlalchemy import Engine, delete, select, update
|
12
|
-
from sqlalchemy.orm import Session, attributes
|
16
|
+
from sqlalchemy.orm import Session, attributes, load_only
|
13
17
|
from sqlalchemy.sql.functions import count
|
14
18
|
|
19
|
+
from ..core import config
|
15
20
|
from ..util.archive_msg import HistoryMessage
|
16
21
|
from ..util.types import URL, CachedPresence, ClientType
|
17
22
|
from ..util.types import Hat as HatTuple
|
18
|
-
from ..util.types import MamMetadata, MucAffiliation, MucRole
|
23
|
+
from ..util.types import MamMetadata, MucAffiliation, MucRole, Sticker
|
19
24
|
from .meta import Base
|
20
25
|
from .models import (
|
21
26
|
ArchivedMessage,
|
22
27
|
ArchivedMessageSource,
|
23
28
|
Attachment,
|
24
29
|
Avatar,
|
30
|
+
Bob,
|
25
31
|
Contact,
|
26
32
|
ContactSent,
|
27
33
|
GatewayUser,
|
@@ -87,6 +93,7 @@ class SlidgeStore(EngineMixin):
|
|
87
93
|
self.rooms = RoomStore(engine)
|
88
94
|
self.sent = SentStore(engine)
|
89
95
|
self.participants = ParticipantStore(engine)
|
96
|
+
self.bob = BobStore(engine)
|
90
97
|
|
91
98
|
|
92
99
|
class UserStore(EngineMixin):
|
@@ -244,15 +251,6 @@ class SentStore(EngineMixin):
|
|
244
251
|
.where(XmppToLegacyIds.type == XmppToLegacyEnum.THREAD)
|
245
252
|
).scalar()
|
246
253
|
|
247
|
-
def get_xmpp_thread(self, user_pk: int, legacy_id: str) -> Optional[str]:
|
248
|
-
with self.session() as session:
|
249
|
-
return session.execute(
|
250
|
-
select(XmppToLegacyIds.xmpp_id)
|
251
|
-
.where(XmppToLegacyIds.user_account_id == user_pk)
|
252
|
-
.where(XmppToLegacyIds.legacy_id == legacy_id)
|
253
|
-
.where(XmppToLegacyIds.type == XmppToLegacyEnum.THREAD)
|
254
|
-
).scalar()
|
255
|
-
|
256
254
|
def was_sent_by_user(self, user_pk: int, legacy_id: str) -> bool:
|
257
255
|
with self.session() as session:
|
258
256
|
return (
|
@@ -407,6 +405,16 @@ class ContactStore(UpdatedMixin):
|
|
407
405
|
|
408
406
|
def add_to_sent(self, contact_pk: int, msg_id: str) -> None:
|
409
407
|
with self.session() as session:
|
408
|
+
if (
|
409
|
+
session.query(ContactSent.id)
|
410
|
+
.where(ContactSent.contact_id == contact_pk)
|
411
|
+
.where(ContactSent.msg_id == msg_id)
|
412
|
+
.first()
|
413
|
+
) is not None:
|
414
|
+
log.warning(
|
415
|
+
"Contact %s has already sent message %s", contact_pk, msg_id
|
416
|
+
)
|
417
|
+
return
|
410
418
|
new = ContactSent(contact_id=contact_pk, msg_id=msg_id)
|
411
419
|
session.add(new)
|
412
420
|
session.commit()
|
@@ -497,6 +505,12 @@ class MAMStore(EngineMixin):
|
|
497
505
|
.where(ArchivedMessage.room_id == room_pk)
|
498
506
|
.where(ArchivedMessage.stanza_id == message.id)
|
499
507
|
).scalar()
|
508
|
+
if existing is None and legacy_msg_id is not None:
|
509
|
+
existing = session.execute(
|
510
|
+
select(ArchivedMessage)
|
511
|
+
.where(ArchivedMessage.room_id == room_pk)
|
512
|
+
.where(ArchivedMessage.legacy_id == legacy_msg_id)
|
513
|
+
).scalar()
|
500
514
|
if existing is not None:
|
501
515
|
log.debug("Updating message %s in room %s", message.id, room_pk)
|
502
516
|
existing.timestamp = message.when
|
@@ -973,6 +987,15 @@ class RoomStore(UpdatedMixin):
|
|
973
987
|
select(Room).where(Room.user_account_id == user_pk)
|
974
988
|
).scalars()
|
975
989
|
|
990
|
+
def get_all_jid_and_names(self, user_pk: int) -> Iterator[Room]:
|
991
|
+
with self.session() as session:
|
992
|
+
yield from session.scalars(
|
993
|
+
select(Room)
|
994
|
+
.filter(Room.user_account_id == user_pk)
|
995
|
+
.options(load_only(Room.jid, Room.name))
|
996
|
+
.order_by(Room.name)
|
997
|
+
).all()
|
998
|
+
|
976
999
|
|
977
1000
|
class ParticipantStore(EngineMixin):
|
978
1001
|
def __init__(self, *a, **kw):
|
@@ -1120,5 +1143,115 @@ class ParticipantStore(EngineMixin):
|
|
1120
1143
|
).scalar()
|
1121
1144
|
|
1122
1145
|
|
1146
|
+
class BobStore(EngineMixin):
|
1147
|
+
_ATTR_MAP = {
|
1148
|
+
"sha-1": "sha_1",
|
1149
|
+
"sha1": "sha_1",
|
1150
|
+
"sha-256": "sha_256",
|
1151
|
+
"sha256": "sha_256",
|
1152
|
+
"sha-512": "sha_512",
|
1153
|
+
"sha512": "sha_512",
|
1154
|
+
}
|
1155
|
+
|
1156
|
+
_ALG_MAP = {
|
1157
|
+
"sha_1": hashlib.sha1,
|
1158
|
+
"sha_256": hashlib.sha256,
|
1159
|
+
"sha_512": hashlib.sha512,
|
1160
|
+
}
|
1161
|
+
|
1162
|
+
def __init__(self, *a, **k):
|
1163
|
+
super().__init__(*a, **k)
|
1164
|
+
self.root_dir = config.HOME_DIR / "slidge_stickers"
|
1165
|
+
self.root_dir.mkdir(exist_ok=True)
|
1166
|
+
|
1167
|
+
@staticmethod
|
1168
|
+
def __split_cid(cid: str) -> list[str]:
|
1169
|
+
return cid.removesuffix("@bob.xmpp.org").split("+")
|
1170
|
+
|
1171
|
+
def __get_condition(self, cid: str):
|
1172
|
+
alg_name, digest = self.__split_cid(cid)
|
1173
|
+
attr = self._ATTR_MAP.get(alg_name)
|
1174
|
+
if attr is None:
|
1175
|
+
log.warning("Unknown hash algo: %s", alg_name)
|
1176
|
+
return None
|
1177
|
+
return getattr(Bob, attr) == digest
|
1178
|
+
|
1179
|
+
def get(self, cid: str) -> Bob | None:
|
1180
|
+
with self.session() as session:
|
1181
|
+
try:
|
1182
|
+
return session.query(Bob).filter(self.__get_condition(cid)).scalar()
|
1183
|
+
except ValueError:
|
1184
|
+
log.warning("Cannot get Bob with CID: %s", cid)
|
1185
|
+
return None
|
1186
|
+
|
1187
|
+
def get_sticker(self, cid: str) -> Sticker | None:
|
1188
|
+
bob = self.get(cid)
|
1189
|
+
if bob is None:
|
1190
|
+
return None
|
1191
|
+
return Sticker(
|
1192
|
+
self.root_dir / bob.file_name,
|
1193
|
+
bob.content_type,
|
1194
|
+
{h: getattr(bob, h) for h in self._ALG_MAP},
|
1195
|
+
)
|
1196
|
+
|
1197
|
+
def get_bob(self, _jid, _node, _ifrom, cid: str) -> BitsOfBinary | None:
|
1198
|
+
stored = self.get(cid)
|
1199
|
+
if stored is None:
|
1200
|
+
return None
|
1201
|
+
bob = BitsOfBinary()
|
1202
|
+
bob["data"] = (self.root_dir / stored.file_name).read_bytes()
|
1203
|
+
if stored.content_type is not None:
|
1204
|
+
bob["type"] = stored.content_type
|
1205
|
+
bob["cid"] = cid
|
1206
|
+
return bob
|
1207
|
+
|
1208
|
+
def del_bob(self, _jid, _node, _ifrom, cid: str) -> None:
|
1209
|
+
with self.session() as orm:
|
1210
|
+
try:
|
1211
|
+
file_name = orm.scalar(
|
1212
|
+
delete(Bob)
|
1213
|
+
.where(self.__get_condition(cid))
|
1214
|
+
.returning(Bob.file_name)
|
1215
|
+
)
|
1216
|
+
except ValueError:
|
1217
|
+
log.warning("Cannot delete Bob with CID: %s", cid)
|
1218
|
+
return None
|
1219
|
+
if file_name is None:
|
1220
|
+
log.warning("No BoB with CID: %s", cid)
|
1221
|
+
return None
|
1222
|
+
(self.root_dir / file_name).unlink()
|
1223
|
+
orm.commit()
|
1224
|
+
|
1225
|
+
def set_bob(self, _jid, _node, _ifrom, bob: BitsOfBinary) -> None:
|
1226
|
+
cid = bob["cid"]
|
1227
|
+
try:
|
1228
|
+
alg_name, digest = self.__split_cid(cid)
|
1229
|
+
except ValueError:
|
1230
|
+
log.warning("Cannot set Bob with CID: %s", cid)
|
1231
|
+
return
|
1232
|
+
attr = self._ATTR_MAP.get(alg_name)
|
1233
|
+
if attr is None:
|
1234
|
+
log.warning("Cannot set BoB with unknown hash algo: %s", alg_name)
|
1235
|
+
return None
|
1236
|
+
with self.session() as orm:
|
1237
|
+
existing = self.get(bob["cid"])
|
1238
|
+
if existing is not None:
|
1239
|
+
log.debug("Bob already known")
|
1240
|
+
return
|
1241
|
+
bytes_ = bob["data"]
|
1242
|
+
path = self.root_dir / uuid.uuid4().hex
|
1243
|
+
if bob["type"]:
|
1244
|
+
path = path.with_suffix(guess_extension(bob["type"]) or "")
|
1245
|
+
path.write_bytes(bytes_)
|
1246
|
+
hashes = {k: v(bytes_).hexdigest() for k, v in self._ALG_MAP.items()}
|
1247
|
+
if hashes[attr] != digest:
|
1248
|
+
raise ValueError(
|
1249
|
+
"The given CID does not correspond to the result of our hash"
|
1250
|
+
)
|
1251
|
+
row = Bob(file_name=path.name, content_type=bob["type"] or None, **hashes)
|
1252
|
+
orm.add(row)
|
1253
|
+
orm.commit()
|
1254
|
+
|
1255
|
+
|
1123
1256
|
log = logging.getLogger(__name__)
|
1124
1257
|
_session: Optional[Session] = None
|
slidge/group/bookmarks.py
CHANGED
@@ -133,6 +133,8 @@ class LegacyBookmarks(
|
|
133
133
|
try:
|
134
134
|
with muc.updating_info():
|
135
135
|
await muc.avatar_wrap_update_info()
|
136
|
+
except XMPPError:
|
137
|
+
raise
|
136
138
|
except Exception as e:
|
137
139
|
raise XMPPError("internal-server-error", str(e))
|
138
140
|
if not muc.user_nick:
|
@@ -160,6 +162,26 @@ class LegacyBookmarks(
|
|
160
162
|
" LegacyBookmarks.fill() was not overridden."
|
161
163
|
)
|
162
164
|
|
163
|
-
def remove(
|
165
|
+
async def remove(
|
166
|
+
self,
|
167
|
+
muc: LegacyMUC,
|
168
|
+
reason="You left this group from the official client.",
|
169
|
+
kick=True,
|
170
|
+
) -> None:
|
171
|
+
"""
|
172
|
+
Delete everything about a specific group.
|
173
|
+
|
174
|
+
This should be called when the user leaves the group from the official
|
175
|
+
app.
|
176
|
+
|
177
|
+
:param muc: The MUC to remove.
|
178
|
+
:param reason: Optionally, a reason why this group was removed.
|
179
|
+
:param kick: Whether the user should be kicked from this group. Set this
|
180
|
+
to False in case you do this somewhere else in your code, eg, on
|
181
|
+
receiving the confirmation that the group was deleted.
|
182
|
+
"""
|
164
183
|
assert muc.pk is not None
|
184
|
+
if kick:
|
185
|
+
user_participant = await muc.get_user_participant()
|
186
|
+
user_participant.kick(reason)
|
165
187
|
self.__store.delete(muc.pk)
|
slidge/group/participant.py
CHANGED
@@ -324,7 +324,7 @@ class LegacyParticipant(
|
|
324
324
|
) -> MessageOrPresenceTypeVar:
|
325
325
|
stanza["occupant-id"]["id"] = self.__occupant_id
|
326
326
|
self.__add_nick_element(stanza)
|
327
|
-
if isinstance(stanza, Presence):
|
327
|
+
if not self.is_user and isinstance(stanza, Presence):
|
328
328
|
if stanza["type"] == "unavailable" and not self._presence_sent:
|
329
329
|
return stanza # type:ignore
|
330
330
|
self._presence_sent = True
|
@@ -432,17 +432,17 @@ class LegacyParticipant(
|
|
432
432
|
"""
|
433
433
|
self.muc.remove_participant(self)
|
434
434
|
|
435
|
-
def kick(self):
|
435
|
+
def kick(self, reason: str | None = None):
|
436
436
|
"""
|
437
437
|
Call this when the participant is kicked from the room
|
438
438
|
"""
|
439
|
-
self.muc.remove_participant(self, kick=True)
|
439
|
+
self.muc.remove_participant(self, kick=True, reason=reason)
|
440
440
|
|
441
|
-
def ban(self):
|
441
|
+
def ban(self, reason: str | None = None):
|
442
442
|
"""
|
443
443
|
Call this when the participant is banned from the room
|
444
444
|
"""
|
445
|
-
self.muc.remove_participant(self, ban=True)
|
445
|
+
self.muc.remove_participant(self, ban=True, reason=reason)
|
446
446
|
|
447
447
|
def get_disco_info(self, jid: OptJid = None, node: Optional[str] = None):
|
448
448
|
if self.contact is not None:
|
slidge/group/room.py
CHANGED
@@ -814,13 +814,20 @@ class LegacyMUC(
|
|
814
814
|
return await self.get_user_participant(**kwargs)
|
815
815
|
return await self.get_participant_by_contact(c, **kwargs)
|
816
816
|
|
817
|
-
def remove_participant(
|
817
|
+
def remove_participant(
|
818
|
+
self,
|
819
|
+
p: "LegacyParticipantType",
|
820
|
+
kick=False,
|
821
|
+
ban=False,
|
822
|
+
reason: str | None = None,
|
823
|
+
):
|
818
824
|
"""
|
819
825
|
Call this when a participant leaves the room
|
820
826
|
|
821
827
|
:param p: The participant
|
822
828
|
:param kick: Whether the participant left because they were kicked
|
823
829
|
:param ban: Whether the participant left because they were banned
|
830
|
+
:param reason: Optionally, a reason why the participant was removed.
|
824
831
|
"""
|
825
832
|
if kick and ban:
|
826
833
|
raise TypeError("Either kick or ban")
|
@@ -834,6 +841,8 @@ class LegacyMUC(
|
|
834
841
|
presence = p._make_presence(ptype="unavailable", status_codes=codes)
|
835
842
|
p._affiliation = "outcast" if ban else "none"
|
836
843
|
p._role = "none"
|
844
|
+
if reason:
|
845
|
+
presence["muc"].set_item_attr("reason", reason)
|
837
846
|
p._send(presence)
|
838
847
|
|
839
848
|
def rename_participant(self, old_nickname: str, new_nickname: str):
|
slidge/util/test.py
CHANGED
@@ -215,13 +215,13 @@ class SlidgeTest(SlixTestPlus):
|
|
215
215
|
self.plugin, LegacyBookmarks, base_ok=True
|
216
216
|
)
|
217
217
|
|
218
|
+
# workaround for duplicate output of sql alchemy's log, cf
|
219
|
+
# https://stackoverflow.com/a/76498428/5902284
|
218
220
|
from sqlalchemy import log as sqlalchemy_log
|
219
221
|
|
220
222
|
sqlalchemy_log._add_default_handler = lambda x: None
|
221
223
|
|
222
|
-
engine = self.db_engine = create_engine(
|
223
|
-
"sqlite+pysqlite:///:memory:", echo=True
|
224
|
-
)
|
224
|
+
engine = self.db_engine = create_engine("sqlite+pysqlite:///:memory:")
|
225
225
|
Base.metadata.create_all(engine)
|
226
226
|
BaseGateway.store = SlidgeStore(engine)
|
227
227
|
BaseGateway._test_mode = True
|
@@ -287,9 +287,13 @@ class SlidgeTest(SlixTestPlus):
|
|
287
287
|
session.execute(delete(Contact))
|
288
288
|
session.commit()
|
289
289
|
|
290
|
-
self.run_coro(
|
290
|
+
self.run_coro(
|
291
|
+
self.xmpp._BaseGateway__dispatcher._on_user_register(
|
292
|
+
Iq(sfrom="romeo@montague.lit/gajim")
|
293
|
+
)
|
294
|
+
)
|
291
295
|
welcome = self.next_sent()
|
292
|
-
assert welcome["body"]
|
296
|
+
assert welcome["body"], welcome
|
293
297
|
stanza = self.next_sent()
|
294
298
|
assert "logging in" in stanza["status"].lower(), stanza
|
295
299
|
stanza = self.next_sent()
|
slidge/util/types.py
CHANGED
@@ -207,3 +207,9 @@ class CachedPresence(NamedTuple):
|
|
207
207
|
ptype: Optional[PresenceTypes] = None
|
208
208
|
pstatus: Optional[str] = None
|
209
209
|
pshow: Optional[PresenceShows] = None
|
210
|
+
|
211
|
+
|
212
|
+
class Sticker(NamedTuple):
|
213
|
+
path: Path
|
214
|
+
content_type: Optional[str]
|
215
|
+
hashes: dict[str, str]
|
slidge/util/util.py
CHANGED
@@ -7,7 +7,7 @@ from abc import ABCMeta
|
|
7
7
|
from functools import wraps
|
8
8
|
from pathlib import Path
|
9
9
|
from time import time
|
10
|
-
from typing import TYPE_CHECKING, Callable, NamedTuple, Optional, Type
|
10
|
+
from typing import TYPE_CHECKING, Callable, NamedTuple, Optional, Type, TypeVar
|
11
11
|
|
12
12
|
from .types import Mention, ResourceDict
|
13
13
|
|
@@ -276,7 +276,10 @@ def deprecated(name: str, new: Callable):
|
|
276
276
|
return wrapped
|
277
277
|
|
278
278
|
|
279
|
-
|
279
|
+
T = TypeVar("T", bound=NamedTuple)
|
280
|
+
|
281
|
+
|
282
|
+
def dict_to_named_tuple(data: dict, cls: Type[T]) -> T:
|
280
283
|
return cls(*(data.get(f) for f in cls._fields)) # type:ignore
|
281
284
|
|
282
285
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: slidge
|
3
|
-
Version: 0.2.
|
3
|
+
Version: 0.2.0b0
|
4
4
|
Summary: XMPP bridging framework
|
5
5
|
Home-page: https://sr.ht/~nicoco/slidge/
|
6
6
|
License: AGPL-3.0-or-later
|
@@ -16,6 +16,7 @@ Requires-Dist: ConfigArgParse (>=1.5.3,<2.0.0)
|
|
16
16
|
Requires-Dist: Pillow (>=10,<11)
|
17
17
|
Requires-Dist: aiohttp[speedups] (>=3.8.3,<4.0.0)
|
18
18
|
Requires-Dist: alembic (>=1.13.1,<2.0.0)
|
19
|
+
Requires-Dist: defusedxml (>=0.7.1,<0.8.0)
|
19
20
|
Requires-Dist: pickle-secure (>=0.99.9,<0.100.0)
|
20
21
|
Requires-Dist: python-magic (>=0.4.27,<0.5.0)
|
21
22
|
Requires-Dist: qrcode (>=7.4.1,<8.0.0)
|
@@ -1,56 +1,66 @@
|
|
1
1
|
slidge/__init__.py,sha256=S0tUjqpZlzsr8G4Y_1Xt-KCYB07qaknTB0OwHU8k29U,1587
|
2
|
-
slidge/__main__.py,sha256=
|
3
|
-
slidge/__version__.py,sha256=
|
2
|
+
slidge/__main__.py,sha256=ydjUklOoavS4YlGfjRX_8BQN2DaSbaXPMi47RkOgcFI,37
|
3
|
+
slidge/__version__.py,sha256=9HCdKNn7jsnGlM4hiADRgDmlQEfeHOYi3XRNJ7Fe-2w,169
|
4
4
|
slidge/command/__init__.py,sha256=UYf1mjCYbZ5G7PIgaFTWSQRAzEJkQ6dTH8Fu_e_XnO0,613
|
5
|
-
slidge/command/adhoc.py,sha256=
|
5
|
+
slidge/command/adhoc.py,sha256=9PsTsGMPKAK_YXQpwdcH9SSDki8YQ49OZ5p65W5HA6k,9412
|
6
6
|
slidge/command/admin.py,sha256=x_kJ0TJhzf6d3OBIOXFjudZFO8bRYUG919td7OjMCug,6008
|
7
|
-
slidge/command/base.py,sha256=
|
7
|
+
slidge/command/base.py,sha256=7NSzPZdBLZElrm3smzvFKgP0GUggxXdkhclxIKCjtT8,13036
|
8
8
|
slidge/command/categories.py,sha256=BJCfaga2qoAxnHfgHD7I_RKZuBA5nnNOukkWHJwsUFE,99
|
9
9
|
slidge/command/chat_command.py,sha256=VBs6IuDka1IyyMzz0ZyE9zMImaEzUZLcnffxq_vwb4M,10565
|
10
10
|
slidge/command/register.py,sha256=fzPcGUoJtainnDOiC13gWV-uYLuJcsmdKGJ-jXT1qIo,6697
|
11
|
-
slidge/command/user.py,sha256=
|
11
|
+
slidge/command/user.py,sha256=P4mU1wn1ywtquo0KKQsWddOhIKMV4HOueZAXOgmVvek,11700
|
12
12
|
slidge/contact/__init__.py,sha256=WMMaHk7UW7YT9EH2LtPdkU0bHQaOp4ikBhbBQskmoc8,191
|
13
13
|
slidge/contact/contact.py,sha256=kKtJ9NPLS9DPVyyahx_K-Mtp5k5UQdQJZavC1XCmWlc,23104
|
14
|
-
slidge/contact/roster.py,sha256
|
14
|
+
slidge/contact/roster.py,sha256=-Ei0f0cXX1LFpY29u4Ik68ikno3m2WRA5n5l8Nbjd_E,10267
|
15
15
|
slidge/core/__init__.py,sha256=RG7Jj5JCJERjhqJ31lOLYV-7bH_oblClQD1KF9LsTXo,68
|
16
|
-
slidge/core/config.py,sha256=
|
17
|
-
slidge/core/
|
18
|
-
slidge/core/
|
19
|
-
slidge/core/
|
20
|
-
slidge/core/
|
21
|
-
slidge/core/
|
22
|
-
slidge/core/
|
23
|
-
slidge/core/
|
24
|
-
slidge/core/
|
25
|
-
slidge/core/
|
26
|
-
slidge/core/
|
27
|
-
slidge/core/
|
28
|
-
slidge/core/
|
29
|
-
slidge/core/
|
16
|
+
slidge/core/config.py,sha256=voRFIlVDtKTZCdjc-zbwgnngFZrGvJjJ1dxRZm0BJK0,7514
|
17
|
+
slidge/core/dispatcher/__init__.py,sha256=1EXcjXietUKlxEqdrCWCV3xZ3q_DSsjHoqWrPMbtYao,84
|
18
|
+
slidge/core/dispatcher/caps.py,sha256=vzCAXo_bhALuLEpJWtyJTzVfWx96g1AsWD8_wkoDl0Y,2028
|
19
|
+
slidge/core/dispatcher/disco.py,sha256=j56VY9NIFzwPEWFKQQZ7YIqS9GdD-ZaF_K8a2L-JvRk,2006
|
20
|
+
slidge/core/dispatcher/message/__init__.py,sha256=vpDGOc_U9XvkUU_ws9n9-5M2NPJ87XGTVpuIxM7Z99k,223
|
21
|
+
slidge/core/dispatcher/message/chat_state.py,sha256=sCdEpzbgmvBmTovNOCv9uY6v0eJZcWVvDYAGlAV3FJ4,1735
|
22
|
+
slidge/core/dispatcher/message/marker.py,sha256=f1ezaMoHupBFZY7aUMsWLAQG7G1J9b3ihxICCkpGtis,2411
|
23
|
+
slidge/core/dispatcher/message/message.py,sha256=HwauW2kGionLyDWG01OSa9a14gYzoovJuJvGbfB4nt4,15296
|
24
|
+
slidge/core/dispatcher/muc/__init__.py,sha256=V8URHLJ_y7mk-7Id6FzRuczb1Uq_Z69fhxvzHuVLH1w,269
|
25
|
+
slidge/core/dispatcher/muc/admin.py,sha256=s21V2LEqc0e_DIpipEhhQdpae762lW1lVqj4wjFhX8M,3364
|
26
|
+
slidge/core/dispatcher/muc/mam.py,sha256=1ROVP4ZPEVEH-HR5qRV4YwHz-V15uu5gyhv1ZwwKhk8,2821
|
27
|
+
slidge/core/dispatcher/muc/misc.py,sha256=bHBjMC-Pu3jR5hAPGMzXf-C05UbACIwg38YbJUxHIxk,4068
|
28
|
+
slidge/core/dispatcher/muc/owner.py,sha256=1a6YV7b_mmi1jC6q1ko8weeL8imQA-s-hYGPLIHd10I,3308
|
29
|
+
slidge/core/dispatcher/muc/ping.py,sha256=lb1VQPhiUPZ19KhbofRXMVCcY6wwQ2w-asnqtANaAwA,1660
|
30
|
+
slidge/core/dispatcher/presence.py,sha256=ZxAmC34yxKxbk_-h6g_S8pTssL7ovULm3q2ishpYaB4,6393
|
31
|
+
slidge/core/dispatcher/registration.py,sha256=Xmbw9NF3LUppCOa3XzreopdKDitZnwl_5HE-kds74n8,3155
|
32
|
+
slidge/core/dispatcher/search.py,sha256=9cGj0wwvyYlP_Yk440Y12sgo4Y1p-JWUDSJP5Zxch0M,3296
|
33
|
+
slidge/core/dispatcher/session_dispatcher.py,sha256=_njTftgpUKKMP-hgAo99Hu0YrIa6E9OTzSYdiMW000w,2844
|
34
|
+
slidge/core/dispatcher/util.py,sha256=YtXyVxM3orE7aYWs-GbJumtLTI63OpaQY_t4FMTjoZo,5754
|
35
|
+
slidge/core/dispatcher/vcard.py,sha256=Rmx-wCz6Lps0mXCO48HppNQlS3GOgMuzuw9hZYBdlVU,5130
|
36
|
+
slidge/core/gateway.py,sha256=NhIgxZKPnOpwsx50OKgyZyk9nfU8ZlUSMddwIDIhFcw,36351
|
30
37
|
slidge/core/mixins/__init__.py,sha256=muReAzgvENgMvlfm0Fpe6BQFfm2EMjoDe9ZhGgo6Vig,627
|
31
|
-
slidge/core/mixins/attachment.py,sha256=
|
38
|
+
slidge/core/mixins/attachment.py,sha256=qHtv2I1buTmPO1jwRIpq2rixq5XTAljeWYj2eMWSw2k,19623
|
32
39
|
slidge/core/mixins/avatar.py,sha256=kGIIZzLSNuxF9bIvt5Bv03_uT_pU5QV1kS7cRu6-GUA,7874
|
33
|
-
slidge/core/mixins/base.py,sha256=
|
40
|
+
slidge/core/mixins/base.py,sha256=MOd-pas38_52VawQVlxWtBtmTKC6My9G0ZaCeQxOJbs,748
|
34
41
|
slidge/core/mixins/db.py,sha256=5Qpegd7D8e5TLXLLINYcf_DuVdN-7wNmsfztUuFYPcU,442
|
35
42
|
slidge/core/mixins/disco.py,sha256=jk3Z1B6zTuisHv8VKNRJodIo0ee5btYHh2ZrlflPj_Q,3670
|
36
|
-
slidge/core/mixins/lock.py,sha256=
|
37
|
-
slidge/core/mixins/message.py,sha256=
|
43
|
+
slidge/core/mixins/lock.py,sha256=Vf1rrkbyNbSprr38WGfZiMgTB7AdbqH8ppFHY8N2yXE,975
|
44
|
+
slidge/core/mixins/message.py,sha256=FB3VoaT81xUNVnaBMSwNJoHfrVv4Iv2678yDQH-23Rw,7551
|
38
45
|
slidge/core/mixins/message_maker.py,sha256=TcCutHi0sIwL6beJNkN7XyR0aDIbA0xZyxd2Gc9ulG4,6022
|
46
|
+
slidge/core/mixins/message_text.py,sha256=pCY4tezEuwB2ZuUyUi72i4v9AJkxp_SWF1jrFsn94Ns,8096
|
39
47
|
slidge/core/mixins/presence.py,sha256=yywo6KAw8C7GaZSMrSMuioNfhW08MrnobHt8XbHd0q8,7891
|
40
48
|
slidge/core/mixins/recipient.py,sha256=U-YppozUO8pA94jmD3-qmhkykTebPNaOVWc3JDPC9w8,1302
|
41
|
-
slidge/core/pubsub.py,sha256=
|
42
|
-
slidge/core/session.py,sha256=
|
49
|
+
slidge/core/pubsub.py,sha256=oTiS5KFQJAmsgkhOsvfvthT-LkuZGQSCrrUG0JskNkI,11907
|
50
|
+
slidge/core/session.py,sha256=nQexpCd1jlHOhQPnFI4ri-5odp3N2pU5HO4l7WFetZY,28148
|
43
51
|
slidge/db/__init__.py,sha256=EBDH1JSEhgqYcli2Bw11CRC749wJk8AOucgBzmhDSvU,105
|
44
52
|
slidge/db/alembic/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
45
53
|
slidge/db/alembic/env.py,sha256=hsBlRNs0zF5diSHGRSa8Fi3qRVQDA2rJdR41AEIdvxc,1642
|
46
54
|
slidge/db/alembic/old_user_store.py,sha256=zFOv0JEWQQK0_TMRlU4Z0G5Mc9pxvEErLyOzXmRAe5Q,5209
|
47
55
|
slidge/db/alembic/script.py.mako,sha256=MEqL-2qATlST9TAOeYgscMn1uy6HUS9NFvDgl93dMj8,635
|
48
56
|
slidge/db/alembic/versions/09f27f098baa_add_missing_attributes_in_room.py,sha256=mUL-0Io6ZPd_QbnKfwGYyjdMcM2uxQ0Wg72H23-2t_E,1033
|
57
|
+
slidge/db/alembic/versions/15b0bd83407a_remove_bogus_unique_constraints_on_room_.py,sha256=bc_H_tPCVjiiUDqWE3oQtnIvsv2tlrzt0NB2f24mbdk,2862
|
49
58
|
slidge/db/alembic/versions/2461390c0af2_store_contacts_caps_verstring_in_db.py,sha256=CLB-kOP9Rc0FJIKDLef912L5sYkjpTIPC8fhrIdrC7k,1084
|
50
59
|
slidge/db/alembic/versions/29f5280c61aa_store_subject_setter_in_room.py,sha256=f8TFS28CXjGhvIn41UYMoHYeODfqhKfo4O7gk-JwA1E,1134
|
51
60
|
slidge/db/alembic/versions/2b1f45ab7379_store_room_subject_setter_by_nickname.py,sha256=CMVP2wFz6s7t57eWdSaGtck8BXzfVPJhHE5AoWi34tI,1359
|
52
61
|
slidge/db/alembic/versions/3071e0fa69d4_add_contact_client_type.py,sha256=O5BY1vpbtuYT5j6i3EMuuJAf6loIYT1kco8c-c6TF5g,1391
|
53
|
-
slidge/db/alembic/versions/
|
62
|
+
slidge/db/alembic/versions/45c24cc73c91_add_bob.py,sha256=UjMySZ5LaInyPt0KbAxx0rF4GQhZh8CwBeqHtNPdG1c,1249
|
63
|
+
slidge/db/alembic/versions/5bd48bfdffa2_lift_room_legacy_id_constraint.py,sha256=m3USa76h0O2Xut-NePXIOZfkXl0bx0d5FyjOYpd34Jo,1977
|
54
64
|
slidge/db/alembic/versions/82a4af84b679_add_muc_history_filled.py,sha256=g37po0ydp8ZmzJrE5oFV7GscnploxjCtPDpw28SqVGk,1429
|
55
65
|
slidge/db/alembic/versions/8b993243a536_add_vcard_content_to_contact_table.py,sha256=18tG8B03Kq8Qz_-mMd28Beed6jow8XNTtrz7gT5QY3g,1210
|
56
66
|
slidge/db/alembic/versions/8d2ced764698_rely_on_db_to_store_contacts_rooms_and_.py,sha256=olXaOEEsUSasqaaKdlP1cBODsMhmV1i90qbpDM2vTm4,4696
|
@@ -62,17 +72,18 @@ slidge/db/alembic/versions/c4a8ec35a0e8_per_room_user_nick.py,sha256=jjQmlRv6nqd
|
|
62
72
|
slidge/db/alembic/versions/e91195719c2c_store_users_avatars_persistently.py,sha256=8Ga3VFgKrzMs_-B8OPtfP-0rey_MFaDg-QGtSbaft3o,640
|
63
73
|
slidge/db/avatar.py,sha256=FfRt2Vu11ZKD9F3x1_drawvUd-TDE3mp7SE3BZ9hOOg,6467
|
64
74
|
slidge/db/meta.py,sha256=v1Jf-npZ28QwdGpsLQWLBHEbEP3-jnPrygRg05tJ_Iw,1831
|
65
|
-
slidge/db/models.py,sha256=
|
66
|
-
slidge/db/store.py,sha256=
|
75
|
+
slidge/db/models.py,sha256=mazginFllRNsC2w-SW_Y9HUMtruYnzSDCGGjsJwwsp8,13782
|
76
|
+
slidge/db/store.py,sha256=7-HIml_wmgwwKe1AxI9yXbtWGz7yxH0cMc_IY4p1Wl4,46696
|
67
77
|
slidge/group/__init__.py,sha256=yFt7cHqeaKIMN6f9ZyhhspOcJJvBtLedGv-iICG7lto,258
|
68
78
|
slidge/group/archive.py,sha256=xGPkdSk8-BT6t6lNVo1FEwiFVAttoxCma8Tsyk5r8Kg,5279
|
69
|
-
slidge/group/bookmarks.py,sha256=
|
70
|
-
slidge/group/participant.py,sha256=
|
71
|
-
slidge/group/room.py,sha256=
|
79
|
+
slidge/group/bookmarks.py,sha256=AvFL34bEX6n3OP1Np309T5hrLK9GnjkjdyLJ3uiLZyc,6616
|
80
|
+
slidge/group/participant.py,sha256=Wtq03Ix55AxlK4pvYVIalLwmKklJiIAsZdeLADJNJgU,17138
|
81
|
+
slidge/group/room.py,sha256=IizSwUBoKLvcvLpDseHIW_2KAky38uWsSv-poJuCAF0,46019
|
72
82
|
slidge/main.py,sha256=8oND7xpR3eLw7b62fT61UhYlmNp_9gv3tNz2N3xR7-c,6232
|
73
83
|
slidge/migration.py,sha256=4BJmPIRB56_WIhRTqBFIIBXuvnhhBjjOMl4CE7jY6oc,1541
|
74
84
|
slidge/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
75
85
|
slidge/slixfix/__init__.py,sha256=7GevigEt68hwgwHqXcsFogN5jRXRHPeqR6kwODCH4hc,3055
|
86
|
+
slidge/slixfix/delivery_receipt.py,sha256=3bWdZH3-X3CZJXmnI_TpjkTUUK-EY4Ktm78lW0-40fc,1366
|
76
87
|
slidge/slixfix/link_preview/__init__.py,sha256=TDPTSEH5FQxgGpQpQIde-D72AHg-6YVWG-tOj4KpKmU,290
|
77
88
|
slidge/slixfix/link_preview/link_preview.py,sha256=9PgdfnoyVMHnXS0w5OFp0wz3ku96Ck-HtRXbVUlDi1U,448
|
78
89
|
slidge/slixfix/link_preview/stanza.py,sha256=YAXoNw2MD0a3nzvldGKlvSemjUMbUEG23regzmj4Ntc,2664
|
@@ -110,11 +121,11 @@ slidge/util/__init__.py,sha256=BELovoTMPcPPGz3D48esBr8A4BRRHXTvavfgnArBgEc,301
|
|
110
121
|
slidge/util/archive_msg.py,sha256=xXAR0BI5r3d6KKWjae9594izCOv6iI03z2WLuTecNw8,1724
|
111
122
|
slidge/util/conf.py,sha256=1j2OnOsCBar1tOObErhXR5RC3Vl3faliOZ1U8J3My58,6613
|
112
123
|
slidge/util/db.py,sha256=4LxZj8oBYgiSnyBUnF_ALjr0TblkfNQq_p28sCfkHMY,242
|
113
|
-
slidge/util/test.py,sha256=
|
114
|
-
slidge/util/types.py,sha256=
|
115
|
-
slidge/util/util.py,sha256=
|
116
|
-
slidge-0.2.
|
117
|
-
slidge-0.2.
|
118
|
-
slidge-0.2.
|
119
|
-
slidge-0.2.
|
120
|
-
slidge-0.2.
|
124
|
+
slidge/util/test.py,sha256=xnGXK0wvua49ncQm4linIfH24Ux6oCkm5A71k2V80zI,14007
|
125
|
+
slidge/util/types.py,sha256=R_xfS5mRL0XUJIoDpnaAkZlTOoLPerduXBFftaVwIAI,5489
|
126
|
+
slidge/util/util.py,sha256=DyJWO2pmE-RiB9Rsy6TUTcvB-BDlmLZBW4PELx4arFQ,9156
|
127
|
+
slidge-0.2.0b0.dist-info/LICENSE,sha256=DZak_2itbUtvHzD3E7GNUYSRK6jdOJ-GqncQ2weavLA,34523
|
128
|
+
slidge-0.2.0b0.dist-info/METADATA,sha256=JrkUpw6lNEWr3ESfNKv-NMuvLPIwqz-3G2Gt7i9nkkk,5005
|
129
|
+
slidge-0.2.0b0.dist-info/WHEEL,sha256=7Z8_27uaHI_UZAc4Uox4PpBhQ9Y5_modZXWMxtUi4NU,88
|
130
|
+
slidge-0.2.0b0.dist-info/entry_points.txt,sha256=btz6mbzx1X6fjFWAS_Bo5qNi8PtxUsDgunt-6r4JDHw,43
|
131
|
+
slidge-0.2.0b0.dist-info/RECORD,,
|
slidge/core/gateway/__init__.py
DELETED
slidge/core/gateway/muc_admin.py
DELETED
@@ -1,35 +0,0 @@
|
|
1
|
-
from typing import TYPE_CHECKING
|
2
|
-
|
3
|
-
from slixmpp import CoroutineCallback, Iq, StanzaPath
|
4
|
-
from slixmpp.exceptions import XMPPError
|
5
|
-
|
6
|
-
if TYPE_CHECKING:
|
7
|
-
from .base import BaseGateway
|
8
|
-
|
9
|
-
|
10
|
-
class MucAdmin:
|
11
|
-
def __init__(self, xmpp: "BaseGateway"):
|
12
|
-
self.xmpp = xmpp
|
13
|
-
xmpp.register_handler(
|
14
|
-
CoroutineCallback(
|
15
|
-
"muc#admin",
|
16
|
-
StanzaPath("iq@type=get/mucadmin_query"),
|
17
|
-
self._handle_admin, # type: ignore
|
18
|
-
)
|
19
|
-
)
|
20
|
-
|
21
|
-
async def _handle_admin(self, iq: Iq):
|
22
|
-
muc = await self.xmpp.get_muc_from_stanza(iq)
|
23
|
-
|
24
|
-
affiliation = iq["mucadmin_query"]["item"]["affiliation"]
|
25
|
-
|
26
|
-
if not affiliation:
|
27
|
-
raise XMPPError("bad-request")
|
28
|
-
|
29
|
-
reply = iq.reply()
|
30
|
-
reply.enable("mucadmin_query")
|
31
|
-
async for participant in muc.get_participants():
|
32
|
-
if not participant.affiliation == affiliation:
|
33
|
-
continue
|
34
|
-
reply["mucadmin_query"].append(participant.mucadmin_item())
|
35
|
-
reply.send()
|