slidge 0.3.0a3__py3-none-any.whl → 0.3.0b1__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/contact/contact.py +2 -2
- slidge/core/gateway.py +1 -1
- slidge/core/mixins/attachment.py +31 -0
- slidge/core/mixins/avatar.py +17 -10
- slidge/core/session.py +5 -1
- slidge/db/alembic/versions/cef02a8b1451_initial_schema.py +361 -0
- slidge/group/participant.py +1 -1
- slidge/group/room.py +25 -36
- slidge/migration.py +14 -5
- {slidge-0.3.0a3.dist-info → slidge-0.3.0b1.dist-info}/METADATA +1 -1
- {slidge-0.3.0a3.dist-info → slidge-0.3.0b1.dist-info}/RECORD +15 -38
- slidge/db/alembic/versions/0337c90c0b96_unify_legacy_xmpp_id_mappings.py +0 -183
- slidge/db/alembic/versions/04cf35e3cf85_add_participant_nickname_no_illegal.py +0 -33
- slidge/db/alembic/versions/09f27f098baa_add_missing_attributes_in_room.py +0 -36
- slidge/db/alembic/versions/15b0bd83407a_remove_bogus_unique_constraints_on_room_.py +0 -85
- slidge/db/alembic/versions/2461390c0af2_store_contacts_caps_verstring_in_db.py +0 -36
- slidge/db/alembic/versions/29f5280c61aa_store_subject_setter_in_room.py +0 -37
- slidge/db/alembic/versions/2b1f45ab7379_store_room_subject_setter_by_nickname.py +0 -41
- slidge/db/alembic/versions/3071e0fa69d4_add_contact_client_type.py +0 -52
- slidge/db/alembic/versions/3231d2c623bc_add_unique_contraint_for_attachment_.py +0 -33
- slidge/db/alembic/versions/45c24cc73c91_add_bob.py +0 -42
- slidge/db/alembic/versions/4dbd23a3f868_new_avatar_store.py +0 -105
- slidge/db/alembic/versions/54ce3cde350c_use_hash_for_avatar_filenames.py +0 -50
- slidge/db/alembic/versions/58b98dacf819_refactor.py +0 -118
- slidge/db/alembic/versions/5bd48bfdffa2_lift_room_legacy_id_constraint.py +0 -61
- slidge/db/alembic/versions/75a62b74b239_ditch_hats_table.py +0 -74
- slidge/db/alembic/versions/82a4af84b679_add_muc_history_filled.py +0 -48
- slidge/db/alembic/versions/8b993243a536_add_vcard_content_to_contact_table.py +0 -43
- slidge/db/alembic/versions/8d2ced764698_rely_on_db_to_store_contacts_rooms_and_.py +0 -139
- slidge/db/alembic/versions/aa9d82a7f6ef_db_creation.py +0 -50
- slidge/db/alembic/versions/abba1ae0edb3_store_avatar_legacy_id_in_the_contact_.py +0 -79
- slidge/db/alembic/versions/b33993e87db3_move_everything_to_persistent_db.py +0 -214
- slidge/db/alembic/versions/b64b1a793483_add_source_and_legacy_id_for_archived_.py +0 -52
- slidge/db/alembic/versions/c4a8ec35a0e8_per_room_user_nick.py +0 -34
- slidge/db/alembic/versions/e91195719c2c_store_users_avatars_persistently.py +0 -26
- {slidge-0.3.0a3.dist-info → slidge-0.3.0b1.dist-info}/WHEEL +0 -0
- {slidge-0.3.0a3.dist-info → slidge-0.3.0b1.dist-info}/entry_points.txt +0 -0
- {slidge-0.3.0a3.dist-info → slidge-0.3.0b1.dist-info}/licenses/LICENSE +0 -0
- {slidge-0.3.0a3.dist-info → slidge-0.3.0b1.dist-info}/top_level.txt +0 -0
slidge/contact/contact.py
CHANGED
@@ -95,7 +95,7 @@ class LegacyContact(
|
|
95
95
|
super().__init__()
|
96
96
|
|
97
97
|
@property
|
98
|
-
def jid(self):
|
98
|
+
def jid(self): # type:ignore[override]
|
99
99
|
jid = JID(self.stored.jid)
|
100
100
|
jid.resource = self.RESOURCE
|
101
101
|
return jid
|
@@ -298,7 +298,7 @@ class LegacyContact(
|
|
298
298
|
)
|
299
299
|
self.commit()
|
300
300
|
for p in self.participants:
|
301
|
-
p.nickname = n
|
301
|
+
p.nickname = n or str(self.legacy_id)
|
302
302
|
|
303
303
|
def _post_avatar_update(self, cached_avatar) -> None:
|
304
304
|
if self.is_friend and self.added_to_roster:
|
slidge/core/gateway.py
CHANGED
slidge/core/mixins/attachment.py
CHANGED
@@ -403,6 +403,37 @@ class AttachmentMixin(TextMessageMixin):
|
|
403
403
|
:param when: when the file was sent, for a "delay" tag (:xep:`0203`)
|
404
404
|
:param thread:
|
405
405
|
"""
|
406
|
+
coro = self.__send_file(
|
407
|
+
attachment,
|
408
|
+
legacy_msg_id,
|
409
|
+
reply_to=reply_to,
|
410
|
+
when=when,
|
411
|
+
thread=thread,
|
412
|
+
**kwargs,
|
413
|
+
)
|
414
|
+
if self.session is NotImplemented:
|
415
|
+
return await coro
|
416
|
+
elif not isinstance(attachment, LegacyAttachment):
|
417
|
+
return await coro
|
418
|
+
elif attachment.legacy_file_id is None:
|
419
|
+
return await coro
|
420
|
+
else:
|
421
|
+
# prevents race conditions where we download the same thing several time
|
422
|
+
# and end up attempting to insert it twice in the DB, raising an
|
423
|
+
# IntegrityError.
|
424
|
+
async with self.session.lock(("attachment", attachment.legacy_file_id)):
|
425
|
+
return await coro
|
426
|
+
|
427
|
+
async def __send_file(
|
428
|
+
self,
|
429
|
+
attachment: LegacyAttachment | Path | str,
|
430
|
+
legacy_msg_id: Optional[LegacyMessageType] = None,
|
431
|
+
*,
|
432
|
+
reply_to: Optional[MessageReference] = None,
|
433
|
+
when: Optional[datetime] = None,
|
434
|
+
thread: Optional[LegacyThreadType] = None,
|
435
|
+
**kwargs,
|
436
|
+
) -> tuple[Optional[str], list[Message]]:
|
406
437
|
store_multi = kwargs.pop("store_multi", True)
|
407
438
|
carbon = kwargs.pop("carbon", False)
|
408
439
|
mto = kwargs.pop("mto", None)
|
slidge/core/mixins/avatar.py
CHANGED
@@ -123,19 +123,26 @@ class AvatarMixin(UpdateInfoMixin):
|
|
123
123
|
else:
|
124
124
|
avatar.path.unlink()
|
125
125
|
|
126
|
-
if cached_avatar is None
|
127
|
-
|
128
|
-
|
129
|
-
self.stored.avatar = cached_avatar.stored
|
126
|
+
stored_avatar = None if cached_avatar is None else cached_avatar.stored
|
127
|
+
self.stored.avatar = stored_avatar
|
128
|
+
|
130
129
|
try:
|
131
130
|
self.commit(merge=True)
|
132
131
|
except IntegrityError as e:
|
133
|
-
self.
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
132
|
+
with self.xmpp.store.session(expire_on_commit=False) as orm:
|
133
|
+
if orm.object_session(self.stored):
|
134
|
+
self.log.debug(
|
135
|
+
"Hit integrity error, attempting to fix by refreshing participants"
|
136
|
+
)
|
137
|
+
orm.refresh(self.stored, ["participants"])
|
138
|
+
else:
|
139
|
+
self.log.debug(
|
140
|
+
"Hit integrity error, attempting to fix by merging contact.stored"
|
141
|
+
)
|
142
|
+
self.stored = orm.merge(self.stored)
|
143
|
+
self.stored.avatar = stored_avatar
|
144
|
+
orm.add(self.stored)
|
145
|
+
orm.commit()
|
139
146
|
|
140
147
|
self._post_avatar_update(cached_avatar)
|
141
148
|
|
slidge/core/session.py
CHANGED
@@ -24,6 +24,7 @@ from ..db.models import Contact, GatewayUser
|
|
24
24
|
from ..group.bookmarks import LegacyBookmarks
|
25
25
|
from ..group.room import LegacyMUC
|
26
26
|
from ..util import ABCSubclassableOnceAtMost
|
27
|
+
from ..util.lock import NamedLockMixin
|
27
28
|
from ..util.types import (
|
28
29
|
LegacyGroupIdType,
|
29
30
|
LegacyMessageType,
|
@@ -50,7 +51,9 @@ class CachedPresence(NamedTuple):
|
|
50
51
|
|
51
52
|
|
52
53
|
class BaseSession(
|
53
|
-
Generic[LegacyMessageType, RecipientType],
|
54
|
+
Generic[LegacyMessageType, RecipientType],
|
55
|
+
NamedLockMixin,
|
56
|
+
metaclass=ABCSubclassableOnceAtMost,
|
54
57
|
):
|
55
58
|
"""
|
56
59
|
The session of a registered :term:`User`.
|
@@ -95,6 +98,7 @@ class BaseSession(
|
|
95
98
|
_bookmarks_cls: Type[LegacyBookmarks]
|
96
99
|
|
97
100
|
def __init__(self, user: GatewayUser) -> None:
|
101
|
+
super().__init__()
|
98
102
|
self.user = user
|
99
103
|
self.log = logging.getLogger(user.jid.bare)
|
100
104
|
|
@@ -0,0 +1,361 @@
|
|
1
|
+
"""Initial schema
|
2
|
+
|
3
|
+
Revision ID: cef02a8b1451
|
4
|
+
Revises:
|
5
|
+
Create Date: 2025-08-28 12:48:16.890606
|
6
|
+
|
7
|
+
"""
|
8
|
+
|
9
|
+
from typing import Sequence, Union
|
10
|
+
|
11
|
+
import sqlalchemy as sa
|
12
|
+
from alembic import op
|
13
|
+
|
14
|
+
import slidge
|
15
|
+
|
16
|
+
# revision identifiers, used by Alembic.
|
17
|
+
revision: str = "cef02a8b1451"
|
18
|
+
down_revision: Union[str, None] = None
|
19
|
+
branch_labels: Union[str, Sequence[str], None] = None
|
20
|
+
depends_on: Union[str, Sequence[str], None] = None
|
21
|
+
|
22
|
+
|
23
|
+
def upgrade() -> None:
|
24
|
+
# ### commands auto generated by Alembic - please adjust! ###
|
25
|
+
op.create_table(
|
26
|
+
"avatar",
|
27
|
+
sa.Column("id", sa.Integer(), nullable=False),
|
28
|
+
sa.Column("hash", sa.String(), nullable=False),
|
29
|
+
sa.Column("height", sa.Integer(), nullable=False),
|
30
|
+
sa.Column("width", sa.Integer(), nullable=False),
|
31
|
+
sa.Column("legacy_id", sa.String(), nullable=True),
|
32
|
+
sa.Column("url", sa.String(), nullable=True),
|
33
|
+
sa.Column("etag", sa.String(), nullable=True),
|
34
|
+
sa.Column("last_modified", sa.String(), nullable=True),
|
35
|
+
sa.PrimaryKeyConstraint("id", name=op.f("pk_avatar")),
|
36
|
+
sa.UniqueConstraint("hash", name=op.f("uq_avatar_hash")),
|
37
|
+
sa.UniqueConstraint("legacy_id", name=op.f("uq_avatar_legacy_id")),
|
38
|
+
)
|
39
|
+
op.create_table(
|
40
|
+
"bob",
|
41
|
+
sa.Column("id", sa.Integer(), nullable=False),
|
42
|
+
sa.Column("file_name", sa.String(), nullable=False),
|
43
|
+
sa.Column("sha_1", sa.String(), nullable=False),
|
44
|
+
sa.Column("sha_256", sa.String(), nullable=False),
|
45
|
+
sa.Column("sha_512", sa.String(), nullable=False),
|
46
|
+
sa.Column("content_type", sa.String(), nullable=False),
|
47
|
+
sa.PrimaryKeyConstraint("id", name=op.f("pk_bob")),
|
48
|
+
sa.UniqueConstraint("sha_1", name=op.f("uq_bob_sha_1")),
|
49
|
+
sa.UniqueConstraint("sha_256", name=op.f("uq_bob_sha_256")),
|
50
|
+
sa.UniqueConstraint("sha_512", name=op.f("uq_bob_sha_512")),
|
51
|
+
)
|
52
|
+
op.create_table(
|
53
|
+
"user_account",
|
54
|
+
sa.Column("id", sa.Integer(), nullable=False),
|
55
|
+
sa.Column("jid", slidge.db.meta.JIDType(), nullable=False),
|
56
|
+
sa.Column(
|
57
|
+
"registration_date",
|
58
|
+
sa.DateTime(),
|
59
|
+
server_default=sa.text("(CURRENT_TIMESTAMP)"),
|
60
|
+
nullable=False,
|
61
|
+
),
|
62
|
+
sa.Column(
|
63
|
+
"legacy_module_data", slidge.db.meta.JSONEncodedDict(), nullable=False
|
64
|
+
),
|
65
|
+
sa.Column("preferences", slidge.db.meta.JSONEncodedDict(), nullable=False),
|
66
|
+
sa.Column("avatar_hash", sa.String(), nullable=True),
|
67
|
+
sa.PrimaryKeyConstraint("id", name=op.f("pk_user_account")),
|
68
|
+
sa.UniqueConstraint("jid", name=op.f("uq_user_account_jid")),
|
69
|
+
)
|
70
|
+
op.create_table(
|
71
|
+
"attachment",
|
72
|
+
sa.Column("id", sa.Integer(), nullable=False),
|
73
|
+
sa.Column("user_account_id", sa.Integer(), nullable=False),
|
74
|
+
sa.Column("legacy_file_id", sa.String(), nullable=True),
|
75
|
+
sa.Column("url", sa.String(), nullable=False),
|
76
|
+
sa.Column("sims", sa.String(), nullable=True),
|
77
|
+
sa.Column("sfs", sa.String(), nullable=True),
|
78
|
+
sa.ForeignKeyConstraint(
|
79
|
+
["user_account_id"],
|
80
|
+
["user_account.id"],
|
81
|
+
name=op.f("fk_attachment_user_account_id_user_account"),
|
82
|
+
),
|
83
|
+
sa.PrimaryKeyConstraint("id", name=op.f("pk_attachment")),
|
84
|
+
sa.UniqueConstraint(
|
85
|
+
"user_account_id",
|
86
|
+
"legacy_file_id",
|
87
|
+
name=op.f("uq_attachment_user_account_id"),
|
88
|
+
),
|
89
|
+
)
|
90
|
+
with op.batch_alter_table("attachment", schema=None) as batch_op:
|
91
|
+
batch_op.create_index(
|
92
|
+
batch_op.f("ix_attachment_legacy_file_id"), ["legacy_file_id"], unique=False
|
93
|
+
)
|
94
|
+
batch_op.create_index(batch_op.f("ix_attachment_url"), ["url"], unique=False)
|
95
|
+
|
96
|
+
op.create_table(
|
97
|
+
"contact",
|
98
|
+
sa.Column("id", sa.Integer(), nullable=False),
|
99
|
+
sa.Column("user_account_id", sa.Integer(), nullable=False),
|
100
|
+
sa.Column("legacy_id", sa.String(), nullable=False),
|
101
|
+
sa.Column("jid", slidge.db.meta.JIDType(), nullable=False),
|
102
|
+
sa.Column("avatar_id", sa.Integer(), nullable=True),
|
103
|
+
sa.Column("nick", sa.String(), nullable=True),
|
104
|
+
sa.Column("cached_presence", sa.Boolean(), nullable=False),
|
105
|
+
sa.Column("last_seen", sa.DateTime(), nullable=True),
|
106
|
+
sa.Column("ptype", sa.String(), nullable=True),
|
107
|
+
sa.Column("pstatus", sa.String(), nullable=True),
|
108
|
+
sa.Column("pshow", sa.String(), nullable=True),
|
109
|
+
sa.Column("caps_ver", sa.String(), nullable=True),
|
110
|
+
sa.Column("is_friend", sa.Boolean(), nullable=False),
|
111
|
+
sa.Column("added_to_roster", sa.Boolean(), nullable=False),
|
112
|
+
sa.Column("extra_attributes", slidge.db.meta.JSONEncodedDict(), nullable=True),
|
113
|
+
sa.Column("updated", sa.Boolean(), nullable=False),
|
114
|
+
sa.Column("vcard", sa.String(), nullable=True),
|
115
|
+
sa.Column("vcard_fetched", sa.Boolean(), nullable=False),
|
116
|
+
sa.Column(
|
117
|
+
"client_type",
|
118
|
+
sa.Enum(
|
119
|
+
"bot",
|
120
|
+
"console",
|
121
|
+
"game",
|
122
|
+
"handheld",
|
123
|
+
"pc",
|
124
|
+
"phone",
|
125
|
+
"sms",
|
126
|
+
"tablet",
|
127
|
+
"web",
|
128
|
+
native_enum=False,
|
129
|
+
),
|
130
|
+
nullable=False,
|
131
|
+
),
|
132
|
+
sa.ForeignKeyConstraint(
|
133
|
+
["avatar_id"], ["avatar.id"], name=op.f("fk_contact_avatar_id_avatar")
|
134
|
+
),
|
135
|
+
sa.ForeignKeyConstraint(
|
136
|
+
["user_account_id"],
|
137
|
+
["user_account.id"],
|
138
|
+
name=op.f("fk_contact_user_account_id_user_account"),
|
139
|
+
),
|
140
|
+
sa.PrimaryKeyConstraint("id", name=op.f("pk_contact")),
|
141
|
+
sa.UniqueConstraint(
|
142
|
+
"user_account_id", "jid", name=op.f("uq_contact_user_account_id")
|
143
|
+
),
|
144
|
+
sa.UniqueConstraint(
|
145
|
+
"user_account_id", "legacy_id", name=op.f("uq_contact_user_account_id")
|
146
|
+
),
|
147
|
+
)
|
148
|
+
op.create_table(
|
149
|
+
"room",
|
150
|
+
sa.Column("id", sa.Integer(), nullable=False),
|
151
|
+
sa.Column("user_account_id", sa.Integer(), nullable=False),
|
152
|
+
sa.Column("legacy_id", sa.String(), nullable=False),
|
153
|
+
sa.Column("jid", slidge.db.meta.JIDType(), nullable=False),
|
154
|
+
sa.Column("avatar_id", sa.Integer(), nullable=True),
|
155
|
+
sa.Column("name", sa.String(), nullable=True),
|
156
|
+
sa.Column("description", sa.String(), nullable=True),
|
157
|
+
sa.Column("subject", sa.String(), nullable=True),
|
158
|
+
sa.Column("subject_date", sa.DateTime(), nullable=True),
|
159
|
+
sa.Column("subject_setter", sa.String(), nullable=True),
|
160
|
+
sa.Column("n_participants", sa.Integer(), nullable=True),
|
161
|
+
sa.Column(
|
162
|
+
"muc_type",
|
163
|
+
sa.Enum("GROUP", "CHANNEL", "CHANNEL_NON_ANONYMOUS", name="muctype"),
|
164
|
+
nullable=False,
|
165
|
+
),
|
166
|
+
sa.Column("user_nick", sa.String(), nullable=True),
|
167
|
+
sa.Column("user_resources", sa.String(), nullable=True),
|
168
|
+
sa.Column("participants_filled", sa.Boolean(), nullable=False),
|
169
|
+
sa.Column("history_filled", sa.Boolean(), nullable=False),
|
170
|
+
sa.Column("extra_attributes", slidge.db.meta.JSONEncodedDict(), nullable=True),
|
171
|
+
sa.Column("updated", sa.Boolean(), nullable=False),
|
172
|
+
sa.ForeignKeyConstraint(
|
173
|
+
["avatar_id"], ["avatar.id"], name=op.f("fk_room_avatar_id_avatar")
|
174
|
+
),
|
175
|
+
sa.ForeignKeyConstraint(
|
176
|
+
["user_account_id"],
|
177
|
+
["user_account.id"],
|
178
|
+
name=op.f("fk_room_user_account_id_user_account"),
|
179
|
+
),
|
180
|
+
sa.PrimaryKeyConstraint("id", name=op.f("pk_room")),
|
181
|
+
sa.UniqueConstraint(
|
182
|
+
"user_account_id", "jid", name="uq_room_user_account_id_jid"
|
183
|
+
),
|
184
|
+
sa.UniqueConstraint(
|
185
|
+
"user_account_id", "legacy_id", name="uq_room_user_account_id_legacy_id"
|
186
|
+
),
|
187
|
+
)
|
188
|
+
op.create_table(
|
189
|
+
"contact_sent",
|
190
|
+
sa.Column("id", sa.Integer(), nullable=False),
|
191
|
+
sa.Column("contact_id", sa.Integer(), nullable=False),
|
192
|
+
sa.Column("msg_id", sa.String(), nullable=False),
|
193
|
+
sa.ForeignKeyConstraint(
|
194
|
+
["contact_id"],
|
195
|
+
["contact.id"],
|
196
|
+
name=op.f("fk_contact_sent_contact_id_contact"),
|
197
|
+
),
|
198
|
+
sa.PrimaryKeyConstraint("id", name=op.f("pk_contact_sent")),
|
199
|
+
sa.UniqueConstraint(
|
200
|
+
"contact_id", "msg_id", name=op.f("uq_contact_sent_contact_id")
|
201
|
+
),
|
202
|
+
)
|
203
|
+
op.create_table(
|
204
|
+
"direct_msg",
|
205
|
+
sa.Column("foreign_key", sa.Integer(), nullable=False),
|
206
|
+
sa.Column("id", sa.Integer(), nullable=False),
|
207
|
+
sa.Column("legacy_id", sa.String(), nullable=False),
|
208
|
+
sa.Column("xmpp_id", sa.String(), nullable=False),
|
209
|
+
sa.ForeignKeyConstraint(
|
210
|
+
["foreign_key"],
|
211
|
+
["contact.id"],
|
212
|
+
name=op.f("fk_direct_msg_foreign_key_contact"),
|
213
|
+
),
|
214
|
+
sa.PrimaryKeyConstraint("id", name=op.f("pk_direct_msg")),
|
215
|
+
)
|
216
|
+
with op.batch_alter_table("direct_msg", schema=None) as batch_op:
|
217
|
+
batch_op.create_index(
|
218
|
+
"ix_direct_msg_legacy_id", ["legacy_id", "foreign_key"], unique=False
|
219
|
+
)
|
220
|
+
|
221
|
+
op.create_table(
|
222
|
+
"direct_thread",
|
223
|
+
sa.Column("foreign_key", sa.Integer(), nullable=False),
|
224
|
+
sa.Column("id", sa.Integer(), nullable=False),
|
225
|
+
sa.Column("legacy_id", sa.String(), nullable=False),
|
226
|
+
sa.Column("xmpp_id", sa.String(), nullable=False),
|
227
|
+
sa.ForeignKeyConstraint(
|
228
|
+
["foreign_key"],
|
229
|
+
["contact.id"],
|
230
|
+
name=op.f("fk_direct_thread_foreign_key_contact"),
|
231
|
+
),
|
232
|
+
sa.PrimaryKeyConstraint("id", name=op.f("pk_direct_thread")),
|
233
|
+
)
|
234
|
+
with op.batch_alter_table("direct_thread", schema=None) as batch_op:
|
235
|
+
batch_op.create_index(
|
236
|
+
"ix_direct_direct_thread_id", ["legacy_id", "foreign_key"], unique=False
|
237
|
+
)
|
238
|
+
|
239
|
+
op.create_table(
|
240
|
+
"group_msg",
|
241
|
+
sa.Column("foreign_key", sa.Integer(), nullable=False),
|
242
|
+
sa.Column("id", sa.Integer(), nullable=False),
|
243
|
+
sa.Column("legacy_id", sa.String(), nullable=False),
|
244
|
+
sa.Column("xmpp_id", sa.String(), nullable=False),
|
245
|
+
sa.ForeignKeyConstraint(
|
246
|
+
["foreign_key"], ["room.id"], name=op.f("fk_group_msg_foreign_key_room")
|
247
|
+
),
|
248
|
+
sa.PrimaryKeyConstraint("id", name=op.f("pk_group_msg")),
|
249
|
+
)
|
250
|
+
with op.batch_alter_table("group_msg", schema=None) as batch_op:
|
251
|
+
batch_op.create_index(
|
252
|
+
"ix_group_msg_legacy_id", ["legacy_id", "foreign_key"], unique=False
|
253
|
+
)
|
254
|
+
|
255
|
+
op.create_table(
|
256
|
+
"group_thread",
|
257
|
+
sa.Column("foreign_key", sa.Integer(), nullable=False),
|
258
|
+
sa.Column("id", sa.Integer(), nullable=False),
|
259
|
+
sa.Column("legacy_id", sa.String(), nullable=False),
|
260
|
+
sa.Column("xmpp_id", sa.String(), nullable=False),
|
261
|
+
sa.ForeignKeyConstraint(
|
262
|
+
["foreign_key"], ["room.id"], name=op.f("fk_group_thread_foreign_key_room")
|
263
|
+
),
|
264
|
+
sa.PrimaryKeyConstraint("id", name=op.f("pk_group_thread")),
|
265
|
+
)
|
266
|
+
with op.batch_alter_table("group_thread", schema=None) as batch_op:
|
267
|
+
batch_op.create_index(
|
268
|
+
"ix_direct_group_thread_id", ["legacy_id", "foreign_key"], unique=False
|
269
|
+
)
|
270
|
+
|
271
|
+
op.create_table(
|
272
|
+
"mam",
|
273
|
+
sa.Column("id", sa.Integer(), nullable=False),
|
274
|
+
sa.Column("room_id", sa.Integer(), nullable=False),
|
275
|
+
sa.Column("stanza_id", sa.String(), nullable=False),
|
276
|
+
sa.Column("timestamp", sa.DateTime(), nullable=False),
|
277
|
+
sa.Column("author_jid", slidge.db.meta.JIDType(), nullable=False),
|
278
|
+
sa.Column(
|
279
|
+
"source",
|
280
|
+
sa.Enum("LIVE", "BACKFILL", name="archivedmessagesource"),
|
281
|
+
nullable=False,
|
282
|
+
),
|
283
|
+
sa.Column("legacy_id", sa.String(), nullable=True),
|
284
|
+
sa.Column("stanza", sa.String(), nullable=False),
|
285
|
+
sa.ForeignKeyConstraint(
|
286
|
+
["room_id"], ["room.id"], name=op.f("fk_mam_room_id_room")
|
287
|
+
),
|
288
|
+
sa.PrimaryKeyConstraint("id", name=op.f("pk_mam")),
|
289
|
+
sa.UniqueConstraint("room_id", "stanza_id", name=op.f("uq_mam_room_id")),
|
290
|
+
)
|
291
|
+
op.create_table(
|
292
|
+
"participant",
|
293
|
+
sa.Column("id", sa.Integer(), nullable=False),
|
294
|
+
sa.Column("room_id", sa.Integer(), nullable=False),
|
295
|
+
sa.Column("contact_id", sa.Integer(), nullable=True),
|
296
|
+
sa.Column("is_user", sa.Boolean(), nullable=False),
|
297
|
+
sa.Column(
|
298
|
+
"affiliation",
|
299
|
+
sa.Enum("outcast", "member", "admin", "owner", "none", native_enum=False),
|
300
|
+
nullable=False,
|
301
|
+
),
|
302
|
+
sa.Column(
|
303
|
+
"role",
|
304
|
+
sa.Enum("moderator", "participant", "visitor", "none", native_enum=False),
|
305
|
+
nullable=False,
|
306
|
+
),
|
307
|
+
sa.Column("presence_sent", sa.Boolean(), nullable=False),
|
308
|
+
sa.Column("resource", sa.String(), nullable=False),
|
309
|
+
sa.Column("nickname", sa.String(), nullable=False),
|
310
|
+
sa.Column("nickname_no_illegal", sa.String(), nullable=False),
|
311
|
+
sa.Column("hats", sa.JSON(), nullable=False),
|
312
|
+
sa.Column("extra_attributes", slidge.db.meta.JSONEncodedDict(), nullable=True),
|
313
|
+
sa.ForeignKeyConstraint(
|
314
|
+
["contact_id"],
|
315
|
+
["contact.id"],
|
316
|
+
name=op.f("fk_participant_contact_id_contact"),
|
317
|
+
),
|
318
|
+
sa.ForeignKeyConstraint(
|
319
|
+
["room_id"], ["room.id"], name=op.f("fk_participant_room_id_room")
|
320
|
+
),
|
321
|
+
sa.PrimaryKeyConstraint("id", name=op.f("pk_participant")),
|
322
|
+
sa.UniqueConstraint(
|
323
|
+
"room_id", "contact_id", name=op.f("uq_participant_room_id")
|
324
|
+
),
|
325
|
+
sa.UniqueConstraint("room_id", "resource", name=op.f("uq_participant_room_id")),
|
326
|
+
)
|
327
|
+
# ### end Alembic commands ###
|
328
|
+
|
329
|
+
|
330
|
+
def downgrade() -> None:
|
331
|
+
# ### commands auto generated by Alembic - please adjust! ###
|
332
|
+
op.drop_table("participant")
|
333
|
+
op.drop_table("mam")
|
334
|
+
with op.batch_alter_table("group_thread", schema=None) as batch_op:
|
335
|
+
batch_op.drop_index("ix_direct_group_thread_id")
|
336
|
+
|
337
|
+
op.drop_table("group_thread")
|
338
|
+
with op.batch_alter_table("group_msg", schema=None) as batch_op:
|
339
|
+
batch_op.drop_index("ix_group_msg_legacy_id")
|
340
|
+
|
341
|
+
op.drop_table("group_msg")
|
342
|
+
with op.batch_alter_table("direct_thread", schema=None) as batch_op:
|
343
|
+
batch_op.drop_index("ix_direct_direct_thread_id")
|
344
|
+
|
345
|
+
op.drop_table("direct_thread")
|
346
|
+
with op.batch_alter_table("direct_msg", schema=None) as batch_op:
|
347
|
+
batch_op.drop_index("ix_direct_msg_legacy_id")
|
348
|
+
|
349
|
+
op.drop_table("direct_msg")
|
350
|
+
op.drop_table("contact_sent")
|
351
|
+
op.drop_table("room")
|
352
|
+
op.drop_table("contact")
|
353
|
+
with op.batch_alter_table("attachment", schema=None) as batch_op:
|
354
|
+
batch_op.drop_index(batch_op.f("ix_attachment_url"))
|
355
|
+
batch_op.drop_index(batch_op.f("ix_attachment_legacy_file_id"))
|
356
|
+
|
357
|
+
op.drop_table("attachment")
|
358
|
+
op.drop_table("user_account")
|
359
|
+
op.drop_table("bob")
|
360
|
+
op.drop_table("avatar")
|
361
|
+
# ### end Alembic commands ###
|
slidge/group/participant.py
CHANGED
slidge/group/room.py
CHANGED
@@ -18,8 +18,8 @@ from slixmpp.plugins.xep_0469.stanza import NS as PINNING_NS
|
|
18
18
|
from slixmpp.plugins.xep_0492.stanza import NS as NOTIFY_NS
|
19
19
|
from slixmpp.plugins.xep_0492.stanza import WhenLiteral
|
20
20
|
from slixmpp.xmlstream import ET
|
21
|
+
from sqlalchemy.exc import InvalidRequestError
|
21
22
|
from sqlalchemy.orm import Session as OrmSession
|
22
|
-
from sqlalchemy.orm.exc import DetachedInstanceError
|
23
23
|
|
24
24
|
from ..contact.contact import LegacyContact
|
25
25
|
from ..contact.roster import ContactIsUser
|
@@ -247,48 +247,33 @@ class LegacyMUC(
|
|
247
247
|
return
|
248
248
|
async with self.lock("fill participants"):
|
249
249
|
parts: list[Participant] = []
|
250
|
-
resources
|
250
|
+
resources = set[str]()
|
251
|
+
# During fill_participants(), self.get_participant*() methods may
|
252
|
+
# return a participant with a conflicting nick/resource.
|
251
253
|
async for participant in self.fill_participants():
|
252
|
-
if participant.stored.
|
254
|
+
if participant.stored.resource in resources:
|
255
|
+
self.log.warning(
|
256
|
+
"Participant '%s' was yielded more than once by fill_participants()",
|
257
|
+
participant.stored.resource,
|
258
|
+
)
|
253
259
|
continue
|
254
|
-
# During fill_participants(), self.get_participant*() methods may
|
255
|
-
# return a participant with a conflicting nick/resource. There is
|
256
|
-
# a better way to fix this than the logic below, but this better way
|
257
|
-
# has not been found yet.
|
258
|
-
if participant.jid.resource in resources:
|
259
|
-
if participant.contact is None:
|
260
|
-
self.log.warning(
|
261
|
-
"Ditching participant %s", participant.nickname
|
262
|
-
)
|
263
|
-
del participant
|
264
|
-
continue
|
265
|
-
else:
|
266
|
-
nickname = (
|
267
|
-
f"{participant.nickname} ({participant.contact.jid.node})"
|
268
|
-
)
|
269
|
-
participant = self._participant_cls(
|
270
|
-
self,
|
271
|
-
Participant(nickname=nickname, room=self.stored),
|
272
|
-
contact=participant.contact,
|
273
|
-
)
|
274
|
-
resources.add(participant.jid.resource)
|
275
260
|
parts.append(participant.stored)
|
261
|
+
resources.add(participant.stored.resource)
|
276
262
|
with self.xmpp.store.session(expire_on_commit=False) as orm:
|
277
|
-
# FIXME: something must be wrong with all these refreshes and merge,
|
278
|
-
# but I did not manage to get rid of them without getting various
|
279
|
-
# sqlalchemy exceptions raised everywhere
|
280
263
|
orm.add(self.stored)
|
281
|
-
|
282
|
-
|
283
|
-
|
264
|
+
# because self.participants() is async, self.stored may be out of sync at
|
265
|
+
# this point.
|
266
|
+
with orm.no_autoflush:
|
267
|
+
orm.refresh(self.stored)
|
284
268
|
for part in parts:
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
269
|
+
try:
|
270
|
+
self.stored.participants.append(part)
|
271
|
+
except InvalidRequestError:
|
272
|
+
# the participant was already stored in the DB. `part` may even
|
273
|
+
# be out-of-sync, so it's fine to just ditch it.
|
274
|
+
pass
|
275
|
+
self.stored.participants_filled = True
|
290
276
|
orm.commit()
|
291
|
-
orm.refresh(self.stored)
|
292
277
|
|
293
278
|
async def get_participants(
|
294
279
|
self, affiliation: Optional[MucAffiliation] = None
|
@@ -642,6 +627,10 @@ class LegacyMUC(
|
|
642
627
|
|
643
628
|
if user_participant is None:
|
644
629
|
user_participant = await self.get_user_participant()
|
630
|
+
with self.xmpp.store.session() as orm:
|
631
|
+
orm.add(self.stored)
|
632
|
+
with orm.no_autoflush:
|
633
|
+
orm.refresh(self.stored, ["participants"])
|
645
634
|
if not user_participant.is_user:
|
646
635
|
self.log.warning("is_user flag not set participant on user_participant")
|
647
636
|
user_participant.is_user = True # type:ignore
|
slidge/migration.py
CHANGED
@@ -1,16 +1,13 @@
|
|
1
1
|
import logging
|
2
2
|
import shutil
|
3
3
|
import sys
|
4
|
+
import traceback
|
4
5
|
from pathlib import Path
|
5
6
|
|
6
7
|
from alembic import command
|
7
8
|
from alembic.config import Config
|
8
|
-
from slixmpp import JID
|
9
9
|
|
10
10
|
from .core import config
|
11
|
-
from .db.meta import get_engine
|
12
|
-
from .db.models import GatewayUser
|
13
|
-
from .db.store import SlidgeStore
|
14
11
|
|
15
12
|
|
16
13
|
def remove_avatar_cache_v1() -> None:
|
@@ -32,7 +29,15 @@ def get_alembic_cfg() -> Config:
|
|
32
29
|
|
33
30
|
def migrate() -> None:
|
34
31
|
remove_avatar_cache_v1()
|
35
|
-
|
32
|
+
try:
|
33
|
+
command.upgrade(get_alembic_cfg(), "head")
|
34
|
+
except Exception as e:
|
35
|
+
traceback.print_exception(e)
|
36
|
+
print(
|
37
|
+
"Something went wrong during the migration. "
|
38
|
+
"This is expected if you upgrade from slidge 0.2, in this case you need to start from a fresh database."
|
39
|
+
)
|
40
|
+
exit(1)
|
36
41
|
|
37
42
|
|
38
43
|
def main() -> None:
|
@@ -41,6 +46,10 @@ def main() -> None:
|
|
41
46
|
|
42
47
|
Usage: python -m slidge.migration "Revision message blah blah blah"
|
43
48
|
"""
|
49
|
+
dev_db = Path(".") / "dev" / "slidge.sqlite"
|
50
|
+
if dev_db.exists():
|
51
|
+
# always start from a clean state
|
52
|
+
dev_db.unlink()
|
44
53
|
alembic_cfg = get_alembic_cfg()
|
45
54
|
command.upgrade(alembic_cfg, "head")
|
46
55
|
command.revision(alembic_cfg, sys.argv[1], autogenerate=True)
|