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
@@ -23,9 +23,13 @@ class TextMessageMixin(MessageMaker):
|
|
23
23
|
|
24
24
|
def _replace_id(self, legacy_msg_id: LegacyMessageType):
|
25
25
|
if self.mtype == "groupchat":
|
26
|
-
|
27
|
-
self.
|
28
|
-
|
26
|
+
with self.xmpp.store.session() as orm:
|
27
|
+
ids = self.xmpp.store.id_map.get_xmpp(
|
28
|
+
orm, self._recipient_pk(), str(legacy_msg_id), True
|
29
|
+
)
|
30
|
+
if ids:
|
31
|
+
return ids[0]
|
32
|
+
return self.session.legacy_to_xmpp_msg_id(legacy_msg_id)
|
29
33
|
else:
|
30
34
|
return self._legacy_to_xmpp(legacy_msg_id)
|
31
35
|
|
@@ -38,9 +42,9 @@ class TextMessageMixin(MessageMaker):
|
|
38
42
|
reply_to: Optional[MessageReference] = None,
|
39
43
|
thread: Optional[LegacyThreadType] = None,
|
40
44
|
hints: Optional[Iterable[ProcessingHint]] = None,
|
41
|
-
carbon=False,
|
42
|
-
archive_only=False,
|
43
|
-
correction=False,
|
45
|
+
carbon: bool = False,
|
46
|
+
archive_only: bool = False,
|
47
|
+
correction: bool = False,
|
44
48
|
correction_event_id: Optional[LegacyMessageType] = None,
|
45
49
|
link_previews: Optional[list[LinkPreview]] = None,
|
46
50
|
**send_kwargs,
|
@@ -69,24 +73,28 @@ class TextMessageMixin(MessageMaker):
|
|
69
73
|
but store it in the archive. Meant to be used during ``MUC.backfill()``
|
70
74
|
"""
|
71
75
|
if carbon and not hasattr(self, "muc"):
|
72
|
-
|
73
|
-
self.
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
76
|
+
with self.xmpp.store.session() as orm:
|
77
|
+
if not correction and self.xmpp.store.id_map.was_sent_by_user(
|
78
|
+
orm, self._recipient_pk(), str(legacy_msg_id), self.is_group
|
79
|
+
):
|
80
|
+
log.warning(
|
81
|
+
"Carbon message for a message an XMPP has sent? This is a bug! %s",
|
82
|
+
legacy_msg_id,
|
83
|
+
)
|
84
|
+
return
|
85
|
+
if hasattr(self, "muc") and not self.is_user: # type:ignore
|
86
|
+
log.warning(
|
87
|
+
"send_text() called with carbon=True on a participant who is not the user",
|
88
|
+
legacy_msg_id,
|
89
|
+
)
|
90
|
+
self.xmpp.store.id_map.set_msg(
|
91
|
+
orm,
|
92
|
+
self._recipient_pk(),
|
93
|
+
str(legacy_msg_id),
|
94
|
+
[self.session.legacy_to_xmpp_msg_id(legacy_msg_id)],
|
95
|
+
self.is_group,
|
84
96
|
)
|
85
|
-
|
86
|
-
self.session.user_pk,
|
87
|
-
str(legacy_msg_id),
|
88
|
-
self.session.legacy_to_xmpp_msg_id(legacy_msg_id),
|
89
|
-
)
|
97
|
+
orm.commit()
|
90
98
|
hints = self.__default_hints(hints)
|
91
99
|
msg = self._make_message(
|
92
100
|
mbody=body,
|
@@ -117,12 +125,12 @@ class TextMessageMixin(MessageMaker):
|
|
117
125
|
reply_to: Optional[MessageReference] = None,
|
118
126
|
thread: Optional[LegacyThreadType] = None,
|
119
127
|
hints: Optional[Iterable[ProcessingHint]] = None,
|
120
|
-
carbon=False,
|
121
|
-
archive_only=False,
|
128
|
+
carbon: bool = False,
|
129
|
+
archive_only: bool = False,
|
122
130
|
correction_event_id: Optional[LegacyMessageType] = None,
|
123
131
|
link_previews: Optional[list[LinkPreview]] = None,
|
124
132
|
**send_kwargs,
|
125
|
-
):
|
133
|
+
) -> None:
|
126
134
|
"""
|
127
135
|
Modify a message that was previously sent by this :term:`XMPP Entity`.
|
128
136
|
|
@@ -165,7 +173,7 @@ class TextMessageMixin(MessageMaker):
|
|
165
173
|
emojis: Iterable[str] = (),
|
166
174
|
thread: Optional[LegacyThreadType] = None,
|
167
175
|
**kwargs,
|
168
|
-
):
|
176
|
+
) -> None:
|
169
177
|
"""
|
170
178
|
Send a reaction (:xep:`0444`) from this :term:`XMPP Entity`.
|
171
179
|
|
@@ -174,7 +182,7 @@ class TextMessageMixin(MessageMaker):
|
|
174
182
|
:param thread:
|
175
183
|
"""
|
176
184
|
msg = self._make_message(
|
177
|
-
hints={"store"}, carbon=kwargs.get("carbon"), thread=thread
|
185
|
+
hints={"store"}, carbon=bool(kwargs.get("carbon")), thread=thread
|
178
186
|
)
|
179
187
|
xmpp_id = kwargs.pop("xmpp_id", None)
|
180
188
|
if not xmpp_id:
|
@@ -187,7 +195,7 @@ class TextMessageMixin(MessageMaker):
|
|
187
195
|
legacy_msg_id: LegacyMessageType,
|
188
196
|
thread: Optional[LegacyThreadType] = None,
|
189
197
|
**kwargs,
|
190
|
-
):
|
198
|
+
) -> None:
|
191
199
|
"""
|
192
200
|
Send a message retraction (:XEP:`0424`) from this :term:`XMPP Entity`.
|
193
201
|
|
@@ -198,7 +206,7 @@ class TextMessageMixin(MessageMaker):
|
|
198
206
|
state=None,
|
199
207
|
hints={"store"},
|
200
208
|
mbody=f"/me retracted the message {legacy_msg_id}",
|
201
|
-
carbon=kwargs.get("carbon"),
|
209
|
+
carbon=bool(kwargs.get("carbon")),
|
202
210
|
thread=thread,
|
203
211
|
)
|
204
212
|
msg.enable("fallback")
|
slidge/core/mixins/presence.py
CHANGED
@@ -1,13 +1,20 @@
|
|
1
1
|
import re
|
2
2
|
from asyncio import Task, sleep
|
3
3
|
from datetime import datetime, timedelta, timezone
|
4
|
-
from
|
4
|
+
from functools import partial
|
5
|
+
from typing import TYPE_CHECKING, Optional
|
5
6
|
|
6
7
|
from slixmpp.types import PresenceShows, PresenceTypes
|
8
|
+
from sqlalchemy.exc import InvalidRequestError
|
9
|
+
from sqlalchemy.orm.exc import DetachedInstanceError
|
7
10
|
|
11
|
+
from ...db.models import Contact, Participant
|
8
12
|
from ...util.types import CachedPresence
|
9
|
-
from .. import config
|
10
13
|
from .base import BaseSender
|
14
|
+
from .db import DBMixin
|
15
|
+
|
16
|
+
if TYPE_CHECKING:
|
17
|
+
from ..session import BaseSession
|
11
18
|
|
12
19
|
|
13
20
|
class _NoChange(Exception):
|
@@ -15,43 +22,84 @@ class _NoChange(Exception):
|
|
15
22
|
|
16
23
|
|
17
24
|
_FRIEND_REQUEST_PRESENCES = {"subscribe", "unsubscribe", "subscribed", "unsubscribed"}
|
25
|
+
_UPDATE_LAST_SEEN_FALLBACK_TASKS = dict[int, Task]()
|
26
|
+
_ONE_WEEK_SECONDS = 3600 * 24 * 7
|
27
|
+
|
28
|
+
|
29
|
+
async def _update_last_seen_fallback(session: "BaseSession", contact_pk: int) -> None:
|
30
|
+
await sleep(_ONE_WEEK_SECONDS)
|
31
|
+
with session.xmpp.store.session() as orm:
|
32
|
+
stored = orm.get(Contact, contact_pk)
|
33
|
+
if stored is None:
|
34
|
+
return
|
35
|
+
contact = session.contacts.from_store(stored)
|
36
|
+
contact.send_last_presence(force=True, no_cache_online=False)
|
18
37
|
|
19
38
|
|
20
|
-
|
39
|
+
def _clear_last_seen_task(contact_pk: int, _task) -> None:
|
40
|
+
try:
|
41
|
+
del _UPDATE_LAST_SEEN_FALLBACK_TASKS[contact_pk]
|
42
|
+
except KeyError:
|
43
|
+
pass
|
44
|
+
|
45
|
+
|
46
|
+
class PresenceMixin(BaseSender, DBMixin):
|
21
47
|
_ONLY_SEND_PRESENCE_CHANGES = False
|
22
|
-
contact_pk: Optional[int] = None
|
23
48
|
|
24
|
-
|
49
|
+
stored: Contact | Participant
|
50
|
+
|
51
|
+
def __init__(self, *a, **k) -> None:
|
25
52
|
super().__init__(*a, **k)
|
26
|
-
# FIXME: this should not be an attribute of this mixin to allow garbage
|
27
|
-
# collection of instances
|
28
|
-
self.__update_last_seen_fallback_task: Optional[Task] = None
|
29
53
|
# this is only used when a presence is set during Contact.update_info(),
|
30
54
|
# when the contact does not have a DB primary key yet, and is written
|
31
55
|
# to DB at the end of update_info()
|
32
56
|
self.cached_presence: Optional[CachedPresence] = None
|
33
57
|
|
34
|
-
|
35
|
-
|
36
|
-
|
58
|
+
def __stored(self) -> Contact | None:
|
59
|
+
if isinstance(self.stored, Contact):
|
60
|
+
return self.stored
|
61
|
+
else:
|
62
|
+
try:
|
63
|
+
return self.stored.contact
|
64
|
+
except DetachedInstanceError:
|
65
|
+
self.merge()
|
66
|
+
return self.stored.contact
|
67
|
+
|
68
|
+
@property
|
69
|
+
def __contact_pk(self) -> int | None:
|
70
|
+
stored = self.__stored()
|
71
|
+
return None if stored is None else stored.id
|
37
72
|
|
38
73
|
def _get_last_presence(self) -> Optional[CachedPresence]:
|
39
|
-
|
74
|
+
stored = self.__stored()
|
75
|
+
if stored is None or not stored.cached_presence:
|
40
76
|
return None
|
41
|
-
return
|
77
|
+
return CachedPresence(
|
78
|
+
None
|
79
|
+
if stored.last_seen is None
|
80
|
+
else stored.last_seen.replace(tzinfo=timezone.utc),
|
81
|
+
stored.ptype, # type:ignore
|
82
|
+
stored.pstatus,
|
83
|
+
stored.pshow, # type:ignore
|
84
|
+
)
|
42
85
|
|
43
|
-
def _store_last_presence(self, new: CachedPresence):
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
86
|
+
def _store_last_presence(self, new: CachedPresence) -> None:
|
87
|
+
stored = self.__stored()
|
88
|
+
if stored is not None:
|
89
|
+
stored.cached_presence = True
|
90
|
+
for k, v in new._asdict().items():
|
91
|
+
setattr(stored, k, v)
|
92
|
+
try:
|
93
|
+
self.commit()
|
94
|
+
except InvalidRequestError:
|
95
|
+
self.commit(merge=True)
|
48
96
|
|
49
97
|
def _make_presence(
|
50
98
|
self,
|
51
99
|
*,
|
52
100
|
last_seen: Optional[datetime] = None,
|
53
|
-
force=False,
|
54
|
-
bare=False,
|
101
|
+
force: bool = False,
|
102
|
+
bare: bool = False,
|
55
103
|
ptype: Optional[PresenceTypes] = None,
|
56
104
|
pstatus: Optional[str] = None,
|
57
105
|
pshow: Optional[PresenceShows] = None,
|
@@ -67,8 +115,10 @@ class PresenceMixin(BaseSender):
|
|
67
115
|
)
|
68
116
|
if old != new:
|
69
117
|
if hasattr(self, "muc") and ptype == "unavailable":
|
70
|
-
|
71
|
-
|
118
|
+
stored = self.__stored()
|
119
|
+
if stored is not None:
|
120
|
+
stored.cached_presence = False
|
121
|
+
self.commit(merge=True)
|
72
122
|
else:
|
73
123
|
self._store_last_presence(new)
|
74
124
|
if old and not force and self._ONLY_SEND_PRESENCE_CHANGES:
|
@@ -87,27 +137,33 @@ class PresenceMixin(BaseSender):
|
|
87
137
|
)
|
88
138
|
if last_seen:
|
89
139
|
# it's ugly to check for the presence of this string, but a better fix is more work
|
90
|
-
if
|
140
|
+
if not re.match(
|
91
141
|
".*Last seen .*", p["status"]
|
92
|
-
):
|
142
|
+
) and self.session.user.preferences.get("last_seen_fallback", True):
|
93
143
|
last_seen_fallback, recent = get_last_seen_fallback(last_seen)
|
94
144
|
if p["status"]:
|
95
145
|
p["status"] = p["status"] + " -- " + last_seen_fallback
|
96
146
|
else:
|
97
147
|
p["status"] = last_seen_fallback
|
98
|
-
|
148
|
+
pk = self.__contact_pk
|
149
|
+
if recent and pk is not None:
|
99
150
|
# if less than a week, we use sth like 'Last seen: Monday, 8:05",
|
100
151
|
# but if lasts more than a week, this is not very informative, so
|
101
152
|
# we need to force resend an updated presence status
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
153
|
+
task = _UPDATE_LAST_SEEN_FALLBACK_TASKS.get(pk)
|
154
|
+
if task is not None:
|
155
|
+
task.cancel()
|
156
|
+
task = self.session.create_task(
|
157
|
+
_update_last_seen_fallback(self.session, pk)
|
106
158
|
)
|
159
|
+
_UPDATE_LAST_SEEN_FALLBACK_TASKS[pk] = task
|
160
|
+
task.add_done_callback(partial(_clear_last_seen_task, pk))
|
107
161
|
p["idle"]["since"] = last_seen
|
108
162
|
return p
|
109
163
|
|
110
|
-
def send_last_presence(
|
164
|
+
def send_last_presence(
|
165
|
+
self, force: bool = False, no_cache_online: bool = False
|
166
|
+
) -> None:
|
111
167
|
if (cache := self._get_last_presence()) is None:
|
112
168
|
if force:
|
113
169
|
if no_cache_online:
|
@@ -129,7 +185,7 @@ class PresenceMixin(BaseSender):
|
|
129
185
|
self,
|
130
186
|
status: Optional[str] = None,
|
131
187
|
last_seen: Optional[datetime] = None,
|
132
|
-
):
|
188
|
+
) -> None:
|
133
189
|
"""
|
134
190
|
Send an "online" presence from this contact to the user.
|
135
191
|
|
@@ -145,7 +201,7 @@ class PresenceMixin(BaseSender):
|
|
145
201
|
self,
|
146
202
|
status: Optional[str] = None,
|
147
203
|
last_seen: Optional[datetime] = None,
|
148
|
-
):
|
204
|
+
) -> None:
|
149
205
|
"""
|
150
206
|
Send an "away" presence from this contact to the user.
|
151
207
|
|
@@ -166,7 +222,7 @@ class PresenceMixin(BaseSender):
|
|
166
222
|
self,
|
167
223
|
status: Optional[str] = None,
|
168
224
|
last_seen: Optional[datetime] = None,
|
169
|
-
):
|
225
|
+
) -> None:
|
170
226
|
"""
|
171
227
|
Send an "extended away" presence from this contact to the user.
|
172
228
|
|
@@ -187,7 +243,7 @@ class PresenceMixin(BaseSender):
|
|
187
243
|
self,
|
188
244
|
status: Optional[str] = None,
|
189
245
|
last_seen: Optional[datetime] = None,
|
190
|
-
):
|
246
|
+
) -> None:
|
191
247
|
"""
|
192
248
|
Send a "busy" (ie, "dnd") presence from this contact to the user,
|
193
249
|
|
@@ -205,7 +261,7 @@ class PresenceMixin(BaseSender):
|
|
205
261
|
self,
|
206
262
|
status: Optional[str] = None,
|
207
263
|
last_seen: Optional[datetime] = None,
|
208
|
-
):
|
264
|
+
) -> None:
|
209
265
|
"""
|
210
266
|
Send an "offline" presence from this contact to the user.
|
211
267
|
|
slidge/core/pubsub.py
CHANGED
@@ -21,8 +21,8 @@ from slixmpp.plugins.xep_0292.stanza import VCard4
|
|
21
21
|
from slixmpp.types import JidStr, OptJidStr
|
22
22
|
|
23
23
|
from ..db.avatar import CachedAvatar, avatar_cache
|
24
|
-
from ..db.
|
25
|
-
from .
|
24
|
+
from ..db.models import GatewayUser
|
25
|
+
from ..util.lock import NamedLockMixin
|
26
26
|
|
27
27
|
if TYPE_CHECKING:
|
28
28
|
from slidge.core.gateway import BaseGateway
|
@@ -32,14 +32,8 @@ if TYPE_CHECKING:
|
|
32
32
|
VCARD4_NAMESPACE = "urn:xmpp:vcard4"
|
33
33
|
|
34
34
|
|
35
|
-
class
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
class PepAvatar(PepItem):
|
40
|
-
store: SlidgeStore
|
41
|
-
|
42
|
-
def __init__(self):
|
35
|
+
class PepAvatar:
|
36
|
+
def __init__(self) -> None:
|
43
37
|
self.metadata: Optional[AvatarMetadata] = None
|
44
38
|
self.id: Optional[str] = None
|
45
39
|
self._avatar_data_path: Optional[Path] = None
|
@@ -52,7 +46,7 @@ class PepAvatar(PepItem):
|
|
52
46
|
data.set_value(self._avatar_data_path.read_bytes())
|
53
47
|
return data
|
54
48
|
|
55
|
-
def set_avatar_from_cache(self, cached_avatar: CachedAvatar):
|
49
|
+
def set_avatar_from_cache(self, cached_avatar: CachedAvatar) -> None:
|
56
50
|
metadata = AvatarMetadata()
|
57
51
|
self.id = cached_avatar.hash
|
58
52
|
metadata.add_info(
|
@@ -66,17 +60,6 @@ class PepAvatar(PepItem):
|
|
66
60
|
self._avatar_data_path = cached_avatar.path
|
67
61
|
|
68
62
|
|
69
|
-
class PepNick(PepItem):
|
70
|
-
contact_store: ContactStore
|
71
|
-
|
72
|
-
def __init__(self, nick: Optional[str] = None):
|
73
|
-
nickname = UserNick()
|
74
|
-
if nick is not None:
|
75
|
-
nickname["nick"] = nick
|
76
|
-
self.nick = nickname
|
77
|
-
self.__nick_str = nick
|
78
|
-
|
79
|
-
|
80
63
|
class PubSubComponent(NamedLockMixin, BasePlugin):
|
81
64
|
xmpp: "BaseGateway"
|
82
65
|
|
@@ -91,11 +74,11 @@ class PubSubComponent(NamedLockMixin, BasePlugin):
|
|
91
74
|
default_config = {"component_name": None}
|
92
75
|
component_name: str
|
93
76
|
|
94
|
-
def __init__(self, *a, **kw):
|
77
|
+
def __init__(self, *a, **kw) -> None:
|
95
78
|
super(PubSubComponent, self).__init__(*a, **kw)
|
96
79
|
register_stanza_plugin(EventItem, UserNick)
|
97
80
|
|
98
|
-
def plugin_init(self):
|
81
|
+
def plugin_init(self) -> None:
|
99
82
|
self.xmpp.register_handler(
|
100
83
|
CoroutineCallback(
|
101
84
|
"pubsub_get_avatar_data",
|
@@ -144,7 +127,7 @@ class PubSubComponent(NamedLockMixin, BasePlugin):
|
|
144
127
|
|
145
128
|
async def on_presence_available(
|
146
129
|
self, p: Presence, contact: Optional["LegacyContact"]
|
147
|
-
):
|
130
|
+
) -> None:
|
148
131
|
if p.get_plugin("muc_join", check=True) is not None:
|
149
132
|
log.debug("Ignoring MUC presence here")
|
150
133
|
return
|
@@ -177,14 +160,16 @@ class PubSubComponent(NamedLockMixin, BasePlugin):
|
|
177
160
|
except XMPPError:
|
178
161
|
pass
|
179
162
|
else:
|
180
|
-
await self.__broadcast(data=pep_nick
|
163
|
+
await self.__broadcast(data=pep_nick, from_=p.get_to(), to=from_)
|
181
164
|
|
182
165
|
if contact is not None and VCARD4_NAMESPACE + "+notify" in features:
|
183
166
|
await self.broadcast_vcard_event(
|
184
167
|
p.get_to(), from_, await contact.get_vcard()
|
185
168
|
)
|
186
169
|
|
187
|
-
async def broadcast_vcard_event(
|
170
|
+
async def broadcast_vcard_event(
|
171
|
+
self, from_: JID, to: JID, vcard: VCard4 | None
|
172
|
+
) -> None:
|
188
173
|
item = Item()
|
189
174
|
item.namespace = VCARD4_NAMESPACE
|
190
175
|
item["id"] = "current"
|
@@ -211,33 +196,33 @@ class PubSubComponent(NamedLockMixin, BasePlugin):
|
|
211
196
|
) -> PepAvatar:
|
212
197
|
if stanza.get_to() == self.xmpp.boundjid.bare:
|
213
198
|
item = PepAvatar()
|
214
|
-
if
|
215
|
-
item.set_avatar_from_cache(
|
199
|
+
if self.xmpp.avatar is not None:
|
200
|
+
item.set_avatar_from_cache(self.xmpp.avatar)
|
216
201
|
return item
|
217
202
|
|
218
203
|
if contact is None:
|
219
204
|
contact = await self.__get_contact(stanza)
|
220
205
|
|
221
206
|
item = PepAvatar()
|
222
|
-
if contact.
|
223
|
-
stored = avatar_cache.
|
207
|
+
if contact.stored.avatar is not None:
|
208
|
+
stored = avatar_cache.get(contact.stored.avatar)
|
224
209
|
assert stored is not None
|
225
210
|
item.set_avatar_from_cache(stored)
|
226
211
|
return item
|
227
212
|
|
228
213
|
async def _get_authorized_nick(
|
229
214
|
self, stanza: Union[Iq, Presence], contact: Optional["LegacyContact"] = None
|
230
|
-
) ->
|
215
|
+
) -> UserNick:
|
231
216
|
if stanza.get_to() == self.xmpp.boundjid.bare:
|
232
|
-
return
|
217
|
+
return get_user_nick(self.xmpp.COMPONENT_NAME)
|
233
218
|
|
234
219
|
if contact is None:
|
235
220
|
contact = await self.__get_contact(stanza)
|
236
221
|
|
237
222
|
if contact.name is not None:
|
238
|
-
return
|
223
|
+
return get_user_nick(contact.name)
|
239
224
|
else:
|
240
|
-
return
|
225
|
+
return UserNick()
|
241
226
|
|
242
227
|
def __reply_with(
|
243
228
|
self, iq: Iq, content: AvatarData | AvatarMetadata | None, item_id: str | None
|
@@ -254,11 +239,11 @@ class PubSubComponent(NamedLockMixin, BasePlugin):
|
|
254
239
|
else:
|
255
240
|
raise XMPPError("item-not-found")
|
256
241
|
|
257
|
-
async def _get_avatar_data(self, iq: Iq):
|
242
|
+
async def _get_avatar_data(self, iq: Iq) -> None:
|
258
243
|
pep_avatar = await self._get_authorized_avatar(iq)
|
259
244
|
self.__reply_with(iq, pep_avatar.data, pep_avatar.id)
|
260
245
|
|
261
|
-
async def _get_avatar_metadata(self, iq: Iq):
|
246
|
+
async def _get_avatar_metadata(self, iq: Iq) -> None:
|
262
247
|
pep_avatar = await self._get_authorized_avatar(iq)
|
263
248
|
self.__reply_with(iq, pep_avatar.metadata, pep_avatar.id)
|
264
249
|
|
@@ -279,7 +264,7 @@ class PubSubComponent(NamedLockMixin, BasePlugin):
|
|
279
264
|
payload: Optional[Union[AvatarMetadata, AvatarData, VCard4]],
|
280
265
|
id_: Optional[str],
|
281
266
|
namespace: Optional[str] = None,
|
282
|
-
):
|
267
|
+
) -> None:
|
283
268
|
result = iq.reply()
|
284
269
|
item = Item()
|
285
270
|
if payload:
|
@@ -291,7 +276,9 @@ class PubSubComponent(NamedLockMixin, BasePlugin):
|
|
291
276
|
result["pubsub"]["items"].append(item)
|
292
277
|
result.send()
|
293
278
|
|
294
|
-
async def __broadcast(
|
279
|
+
async def __broadcast(
|
280
|
+
self, data, from_: JidStr, to: OptJidStr = None, **kwargs
|
281
|
+
) -> None:
|
295
282
|
from_ = JID(from_)
|
296
283
|
if from_ != self.xmpp.boundjid.bare and to is not None:
|
297
284
|
to = JID(to)
|
@@ -319,10 +306,11 @@ class PubSubComponent(NamedLockMixin, BasePlugin):
|
|
319
306
|
msg.append(event)
|
320
307
|
|
321
308
|
if to is None:
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
309
|
+
with self.xmpp.store.session() as orm:
|
310
|
+
for u in orm.query(GatewayUser).all():
|
311
|
+
new_msg = copy(msg)
|
312
|
+
new_msg.set_to(u.jid.bare)
|
313
|
+
new_msg.send()
|
326
314
|
else:
|
327
315
|
msg.set_to(to)
|
328
316
|
msg.send()
|
@@ -345,11 +333,18 @@ class PubSubComponent(NamedLockMixin, BasePlugin):
|
|
345
333
|
user_jid: JID,
|
346
334
|
jid: JidStr,
|
347
335
|
nick: Optional[str] = None,
|
348
|
-
):
|
336
|
+
) -> None:
|
349
337
|
jid = JID(jid)
|
350
|
-
nickname =
|
351
|
-
log.debug("New nickname: %s", nickname
|
352
|
-
self.xmpp.loop.create_task(self.__broadcast(nickname
|
338
|
+
nickname = get_user_nick(nick)
|
339
|
+
log.debug("New nickname: %s", nickname)
|
340
|
+
self.xmpp.loop.create_task(self.__broadcast(nickname, jid, user_jid.bare))
|
341
|
+
|
342
|
+
|
343
|
+
def get_user_nick(nick: Optional[str] = None) -> UserNick:
|
344
|
+
user_nick = UserNick()
|
345
|
+
if nick is not None:
|
346
|
+
user_nick["nick"] = nick
|
347
|
+
return user_nick
|
353
348
|
|
354
349
|
|
355
350
|
log = logging.getLogger(__name__)
|