slidge 0.2.11__py3-none-any.whl → 0.3.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- slidge/__init__.py +5 -2
- slidge/command/adhoc.py +9 -3
- slidge/command/admin.py +16 -12
- slidge/command/base.py +16 -12
- slidge/command/chat_command.py +25 -16
- slidge/command/user.py +7 -8
- slidge/contact/contact.py +123 -210
- slidge/contact/roster.py +108 -105
- slidge/core/config.py +2 -43
- slidge/core/dispatcher/caps.py +9 -2
- slidge/core/dispatcher/disco.py +13 -3
- slidge/core/dispatcher/message/__init__.py +1 -1
- slidge/core/dispatcher/message/chat_state.py +17 -8
- slidge/core/dispatcher/message/marker.py +7 -5
- slidge/core/dispatcher/message/message.py +120 -93
- slidge/core/dispatcher/muc/__init__.py +1 -1
- slidge/core/dispatcher/muc/admin.py +4 -4
- slidge/core/dispatcher/muc/mam.py +10 -6
- slidge/core/dispatcher/muc/misc.py +4 -2
- slidge/core/dispatcher/muc/owner.py +5 -3
- slidge/core/dispatcher/muc/ping.py +3 -1
- slidge/core/dispatcher/presence.py +26 -15
- slidge/core/dispatcher/registration.py +20 -12
- slidge/core/dispatcher/search.py +7 -3
- slidge/core/dispatcher/session_dispatcher.py +13 -5
- slidge/core/dispatcher/util.py +37 -27
- slidge/core/dispatcher/vcard.py +7 -4
- slidge/core/gateway.py +177 -87
- slidge/core/mixins/__init__.py +1 -11
- slidge/core/mixins/attachment.py +200 -147
- slidge/core/mixins/avatar.py +105 -177
- slidge/core/mixins/base.py +3 -1
- slidge/core/mixins/db.py +50 -2
- slidge/core/mixins/disco.py +1 -1
- slidge/core/mixins/message.py +19 -17
- slidge/core/mixins/message_maker.py +29 -15
- slidge/core/mixins/message_text.py +67 -30
- slidge/core/mixins/presence.py +94 -37
- slidge/core/pubsub.py +42 -47
- slidge/core/session.py +95 -60
- slidge/db/alembic/versions/cef02a8b1451_initial_schema.py +361 -0
- slidge/db/avatar.py +150 -119
- slidge/db/meta.py +33 -22
- slidge/db/models.py +69 -117
- slidge/db/store.py +414 -1094
- slidge/group/archive.py +65 -55
- slidge/group/bookmarks.py +96 -59
- slidge/group/participant.py +150 -144
- slidge/group/room.py +351 -328
- slidge/main.py +34 -22
- slidge/migration.py +17 -29
- slidge/slixfix/__init__.py +20 -4
- slidge/slixfix/delivery_receipt.py +6 -4
- slidge/slixfix/link_preview/link_preview.py +1 -1
- slidge/slixfix/link_preview/stanza.py +1 -1
- slidge/slixfix/roster.py +5 -7
- slidge/slixfix/xep_0077/register.py +8 -8
- slidge/slixfix/xep_0077/stanza.py +7 -7
- slidge/slixfix/xep_0100/gateway.py +12 -13
- slidge/slixfix/xep_0153/vcard_avatar.py +1 -1
- slidge/slixfix/xep_0292/vcard4.py +12 -2
- slidge/util/archive_msg.py +11 -5
- slidge/util/conf.py +27 -21
- slidge/util/jid_escaping.py +1 -1
- slidge/{core/mixins → util}/lock.py +6 -6
- slidge/util/test.py +30 -29
- slidge/util/types.py +24 -18
- slidge/util/util.py +26 -22
- {slidge-0.2.11.dist-info → slidge-0.3.0.dist-info}/METADATA +1 -1
- slidge-0.3.0.dist-info/RECORD +95 -0
- {slidge-0.2.11.dist-info → slidge-0.3.0.dist-info}/WHEEL +1 -1
- 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/45c24cc73c91_add_bob.py +0 -42
- slidge/db/alembic/versions/5bd48bfdffa2_lift_room_legacy_id_constraint.py +0 -61
- 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.2.11.dist-info/RECORD +0 -112
- {slidge-0.2.11.dist-info → slidge-0.3.0.dist-info}/entry_points.txt +0 -0
- {slidge-0.2.11.dist-info → slidge-0.3.0.dist-info}/licenses/LICENSE +0 -0
- {slidge-0.2.11.dist-info → slidge-0.3.0.dist-info}/top_level.txt +0 -0
slidge/db/store.py
CHANGED
@@ -1,550 +1,311 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
3
|
import hashlib
|
4
|
-
import json
|
5
4
|
import logging
|
6
5
|
import uuid
|
7
|
-
from contextlib import contextmanager
|
8
6
|
from datetime import datetime, timedelta, timezone
|
9
7
|
from mimetypes import guess_extension
|
10
|
-
from typing import
|
8
|
+
from typing import Collection, Iterator, Optional, Type
|
11
9
|
|
12
|
-
from slixmpp import JID, Iq, Message, Presence
|
13
10
|
from slixmpp.exceptions import XMPPError
|
14
11
|
from slixmpp.plugins.xep_0231.stanza import BitsOfBinary
|
15
12
|
from sqlalchemy import Engine, delete, select, update
|
16
|
-
from sqlalchemy.
|
17
|
-
from sqlalchemy.
|
13
|
+
from sqlalchemy.exc import InvalidRequestError
|
14
|
+
from sqlalchemy.orm import Session, attributes, sessionmaker
|
18
15
|
|
19
16
|
from ..core import config
|
20
17
|
from ..util.archive_msg import HistoryMessage
|
21
|
-
from ..util.types import
|
22
|
-
URL,
|
23
|
-
CachedPresence,
|
24
|
-
ClientType,
|
25
|
-
MamMetadata,
|
26
|
-
MucAffiliation,
|
27
|
-
MucRole,
|
28
|
-
Sticker,
|
29
|
-
)
|
30
|
-
from ..util.types import Hat as HatTuple
|
18
|
+
from ..util.types import MamMetadata, Sticker
|
31
19
|
from .meta import Base
|
32
20
|
from .models import (
|
33
21
|
ArchivedMessage,
|
34
22
|
ArchivedMessageSource,
|
35
|
-
Attachment,
|
36
|
-
Avatar,
|
37
23
|
Bob,
|
38
24
|
Contact,
|
39
25
|
ContactSent,
|
26
|
+
DirectMessages,
|
27
|
+
DirectThreads,
|
40
28
|
GatewayUser,
|
41
|
-
|
42
|
-
|
29
|
+
GroupMessages,
|
30
|
+
GroupThreads,
|
43
31
|
Participant,
|
44
32
|
Room,
|
45
|
-
XmppIdsMulti,
|
46
|
-
XmppToLegacyEnum,
|
47
|
-
XmppToLegacyIds,
|
48
|
-
participant_hats,
|
49
33
|
)
|
50
34
|
|
51
|
-
if TYPE_CHECKING:
|
52
|
-
from ..contact.contact import LegacyContact
|
53
|
-
from ..group.participant import LegacyParticipant
|
54
|
-
from ..group.room import LegacyMUC
|
55
35
|
|
36
|
+
class UpdatedMixin:
|
37
|
+
model: Type[Base] = NotImplemented
|
56
38
|
|
57
|
-
|
58
|
-
|
59
|
-
self._engine = engine
|
39
|
+
def __init__(self, session: Session) -> None:
|
40
|
+
session.execute(update(self.model).values(updated=False))
|
60
41
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
def session(self, **session_kwargs) -> Iterator[Session]:
|
65
|
-
global _session
|
66
|
-
if _session is not None:
|
67
|
-
yield _session
|
68
|
-
return
|
69
|
-
with Session(self._engine, **session_kwargs) as session:
|
70
|
-
_session = session
|
71
|
-
try:
|
72
|
-
yield session
|
73
|
-
finally:
|
74
|
-
_session = None
|
42
|
+
def get_by_pk(self, session: Session, pk: int) -> Type[Base]:
|
43
|
+
stmt = select(self.model).where(self.model.id == pk) # type:ignore
|
44
|
+
return session.scalar(stmt)
|
75
45
|
|
76
46
|
|
77
|
-
class
|
78
|
-
|
47
|
+
class SlidgeStore:
|
48
|
+
def __init__(self, engine: Engine) -> None:
|
49
|
+
self._engine = engine
|
50
|
+
self.session = sessionmaker(engine)
|
79
51
|
|
80
|
-
|
81
|
-
|
52
|
+
self.users = UserStore(self.session)
|
53
|
+
self.avatars = AvatarStore(self.session)
|
54
|
+
self.id_map = IdMapStore()
|
55
|
+
self.bob = BobStore()
|
82
56
|
with self.session() as session:
|
83
|
-
|
57
|
+
self.contacts = ContactStore(session)
|
58
|
+
self.mam = MAMStore(session, self.session)
|
59
|
+
self.rooms = RoomStore(session)
|
60
|
+
self.participants = ParticipantStore(session)
|
84
61
|
session.commit()
|
85
62
|
|
86
|
-
def get_by_pk(self, pk: int) -> Optional[Base]:
|
87
|
-
with self.session() as session:
|
88
|
-
return session.execute(
|
89
|
-
select(self.model).where(self.model.id == pk) # type:ignore
|
90
|
-
).scalar()
|
91
63
|
|
64
|
+
class UserStore:
|
65
|
+
def __init__(self, session_maker) -> None:
|
66
|
+
self.session = session_maker
|
92
67
|
|
93
|
-
|
94
|
-
def __init__(self, engine: Engine):
|
95
|
-
super().__init__(engine)
|
96
|
-
self.users = UserStore(engine)
|
97
|
-
self.avatars = AvatarStore(engine)
|
98
|
-
self.contacts = ContactStore(engine)
|
99
|
-
self.mam = MAMStore(engine)
|
100
|
-
self.multi = MultiStore(engine)
|
101
|
-
self.attachments = AttachmentStore(engine)
|
102
|
-
self.rooms = RoomStore(engine)
|
103
|
-
self.sent = SentStore(engine)
|
104
|
-
self.participants = ParticipantStore(engine)
|
105
|
-
self.bob = BobStore(engine)
|
106
|
-
|
107
|
-
|
108
|
-
class UserStore(EngineMixin):
|
109
|
-
def new(self, jid: JID, legacy_module_data: dict) -> GatewayUser:
|
110
|
-
if jid.resource:
|
111
|
-
jid = JID(jid.bare)
|
68
|
+
def update(self, user: GatewayUser) -> None:
|
112
69
|
with self.session(expire_on_commit=False) as session:
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
session.add(user)
|
120
|
-
session.commit()
|
121
|
-
return user
|
122
|
-
|
123
|
-
def update(self, user: GatewayUser):
|
124
|
-
# https://github.com/sqlalchemy/sqlalchemy/discussions/6473
|
125
|
-
attributes.flag_modified(user, "legacy_module_data")
|
126
|
-
attributes.flag_modified(user, "preferences")
|
127
|
-
with self.session() as session:
|
70
|
+
# https://github.com/sqlalchemy/sqlalchemy/discussions/6473
|
71
|
+
try:
|
72
|
+
attributes.flag_modified(user, "legacy_module_data")
|
73
|
+
attributes.flag_modified(user, "preferences")
|
74
|
+
except InvalidRequestError:
|
75
|
+
pass
|
128
76
|
session.add(user)
|
129
77
|
session.commit()
|
130
78
|
|
131
|
-
def get_all(self) -> Iterator[GatewayUser]:
|
132
|
-
with self.session() as session:
|
133
|
-
yield from session.execute(select(GatewayUser)).scalars()
|
134
|
-
|
135
|
-
def get(self, jid: JID) -> Optional[GatewayUser]:
|
136
|
-
with self.session() as session:
|
137
|
-
return session.execute(
|
138
|
-
select(GatewayUser).where(GatewayUser.jid == jid.bare)
|
139
|
-
).scalar()
|
140
|
-
|
141
|
-
def get_by_stanza(self, stanza: Iq | Message | Presence) -> Optional[GatewayUser]:
|
142
|
-
return self.get(stanza.get_from())
|
143
|
-
|
144
|
-
def delete(self, jid: JID) -> None:
|
145
|
-
with self.session() as session:
|
146
|
-
session.delete(self.get(jid))
|
147
|
-
session.commit()
|
148
|
-
|
149
|
-
def set_avatar_hash(self, pk: int, h: str | None = None) -> None:
|
150
|
-
with self.session() as session:
|
151
|
-
session.execute(
|
152
|
-
update(GatewayUser).where(GatewayUser.id == pk).values(avatar_hash=h)
|
153
|
-
)
|
154
|
-
session.commit()
|
155
|
-
|
156
|
-
|
157
|
-
class AvatarStore(EngineMixin):
|
158
|
-
def get_by_url(self, url: URL | str) -> Optional[Avatar]:
|
159
|
-
with self.session() as session:
|
160
|
-
return session.execute(select(Avatar).where(Avatar.url == url)).scalar()
|
161
79
|
|
162
|
-
|
163
|
-
|
164
|
-
|
80
|
+
class AvatarStore:
|
81
|
+
def __init__(self, session_maker) -> None:
|
82
|
+
self.session = session_maker
|
165
83
|
|
166
|
-
def delete_by_pk(self, pk: int):
|
167
|
-
with self.session() as session:
|
168
|
-
session.execute(delete(Avatar).where(Avatar.id == pk))
|
169
|
-
session.commit()
|
170
84
|
|
171
|
-
|
172
|
-
|
173
|
-
|
85
|
+
LegacyToXmppType = (
|
86
|
+
Type[DirectMessages]
|
87
|
+
| Type[DirectThreads]
|
88
|
+
| Type[GroupMessages]
|
89
|
+
| Type[GroupThreads]
|
90
|
+
)
|
174
91
|
|
175
92
|
|
176
|
-
class
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
93
|
+
class IdMapStore:
|
94
|
+
@staticmethod
|
95
|
+
def _set(
|
96
|
+
session: Session,
|
97
|
+
foreign_key: int,
|
98
|
+
legacy_id: str,
|
99
|
+
xmpp_ids: list[str],
|
100
|
+
type_: LegacyToXmppType,
|
101
|
+
) -> None:
|
102
|
+
kwargs = dict(foreign_key=foreign_key, legacy_id=legacy_id)
|
103
|
+
ids = session.scalars(
|
104
|
+
select(type_.id).filter(
|
105
|
+
type_.foreign_key == foreign_key, type_.legacy_id == legacy_id
|
185
106
|
)
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
msg
|
192
|
-
msg.type = XmppToLegacyEnum.DM
|
107
|
+
)
|
108
|
+
if ids:
|
109
|
+
log.debug("Resetting legacy ID %s", legacy_id)
|
110
|
+
session.execute(delete(type_).where(type_.id.in_(ids)))
|
111
|
+
for xmpp_id in xmpp_ids:
|
112
|
+
msg = type_(xmpp_id=xmpp_id, **kwargs)
|
193
113
|
session.add(msg)
|
194
|
-
session.commit()
|
195
114
|
|
196
|
-
def
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
115
|
+
def set_thread(
|
116
|
+
self,
|
117
|
+
session: Session,
|
118
|
+
foreign_key: int,
|
119
|
+
legacy_id: str,
|
120
|
+
xmpp_id: str,
|
121
|
+
group: bool,
|
122
|
+
) -> None:
|
123
|
+
self._set(
|
124
|
+
session,
|
125
|
+
foreign_key,
|
126
|
+
legacy_id,
|
127
|
+
[xmpp_id],
|
128
|
+
GroupThreads if group else DirectThreads,
|
129
|
+
)
|
204
130
|
|
205
|
-
def
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
131
|
+
def set_msg(
|
132
|
+
self,
|
133
|
+
session: Session,
|
134
|
+
foreign_key: int,
|
135
|
+
legacy_id: str,
|
136
|
+
xmpp_ids: list[str],
|
137
|
+
group: bool,
|
138
|
+
) -> None:
|
139
|
+
self._set(
|
140
|
+
session,
|
141
|
+
foreign_key,
|
142
|
+
legacy_id,
|
143
|
+
xmpp_ids,
|
144
|
+
GroupMessages if group else DirectMessages,
|
145
|
+
)
|
213
146
|
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
147
|
+
@staticmethod
|
148
|
+
def _get(
|
149
|
+
session: Session, foreign_key: int, legacy_id: str, type_: LegacyToXmppType
|
150
|
+
) -> list[str]:
|
151
|
+
return list(
|
152
|
+
session.scalars(
|
153
|
+
select(type_.xmpp_id).filter_by(
|
154
|
+
foreign_key=foreign_key, legacy_id=legacy_id
|
155
|
+
)
|
221
156
|
)
|
222
|
-
|
223
|
-
session.commit()
|
157
|
+
)
|
224
158
|
|
225
|
-
def
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
159
|
+
def get_xmpp(
|
160
|
+
self, session: Session, foreign_key: int, legacy_id: str, group: bool
|
161
|
+
) -> list[str]:
|
162
|
+
return self._get(
|
163
|
+
session,
|
164
|
+
foreign_key,
|
165
|
+
legacy_id,
|
166
|
+
GroupMessages if group else DirectMessages,
|
167
|
+
)
|
233
168
|
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
).scalar()
|
169
|
+
@staticmethod
|
170
|
+
def _get_legacy(
|
171
|
+
session: Session, foreign_key: int, xmpp_id: str, type_: LegacyToXmppType
|
172
|
+
) -> Optional[str]:
|
173
|
+
return session.scalar(
|
174
|
+
select(type_.legacy_id).filter_by(foreign_key=foreign_key, xmpp_id=xmpp_id)
|
175
|
+
)
|
242
176
|
|
243
|
-
def
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
session.commit()
|
177
|
+
def get_legacy(
|
178
|
+
self, session: Session, foreign_key: int, xmpp_id: str, group: bool
|
179
|
+
) -> Optional[str]:
|
180
|
+
return self._get_legacy(
|
181
|
+
session,
|
182
|
+
foreign_key,
|
183
|
+
xmpp_id,
|
184
|
+
GroupMessages if group else DirectMessages,
|
185
|
+
)
|
253
186
|
|
254
|
-
def
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
187
|
+
def get_thread(
|
188
|
+
self, session: Session, foreign_key: int, xmpp_id: str, group: bool
|
189
|
+
) -> Optional[str]:
|
190
|
+
return self._get_legacy(
|
191
|
+
session,
|
192
|
+
foreign_key,
|
193
|
+
xmpp_id,
|
194
|
+
GroupThreads if group else DirectThreads,
|
195
|
+
)
|
262
196
|
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
).
|
271
|
-
|
272
|
-
|
197
|
+
@staticmethod
|
198
|
+
def was_sent_by_user(
|
199
|
+
session: Session, foreign_key: int, legacy_id: str, group: bool
|
200
|
+
) -> bool:
|
201
|
+
type_ = GroupMessages if group else DirectMessages
|
202
|
+
return (
|
203
|
+
session.scalar(
|
204
|
+
select(type_.id).filter_by(foreign_key=foreign_key, legacy_id=legacy_id)
|
205
|
+
)
|
206
|
+
is not None
|
207
|
+
)
|
273
208
|
|
274
209
|
|
275
210
|
class ContactStore(UpdatedMixin):
|
276
211
|
model = Contact
|
277
212
|
|
278
|
-
def __init__(self,
|
279
|
-
super().__init__(
|
280
|
-
|
281
|
-
session.execute(update(Contact).values(cached_presence=False))
|
282
|
-
session.commit()
|
283
|
-
|
284
|
-
def get_all(self, user_pk: int) -> Iterator[Contact]:
|
285
|
-
with self.session() as session:
|
286
|
-
yield from session.execute(
|
287
|
-
select(Contact).where(Contact.user_account_id == user_pk)
|
288
|
-
).scalars()
|
289
|
-
|
290
|
-
def get_by_jid(self, user_pk: int, jid: JID) -> Optional[Contact]:
|
291
|
-
with self.session() as session:
|
292
|
-
return session.execute(
|
293
|
-
select(Contact)
|
294
|
-
.where(Contact.jid == jid.bare)
|
295
|
-
.where(Contact.user_account_id == user_pk)
|
296
|
-
).scalar()
|
297
|
-
|
298
|
-
def get_by_legacy_id(self, user_pk: int, legacy_id: str) -> Optional[Contact]:
|
299
|
-
with self.session() as session:
|
300
|
-
return session.execute(
|
301
|
-
select(Contact)
|
302
|
-
.where(Contact.legacy_id == legacy_id)
|
303
|
-
.where(Contact.user_account_id == user_pk)
|
304
|
-
).scalar()
|
305
|
-
|
306
|
-
def update_nick(self, contact_pk: int, nick: Optional[str]) -> None:
|
307
|
-
with self.session() as session:
|
308
|
-
session.execute(
|
309
|
-
update(Contact).where(Contact.id == contact_pk).values(nick=nick)
|
310
|
-
)
|
311
|
-
session.commit()
|
213
|
+
def __init__(self, session: Session) -> None:
|
214
|
+
super().__init__(session)
|
215
|
+
session.execute(update(Contact).values(cached_presence=False))
|
312
216
|
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
return None
|
326
|
-
return CachedPresence(*presence[:-1])
|
327
|
-
|
328
|
-
def set_presence(self, contact_pk: int, presence: CachedPresence) -> None:
|
329
|
-
with self.session() as session:
|
330
|
-
session.execute(
|
331
|
-
update(Contact)
|
332
|
-
.where(Contact.id == contact_pk)
|
333
|
-
.values(**presence._asdict(), cached_presence=True)
|
334
|
-
)
|
335
|
-
session.commit()
|
336
|
-
|
337
|
-
def reset_presence(self, contact_pk: int):
|
338
|
-
with self.session() as session:
|
339
|
-
session.execute(
|
340
|
-
update(Contact)
|
341
|
-
.where(Contact.id == contact_pk)
|
342
|
-
.values(
|
343
|
-
last_seen=None,
|
344
|
-
ptype=None,
|
345
|
-
pstatus=None,
|
346
|
-
pshow=None,
|
347
|
-
cached_presence=False,
|
348
|
-
)
|
349
|
-
)
|
350
|
-
session.commit()
|
351
|
-
|
352
|
-
def set_avatar(
|
353
|
-
self, contact_pk: int, avatar_pk: Optional[int], avatar_legacy_id: Optional[str]
|
354
|
-
):
|
355
|
-
with self.session() as session:
|
356
|
-
session.execute(
|
357
|
-
update(Contact)
|
358
|
-
.where(Contact.id == contact_pk)
|
359
|
-
.values(avatar_id=avatar_pk, avatar_legacy_id=avatar_legacy_id)
|
360
|
-
)
|
361
|
-
session.commit()
|
362
|
-
|
363
|
-
def get_avatar_legacy_id(self, contact_pk: int) -> Optional[str]:
|
364
|
-
with self.session() as session:
|
365
|
-
contact = session.execute(
|
366
|
-
select(Contact).where(Contact.id == contact_pk)
|
367
|
-
).scalar()
|
368
|
-
if contact is None or contact.avatar is None:
|
369
|
-
return None
|
370
|
-
return contact.avatar_legacy_id
|
371
|
-
|
372
|
-
def update(self, contact: "LegacyContact", commit=True) -> int:
|
373
|
-
with self.session() as session:
|
374
|
-
if contact.contact_pk is None:
|
375
|
-
if contact.cached_presence is not None:
|
376
|
-
presence_kwargs = contact.cached_presence._asdict()
|
377
|
-
presence_kwargs["cached_presence"] = True
|
378
|
-
else:
|
379
|
-
presence_kwargs = {}
|
380
|
-
row = Contact(
|
381
|
-
jid=contact.jid.bare,
|
382
|
-
legacy_id=str(contact.legacy_id),
|
383
|
-
user_account_id=contact.user_pk,
|
384
|
-
**presence_kwargs,
|
385
|
-
)
|
386
|
-
else:
|
387
|
-
row = (
|
388
|
-
session.query(Contact)
|
389
|
-
.filter(Contact.id == contact.contact_pk)
|
390
|
-
.one()
|
391
|
-
)
|
392
|
-
row.nick = contact.name
|
393
|
-
row.is_friend = contact.is_friend
|
394
|
-
row.added_to_roster = contact.added_to_roster
|
395
|
-
row.updated = True
|
396
|
-
row.extra_attributes = contact.serialize_extra_attributes()
|
397
|
-
row.caps_ver = contact._caps_ver
|
398
|
-
row.vcard = contact._vcard
|
399
|
-
row.vcard_fetched = contact._vcard_fetched
|
400
|
-
row.client_type = contact.client_type
|
401
|
-
session.add(row)
|
402
|
-
if commit:
|
403
|
-
session.commit()
|
404
|
-
return row.id
|
405
|
-
|
406
|
-
def set_vcard(self, contact_pk: int, vcard: str | None) -> None:
|
407
|
-
with self.session() as session:
|
408
|
-
session.execute(
|
409
|
-
update(Contact)
|
410
|
-
.where(Contact.id == contact_pk)
|
411
|
-
.values(vcard=vcard, vcard_fetched=True)
|
412
|
-
)
|
413
|
-
session.commit()
|
414
|
-
|
415
|
-
def add_to_sent(self, contact_pk: int, msg_id: str) -> None:
|
416
|
-
with self.session() as session:
|
417
|
-
if (
|
418
|
-
session.query(ContactSent.id)
|
419
|
-
.where(ContactSent.contact_id == contact_pk)
|
420
|
-
.where(ContactSent.msg_id == msg_id)
|
421
|
-
.first()
|
422
|
-
) is not None:
|
423
|
-
log.warning(
|
424
|
-
"Contact %s has already sent message %s", contact_pk, msg_id
|
425
|
-
)
|
426
|
-
return
|
427
|
-
new = ContactSent(contact_id=contact_pk, msg_id=msg_id)
|
428
|
-
session.add(new)
|
429
|
-
session.commit()
|
217
|
+
@staticmethod
|
218
|
+
def add_to_sent(session: Session, contact_pk: int, msg_id: str) -> None:
|
219
|
+
if (
|
220
|
+
session.query(ContactSent.id)
|
221
|
+
.where(ContactSent.contact_id == contact_pk)
|
222
|
+
.where(ContactSent.msg_id == msg_id)
|
223
|
+
.first()
|
224
|
+
) is not None:
|
225
|
+
log.warning("Contact %s has already sent message %s", contact_pk, msg_id)
|
226
|
+
return
|
227
|
+
new = ContactSent(contact_id=contact_pk, msg_id=msg_id)
|
228
|
+
session.add(new)
|
430
229
|
|
431
|
-
|
230
|
+
@staticmethod
|
231
|
+
def pop_sent_up_to(session: Session, contact_pk: int, msg_id: str) -> list[str]:
|
432
232
|
result = []
|
433
233
|
to_del = []
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
for row_id in to_del:
|
445
|
-
session.execute(delete(ContactSent).where(ContactSent.id == row_id))
|
446
|
-
session.commit()
|
234
|
+
for row in session.execute(
|
235
|
+
select(ContactSent)
|
236
|
+
.where(ContactSent.contact_id == contact_pk)
|
237
|
+
.order_by(ContactSent.id)
|
238
|
+
).scalars():
|
239
|
+
to_del.append(row.id)
|
240
|
+
result.append(row.msg_id)
|
241
|
+
if row.msg_id == msg_id:
|
242
|
+
break
|
243
|
+
session.execute(delete(ContactSent).where(ContactSent.id.in_(to_del)))
|
447
244
|
return result
|
448
245
|
|
449
|
-
def set_friend(self, contact_pk: int, is_friend: bool) -> None:
|
450
|
-
with self.session() as session:
|
451
|
-
session.execute(
|
452
|
-
update(Contact)
|
453
|
-
.where(Contact.id == contact_pk)
|
454
|
-
.values(is_friend=is_friend)
|
455
|
-
)
|
456
|
-
session.commit()
|
457
|
-
|
458
|
-
def set_added_to_roster(self, contact_pk: int, value: bool) -> None:
|
459
|
-
with self.session() as session:
|
460
|
-
session.execute(
|
461
|
-
update(Contact)
|
462
|
-
.where(Contact.id == contact_pk)
|
463
|
-
.values(added_to_roster=value)
|
464
|
-
)
|
465
|
-
session.commit()
|
466
|
-
|
467
|
-
def delete(self, contact_pk: int) -> None:
|
468
|
-
with self.session() as session:
|
469
|
-
session.execute(delete(Contact).where(Contact.id == contact_pk))
|
470
|
-
session.commit()
|
471
|
-
|
472
|
-
def set_client_type(self, contact_pk: int, value: ClientType):
|
473
|
-
with self.session() as session:
|
474
|
-
session.execute(
|
475
|
-
update(Contact)
|
476
|
-
.where(Contact.id == contact_pk)
|
477
|
-
.values(client_type=value)
|
478
|
-
)
|
479
|
-
session.commit()
|
480
|
-
|
481
246
|
|
482
|
-
class MAMStore
|
483
|
-
def __init__(self,
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
)
|
489
|
-
session.commit()
|
247
|
+
class MAMStore:
|
248
|
+
def __init__(self, session: Session, session_maker) -> None:
|
249
|
+
self.session = session_maker
|
250
|
+
session.execute(
|
251
|
+
update(ArchivedMessage).values(source=ArchivedMessageSource.BACKFILL)
|
252
|
+
)
|
490
253
|
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
)
|
254
|
+
@staticmethod
|
255
|
+
def nuke_older_than(session: Session, days: int) -> None:
|
256
|
+
session.execute(
|
257
|
+
delete(ArchivedMessage).where(
|
258
|
+
ArchivedMessage.timestamp < datetime.now() - timedelta(days=days)
|
497
259
|
)
|
498
|
-
|
260
|
+
)
|
499
261
|
|
262
|
+
@staticmethod
|
500
263
|
def add_message(
|
501
|
-
|
264
|
+
session: Session,
|
502
265
|
room_pk: int,
|
503
266
|
message: HistoryMessage,
|
504
267
|
archive_only: bool,
|
505
|
-
legacy_msg_id: str
|
268
|
+
legacy_msg_id: Optional[str],
|
506
269
|
) -> None:
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
270
|
+
source = (
|
271
|
+
ArchivedMessageSource.BACKFILL
|
272
|
+
if archive_only
|
273
|
+
else ArchivedMessageSource.LIVE
|
274
|
+
)
|
275
|
+
existing = session.execute(
|
276
|
+
select(ArchivedMessage)
|
277
|
+
.where(ArchivedMessage.room_id == room_pk)
|
278
|
+
.where(ArchivedMessage.stanza_id == message.id)
|
279
|
+
).scalar()
|
280
|
+
if existing is None and legacy_msg_id is not None:
|
513
281
|
existing = session.execute(
|
514
282
|
select(ArchivedMessage)
|
515
283
|
.where(ArchivedMessage.room_id == room_pk)
|
516
|
-
.where(ArchivedMessage.
|
284
|
+
.where(ArchivedMessage.legacy_id == legacy_msg_id)
|
517
285
|
).scalar()
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
stanza=str(message.stanza),
|
538
|
-
author_jid=message.stanza.get_from(),
|
539
|
-
room_id=room_pk,
|
540
|
-
source=source,
|
541
|
-
legacy_id=legacy_msg_id,
|
542
|
-
)
|
543
|
-
session.add(mam_msg)
|
544
|
-
session.commit()
|
286
|
+
if existing is not None:
|
287
|
+
log.debug("Updating message %s in room %s", message.id, room_pk)
|
288
|
+
existing.timestamp = message.when
|
289
|
+
existing.stanza = str(message.stanza)
|
290
|
+
existing.author_jid = message.stanza.get_from()
|
291
|
+
existing.source = source
|
292
|
+
existing.legacy_id = legacy_msg_id
|
293
|
+
session.add(existing)
|
294
|
+
return
|
295
|
+
mam_msg = ArchivedMessage(
|
296
|
+
stanza_id=message.id,
|
297
|
+
timestamp=message.when,
|
298
|
+
stanza=str(message.stanza),
|
299
|
+
author_jid=message.stanza.get_from(),
|
300
|
+
room_id=room_pk,
|
301
|
+
source=source,
|
302
|
+
legacy_id=legacy_msg_id,
|
303
|
+
)
|
304
|
+
session.add(mam_msg)
|
545
305
|
|
306
|
+
@staticmethod
|
546
307
|
def get_messages(
|
547
|
-
|
308
|
+
session: Session,
|
548
309
|
room_pk: int,
|
549
310
|
start_date: Optional[datetime] = None,
|
550
311
|
end_date: Optional[datetime] = None,
|
@@ -553,612 +314,179 @@ class MAMStore(EngineMixin):
|
|
553
314
|
ids: Collection[str] = (),
|
554
315
|
last_page_n: Optional[int] = None,
|
555
316
|
sender: Optional[str] = None,
|
556
|
-
flip=False,
|
317
|
+
flip: bool = False,
|
557
318
|
) -> Iterator[HistoryMessage]:
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
570
|
-
|
571
|
-
raise XMPPError(
|
572
|
-
"item-not-found",
|
573
|
-
f"Message {before_id} not found",
|
574
|
-
)
|
575
|
-
q = q.where(ArchivedMessage.timestamp < stamp)
|
576
|
-
if after_id is not None:
|
577
|
-
stamp = session.execute(
|
578
|
-
select(ArchivedMessage.timestamp).where(
|
579
|
-
ArchivedMessage.stanza_id == after_id
|
580
|
-
)
|
581
|
-
).scalar()
|
582
|
-
if stamp is None:
|
583
|
-
raise XMPPError(
|
584
|
-
"item-not-found",
|
585
|
-
f"Message {after_id} not found",
|
586
|
-
)
|
587
|
-
q = q.where(ArchivedMessage.timestamp > stamp)
|
588
|
-
if ids:
|
589
|
-
q = q.filter(ArchivedMessage.stanza_id.in_(ids))
|
590
|
-
if sender is not None:
|
591
|
-
q = q.where(ArchivedMessage.author_jid == sender)
|
592
|
-
if flip:
|
593
|
-
q = q.order_by(ArchivedMessage.timestamp.desc())
|
594
|
-
else:
|
595
|
-
q = q.order_by(ArchivedMessage.timestamp.asc())
|
596
|
-
msgs = list(session.execute(q).scalars())
|
597
|
-
if ids and len(msgs) != len(ids):
|
319
|
+
q = select(ArchivedMessage).where(ArchivedMessage.room_id == room_pk)
|
320
|
+
if start_date is not None:
|
321
|
+
q = q.where(ArchivedMessage.timestamp >= start_date)
|
322
|
+
if end_date is not None:
|
323
|
+
q = q.where(ArchivedMessage.timestamp <= end_date)
|
324
|
+
if before_id is not None:
|
325
|
+
stamp = session.execute(
|
326
|
+
select(ArchivedMessage.timestamp).where(
|
327
|
+
ArchivedMessage.stanza_id == before_id,
|
328
|
+
ArchivedMessage.room_id == room_pk,
|
329
|
+
)
|
330
|
+
).scalar_one_or_none()
|
331
|
+
if stamp is None:
|
598
332
|
raise XMPPError(
|
599
333
|
"item-not-found",
|
600
|
-
"
|
601
|
-
"with the given constraints.",
|
334
|
+
f"Message {before_id} not found",
|
602
335
|
)
|
603
|
-
|
604
|
-
|
605
|
-
|
606
|
-
|
607
|
-
|
608
|
-
|
609
|
-
yield HistoryMessage(
|
610
|
-
stanza=str(h.stanza), when=h.timestamp.replace(tzinfo=timezone.utc)
|
336
|
+
q = q.where(ArchivedMessage.timestamp < stamp)
|
337
|
+
if after_id is not None:
|
338
|
+
stamp = session.execute(
|
339
|
+
select(ArchivedMessage.timestamp).where(
|
340
|
+
ArchivedMessage.stanza_id == after_id,
|
341
|
+
ArchivedMessage.room_id == room_pk,
|
611
342
|
)
|
612
|
-
|
613
|
-
|
614
|
-
|
615
|
-
|
616
|
-
|
617
|
-
|
618
|
-
|
343
|
+
).scalar_one_or_none()
|
344
|
+
if stamp is None:
|
345
|
+
raise XMPPError(
|
346
|
+
"item-not-found",
|
347
|
+
f"Message {after_id} not found",
|
348
|
+
)
|
349
|
+
q = q.where(ArchivedMessage.timestamp > stamp)
|
350
|
+
if ids:
|
351
|
+
q = q.filter(ArchivedMessage.stanza_id.in_(ids))
|
352
|
+
if sender is not None:
|
353
|
+
q = q.where(ArchivedMessage.author_jid == sender)
|
354
|
+
if flip:
|
355
|
+
q = q.order_by(ArchivedMessage.timestamp.desc())
|
356
|
+
else:
|
357
|
+
q = q.order_by(ArchivedMessage.timestamp.asc())
|
358
|
+
msgs = list(session.execute(q).scalars())
|
359
|
+
if ids and len(msgs) != len(ids):
|
360
|
+
raise XMPPError(
|
361
|
+
"item-not-found",
|
362
|
+
"One of the requested messages IDs could not be found "
|
363
|
+
"with the given constraints.",
|
364
|
+
)
|
365
|
+
if last_page_n is not None:
|
366
|
+
if flip:
|
367
|
+
msgs = msgs[:last_page_n]
|
368
|
+
else:
|
369
|
+
msgs = msgs[-last_page_n:]
|
370
|
+
for h in msgs:
|
371
|
+
yield HistoryMessage(
|
372
|
+
stanza=str(h.stanza), when=h.timestamp.replace(tzinfo=timezone.utc)
|
619
373
|
)
|
620
|
-
if with_legacy_id:
|
621
|
-
q = q.filter(ArchivedMessage.legacy_id.isnot(None))
|
622
|
-
return session.execute(q).scalar()
|
623
374
|
|
375
|
+
@staticmethod
|
376
|
+
def get_first(
|
377
|
+
session: Session, room_pk: int, with_legacy_id: bool = False
|
378
|
+
) -> Optional[ArchivedMessage]:
|
379
|
+
q = (
|
380
|
+
select(ArchivedMessage)
|
381
|
+
.where(ArchivedMessage.room_id == room_pk)
|
382
|
+
.order_by(ArchivedMessage.timestamp.asc())
|
383
|
+
)
|
384
|
+
if with_legacy_id:
|
385
|
+
q = q.filter(ArchivedMessage.legacy_id.isnot(None))
|
386
|
+
return session.execute(q).scalar()
|
387
|
+
|
388
|
+
@staticmethod
|
624
389
|
def get_last(
|
625
|
-
|
626
|
-
) -> ArchivedMessage
|
627
|
-
|
628
|
-
q = select(ArchivedMessage).where(ArchivedMessage.room_id == room_pk)
|
390
|
+
session: Session, room_pk: int, source: Optional[ArchivedMessageSource] = None
|
391
|
+
) -> Optional[ArchivedMessage]:
|
392
|
+
q = select(ArchivedMessage).where(ArchivedMessage.room_id == room_pk)
|
629
393
|
|
630
|
-
|
631
|
-
|
394
|
+
if source is not None:
|
395
|
+
q = q.where(ArchivedMessage.source == source)
|
632
396
|
|
633
|
-
|
634
|
-
q.order_by(ArchivedMessage.timestamp.desc())
|
635
|
-
).scalar()
|
397
|
+
return session.execute(q.order_by(ArchivedMessage.timestamp.desc())).scalar()
|
636
398
|
|
637
|
-
def get_first_and_last(self, room_pk: int) -> list[MamMetadata]:
|
399
|
+
def get_first_and_last(self, session: Session, room_pk: int) -> list[MamMetadata]:
|
638
400
|
r = []
|
639
|
-
|
640
|
-
|
641
|
-
|
642
|
-
|
643
|
-
|
644
|
-
|
645
|
-
r.append(MamMetadata(last.stanza_id, last.timestamp))
|
401
|
+
first = self.get_first(session, room_pk)
|
402
|
+
if first is not None:
|
403
|
+
r.append(MamMetadata(first.stanza_id, first.timestamp))
|
404
|
+
last = self.get_last(session, room_pk)
|
405
|
+
if last is not None:
|
406
|
+
r.append(MamMetadata(last.stanza_id, last.timestamp))
|
646
407
|
return r
|
647
408
|
|
409
|
+
@staticmethod
|
648
410
|
def get_most_recent_with_legacy_id(
|
649
|
-
|
650
|
-
) -> ArchivedMessage
|
651
|
-
|
652
|
-
|
653
|
-
|
654
|
-
|
655
|
-
|
656
|
-
|
657
|
-
|
658
|
-
|
659
|
-
return session.execute(
|
660
|
-
q.order_by(ArchivedMessage.timestamp.desc())
|
661
|
-
).scalar()
|
411
|
+
session: Session, room_pk: int, source: Optional[ArchivedMessageSource] = None
|
412
|
+
) -> Optional[ArchivedMessage]:
|
413
|
+
q = (
|
414
|
+
select(ArchivedMessage)
|
415
|
+
.where(ArchivedMessage.room_id == room_pk)
|
416
|
+
.where(ArchivedMessage.legacy_id.isnot(None))
|
417
|
+
)
|
418
|
+
if source is not None:
|
419
|
+
q = q.where(ArchivedMessage.source == source)
|
420
|
+
return session.execute(q.order_by(ArchivedMessage.timestamp.desc())).scalar()
|
662
421
|
|
422
|
+
@staticmethod
|
663
423
|
def get_least_recent_with_legacy_id_after(
|
664
|
-
|
665
|
-
|
666
|
-
|
667
|
-
|
668
|
-
|
669
|
-
|
670
|
-
|
671
|
-
|
672
|
-
)
|
673
|
-
|
674
|
-
|
675
|
-
|
676
|
-
|
677
|
-
|
678
|
-
|
679
|
-
)
|
680
|
-
|
681
|
-
|
682
|
-
|
683
|
-
with self.session() as session:
|
684
|
-
return (
|
685
|
-
session.query(ArchivedMessage)
|
686
|
-
.filter(ArchivedMessage.room_id == room_pk)
|
687
|
-
.filter(ArchivedMessage.legacy_id == legacy_id)
|
688
|
-
.first()
|
689
|
-
)
|
690
|
-
|
691
|
-
|
692
|
-
class MultiStore(EngineMixin):
|
693
|
-
def get_xmpp_ids(self, user_pk: int, xmpp_id: str) -> list[str]:
|
694
|
-
with self.session() as session:
|
695
|
-
multi = session.execute(
|
696
|
-
select(XmppIdsMulti)
|
697
|
-
.where(XmppIdsMulti.xmpp_id == xmpp_id)
|
698
|
-
.where(XmppIdsMulti.user_account_id == user_pk)
|
699
|
-
).scalar()
|
700
|
-
if multi is None:
|
701
|
-
return []
|
702
|
-
if multi.legacy_ids_multi is None:
|
703
|
-
return []
|
704
|
-
return [m.xmpp_id for m in multi.legacy_ids_multi.xmpp_ids]
|
705
|
-
|
706
|
-
def set_xmpp_ids(
|
707
|
-
self, user_pk: int, legacy_msg_id: str, xmpp_ids: list[str], fail=False
|
708
|
-
) -> None:
|
709
|
-
with self.session() as session:
|
710
|
-
existing = session.execute(
|
711
|
-
select(LegacyIdsMulti)
|
712
|
-
.where(LegacyIdsMulti.user_account_id == user_pk)
|
713
|
-
.where(LegacyIdsMulti.legacy_id == legacy_msg_id)
|
714
|
-
).scalar()
|
715
|
-
if existing is not None:
|
716
|
-
if fail:
|
717
|
-
raise
|
718
|
-
log.debug("Resetting multi for %s", legacy_msg_id)
|
719
|
-
session.execute(
|
720
|
-
delete(LegacyIdsMulti)
|
721
|
-
.where(LegacyIdsMulti.user_account_id == user_pk)
|
722
|
-
.where(LegacyIdsMulti.legacy_id == legacy_msg_id)
|
723
|
-
)
|
724
|
-
for i in xmpp_ids:
|
725
|
-
session.execute(
|
726
|
-
delete(XmppIdsMulti)
|
727
|
-
.where(XmppIdsMulti.user_account_id == user_pk)
|
728
|
-
.where(XmppIdsMulti.xmpp_id == i)
|
729
|
-
)
|
730
|
-
session.commit()
|
731
|
-
self.set_xmpp_ids(user_pk, legacy_msg_id, xmpp_ids, True)
|
732
|
-
return
|
733
|
-
|
734
|
-
row = LegacyIdsMulti(
|
735
|
-
user_account_id=user_pk,
|
736
|
-
legacy_id=legacy_msg_id,
|
737
|
-
xmpp_ids=[
|
738
|
-
XmppIdsMulti(user_account_id=user_pk, xmpp_id=i)
|
739
|
-
for i in xmpp_ids
|
740
|
-
if i
|
741
|
-
],
|
742
|
-
)
|
743
|
-
session.add(row)
|
744
|
-
session.commit()
|
745
|
-
|
746
|
-
def get_legacy_id(self, user_pk: int, xmpp_id: str) -> Optional[str]:
|
747
|
-
with self.session() as session:
|
748
|
-
multi = session.execute(
|
749
|
-
select(XmppIdsMulti)
|
750
|
-
.where(XmppIdsMulti.xmpp_id == xmpp_id)
|
751
|
-
.where(XmppIdsMulti.user_account_id == user_pk)
|
752
|
-
).scalar()
|
753
|
-
if multi is None:
|
754
|
-
return None
|
755
|
-
if multi.legacy_ids_multi is None:
|
756
|
-
return None
|
757
|
-
return multi.legacy_ids_multi.legacy_id
|
758
|
-
|
759
|
-
|
760
|
-
class AttachmentStore(EngineMixin):
|
761
|
-
def get_url(self, legacy_file_id: str) -> Optional[str]:
|
762
|
-
with self.session() as session:
|
763
|
-
return session.execute(
|
764
|
-
select(Attachment.url).where(
|
765
|
-
Attachment.legacy_file_id == legacy_file_id
|
766
|
-
)
|
767
|
-
).scalar()
|
768
|
-
|
769
|
-
def set_url(self, user_pk: int, legacy_file_id: str, url: str) -> None:
|
770
|
-
with self.session() as session:
|
771
|
-
att = session.execute(
|
772
|
-
select(Attachment)
|
773
|
-
.where(Attachment.legacy_file_id == legacy_file_id)
|
774
|
-
.where(Attachment.user_account_id == user_pk)
|
775
|
-
).scalar()
|
776
|
-
if att is None:
|
777
|
-
att = Attachment(
|
778
|
-
legacy_file_id=legacy_file_id, url=url, user_account_id=user_pk
|
779
|
-
)
|
780
|
-
session.add(att)
|
781
|
-
else:
|
782
|
-
att.url = url
|
783
|
-
session.commit()
|
784
|
-
|
785
|
-
def get_sims(self, url: str) -> Optional[str]:
|
786
|
-
with self.session() as session:
|
787
|
-
return session.execute(
|
788
|
-
select(Attachment.sims).where(Attachment.url == url)
|
789
|
-
).scalar()
|
790
|
-
|
791
|
-
def set_sims(self, url: str, sims: str) -> None:
|
792
|
-
with self.session() as session:
|
793
|
-
session.execute(
|
794
|
-
update(Attachment).where(Attachment.url == url).values(sims=sims)
|
795
|
-
)
|
796
|
-
session.commit()
|
797
|
-
|
798
|
-
def get_sfs(self, url: str) -> Optional[str]:
|
799
|
-
with self.session() as session:
|
800
|
-
return session.execute(
|
801
|
-
select(Attachment.sfs).where(Attachment.url == url)
|
802
|
-
).scalar()
|
803
|
-
|
804
|
-
def set_sfs(self, url: str, sfs: str) -> None:
|
805
|
-
with self.session() as session:
|
806
|
-
session.execute(
|
807
|
-
update(Attachment).where(Attachment.url == url).values(sfs=sfs)
|
808
|
-
)
|
809
|
-
session.commit()
|
424
|
+
session: Session,
|
425
|
+
room_pk: int,
|
426
|
+
after_id: str,
|
427
|
+
source: ArchivedMessageSource = ArchivedMessageSource.LIVE,
|
428
|
+
) -> Optional[ArchivedMessage]:
|
429
|
+
after_timestamp = (
|
430
|
+
session.query(ArchivedMessage.timestamp)
|
431
|
+
.filter(ArchivedMessage.room_id == room_pk)
|
432
|
+
.filter(ArchivedMessage.legacy_id == after_id)
|
433
|
+
.scalar()
|
434
|
+
)
|
435
|
+
q = (
|
436
|
+
select(ArchivedMessage)
|
437
|
+
.where(ArchivedMessage.room_id == room_pk)
|
438
|
+
.where(ArchivedMessage.legacy_id.isnot(None))
|
439
|
+
.where(ArchivedMessage.source == source)
|
440
|
+
.where(ArchivedMessage.timestamp > after_timestamp)
|
441
|
+
)
|
442
|
+
return session.execute(q.order_by(ArchivedMessage.timestamp.asc())).scalar()
|
810
443
|
|
811
|
-
|
812
|
-
|
813
|
-
|
814
|
-
|
815
|
-
|
816
|
-
session.
|
444
|
+
@staticmethod
|
445
|
+
def get_by_legacy_id(
|
446
|
+
session: Session, room_pk: int, legacy_id: str
|
447
|
+
) -> Optional[ArchivedMessage]:
|
448
|
+
return (
|
449
|
+
session.query(ArchivedMessage)
|
450
|
+
.filter(ArchivedMessage.room_id == room_pk)
|
451
|
+
.filter(ArchivedMessage.legacy_id == legacy_id)
|
452
|
+
.first()
|
453
|
+
)
|
817
454
|
|
818
455
|
|
819
456
|
class RoomStore(UpdatedMixin):
|
820
457
|
model = Room
|
821
458
|
|
822
|
-
def __init__(self,
|
823
|
-
super().__init__(
|
824
|
-
|
825
|
-
|
826
|
-
|
827
|
-
|
828
|
-
|
829
|
-
|
830
|
-
participants_filled=False,
|
831
|
-
)
|
832
|
-
)
|
833
|
-
session.commit()
|
834
|
-
|
835
|
-
def set_avatar(
|
836
|
-
self, room_pk: int, avatar_pk: int | None, avatar_legacy_id: str | None
|
837
|
-
) -> None:
|
838
|
-
with self.session() as session:
|
839
|
-
session.execute(
|
840
|
-
update(Room)
|
841
|
-
.where(Room.id == room_pk)
|
842
|
-
.values(avatar_id=avatar_pk, avatar_legacy_id=avatar_legacy_id)
|
843
|
-
)
|
844
|
-
session.commit()
|
845
|
-
|
846
|
-
def get_avatar_legacy_id(self, room_pk: int) -> Optional[str]:
|
847
|
-
with self.session() as session:
|
848
|
-
room = session.execute(select(Room).where(Room.id == room_pk)).scalar()
|
849
|
-
if room is None or room.avatar is None:
|
850
|
-
return None
|
851
|
-
return room.avatar_legacy_id
|
852
|
-
|
853
|
-
def get_by_jid(self, user_pk: int, jid: JID) -> Optional[Room]:
|
854
|
-
if jid.resource:
|
855
|
-
raise TypeError
|
856
|
-
with self.session() as session:
|
857
|
-
return session.execute(
|
858
|
-
select(Room)
|
859
|
-
.where(Room.user_account_id == user_pk)
|
860
|
-
.where(Room.jid == jid)
|
861
|
-
).scalar()
|
862
|
-
|
863
|
-
def get_by_legacy_id(self, user_pk: int, legacy_id: str) -> Optional[Room]:
|
864
|
-
with self.session() as session:
|
865
|
-
return session.execute(
|
866
|
-
select(Room)
|
867
|
-
.where(Room.user_account_id == user_pk)
|
868
|
-
.where(Room.legacy_id == legacy_id)
|
869
|
-
).scalar()
|
870
|
-
|
871
|
-
def update_subject_setter(self, room_pk: int, subject_setter: str | None):
|
872
|
-
with self.session() as session:
|
873
|
-
session.execute(
|
874
|
-
update(Room)
|
875
|
-
.where(Room.id == room_pk)
|
876
|
-
.values(subject_setter=subject_setter)
|
877
|
-
)
|
878
|
-
session.commit()
|
879
|
-
|
880
|
-
def update(self, muc: "LegacyMUC") -> int:
|
881
|
-
with self.session() as session:
|
882
|
-
if muc.pk is None:
|
883
|
-
row = Room(
|
884
|
-
jid=muc.jid,
|
885
|
-
legacy_id=str(muc.legacy_id),
|
886
|
-
user_account_id=muc.user_pk,
|
887
|
-
)
|
888
|
-
else:
|
889
|
-
row = session.query(Room).filter(Room.id == muc.pk).one()
|
890
|
-
|
891
|
-
row.updated = True
|
892
|
-
row.extra_attributes = muc.serialize_extra_attributes()
|
893
|
-
row.name = muc.name
|
894
|
-
row.description = muc.description
|
895
|
-
row.user_resources = (
|
896
|
-
None
|
897
|
-
if not muc._user_resources
|
898
|
-
else json.dumps(list(muc._user_resources))
|
899
|
-
)
|
900
|
-
row.muc_type = muc.type
|
901
|
-
row.subject = muc.subject
|
902
|
-
row.subject_date = muc.subject_date
|
903
|
-
row.subject_setter = muc.subject_setter
|
904
|
-
row.participants_filled = muc._participants_filled
|
905
|
-
row.n_participants = muc._n_participants
|
906
|
-
row.user_nick = muc.user_nick
|
907
|
-
session.add(row)
|
908
|
-
session.commit()
|
909
|
-
return row.id
|
910
|
-
|
911
|
-
def update_subject_date(
|
912
|
-
self, room_pk: int, subject_date: Optional[datetime]
|
913
|
-
) -> None:
|
914
|
-
with self.session() as session:
|
915
|
-
session.execute(
|
916
|
-
update(Room).where(Room.id == room_pk).values(subject_date=subject_date)
|
917
|
-
)
|
918
|
-
session.commit()
|
919
|
-
|
920
|
-
def update_subject(self, room_pk: int, subject: Optional[str]) -> None:
|
921
|
-
with self.session() as session:
|
922
|
-
session.execute(
|
923
|
-
update(Room).where(Room.id == room_pk).values(subject=subject)
|
924
|
-
)
|
925
|
-
session.commit()
|
926
|
-
|
927
|
-
def update_description(self, room_pk: int, desc: Optional[str]) -> None:
|
928
|
-
with self.session() as session:
|
929
|
-
session.execute(
|
930
|
-
update(Room).where(Room.id == room_pk).values(description=desc)
|
931
|
-
)
|
932
|
-
session.commit()
|
933
|
-
|
934
|
-
def update_name(self, room_pk: int, name: Optional[str]) -> None:
|
935
|
-
with self.session() as session:
|
936
|
-
session.execute(update(Room).where(Room.id == room_pk).values(name=name))
|
937
|
-
session.commit()
|
938
|
-
|
939
|
-
def update_n_participants(self, room_pk: int, n: Optional[int]) -> None:
|
940
|
-
with self.session() as session:
|
941
|
-
session.execute(
|
942
|
-
update(Room).where(Room.id == room_pk).values(n_participants=n)
|
943
|
-
)
|
944
|
-
session.commit()
|
945
|
-
|
946
|
-
def update_user_nick(self, room_pk, nick: str) -> None:
|
947
|
-
with self.session() as session:
|
948
|
-
session.execute(
|
949
|
-
update(Room).where(Room.id == room_pk).values(user_nick=nick)
|
950
|
-
)
|
951
|
-
session.commit()
|
952
|
-
|
953
|
-
def delete(self, room_pk: int) -> None:
|
954
|
-
with self.session() as session:
|
955
|
-
session.execute(delete(Room).where(Room.id == room_pk))
|
956
|
-
session.execute(delete(Participant).where(Participant.room_id == room_pk))
|
957
|
-
session.commit()
|
958
|
-
|
959
|
-
def set_resource(self, room_pk: int, resources: set[str]) -> None:
|
960
|
-
with self.session() as session:
|
961
|
-
session.execute(
|
962
|
-
update(Room)
|
963
|
-
.where(Room.id == room_pk)
|
964
|
-
.values(
|
965
|
-
user_resources=(
|
966
|
-
None if not resources else json.dumps(list(resources))
|
967
|
-
)
|
968
|
-
)
|
969
|
-
)
|
970
|
-
session.commit()
|
971
|
-
|
972
|
-
def nickname_is_available(self, room_pk: int, nickname: str) -> bool:
|
973
|
-
with self.session() as session:
|
974
|
-
return (
|
975
|
-
session.execute(
|
976
|
-
select(Participant)
|
977
|
-
.where(Participant.room_id == room_pk)
|
978
|
-
.where(Participant.nickname == nickname)
|
979
|
-
).scalar()
|
980
|
-
is None
|
981
|
-
)
|
982
|
-
|
983
|
-
def set_participants_filled(self, room_pk: int, val=True) -> None:
|
984
|
-
with self.session() as session:
|
985
|
-
session.execute(
|
986
|
-
update(Room).where(Room.id == room_pk).values(participants_filled=val)
|
987
|
-
)
|
988
|
-
session.commit()
|
989
|
-
|
990
|
-
def set_history_filled(self, room_pk: int, val=True) -> None:
|
991
|
-
with self.session() as session:
|
992
|
-
session.execute(
|
993
|
-
update(Room).where(Room.id == room_pk).values(history_filled=True)
|
459
|
+
def __init__(self, session: Session) -> None:
|
460
|
+
super().__init__(session)
|
461
|
+
session.execute(
|
462
|
+
update(Room).values(
|
463
|
+
subject_setter=None,
|
464
|
+
user_resources=None,
|
465
|
+
history_filled=False,
|
466
|
+
participants_filled=False,
|
994
467
|
)
|
995
|
-
|
996
|
-
|
997
|
-
def get_all(self, user_pk: int) -> Iterator[Room]:
|
998
|
-
with self.session() as session:
|
999
|
-
yield from session.execute(
|
1000
|
-
select(Room).where(Room.user_account_id == user_pk)
|
1001
|
-
).scalars()
|
1002
|
-
|
1003
|
-
def get_all_jid_and_names(self, user_pk: int) -> Iterator[Room]:
|
1004
|
-
with self.session() as session:
|
1005
|
-
yield from session.scalars(
|
1006
|
-
select(Room)
|
1007
|
-
.filter(Room.user_account_id == user_pk)
|
1008
|
-
.options(load_only(Room.jid, Room.name))
|
1009
|
-
.order_by(Room.name)
|
1010
|
-
).all()
|
1011
|
-
|
1012
|
-
|
1013
|
-
class ParticipantStore(EngineMixin):
|
1014
|
-
def __init__(self, *a, **kw):
|
1015
|
-
super().__init__(*a, **kw)
|
1016
|
-
with self.session() as session:
|
1017
|
-
session.execute(delete(participant_hats))
|
1018
|
-
session.execute(delete(Hat))
|
1019
|
-
session.execute(delete(Participant))
|
1020
|
-
session.commit()
|
1021
|
-
|
1022
|
-
def add(self, room_pk: int, nickname: str) -> int:
|
1023
|
-
with self.session() as session:
|
1024
|
-
existing = session.execute(
|
1025
|
-
select(Participant.id)
|
1026
|
-
.where(Participant.room_id == room_pk)
|
1027
|
-
.where(Participant.nickname == nickname)
|
1028
|
-
).scalar()
|
1029
|
-
if existing is not None:
|
1030
|
-
return existing
|
1031
|
-
participant = Participant(room_id=room_pk, nickname=nickname)
|
1032
|
-
session.add(participant)
|
1033
|
-
session.commit()
|
1034
|
-
return participant.id
|
1035
|
-
|
1036
|
-
def get_by_nickname(self, room_pk: int, nickname: str) -> Optional[Participant]:
|
1037
|
-
with self.session() as session:
|
1038
|
-
return session.execute(
|
1039
|
-
select(Participant)
|
1040
|
-
.where(Participant.room_id == room_pk)
|
1041
|
-
.where(Participant.nickname == nickname)
|
1042
|
-
).scalar()
|
1043
|
-
|
1044
|
-
def get_by_resource(self, room_pk: int, resource: str) -> Optional[Participant]:
|
1045
|
-
with self.session() as session:
|
1046
|
-
return session.execute(
|
1047
|
-
select(Participant)
|
1048
|
-
.where(Participant.room_id == room_pk)
|
1049
|
-
.where(Participant.resource == resource)
|
1050
|
-
).scalar()
|
1051
|
-
|
1052
|
-
def get_by_contact(self, room_pk: int, contact_pk: int) -> Optional[Participant]:
|
1053
|
-
with self.session() as session:
|
1054
|
-
return session.execute(
|
1055
|
-
select(Participant)
|
1056
|
-
.where(Participant.room_id == room_pk)
|
1057
|
-
.where(Participant.contact_id == contact_pk)
|
1058
|
-
).scalar()
|
1059
|
-
|
1060
|
-
def get_all(self, room_pk: int, user_included=True) -> Iterator[Participant]:
|
1061
|
-
with self.session() as session:
|
1062
|
-
q = select(Participant).where(Participant.room_id == room_pk)
|
1063
|
-
if not user_included:
|
1064
|
-
q = q.where(~Participant.is_user)
|
1065
|
-
yield from session.execute(q).scalars()
|
1066
|
-
|
1067
|
-
def get_for_contact(self, contact_pk: int) -> Iterator[Participant]:
|
1068
|
-
with self.session() as session:
|
1069
|
-
yield from session.execute(
|
1070
|
-
select(Participant).where(Participant.contact_id == contact_pk)
|
1071
|
-
).scalars()
|
1072
|
-
|
1073
|
-
def update(self, participant: "LegacyParticipant") -> None:
|
1074
|
-
with self.session() as session:
|
1075
|
-
session.execute(
|
1076
|
-
update(Participant)
|
1077
|
-
.where(Participant.id == participant.pk)
|
1078
|
-
.values(
|
1079
|
-
nickname=participant.nickname,
|
1080
|
-
resource=participant.jid.resource,
|
1081
|
-
nickname_no_illegal=participant._nickname_no_illegal,
|
1082
|
-
affiliation=participant.affiliation,
|
1083
|
-
role=participant.role,
|
1084
|
-
presence_sent=participant._presence_sent, # type:ignore
|
1085
|
-
# hats=[self.add_hat(h.uri, h.title) for h in participant._hats],
|
1086
|
-
is_user=participant.is_user,
|
1087
|
-
contact_id=(
|
1088
|
-
None
|
1089
|
-
if participant.contact is None
|
1090
|
-
else participant.contact.contact_pk
|
1091
|
-
),
|
1092
|
-
)
|
1093
|
-
)
|
1094
|
-
session.commit()
|
1095
|
-
|
1096
|
-
def add_hat(self, uri: str, title: str) -> Hat:
|
1097
|
-
with self.session() as session:
|
1098
|
-
existing = session.execute(
|
1099
|
-
select(Hat).where(Hat.uri == uri).where(Hat.title == title)
|
1100
|
-
).scalar()
|
1101
|
-
if existing is not None:
|
1102
|
-
return existing
|
1103
|
-
hat = Hat(uri=uri, title=title)
|
1104
|
-
session.add(hat)
|
1105
|
-
session.commit()
|
1106
|
-
return hat
|
468
|
+
)
|
1107
469
|
|
1108
|
-
|
1109
|
-
|
1110
|
-
|
1111
|
-
update(Participant)
|
1112
|
-
.where(Participant.id == participant_pk)
|
1113
|
-
.values(presence_sent=True)
|
1114
|
-
)
|
1115
|
-
session.commit()
|
470
|
+
@staticmethod
|
471
|
+
def get_all(session: Session, user_pk: int) -> Iterator[Room]:
|
472
|
+
yield from session.scalars(select(Room).where(Room.user_account_id == user_pk))
|
1116
473
|
|
1117
|
-
def set_affiliation(self, participant_pk: int, affiliation: MucAffiliation) -> None:
|
1118
|
-
with self.session() as session:
|
1119
|
-
session.execute(
|
1120
|
-
update(Participant)
|
1121
|
-
.where(Participant.id == participant_pk)
|
1122
|
-
.values(affiliation=affiliation)
|
1123
|
-
)
|
1124
|
-
session.commit()
|
1125
474
|
|
1126
|
-
|
1127
|
-
|
1128
|
-
|
1129
|
-
update(Participant)
|
1130
|
-
.where(Participant.id == participant_pk)
|
1131
|
-
.values(role=role)
|
1132
|
-
)
|
1133
|
-
session.commit()
|
475
|
+
class ParticipantStore:
|
476
|
+
def __init__(self, session: Session) -> None:
|
477
|
+
session.execute(delete(Participant))
|
1134
478
|
|
1135
|
-
|
1136
|
-
|
1137
|
-
|
1138
|
-
|
1139
|
-
|
1140
|
-
|
1141
|
-
|
1142
|
-
|
1143
|
-
for h in hats:
|
1144
|
-
hat = self.add_hat(*h)
|
1145
|
-
if hat in part.hats:
|
1146
|
-
continue
|
1147
|
-
part.hats.append(hat)
|
1148
|
-
session.commit()
|
1149
|
-
|
1150
|
-
def delete(self, participant_pk: int) -> None:
|
1151
|
-
with self.session() as session:
|
1152
|
-
session.execute(delete(Participant).where(Participant.id == participant_pk))
|
1153
|
-
|
1154
|
-
def get_count(self, room_pk: int) -> int:
|
1155
|
-
with self.session() as session:
|
1156
|
-
return session.query(
|
1157
|
-
count(Participant.id).filter(Participant.room_id == room_pk)
|
1158
|
-
).scalar()
|
479
|
+
@staticmethod
|
480
|
+
def get_all(
|
481
|
+
session: Session, room_pk: int, user_included: bool = True
|
482
|
+
) -> Iterator[Participant]:
|
483
|
+
query = select(Participant).where(Participant.room_id == room_pk)
|
484
|
+
if not user_included:
|
485
|
+
query = query.where(~Participant.is_user)
|
486
|
+
yield from session.scalars(query).unique()
|
1159
487
|
|
1160
488
|
|
1161
|
-
class BobStore
|
489
|
+
class BobStore:
|
1162
490
|
_ATTR_MAP = {
|
1163
491
|
"sha-1": "sha_1",
|
1164
492
|
"sha1": "sha_1",
|
@@ -1174,8 +502,7 @@ class BobStore(EngineMixin):
|
|
1174
502
|
"sha_512": hashlib.sha512,
|
1175
503
|
}
|
1176
504
|
|
1177
|
-
def __init__(self
|
1178
|
-
super().__init__(*a, **k)
|
505
|
+
def __init__(self) -> None:
|
1179
506
|
self.root_dir = config.HOME_DIR / "slidge_stickers"
|
1180
507
|
self.root_dir.mkdir(exist_ok=True)
|
1181
508
|
|
@@ -1187,20 +514,19 @@ class BobStore(EngineMixin):
|
|
1187
514
|
alg_name, digest = self.__split_cid(cid)
|
1188
515
|
attr = self._ATTR_MAP.get(alg_name)
|
1189
516
|
if attr is None:
|
1190
|
-
log.warning("Unknown hash
|
517
|
+
log.warning("Unknown hash algorithm: %s", alg_name)
|
1191
518
|
return None
|
1192
519
|
return getattr(Bob, attr) == digest
|
1193
520
|
|
1194
|
-
def get(self, cid: str) -> Bob | None:
|
1195
|
-
|
1196
|
-
|
1197
|
-
|
1198
|
-
|
1199
|
-
|
1200
|
-
return None
|
521
|
+
def get(self, session: Session, cid: str) -> Bob | None:
|
522
|
+
try:
|
523
|
+
return session.query(Bob).filter(self.__get_condition(cid)).scalar()
|
524
|
+
except ValueError:
|
525
|
+
log.warning("Cannot get Bob with CID: %s", cid)
|
526
|
+
return None
|
1201
527
|
|
1202
|
-
def get_sticker(self, cid: str) -> Sticker | None:
|
1203
|
-
bob = self.get(cid)
|
528
|
+
def get_sticker(self, session: Session, cid: str) -> Sticker | None:
|
529
|
+
bob = self.get(session, cid)
|
1204
530
|
if bob is None:
|
1205
531
|
return None
|
1206
532
|
return Sticker(
|
@@ -1209,8 +535,10 @@ class BobStore(EngineMixin):
|
|
1209
535
|
{h: getattr(bob, h) for h in self._ALG_MAP},
|
1210
536
|
)
|
1211
537
|
|
1212
|
-
def get_bob(
|
1213
|
-
|
538
|
+
def get_bob(
|
539
|
+
self, session: Session, _jid, _node, _ifrom, cid: str
|
540
|
+
) -> BitsOfBinary | None:
|
541
|
+
stored = self.get(session, cid)
|
1214
542
|
if stored is None:
|
1215
543
|
return None
|
1216
544
|
bob = BitsOfBinary()
|
@@ -1220,53 +548,45 @@ class BobStore(EngineMixin):
|
|
1220
548
|
bob["cid"] = cid
|
1221
549
|
return bob
|
1222
550
|
|
1223
|
-
def del_bob(self, _jid, _node, _ifrom, cid: str) -> None:
|
1224
|
-
|
1225
|
-
|
1226
|
-
|
1227
|
-
|
1228
|
-
|
1229
|
-
|
1230
|
-
|
1231
|
-
|
1232
|
-
|
1233
|
-
|
1234
|
-
|
1235
|
-
|
1236
|
-
|
1237
|
-
(self.root_dir / file_name).unlink()
|
1238
|
-
orm.commit()
|
1239
|
-
|
1240
|
-
def set_bob(self, _jid, _node, _ifrom, bob: BitsOfBinary) -> None:
|
551
|
+
def del_bob(self, session: Session, _jid, _node, _ifrom, cid: str) -> None:
|
552
|
+
try:
|
553
|
+
file_name = session.scalar(
|
554
|
+
delete(Bob).where(self.__get_condition(cid)).returning(Bob.file_name)
|
555
|
+
)
|
556
|
+
except ValueError:
|
557
|
+
log.warning("Cannot delete Bob with CID: %s", cid)
|
558
|
+
return None
|
559
|
+
if file_name is None:
|
560
|
+
log.warning("No BoB with CID: %s", cid)
|
561
|
+
return None
|
562
|
+
(self.root_dir / file_name).unlink()
|
563
|
+
|
564
|
+
def set_bob(self, session: Session, _jid, _node, _ifrom, bob: BitsOfBinary) -> None:
|
1241
565
|
cid = bob["cid"]
|
1242
566
|
try:
|
1243
567
|
alg_name, digest = self.__split_cid(cid)
|
1244
568
|
except ValueError:
|
1245
|
-
log.warning("
|
569
|
+
log.warning("Invalid CID provided: %s", cid)
|
1246
570
|
return
|
1247
571
|
attr = self._ATTR_MAP.get(alg_name)
|
1248
572
|
if attr is None:
|
1249
|
-
log.warning("Cannot set
|
1250
|
-
return
|
1251
|
-
|
1252
|
-
|
1253
|
-
|
1254
|
-
|
1255
|
-
|
1256
|
-
|
1257
|
-
|
1258
|
-
|
1259
|
-
|
1260
|
-
|
1261
|
-
|
1262
|
-
|
1263
|
-
|
1264
|
-
|
1265
|
-
|
1266
|
-
row = Bob(file_name=path.name, content_type=bob["type"] or None, **hashes)
|
1267
|
-
orm.add(row)
|
1268
|
-
orm.commit()
|
573
|
+
log.warning("Cannot set Bob: Unknown algorithm type: %s", alg_name)
|
574
|
+
return
|
575
|
+
existing = self.get(session, bob["cid"])
|
576
|
+
if existing:
|
577
|
+
log.debug("Bob already exists")
|
578
|
+
return
|
579
|
+
bytes_ = bob["data"]
|
580
|
+
path = self.root_dir / uuid.uuid4().hex
|
581
|
+
if bob["type"]:
|
582
|
+
path = path.with_suffix(guess_extension(bob["type"]) or "")
|
583
|
+
path.write_bytes(bytes_)
|
584
|
+
hashes = {k: v(bytes_).hexdigest() for k, v in self._ALG_MAP.items()}
|
585
|
+
if hashes[attr] != digest:
|
586
|
+
path.unlink(missing_ok=True)
|
587
|
+
raise ValueError("Provided CID does not match calculated hash")
|
588
|
+
row = Bob(file_name=path.name, content_type=bob["type"] or None, **hashes)
|
589
|
+
session.add(row)
|
1269
590
|
|
1270
591
|
|
1271
592
|
log = logging.getLogger(__name__)
|
1272
|
-
_session: Optional[Session] = None
|