slidge 0.2.12__py3-none-any.whl → 0.3.0a0__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 +119 -209
- slidge/contact/roster.py +106 -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 +117 -92
- 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 +21 -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 +168 -84
- slidge/core/mixins/__init__.py +1 -11
- slidge/core/mixins/attachment.py +163 -148
- slidge/core/mixins/avatar.py +100 -177
- slidge/core/mixins/db.py +50 -2
- slidge/core/mixins/message.py +19 -17
- slidge/core/mixins/message_maker.py +29 -15
- slidge/core/mixins/message_text.py +38 -30
- slidge/core/mixins/presence.py +91 -35
- slidge/core/pubsub.py +42 -47
- slidge/core/session.py +88 -57
- slidge/db/alembic/versions/0337c90c0b96_unify_legacy_xmpp_id_mappings.py +183 -0
- slidge/db/alembic/versions/4dbd23a3f868_new_avatar_store.py +56 -0
- slidge/db/alembic/versions/54ce3cde350c_use_hash_for_avatar_filenames.py +50 -0
- slidge/db/alembic/versions/58b98dacf819_refactor.py +118 -0
- slidge/db/alembic/versions/75a62b74b239_ditch_hats_table.py +74 -0
- slidge/db/avatar.py +150 -119
- slidge/db/meta.py +33 -22
- slidge/db/models.py +68 -117
- slidge/db/store.py +412 -1094
- slidge/group/archive.py +61 -54
- slidge/group/bookmarks.py +74 -55
- slidge/group/participant.py +135 -142
- slidge/group/room.py +315 -312
- slidge/main.py +28 -18
- slidge/migration.py +2 -12
- 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 +1 -1
- slidge/util/archive_msg.py +11 -5
- slidge/util/conf.py +23 -20
- 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 +22 -18
- slidge/util/util.py +19 -22
- {slidge-0.2.12.dist-info → slidge-0.3.0a0.dist-info}/METADATA +1 -1
- slidge-0.3.0a0.dist-info/RECORD +117 -0
- {slidge-0.2.12.dist-info → slidge-0.3.0a0.dist-info}/WHEEL +1 -1
- slidge-0.2.12.dist-info/RECORD +0 -112
- {slidge-0.2.12.dist-info → slidge-0.3.0a0.dist-info}/entry_points.txt +0 -0
- {slidge-0.2.12.dist-info → slidge-0.3.0a0.dist-info}/licenses/LICENSE +0 -0
- {slidge-0.2.12.dist-info → slidge-0.3.0a0.dist-info}/top_level.txt +0 -0
slidge/group/archive.py
CHANGED
@@ -6,7 +6,7 @@ from typing import TYPE_CHECKING, Collection, Optional
|
|
6
6
|
|
7
7
|
from slixmpp import Iq, Message
|
8
8
|
|
9
|
-
from ..db.models import ArchivedMessage, ArchivedMessageSource
|
9
|
+
from ..db.models import ArchivedMessage, ArchivedMessageSource, Room
|
10
10
|
from ..db.store import MAMStore
|
11
11
|
from ..util.archive_msg import HistoryMessage
|
12
12
|
from ..util.types import HoleBound
|
@@ -16,17 +16,17 @@ if TYPE_CHECKING:
|
|
16
16
|
|
17
17
|
|
18
18
|
class MessageArchive:
|
19
|
-
def __init__(self,
|
20
|
-
self.
|
19
|
+
def __init__(self, room: Room, store: MAMStore) -> None:
|
20
|
+
self.room = room
|
21
21
|
self.__store = store
|
22
22
|
|
23
23
|
def add(
|
24
24
|
self,
|
25
25
|
msg: Message,
|
26
26
|
participant: Optional["LegacyParticipant"] = None,
|
27
|
-
archive_only=False,
|
27
|
+
archive_only: bool = False,
|
28
28
|
legacy_msg_id=None,
|
29
|
-
):
|
29
|
+
) -> None:
|
30
30
|
"""
|
31
31
|
Add a message to the archive if it is deemed archivable
|
32
32
|
|
@@ -39,8 +39,8 @@ class MessageArchive:
|
|
39
39
|
return
|
40
40
|
new_msg = copy(msg)
|
41
41
|
if participant and not participant.muc.is_anonymous:
|
42
|
-
new_msg["muc"]["role"] = participant.role
|
43
|
-
new_msg["muc"]["affiliation"] = participant.affiliation
|
42
|
+
new_msg["muc"]["role"] = participant.role or "participant"
|
43
|
+
new_msg["muc"]["affiliation"] = participant.affiliation or "member"
|
44
44
|
if participant.contact:
|
45
45
|
new_msg["muc"]["jid"] = participant.contact.jid.bare
|
46
46
|
elif participant.is_user:
|
@@ -53,12 +53,15 @@ class MessageArchive:
|
|
53
53
|
f"{uuid.uuid4()}@{participant.xmpp.boundjid.bare}"
|
54
54
|
)
|
55
55
|
|
56
|
-
self.__store.
|
57
|
-
self.
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
56
|
+
with self.__store.session() as orm:
|
57
|
+
self.__store.add_message(
|
58
|
+
orm,
|
59
|
+
self.room.id,
|
60
|
+
HistoryMessage(new_msg),
|
61
|
+
archive_only,
|
62
|
+
None if legacy_msg_id is None else str(legacy_msg_id),
|
63
|
+
)
|
64
|
+
orm.commit()
|
62
65
|
|
63
66
|
def __iter__(self):
|
64
67
|
return iter(self.get_all())
|
@@ -71,31 +74,32 @@ class MessageArchive:
|
|
71
74
|
)
|
72
75
|
|
73
76
|
def get_hole_bounds(self) -> tuple[HoleBound | None, HoleBound | None]:
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
self.
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
77
|
+
with self.__store.session() as orm:
|
78
|
+
most_recent = self.__store.get_most_recent_with_legacy_id(orm, self.room.id)
|
79
|
+
if most_recent is None:
|
80
|
+
return None, None
|
81
|
+
if most_recent.source == ArchivedMessageSource.BACKFILL:
|
82
|
+
# most recent = only backfill, fetch everything since last backfill
|
83
|
+
return self.__to_bound(most_recent), None
|
84
|
+
|
85
|
+
most_recent_back_filled = self.__store.get_most_recent_with_legacy_id(
|
86
|
+
orm, self.room.id, ArchivedMessageSource.BACKFILL
|
87
|
+
)
|
88
|
+
if most_recent_back_filled is None:
|
89
|
+
# group was never back-filled, fetch everything before first live
|
90
|
+
least_recent_live = self.__store.get_first(orm, self.room.id, True)
|
91
|
+
assert least_recent_live is not None
|
92
|
+
return None, self.__to_bound(least_recent_live)
|
93
|
+
|
94
|
+
assert most_recent_back_filled.legacy_id is not None
|
95
|
+
least_recent_live = self.__store.get_least_recent_with_legacy_id_after(
|
96
|
+
orm, self.room.id, most_recent_back_filled.legacy_id
|
97
|
+
)
|
87
98
|
assert least_recent_live is not None
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
self.room_pk, most_recent_back_filled.legacy_id
|
93
|
-
)
|
94
|
-
assert least_recent_live is not None
|
95
|
-
# this is a hole caused by slidge downtime
|
96
|
-
return self.__to_bound(most_recent_back_filled), self.__to_bound(
|
97
|
-
least_recent_live
|
98
|
-
)
|
99
|
+
# this is a hole caused by slidge downtime
|
100
|
+
return self.__to_bound(most_recent_back_filled), self.__to_bound(
|
101
|
+
least_recent_live
|
102
|
+
)
|
99
103
|
|
100
104
|
def get_all(
|
101
105
|
self,
|
@@ -106,22 +110,24 @@ class MessageArchive:
|
|
106
110
|
ids: Collection[str] = (),
|
107
111
|
last_page_n: Optional[int] = None,
|
108
112
|
sender: Optional[str] = None,
|
109
|
-
flip=False,
|
113
|
+
flip: bool = False,
|
110
114
|
):
|
111
|
-
|
112
|
-
self.
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
115
|
+
with self.__store.session() as orm:
|
116
|
+
for msg in self.__store.get_messages(
|
117
|
+
orm,
|
118
|
+
self.room.id,
|
119
|
+
before_id=before_id,
|
120
|
+
after_id=after_id,
|
121
|
+
ids=ids,
|
122
|
+
last_page_n=last_page_n,
|
123
|
+
sender=sender,
|
124
|
+
start_date=start_date,
|
125
|
+
end_date=end_date,
|
126
|
+
flip=flip,
|
127
|
+
):
|
128
|
+
yield msg
|
129
|
+
|
130
|
+
async def send_metadata(self, iq: Iq) -> None:
|
125
131
|
"""
|
126
132
|
Send archive extent, as per the spec
|
127
133
|
|
@@ -129,7 +135,8 @@ class MessageArchive:
|
|
129
135
|
:return:
|
130
136
|
"""
|
131
137
|
reply = iq.reply()
|
132
|
-
|
138
|
+
with self.__store.session() as orm:
|
139
|
+
messages = self.__store.get_first_and_last(orm, self.room.id)
|
133
140
|
if messages:
|
134
141
|
for x, m in [("start", messages[0]), ("end", messages[-1])]:
|
135
142
|
reply["mam_metadata"][x]["id"] = m.id
|
@@ -141,7 +148,7 @@ class MessageArchive:
|
|
141
148
|
reply.send()
|
142
149
|
|
143
150
|
|
144
|
-
def archivable(msg: Message):
|
151
|
+
def archivable(msg: Message) -> bool:
|
145
152
|
"""
|
146
153
|
Determine if a message stanza is worth archiving, ie, convey meaningful
|
147
154
|
info
|
slidge/group/bookmarks.py
CHANGED
@@ -5,12 +5,11 @@ from typing import TYPE_CHECKING, Generic, Iterator, Optional, Type
|
|
5
5
|
from slixmpp import JID
|
6
6
|
from slixmpp.exceptions import XMPPError
|
7
7
|
|
8
|
-
from ..core.mixins.lock import NamedLockMixin
|
9
8
|
from ..db.models import Room
|
10
9
|
from ..util import SubclassableOnce
|
11
10
|
from ..util.jid_escaping import ESCAPE_TABLE, unescape_node
|
11
|
+
from ..util.lock import NamedLockMixin
|
12
12
|
from ..util.types import LegacyGroupIdType, LegacyMUCType
|
13
|
-
from .archive import MessageArchive
|
14
13
|
from .room import LegacyMUC
|
15
14
|
|
16
15
|
if TYPE_CHECKING:
|
@@ -26,13 +25,12 @@ class LegacyBookmarks(
|
|
26
25
|
This is instantiated once per :class:`~slidge.BaseSession`
|
27
26
|
"""
|
28
27
|
|
29
|
-
|
28
|
+
_muc_cls: Type[LegacyMUCType]
|
29
|
+
|
30
|
+
def __init__(self, session: "BaseSession") -> None:
|
30
31
|
self.session = session
|
31
32
|
self.xmpp = session.xmpp
|
32
33
|
self.user_jid = session.user_jid
|
33
|
-
self.__store = self.xmpp.store.rooms
|
34
|
-
|
35
|
-
self._muc_class: Type[LegacyMUCType] = LegacyMUC.get_self_or_unique_subclass()
|
36
34
|
|
37
35
|
self._user_nick: str = self.session.user_jid.node
|
38
36
|
|
@@ -47,23 +45,28 @@ class LegacyBookmarks(
|
|
47
45
|
return self._user_nick
|
48
46
|
|
49
47
|
@user_nick.setter
|
50
|
-
def user_nick(self, nick: str):
|
48
|
+
def user_nick(self, nick: str) -> None:
|
51
49
|
self._user_nick = nick
|
52
50
|
|
53
|
-
def
|
54
|
-
|
55
|
-
yield self._muc_class.from_store(self.session, stored)
|
51
|
+
def from_store(self, stored: Room) -> LegacyMUCType:
|
52
|
+
return self._muc_cls(self.session, stored)
|
56
53
|
|
57
|
-
def
|
54
|
+
def __iter__(self) -> Iterator[LegacyMUC]:
|
55
|
+
with self.xmpp.store.session() as orm:
|
56
|
+
for stored in orm.query(Room).filter_by(user=self.session.user).all():
|
57
|
+
if stored.updated:
|
58
|
+
yield self.from_store(stored)
|
59
|
+
|
60
|
+
def __repr__(self) -> str:
|
58
61
|
return f"<Bookmarks of {self.user_jid}>"
|
59
62
|
|
60
|
-
async def legacy_id_to_jid_local_part(self, legacy_id: LegacyGroupIdType):
|
63
|
+
async def legacy_id_to_jid_local_part(self, legacy_id: LegacyGroupIdType) -> str:
|
61
64
|
return await self.legacy_id_to_jid_username(legacy_id)
|
62
65
|
|
63
|
-
async def jid_local_part_to_legacy_id(self, local_part: str):
|
66
|
+
async def jid_local_part_to_legacy_id(self, local_part: str) -> LegacyGroupIdType:
|
64
67
|
return await self.jid_username_to_legacy_id(local_part)
|
65
68
|
|
66
|
-
async def legacy_id_to_jid_username(self, legacy_id: LegacyGroupIdType):
|
69
|
+
async def legacy_id_to_jid_username(self, legacy_id: LegacyGroupIdType) -> str:
|
67
70
|
"""
|
68
71
|
The default implementation calls ``str()`` on the legacy_id and
|
69
72
|
escape characters according to :xep:`0106`.
|
@@ -88,21 +91,32 @@ class LegacyBookmarks(
|
|
88
91
|
if jid.resource:
|
89
92
|
jid = JID(jid.bare)
|
90
93
|
async with self.lock(("bare", jid.bare)):
|
91
|
-
|
92
|
-
legacy_id = await self.jid_local_part_to_legacy_id(jid.user)
|
94
|
+
legacy_id = await self.jid_local_part_to_legacy_id(jid.node)
|
93
95
|
if self.get_lock(("legacy_id", legacy_id)):
|
94
|
-
self.log.debug("
|
96
|
+
self.session.log.debug("Already updating %s via by_legacy_id()", jid)
|
95
97
|
return await self.by_legacy_id(legacy_id)
|
96
98
|
|
97
|
-
with self.
|
98
|
-
stored =
|
99
|
-
|
99
|
+
with self.session.xmpp.store.session() as orm:
|
100
|
+
stored = (
|
101
|
+
orm.query(Room)
|
102
|
+
.filter_by(user_account_id=self.session.user_pk, jid=jid)
|
103
|
+
.one_or_none()
|
104
|
+
)
|
105
|
+
if stored is None:
|
106
|
+
stored = Room(
|
107
|
+
user_account_id=self.session.user_pk,
|
108
|
+
jid=jid,
|
109
|
+
legacy_id=legacy_id,
|
110
|
+
)
|
111
|
+
return await self.__update_if_needed(stored)
|
100
112
|
|
101
113
|
def by_jid_only_if_exists(self, jid: JID) -> Optional[LegacyMUCType]:
|
102
|
-
with self.
|
103
|
-
stored =
|
114
|
+
with self.xmpp.store.session() as orm:
|
115
|
+
stored = (
|
116
|
+
orm.query(Room).filter_by(user=self.session.user, jid=jid).one_or_none()
|
117
|
+
)
|
104
118
|
if stored is not None and stored.updated:
|
105
|
-
return self.
|
119
|
+
return self.from_store(stored)
|
106
120
|
return None
|
107
121
|
|
108
122
|
async def by_legacy_id(self, legacy_id: LegacyGroupIdType) -> LegacyMUCType:
|
@@ -110,37 +124,41 @@ class LegacyBookmarks(
|
|
110
124
|
local = await self.legacy_id_to_jid_local_part(legacy_id)
|
111
125
|
jid = JID(f"{local}@{self.xmpp.boundjid}")
|
112
126
|
if self.get_lock(("bare", jid.bare)):
|
113
|
-
self.log.debug("
|
127
|
+
self.session.log.debug("Already updating %s via by_jid()", jid)
|
114
128
|
return await self.by_jid(jid)
|
115
129
|
|
116
|
-
with self.
|
117
|
-
stored =
|
118
|
-
|
130
|
+
with self.xmpp.store.session() as orm:
|
131
|
+
stored = (
|
132
|
+
orm.query(Room)
|
133
|
+
.filter_by(
|
134
|
+
user_account_id=self.session.user_pk,
|
135
|
+
legacy_id=str(legacy_id),
|
136
|
+
)
|
137
|
+
.one_or_none()
|
119
138
|
)
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
muc.archive = MessageArchive(muc.pk, self.xmpp.store.mam)
|
139
|
+
if stored is None:
|
140
|
+
stored = Room(
|
141
|
+
user_account_id=self.session.user_pk,
|
142
|
+
jid=jid,
|
143
|
+
legacy_id=legacy_id,
|
144
|
+
)
|
145
|
+
return await self.__update_if_needed(stored)
|
146
|
+
|
147
|
+
async def __update_if_needed(self, stored: Room) -> LegacyMUCType:
|
148
|
+
muc = self.from_store(stored)
|
149
|
+
if muc.stored.updated:
|
150
|
+
return muc
|
151
|
+
|
152
|
+
with muc.updating_info():
|
153
|
+
try:
|
154
|
+
await muc.update_info()
|
155
|
+
except NotImplementedError:
|
156
|
+
pass
|
157
|
+
except XMPPError:
|
158
|
+
raise
|
159
|
+
except Exception as e:
|
160
|
+
raise XMPPError("internal-server-error", str(e))
|
161
|
+
|
144
162
|
return muc
|
145
163
|
|
146
164
|
@abc.abstractmethod
|
@@ -164,8 +182,8 @@ class LegacyBookmarks(
|
|
164
182
|
async def remove(
|
165
183
|
self,
|
166
184
|
muc: LegacyMUC,
|
167
|
-
reason="You left this group from the official client.",
|
168
|
-
kick=True,
|
185
|
+
reason: str = "You left this group from the official client.",
|
186
|
+
kick: bool = True,
|
169
187
|
) -> None:
|
170
188
|
"""
|
171
189
|
Delete everything about a specific group.
|
@@ -179,8 +197,9 @@ class LegacyBookmarks(
|
|
179
197
|
to False in case you do this somewhere else in your code, eg, on
|
180
198
|
receiving the confirmation that the group was deleted.
|
181
199
|
"""
|
182
|
-
assert muc.pk is not None
|
183
200
|
if kick:
|
184
201
|
user_participant = await muc.get_user_participant()
|
185
202
|
user_participant.kick(reason)
|
186
|
-
self.
|
203
|
+
with self.xmpp.store.session() as orm:
|
204
|
+
orm.delete(muc.stored)
|
205
|
+
orm.commit()
|