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