slidge 0.2.12__py3-none-any.whl → 0.3.0a0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. slidge/__init__.py +5 -2
  2. slidge/command/adhoc.py +9 -3
  3. slidge/command/admin.py +16 -12
  4. slidge/command/base.py +16 -12
  5. slidge/command/chat_command.py +25 -16
  6. slidge/command/user.py +7 -8
  7. slidge/contact/contact.py +119 -209
  8. slidge/contact/roster.py +106 -105
  9. slidge/core/config.py +2 -43
  10. slidge/core/dispatcher/caps.py +9 -2
  11. slidge/core/dispatcher/disco.py +13 -3
  12. slidge/core/dispatcher/message/__init__.py +1 -1
  13. slidge/core/dispatcher/message/chat_state.py +17 -8
  14. slidge/core/dispatcher/message/marker.py +7 -5
  15. slidge/core/dispatcher/message/message.py +117 -92
  16. slidge/core/dispatcher/muc/__init__.py +1 -1
  17. slidge/core/dispatcher/muc/admin.py +4 -4
  18. slidge/core/dispatcher/muc/mam.py +10 -6
  19. slidge/core/dispatcher/muc/misc.py +4 -2
  20. slidge/core/dispatcher/muc/owner.py +5 -3
  21. slidge/core/dispatcher/muc/ping.py +3 -1
  22. slidge/core/dispatcher/presence.py +21 -15
  23. slidge/core/dispatcher/registration.py +20 -12
  24. slidge/core/dispatcher/search.py +7 -3
  25. slidge/core/dispatcher/session_dispatcher.py +13 -5
  26. slidge/core/dispatcher/util.py +37 -27
  27. slidge/core/dispatcher/vcard.py +7 -4
  28. slidge/core/gateway.py +168 -84
  29. slidge/core/mixins/__init__.py +1 -11
  30. slidge/core/mixins/attachment.py +163 -148
  31. slidge/core/mixins/avatar.py +100 -177
  32. slidge/core/mixins/db.py +50 -2
  33. slidge/core/mixins/message.py +19 -17
  34. slidge/core/mixins/message_maker.py +29 -15
  35. slidge/core/mixins/message_text.py +38 -30
  36. slidge/core/mixins/presence.py +91 -35
  37. slidge/core/pubsub.py +42 -47
  38. slidge/core/session.py +88 -57
  39. slidge/db/alembic/versions/0337c90c0b96_unify_legacy_xmpp_id_mappings.py +183 -0
  40. slidge/db/alembic/versions/4dbd23a3f868_new_avatar_store.py +56 -0
  41. slidge/db/alembic/versions/54ce3cde350c_use_hash_for_avatar_filenames.py +50 -0
  42. slidge/db/alembic/versions/58b98dacf819_refactor.py +118 -0
  43. slidge/db/alembic/versions/75a62b74b239_ditch_hats_table.py +74 -0
  44. slidge/db/avatar.py +150 -119
  45. slidge/db/meta.py +33 -22
  46. slidge/db/models.py +68 -117
  47. slidge/db/store.py +412 -1094
  48. slidge/group/archive.py +61 -54
  49. slidge/group/bookmarks.py +74 -55
  50. slidge/group/participant.py +135 -142
  51. slidge/group/room.py +315 -312
  52. slidge/main.py +28 -18
  53. slidge/migration.py +2 -12
  54. slidge/slixfix/__init__.py +20 -4
  55. slidge/slixfix/delivery_receipt.py +6 -4
  56. slidge/slixfix/link_preview/link_preview.py +1 -1
  57. slidge/slixfix/link_preview/stanza.py +1 -1
  58. slidge/slixfix/roster.py +5 -7
  59. slidge/slixfix/xep_0077/register.py +8 -8
  60. slidge/slixfix/xep_0077/stanza.py +7 -7
  61. slidge/slixfix/xep_0100/gateway.py +12 -13
  62. slidge/slixfix/xep_0153/vcard_avatar.py +1 -1
  63. slidge/slixfix/xep_0292/vcard4.py +1 -1
  64. slidge/util/archive_msg.py +11 -5
  65. slidge/util/conf.py +23 -20
  66. slidge/util/jid_escaping.py +1 -1
  67. slidge/{core/mixins → util}/lock.py +6 -6
  68. slidge/util/test.py +30 -29
  69. slidge/util/types.py +22 -18
  70. slidge/util/util.py +19 -22
  71. {slidge-0.2.12.dist-info → slidge-0.3.0a0.dist-info}/METADATA +1 -1
  72. slidge-0.3.0a0.dist-info/RECORD +117 -0
  73. {slidge-0.2.12.dist-info → slidge-0.3.0a0.dist-info}/WHEEL +1 -1
  74. slidge-0.2.12.dist-info/RECORD +0 -112
  75. {slidge-0.2.12.dist-info → slidge-0.3.0a0.dist-info}/entry_points.txt +0 -0
  76. {slidge-0.2.12.dist-info → slidge-0.3.0a0.dist-info}/licenses/LICENSE +0 -0
  77. {slidge-0.2.12.dist-info → slidge-0.3.0a0.dist-info}/top_level.txt +0 -0
slidge/db/models.py CHANGED
@@ -6,24 +6,13 @@ from typing import Optional
6
6
  import sqlalchemy as sa
7
7
  from slixmpp import JID
8
8
  from slixmpp.types import MucAffiliation, MucRole
9
- from sqlalchemy import ForeignKey, Index, UniqueConstraint
10
- from sqlalchemy.orm import Mapped, mapped_column, relationship
9
+ from sqlalchemy import JSON, ForeignKey, Index, UniqueConstraint
10
+ from sqlalchemy.orm import Mapped, declared_attr, mapped_column, relationship
11
11
 
12
12
  from ..util.types import ClientType, MucType
13
13
  from .meta import Base, JSONSerializable, JSONSerializableTypes
14
14
 
15
15
 
16
- class XmppToLegacyEnum(IntEnum):
17
- """
18
- XMPP-client generated IDs, used in the XmppToLegacyIds table to keep track
19
- of corresponding legacy IDs
20
- """
21
-
22
- DM = 1
23
- GROUP_CHAT = 2
24
- THREAD = 3
25
-
26
-
27
16
  class ArchivedMessageSource(IntEnum):
28
17
  """
29
18
  Whether an archived message comes from ``LegacyMUC.backfill()`` or was received
@@ -63,16 +52,7 @@ class GatewayUser(Base):
63
52
  rooms: Mapped[list["Room"]] = relationship(
64
53
  back_populates="user", cascade="all, delete-orphan"
65
54
  )
66
- xmpp_to_legacy: Mapped[list["XmppToLegacyIds"]] = relationship(
67
- cascade="all, delete-orphan"
68
- )
69
55
  attachments: Mapped[list["Attachment"]] = relationship(cascade="all, delete-orphan")
70
- multi_legacy: Mapped[list["LegacyIdsMulti"]] = relationship(
71
- cascade="all, delete-orphan"
72
- )
73
- multi_xmpp: Mapped[list["XmppIdsMulti"]] = relationship(
74
- cascade="all, delete-orphan"
75
- )
76
56
 
77
57
  def __repr__(self) -> str:
78
58
  return f"User(id={self.id!r}, jid={self.jid!r})"
@@ -109,11 +89,12 @@ class Avatar(Base):
109
89
 
110
90
  id: Mapped[int] = mapped_column(primary_key=True)
111
91
 
112
- filename: Mapped[str] = mapped_column(unique=True)
113
92
  hash: Mapped[str] = mapped_column(unique=True)
114
93
  height: Mapped[int] = mapped_column()
115
94
  width: Mapped[int] = mapped_column()
116
95
 
96
+ legacy_id: Mapped[Optional[str]] = mapped_column(unique=True, nullable=True)
97
+
117
98
  # this is only used when avatars are available as HTTP URLs and do not
118
99
  # have a legacy_id
119
100
  url: Mapped[Optional[str]] = mapped_column(default=None)
@@ -137,13 +118,17 @@ class Contact(Base):
137
118
 
138
119
  id: Mapped[int] = mapped_column(primary_key=True)
139
120
  user_account_id: Mapped[int] = mapped_column(ForeignKey("user_account.id"))
140
- user: Mapped[GatewayUser] = relationship(back_populates="contacts")
121
+ user: Mapped[GatewayUser] = relationship(lazy=True, back_populates="contacts")
141
122
  legacy_id: Mapped[str] = mapped_column(nullable=False)
142
123
 
143
124
  jid: Mapped[JID] = mapped_column()
144
125
 
145
- avatar_id: Mapped[int] = mapped_column(ForeignKey("avatar.id"), nullable=True)
146
- avatar: Mapped[Avatar] = relationship(back_populates="contacts")
126
+ avatar_id: Mapped[Optional[int]] = mapped_column(
127
+ ForeignKey("avatar.id"), nullable=True
128
+ )
129
+ avatar: Mapped[Optional[Avatar]] = relationship(
130
+ lazy=False, back_populates="contacts"
131
+ )
147
132
 
148
133
  nick: Mapped[Optional[str]] = mapped_column(nullable=True)
149
134
 
@@ -170,10 +155,13 @@ class Contact(Base):
170
155
 
171
156
  participants: Mapped[list["Participant"]] = relationship(back_populates="contact")
172
157
 
173
- avatar_legacy_id: Mapped[Optional[str]] = mapped_column(nullable=True)
174
-
175
158
  client_type: Mapped[ClientType] = mapped_column(nullable=False, default="pc")
176
159
 
160
+ messages: Mapped[list["DirectMessages"]] = relationship(
161
+ cascade="all, delete-orphan"
162
+ )
163
+ threads: Mapped[list["DirectThreads"]] = relationship(cascade="all, delete-orphan")
164
+
177
165
 
178
166
  class ContactSent(Base):
179
167
  """
@@ -207,13 +195,15 @@ class Room(Base):
207
195
  __tablename__ = "room"
208
196
  id: Mapped[int] = mapped_column(primary_key=True)
209
197
  user_account_id: Mapped[int] = mapped_column(ForeignKey("user_account.id"))
210
- user: Mapped[GatewayUser] = relationship(back_populates="rooms")
198
+ user: Mapped[GatewayUser] = relationship(lazy=True, back_populates="rooms")
211
199
  legacy_id: Mapped[str] = mapped_column(nullable=False)
212
200
 
213
201
  jid: Mapped[JID] = mapped_column(nullable=False)
214
202
 
215
- avatar_id: Mapped[int] = mapped_column(ForeignKey("avatar.id"), nullable=True)
216
- avatar: Mapped[Avatar] = relationship(back_populates="rooms")
203
+ avatar_id: Mapped[Optional[int]] = mapped_column(
204
+ ForeignKey("avatar.id"), nullable=True
205
+ )
206
+ avatar: Mapped[Optional[Avatar]] = relationship(lazy=False, back_populates="rooms")
217
207
 
218
208
  name: Mapped[Optional[str]] = mapped_column(nullable=True)
219
209
  description: Mapped[Optional[str]] = mapped_column(nullable=True)
@@ -223,7 +213,7 @@ class Room(Base):
223
213
 
224
214
  n_participants: Mapped[Optional[int]] = mapped_column(default=None)
225
215
 
226
- muc_type: Mapped[Optional[MucType]] = mapped_column(default=MucType.GROUP)
216
+ muc_type: Mapped[MucType] = mapped_column(default=MucType.CHANNEL)
227
217
 
228
218
  user_nick: Mapped[Optional[str]] = mapped_column()
229
219
  user_resources: Mapped[Optional[str]] = mapped_column(nullable=True)
@@ -240,12 +230,13 @@ class Room(Base):
240
230
  cascade="all, delete-orphan",
241
231
  )
242
232
 
243
- avatar_legacy_id: Mapped[Optional[str]] = mapped_column(nullable=True)
244
-
245
233
  archive: Mapped[list["ArchivedMessage"]] = relationship(
246
234
  cascade="all, delete-orphan"
247
235
  )
248
236
 
237
+ messages: Mapped[list["GroupMessages"]] = relationship(cascade="all, delete-orphan")
238
+ threads: Mapped[list["GroupThreads"]] = relationship(cascade="all, delete-orphan")
239
+
249
240
 
250
241
  class ArchivedMessage(Base):
251
242
  """
@@ -267,23 +258,40 @@ class ArchivedMessage(Base):
267
258
  stanza: Mapped[str] = mapped_column(nullable=False)
268
259
 
269
260
 
270
- class XmppToLegacyIds(Base):
261
+ class _LegacyToXmppIdsBase:
271
262
  """
272
- XMPP-client generated IDs, and mapping to the corresponding legacy IDs
263
+ XMPP-client generated IDs, and mapping to the corresponding legacy IDs.
264
+
265
+ A single legacy ID can map to several XMPP ids.
273
266
  """
274
267
 
275
- __tablename__ = "xmpp_to_legacy_ids"
276
- __table_args__ = (
277
- Index("xmpp_legacy", "user_account_id", "xmpp_id", "legacy_id", unique=True),
278
- )
279
268
  id: Mapped[int] = mapped_column(primary_key=True)
280
- user_account_id: Mapped[int] = mapped_column(ForeignKey("user_account.id"))
281
- user: Mapped[GatewayUser] = relationship(back_populates="xmpp_to_legacy")
282
-
283
- xmpp_id: Mapped[str] = mapped_column(nullable=False)
284
269
  legacy_id: Mapped[str] = mapped_column(nullable=False)
270
+ xmpp_id: Mapped[str] = mapped_column(nullable=False)
271
+
272
+
273
+ class DirectMessages(_LegacyToXmppIdsBase, Base):
274
+ __tablename__ = "direct_msg"
275
+ __table_args__ = (Index("ix_direct_msg_legacy_id", "legacy_id", "foreign_key"),)
276
+ foreign_key: Mapped[int] = mapped_column(ForeignKey("contact.id"), nullable=False)
277
+
278
+
279
+ class GroupMessages(_LegacyToXmppIdsBase, Base):
280
+ __tablename__ = "group_msg"
281
+ __table_args__ = (Index("ix_group_msg_legacy_id", "legacy_id", "foreign_key"),)
282
+ foreign_key: Mapped[int] = mapped_column(ForeignKey("room.id"), nullable=False)
285
283
 
286
- type: Mapped[XmppToLegacyEnum] = mapped_column(nullable=False)
284
+
285
+ class DirectThreads(_LegacyToXmppIdsBase, Base):
286
+ __tablename__ = "direct_thread"
287
+ __table_args__ = (Index("ix_direct_direct_thread_id", "legacy_id", "foreign_key"),)
288
+ foreign_key: Mapped[int] = mapped_column(ForeignKey("contact.id"), nullable=False)
289
+
290
+
291
+ class GroupThreads(_LegacyToXmppIdsBase, Base):
292
+ __tablename__ = "group_thread"
293
+ __table_args__ = (Index("ix_direct_group_thread_id", "legacy_id", "foreign_key"),)
294
+ foreign_key: Mapped[int] = mapped_column(ForeignKey("room.id"), nullable=False)
287
295
 
288
296
 
289
297
  class Attachment(Base):
@@ -303,81 +311,26 @@ class Attachment(Base):
303
311
  sfs: Mapped[Optional[str]] = mapped_column()
304
312
 
305
313
 
306
- class LegacyIdsMulti(Base):
307
- """
308
- Legacy messages with multiple attachments are split as several XMPP messages,
309
- this table and the next maps a single legacy ID to multiple XMPP IDs.
310
- """
311
-
312
- __tablename__ = "legacy_ids_multi"
313
- __table_args__ = (
314
- Index(
315
- "legacy_ids_multi_user_account_id_legacy_id",
316
- "user_account_id",
317
- "legacy_id",
318
- unique=True,
319
- ),
320
- )
321
- id: Mapped[int] = mapped_column(primary_key=True)
322
- user_account_id: Mapped[int] = mapped_column(ForeignKey("user_account.id"))
323
-
324
- legacy_id: Mapped[str] = mapped_column(nullable=False)
325
- xmpp_ids: Mapped[list["XmppIdsMulti"]] = relationship(
326
- back_populates="legacy_ids_multi", cascade="all, delete-orphan"
327
- )
328
-
329
-
330
- class XmppIdsMulti(Base):
331
- __tablename__ = "xmpp_ids_multi"
332
- __table_args__ = (
333
- Index(
334
- "legacy_ids_multi_user_account_id_xmpp_id",
335
- "user_account_id",
336
- "xmpp_id",
337
- unique=True,
338
- ),
339
- )
340
- id: Mapped[int] = mapped_column(primary_key=True)
341
- user_account_id: Mapped[int] = mapped_column(ForeignKey("user_account.id"))
342
-
343
- xmpp_id: Mapped[str] = mapped_column(nullable=False)
344
-
345
- legacy_ids_multi_id: Mapped[int] = mapped_column(ForeignKey("legacy_ids_multi.id"))
346
- legacy_ids_multi: Mapped[LegacyIdsMulti] = relationship(back_populates="xmpp_ids")
347
-
348
-
349
- participant_hats = sa.Table(
350
- "participant_hats",
351
- Base.metadata,
352
- sa.Column("participant_id", ForeignKey("participant.id"), primary_key=True),
353
- sa.Column("hat_id", ForeignKey("hat.id"), primary_key=True),
354
- )
355
-
356
-
357
- class Hat(Base):
358
- __tablename__ = "hat"
359
- __table_args__ = (UniqueConstraint("title", "uri"),)
360
-
361
- id: Mapped[int] = mapped_column(primary_key=True)
362
- title: Mapped[str] = mapped_column()
363
- uri: Mapped[str] = mapped_column()
364
- participants: Mapped[list["Participant"]] = relationship(
365
- secondary=participant_hats, back_populates="hats"
366
- )
367
-
368
-
369
314
  class Participant(Base):
370
315
  __tablename__ = "participant"
316
+ __table_args__ = (
317
+ UniqueConstraint("room_id", "resource"),
318
+ UniqueConstraint("room_id", "contact_id"),
319
+ )
371
320
 
372
321
  id: Mapped[int] = mapped_column(primary_key=True)
373
322
 
374
323
  room_id: Mapped[int] = mapped_column(ForeignKey("room.id"), nullable=False)
375
324
  room: Mapped[Room] = relationship(
376
- back_populates="participants", primaryjoin=Room.id == room_id
325
+ lazy=False, back_populates="participants", primaryjoin=Room.id == room_id
377
326
  )
378
327
 
379
- contact_id: Mapped[int] = mapped_column(ForeignKey("contact.id"), nullable=True)
380
- contact: Mapped[Contact] = relationship(lazy=False, back_populates="participants")
328
+ contact_id: Mapped[Optional[int]] = mapped_column(
329
+ ForeignKey("contact.id"), nullable=True
330
+ )
331
+ contact: Mapped[Optional[Contact]] = relationship(
332
+ lazy=False, back_populates="participants"
333
+ )
381
334
 
382
335
  is_user: Mapped[bool] = mapped_column(default=False)
383
336
 
@@ -386,13 +339,11 @@ class Participant(Base):
386
339
 
387
340
  presence_sent: Mapped[bool] = mapped_column(default=False)
388
341
 
389
- resource: Mapped[Optional[str]] = mapped_column(default=None)
390
- nickname: Mapped[str] = mapped_column(nullable=True, default=None)
391
- nickname_no_illegal: Mapped[str] = mapped_column(nullable=True, default=None)
342
+ resource: Mapped[str] = mapped_column(nullable=False)
343
+ nickname: Mapped[str] = mapped_column(nullable=False, default=None)
344
+ nickname_no_illegal: Mapped[str] = mapped_column(nullable=False, default=None)
392
345
 
393
- hats: Mapped[list["Hat"]] = relationship(
394
- secondary=participant_hats, back_populates="participants"
395
- )
346
+ hats: Mapped[list[tuple[str, str]]] = mapped_column(JSON, default=list)
396
347
 
397
348
  extra_attributes: Mapped[Optional[JSONSerializable]] = mapped_column(default=None)
398
349