slidge 0.3.0b4__py3-none-any.whl → 0.3.2__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 +0 -1
- slidge/command/user.py +25 -6
- slidge/contact/contact.py +4 -1
- slidge/contact/roster.py +6 -1
- slidge/core/config.py +43 -9
- slidge/core/dispatcher/message/message.py +1 -0
- slidge/core/dispatcher/muc/misc.py +27 -2
- slidge/core/dispatcher/presence.py +32 -26
- slidge/core/dispatcher/registration.py +1 -1
- slidge/core/dispatcher/search.py +1 -5
- slidge/core/dispatcher/util.py +3 -1
- slidge/core/gateway.py +12 -1
- slidge/core/mixins/attachment.py +62 -8
- slidge/core/mixins/db.py +23 -1
- slidge/core/mixins/message_maker.py +3 -3
- slidge/core/mixins/message_text.py +1 -1
- slidge/core/mixins/presence.py +28 -10
- slidge/core/pubsub.py +1 -1
- slidge/core/session.py +13 -0
- slidge/db/models.py +1 -1
- slidge/db/store.py +29 -1
- slidge/group/archive.py +3 -0
- slidge/group/participant.py +19 -0
- slidge/group/room.py +17 -2
- slidge/slixfix/xep_0292/vcard4.py +11 -1
- slidge/util/test.py +3 -2
- slidge/util/util.py +6 -4
- {slidge-0.3.0b4.dist-info → slidge-0.3.2.dist-info}/METADATA +1 -1
- {slidge-0.3.0b4.dist-info → slidge-0.3.2.dist-info}/RECORD +33 -33
- {slidge-0.3.0b4.dist-info → slidge-0.3.2.dist-info}/WHEEL +0 -0
- {slidge-0.3.0b4.dist-info → slidge-0.3.2.dist-info}/entry_points.txt +0 -0
- {slidge-0.3.0b4.dist-info → slidge-0.3.2.dist-info}/licenses/LICENSE +0 -0
- {slidge-0.3.0b4.dist-info → slidge-0.3.2.dist-info}/top_level.txt +0 -0
slidge/core/mixins/attachment.py
CHANGED
@@ -162,15 +162,12 @@ class AttachmentMixin(TextMessageMixin):
|
|
162
162
|
legacy_file_id=None
|
163
163
|
if attachment.legacy_file_id is None
|
164
164
|
else str(attachment.legacy_file_id),
|
165
|
-
url=attachment.url,
|
165
|
+
url=attachment.url if config.USE_ATTACHMENT_ORIGINAL_URLS else None,
|
166
166
|
)
|
167
167
|
|
168
168
|
async def __get_url(
|
169
169
|
self, attachment: LegacyAttachment, stored: Attachment
|
170
170
|
) -> tuple[bool, Optional[Path], str]:
|
171
|
-
if attachment.url and config.USE_ATTACHMENT_ORIGINAL_URLS:
|
172
|
-
return False, None, attachment.url
|
173
|
-
|
174
171
|
file_name = attachment.name
|
175
172
|
content_type = attachment.content_type
|
176
173
|
file_path = attachment.path
|
@@ -221,7 +218,9 @@ class AttachmentMixin(TextMessageMixin):
|
|
221
218
|
|
222
219
|
assert isinstance(file_path, Path)
|
223
220
|
if config.FIX_FILENAME_SUFFIX_MIME_TYPE:
|
224
|
-
file_name =
|
221
|
+
file_name, content_type = fix_suffix(file_path, content_type, file_name)
|
222
|
+
attachment.content_type = content_type
|
223
|
+
attachment.name = file_name
|
225
224
|
|
226
225
|
if config.NO_UPLOAD_PATH:
|
227
226
|
local_path, new_url = await self.__no_upload(
|
@@ -312,6 +311,50 @@ class AttachmentMixin(TextMessageMixin):
|
|
312
311
|
stored.sfs = str(sfs)
|
313
312
|
msg.append(sfs)
|
314
313
|
|
314
|
+
async def __set_sfs_and_sims_without_download(
|
315
|
+
self, msg: Message, attachment: LegacyAttachment
|
316
|
+
) -> None:
|
317
|
+
assert attachment.url is not None
|
318
|
+
|
319
|
+
if not any(
|
320
|
+
(
|
321
|
+
attachment.content_type,
|
322
|
+
attachment.name,
|
323
|
+
attachment.disposition,
|
324
|
+
)
|
325
|
+
):
|
326
|
+
return
|
327
|
+
|
328
|
+
sims = self.xmpp.plugin["xep_0385"].stanza.Sims()
|
329
|
+
ref = self.xmpp["xep_0372"].stanza.Reference()
|
330
|
+
|
331
|
+
ref["uri"] = attachment.url
|
332
|
+
ref["type"] = "data"
|
333
|
+
sims["sources"].append(ref)
|
334
|
+
sims.enable("file")
|
335
|
+
|
336
|
+
xep_0447_stanza = self.xmpp.plugin["xep_0447"].stanza
|
337
|
+
sfs = xep_0447_stanza.StatelessFileSharing()
|
338
|
+
url_data = xep_0447_stanza.UrlData()
|
339
|
+
url_data["target"] = attachment.url
|
340
|
+
sfs["sources"].append(url_data)
|
341
|
+
sfs.enable("file")
|
342
|
+
|
343
|
+
if attachment.content_type:
|
344
|
+
sims["file"]["media-type"] = attachment.content_type
|
345
|
+
sfs["file"]["media-type"] = attachment.content_type
|
346
|
+
if attachment.caption:
|
347
|
+
sims["file"]["desc"] = attachment.caption
|
348
|
+
sfs["file"]["desc"] = attachment.caption
|
349
|
+
if attachment.name:
|
350
|
+
sims["file"]["name"] = attachment.name
|
351
|
+
sfs["file"]["name"] = attachment.name
|
352
|
+
if attachment.disposition:
|
353
|
+
sfs["disposition"] = attachment.disposition
|
354
|
+
|
355
|
+
msg.append(sims)
|
356
|
+
msg.append(sfs)
|
357
|
+
|
315
358
|
def __send_url(
|
316
359
|
self,
|
317
360
|
msg: Message,
|
@@ -325,6 +368,9 @@ class AttachmentMixin(TextMessageMixin):
|
|
325
368
|
) -> list[Message]:
|
326
369
|
msg["oob"]["url"] = uploaded_url
|
327
370
|
msg["body"] = uploaded_url
|
371
|
+
if msg.get_plugin("sfs", check=True):
|
372
|
+
msg["fallback"].enable("body")
|
373
|
+
msg["fallback"]["for"] = self.xmpp.plugin["xep_0447"].stanza.NAMESPACE
|
328
374
|
if caption:
|
329
375
|
m1 = self._send(msg, carbon=carbon, **kwargs)
|
330
376
|
m2 = self.send_text(
|
@@ -465,7 +511,10 @@ class AttachmentMixin(TextMessageMixin):
|
|
465
511
|
new_url = stored.url
|
466
512
|
else:
|
467
513
|
is_temp, local_path, new_url = await self.__get_url(attachment, stored)
|
468
|
-
if new_url is None
|
514
|
+
if new_url is None or (
|
515
|
+
local_path is not None and local_path.stat().st_size == 0
|
516
|
+
):
|
517
|
+
log.warning("Something went wrong with this attachment: %s", attachment)
|
469
518
|
msg["body"] = (
|
470
519
|
"I tried to send a file, but something went wrong. "
|
471
520
|
"Tell your slidge admin to check the logs."
|
@@ -474,8 +523,13 @@ class AttachmentMixin(TextMessageMixin):
|
|
474
523
|
return None, [self._send(msg, **kwargs)]
|
475
524
|
|
476
525
|
stored.url = new_url
|
477
|
-
|
478
|
-
|
526
|
+
if config.USE_ATTACHMENT_ORIGINAL_URLS and attachment.url:
|
527
|
+
await self.__set_sfs_and_sims_without_download(msg, attachment)
|
528
|
+
else:
|
529
|
+
thumbnail = await self.__set_sims(
|
530
|
+
msg, new_url, local_path, attachment, stored
|
531
|
+
)
|
532
|
+
self.__set_sfs(msg, new_url, local_path, attachment, stored, thumbnail)
|
479
533
|
|
480
534
|
if self.session is not NotImplemented:
|
481
535
|
with self.xmpp.store.session(expire_on_commit=False) as orm:
|
slidge/core/mixins/db.py
CHANGED
@@ -2,7 +2,7 @@ import logging
|
|
2
2
|
import typing
|
3
3
|
from contextlib import contextmanager
|
4
4
|
|
5
|
-
from ...db.models import Base, Contact,
|
5
|
+
from ...db.models import Base, Contact, Room
|
6
6
|
|
7
7
|
if typing.TYPE_CHECKING:
|
8
8
|
from slidge import BaseGateway
|
@@ -41,13 +41,35 @@ class UpdateInfoMixin(DBMixin):
|
|
41
41
|
def __init__(self, *args, **kwargs) -> None:
|
42
42
|
super().__init__(*args, **kwargs)
|
43
43
|
self._updating_info = False
|
44
|
+
self.__deserialize()
|
45
|
+
|
46
|
+
def __deserialize(self):
|
44
47
|
if self.stored.extra_attributes is not None:
|
45
48
|
self.deserialize_extra_attributes(self.stored.extra_attributes)
|
46
49
|
|
50
|
+
def refresh(self) -> None:
|
51
|
+
with self.xmpp.store.session(expire_on_commit=False) as orm:
|
52
|
+
orm.add(self.stored)
|
53
|
+
orm.refresh(self.stored)
|
54
|
+
self.__deserialize()
|
55
|
+
|
47
56
|
def serialize_extra_attributes(self) -> dict | None:
|
57
|
+
"""
|
58
|
+
If you want custom attributes of your instance to be stored persistently
|
59
|
+
to the DB, here is where you have to return them as a dict to be used in
|
60
|
+
`deserialize_extra_attributes()`.
|
61
|
+
|
62
|
+
"""
|
48
63
|
return None
|
49
64
|
|
50
65
|
def deserialize_extra_attributes(self, data: dict) -> None:
|
66
|
+
"""
|
67
|
+
This is where you get the dict that you passed in
|
68
|
+
`serialize_extra_attributes()`.
|
69
|
+
|
70
|
+
⚠ Since it is serialized as json, dictionary keys are converted to strings!
|
71
|
+
Be sure to convert to other types if necessary.
|
72
|
+
"""
|
51
73
|
pass
|
52
74
|
|
53
75
|
@contextmanager
|
@@ -1,7 +1,7 @@
|
|
1
|
+
import uuid
|
1
2
|
import warnings
|
2
3
|
from datetime import datetime, timezone
|
3
4
|
from typing import TYPE_CHECKING, Iterable, Optional, cast
|
4
|
-
from uuid import uuid4
|
5
5
|
|
6
6
|
from slixmpp import JID, Message
|
7
7
|
from slixmpp.types import MessageTypes
|
@@ -95,7 +95,7 @@ class MessageMaker(BaseSender):
|
|
95
95
|
msg["stanza_id"]["id"] = i
|
96
96
|
msg["stanza_id"]["by"] = self.muc.jid # type: ignore
|
97
97
|
elif self.USE_STANZA_ID:
|
98
|
-
msg["stanza_id"]["id"] = str(uuid4())
|
98
|
+
msg["stanza_id"]["id"] = str(uuid.uuid4())
|
99
99
|
msg["stanza_id"]["by"] = self.muc.jid # type: ignore
|
100
100
|
|
101
101
|
def _legacy_to_xmpp(self, legacy_id: LegacyMessageType) -> str:
|
@@ -112,7 +112,7 @@ class MessageMaker(BaseSender):
|
|
112
112
|
if when.tzinfo is None:
|
113
113
|
when = when.astimezone(timezone.utc)
|
114
114
|
if self.STRIP_SHORT_DELAY:
|
115
|
-
delay = datetime.now().astimezone(timezone.utc) - when
|
115
|
+
delay = (datetime.now().astimezone(timezone.utc) - when).seconds
|
116
116
|
if delay < config.IGNORE_DELAY_THRESHOLD:
|
117
117
|
return
|
118
118
|
msg["delay"].set_stamp(when)
|
@@ -191,7 +191,7 @@ class TextMessageMixin(MessageMaker):
|
|
191
191
|
xmpp_id = kwargs.pop("xmpp_id", None)
|
192
192
|
if not xmpp_id:
|
193
193
|
xmpp_id = self._legacy_to_xmpp(legacy_msg_id)
|
194
|
-
self.xmpp["xep_0444"].set_reactions(msg, to_id=xmpp_id, reactions=emojis)
|
194
|
+
self.xmpp["xep_0444"].set_reactions(msg, to_id=xmpp_id, reactions=set(emojis))
|
195
195
|
self.__add_reaction_fallback(msg, legacy_msg_id, emojis)
|
196
196
|
self._send(msg, **kwargs)
|
197
197
|
|
slidge/core/mixins/presence.py
CHANGED
@@ -46,6 +46,8 @@ def _clear_last_seen_task(contact_pk: int, _task) -> None:
|
|
46
46
|
class PresenceMixin(BaseSender, DBMixin):
|
47
47
|
_ONLY_SEND_PRESENCE_CHANGES = False
|
48
48
|
|
49
|
+
# this attribute actually only exists for contacts and not participants
|
50
|
+
_updating_info: bool
|
49
51
|
stored: Contact | Participant
|
50
52
|
|
51
53
|
def __init__(self, *a, **k) -> None:
|
@@ -55,15 +57,24 @@ class PresenceMixin(BaseSender, DBMixin):
|
|
55
57
|
# to DB at the end of update_info()
|
56
58
|
self.cached_presence: Optional[CachedPresence] = None
|
57
59
|
|
60
|
+
def __is_contact(self) -> bool:
|
61
|
+
return isinstance(self.stored, Contact)
|
62
|
+
|
58
63
|
def __stored(self) -> Contact | None:
|
59
|
-
if
|
64
|
+
if self.__is_contact():
|
65
|
+
assert isinstance(self.stored, Contact)
|
60
66
|
return self.stored
|
61
67
|
else:
|
68
|
+
assert isinstance(self.stored, Participant)
|
62
69
|
try:
|
63
70
|
return self.stored.contact
|
64
71
|
except DetachedInstanceError:
|
65
72
|
with self.xmpp.store.session() as orm:
|
66
73
|
orm.add(self.stored)
|
74
|
+
if self.stored.contact is None:
|
75
|
+
return None
|
76
|
+
orm.refresh(self.stored.contact)
|
77
|
+
orm.merge(self.stored)
|
67
78
|
return self.stored.contact
|
68
79
|
|
69
80
|
@property
|
@@ -85,15 +96,22 @@ class PresenceMixin(BaseSender, DBMixin):
|
|
85
96
|
)
|
86
97
|
|
87
98
|
def _store_last_presence(self, new: CachedPresence) -> None:
|
88
|
-
|
89
|
-
if
|
90
|
-
|
91
|
-
|
92
|
-
|
99
|
+
stored_contact = self.__stored()
|
100
|
+
if stored_contact is None:
|
101
|
+
return
|
102
|
+
stored_contact.cached_presence = True
|
103
|
+
for k, v in new._asdict().items():
|
104
|
+
setattr(stored_contact, k, v)
|
105
|
+
if self.__is_contact() and self._updating_info:
|
106
|
+
return
|
107
|
+
with self.xmpp.store.session(expire_on_commit=False) as orm:
|
93
108
|
try:
|
94
|
-
|
109
|
+
orm.add(stored_contact)
|
95
110
|
except InvalidRequestError:
|
96
|
-
|
111
|
+
stored_contact = orm.merge(stored_contact)
|
112
|
+
orm.add(stored_contact)
|
113
|
+
|
114
|
+
orm.commit()
|
97
115
|
|
98
116
|
def _make_presence(
|
99
117
|
self,
|
@@ -279,9 +297,9 @@ class PresenceMixin(BaseSender, DBMixin):
|
|
279
297
|
pass
|
280
298
|
|
281
299
|
|
282
|
-
def get_last_seen_fallback(last_seen: datetime):
|
300
|
+
def get_last_seen_fallback(last_seen: datetime) -> tuple[str, bool]:
|
283
301
|
now = datetime.now(tz=timezone.utc)
|
284
302
|
if now - last_seen < timedelta(days=7):
|
285
|
-
return f"Last seen {last_seen:%A %H:%M GMT}", True
|
303
|
+
return f"Last seen {last_seen:%A %H:%M %p GMT}", True
|
286
304
|
else:
|
287
305
|
return f"Last seen {last_seen:%b %-d %Y}", False
|
slidge/core/pubsub.py
CHANGED
@@ -120,7 +120,7 @@ class PubSubComponent(NamedLockMixin, BasePlugin):
|
|
120
120
|
try:
|
121
121
|
iq = await self.xmpp.plugin["xep_0030"].get_info(from_)
|
122
122
|
except (IqError, IqTimeout):
|
123
|
-
log.debug("Could get disco#info of %s, ignoring", from_)
|
123
|
+
log.debug("Could not get disco#info of %s, ignoring", from_)
|
124
124
|
return []
|
125
125
|
info = iq["disco_info"]
|
126
126
|
return info["features"]
|
slidge/core/session.py
CHANGED
@@ -539,6 +539,17 @@ class BaseSession(
|
|
539
539
|
"""
|
540
540
|
raise NotImplementedError
|
541
541
|
|
542
|
+
async def on_preferences(
|
543
|
+
self, previous: dict[str, Any], new: dict[str, Any]
|
544
|
+
) -> None:
|
545
|
+
"""
|
546
|
+
This is called when the user updates their preferences.
|
547
|
+
|
548
|
+
Override this if you need set custom preferences field and need to trigger
|
549
|
+
something when a preference has changed.
|
550
|
+
"""
|
551
|
+
raise NotImplementedError
|
552
|
+
|
542
553
|
def __reset_ready(self) -> None:
|
543
554
|
self.ready = self.xmpp.loop.create_future()
|
544
555
|
|
@@ -708,6 +719,8 @@ class BaseSession(
|
|
708
719
|
log.warning("User not found during unregistration")
|
709
720
|
return
|
710
721
|
|
722
|
+
session.cancel_all_tasks()
|
723
|
+
|
711
724
|
await cls.xmpp.unregister(session)
|
712
725
|
with cls.xmpp.store.session() as orm:
|
713
726
|
orm.delete(session.user)
|
slidge/db/models.py
CHANGED
@@ -7,7 +7,7 @@ import sqlalchemy as sa
|
|
7
7
|
from slixmpp import JID
|
8
8
|
from slixmpp.types import MucAffiliation, MucRole
|
9
9
|
from sqlalchemy import JSON, ForeignKey, Index, UniqueConstraint
|
10
|
-
from sqlalchemy.orm import Mapped,
|
10
|
+
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
11
11
|
|
12
12
|
from ..util.types import ClientType, MucType
|
13
13
|
from .meta import Base, JSONSerializable, JSONSerializableTypes
|
slidge/db/store.py
CHANGED
@@ -7,9 +7,10 @@ from datetime import datetime, timedelta, timezone
|
|
7
7
|
from mimetypes import guess_extension
|
8
8
|
from typing import Collection, Iterator, Optional, Type
|
9
9
|
|
10
|
+
import sqlalchemy as sa
|
10
11
|
from slixmpp.exceptions import XMPPError
|
11
12
|
from slixmpp.plugins.xep_0231.stanza import BitsOfBinary
|
12
|
-
from sqlalchemy import Engine, delete, select, update
|
13
|
+
from sqlalchemy import Engine, delete, event, select, update
|
13
14
|
from sqlalchemy.exc import InvalidRequestError
|
14
15
|
from sqlalchemy.orm import Session, attributes, sessionmaker
|
15
16
|
|
@@ -20,6 +21,7 @@ from .meta import Base
|
|
20
21
|
from .models import (
|
21
22
|
ArchivedMessage,
|
22
23
|
ArchivedMessageSource,
|
24
|
+
Avatar,
|
23
25
|
Bob,
|
24
26
|
Contact,
|
25
27
|
ContactSent,
|
@@ -213,6 +215,7 @@ class ContactStore(UpdatedMixin):
|
|
213
215
|
def __init__(self, session: Session) -> None:
|
214
216
|
super().__init__(session)
|
215
217
|
session.execute(update(Contact).values(cached_presence=False))
|
218
|
+
session.execute(update(Contact).values(caps_ver=None))
|
216
219
|
|
217
220
|
@staticmethod
|
218
221
|
def add_to_sent(session: Session, contact_pk: int, msg_id: str) -> None:
|
@@ -589,4 +592,29 @@ class BobStore:
|
|
589
592
|
session.add(row)
|
590
593
|
|
591
594
|
|
595
|
+
@event.listens_for(sa.orm.Session, "after_flush")
|
596
|
+
def _check_avatar_orphans(session, flush_context):
|
597
|
+
if not session.deleted:
|
598
|
+
return
|
599
|
+
|
600
|
+
potentially_orphaned = set()
|
601
|
+
for obj in session.deleted:
|
602
|
+
if isinstance(obj, (Contact, Room)) and obj.avatar_id:
|
603
|
+
potentially_orphaned.add(obj.avatar_id)
|
604
|
+
if not potentially_orphaned:
|
605
|
+
return
|
606
|
+
|
607
|
+
result = session.execute(
|
608
|
+
sa.delete(Avatar).where(
|
609
|
+
sa.and_(
|
610
|
+
Avatar.id.in_(potentially_orphaned),
|
611
|
+
sa.not_(sa.exists().where(Contact.avatar_id == Avatar.id)),
|
612
|
+
sa.not_(sa.exists().where(Room.avatar_id == Avatar.id)),
|
613
|
+
)
|
614
|
+
)
|
615
|
+
)
|
616
|
+
deleted_count = result.rowcount
|
617
|
+
log.debug(f"Auto-deleted %s orphaned avatars", deleted_count)
|
618
|
+
|
619
|
+
|
592
620
|
log = logging.getLogger(__name__)
|
slidge/group/archive.py
CHANGED
slidge/group/participant.py
CHANGED
@@ -22,6 +22,7 @@ from ..util.types import (
|
|
22
22
|
CachedPresence,
|
23
23
|
Hat,
|
24
24
|
LegacyMessageType,
|
25
|
+
LegacyThreadType,
|
25
26
|
MessageOrPresenceTypeVar,
|
26
27
|
MucAffiliation,
|
27
28
|
MucRole,
|
@@ -526,6 +527,24 @@ class LegacyParticipant(
|
|
526
527
|
msg.xml.append(ET.Element(f"{{{msg.namespace}}}subject"))
|
527
528
|
self._send(msg, full_jid)
|
528
529
|
|
530
|
+
def set_thread_subject(
|
531
|
+
self,
|
532
|
+
thread: LegacyThreadType,
|
533
|
+
subject: str | None,
|
534
|
+
when: Optional[datetime] = None,
|
535
|
+
) -> None:
|
536
|
+
msg = self._make_message()
|
537
|
+
msg["thread"] = str(thread)
|
538
|
+
if when is not None:
|
539
|
+
msg["delay"].set_stamp(when)
|
540
|
+
msg["delay"]["from"] = self.muc.jid
|
541
|
+
if subject:
|
542
|
+
msg["subject"] = subject
|
543
|
+
else:
|
544
|
+
# may be simplified if slixmpp lets it do it more easily some day
|
545
|
+
msg.xml.append(ET.Element(f"{{{msg.namespace}}}subject"))
|
546
|
+
self._send(msg)
|
547
|
+
|
529
548
|
|
530
549
|
def escape_nickname(muc_jid: JID, nickname: str) -> tuple[str, JID]:
|
531
550
|
nickname = nickname_no_illegal = strip_illegal_chars(nickname)
|
slidge/group/room.py
CHANGED
@@ -43,6 +43,7 @@ from ..util.types import (
|
|
43
43
|
LegacyGroupIdType,
|
44
44
|
LegacyMessageType,
|
45
45
|
LegacyParticipantType,
|
46
|
+
LegacyThreadType,
|
46
47
|
LegacyUserIdType,
|
47
48
|
Mention,
|
48
49
|
MucAffiliation,
|
@@ -295,7 +296,7 @@ class LegacyMUC(
|
|
295
296
|
self, affiliation: Optional[MucAffiliation] = None
|
296
297
|
) -> AsyncIterator[LegacyParticipantType]:
|
297
298
|
await self.__fill_participants()
|
298
|
-
with self.xmpp.store.session(expire_on_commit=False) as orm:
|
299
|
+
with self.xmpp.store.session(expire_on_commit=False, autoflush=False) as orm:
|
299
300
|
orm.add(self.stored)
|
300
301
|
for db_participant in self.stored.participants:
|
301
302
|
if (
|
@@ -523,6 +524,9 @@ class LegacyMUC(
|
|
523
524
|
if s := self.subject:
|
524
525
|
form.add_field("muc#roominfo_subject", value=s)
|
525
526
|
|
527
|
+
if name := self.name:
|
528
|
+
form.add_field("muc#roomconfig_roomname", value=name)
|
529
|
+
|
526
530
|
if self._set_avatar_task is not None:
|
527
531
|
await self._set_avatar_task
|
528
532
|
avatar = self.get_avatar()
|
@@ -651,7 +655,7 @@ class LegacyMUC(
|
|
651
655
|
with orm.no_autoflush:
|
652
656
|
orm.refresh(self.stored, ["participants"])
|
653
657
|
if not user_participant.is_user:
|
654
|
-
self.log.warning("is_user flag not set
|
658
|
+
self.log.warning("is_user flag not set on user_participant")
|
655
659
|
user_participant.is_user = True
|
656
660
|
user_participant.send_initial_presence(
|
657
661
|
user_full_jid,
|
@@ -1335,6 +1339,17 @@ class LegacyMUC(
|
|
1335
1339
|
"""
|
1336
1340
|
raise NotImplementedError
|
1337
1341
|
|
1342
|
+
async def on_set_thread_subject(
|
1343
|
+
self, thread: LegacyThreadType, subject: str
|
1344
|
+
) -> None:
|
1345
|
+
"""
|
1346
|
+
Triggered when the user requests changing the subject of a specific thread.
|
1347
|
+
|
1348
|
+
:param thread: Legacy identifier of the thread
|
1349
|
+
:param subject: The new subject for this thread.
|
1350
|
+
"""
|
1351
|
+
raise NotImplementedError
|
1352
|
+
|
1338
1353
|
@property
|
1339
1354
|
def participants_filled(self) -> bool:
|
1340
1355
|
return self.stored.participants_filled
|
@@ -1,5 +1,6 @@
|
|
1
|
+
from slixmpp import register_stanza_plugin, __version_info__
|
1
2
|
from slixmpp.plugins.base import BasePlugin, register_plugin
|
2
|
-
from slixmpp.plugins.xep_0292.stanza import NS
|
3
|
+
from slixmpp.plugins.xep_0292.stanza import NS, _VCardTextElementBase, VCard4
|
3
4
|
|
4
5
|
|
5
6
|
class VCard4Provider(BasePlugin):
|
@@ -11,4 +12,13 @@ class VCard4Provider(BasePlugin):
|
|
11
12
|
self.xmpp.plugin["xep_0030"].add_feature(NS)
|
12
13
|
|
13
14
|
|
15
|
+
|
16
|
+
|
14
17
|
register_plugin(VCard4Provider)
|
18
|
+
|
19
|
+
|
20
|
+
if __version_info__[0] <= 1 and __version_info__[1] <= 11:
|
21
|
+
class Pronouns(_VCardTextElementBase):
|
22
|
+
name = plugin_attrib = "pronouns"
|
23
|
+
|
24
|
+
register_stanza_plugin(VCard4, Pronouns)
|
slidge/util/test.py
CHANGED
@@ -42,7 +42,7 @@ from slidge import (
|
|
42
42
|
|
43
43
|
from ..command import Command
|
44
44
|
from ..core import config
|
45
|
-
from ..core.
|
45
|
+
from ..core.session import _sessions
|
46
46
|
from ..db import SlidgeStore
|
47
47
|
from ..db.avatar import avatar_cache
|
48
48
|
from ..db.meta import Base
|
@@ -206,7 +206,7 @@ class SlidgeTest(SlixTestPlus):
|
|
206
206
|
user_jid_validator = ".*"
|
207
207
|
admins: list[str] = []
|
208
208
|
upload_requester = None
|
209
|
-
ignore_delay_threshold =
|
209
|
+
ignore_delay_threshold = 300
|
210
210
|
|
211
211
|
@classmethod
|
212
212
|
def setUpClass(cls) -> None:
|
@@ -281,6 +281,7 @@ class SlidgeTest(SlixTestPlus):
|
|
281
281
|
self.db_engine.echo = False
|
282
282
|
super().tearDown()
|
283
283
|
Base.metadata.drop_all(self.xmpp.store._engine)
|
284
|
+
_sessions.clear()
|
284
285
|
|
285
286
|
def setup_logged_session(self, n_contacts: int = 0) -> None:
|
286
287
|
with self.xmpp.store.session() as orm:
|
slidge/util/util.py
CHANGED
@@ -34,7 +34,9 @@ except ImportError as e:
|
|
34
34
|
)
|
35
35
|
|
36
36
|
|
37
|
-
def fix_suffix(
|
37
|
+
def fix_suffix(
|
38
|
+
path: Path, mime_type: Optional[str], file_name: Optional[str]
|
39
|
+
) -> tuple[str, str]:
|
38
40
|
guessed = magic.from_file(path, mime=True)
|
39
41
|
if guessed == mime_type:
|
40
42
|
log.debug("Magic and given MIME match")
|
@@ -53,15 +55,15 @@ def fix_suffix(path: Path, mime_type: Optional[str], file_name: Optional[str]) -
|
|
53
55
|
|
54
56
|
if suffix in valid_suffix_list:
|
55
57
|
log.debug("Suffix %s is in %s", suffix, valid_suffix_list)
|
56
|
-
return name
|
58
|
+
return str(name), guessed
|
57
59
|
|
58
60
|
valid_suffix = mimetypes.guess_extension(mime_type.split(";")[0], strict=False)
|
59
61
|
if valid_suffix is None:
|
60
62
|
log.debug("No valid suffix found")
|
61
|
-
return name
|
63
|
+
return str(name), guessed
|
62
64
|
|
63
65
|
log.debug("Changing suffix of %s to %s", file_name or path.name, valid_suffix)
|
64
|
-
return name.with_suffix(valid_suffix)
|
66
|
+
return str(name.with_suffix(valid_suffix)), guessed
|
65
67
|
|
66
68
|
|
67
69
|
class SubclassableOnce(type):
|