slidge 0.1.3__py3-none-any.whl → 0.2.0a0__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. slidge/__init__.py +3 -5
  2. slidge/__main__.py +2 -196
  3. slidge/__version__.py +5 -0
  4. slidge/command/adhoc.py +8 -1
  5. slidge/command/admin.py +5 -6
  6. slidge/command/base.py +1 -2
  7. slidge/command/register.py +32 -16
  8. slidge/command/user.py +85 -5
  9. slidge/contact/contact.py +93 -31
  10. slidge/contact/roster.py +54 -39
  11. slidge/core/config.py +13 -7
  12. slidge/core/gateway/base.py +139 -34
  13. slidge/core/gateway/disco.py +2 -4
  14. slidge/core/gateway/mam.py +1 -4
  15. slidge/core/gateway/ping.py +2 -3
  16. slidge/core/gateway/presence.py +1 -1
  17. slidge/core/gateway/registration.py +32 -21
  18. slidge/core/gateway/search.py +3 -5
  19. slidge/core/gateway/session_dispatcher.py +100 -51
  20. slidge/core/gateway/vcard_temp.py +6 -4
  21. slidge/core/mixins/__init__.py +11 -1
  22. slidge/core/mixins/attachment.py +15 -10
  23. slidge/core/mixins/avatar.py +66 -18
  24. slidge/core/mixins/base.py +8 -2
  25. slidge/core/mixins/message.py +11 -7
  26. slidge/core/mixins/message_maker.py +17 -9
  27. slidge/core/mixins/presence.py +14 -4
  28. slidge/core/pubsub.py +54 -212
  29. slidge/core/session.py +65 -33
  30. slidge/db/__init__.py +4 -0
  31. slidge/db/alembic/env.py +64 -0
  32. slidge/db/alembic/script.py.mako +26 -0
  33. slidge/db/alembic/versions/09f27f098baa_add_missing_attributes_in_room.py +36 -0
  34. slidge/db/alembic/versions/29f5280c61aa_store_subject_setter_in_room.py +37 -0
  35. slidge/db/alembic/versions/8d2ced764698_rely_on_db_to_store_contacts_rooms_and_.py +133 -0
  36. slidge/db/alembic/versions/aa9d82a7f6ef_db_creation.py +76 -0
  37. slidge/db/alembic/versions/b33993e87db3_move_everything_to_persistent_db.py +214 -0
  38. slidge/db/alembic/versions/e91195719c2c_store_users_avatars_persistently.py +26 -0
  39. slidge/db/avatar.py +224 -0
  40. slidge/db/meta.py +65 -0
  41. slidge/db/models.py +365 -0
  42. slidge/db/store.py +976 -0
  43. slidge/group/archive.py +13 -14
  44. slidge/group/bookmarks.py +59 -56
  45. slidge/group/participant.py +77 -25
  46. slidge/group/room.py +242 -142
  47. slidge/main.py +201 -0
  48. slidge/migration.py +30 -0
  49. slidge/slixfix/__init__.py +35 -2
  50. slidge/slixfix/roster.py +11 -4
  51. slidge/slixfix/xep_0292/vcard4.py +1 -0
  52. slidge/util/db.py +1 -47
  53. slidge/util/test.py +21 -4
  54. slidge/util/types.py +24 -4
  55. {slidge-0.1.3.dist-info → slidge-0.2.0a0.dist-info}/METADATA +3 -1
  56. slidge-0.2.0a0.dist-info/RECORD +108 -0
  57. slidge/core/cache.py +0 -183
  58. slidge/util/schema.sql +0 -126
  59. slidge/util/sql.py +0 -508
  60. slidge-0.1.3.dist-info/RECORD +0 -96
  61. {slidge-0.1.3.dist-info → slidge-0.2.0a0.dist-info}/LICENSE +0 -0
  62. {slidge-0.1.3.dist-info → slidge-0.2.0a0.dist-info}/WHEEL +0 -0
  63. {slidge-0.1.3.dist-info → slidge-0.2.0a0.dist-info}/entry_points.txt +0 -0
slidge/db/models.py ADDED
@@ -0,0 +1,365 @@
1
+ import warnings
2
+ from datetime import datetime
3
+ from enum import IntEnum
4
+ from typing import Optional
5
+
6
+ import sqlalchemy as sa
7
+ from slixmpp import JID
8
+ from slixmpp.types import MucAffiliation, MucRole
9
+ from sqlalchemy import ForeignKey, Index, UniqueConstraint
10
+ from sqlalchemy.orm import Mapped, mapped_column, relationship
11
+
12
+ from ..util.types import MucType
13
+ from .meta import Base, JSONSerializable, JSONSerializableTypes
14
+
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
+ class GatewayUser(Base):
28
+ """
29
+ A user, registered to the gateway component.
30
+ """
31
+
32
+ __tablename__ = "user_account"
33
+ id: Mapped[int] = mapped_column(primary_key=True)
34
+ jid: Mapped[JID] = mapped_column(unique=True)
35
+ registration_date: Mapped[datetime] = mapped_column(
36
+ sa.DateTime, server_default=sa.func.now()
37
+ )
38
+
39
+ legacy_module_data: Mapped[JSONSerializable] = mapped_column(default={})
40
+ """
41
+ Arbitrary non-relational data that legacy modules can use
42
+ """
43
+ preferences: Mapped[JSONSerializable] = mapped_column(default={})
44
+ avatar_hash: Mapped[Optional[str]] = mapped_column(default=None)
45
+ """
46
+ Hash of the user's avatar, to avoid re-publishing the same avatar on the
47
+ legacy network
48
+ """
49
+
50
+ contacts: Mapped[list["Contact"]] = relationship(
51
+ back_populates="user", cascade="all, delete-orphan"
52
+ )
53
+ rooms: Mapped[list["Room"]] = relationship(
54
+ back_populates="user", cascade="all, delete-orphan"
55
+ )
56
+ xmpp_to_legacy: Mapped[list["XmppToLegacyIds"]] = relationship(
57
+ cascade="all, delete-orphan"
58
+ )
59
+ attachments: Mapped[list["Attachment"]] = relationship(cascade="all, delete-orphan")
60
+ multi_legacy: Mapped[list["LegacyIdsMulti"]] = relationship(
61
+ cascade="all, delete-orphan"
62
+ )
63
+ multi_xmpp: Mapped[list["XmppIdsMulti"]] = relationship(
64
+ cascade="all, delete-orphan"
65
+ )
66
+
67
+ def __repr__(self) -> str:
68
+ return f"User(id={self.id!r}, jid={self.jid!r})"
69
+
70
+ def get(self, field: str, default: str = "") -> JSONSerializableTypes:
71
+ # """
72
+ # Get fields from the registration form (required to comply with slixmpp backend protocol)
73
+ #
74
+ # :param field: Name of the field
75
+ # :param default: Default value to return if the field is not present
76
+ #
77
+ # :return: Value of the field
78
+ # """
79
+ return self.legacy_module_data.get(field, default)
80
+
81
+ @property
82
+ def registration_form(self) -> dict:
83
+ # Kept for retrocompat, should be
84
+ # FIXME: delete me
85
+ warnings.warn(
86
+ "GatewayUser.registration_form is deprecated.", DeprecationWarning
87
+ )
88
+ return self.legacy_module_data
89
+
90
+
91
+ class Avatar(Base):
92
+ """
93
+ Avatars of contacts, rooms and participants.
94
+
95
+ To comply with XEPs, we convert them all to PNG before storing them.
96
+ """
97
+
98
+ __tablename__ = "avatar"
99
+
100
+ id: Mapped[int] = mapped_column(primary_key=True)
101
+
102
+ filename: Mapped[str] = mapped_column(unique=True)
103
+ hash: Mapped[str] = mapped_column(unique=True)
104
+ height: Mapped[int] = mapped_column()
105
+ width: Mapped[int] = mapped_column()
106
+
107
+ # legacy network-wide unique identifier for the avatar
108
+ legacy_id: Mapped[Optional[str]] = mapped_column(unique=True, nullable=True)
109
+
110
+ # this is only used when avatars are available as HTTP URLs and do not
111
+ # have a legacy_id
112
+ url: Mapped[Optional[str]] = mapped_column(default=None)
113
+ etag: Mapped[Optional[str]] = mapped_column(default=None)
114
+ last_modified: Mapped[Optional[str]] = mapped_column(default=None)
115
+
116
+ contacts: Mapped[list["Contact"]] = relationship(back_populates="avatar")
117
+ rooms: Mapped[list["Room"]] = relationship(back_populates="avatar")
118
+
119
+
120
+ class Contact(Base):
121
+ """
122
+ Legacy contacts
123
+ """
124
+
125
+ __tablename__ = "contact"
126
+ __table_args__ = (
127
+ UniqueConstraint("user_account_id", "legacy_id"),
128
+ UniqueConstraint("user_account_id", "jid"),
129
+ )
130
+
131
+ id: Mapped[int] = mapped_column(primary_key=True)
132
+ user_account_id: Mapped[int] = mapped_column(ForeignKey("user_account.id"))
133
+ user: Mapped[GatewayUser] = relationship(back_populates="contacts")
134
+ legacy_id: Mapped[str] = mapped_column(nullable=False)
135
+
136
+ jid: Mapped[JID] = mapped_column()
137
+
138
+ avatar_id: Mapped[int] = mapped_column(ForeignKey("avatar.id"), nullable=True)
139
+ avatar: Mapped[Avatar] = relationship(back_populates="contacts")
140
+
141
+ nick: Mapped[Optional[str]] = mapped_column(nullable=True)
142
+
143
+ cached_presence: Mapped[bool] = mapped_column(default=False)
144
+ last_seen: Mapped[Optional[datetime]] = mapped_column(nullable=True)
145
+ ptype: Mapped[Optional[str]] = mapped_column(nullable=True)
146
+ pstatus: Mapped[Optional[str]] = mapped_column(nullable=True)
147
+ pshow: Mapped[Optional[str]] = mapped_column(nullable=True)
148
+
149
+ is_friend: Mapped[bool] = mapped_column(default=False)
150
+ added_to_roster: Mapped[bool] = mapped_column(default=False)
151
+ sent_order: Mapped[list["ContactSent"]] = relationship(back_populates="contact")
152
+
153
+ extra_attributes: Mapped[Optional[JSONSerializable]] = mapped_column(
154
+ default=None, nullable=True
155
+ )
156
+ updated: Mapped[bool] = mapped_column(default=False)
157
+
158
+ participants: Mapped[list["Participant"]] = relationship(back_populates="contact")
159
+
160
+
161
+ class ContactSent(Base):
162
+ """
163
+ Keep track of XMPP msg ids sent by a specific contact for networks in which
164
+ all messages need to be marked as read.
165
+
166
+ (XMPP displayed markers convey a "read up to here" semantic.)
167
+ """
168
+
169
+ __tablename__ = "contact_sent"
170
+ __table_args__ = (UniqueConstraint("contact_id", "msg_id"),)
171
+
172
+ id: Mapped[int] = mapped_column(primary_key=True)
173
+ contact_id: Mapped[int] = mapped_column(ForeignKey("contact.id"))
174
+ contact: Mapped[Contact] = relationship(back_populates="sent_order")
175
+ msg_id: Mapped[str] = mapped_column()
176
+
177
+
178
+ class Room(Base):
179
+ """
180
+ Legacy room
181
+ """
182
+
183
+ __tablename__ = "room"
184
+ id: Mapped[int] = mapped_column(primary_key=True)
185
+ user_account_id: Mapped[int] = mapped_column(ForeignKey("user_account.id"))
186
+ user: Mapped[GatewayUser] = relationship(back_populates="rooms")
187
+ legacy_id: Mapped[str] = mapped_column(unique=True, nullable=False)
188
+
189
+ jid: Mapped[JID] = mapped_column(unique=True)
190
+
191
+ avatar_id: Mapped[int] = mapped_column(ForeignKey("avatar.id"), nullable=True)
192
+ avatar: Mapped[Avatar] = relationship(back_populates="rooms")
193
+
194
+ name: Mapped[Optional[str]] = mapped_column(nullable=True)
195
+ description: Mapped[Optional[str]] = mapped_column(nullable=True)
196
+ subject: Mapped[Optional[str]] = mapped_column(nullable=True)
197
+ subject_date: Mapped[Optional[datetime]] = mapped_column(nullable=True)
198
+ subject_setter_id: Mapped[Optional[int]] = mapped_column(
199
+ ForeignKey("participant.id"), nullable=True
200
+ )
201
+ subject_setter: Mapped["Participant"] = relationship(
202
+ foreign_keys="Room.subject_setter_id"
203
+ )
204
+
205
+ n_participants: Mapped[Optional[int]] = mapped_column(default=None)
206
+
207
+ muc_type: Mapped[Optional[MucType]] = mapped_column(default=MucType.GROUP)
208
+
209
+ user_resources: Mapped[Optional[str]] = mapped_column(nullable=True)
210
+
211
+ participants_filled: Mapped[bool] = mapped_column(default=False)
212
+
213
+ extra_attributes: Mapped[Optional[JSONSerializable]] = mapped_column(default=None)
214
+ updated: Mapped[bool] = mapped_column(default=False)
215
+
216
+ participants: Mapped[list["Participant"]] = relationship(
217
+ back_populates="room", primaryjoin="Participant.room_id == Room.id"
218
+ )
219
+
220
+
221
+ class ArchivedMessage(Base):
222
+ """
223
+ Messages of rooms, that we store to act as a MAM server
224
+ """
225
+
226
+ __tablename__ = "mam"
227
+ __table_args__ = (UniqueConstraint("room_id", "stanza_id"),)
228
+
229
+ id: Mapped[int] = mapped_column(primary_key=True)
230
+ room_id: Mapped[int] = mapped_column(ForeignKey("room.id"), nullable=False)
231
+
232
+ stanza_id: Mapped[str] = mapped_column(nullable=False)
233
+ timestamp: Mapped[datetime] = mapped_column(nullable=False)
234
+ author_jid: Mapped[JID] = mapped_column(nullable=False)
235
+
236
+ stanza: Mapped[str] = mapped_column(nullable=False)
237
+
238
+
239
+ class XmppToLegacyIds(Base):
240
+ """
241
+ XMPP-client generated IDs, and mapping to the corresponding legacy IDs
242
+ """
243
+
244
+ __tablename__ = "xmpp_to_legacy_ids"
245
+ __table_args__ = (
246
+ Index("xmpp_legacy", "user_account_id", "xmpp_id", "legacy_id", unique=True),
247
+ )
248
+ id: Mapped[int] = mapped_column(primary_key=True)
249
+ user_account_id: Mapped[int] = mapped_column(ForeignKey("user_account.id"))
250
+ user: Mapped[GatewayUser] = relationship(back_populates="xmpp_to_legacy")
251
+
252
+ xmpp_id: Mapped[str] = mapped_column(nullable=False)
253
+ legacy_id: Mapped[str] = mapped_column(nullable=False)
254
+
255
+ type: Mapped[XmppToLegacyEnum] = mapped_column(nullable=False)
256
+
257
+
258
+ class Attachment(Base):
259
+ """
260
+ Legacy attachments
261
+ """
262
+
263
+ __tablename__ = "attachment"
264
+
265
+ id: Mapped[int] = mapped_column(primary_key=True)
266
+ user_account_id: Mapped[int] = mapped_column(ForeignKey("user_account.id"))
267
+ user: Mapped[GatewayUser] = relationship(back_populates="attachments")
268
+
269
+ legacy_file_id: Mapped[Optional[str]] = mapped_column(index=True, nullable=True)
270
+ url: Mapped[str] = mapped_column(index=True, nullable=False)
271
+ sims: Mapped[Optional[str]] = mapped_column()
272
+ sfs: Mapped[Optional[str]] = mapped_column()
273
+
274
+
275
+ class LegacyIdsMulti(Base):
276
+ """
277
+ Legacy messages with multiple attachments are split as several XMPP messages,
278
+ this table and the next maps a single legacy ID to multiple XMPP IDs.
279
+ """
280
+
281
+ __tablename__ = "legacy_ids_multi"
282
+ __table_args__ = (
283
+ Index(
284
+ "legacy_ids_multi_user_account_id_legacy_id",
285
+ "user_account_id",
286
+ "legacy_id",
287
+ unique=True,
288
+ ),
289
+ )
290
+ id: Mapped[int] = mapped_column(primary_key=True)
291
+ user_account_id: Mapped[int] = mapped_column(ForeignKey("user_account.id"))
292
+
293
+ legacy_id: Mapped[str] = mapped_column(nullable=False)
294
+ xmpp_ids: Mapped[list["XmppIdsMulti"]] = relationship(
295
+ back_populates="legacy_ids_multi"
296
+ )
297
+
298
+
299
+ class XmppIdsMulti(Base):
300
+ __tablename__ = "xmpp_ids_multi"
301
+ __table_args__ = (
302
+ Index(
303
+ "legacy_ids_multi_user_account_id_xmpp_id",
304
+ "user_account_id",
305
+ "xmpp_id",
306
+ unique=True,
307
+ ),
308
+ )
309
+ id: Mapped[int] = mapped_column(primary_key=True)
310
+ user_account_id: Mapped[int] = mapped_column(ForeignKey("user_account.id"))
311
+
312
+ xmpp_id: Mapped[str] = mapped_column(nullable=False)
313
+
314
+ legacy_ids_multi_id: Mapped[int] = mapped_column(ForeignKey("legacy_ids_multi.id"))
315
+ legacy_ids_multi: Mapped[LegacyIdsMulti] = relationship(back_populates="xmpp_ids")
316
+
317
+
318
+ participant_hats = sa.Table(
319
+ "participant_hats",
320
+ Base.metadata,
321
+ sa.Column("participant_id", ForeignKey("participant.id"), primary_key=True),
322
+ sa.Column("hat_id", ForeignKey("hat.id"), primary_key=True),
323
+ )
324
+
325
+
326
+ class Hat(Base):
327
+ __tablename__ = "hat"
328
+ __table_args__ = (UniqueConstraint("title", "uri"),)
329
+
330
+ id: Mapped[int] = mapped_column(primary_key=True)
331
+ title: Mapped[str] = mapped_column()
332
+ uri: Mapped[str] = mapped_column()
333
+ participants: Mapped[list["Participant"]] = relationship(
334
+ secondary=participant_hats, back_populates="hats"
335
+ )
336
+
337
+
338
+ class Participant(Base):
339
+ __tablename__ = "participant"
340
+
341
+ id: Mapped[int] = mapped_column(primary_key=True)
342
+
343
+ room_id: Mapped[int] = mapped_column(ForeignKey("room.id"), nullable=False)
344
+ room: Mapped[Room] = relationship(
345
+ back_populates="participants", primaryjoin=Room.id == room_id
346
+ )
347
+
348
+ contact_id: Mapped[int] = mapped_column(ForeignKey("contact.id"), nullable=True)
349
+ contact: Mapped[Contact] = relationship(back_populates="participants")
350
+
351
+ is_user: Mapped[bool] = mapped_column(default=False)
352
+
353
+ affiliation: Mapped[MucAffiliation] = mapped_column(default="member")
354
+ role: Mapped[MucRole] = mapped_column(default="participant")
355
+
356
+ presence_sent: Mapped[bool] = mapped_column(default=False)
357
+
358
+ resource: Mapped[Optional[str]] = mapped_column(default=None)
359
+ nickname: Mapped[str] = mapped_column(nullable=True, default=None)
360
+
361
+ hats: Mapped[list["Hat"]] = relationship(
362
+ secondary=participant_hats, back_populates="participants"
363
+ )
364
+
365
+ extra_attributes: Mapped[Optional[JSONSerializable]] = mapped_column(default=None)