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.
Files changed (56) hide show
  1. slidge/__main__.py +2 -3
  2. slidge/__version__.py +1 -1
  3. slidge/command/adhoc.py +1 -1
  4. slidge/command/base.py +4 -4
  5. slidge/command/user.py +5 -1
  6. slidge/contact/roster.py +9 -0
  7. slidge/core/config.py +0 -3
  8. slidge/core/dispatcher/__init__.py +3 -0
  9. slidge/core/{gateway → dispatcher}/caps.py +6 -4
  10. slidge/core/{gateway → dispatcher}/disco.py +11 -17
  11. slidge/core/dispatcher/message/__init__.py +10 -0
  12. slidge/core/dispatcher/message/chat_state.py +40 -0
  13. slidge/core/dispatcher/message/marker.py +62 -0
  14. slidge/core/dispatcher/message/message.py +397 -0
  15. slidge/core/dispatcher/muc/__init__.py +12 -0
  16. slidge/core/dispatcher/muc/admin.py +98 -0
  17. slidge/core/{gateway → dispatcher/muc}/mam.py +26 -15
  18. slidge/core/dispatcher/muc/misc.py +121 -0
  19. slidge/core/dispatcher/muc/owner.py +96 -0
  20. slidge/core/{gateway → dispatcher/muc}/ping.py +10 -15
  21. slidge/core/dispatcher/presence.py +177 -0
  22. slidge/core/{gateway → dispatcher}/registration.py +23 -2
  23. slidge/core/{gateway → dispatcher}/search.py +9 -14
  24. slidge/core/dispatcher/session_dispatcher.py +84 -0
  25. slidge/core/dispatcher/util.py +174 -0
  26. slidge/core/{gateway/vcard_temp.py → dispatcher/vcard.py} +26 -12
  27. slidge/core/{gateway/base.py → gateway.py} +42 -137
  28. slidge/core/mixins/attachment.py +24 -8
  29. slidge/core/mixins/base.py +2 -2
  30. slidge/core/mixins/lock.py +10 -8
  31. slidge/core/mixins/message.py +9 -203
  32. slidge/core/mixins/message_text.py +211 -0
  33. slidge/core/pubsub.py +2 -1
  34. slidge/core/session.py +28 -2
  35. slidge/db/alembic/versions/15b0bd83407a_remove_bogus_unique_constraints_on_room_.py +83 -0
  36. slidge/db/alembic/versions/45c24cc73c91_add_bob.py +42 -0
  37. slidge/db/alembic/versions/5bd48bfdffa2_lift_room_legacy_id_constraint.py +12 -1
  38. slidge/db/models.py +16 -1
  39. slidge/db/store.py +144 -11
  40. slidge/group/bookmarks.py +23 -1
  41. slidge/group/participant.py +5 -5
  42. slidge/group/room.py +10 -1
  43. slidge/{core/gateway → slixfix}/delivery_receipt.py +1 -1
  44. slidge/util/test.py +9 -5
  45. slidge/util/types.py +6 -0
  46. slidge/util/util.py +5 -2
  47. {slidge-0.2.0a9.dist-info → slidge-0.2.0b0.dist-info}/METADATA +2 -1
  48. {slidge-0.2.0a9.dist-info → slidge-0.2.0b0.dist-info}/RECORD +51 -40
  49. slidge-0.2.0b0.dist-info/entry_points.txt +3 -0
  50. slidge/core/gateway/__init__.py +0 -3
  51. slidge/core/gateway/muc_admin.py +0 -35
  52. slidge/core/gateway/presence.py +0 -95
  53. slidge/core/gateway/session_dispatcher.py +0 -895
  54. slidge-0.2.0a9.dist-info/entry_points.txt +0 -3
  55. {slidge-0.2.0a9.dist-info → slidge-0.2.0b0.dist-info}/LICENSE +0 -0
  56. {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(back_populates="contact")
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(self, muc: LegacyMUC):
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)
@@ -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(self, p: "LegacyParticipantType", kick=False, ban=False):
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):
@@ -11,7 +11,7 @@ from slixmpp import JID, Message
11
11
  from slixmpp.types import MessageTypes
12
12
 
13
13
  if TYPE_CHECKING:
14
- from .base import BaseGateway
14
+ from slidge.core.gateway import BaseGateway
15
15
 
16
16
 
17
17
  class DeliveryReceipt:
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(self.xmpp._on_user_register(Iq(sfrom="romeo@montague.lit/gajim")))
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
- def dict_to_named_tuple(data: dict, cls: Type[NamedTuple]):
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.0a9
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=Y12eh1TD_C5MB50KgEAuMffGnRFCvKYFKHD4UYSmHA0,72
3
- slidge/__version__.py,sha256=9y4M-LYXgK7-PIasL6QWLN6L5LRaek-yXy_SekzwWrg,170
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=5xLLoWyUJqCJB7kAwD2VKYTl_7MObCsgdz-qg_WkWGs,9417
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=xTlzxAbTWTpbFgQ0FxKjmp3ZImzempwJvGn9UiVUUTc,13044
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=uFheYOprhypkHEEl6qSTEM7T2N28xXaDi7v1he-AET8,11512
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=YF5NiParDKYNqW52fu1xSVW1skR-guy7YzIQ5kDYoDk,9874
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=leNcN_TI0Ka1hhzOHx7cBW3fNj5xZwsiv9l8AfRY_vU,7630
17
- slidge/core/gateway/__init__.py,sha256=rZckY2gAE-mon77_DSsAW1XtWqhBAETE2d4FqZ8pJXk,58
18
- slidge/core/gateway/base.py,sha256=Dxy39bAgOvJ-vN1RA9PU821YYuvkp_V06y_-vUDW7bU,38752
19
- slidge/core/gateway/caps.py,sha256=jemB4tB_2MTAxqQw5Bs4b7qNQ8gLPuAve0aoH6TzLEs,1937
20
- slidge/core/gateway/delivery_receipt.py,sha256=AT_9gvZrtWpSRsDJcYjE8CmF7TW-YBbUPdqNW5zWAdo,1352
21
- slidge/core/gateway/disco.py,sha256=uazgDXSDb5KrardjPCvElItARcxkeBRohtx82A2BlCQ,2203
22
- slidge/core/gateway/mam.py,sha256=ZUZXX7YcpK1WHq8RlgYrXo2zrLm5pOM1sVzON61mXYQ,2418
23
- slidge/core/gateway/muc_admin.py,sha256=PslX1gZK_PhY2VaeAPP1nEzThMaBBGPRgs-wiJ35zJQ,1033
24
- slidge/core/gateway/ping.py,sha256=_zzPkjqvxjTxLNP1jbj0WVLMaybxbYqrKDRM5jHSDjs,1729
25
- slidge/core/gateway/presence.py,sha256=Ls8IY4uNQaW8F3F1CpRhfyFIVbd_py_VkZyJKMMei8s,2732
26
- slidge/core/gateway/registration.py,sha256=JXwIQ-QqZCPXEmCU2G8FvIYDGvD8L8CqGb_Qkbycgt0,2303
27
- slidge/core/gateway/search.py,sha256=08ds6gvzX3EnTH-AU8X8J8JKEKYaSrRGTFwwClTT-Rc,3495
28
- slidge/core/gateway/session_dispatcher.py,sha256=FBCbMNNa3SQCd2n_NEdD5q7cjdEgUHGuI_DIWh5cTUY,33202
29
- slidge/core/gateway/vcard_temp.py,sha256=cnzHZO6qF8OUWSvBieEdNAV_Ndv1ixZOzBCBXy2Vopw,4637
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=5Xa_GkSL_rRTC45TRjW98jztk__4M5P-3EG4IF91K0c,19022
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=9hjf2Pw5r0poSi1Abv-_znk83Kngsu9EXTf05gpFDdI,768
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=mVzwVVEoq1hrAMgGLh4K84BTLt7JTJ33B8HSGSorTdY,913
37
- slidge/core/mixins/message.py,sha256=jJz_peNQmDf0uVQcI7TWG7oUaSGjUL8eiulNicGgWhI,15071
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=ITOhgUKdYh6oJBpmZ8nevDQlYT4WJMC-k7Z76YchXqY,11906
42
- slidge/core/session.py,sha256=iYRdG_CZYiH-a4A69vqKLf-vnSF_N-XnxEF1veBMCWk,27182
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/5bd48bfdffa2_lift_room_legacy_id_constraint.py,sha256=cObfqUzqdNezIeJdHe7YKFwRwtelXk8y1PwZ75chXDc,1629
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=yO4hM0VE1cQdDqWiZU1YW5ZjzwSnHUTavkGbkAMEB9A,13297
66
- slidge/db/store.py,sha256=jBkjTVXIg_N558rEfaDWzQ7mnIYMp2ziTk0o9KRVioo,42071
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=_LDf7A7aWkwPH88v7c-mOp8VJs3gSFM1-uCqSb4ThO8,5825
70
- slidge/group/participant.py,sha256=VNMtqr98QVuYgiTsJ9BaaIG1noz-xe3ewyKhLeDRhBk,17033
71
- slidge/group/room.py,sha256=v5mCV7ZrCdgXtDu_K7oDTdtjNYJV-y9wPmlg_RN_4s4,45789
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=S8Kp5uJlWYPvnw8HMHUPlbrvsqwdBLs9MI99svMfjLg,13832
114
- slidge/util/types.py,sha256=o_5rbwfL_3e73JPT1t9o3kyDpJscFHzn4_bt8i3LoSk,5386
115
- slidge/util/util.py,sha256=8JeE0QObNGQr_Tw4OFPwQSz_EB_zh_0t9IJmNNhW0ic,9114
116
- slidge-0.2.0a9.dist-info/LICENSE,sha256=DZak_2itbUtvHzD3E7GNUYSRK6jdOJ-GqncQ2weavLA,34523
117
- slidge-0.2.0a9.dist-info/METADATA,sha256=UYbPeUa9y7-m4f1ulm4CFYAdKYoGbf53gC-n_lf6aoI,4962
118
- slidge-0.2.0a9.dist-info/WHEEL,sha256=7Z8_27uaHI_UZAc4Uox4PpBhQ9Y5_modZXWMxtUi4NU,88
119
- slidge-0.2.0a9.dist-info/entry_points.txt,sha256=SNl72KSocF5plsu_67xyH6wVWfGTXQbzkQgXbLtzDrQ,47
120
- slidge-0.2.0a9.dist-info/RECORD,,
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,,
@@ -0,0 +1,3 @@
1
+ [console_scripts]
2
+ slidge=slidge.main:main
3
+
@@ -1,3 +0,0 @@
1
- from .base import BaseGateway
2
-
3
- __all__ = ("BaseGateway",)
@@ -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()