slidge 0.1.2__py3-none-any.whl → 0.2.0__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 -197
- slidge/__version__.py +5 -0
- slidge/command/adhoc.py +40 -17
- slidge/command/admin.py +24 -12
- slidge/command/base.py +10 -8
- slidge/command/categories.py +13 -3
- slidge/command/chat_command.py +29 -2
- slidge/command/register.py +32 -16
- slidge/command/user.py +106 -13
- slidge/contact/contact.py +254 -50
- slidge/contact/roster.py +124 -53
- slidge/core/config.py +19 -13
- slidge/core/dispatcher/__init__.py +3 -0
- slidge/core/{gateway → dispatcher}/caps.py +12 -8
- slidge/core/{gateway → dispatcher}/disco.py +10 -18
- slidge/core/dispatcher/message/__init__.py +10 -0
- slidge/core/dispatcher/message/chat_state.py +40 -0
- slidge/core/dispatcher/message/marker.py +62 -0
- slidge/core/dispatcher/message/message.py +397 -0
- slidge/core/dispatcher/muc/__init__.py +12 -0
- slidge/core/dispatcher/muc/admin.py +98 -0
- slidge/core/{gateway → dispatcher/muc}/mam.py +25 -17
- slidge/core/dispatcher/muc/misc.py +121 -0
- slidge/core/dispatcher/muc/owner.py +96 -0
- slidge/core/{gateway → dispatcher/muc}/ping.py +11 -17
- slidge/core/dispatcher/presence.py +176 -0
- slidge/core/dispatcher/registration.py +85 -0
- slidge/core/{gateway → dispatcher}/search.py +9 -16
- slidge/core/dispatcher/session_dispatcher.py +84 -0
- slidge/core/dispatcher/util.py +174 -0
- slidge/core/{gateway/vcard_temp.py → dispatcher/vcard.py} +35 -19
- slidge/core/{gateway/base.py → gateway.py} +176 -153
- slidge/core/mixins/__init__.py +11 -1
- slidge/core/mixins/attachment.py +106 -67
- slidge/core/mixins/avatar.py +94 -25
- slidge/core/mixins/base.py +10 -4
- slidge/core/mixins/db.py +18 -0
- slidge/core/mixins/disco.py +0 -10
- slidge/core/mixins/lock.py +10 -8
- slidge/core/mixins/message.py +11 -195
- slidge/core/mixins/message_maker.py +17 -9
- slidge/core/mixins/message_text.py +211 -0
- slidge/core/mixins/presence.py +17 -4
- slidge/core/pubsub.py +114 -288
- slidge/core/session.py +101 -40
- slidge/db/__init__.py +4 -0
- slidge/db/alembic/__init__.py +0 -0
- slidge/db/alembic/env.py +64 -0
- slidge/db/alembic/old_user_store.py +183 -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/15b0bd83407a_remove_bogus_unique_constraints_on_room_.py +85 -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/3071e0fa69d4_add_contact_client_type.py +52 -0
- slidge/db/alembic/versions/45c24cc73c91_add_bob.py +42 -0
- slidge/db/alembic/versions/5bd48bfdffa2_lift_room_legacy_id_constraint.py +61 -0
- slidge/db/alembic/versions/82a4af84b679_add_muc_history_filled.py +48 -0
- slidge/db/alembic/versions/8b993243a536_add_vcard_content_to_contact_table.py +43 -0
- slidge/db/alembic/versions/8d2ced764698_rely_on_db_to_store_contacts_rooms_and_.py +139 -0
- slidge/db/alembic/versions/aa9d82a7f6ef_db_creation.py +101 -0
- slidge/db/alembic/versions/abba1ae0edb3_store_avatar_legacy_id_in_the_contact_.py +79 -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 +52 -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 +205 -0
- slidge/db/meta.py +72 -0
- slidge/db/models.py +405 -0
- slidge/db/store.py +1257 -0
- slidge/group/archive.py +58 -14
- slidge/group/bookmarks.py +89 -65
- slidge/group/participant.py +111 -44
- slidge/group/room.py +402 -213
- slidge/main.py +202 -0
- slidge/migration.py +45 -1
- slidge/slixfix/__init__.py +31 -1
- slidge/{core/gateway → slixfix}/delivery_receipt.py +1 -1
- slidge/slixfix/roster.py +13 -4
- slidge/slixfix/xep_0292/vcard4.py +1 -87
- slidge/util/archive_msg.py +2 -1
- slidge/util/db.py +4 -228
- slidge/util/test.py +91 -4
- slidge/util/types.py +39 -4
- slidge/util/util.py +45 -2
- {slidge-0.1.2.dist-info → slidge-0.2.0.dist-info}/METADATA +10 -5
- slidge-0.2.0.dist-info/RECORD +131 -0
- slidge-0.2.0.dist-info/entry_points.txt +3 -0
- slidge/core/cache.py +0 -183
- slidge/core/gateway/__init__.py +0 -3
- slidge/core/gateway/muc_admin.py +0 -35
- slidge/core/gateway/presence.py +0 -95
- slidge/core/gateway/registration.py +0 -53
- slidge/core/gateway/session_dispatcher.py +0 -795
- 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/entry_points.txt +0 -3
- {slidge-0.1.2.dist-info → slidge-0.2.0.dist-info}/LICENSE +0 -0
- {slidge-0.1.2.dist-info → slidge-0.2.0.dist-info}/WHEEL +0 -0
slidge/db/models.py
ADDED
@@ -0,0 +1,405 @@
|
|
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 ClientType, 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
|
+
# this is only used when avatars are available as HTTP URLs and do not
|
118
|
+
# have a legacy_id
|
119
|
+
url: Mapped[Optional[str]] = mapped_column(default=None)
|
120
|
+
etag: Mapped[Optional[str]] = mapped_column(default=None)
|
121
|
+
last_modified: Mapped[Optional[str]] = mapped_column(default=None)
|
122
|
+
|
123
|
+
contacts: Mapped[list["Contact"]] = relationship(back_populates="avatar")
|
124
|
+
rooms: Mapped[list["Room"]] = relationship(back_populates="avatar")
|
125
|
+
|
126
|
+
|
127
|
+
class Contact(Base):
|
128
|
+
"""
|
129
|
+
Legacy contacts
|
130
|
+
"""
|
131
|
+
|
132
|
+
__tablename__ = "contact"
|
133
|
+
__table_args__ = (
|
134
|
+
UniqueConstraint("user_account_id", "legacy_id"),
|
135
|
+
UniqueConstraint("user_account_id", "jid"),
|
136
|
+
)
|
137
|
+
|
138
|
+
id: Mapped[int] = mapped_column(primary_key=True)
|
139
|
+
user_account_id: Mapped[int] = mapped_column(ForeignKey("user_account.id"))
|
140
|
+
user: Mapped[GatewayUser] = relationship(back_populates="contacts")
|
141
|
+
legacy_id: Mapped[str] = mapped_column(nullable=False)
|
142
|
+
|
143
|
+
jid: Mapped[JID] = mapped_column()
|
144
|
+
|
145
|
+
avatar_id: Mapped[int] = mapped_column(ForeignKey("avatar.id"), nullable=True)
|
146
|
+
avatar: Mapped[Avatar] = relationship(back_populates="contacts")
|
147
|
+
|
148
|
+
nick: Mapped[Optional[str]] = mapped_column(nullable=True)
|
149
|
+
|
150
|
+
cached_presence: Mapped[bool] = mapped_column(default=False)
|
151
|
+
last_seen: Mapped[Optional[datetime]] = mapped_column(nullable=True)
|
152
|
+
ptype: Mapped[Optional[str]] = mapped_column(nullable=True)
|
153
|
+
pstatus: Mapped[Optional[str]] = mapped_column(nullable=True)
|
154
|
+
pshow: Mapped[Optional[str]] = mapped_column(nullable=True)
|
155
|
+
caps_ver: Mapped[Optional[str]] = mapped_column(nullable=True)
|
156
|
+
|
157
|
+
is_friend: Mapped[bool] = mapped_column(default=False)
|
158
|
+
added_to_roster: Mapped[bool] = mapped_column(default=False)
|
159
|
+
sent_order: Mapped[list["ContactSent"]] = relationship(
|
160
|
+
back_populates="contact", cascade="all, delete-orphan"
|
161
|
+
)
|
162
|
+
|
163
|
+
extra_attributes: Mapped[Optional[JSONSerializable]] = mapped_column(
|
164
|
+
default=None, nullable=True
|
165
|
+
)
|
166
|
+
updated: Mapped[bool] = mapped_column(default=False)
|
167
|
+
|
168
|
+
vcard: Mapped[Optional[str]] = mapped_column()
|
169
|
+
vcard_fetched: Mapped[bool] = mapped_column(default=False)
|
170
|
+
|
171
|
+
participants: Mapped[list["Participant"]] = relationship(back_populates="contact")
|
172
|
+
|
173
|
+
avatar_legacy_id: Mapped[Optional[str]] = mapped_column(nullable=True)
|
174
|
+
|
175
|
+
client_type: Mapped[ClientType] = mapped_column(nullable=False, default="pc")
|
176
|
+
|
177
|
+
|
178
|
+
class ContactSent(Base):
|
179
|
+
"""
|
180
|
+
Keep track of XMPP msg ids sent by a specific contact for networks in which
|
181
|
+
all messages need to be marked as read.
|
182
|
+
|
183
|
+
(XMPP displayed markers convey a "read up to here" semantic.)
|
184
|
+
"""
|
185
|
+
|
186
|
+
__tablename__ = "contact_sent"
|
187
|
+
__table_args__ = (UniqueConstraint("contact_id", "msg_id"),)
|
188
|
+
|
189
|
+
id: Mapped[int] = mapped_column(primary_key=True)
|
190
|
+
contact_id: Mapped[int] = mapped_column(ForeignKey("contact.id"))
|
191
|
+
contact: Mapped[Contact] = relationship(back_populates="sent_order")
|
192
|
+
msg_id: Mapped[str] = mapped_column()
|
193
|
+
|
194
|
+
|
195
|
+
class Room(Base):
|
196
|
+
"""
|
197
|
+
Legacy room
|
198
|
+
"""
|
199
|
+
|
200
|
+
__table_args__ = (
|
201
|
+
UniqueConstraint(
|
202
|
+
"user_account_id", "legacy_id", name="uq_room_user_account_id_legacy_id"
|
203
|
+
),
|
204
|
+
UniqueConstraint("user_account_id", "jid", name="uq_room_user_account_id_jid"),
|
205
|
+
)
|
206
|
+
|
207
|
+
__tablename__ = "room"
|
208
|
+
id: Mapped[int] = mapped_column(primary_key=True)
|
209
|
+
user_account_id: Mapped[int] = mapped_column(ForeignKey("user_account.id"))
|
210
|
+
user: Mapped[GatewayUser] = relationship(back_populates="rooms")
|
211
|
+
legacy_id: Mapped[str] = mapped_column(nullable=False)
|
212
|
+
|
213
|
+
jid: Mapped[JID] = mapped_column(nullable=False)
|
214
|
+
|
215
|
+
avatar_id: Mapped[int] = mapped_column(ForeignKey("avatar.id"), nullable=True)
|
216
|
+
avatar: Mapped[Avatar] = relationship(back_populates="rooms")
|
217
|
+
|
218
|
+
name: Mapped[Optional[str]] = mapped_column(nullable=True)
|
219
|
+
description: Mapped[Optional[str]] = mapped_column(nullable=True)
|
220
|
+
subject: Mapped[Optional[str]] = mapped_column(nullable=True)
|
221
|
+
subject_date: Mapped[Optional[datetime]] = mapped_column(nullable=True)
|
222
|
+
subject_setter: Mapped[Optional[str]] = mapped_column(nullable=True)
|
223
|
+
|
224
|
+
n_participants: Mapped[Optional[int]] = mapped_column(default=None)
|
225
|
+
|
226
|
+
muc_type: Mapped[Optional[MucType]] = mapped_column(default=MucType.GROUP)
|
227
|
+
|
228
|
+
user_nick: Mapped[Optional[str]] = mapped_column()
|
229
|
+
user_resources: Mapped[Optional[str]] = mapped_column(nullable=True)
|
230
|
+
|
231
|
+
participants_filled: Mapped[bool] = mapped_column(default=False)
|
232
|
+
history_filled: Mapped[bool] = mapped_column(default=False)
|
233
|
+
|
234
|
+
extra_attributes: Mapped[Optional[JSONSerializable]] = mapped_column(default=None)
|
235
|
+
updated: Mapped[bool] = mapped_column(default=False)
|
236
|
+
|
237
|
+
participants: Mapped[list["Participant"]] = relationship(
|
238
|
+
back_populates="room",
|
239
|
+
primaryjoin="Participant.room_id == Room.id",
|
240
|
+
cascade="all, delete-orphan",
|
241
|
+
)
|
242
|
+
|
243
|
+
avatar_legacy_id: Mapped[Optional[str]] = mapped_column(nullable=True)
|
244
|
+
|
245
|
+
|
246
|
+
class ArchivedMessage(Base):
|
247
|
+
"""
|
248
|
+
Messages of rooms, that we store to act as a MAM server
|
249
|
+
"""
|
250
|
+
|
251
|
+
__tablename__ = "mam"
|
252
|
+
__table_args__ = (UniqueConstraint("room_id", "stanza_id"),)
|
253
|
+
|
254
|
+
id: Mapped[int] = mapped_column(primary_key=True)
|
255
|
+
room_id: Mapped[int] = mapped_column(ForeignKey("room.id"), nullable=False)
|
256
|
+
|
257
|
+
stanza_id: Mapped[str] = mapped_column(nullable=False)
|
258
|
+
timestamp: Mapped[datetime] = mapped_column(nullable=False)
|
259
|
+
author_jid: Mapped[JID] = mapped_column(nullable=False)
|
260
|
+
source: Mapped[ArchivedMessageSource] = mapped_column(nullable=False)
|
261
|
+
legacy_id: Mapped[Optional[str]] = mapped_column(nullable=True)
|
262
|
+
|
263
|
+
stanza: Mapped[str] = mapped_column(nullable=False)
|
264
|
+
|
265
|
+
|
266
|
+
class XmppToLegacyIds(Base):
|
267
|
+
"""
|
268
|
+
XMPP-client generated IDs, and mapping to the corresponding legacy IDs
|
269
|
+
"""
|
270
|
+
|
271
|
+
__tablename__ = "xmpp_to_legacy_ids"
|
272
|
+
__table_args__ = (
|
273
|
+
Index("xmpp_legacy", "user_account_id", "xmpp_id", "legacy_id", unique=True),
|
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="xmpp_to_legacy")
|
278
|
+
|
279
|
+
xmpp_id: Mapped[str] = mapped_column(nullable=False)
|
280
|
+
legacy_id: Mapped[str] = mapped_column(nullable=False)
|
281
|
+
|
282
|
+
type: Mapped[XmppToLegacyEnum] = mapped_column(nullable=False)
|
283
|
+
|
284
|
+
|
285
|
+
class Attachment(Base):
|
286
|
+
"""
|
287
|
+
Legacy attachments
|
288
|
+
"""
|
289
|
+
|
290
|
+
__tablename__ = "attachment"
|
291
|
+
|
292
|
+
id: Mapped[int] = mapped_column(primary_key=True)
|
293
|
+
user_account_id: Mapped[int] = mapped_column(ForeignKey("user_account.id"))
|
294
|
+
user: Mapped[GatewayUser] = relationship(back_populates="attachments")
|
295
|
+
|
296
|
+
legacy_file_id: Mapped[Optional[str]] = mapped_column(index=True, nullable=True)
|
297
|
+
url: Mapped[str] = mapped_column(index=True, nullable=False)
|
298
|
+
sims: Mapped[Optional[str]] = mapped_column()
|
299
|
+
sfs: Mapped[Optional[str]] = mapped_column()
|
300
|
+
|
301
|
+
|
302
|
+
class LegacyIdsMulti(Base):
|
303
|
+
"""
|
304
|
+
Legacy messages with multiple attachments are split as several XMPP messages,
|
305
|
+
this table and the next maps a single legacy ID to multiple XMPP IDs.
|
306
|
+
"""
|
307
|
+
|
308
|
+
__tablename__ = "legacy_ids_multi"
|
309
|
+
__table_args__ = (
|
310
|
+
Index(
|
311
|
+
"legacy_ids_multi_user_account_id_legacy_id",
|
312
|
+
"user_account_id",
|
313
|
+
"legacy_id",
|
314
|
+
unique=True,
|
315
|
+
),
|
316
|
+
)
|
317
|
+
id: Mapped[int] = mapped_column(primary_key=True)
|
318
|
+
user_account_id: Mapped[int] = mapped_column(ForeignKey("user_account.id"))
|
319
|
+
|
320
|
+
legacy_id: Mapped[str] = mapped_column(nullable=False)
|
321
|
+
xmpp_ids: Mapped[list["XmppIdsMulti"]] = relationship(
|
322
|
+
back_populates="legacy_ids_multi", cascade="all, delete-orphan"
|
323
|
+
)
|
324
|
+
|
325
|
+
|
326
|
+
class XmppIdsMulti(Base):
|
327
|
+
__tablename__ = "xmpp_ids_multi"
|
328
|
+
__table_args__ = (
|
329
|
+
Index(
|
330
|
+
"legacy_ids_multi_user_account_id_xmpp_id",
|
331
|
+
"user_account_id",
|
332
|
+
"xmpp_id",
|
333
|
+
unique=True,
|
334
|
+
),
|
335
|
+
)
|
336
|
+
id: Mapped[int] = mapped_column(primary_key=True)
|
337
|
+
user_account_id: Mapped[int] = mapped_column(ForeignKey("user_account.id"))
|
338
|
+
|
339
|
+
xmpp_id: Mapped[str] = mapped_column(nullable=False)
|
340
|
+
|
341
|
+
legacy_ids_multi_id: Mapped[int] = mapped_column(ForeignKey("legacy_ids_multi.id"))
|
342
|
+
legacy_ids_multi: Mapped[LegacyIdsMulti] = relationship(back_populates="xmpp_ids")
|
343
|
+
|
344
|
+
|
345
|
+
participant_hats = sa.Table(
|
346
|
+
"participant_hats",
|
347
|
+
Base.metadata,
|
348
|
+
sa.Column("participant_id", ForeignKey("participant.id"), primary_key=True),
|
349
|
+
sa.Column("hat_id", ForeignKey("hat.id"), primary_key=True),
|
350
|
+
)
|
351
|
+
|
352
|
+
|
353
|
+
class Hat(Base):
|
354
|
+
__tablename__ = "hat"
|
355
|
+
__table_args__ = (UniqueConstraint("title", "uri"),)
|
356
|
+
|
357
|
+
id: Mapped[int] = mapped_column(primary_key=True)
|
358
|
+
title: Mapped[str] = mapped_column()
|
359
|
+
uri: Mapped[str] = mapped_column()
|
360
|
+
participants: Mapped[list["Participant"]] = relationship(
|
361
|
+
secondary=participant_hats, back_populates="hats"
|
362
|
+
)
|
363
|
+
|
364
|
+
|
365
|
+
class Participant(Base):
|
366
|
+
__tablename__ = "participant"
|
367
|
+
|
368
|
+
id: Mapped[int] = mapped_column(primary_key=True)
|
369
|
+
|
370
|
+
room_id: Mapped[int] = mapped_column(ForeignKey("room.id"), nullable=False)
|
371
|
+
room: Mapped[Room] = relationship(
|
372
|
+
back_populates="participants", primaryjoin=Room.id == room_id
|
373
|
+
)
|
374
|
+
|
375
|
+
contact_id: Mapped[int] = mapped_column(ForeignKey("contact.id"), nullable=True)
|
376
|
+
contact: Mapped[Contact] = relationship(lazy=False, back_populates="participants")
|
377
|
+
|
378
|
+
is_user: Mapped[bool] = mapped_column(default=False)
|
379
|
+
|
380
|
+
affiliation: Mapped[MucAffiliation] = mapped_column(default="member")
|
381
|
+
role: Mapped[MucRole] = mapped_column(default="participant")
|
382
|
+
|
383
|
+
presence_sent: Mapped[bool] = mapped_column(default=False)
|
384
|
+
|
385
|
+
resource: Mapped[Optional[str]] = mapped_column(default=None)
|
386
|
+
nickname: Mapped[str] = mapped_column(nullable=True, default=None)
|
387
|
+
|
388
|
+
hats: Mapped[list["Hat"]] = relationship(
|
389
|
+
secondary=participant_hats, back_populates="participants"
|
390
|
+
)
|
391
|
+
|
392
|
+
extra_attributes: Mapped[Optional[JSONSerializable]] = mapped_column(default=None)
|
393
|
+
|
394
|
+
|
395
|
+
class Bob(Base):
|
396
|
+
__tablename__ = "bob"
|
397
|
+
|
398
|
+
id: Mapped[int] = mapped_column(primary_key=True)
|
399
|
+
file_name: Mapped[str] = mapped_column(nullable=False)
|
400
|
+
|
401
|
+
sha_1: Mapped[str] = mapped_column(nullable=False, unique=True)
|
402
|
+
sha_256: Mapped[str] = mapped_column(nullable=False, unique=True)
|
403
|
+
sha_512: Mapped[str] = mapped_column(nullable=False, unique=True)
|
404
|
+
|
405
|
+
content_type: Mapped[Optional[str]] = mapped_column(nullable=False)
|