slidge 0.1.3__py3-none-any.whl → 0.2.0__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- slidge/__init__.py +3 -5
- slidge/__main__.py +2 -197
- slidge/__version__.py +5 -0
- slidge/command/adhoc.py +40 -17
- slidge/command/admin.py +24 -12
- slidge/command/base.py +10 -8
- slidge/command/categories.py +13 -3
- slidge/command/chat_command.py +29 -2
- slidge/command/register.py +32 -16
- slidge/command/user.py +106 -13
- slidge/contact/contact.py +254 -50
- slidge/contact/roster.py +124 -53
- slidge/core/config.py +19 -13
- slidge/core/dispatcher/__init__.py +3 -0
- slidge/core/{gateway → dispatcher}/caps.py +12 -8
- slidge/core/{gateway → dispatcher}/disco.py +10 -18
- slidge/core/dispatcher/message/__init__.py +10 -0
- slidge/core/dispatcher/message/chat_state.py +40 -0
- slidge/core/dispatcher/message/marker.py +62 -0
- slidge/core/dispatcher/message/message.py +397 -0
- slidge/core/dispatcher/muc/__init__.py +12 -0
- slidge/core/dispatcher/muc/admin.py +98 -0
- slidge/core/{gateway → dispatcher/muc}/mam.py +25 -17
- slidge/core/dispatcher/muc/misc.py +121 -0
- slidge/core/dispatcher/muc/owner.py +96 -0
- slidge/core/{gateway → dispatcher/muc}/ping.py +11 -17
- slidge/core/dispatcher/presence.py +176 -0
- slidge/core/dispatcher/registration.py +85 -0
- slidge/core/{gateway → dispatcher}/search.py +9 -16
- slidge/core/dispatcher/session_dispatcher.py +84 -0
- slidge/core/dispatcher/util.py +174 -0
- slidge/core/{gateway/vcard_temp.py → dispatcher/vcard.py} +35 -19
- slidge/core/{gateway/base.py → gateway.py} +176 -153
- slidge/core/mixins/__init__.py +11 -1
- slidge/core/mixins/attachment.py +106 -67
- slidge/core/mixins/avatar.py +94 -25
- slidge/core/mixins/base.py +10 -4
- slidge/core/mixins/db.py +18 -0
- slidge/core/mixins/disco.py +0 -10
- slidge/core/mixins/lock.py +10 -8
- slidge/core/mixins/message.py +11 -195
- slidge/core/mixins/message_maker.py +17 -9
- slidge/core/mixins/message_text.py +211 -0
- slidge/core/mixins/presence.py +17 -4
- slidge/core/pubsub.py +114 -288
- slidge/core/session.py +101 -40
- slidge/db/__init__.py +4 -0
- slidge/db/alembic/__init__.py +0 -0
- slidge/db/alembic/env.py +64 -0
- slidge/db/alembic/old_user_store.py +183 -0
- slidge/db/alembic/script.py.mako +26 -0
- slidge/db/alembic/versions/09f27f098baa_add_missing_attributes_in_room.py +36 -0
- slidge/db/alembic/versions/15b0bd83407a_remove_bogus_unique_constraints_on_room_.py +85 -0
- slidge/db/alembic/versions/2461390c0af2_store_contacts_caps_verstring_in_db.py +36 -0
- slidge/db/alembic/versions/29f5280c61aa_store_subject_setter_in_room.py +37 -0
- slidge/db/alembic/versions/2b1f45ab7379_store_room_subject_setter_by_nickname.py +41 -0
- slidge/db/alembic/versions/3071e0fa69d4_add_contact_client_type.py +52 -0
- slidge/db/alembic/versions/45c24cc73c91_add_bob.py +42 -0
- slidge/db/alembic/versions/5bd48bfdffa2_lift_room_legacy_id_constraint.py +61 -0
- slidge/db/alembic/versions/82a4af84b679_add_muc_history_filled.py +48 -0
- slidge/db/alembic/versions/8b993243a536_add_vcard_content_to_contact_table.py +43 -0
- slidge/db/alembic/versions/8d2ced764698_rely_on_db_to_store_contacts_rooms_and_.py +139 -0
- slidge/db/alembic/versions/aa9d82a7f6ef_db_creation.py +101 -0
- slidge/db/alembic/versions/abba1ae0edb3_store_avatar_legacy_id_in_the_contact_.py +79 -0
- slidge/db/alembic/versions/b33993e87db3_move_everything_to_persistent_db.py +214 -0
- slidge/db/alembic/versions/b64b1a793483_add_source_and_legacy_id_for_archived_.py +52 -0
- slidge/db/alembic/versions/c4a8ec35a0e8_per_room_user_nick.py +34 -0
- slidge/db/alembic/versions/e91195719c2c_store_users_avatars_persistently.py +26 -0
- slidge/db/avatar.py +205 -0
- slidge/db/meta.py +72 -0
- slidge/db/models.py +405 -0
- slidge/db/store.py +1257 -0
- slidge/group/archive.py +58 -14
- slidge/group/bookmarks.py +89 -65
- slidge/group/participant.py +107 -40
- slidge/group/room.py +402 -213
- slidge/main.py +202 -0
- slidge/migration.py +45 -1
- slidge/slixfix/__init__.py +31 -1
- slidge/{core/gateway → slixfix}/delivery_receipt.py +1 -1
- slidge/slixfix/roster.py +13 -4
- slidge/slixfix/xep_0292/vcard4.py +1 -87
- slidge/util/archive_msg.py +2 -1
- slidge/util/db.py +4 -228
- slidge/util/test.py +91 -4
- slidge/util/types.py +39 -4
- slidge/util/util.py +45 -2
- {slidge-0.1.3.dist-info → slidge-0.2.0.dist-info}/METADATA +10 -5
- slidge-0.2.0.dist-info/RECORD +131 -0
- slidge-0.2.0.dist-info/entry_points.txt +3 -0
- slidge/core/cache.py +0 -183
- slidge/core/gateway/__init__.py +0 -3
- slidge/core/gateway/muc_admin.py +0 -35
- slidge/core/gateway/presence.py +0 -95
- slidge/core/gateway/registration.py +0 -53
- slidge/core/gateway/session_dispatcher.py +0 -804
- slidge/util/schema.sql +0 -126
- slidge/util/sql.py +0 -508
- slidge-0.1.3.dist-info/RECORD +0 -96
- slidge-0.1.3.dist-info/entry_points.txt +0 -3
- {slidge-0.1.3.dist-info → slidge-0.2.0.dist-info}/LICENSE +0 -0
- {slidge-0.1.3.dist-info → slidge-0.2.0.dist-info}/WHEEL +0 -0
slidge/core/pubsub.py
CHANGED
@@ -1,12 +1,8 @@
|
|
1
|
-
import hashlib
|
2
|
-
import io
|
3
1
|
import logging
|
4
2
|
from copy import copy
|
5
3
|
from pathlib import Path
|
6
|
-
from typing import TYPE_CHECKING, Optional,
|
4
|
+
from typing import TYPE_CHECKING, Optional, Union
|
7
5
|
|
8
|
-
from PIL import Image, UnidentifiedImageError
|
9
|
-
from PIL.Image import Image as PILImage
|
10
6
|
from slixmpp import (
|
11
7
|
JID,
|
12
8
|
CoroutineCallback,
|
@@ -24,34 +20,28 @@ from slixmpp.plugins.xep_0172 import UserNick
|
|
24
20
|
from slixmpp.plugins.xep_0292.stanza import VCard4
|
25
21
|
from slixmpp.types import JidStr, OptJidStr
|
26
22
|
|
27
|
-
from ..
|
28
|
-
from ..
|
29
|
-
from ..util.db import GatewayUser, user_store
|
30
|
-
from ..util.sql import db
|
31
|
-
from ..util.types import AvatarType, LegacyFileIdType, PepItemType
|
32
|
-
from .cache import CachedAvatar, avatar_cache
|
23
|
+
from ..db.avatar import CachedAvatar, avatar_cache
|
24
|
+
from ..db.store import ContactStore, SlidgeStore
|
33
25
|
from .mixins.lock import NamedLockMixin
|
34
26
|
|
35
27
|
if TYPE_CHECKING:
|
36
|
-
from slidge import BaseGateway
|
28
|
+
from slidge.core.gateway import BaseGateway
|
29
|
+
|
30
|
+
from ..contact.contact import LegacyContact
|
37
31
|
|
38
32
|
VCARD4_NAMESPACE = "urn:xmpp:vcard4"
|
39
33
|
|
40
34
|
|
41
35
|
class PepItem:
|
42
|
-
|
43
|
-
def from_db(jid: JID, user: Optional[GatewayUser] = None) -> Optional["PepItem"]:
|
44
|
-
raise NotImplementedError
|
45
|
-
|
46
|
-
def to_db(self, jid: JID, user: Optional[GatewayUser] = None):
|
47
|
-
raise NotImplementedError
|
36
|
+
pass
|
48
37
|
|
49
38
|
|
50
39
|
class PepAvatar(PepItem):
|
51
|
-
|
40
|
+
store: SlidgeStore
|
41
|
+
|
42
|
+
def __init__(self):
|
52
43
|
self.metadata: Optional[AvatarMetadata] = None
|
53
44
|
self.id: Optional[str] = None
|
54
|
-
self.jid = jid
|
55
45
|
self._avatar_data_path: Optional[Path] = None
|
56
46
|
|
57
47
|
@property
|
@@ -62,63 +52,7 @@ class PepAvatar(PepItem):
|
|
62
52
|
data.set_value(self._avatar_data_path.read_bytes())
|
63
53
|
return data
|
64
54
|
|
65
|
-
|
66
|
-
def _sha(b: bytes):
|
67
|
-
return hashlib.sha1(b).hexdigest()
|
68
|
-
|
69
|
-
def _get_weak_unique_id(self, avatar: AvatarType):
|
70
|
-
if isinstance(avatar, str):
|
71
|
-
return avatar # url
|
72
|
-
elif isinstance(avatar, bytes):
|
73
|
-
return self._sha(avatar)
|
74
|
-
elif isinstance(avatar, Path):
|
75
|
-
return self._sha(avatar.read_bytes())
|
76
|
-
|
77
|
-
@staticmethod
|
78
|
-
async def _get_image(avatar: AvatarType) -> PILImage:
|
79
|
-
if isinstance(avatar, str):
|
80
|
-
# async with aiohttp.ClientSession() as session:
|
81
|
-
async with avatar_cache.http.get(avatar) as response:
|
82
|
-
return Image.open(io.BytesIO(await response.read()))
|
83
|
-
elif isinstance(avatar, bytes):
|
84
|
-
return Image.open(io.BytesIO(avatar))
|
85
|
-
elif isinstance(avatar, Path):
|
86
|
-
return Image.open(avatar)
|
87
|
-
else:
|
88
|
-
raise TypeError("Avatar must be bytes, a Path or a str (URL)", avatar)
|
89
|
-
|
90
|
-
async def set_avatar(
|
91
|
-
self, avatar: AvatarType, unique_id: Optional[LegacyFileIdType] = None
|
92
|
-
):
|
93
|
-
if unique_id is None:
|
94
|
-
if isinstance(avatar, str):
|
95
|
-
await self._set_avatar_from_url_alone(avatar)
|
96
|
-
return
|
97
|
-
unique_id = self._get_weak_unique_id(avatar)
|
98
|
-
|
99
|
-
await self._set_avatar_from_unique_id(avatar, unique_id)
|
100
|
-
|
101
|
-
async def _set_avatar_from_unique_id(
|
102
|
-
self, avatar: AvatarType, unique_id: LegacyFileIdType
|
103
|
-
):
|
104
|
-
cached_avatar = avatar_cache.get(unique_id)
|
105
|
-
if cached_avatar:
|
106
|
-
# this shouldn't be necessary but here to re-use avatars downloaded
|
107
|
-
# before the change introducing the JID to unique ID mapping
|
108
|
-
avatar_cache.store_jid(self.jid, unique_id)
|
109
|
-
else:
|
110
|
-
img = await self._get_image(avatar)
|
111
|
-
cached_avatar = await avatar_cache.convert_and_store(
|
112
|
-
img, unique_id, self.jid
|
113
|
-
)
|
114
|
-
|
115
|
-
self._set_avatar_from_cache(cached_avatar)
|
116
|
-
|
117
|
-
async def _set_avatar_from_url_alone(self, url: str):
|
118
|
-
cached_avatar = await avatar_cache.get_avatar_from_url_alone(url, self.jid)
|
119
|
-
self._set_avatar_from_cache(cached_avatar)
|
120
|
-
|
121
|
-
def _set_avatar_from_cache(self, cached_avatar: CachedAvatar):
|
55
|
+
def set_avatar_from_cache(self, cached_avatar: CachedAvatar):
|
122
56
|
metadata = AvatarMetadata()
|
123
57
|
self.id = cached_avatar.hash
|
124
58
|
metadata.add_info(
|
@@ -131,31 +65,10 @@ class PepAvatar(PepItem):
|
|
131
65
|
self.metadata = metadata
|
132
66
|
self._avatar_data_path = cached_avatar.path
|
133
67
|
|
134
|
-
@staticmethod
|
135
|
-
def from_db(jid: JID, user: Optional[GatewayUser] = None) -> Optional["PepAvatar"]:
|
136
|
-
cached_id = db.avatar_get(jid)
|
137
|
-
if cached_id is None:
|
138
|
-
return None
|
139
|
-
item = PepAvatar(jid)
|
140
|
-
cached_avatar = avatar_cache.get(cached_id)
|
141
|
-
if cached_avatar is None:
|
142
|
-
raise XMPPError("internal-server-error")
|
143
|
-
item._set_avatar_from_cache(cached_avatar)
|
144
|
-
return item
|
145
|
-
|
146
|
-
def to_db(self, jid: JID, user=None):
|
147
|
-
cached_id = avatar_cache.get_cached_id_for(jid)
|
148
|
-
if cached_id is None:
|
149
|
-
log.warning("Could not store avatar for %s", jid)
|
150
|
-
return
|
151
|
-
db.avatar_store(jid, cached_id)
|
152
|
-
|
153
|
-
@staticmethod
|
154
|
-
def remove_from_db(jid: JID):
|
155
|
-
db.avatar_delete(jid)
|
156
|
-
|
157
68
|
|
158
69
|
class PepNick(PepItem):
|
70
|
+
contact_store: ContactStore
|
71
|
+
|
159
72
|
def __init__(self, nick: Optional[str] = None):
|
160
73
|
nickname = UserNick()
|
161
74
|
if nick is not None:
|
@@ -163,20 +76,6 @@ class PepNick(PepItem):
|
|
163
76
|
self.nick = nickname
|
164
77
|
self.__nick_str = nick
|
165
78
|
|
166
|
-
@staticmethod
|
167
|
-
def from_db(jid: JID, user: Optional[GatewayUser] = None) -> Optional["PepNick"]:
|
168
|
-
if user is None:
|
169
|
-
raise XMPPError("not-allowed")
|
170
|
-
nick = db.nick_get(jid, user)
|
171
|
-
if nick is None:
|
172
|
-
return None
|
173
|
-
return PepNick(nick)
|
174
|
-
|
175
|
-
def to_db(self, jid: JID, user: Optional[GatewayUser] = None):
|
176
|
-
if user is None:
|
177
|
-
raise XMPPError("not-allowed")
|
178
|
-
db.nick_store(jid, str(self.__nick_str), user)
|
179
|
-
|
180
79
|
|
181
80
|
class PubSubComponent(NamedLockMixin, BasePlugin):
|
182
81
|
xmpp: "BaseGateway"
|
@@ -186,7 +85,6 @@ class PubSubComponent(NamedLockMixin, BasePlugin):
|
|
186
85
|
dependencies = {
|
187
86
|
"xep_0030",
|
188
87
|
"xep_0060",
|
189
|
-
# "xep_0084",
|
190
88
|
"xep_0115",
|
191
89
|
"xep_0163",
|
192
90
|
}
|
@@ -219,7 +117,6 @@ class PubSubComponent(NamedLockMixin, BasePlugin):
|
|
219
117
|
self._get_vcard, # type:ignore
|
220
118
|
)
|
221
119
|
)
|
222
|
-
self.xmpp.add_event_handler("presence_available", self._on_presence_available)
|
223
120
|
|
224
121
|
disco = self.xmpp.plugin["xep_0030"]
|
225
122
|
disco.add_identity("pubsub", "pep", self.component_name)
|
@@ -228,82 +125,72 @@ class PubSubComponent(NamedLockMixin, BasePlugin):
|
|
228
125
|
disco.add_feature("http://jabber.org/protocol/pubsub#retrieve-items")
|
229
126
|
disco.add_feature("http://jabber.org/protocol/pubsub#persistent-items")
|
230
127
|
|
231
|
-
async def
|
128
|
+
async def __get_features(self, presence: Presence) -> list[str]:
|
129
|
+
from_ = presence.get_from()
|
130
|
+
ver_string = presence["caps"]["ver"]
|
131
|
+
if ver_string:
|
132
|
+
info = await self.xmpp.plugin["xep_0115"].get_caps(from_)
|
133
|
+
else:
|
134
|
+
info = None
|
135
|
+
if info is None:
|
136
|
+
async with self.lock(from_):
|
137
|
+
iq = await self.xmpp.plugin["xep_0030"].get_info(from_)
|
138
|
+
info = iq["disco_info"]
|
139
|
+
return info["features"]
|
140
|
+
|
141
|
+
async def on_presence_available(
|
142
|
+
self, p: Presence, contact: Optional["LegacyContact"]
|
143
|
+
):
|
232
144
|
if p.get_plugin("muc_join", check=True) is not None:
|
233
145
|
log.debug("Ignoring MUC presence here")
|
234
146
|
return
|
235
147
|
|
236
|
-
from_ = p.get_from()
|
237
|
-
ver_string = p["caps"]["ver"]
|
238
|
-
info = None
|
239
|
-
|
240
148
|
to = p.get_to()
|
241
|
-
|
242
|
-
# we don't want to push anything for contacts that are not in the user's roster
|
243
149
|
if to != self.xmpp.boundjid.bare:
|
244
|
-
|
245
|
-
|
246
|
-
if session is None:
|
150
|
+
# we don't want to push anything for contacts that are not in the user's roster
|
151
|
+
if contact is None or not contact.is_friend:
|
247
152
|
return
|
248
153
|
|
249
|
-
|
250
|
-
|
251
|
-
contact = await session.contacts.by_jid(to)
|
252
|
-
except XMPPError as e:
|
253
|
-
log.debug(
|
254
|
-
"Could not determine if %s was added to the roster: %s", to, e
|
255
|
-
)
|
256
|
-
return
|
257
|
-
except Exception as e:
|
258
|
-
log.warning("Could not determine if %s was added to the roster.", to)
|
259
|
-
log.exception(e)
|
260
|
-
return
|
261
|
-
if not contact.is_friend:
|
262
|
-
return
|
154
|
+
from_ = p.get_from()
|
155
|
+
features = await self.__get_features(p)
|
263
156
|
|
264
|
-
if ver_string:
|
265
|
-
info = await self.xmpp.plugin["xep_0115"].get_caps(from_)
|
266
|
-
if info is None:
|
267
|
-
async with self.lock(from_):
|
268
|
-
iq = await self.xmpp.plugin["xep_0030"].get_info(from_)
|
269
|
-
info = iq["disco_info"]
|
270
|
-
features = info["features"]
|
271
157
|
if AvatarMetadata.namespace + "+notify" in features:
|
272
158
|
try:
|
273
|
-
pep_avatar = await self._get_authorized_avatar(p)
|
159
|
+
pep_avatar = await self._get_authorized_avatar(p, contact)
|
274
160
|
except XMPPError:
|
275
161
|
pass
|
276
162
|
else:
|
277
|
-
if pep_avatar.metadata is None:
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
)
|
163
|
+
if pep_avatar.metadata is not None:
|
164
|
+
await self.__broadcast(
|
165
|
+
data=pep_avatar.metadata,
|
166
|
+
from_=p.get_to().bare,
|
167
|
+
to=from_,
|
168
|
+
id=pep_avatar.metadata["info"]["id"],
|
169
|
+
)
|
285
170
|
if UserNick.namespace + "+notify" in features:
|
286
171
|
try:
|
287
|
-
pep_nick = await self._get_authorized_nick(p)
|
172
|
+
pep_nick = await self._get_authorized_nick(p, contact)
|
288
173
|
except XMPPError:
|
289
174
|
pass
|
290
175
|
else:
|
291
|
-
await self.
|
176
|
+
await self.__broadcast(data=pep_nick.nick, from_=p.get_to(), to=from_)
|
292
177
|
|
293
|
-
if VCARD4_NAMESPACE + "+notify" in features:
|
294
|
-
await self.broadcast_vcard_event(
|
178
|
+
if contact is not None and VCARD4_NAMESPACE + "+notify" in features:
|
179
|
+
await self.broadcast_vcard_event(
|
180
|
+
p.get_to(), from_, await contact.get_vcard()
|
181
|
+
)
|
295
182
|
|
296
|
-
async def broadcast_vcard_event(self, from_, to):
|
183
|
+
async def broadcast_vcard_event(self, from_: JID, to: JID, vcard: VCard4 | None):
|
297
184
|
item = Item()
|
298
185
|
item.namespace = VCARD4_NAMESPACE
|
299
186
|
item["id"] = "current"
|
300
|
-
vcard: VCard4 = await self.xmpp["xep_0292_provider"].get_vcard(from_, to)
|
187
|
+
# vcard: VCard4 = await self.xmpp["xep_0292_provider"].get_vcard(from_, to)
|
301
188
|
# The vcard content should NOT be in this event according to the spec:
|
302
189
|
# https://xmpp.org/extensions/xep-0292.html#sect-idm45669698174224
|
303
|
-
# but movim expects it to be here, and I guess
|
190
|
+
# but movim expects it to be here, and I guess it does not hurt
|
304
191
|
|
305
192
|
log.debug("Broadcast vcard4 event: %s", vcard)
|
306
|
-
await self.
|
193
|
+
await self.__broadcast(
|
307
194
|
data=vcard,
|
308
195
|
from_=JID(from_).bare,
|
309
196
|
to=to,
|
@@ -311,71 +198,76 @@ class PubSubComponent(NamedLockMixin, BasePlugin):
|
|
311
198
|
node=VCARD4_NAMESPACE,
|
312
199
|
)
|
313
200
|
|
314
|
-
async def
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
201
|
+
async def __get_contact(self, stanza: Union[Iq, Presence]):
|
202
|
+
session = self.xmpp.get_session_from_stanza(stanza)
|
203
|
+
return await session.contacts.by_jid(stanza.get_to())
|
204
|
+
|
205
|
+
async def _get_authorized_avatar(
|
206
|
+
self, stanza: Union[Iq, Presence], contact: Optional["LegacyContact"] = None
|
207
|
+
) -> PepAvatar:
|
208
|
+
if stanza.get_to() == self.xmpp.boundjid.bare:
|
209
|
+
item = PepAvatar()
|
210
|
+
item.set_avatar_from_cache(avatar_cache.get_by_pk(self.xmpp.avatar_pk))
|
211
|
+
return item
|
212
|
+
|
213
|
+
if contact is None:
|
214
|
+
contact = await self.__get_contact(stanza)
|
215
|
+
|
216
|
+
item = PepAvatar()
|
217
|
+
if contact.avatar_pk is not None:
|
218
|
+
stored = avatar_cache.get_by_pk(contact.avatar_pk)
|
219
|
+
assert stored is not None
|
220
|
+
item.set_avatar_from_cache(stored)
|
221
|
+
return item
|
328
222
|
|
329
|
-
async def
|
330
|
-
|
223
|
+
async def _get_authorized_nick(
|
224
|
+
self, stanza: Union[Iq, Presence], contact: Optional["LegacyContact"] = None
|
225
|
+
) -> PepNick:
|
226
|
+
if stanza.get_to() == self.xmpp.boundjid.bare:
|
227
|
+
return PepNick(self.xmpp.COMPONENT_NAME)
|
331
228
|
|
332
|
-
|
333
|
-
|
229
|
+
if contact is None:
|
230
|
+
contact = await self.__get_contact(stanza)
|
334
231
|
|
335
|
-
|
336
|
-
|
232
|
+
if contact.name is not None:
|
233
|
+
return PepNick(contact.name)
|
234
|
+
else:
|
235
|
+
return PepNick()
|
337
236
|
|
237
|
+
def __reply_with(
|
238
|
+
self, iq: Iq, content: AvatarData | AvatarMetadata | None, item_id: str | None
|
239
|
+
) -> None:
|
338
240
|
requested_items = iq["pubsub"]["items"]
|
241
|
+
|
339
242
|
if len(requested_items) == 0:
|
340
|
-
self._reply_with_payload(iq,
|
243
|
+
self._reply_with_payload(iq, content, item_id)
|
341
244
|
else:
|
342
245
|
for item in requested_items:
|
343
|
-
if item["id"] ==
|
344
|
-
self._reply_with_payload(iq,
|
246
|
+
if item["id"] == item_id:
|
247
|
+
self._reply_with_payload(iq, content, item_id)
|
345
248
|
return
|
346
249
|
else:
|
347
250
|
raise XMPPError("item-not-found")
|
348
251
|
|
349
|
-
async def
|
252
|
+
async def _get_avatar_data(self, iq: Iq):
|
350
253
|
pep_avatar = await self._get_authorized_avatar(iq)
|
254
|
+
self.__reply_with(iq, pep_avatar.data, pep_avatar.id)
|
351
255
|
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
else:
|
356
|
-
for item in requested_items:
|
357
|
-
if item["id"] == pep_avatar.id:
|
358
|
-
self._reply_with_payload(iq, pep_avatar.metadata, pep_avatar.id)
|
359
|
-
return
|
360
|
-
else:
|
361
|
-
raise XMPPError("item-not-found")
|
256
|
+
async def _get_avatar_metadata(self, iq: Iq):
|
257
|
+
pep_avatar = await self._get_authorized_avatar(iq)
|
258
|
+
self.__reply_with(iq, pep_avatar.metadata, pep_avatar.id)
|
362
259
|
|
363
260
|
async def _get_vcard(self, iq: Iq):
|
364
261
|
# this is not the proper way that clients should retrieve VCards, but
|
365
262
|
# gajim does it this way.
|
366
263
|
# https://xmpp.org/extensions/xep-0292.html#sect-idm45669698174224
|
367
|
-
|
368
|
-
|
369
|
-
)
|
370
|
-
log.debug("VCARD: %s -- %s -- %s", iq.get_to().bare, iq.get_from().bare, vcard)
|
264
|
+
session = self.xmpp.get_session_from_stanza(iq)
|
265
|
+
contact = await session.contacts.by_jid(iq.get_to())
|
266
|
+
vcard = await contact.get_vcard()
|
371
267
|
if vcard is None:
|
372
268
|
raise XMPPError("item-not-found")
|
373
269
|
self._reply_with_payload(iq, vcard, "current", VCARD4_NAMESPACE)
|
374
270
|
|
375
|
-
@staticmethod
|
376
|
-
def get_avatar(jid: JID):
|
377
|
-
return PepAvatar.from_db(jid)
|
378
|
-
|
379
271
|
@staticmethod
|
380
272
|
def _reply_with_payload(
|
381
273
|
iq: Iq,
|
@@ -394,7 +286,7 @@ class PubSubComponent(NamedLockMixin, BasePlugin):
|
|
394
286
|
result["pubsub"]["items"].append(item)
|
395
287
|
result.send()
|
396
288
|
|
397
|
-
async def
|
289
|
+
async def __broadcast(self, data, from_: JidStr, to: OptJidStr = None, **kwargs):
|
398
290
|
from_ = JID(from_)
|
399
291
|
if from_ != self.xmpp.boundjid.bare and to is not None:
|
400
292
|
to = JID(to)
|
@@ -402,12 +294,6 @@ class PubSubComponent(NamedLockMixin, BasePlugin):
|
|
402
294
|
if session is None:
|
403
295
|
return
|
404
296
|
await session.ready
|
405
|
-
try:
|
406
|
-
entity = await session.get_contact_or_group_or_participant(from_)
|
407
|
-
except ContactIsUser:
|
408
|
-
return
|
409
|
-
if isinstance(entity, LegacyContact) and not entity.is_friend:
|
410
|
-
return
|
411
297
|
|
412
298
|
item = EventItem()
|
413
299
|
if data:
|
@@ -428,97 +314,37 @@ class PubSubComponent(NamedLockMixin, BasePlugin):
|
|
428
314
|
msg.append(event)
|
429
315
|
|
430
316
|
if to is None:
|
431
|
-
for u in
|
317
|
+
for u in self.xmpp.store.users.get_all():
|
432
318
|
new_msg = copy(msg)
|
433
|
-
new_msg.set_to(u.
|
319
|
+
new_msg.set_to(u.jid.bare)
|
434
320
|
new_msg.send()
|
435
321
|
else:
|
436
322
|
msg.set_to(to)
|
437
323
|
msg.send()
|
438
324
|
|
439
|
-
async def
|
440
|
-
self,
|
441
|
-
|
442
|
-
avatar: Optional[AvatarType] = None,
|
443
|
-
broadcast_to: OptJidStr = None,
|
444
|
-
unique_id=None,
|
445
|
-
broadcast=True,
|
446
|
-
):
|
447
|
-
jid = JID(jid)
|
448
|
-
if avatar is None:
|
449
|
-
PepAvatar.remove_from_db(jid)
|
450
|
-
await self._broadcast(AvatarMetadata(), jid, broadcast_to)
|
451
|
-
avatar_cache.delete_jid(jid)
|
452
|
-
else:
|
453
|
-
pep_avatar = PepAvatar(jid)
|
454
|
-
try:
|
455
|
-
await pep_avatar.set_avatar(avatar, unique_id)
|
456
|
-
except (UnidentifiedImageError, FileNotFoundError) as e:
|
457
|
-
log.warning("Failed to set avatar for %s: %r", self, e)
|
458
|
-
return
|
459
|
-
pep_avatar.to_db(jid)
|
460
|
-
if pep_avatar.metadata is None:
|
461
|
-
raise RuntimeError
|
462
|
-
if not broadcast:
|
463
|
-
return
|
464
|
-
await self._broadcast(
|
465
|
-
pep_avatar.metadata,
|
466
|
-
jid,
|
467
|
-
broadcast_to,
|
468
|
-
id=pep_avatar.metadata["info"]["id"],
|
469
|
-
)
|
470
|
-
|
471
|
-
async def set_avatar_from_cache(
|
472
|
-
self, jid: JID, send_empty: bool, broadcast_to: OptJidStr = None, broadcast=True
|
473
|
-
):
|
474
|
-
uid = avatar_cache.get_cached_id_for(jid)
|
475
|
-
if uid is None:
|
476
|
-
if not send_empty:
|
477
|
-
return
|
478
|
-
self.xmpp.loop.create_task(
|
479
|
-
self.set_avatar(jid, None, broadcast_to, uid, broadcast)
|
480
|
-
)
|
481
|
-
return
|
482
|
-
cached_avatar = avatar_cache.get(str(uid))
|
325
|
+
async def broadcast_avatar(
|
326
|
+
self, from_: JidStr, to: JidStr, cached_avatar: Optional[CachedAvatar]
|
327
|
+
) -> None:
|
483
328
|
if cached_avatar is None:
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
329
|
+
await self.__broadcast(AvatarMetadata(), from_, to)
|
330
|
+
else:
|
331
|
+
pep_avatar = PepAvatar()
|
332
|
+
pep_avatar.set_avatar_from_cache(cached_avatar)
|
333
|
+
assert pep_avatar.metadata is not None
|
334
|
+
await self.__broadcast(
|
335
|
+
pep_avatar.metadata, from_, to, id=pep_avatar.metadata["info"]["id"]
|
489
336
|
)
|
490
|
-
return
|
491
|
-
self.xmpp.loop.create_task(
|
492
|
-
self.set_avatar(jid, cached_avatar.path, broadcast_to, uid, broadcast)
|
493
|
-
)
|
494
337
|
|
495
|
-
def
|
338
|
+
def broadcast_nick(
|
496
339
|
self,
|
497
|
-
|
340
|
+
user_jid: JID,
|
498
341
|
jid: JidStr,
|
499
342
|
nick: Optional[str] = None,
|
500
343
|
):
|
501
344
|
jid = JID(jid)
|
502
345
|
nickname = PepNick(nick)
|
503
|
-
nickname.to_db(jid, user)
|
504
346
|
log.debug("New nickname: %s", nickname.nick)
|
505
|
-
self.xmpp.loop.create_task(self.
|
506
|
-
|
507
|
-
async def broadcast_all(self, from_: JID, to: JID):
|
508
|
-
"""
|
509
|
-
Force push avatar and nick for a stored JID.
|
510
|
-
"""
|
511
|
-
a = PepAvatar.from_db(from_)
|
512
|
-
if a:
|
513
|
-
if a.metadata:
|
514
|
-
await self._broadcast(
|
515
|
-
a.metadata, from_, to, id=a.metadata["info"]["id"]
|
516
|
-
)
|
517
|
-
else:
|
518
|
-
log.warning("No metadata associated to this cached avatar?!")
|
519
|
-
n = PepNick.from_db(from_, user_store.get_by_jid(to))
|
520
|
-
if n:
|
521
|
-
await self._broadcast(n.nick, from_, to)
|
347
|
+
self.xmpp.loop.create_task(self.__broadcast(nickname.nick, jid, user_jid.bare))
|
522
348
|
|
523
349
|
|
524
350
|
log = logging.getLogger(__name__)
|