slidge 0.2.0a9__py3-none-any.whl → 0.2.0b0__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
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()