slidge 0.2.0a0__py3-none-any.whl → 0.2.0a1__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/__version__.py +1 -1
- slidge/command/admin.py +1 -1
- slidge/command/user.py +0 -1
- slidge/contact/contact.py +86 -32
- slidge/contact/roster.py +79 -19
- slidge/core/config.py +1 -4
- slidge/core/gateway/base.py +9 -2
- slidge/core/gateway/caps.py +7 -5
- slidge/core/gateway/muc_admin.py +1 -1
- slidge/core/gateway/session_dispatcher.py +20 -6
- slidge/core/gateway/vcard_temp.py +1 -1
- slidge/core/mixins/attachment.py +17 -4
- slidge/core/mixins/avatar.py +26 -9
- slidge/core/mixins/db.py +18 -0
- slidge/core/mixins/disco.py +0 -10
- slidge/core/mixins/message.py +7 -1
- slidge/core/mixins/presence.py +6 -3
- slidge/core/pubsub.py +7 -15
- slidge/core/session.py +6 -3
- slidge/db/alembic/versions/2461390c0af2_store_contacts_caps_verstring_in_db.py +36 -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/aa9d82a7f6ef_db_creation.py +11 -2
- 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/avatar.py +20 -9
- slidge/db/models.py +16 -6
- slidge/db/store.py +217 -115
- slidge/group/archive.py +46 -1
- slidge/group/bookmarks.py +17 -5
- slidge/group/participant.py +10 -3
- slidge/group/room.py +183 -125
- slidge/main.py +3 -3
- slidge/slixfix/xep_0292/vcard4.py +2 -0
- slidge/util/archive_msg.py +2 -1
- slidge/util/test.py +54 -4
- slidge/util/types.py +5 -0
- slidge/util/util.py +22 -0
- {slidge-0.2.0a0.dist-info → slidge-0.2.0a1.dist-info}/METADATA +2 -4
- {slidge-0.2.0a0.dist-info → slidge-0.2.0a1.dist-info}/RECORD +43 -37
- {slidge-0.2.0a0.dist-info → slidge-0.2.0a1.dist-info}/LICENSE +0 -0
- {slidge-0.2.0a0.dist-info → slidge-0.2.0a1.dist-info}/WHEEL +0 -0
- {slidge-0.2.0a0.dist-info → slidge-0.2.0a1.dist-info}/entry_points.txt +0 -0
slidge/core/mixins/avatar.py
CHANGED
@@ -28,7 +28,6 @@ class AvatarMixin:
|
|
28
28
|
|
29
29
|
jid: JID = NotImplemented
|
30
30
|
session: AnyBaseSession = NotImplemented
|
31
|
-
_avatar_pubsub_broadcast: bool = NotImplemented
|
32
31
|
_avatar_bare_jid: bool = NotImplemented
|
33
32
|
|
34
33
|
def __init__(self) -> None:
|
@@ -86,7 +85,9 @@ class AvatarMixin:
|
|
86
85
|
return None
|
87
86
|
raise TypeError("Bad avatar", a)
|
88
87
|
|
89
|
-
async def __set_avatar(
|
88
|
+
async def __set_avatar(
|
89
|
+
self, a: Optional[AvatarType], uid: Optional[AvatarIdType], delete: bool
|
90
|
+
):
|
90
91
|
self.__avatar_unique_id = uid
|
91
92
|
|
92
93
|
if a is None:
|
@@ -105,13 +106,21 @@ class AvatarMixin:
|
|
105
106
|
return
|
106
107
|
self._avatar_pk = cached_avatar.pk
|
107
108
|
|
108
|
-
if self.
|
109
|
+
if self.__should_pubsub_broadcast():
|
109
110
|
await self.session.xmpp.pubsub.broadcast_avatar(
|
110
111
|
self.__avatar_jid, self.session.user_jid, cached_avatar
|
111
112
|
)
|
112
113
|
|
114
|
+
if delete and isinstance(a, Path):
|
115
|
+
a.unlink()
|
116
|
+
|
113
117
|
self._post_avatar_update()
|
114
118
|
|
119
|
+
def __should_pubsub_broadcast(self):
|
120
|
+
return getattr(self, "is_friend", False) and getattr(
|
121
|
+
self, "added_to_roster", False
|
122
|
+
)
|
123
|
+
|
115
124
|
async def _no_change(self, a: Optional[AvatarType], uid: Optional[AvatarIdType]):
|
116
125
|
if a is None:
|
117
126
|
return self.__avatar_unique_id is None
|
@@ -127,16 +136,20 @@ class AvatarMixin:
|
|
127
136
|
self,
|
128
137
|
a: Optional[AvatarType],
|
129
138
|
avatar_unique_id: Optional[LegacyFileIdType] = None,
|
139
|
+
delete: bool = False,
|
130
140
|
blocking=False,
|
131
141
|
cancel=True,
|
132
142
|
) -> None:
|
133
143
|
"""
|
134
144
|
Set an avatar for this entity
|
135
145
|
|
136
|
-
:param a:
|
137
|
-
:param avatar_unique_id:
|
138
|
-
|
139
|
-
:param
|
146
|
+
:param a: The avatar, in one of the types slidge supports
|
147
|
+
:param avatar_unique_id: A globally unique ID for the avatar on the
|
148
|
+
legacy network
|
149
|
+
:param delete: If the avatar is provided as a Path, whether to delete
|
150
|
+
it once used or not.
|
151
|
+
:param blocking: Internal use by slidge for tests, do not use!
|
152
|
+
:param cancel: Internal use by slidge, do not use!
|
140
153
|
"""
|
141
154
|
if avatar_unique_id is None and a is not None:
|
142
155
|
avatar_unique_id = self.__get_uid(a)
|
@@ -145,7 +158,7 @@ class AvatarMixin:
|
|
145
158
|
if cancel and self._set_avatar_task:
|
146
159
|
self._set_avatar_task.cancel()
|
147
160
|
awaitable = create_task(
|
148
|
-
self.__set_avatar(a, avatar_unique_id),
|
161
|
+
self.__set_avatar(a, avatar_unique_id, delete),
|
149
162
|
name=f"Set pubsub avatar of {self}",
|
150
163
|
)
|
151
164
|
if not self._set_avatar_task or self._set_avatar_task.done():
|
@@ -195,7 +208,7 @@ class AvatarMixin:
|
|
195
208
|
# need to do anything else
|
196
209
|
return
|
197
210
|
|
198
|
-
if self.
|
211
|
+
if self.__should_pubsub_broadcast():
|
199
212
|
if new_id is None and cached_id is None:
|
200
213
|
return
|
201
214
|
cached_avatar = avatar_cache.get(cached_id)
|
@@ -208,6 +221,10 @@ class AvatarMixin:
|
|
208
221
|
def _set_avatar_from_store(self, stored):
|
209
222
|
if stored.avatar_id is None:
|
210
223
|
return
|
224
|
+
if stored.avatar is None:
|
225
|
+
# seems to happen after avatar cleanup for some reason?
|
226
|
+
self.__avatar_unique_id = None
|
227
|
+
return
|
211
228
|
self.__avatar_unique_id = (
|
212
229
|
stored.avatar.legacy_id
|
213
230
|
if stored.avatar.legacy_id is not None
|
slidge/core/mixins/db.py
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
from contextlib import contextmanager
|
2
|
+
|
3
|
+
|
4
|
+
class UpdateInfoMixin:
|
5
|
+
"""
|
6
|
+
This mixin just adds a context manager that prevents commiting to the DB
|
7
|
+
on every attribute change.
|
8
|
+
"""
|
9
|
+
|
10
|
+
def __init__(self, *args, **kwargs):
|
11
|
+
super().__init__(*args, **kwargs)
|
12
|
+
self._updating_info = False
|
13
|
+
|
14
|
+
@contextmanager
|
15
|
+
def updating_info(self):
|
16
|
+
self._updating_info = True
|
17
|
+
yield
|
18
|
+
self._updating_info = False
|
slidge/core/mixins/disco.py
CHANGED
@@ -13,10 +13,6 @@ class BaseDiscoMixin(Base):
|
|
13
13
|
DISCO_NAME: str = NotImplemented
|
14
14
|
DISCO_LANG = None
|
15
15
|
|
16
|
-
def __init__(self):
|
17
|
-
super().__init__()
|
18
|
-
self.__caps_cache: Optional[str] = None
|
19
|
-
|
20
16
|
def _get_disco_name(self):
|
21
17
|
if self.DISCO_NAME is NotImplemented:
|
22
18
|
return self.xmpp.COMPONENT_NAME
|
@@ -44,17 +40,11 @@ class BaseDiscoMixin(Base):
|
|
44
40
|
return info
|
45
41
|
|
46
42
|
async def get_caps_ver(self, jid: OptJid = None, node: Optional[str] = None):
|
47
|
-
if self.__caps_cache:
|
48
|
-
return self.__caps_cache
|
49
43
|
info = await self.get_disco_info(jid, node)
|
50
44
|
caps = self.xmpp.plugin["xep_0115"]
|
51
45
|
ver = caps.generate_verstring(info, caps.hash)
|
52
|
-
self.__caps_cache = ver
|
53
46
|
return ver
|
54
47
|
|
55
|
-
def reset_caps_cache(self):
|
56
|
-
self.__caps_cache = None
|
57
|
-
|
58
48
|
|
59
49
|
class ChatterDiscoMixin(BaseDiscoMixin):
|
60
50
|
AVATAR = True
|
slidge/core/mixins/message.py
CHANGED
@@ -241,7 +241,13 @@ class ContentMessageMixin(AttachmentMixin):
|
|
241
241
|
)
|
242
242
|
if correction:
|
243
243
|
msg["replace"]["id"] = self.__replace_id(legacy_msg_id)
|
244
|
-
return self._send(
|
244
|
+
return self._send(
|
245
|
+
msg,
|
246
|
+
archive_only=archive_only,
|
247
|
+
carbon=carbon,
|
248
|
+
legacy_msg_id=legacy_msg_id,
|
249
|
+
**send_kwargs,
|
250
|
+
)
|
245
251
|
|
246
252
|
def correct(
|
247
253
|
self,
|
slidge/core/mixins/presence.py
CHANGED
@@ -19,27 +19,30 @@ _FRIEND_REQUEST_PRESENCES = {"subscribe", "unsubscribe", "subscribed", "unsubscr
|
|
19
19
|
|
20
20
|
class PresenceMixin(BaseSender):
|
21
21
|
_ONLY_SEND_PRESENCE_CHANGES = False
|
22
|
-
contact_pk: Optional[int]
|
22
|
+
contact_pk: Optional[int] = None
|
23
23
|
|
24
24
|
def __init__(self, *a, **k):
|
25
25
|
super().__init__(*a, **k)
|
26
26
|
# FIXME: this should not be an attribute of this mixin to allow garbage
|
27
27
|
# collection of instances
|
28
28
|
self.__update_last_seen_fallback_task: Optional[Task] = None
|
29
|
+
# this is only used when a presence is set during Contact.update_info(),
|
30
|
+
# when the contact does not have a DB primary key yet, and is written
|
31
|
+
# to DB at the end of update_info()
|
32
|
+
self.cached_presence: Optional[CachedPresence] = None
|
29
33
|
|
30
34
|
async def __update_last_seen_fallback(self):
|
31
35
|
await sleep(3600 * 7)
|
32
36
|
self.send_last_presence(force=True, no_cache_online=False)
|
33
37
|
|
34
38
|
def _get_last_presence(self) -> Optional[CachedPresence]:
|
35
|
-
# TODO: use contact PK instead of JID
|
36
39
|
if self.contact_pk is None:
|
37
40
|
return None
|
38
41
|
return self.xmpp.store.contacts.get_presence(self.contact_pk)
|
39
42
|
|
40
43
|
def _store_last_presence(self, new: CachedPresence):
|
41
|
-
# TODO: use contact PK instead of JID
|
42
44
|
if self.contact_pk is None:
|
45
|
+
self.cached_presence = new
|
43
46
|
return
|
44
47
|
self.xmpp.store.contacts.set_presence(self.contact_pk, new)
|
45
48
|
|
slidge/core/pubsub.py
CHANGED
@@ -20,8 +20,6 @@ from slixmpp.plugins.xep_0172 import UserNick
|
|
20
20
|
from slixmpp.plugins.xep_0292.stanza import VCard4
|
21
21
|
from slixmpp.types import JidStr, OptJidStr
|
22
22
|
|
23
|
-
from ..contact.contact import LegacyContact
|
24
|
-
from ..contact.roster import ContactIsUser
|
25
23
|
from ..db.avatar import CachedAvatar, avatar_cache
|
26
24
|
from ..db.store import ContactStore, SlidgeStore
|
27
25
|
from .mixins.lock import NamedLockMixin
|
@@ -174,7 +172,7 @@ class PubSubComponent(NamedLockMixin, BasePlugin):
|
|
174
172
|
else:
|
175
173
|
if pep_avatar.metadata is None:
|
176
174
|
raise XMPPError("internal-server-error", "Avatar but no metadata?")
|
177
|
-
await self.
|
175
|
+
await self.__broadcast(
|
178
176
|
data=pep_avatar.metadata,
|
179
177
|
from_=p.get_to(),
|
180
178
|
to=from_,
|
@@ -186,7 +184,7 @@ class PubSubComponent(NamedLockMixin, BasePlugin):
|
|
186
184
|
except XMPPError:
|
187
185
|
pass
|
188
186
|
else:
|
189
|
-
await self.
|
187
|
+
await self.__broadcast(data=pep_nick.nick, from_=p.get_to(), to=from_)
|
190
188
|
|
191
189
|
if VCARD4_NAMESPACE + "+notify" in features:
|
192
190
|
await self.broadcast_vcard_event(p.get_to(), to=from_)
|
@@ -201,7 +199,7 @@ class PubSubComponent(NamedLockMixin, BasePlugin):
|
|
201
199
|
# but movim expects it to be here, and I guess
|
202
200
|
|
203
201
|
log.debug("Broadcast vcard4 event: %s", vcard)
|
204
|
-
await self.
|
202
|
+
await self.__broadcast(
|
205
203
|
data=vcard,
|
206
204
|
from_=JID(from_).bare,
|
207
205
|
to=to,
|
@@ -296,7 +294,7 @@ class PubSubComponent(NamedLockMixin, BasePlugin):
|
|
296
294
|
result["pubsub"]["items"].append(item)
|
297
295
|
result.send()
|
298
296
|
|
299
|
-
async def
|
297
|
+
async def __broadcast(self, data, from_: JidStr, to: OptJidStr = None, **kwargs):
|
300
298
|
from_ = JID(from_)
|
301
299
|
if from_ != self.xmpp.boundjid.bare and to is not None:
|
302
300
|
to = JID(to)
|
@@ -304,12 +302,6 @@ class PubSubComponent(NamedLockMixin, BasePlugin):
|
|
304
302
|
if session is None:
|
305
303
|
return
|
306
304
|
await session.ready
|
307
|
-
try:
|
308
|
-
entity = await session.get_contact_or_group_or_participant(from_)
|
309
|
-
except ContactIsUser:
|
310
|
-
return
|
311
|
-
if isinstance(entity, LegacyContact) and not entity.is_friend:
|
312
|
-
return
|
313
305
|
|
314
306
|
item = EventItem()
|
315
307
|
if data:
|
@@ -342,12 +334,12 @@ class PubSubComponent(NamedLockMixin, BasePlugin):
|
|
342
334
|
self, from_: JidStr, to: JidStr, cached_avatar: Optional[CachedAvatar]
|
343
335
|
) -> None:
|
344
336
|
if cached_avatar is None:
|
345
|
-
await self.
|
337
|
+
await self.__broadcast(AvatarMetadata(), from_, to)
|
346
338
|
else:
|
347
339
|
pep_avatar = PepAvatar()
|
348
340
|
pep_avatar.set_avatar_from_cache(cached_avatar)
|
349
341
|
assert pep_avatar.metadata is not None
|
350
|
-
await self.
|
342
|
+
await self.__broadcast(
|
351
343
|
pep_avatar.metadata, from_, to, id=pep_avatar.metadata["info"]["id"]
|
352
344
|
)
|
353
345
|
|
@@ -360,7 +352,7 @@ class PubSubComponent(NamedLockMixin, BasePlugin):
|
|
360
352
|
jid = JID(jid)
|
361
353
|
nickname = PepNick(nick)
|
362
354
|
log.debug("New nickname: %s", nickname.nick)
|
363
|
-
self.xmpp.loop.create_task(self.
|
355
|
+
self.xmpp.loop.create_task(self.__broadcast(nickname.nick, jid, user_jid.bare))
|
364
356
|
|
365
357
|
|
366
358
|
log = logging.getLogger(__name__)
|
slidge/core/session.py
CHANGED
@@ -481,14 +481,14 @@ class BaseSession(
|
|
481
481
|
"""
|
482
482
|
await muc.on_set_affiliation(contact, "member", reason, None)
|
483
483
|
|
484
|
-
async def on_leave_group(self,
|
484
|
+
async def on_leave_group(self, muc_legacy_id: LegacyGroupIdType):
|
485
485
|
"""
|
486
486
|
Triggered when the user leaves a group via the dedicated slidge command
|
487
487
|
or the :xep:`0077` ``<remove />`` mechanism.
|
488
488
|
|
489
489
|
This should be interpreted as definitely leaving the group.
|
490
490
|
|
491
|
-
:param
|
491
|
+
:param muc_legacy_id: The legacy ID of the group to leave
|
492
492
|
"""
|
493
493
|
raise NotImplementedError
|
494
494
|
|
@@ -728,7 +728,7 @@ class BaseSession(
|
|
728
728
|
# No reason to override this
|
729
729
|
self.xmpp.re_login(self)
|
730
730
|
|
731
|
-
async def get_contact_or_group_or_participant(self, jid: JID):
|
731
|
+
async def get_contact_or_group_or_participant(self, jid: JID, create=True):
|
732
732
|
if jid.bare in (contacts := self.contacts.known_contacts(only_friends=False)):
|
733
733
|
return contacts[jid.bare]
|
734
734
|
if (muc := self.bookmarks.by_jid_only_if_exists(JID(jid.bare))) is not None:
|
@@ -736,6 +736,9 @@ class BaseSession(
|
|
736
736
|
else:
|
737
737
|
muc = None
|
738
738
|
|
739
|
+
if not create:
|
740
|
+
return None
|
741
|
+
|
739
742
|
try:
|
740
743
|
return await self.contacts.by_jid(jid)
|
741
744
|
except XMPPError:
|
@@ -0,0 +1,36 @@
|
|
1
|
+
"""Store contacts caps verstring in DB
|
2
|
+
|
3
|
+
Revision ID: 2461390c0af2
|
4
|
+
Revises: 2b1f45ab7379
|
5
|
+
Create Date: 2024-07-20 08:00:11.675735
|
6
|
+
|
7
|
+
"""
|
8
|
+
|
9
|
+
from typing import Sequence, Union
|
10
|
+
|
11
|
+
import sqlalchemy as sa
|
12
|
+
from alembic import op
|
13
|
+
|
14
|
+
# revision identifiers, used by Alembic.
|
15
|
+
revision: str = "2461390c0af2"
|
16
|
+
down_revision: Union[str, None] = "2b1f45ab7379"
|
17
|
+
branch_labels: Union[str, Sequence[str], None] = None
|
18
|
+
depends_on: Union[str, Sequence[str], None] = None
|
19
|
+
|
20
|
+
|
21
|
+
def upgrade() -> None:
|
22
|
+
# ### commands auto generated by Alembic - please adjust! ###
|
23
|
+
with op.batch_alter_table("contact", schema=None) as batch_op:
|
24
|
+
batch_op.add_column(sa.Column("caps_ver_bare", sa.String(), nullable=True))
|
25
|
+
batch_op.add_column(sa.Column("caps_ver", sa.String(), nullable=True))
|
26
|
+
|
27
|
+
# ### end Alembic commands ###
|
28
|
+
|
29
|
+
|
30
|
+
def downgrade() -> None:
|
31
|
+
# ### commands auto generated by Alembic - please adjust! ###
|
32
|
+
with op.batch_alter_table("contact", schema=None) as batch_op:
|
33
|
+
batch_op.drop_column("caps_ver")
|
34
|
+
batch_op.drop_column("caps_ver_bare")
|
35
|
+
|
36
|
+
# ### end Alembic commands ###
|
@@ -0,0 +1,41 @@
|
|
1
|
+
"""Store room subject setter by nickname
|
2
|
+
|
3
|
+
Revision ID: 2b1f45ab7379
|
4
|
+
Revises: c4a8ec35a0e8
|
5
|
+
Create Date: 2024-07-20 00:14:36.882689
|
6
|
+
|
7
|
+
"""
|
8
|
+
|
9
|
+
from typing import Sequence, Union
|
10
|
+
|
11
|
+
import sqlalchemy as sa
|
12
|
+
from alembic import op
|
13
|
+
|
14
|
+
# revision identifiers, used by Alembic.
|
15
|
+
revision: str = "2b1f45ab7379"
|
16
|
+
down_revision: Union[str, None] = "c4a8ec35a0e8"
|
17
|
+
branch_labels: Union[str, Sequence[str], None] = None
|
18
|
+
depends_on: Union[str, Sequence[str], None] = None
|
19
|
+
|
20
|
+
|
21
|
+
def upgrade() -> None:
|
22
|
+
# ### commands auto generated by Alembic - please adjust! ###
|
23
|
+
with op.batch_alter_table("room", schema=None) as batch_op:
|
24
|
+
batch_op.add_column(sa.Column("subject_setter", sa.String(), nullable=True))
|
25
|
+
batch_op.drop_constraint("subject_setter_id_foreign_key", type_="foreignkey")
|
26
|
+
batch_op.drop_column("subject_setter_id")
|
27
|
+
# ### end Alembic commands ###
|
28
|
+
|
29
|
+
|
30
|
+
def downgrade() -> None:
|
31
|
+
# ### commands auto generated by Alembic - please adjust! ###
|
32
|
+
with op.batch_alter_table("room", schema=None) as batch_op:
|
33
|
+
batch_op.add_column(sa.Column("subject_setter_id", sa.INTEGER(), nullable=True))
|
34
|
+
batch_op.create_foreign_key(
|
35
|
+
"subject_setter_id_foreign_key",
|
36
|
+
"participant",
|
37
|
+
["subject_setter_id"],
|
38
|
+
["id"],
|
39
|
+
)
|
40
|
+
batch_op.drop_column("subject_setter")
|
41
|
+
# ### end Alembic commands ###
|
@@ -0,0 +1,48 @@
|
|
1
|
+
"""Add MUC.history_filled
|
2
|
+
|
3
|
+
Also drop caps_ver_bare column that should never have been added.
|
4
|
+
|
5
|
+
Revision ID: 82a4af84b679
|
6
|
+
Revises: 2461390c0af2
|
7
|
+
Create Date: 2024-07-22 07:01:05.352737
|
8
|
+
|
9
|
+
"""
|
10
|
+
|
11
|
+
from typing import Sequence, Union
|
12
|
+
|
13
|
+
import sqlalchemy as sa
|
14
|
+
from alembic import op
|
15
|
+
|
16
|
+
# revision identifiers, used by Alembic.
|
17
|
+
revision: str = "82a4af84b679"
|
18
|
+
down_revision: Union[str, None] = "2461390c0af2"
|
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
|
+
with op.batch_alter_table("contact", schema=None) as batch_op:
|
26
|
+
batch_op.drop_column("caps_ver_bare")
|
27
|
+
|
28
|
+
with op.batch_alter_table("room", schema=None) as batch_op:
|
29
|
+
batch_op.add_column(
|
30
|
+
sa.Column(
|
31
|
+
"history_filled",
|
32
|
+
sa.Boolean(),
|
33
|
+
nullable=False,
|
34
|
+
server_default=sa.False_(), # manually added
|
35
|
+
)
|
36
|
+
)
|
37
|
+
# ### end Alembic commands ###
|
38
|
+
|
39
|
+
|
40
|
+
def downgrade() -> None:
|
41
|
+
# ### commands auto generated by Alembic - please adjust! ###
|
42
|
+
with op.batch_alter_table("room", schema=None) as batch_op:
|
43
|
+
batch_op.drop_column("history_filled")
|
44
|
+
|
45
|
+
with op.batch_alter_table("contact", schema=None) as batch_op:
|
46
|
+
batch_op.add_column(sa.Column("caps_ver_bare", sa.VARCHAR(), nullable=True))
|
47
|
+
|
48
|
+
# ### end Alembic commands ###
|
@@ -9,6 +9,7 @@ Create Date: 2024-04-17 20:57:01.357041
|
|
9
9
|
"""
|
10
10
|
|
11
11
|
import logging
|
12
|
+
from datetime import datetime
|
12
13
|
from typing import Sequence, Union
|
13
14
|
|
14
15
|
import sqlalchemy as sa
|
@@ -43,7 +44,11 @@ def upgrade() -> None:
|
|
43
44
|
sa.UniqueConstraint("jid"),
|
44
45
|
)
|
45
46
|
# ### end Alembic commands ###
|
46
|
-
|
47
|
+
try:
|
48
|
+
migrate_from_shelf(accounts)
|
49
|
+
except Exception:
|
50
|
+
downgrade()
|
51
|
+
raise
|
47
52
|
|
48
53
|
|
49
54
|
def downgrade() -> None:
|
@@ -67,7 +72,11 @@ def migrate_from_shelf(accounts: sa.Table) -> None:
|
|
67
72
|
[
|
68
73
|
{
|
69
74
|
"jid": user.jid,
|
70
|
-
"registration_date":
|
75
|
+
"registration_date": (
|
76
|
+
user.registration_date
|
77
|
+
if user.registration_date is not None
|
78
|
+
else datetime.now()
|
79
|
+
),
|
71
80
|
"legacy_module_data": user.registration_form,
|
72
81
|
"preferences": {},
|
73
82
|
}
|
@@ -0,0 +1,48 @@
|
|
1
|
+
"""Add source and legacy ID for archived messages
|
2
|
+
|
3
|
+
Revision ID: b64b1a793483
|
4
|
+
Revises: 82a4af84b679
|
5
|
+
Create Date: 2024-07-22 21:06:35.020569
|
6
|
+
|
7
|
+
"""
|
8
|
+
|
9
|
+
from typing import Sequence, Union
|
10
|
+
|
11
|
+
import sqlalchemy as sa
|
12
|
+
from alembic import op
|
13
|
+
|
14
|
+
from slidge.db.models import ArchivedMessage
|
15
|
+
|
16
|
+
# revision identifiers, used by Alembic.
|
17
|
+
revision: str = "b64b1a793483"
|
18
|
+
down_revision: Union[str, None] = "82a4af84b679"
|
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
|
+
# since we don't want source to be nullable, we drop all rows first.
|
25
|
+
# This is what you get by using alpha versions!
|
26
|
+
op.execute(sa.delete(ArchivedMessage))
|
27
|
+
# ### commands auto generated by Alembic - please adjust! ###
|
28
|
+
|
29
|
+
with op.batch_alter_table("mam", schema=None) as batch_op:
|
30
|
+
batch_op.add_column(
|
31
|
+
sa.Column(
|
32
|
+
"source",
|
33
|
+
sa.Enum("LIVE", "BACKFILL", name="archivedmessagesource"),
|
34
|
+
nullable=False,
|
35
|
+
)
|
36
|
+
)
|
37
|
+
batch_op.add_column(sa.Column("legacy_id", sa.String(), nullable=True))
|
38
|
+
|
39
|
+
# ### end Alembic commands ###
|
40
|
+
|
41
|
+
|
42
|
+
def downgrade() -> None:
|
43
|
+
# ### commands auto generated by Alembic - please adjust! ###
|
44
|
+
with op.batch_alter_table("mam", schema=None) as batch_op:
|
45
|
+
batch_op.drop_column("legacy_id")
|
46
|
+
batch_op.drop_column("source")
|
47
|
+
|
48
|
+
# ### end Alembic commands ###
|
@@ -0,0 +1,34 @@
|
|
1
|
+
"""Per-room user nick
|
2
|
+
|
3
|
+
Revision ID: c4a8ec35a0e8
|
4
|
+
Revises: 09f27f098baa
|
5
|
+
Create Date: 2024-07-12 06:27:47.397925
|
6
|
+
|
7
|
+
"""
|
8
|
+
|
9
|
+
from typing import Sequence, Union
|
10
|
+
|
11
|
+
import sqlalchemy as sa
|
12
|
+
from alembic import op
|
13
|
+
|
14
|
+
# revision identifiers, used by Alembic.
|
15
|
+
revision: str = "c4a8ec35a0e8"
|
16
|
+
down_revision: Union[str, None] = "09f27f098baa"
|
17
|
+
branch_labels: Union[str, Sequence[str], None] = None
|
18
|
+
depends_on: Union[str, Sequence[str], None] = None
|
19
|
+
|
20
|
+
|
21
|
+
def upgrade() -> None:
|
22
|
+
# ### commands auto generated by Alembic - please adjust! ###
|
23
|
+
with op.batch_alter_table("room", schema=None) as batch_op:
|
24
|
+
batch_op.add_column(sa.Column("user_nick", sa.String(), nullable=True))
|
25
|
+
|
26
|
+
# ### end Alembic commands ###
|
27
|
+
|
28
|
+
|
29
|
+
def downgrade() -> None:
|
30
|
+
# ### commands auto generated by Alembic - please adjust! ###
|
31
|
+
with op.batch_alter_table("room", schema=None) as batch_op:
|
32
|
+
batch_op.drop_column("user_nick")
|
33
|
+
|
34
|
+
# ### end Alembic commands ###
|
slidge/db/avatar.py
CHANGED
@@ -70,6 +70,17 @@ class AvatarCache:
|
|
70
70
|
def set_dir(self, path: Path):
|
71
71
|
self.dir = path
|
72
72
|
self.dir.mkdir(exist_ok=True)
|
73
|
+
with self.store.session():
|
74
|
+
for stored in self.store.get_all():
|
75
|
+
avatar = CachedAvatar.from_store(stored, root_dir=path)
|
76
|
+
if avatar.path.exists():
|
77
|
+
continue
|
78
|
+
log.warning(
|
79
|
+
"Removing avatar %s from store because %s does not exist",
|
80
|
+
avatar.hash,
|
81
|
+
avatar.path,
|
82
|
+
)
|
83
|
+
self.store.delete_by_pk(stored.id)
|
73
84
|
|
74
85
|
def close(self):
|
75
86
|
self._thread_pool.shutdown(cancel_futures=True)
|
@@ -142,15 +153,15 @@ class AvatarCache:
|
|
142
153
|
|
143
154
|
if isinstance(avatar, (URL, str)):
|
144
155
|
if unique_id is None:
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
156
|
+
with self.store.session():
|
157
|
+
stored = self.store.get_by_url(avatar)
|
158
|
+
try:
|
159
|
+
img, response_headers = await self.__download(
|
160
|
+
avatar, self.__get_http_headers(stored)
|
161
|
+
)
|
162
|
+
except NotModified:
|
163
|
+
assert stored is not None
|
164
|
+
return CachedAvatar.from_store(stored, self.dir)
|
154
165
|
else:
|
155
166
|
img, _ = await self.__download(avatar, {})
|
156
167
|
response_headers = None
|
slidge/db/models.py
CHANGED
@@ -24,6 +24,16 @@ class XmppToLegacyEnum(IntEnum):
|
|
24
24
|
THREAD = 3
|
25
25
|
|
26
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
|
+
|
27
37
|
class GatewayUser(Base):
|
28
38
|
"""
|
29
39
|
A user, registered to the gateway component.
|
@@ -145,6 +155,7 @@ class Contact(Base):
|
|
145
155
|
ptype: Mapped[Optional[str]] = mapped_column(nullable=True)
|
146
156
|
pstatus: Mapped[Optional[str]] = mapped_column(nullable=True)
|
147
157
|
pshow: Mapped[Optional[str]] = mapped_column(nullable=True)
|
158
|
+
caps_ver: Mapped[Optional[str]] = mapped_column(nullable=True)
|
148
159
|
|
149
160
|
is_friend: Mapped[bool] = mapped_column(default=False)
|
150
161
|
added_to_roster: Mapped[bool] = mapped_column(default=False)
|
@@ -195,20 +206,17 @@ class Room(Base):
|
|
195
206
|
description: Mapped[Optional[str]] = mapped_column(nullable=True)
|
196
207
|
subject: Mapped[Optional[str]] = mapped_column(nullable=True)
|
197
208
|
subject_date: Mapped[Optional[datetime]] = mapped_column(nullable=True)
|
198
|
-
|
199
|
-
ForeignKey("participant.id"), nullable=True
|
200
|
-
)
|
201
|
-
subject_setter: Mapped["Participant"] = relationship(
|
202
|
-
foreign_keys="Room.subject_setter_id"
|
203
|
-
)
|
209
|
+
subject_setter: Mapped[Optional[str]] = mapped_column(nullable=True)
|
204
210
|
|
205
211
|
n_participants: Mapped[Optional[int]] = mapped_column(default=None)
|
206
212
|
|
207
213
|
muc_type: Mapped[Optional[MucType]] = mapped_column(default=MucType.GROUP)
|
208
214
|
|
215
|
+
user_nick: Mapped[Optional[str]] = mapped_column()
|
209
216
|
user_resources: Mapped[Optional[str]] = mapped_column(nullable=True)
|
210
217
|
|
211
218
|
participants_filled: Mapped[bool] = mapped_column(default=False)
|
219
|
+
history_filled: Mapped[bool] = mapped_column(default=False)
|
212
220
|
|
213
221
|
extra_attributes: Mapped[Optional[JSONSerializable]] = mapped_column(default=None)
|
214
222
|
updated: Mapped[bool] = mapped_column(default=False)
|
@@ -232,6 +240,8 @@ class ArchivedMessage(Base):
|
|
232
240
|
stanza_id: Mapped[str] = mapped_column(nullable=False)
|
233
241
|
timestamp: Mapped[datetime] = mapped_column(nullable=False)
|
234
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)
|
235
245
|
|
236
246
|
stanza: Mapped[str] = mapped_column(nullable=False)
|
237
247
|
|