slidge 0.1.2__py3-none-any.whl → 0.2.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.
- slidge/__init__.py +3 -5
- slidge/__main__.py +2 -196
- slidge/__version__.py +5 -0
- slidge/command/adhoc.py +8 -1
- slidge/command/admin.py +5 -6
- slidge/command/base.py +1 -2
- slidge/command/register.py +32 -16
- slidge/command/user.py +85 -5
- slidge/contact/contact.py +93 -31
- slidge/contact/roster.py +54 -39
- slidge/core/config.py +13 -7
- slidge/core/gateway/base.py +139 -34
- slidge/core/gateway/disco.py +2 -4
- slidge/core/gateway/mam.py +1 -4
- slidge/core/gateway/ping.py +2 -3
- slidge/core/gateway/presence.py +1 -1
- slidge/core/gateway/registration.py +32 -21
- slidge/core/gateway/search.py +3 -5
- slidge/core/gateway/session_dispatcher.py +109 -51
- slidge/core/gateway/vcard_temp.py +6 -4
- slidge/core/mixins/__init__.py +11 -1
- slidge/core/mixins/attachment.py +15 -10
- slidge/core/mixins/avatar.py +66 -18
- slidge/core/mixins/base.py +8 -2
- slidge/core/mixins/message.py +11 -7
- slidge/core/mixins/message_maker.py +17 -9
- slidge/core/mixins/presence.py +14 -4
- slidge/core/pubsub.py +54 -212
- slidge/core/session.py +65 -33
- slidge/db/__init__.py +4 -0
- slidge/db/alembic/env.py +64 -0
- slidge/db/alembic/script.py.mako +26 -0
- slidge/db/alembic/versions/09f27f098baa_add_missing_attributes_in_room.py +36 -0
- slidge/db/alembic/versions/29f5280c61aa_store_subject_setter_in_room.py +37 -0
- slidge/db/alembic/versions/8d2ced764698_rely_on_db_to_store_contacts_rooms_and_.py +133 -0
- slidge/db/alembic/versions/aa9d82a7f6ef_db_creation.py +76 -0
- slidge/db/alembic/versions/b33993e87db3_move_everything_to_persistent_db.py +214 -0
- slidge/db/alembic/versions/e91195719c2c_store_users_avatars_persistently.py +26 -0
- slidge/db/avatar.py +224 -0
- slidge/db/meta.py +65 -0
- slidge/db/models.py +365 -0
- slidge/db/store.py +976 -0
- slidge/group/archive.py +13 -14
- slidge/group/bookmarks.py +59 -56
- slidge/group/participant.py +81 -29
- slidge/group/room.py +242 -142
- slidge/main.py +201 -0
- slidge/migration.py +30 -0
- slidge/slixfix/__init__.py +35 -2
- slidge/slixfix/roster.py +11 -4
- slidge/slixfix/xep_0292/vcard4.py +1 -0
- slidge/util/db.py +1 -47
- slidge/util/test.py +21 -4
- slidge/util/types.py +24 -4
- {slidge-0.1.2.dist-info → slidge-0.2.0a0.dist-info}/METADATA +3 -1
- slidge-0.2.0a0.dist-info/RECORD +108 -0
- slidge/core/cache.py +0 -183
- slidge/util/schema.sql +0 -126
- slidge/util/sql.py +0 -508
- slidge-0.1.2.dist-info/RECORD +0 -96
- {slidge-0.1.2.dist-info → slidge-0.2.0a0.dist-info}/LICENSE +0 -0
- {slidge-0.1.2.dist-info → slidge-0.2.0a0.dist-info}/WHEEL +0 -0
- {slidge-0.1.2.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)
|