slidge 0.2.0a0__py3-none-any.whl → 0.2.0a1__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/__version__.py +1 -1
 - slidge/command/admin.py +1 -1
 - slidge/command/user.py +0 -1
 - slidge/contact/contact.py +86 -32
 - slidge/contact/roster.py +79 -19
 - slidge/core/config.py +1 -4
 - slidge/core/gateway/base.py +9 -2
 - slidge/core/gateway/caps.py +7 -5
 - slidge/core/gateway/muc_admin.py +1 -1
 - slidge/core/gateway/session_dispatcher.py +20 -6
 - slidge/core/gateway/vcard_temp.py +1 -1
 - slidge/core/mixins/attachment.py +17 -4
 - slidge/core/mixins/avatar.py +26 -9
 - slidge/core/mixins/db.py +18 -0
 - slidge/core/mixins/disco.py +0 -10
 - slidge/core/mixins/message.py +7 -1
 - slidge/core/mixins/presence.py +6 -3
 - slidge/core/pubsub.py +7 -15
 - slidge/core/session.py +6 -3
 - slidge/db/alembic/versions/2461390c0af2_store_contacts_caps_verstring_in_db.py +36 -0
 - slidge/db/alembic/versions/2b1f45ab7379_store_room_subject_setter_by_nickname.py +41 -0
 - slidge/db/alembic/versions/82a4af84b679_add_muc_history_filled.py +48 -0
 - slidge/db/alembic/versions/aa9d82a7f6ef_db_creation.py +11 -2
 - slidge/db/alembic/versions/b64b1a793483_add_source_and_legacy_id_for_archived_.py +48 -0
 - slidge/db/alembic/versions/c4a8ec35a0e8_per_room_user_nick.py +34 -0
 - slidge/db/avatar.py +20 -9
 - slidge/db/models.py +16 -6
 - slidge/db/store.py +217 -115
 - slidge/group/archive.py +46 -1
 - slidge/group/bookmarks.py +17 -5
 - slidge/group/participant.py +10 -3
 - slidge/group/room.py +183 -125
 - slidge/main.py +3 -3
 - slidge/slixfix/xep_0292/vcard4.py +2 -0
 - slidge/util/archive_msg.py +2 -1
 - slidge/util/test.py +54 -4
 - slidge/util/types.py +5 -0
 - slidge/util/util.py +22 -0
 - {slidge-0.2.0a0.dist-info → slidge-0.2.0a1.dist-info}/METADATA +2 -4
 - {slidge-0.2.0a0.dist-info → slidge-0.2.0a1.dist-info}/RECORD +43 -37
 - {slidge-0.2.0a0.dist-info → slidge-0.2.0a1.dist-info}/LICENSE +0 -0
 - {slidge-0.2.0a0.dist-info → slidge-0.2.0a1.dist-info}/WHEEL +0 -0
 - {slidge-0.2.0a0.dist-info → slidge-0.2.0a1.dist-info}/entry_points.txt +0 -0
 
    
        slidge/group/room.py
    CHANGED
    
    | 
         @@ -5,7 +5,7 @@ import string 
     | 
|
| 
       5 
5 
     | 
    
         
             
            import warnings
         
     | 
| 
       6 
6 
     | 
    
         
             
            from copy import copy
         
     | 
| 
       7 
7 
     | 
    
         
             
            from datetime import datetime, timedelta, timezone
         
     | 
| 
       8 
     | 
    
         
            -
            from typing import TYPE_CHECKING, Generic, Optional, Self, Union
         
     | 
| 
      
 8 
     | 
    
         
            +
            from typing import TYPE_CHECKING, AsyncIterator, Generic, Optional, Self, Union
         
     | 
| 
       9 
9 
     | 
    
         
             
            from uuid import uuid4
         
     | 
| 
       10 
10 
     | 
    
         | 
| 
       11 
11 
     | 
    
         
             
            from slixmpp import JID, Iq, Message, Presence
         
     | 
| 
         @@ -21,12 +21,14 @@ from ..contact.roster import ContactIsUser 
     | 
|
| 
       21 
21 
     | 
    
         
             
            from ..core import config
         
     | 
| 
       22 
22 
     | 
    
         
             
            from ..core.mixins import StoredAttributeMixin
         
     | 
| 
       23 
23 
     | 
    
         
             
            from ..core.mixins.avatar import AvatarMixin
         
     | 
| 
      
 24 
     | 
    
         
            +
            from ..core.mixins.db import UpdateInfoMixin
         
     | 
| 
       24 
25 
     | 
    
         
             
            from ..core.mixins.disco import ChatterDiscoMixin
         
     | 
| 
       25 
26 
     | 
    
         
             
            from ..core.mixins.lock import NamedLockMixin
         
     | 
| 
       26 
27 
     | 
    
         
             
            from ..core.mixins.recipient import ReactionRecipientMixin, ThreadRecipientMixin
         
     | 
| 
       27 
28 
     | 
    
         
             
            from ..db.models import Room
         
     | 
| 
       28 
29 
     | 
    
         
             
            from ..util import ABCSubclassableOnceAtMost
         
     | 
| 
       29 
30 
     | 
    
         
             
            from ..util.types import (
         
     | 
| 
      
 31 
     | 
    
         
            +
                HoleBound,
         
     | 
| 
       30 
32 
     | 
    
         
             
                LegacyGroupIdType,
         
     | 
| 
       31 
33 
     | 
    
         
             
                LegacyMessageType,
         
     | 
| 
       32 
34 
     | 
    
         
             
                LegacyParticipantType,
         
     | 
| 
         @@ -35,7 +37,7 @@ from ..util.types import ( 
     | 
|
| 
       35 
37 
     | 
    
         
             
                MucAffiliation,
         
     | 
| 
       36 
38 
     | 
    
         
             
                MucType,
         
     | 
| 
       37 
39 
     | 
    
         
             
            )
         
     | 
| 
       38 
     | 
    
         
            -
            from ..util.util import deprecated
         
     | 
| 
      
 40 
     | 
    
         
            +
            from ..util.util import deprecated, timeit, with_session
         
     | 
| 
       39 
41 
     | 
    
         
             
            from .archive import MessageArchive
         
     | 
| 
       40 
42 
     | 
    
         
             
            from .participant import LegacyParticipant
         
     | 
| 
       41 
43 
     | 
    
         | 
| 
         @@ -45,11 +47,14 @@ if TYPE_CHECKING: 
     | 
|
| 
       45 
47 
     | 
    
         | 
| 
       46 
48 
     | 
    
         
             
            ADMIN_NS = "http://jabber.org/protocol/muc#admin"
         
     | 
| 
       47 
49 
     | 
    
         | 
| 
      
 50 
     | 
    
         
            +
            SubjectSetterType = Union[str, None, "LegacyContact", "LegacyParticipant"]
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
       48 
52 
     | 
    
         | 
| 
       49 
53 
     | 
    
         
             
            class LegacyMUC(
         
     | 
| 
       50 
54 
     | 
    
         
             
                Generic[
         
     | 
| 
       51 
55 
     | 
    
         
             
                    LegacyGroupIdType, LegacyMessageType, LegacyParticipantType, LegacyUserIdType
         
     | 
| 
       52 
56 
     | 
    
         
             
                ],
         
     | 
| 
      
 57 
     | 
    
         
            +
                UpdateInfoMixin,
         
     | 
| 
       53 
58 
     | 
    
         
             
                StoredAttributeMixin,
         
     | 
| 
       54 
59 
     | 
    
         
             
                AvatarMixin,
         
     | 
| 
       55 
60 
     | 
    
         
             
                NamedLockMixin,
         
     | 
| 
         @@ -120,7 +125,6 @@ class LegacyMUC( 
     | 
|
| 
       120 
125 
     | 
    
         
             
                tries to set the room subject.
         
     | 
| 
       121 
126 
     | 
    
         
             
                """
         
     | 
| 
       122 
127 
     | 
    
         | 
| 
       123 
     | 
    
         
            -
                _avatar_pubsub_broadcast = False
         
     | 
| 
       124 
128 
     | 
    
         
             
                _avatar_bare_jid = True
         
     | 
| 
       125 
129 
     | 
    
         
             
                archive: MessageArchive
         
     | 
| 
       126 
130 
     | 
    
         | 
| 
         @@ -137,15 +141,13 @@ class LegacyMUC( 
     | 
|
| 
       137 
141 
     | 
    
         
             
                    self.Participant = LegacyParticipant.get_self_or_unique_subclass()
         
     | 
| 
       138 
142 
     | 
    
         | 
| 
       139 
143 
     | 
    
         
             
                    self._subject = ""
         
     | 
| 
       140 
     | 
    
         
            -
                    self._subject_setter:  
     | 
| 
       141 
     | 
    
         
            -
                        None
         
     | 
| 
       142 
     | 
    
         
            -
                    )
         
     | 
| 
      
 144 
     | 
    
         
            +
                    self._subject_setter: Optional[str] = None
         
     | 
| 
       143 
145 
     | 
    
         | 
| 
       144 
146 
     | 
    
         
             
                    self.pk: Optional[int] = None
         
     | 
| 
       145 
147 
     | 
    
         
             
                    self._user_nick: Optional[str] = None
         
     | 
| 
       146 
148 
     | 
    
         | 
| 
       147 
149 
     | 
    
         
             
                    self._participants_filled = False
         
     | 
| 
       148 
     | 
    
         
            -
                    self. 
     | 
| 
      
 150 
     | 
    
         
            +
                    self._history_filled = False
         
     | 
| 
       149 
151 
     | 
    
         
             
                    self._description = ""
         
     | 
| 
       150 
152 
     | 
    
         
             
                    self._subject_date: Optional[datetime] = None
         
     | 
| 
       151 
153 
     | 
    
         | 
| 
         @@ -165,6 +167,8 @@ class LegacyMUC( 
     | 
|
| 
       165 
167 
     | 
    
         
             
                    if self._n_participants == n_participants:
         
     | 
| 
       166 
168 
     | 
    
         
             
                        return
         
     | 
| 
       167 
169 
     | 
    
         
             
                    self._n_participants = n_participants
         
     | 
| 
      
 170 
     | 
    
         
            +
                    if self._updating_info:
         
     | 
| 
      
 171 
     | 
    
         
            +
                        return
         
     | 
| 
       168 
172 
     | 
    
         
             
                    assert self.pk is not None
         
     | 
| 
       169 
173 
     | 
    
         
             
                    self.__store.update_n_participants(self.pk, n_participants)
         
     | 
| 
       170 
174 
     | 
    
         | 
| 
         @@ -182,6 +186,8 @@ class LegacyMUC( 
     | 
|
| 
       182 
186 
     | 
    
         
             
                @subject_date.setter
         
     | 
| 
       183 
187 
     | 
    
         
             
                def subject_date(self, when: Optional[datetime]) -> None:
         
     | 
| 
       184 
188 
     | 
    
         
             
                    self._subject_date = when
         
     | 
| 
      
 189 
     | 
    
         
            +
                    if self._updating_info:
         
     | 
| 
      
 190 
     | 
    
         
            +
                        return
         
     | 
| 
       185 
191 
     | 
    
         
             
                    assert self.pk is not None
         
     | 
| 
       186 
192 
     | 
    
         
             
                    self.__store.update_subject_date(self.pk, when)
         
     | 
| 
       187 
193 
     | 
    
         | 
| 
         @@ -211,39 +217,66 @@ class LegacyMUC( 
     | 
|
| 
       211 
217 
     | 
    
         
             
                    self.__store.set_resource(self.pk, self._user_resources)
         
     | 
| 
       212 
218 
     | 
    
         | 
| 
       213 
219 
     | 
    
         
             
                async def __fill_participants(self):
         
     | 
| 
      
 220 
     | 
    
         
            +
                    if self._participants_filled:
         
     | 
| 
      
 221 
     | 
    
         
            +
                        return
         
     | 
| 
      
 222 
     | 
    
         
            +
                    assert self.pk is not None
         
     | 
| 
       214 
223 
     | 
    
         
             
                    async with self.lock("fill participants"):
         
     | 
| 
       215 
     | 
    
         
            -
                        if self._participants_filled:
         
     | 
| 
       216 
     | 
    
         
            -
                            return
         
     | 
| 
       217 
224 
     | 
    
         
             
                        self._participants_filled = True
         
     | 
| 
       218 
     | 
    
         
            -
                         
     | 
| 
       219 
     | 
    
         
            -
                             
     | 
| 
       220 
     | 
    
         
            -
             
     | 
| 
       221 
     | 
    
         
            -
             
     | 
| 
      
 225 
     | 
    
         
            +
                        async for p in self.fill_participants():
         
     | 
| 
      
 226 
     | 
    
         
            +
                            self.__participants_store.update(p)
         
     | 
| 
      
 227 
     | 
    
         
            +
                            self.__store.set_participants_filled(self.pk)
         
     | 
| 
      
 228 
     | 
    
         
            +
             
     | 
| 
      
 229 
     | 
    
         
            +
                async def get_participants(self) -> AsyncIterator[LegacyParticipant]:
         
     | 
| 
      
 230 
     | 
    
         
            +
                    assert self.pk is not None
         
     | 
| 
      
 231 
     | 
    
         
            +
                    if self._participants_filled:
         
     | 
| 
      
 232 
     | 
    
         
            +
                        for db_participant in self.xmpp.store.participants.get_all(
         
     | 
| 
      
 233 
     | 
    
         
            +
                            self.pk, user_included=True
         
     | 
| 
      
 234 
     | 
    
         
            +
                        ):
         
     | 
| 
      
 235 
     | 
    
         
            +
                            participant = self.Participant.from_store(self.session, db_participant)
         
     | 
| 
      
 236 
     | 
    
         
            +
                            yield participant
         
     | 
| 
      
 237 
     | 
    
         
            +
                        return
         
     | 
| 
      
 238 
     | 
    
         
            +
             
     | 
| 
      
 239 
     | 
    
         
            +
                    async with self.lock("fill participants"):
         
     | 
| 
      
 240 
     | 
    
         
            +
                        self._participants_filled = True
         
     | 
| 
      
 241 
     | 
    
         
            +
                        # We only fill the participants list if/when the MUC is first
         
     | 
| 
      
 242 
     | 
    
         
            +
                        # joined by an XMPP client. But we may have instantiated
         
     | 
| 
      
 243 
     | 
    
         
            +
                        resources = set[str]()
         
     | 
| 
      
 244 
     | 
    
         
            +
                        for db_participant in self.xmpp.store.participants.get_all(
         
     | 
| 
      
 245 
     | 
    
         
            +
                            self.pk, user_included=True
         
     | 
| 
      
 246 
     | 
    
         
            +
                        ):
         
     | 
| 
      
 247 
     | 
    
         
            +
                            participant = self.Participant.from_store(self.session, db_participant)
         
     | 
| 
      
 248 
     | 
    
         
            +
                            resources.add(participant.jid.resource)
         
     | 
| 
      
 249 
     | 
    
         
            +
                            yield participant
         
     | 
| 
      
 250 
     | 
    
         
            +
                        async for p in self.fill_participants():
         
     | 
| 
      
 251 
     | 
    
         
            +
                            if p.jid.resource not in resources:
         
     | 
| 
      
 252 
     | 
    
         
            +
                                yield p
         
     | 
| 
      
 253 
     | 
    
         
            +
                        self.__store.set_participants_filled(self.pk)
         
     | 
| 
      
 254 
     | 
    
         
            +
                        return
         
     | 
| 
       222 
255 
     | 
    
         | 
| 
       223 
256 
     | 
    
         
             
                async def __fill_history(self):
         
     | 
| 
       224 
257 
     | 
    
         
             
                    async with self.lock("fill history"):
         
     | 
| 
       225 
     | 
    
         
            -
                        if self. 
     | 
| 
      
 258 
     | 
    
         
            +
                        if self._history_filled:
         
     | 
| 
       226 
259 
     | 
    
         
             
                            log.debug("History has already been fetched %s", self)
         
     | 
| 
       227 
260 
     | 
    
         
             
                            return
         
     | 
| 
       228 
261 
     | 
    
         
             
                        log.debug("Fetching history for %s", self)
         
     | 
| 
       229 
     | 
    
         
            -
                        for msg in self.archive:
         
     | 
| 
       230 
     | 
    
         
            -
                            try:
         
     | 
| 
       231 
     | 
    
         
            -
                                legacy_id = self.session.xmpp_to_legacy_msg_id(msg.id)
         
     | 
| 
       232 
     | 
    
         
            -
                                oldest_date = msg.when
         
     | 
| 
       233 
     | 
    
         
            -
                            except Exception as e:
         
     | 
| 
       234 
     | 
    
         
            -
                                # not all archived stanzas have a valid legacy msg ID, eg
         
     | 
| 
       235 
     | 
    
         
            -
                                # reactions, corrections, message with multiple attachments…
         
     | 
| 
       236 
     | 
    
         
            -
                                self.log.debug(f"Could not convert during history back-filling {e}")
         
     | 
| 
       237 
     | 
    
         
            -
                            else:
         
     | 
| 
       238 
     | 
    
         
            -
                                break
         
     | 
| 
       239 
     | 
    
         
            -
                        else:
         
     | 
| 
       240 
     | 
    
         
            -
                            legacy_id = None
         
     | 
| 
       241 
     | 
    
         
            -
                            oldest_date = None
         
     | 
| 
       242 
262 
     | 
    
         
             
                        try:
         
     | 
| 
       243 
     | 
    
         
            -
                             
     | 
| 
      
 263 
     | 
    
         
            +
                            before, after = self.archive.get_hole_bounds()
         
     | 
| 
      
 264 
     | 
    
         
            +
                            if before is not None:
         
     | 
| 
      
 265 
     | 
    
         
            +
                                before = before._replace(
         
     | 
| 
      
 266 
     | 
    
         
            +
                                    id=self.xmpp.LEGACY_MSG_ID_TYPE(before.id)  # type:ignore
         
     | 
| 
      
 267 
     | 
    
         
            +
                                )
         
     | 
| 
      
 268 
     | 
    
         
            +
                            if after is not None:
         
     | 
| 
      
 269 
     | 
    
         
            +
                                after = after._replace(
         
     | 
| 
      
 270 
     | 
    
         
            +
                                    id=self.xmpp.LEGACY_MSG_ID_TYPE(after.id)  # type:ignore
         
     | 
| 
      
 271 
     | 
    
         
            +
                                )
         
     | 
| 
      
 272 
     | 
    
         
            +
                            await self.backfill(before, after)
         
     | 
| 
       244 
273 
     | 
    
         
             
                        except NotImplementedError:
         
     | 
| 
       245 
274 
     | 
    
         
             
                            return
         
     | 
| 
       246 
     | 
    
         
            -
                         
     | 
| 
      
 275 
     | 
    
         
            +
                        except Exception as e:
         
     | 
| 
      
 276 
     | 
    
         
            +
                            log.exception("Could not backfill: %s", e)
         
     | 
| 
      
 277 
     | 
    
         
            +
                        assert self.pk is not None
         
     | 
| 
      
 278 
     | 
    
         
            +
                        self.__store.set_history_filled(self.pk, True)
         
     | 
| 
      
 279 
     | 
    
         
            +
                        self._history_filled = True
         
     | 
| 
       247 
280 
     | 
    
         | 
| 
       248 
281 
     | 
    
         
             
                @property
         
     | 
| 
       249 
282 
     | 
    
         
             
                def name(self):
         
     | 
| 
         @@ -255,6 +288,10 @@ class LegacyMUC( 
     | 
|
| 
       255 
288 
     | 
    
         
             
                        return
         
     | 
| 
       256 
289 
     | 
    
         
             
                    self.DISCO_NAME = n
         
     | 
| 
       257 
290 
     | 
    
         
             
                    self.__send_configuration_change((104,))
         
     | 
| 
      
 291 
     | 
    
         
            +
                    if self._updating_info:
         
     | 
| 
      
 292 
     | 
    
         
            +
                        return
         
     | 
| 
      
 293 
     | 
    
         
            +
                    assert self.pk is not None
         
     | 
| 
      
 294 
     | 
    
         
            +
                    self.__store.update_name(self.pk, n)
         
     | 
| 
       258 
295 
     | 
    
         | 
| 
       259 
296 
     | 
    
         
             
                @property
         
     | 
| 
       260 
297 
     | 
    
         
             
                def description(self):
         
     | 
| 
         @@ -265,9 +302,11 @@ class LegacyMUC( 
     | 
|
| 
       265 
302 
     | 
    
         
             
                    if self._description == d:
         
     | 
| 
       266 
303 
     | 
    
         
             
                        return
         
     | 
| 
       267 
304 
     | 
    
         
             
                    self._description = d
         
     | 
| 
      
 305 
     | 
    
         
            +
                    self.__send_configuration_change((104,))
         
     | 
| 
      
 306 
     | 
    
         
            +
                    if self._updating_info:
         
     | 
| 
      
 307 
     | 
    
         
            +
                        return
         
     | 
| 
       268 
308 
     | 
    
         
             
                    assert self.pk is not None
         
     | 
| 
       269 
309 
     | 
    
         
             
                    self.__store.update_description(self.pk, d)
         
     | 
| 
       270 
     | 
    
         
            -
                    self.__send_configuration_change((104,))
         
     | 
| 
       271 
310 
     | 
    
         | 
| 
       272 
311 
     | 
    
         
             
                def on_presence_unavailable(self, p: Presence):
         
     | 
| 
       273 
312 
     | 
    
         
             
                    pto = p.get_to()
         
     | 
| 
         @@ -307,27 +346,35 @@ class LegacyMUC( 
     | 
|
| 
       307 
346 
     | 
    
         | 
| 
       308 
347 
     | 
    
         
             
                async def backfill(
         
     | 
| 
       309 
348 
     | 
    
         
             
                    self,
         
     | 
| 
       310 
     | 
    
         
            -
                     
     | 
| 
       311 
     | 
    
         
            -
                     
     | 
| 
      
 349 
     | 
    
         
            +
                    after: Optional[HoleBound] = None,
         
     | 
| 
      
 350 
     | 
    
         
            +
                    before: Optional[HoleBound] = None,
         
     | 
| 
       312 
351 
     | 
    
         
             
                ):
         
     | 
| 
       313 
352 
     | 
    
         
             
                    """
         
     | 
| 
       314 
     | 
    
         
            -
                    Override this if the legacy network provide server-side  
     | 
| 
       315 
     | 
    
         
            -
             
     | 
| 
       316 
     | 
    
         
            -
                     
     | 
| 
       317 
     | 
    
         
            -
             
     | 
| 
       318 
     | 
    
         
            -
                     
     | 
| 
       319 
     | 
    
         
            -
             
     | 
| 
       320 
     | 
    
         
            -
                    :param  
     | 
| 
       321 
     | 
    
         
            -
             
     | 
| 
      
 353 
     | 
    
         
            +
                    Override this if the legacy network provide server-side group archives.
         
     | 
| 
      
 354 
     | 
    
         
            +
             
     | 
| 
      
 355 
     | 
    
         
            +
                    In it, send history messages using ``self.get_participant(xxx).send_xxxx``,
         
     | 
| 
      
 356 
     | 
    
         
            +
                    with the ``archive_only=True`` kwarg. This is only called once per slidge
         
     | 
| 
      
 357 
     | 
    
         
            +
                    run for a given group.
         
     | 
| 
      
 358 
     | 
    
         
            +
             
     | 
| 
      
 359 
     | 
    
         
            +
                    :param after: Fetch messages after this one. If ``None``, it's up to you
         
     | 
| 
      
 360 
     | 
    
         
            +
                        to decide how far you want to go in the archive. If it's not ``None``,
         
     | 
| 
      
 361 
     | 
    
         
            +
                        it means slidge has some messages in this archive and you should really try
         
     | 
| 
      
 362 
     | 
    
         
            +
                        to complete it to avoid "holes" in the history of this group.
         
     | 
| 
      
 363 
     | 
    
         
            +
                    :param before: Fetch messages before this one. If ``None``, fetch all messages
         
     | 
| 
      
 364 
     | 
    
         
            +
                        up to the most recent one
         
     | 
| 
       322 
365 
     | 
    
         
             
                    """
         
     | 
| 
       323 
366 
     | 
    
         
             
                    raise NotImplementedError
         
     | 
| 
       324 
367 
     | 
    
         | 
| 
       325 
     | 
    
         
            -
                async def fill_participants(self):
         
     | 
| 
      
 368 
     | 
    
         
            +
                async def fill_participants(self) -> AsyncIterator[LegacyParticipant]:
         
     | 
| 
       326 
369 
     | 
    
         
             
                    """
         
     | 
| 
       327 
     | 
    
         
            -
                     
     | 
| 
       328 
     | 
    
         
            -
             
     | 
| 
      
 370 
     | 
    
         
            +
                    This method should yield the list of all members of this group.
         
     | 
| 
      
 371 
     | 
    
         
            +
             
     | 
| 
      
 372 
     | 
    
         
            +
                    Typically, use ``participant = self.get_participant()``, self.get_participant_by_contact(),
         
     | 
| 
      
 373 
     | 
    
         
            +
                    of self.get_user_participant(), and update their affiliation, hats, etc.
         
     | 
| 
      
 374 
     | 
    
         
            +
                    before yielding them.
         
     | 
| 
       329 
375 
     | 
    
         
             
                    """
         
     | 
| 
       330 
     | 
    
         
            -
                     
     | 
| 
      
 376 
     | 
    
         
            +
                    return
         
     | 
| 
      
 377 
     | 
    
         
            +
                    yield
         
     | 
| 
       331 
378 
     | 
    
         | 
| 
       332 
379 
     | 
    
         
             
                @property
         
     | 
| 
       333 
380 
     | 
    
         
             
                def subject(self):
         
     | 
| 
         @@ -337,14 +384,13 @@ class LegacyMUC( 
     | 
|
| 
       337 
384 
     | 
    
         
             
                def subject(self, s: str):
         
     | 
| 
       338 
385 
     | 
    
         
             
                    if s == self._subject:
         
     | 
| 
       339 
386 
     | 
    
         
             
                        return
         
     | 
| 
       340 
     | 
    
         
            -
                    self. 
     | 
| 
       341 
     | 
    
         
            -
                        self. 
     | 
| 
       342 
     | 
    
         
            -
                    ).add_done_callback(
         
     | 
| 
       343 
     | 
    
         
            -
                        lambda task: task.result().set_room_subject(
         
     | 
| 
       344 
     | 
    
         
            -
                            s, None, self.subject_date, False
         
     | 
| 
       345 
     | 
    
         
            -
                        )
         
     | 
| 
      
 387 
     | 
    
         
            +
                    self.__get_subject_setter_participant().set_room_subject(
         
     | 
| 
      
 388 
     | 
    
         
            +
                        s, None, self.subject_date, False
         
     | 
| 
       346 
389 
     | 
    
         
             
                    )
         
     | 
| 
      
 390 
     | 
    
         
            +
             
     | 
| 
       347 
391 
     | 
    
         
             
                    self._subject = s
         
     | 
| 
      
 392 
     | 
    
         
            +
                    if self._updating_info:
         
     | 
| 
      
 393 
     | 
    
         
            +
                        return
         
     | 
| 
       348 
394 
     | 
    
         
             
                    assert self.pk is not None
         
     | 
| 
       349 
395 
     | 
    
         
             
                    self.__store.update_subject(self.pk, s)
         
     | 
| 
       350 
396 
     | 
    
         | 
| 
         @@ -353,25 +399,29 @@ class LegacyMUC( 
     | 
|
| 
       353 
399 
     | 
    
         
             
                    return self.type == MucType.CHANNEL
         
     | 
| 
       354 
400 
     | 
    
         | 
| 
       355 
401 
     | 
    
         
             
                @property
         
     | 
| 
       356 
     | 
    
         
            -
                def subject_setter(self):
         
     | 
| 
      
 402 
     | 
    
         
            +
                def subject_setter(self) -> Optional[str]:
         
     | 
| 
       357 
403 
     | 
    
         
             
                    return self._subject_setter
         
     | 
| 
       358 
404 
     | 
    
         | 
| 
       359 
405 
     | 
    
         
             
                @subject_setter.setter
         
     | 
| 
       360 
     | 
    
         
            -
                def subject_setter(self, subject_setter):
         
     | 
| 
       361 
     | 
    
         
            -
                     
     | 
| 
       362 
     | 
    
         
            -
             
     | 
| 
      
 406 
     | 
    
         
            +
                def subject_setter(self, subject_setter: SubjectSetterType) -> None:
         
     | 
| 
      
 407 
     | 
    
         
            +
                    if isinstance(subject_setter, LegacyContact):
         
     | 
| 
      
 408 
     | 
    
         
            +
                        subject_setter = subject_setter.name
         
     | 
| 
      
 409 
     | 
    
         
            +
                    elif isinstance(subject_setter, LegacyParticipant):
         
     | 
| 
      
 410 
     | 
    
         
            +
                        subject_setter = subject_setter.nickname
         
     | 
| 
       363 
411 
     | 
    
         | 
| 
       364 
     | 
    
         
            -
             
     | 
| 
       365 
     | 
    
         
            -
             
     | 
| 
      
 412 
     | 
    
         
            +
                    if subject_setter == self._subject_setter:
         
     | 
| 
      
 413 
     | 
    
         
            +
                        return
         
     | 
| 
      
 414 
     | 
    
         
            +
                    assert isinstance(subject_setter, str)
         
     | 
| 
      
 415 
     | 
    
         
            +
                    self._subject_setter = subject_setter
         
     | 
| 
      
 416 
     | 
    
         
            +
                    if self._updating_info:
         
     | 
| 
      
 417 
     | 
    
         
            +
                        return
         
     | 
| 
      
 418 
     | 
    
         
            +
                    assert self.pk is not None
         
     | 
| 
      
 419 
     | 
    
         
            +
                    self.__store.update_subject_setter(self.pk, subject_setter)
         
     | 
| 
       366 
420 
     | 
    
         | 
| 
       367 
     | 
    
         
            -
             
     | 
| 
       368 
     | 
    
         
            -
             
     | 
| 
       369 
     | 
    
         
            -
                    elif isinstance(who, str):
         
     | 
| 
       370 
     | 
    
         
            -
                        return await self.get_participant(who, store=False)
         
     | 
| 
       371 
     | 
    
         
            -
                    elif isinstance(self.subject_setter, LegacyContact):
         
     | 
| 
       372 
     | 
    
         
            -
                        return await self.get_participant_by_contact(who)
         
     | 
| 
       373 
     | 
    
         
            -
                    else:
         
     | 
| 
      
 421 
     | 
    
         
            +
                def __get_subject_setter_participant(self) -> LegacyParticipant:
         
     | 
| 
      
 422 
     | 
    
         
            +
                    if self._subject_setter is None:
         
     | 
| 
       374 
423 
     | 
    
         
             
                        return self.get_system_participant()
         
     | 
| 
      
 424 
     | 
    
         
            +
                    return self.Participant(self, self._subject_setter)
         
     | 
| 
       375 
425 
     | 
    
         | 
| 
       376 
426 
     | 
    
         
             
                def features(self):
         
     | 
| 
       377 
427 
     | 
    
         
             
                    features = [
         
     | 
| 
         @@ -410,7 +460,8 @@ class LegacyMUC( 
     | 
|
| 
       410 
460 
     | 
    
         
             
                    form.add_field("muc#roominfo_subjectmod", "boolean", value=False)
         
     | 
| 
       411 
461 
     | 
    
         | 
| 
       412 
462 
     | 
    
         
             
                    if self._ALL_INFO_FILLED_ON_STARTUP or self._participants_filled:
         
     | 
| 
       413 
     | 
    
         
            -
                         
     | 
| 
      
 463 
     | 
    
         
            +
                        assert self.pk is not None
         
     | 
| 
      
 464 
     | 
    
         
            +
                        n: Optional[int] = self.__participants_store.get_count(self.pk)
         
     | 
| 
       414 
465 
     | 
    
         
             
                    else:
         
     | 
| 
       415 
466 
     | 
    
         
             
                        n = self._n_participants
         
     | 
| 
       416 
467 
     | 
    
         
             
                    if n is not None:
         
     | 
| 
         @@ -504,12 +555,14 @@ class LegacyMUC( 
     | 
|
| 
       504 
555 
     | 
    
         
             
                        msg.send()
         
     | 
| 
       505 
556 
     | 
    
         | 
| 
       506 
557 
     | 
    
         
             
                def _get_cached_avatar_id(self):
         
     | 
| 
       507 
     | 
    
         
            -
                     
     | 
| 
      
 558 
     | 
    
         
            +
                    if self.pk is None:
         
     | 
| 
      
 559 
     | 
    
         
            +
                        return None
         
     | 
| 
       508 
560 
     | 
    
         
             
                    return self.xmpp.store.rooms.get_avatar_legacy_id(self.pk)
         
     | 
| 
       509 
561 
     | 
    
         | 
| 
       510 
562 
     | 
    
         
             
                def _post_avatar_update(self) -> None:
         
     | 
| 
       511 
     | 
    
         
            -
                    if self.pk is None 
     | 
| 
      
 563 
     | 
    
         
            +
                    if self.pk is None:
         
     | 
| 
       512 
564 
     | 
    
         
             
                        return
         
     | 
| 
      
 565 
     | 
    
         
            +
                    assert self.pk is not None
         
     | 
| 
       513 
566 
     | 
    
         
             
                    self.xmpp.store.rooms.set_avatar(self.pk, self._avatar_pk)
         
     | 
| 
       514 
567 
     | 
    
         
             
                    self.__send_configuration_change((104,))
         
     | 
| 
       515 
568 
     | 
    
         
             
                    self._send_room_presence()
         
     | 
| 
         @@ -527,6 +580,8 @@ class LegacyMUC( 
     | 
|
| 
       527 
580 
     | 
    
         
             
                            p["vcard_temp_update"]["photo"] = ""
         
     | 
| 
       528 
581 
     | 
    
         
             
                        p.send()
         
     | 
| 
       529 
582 
     | 
    
         | 
| 
      
 583 
     | 
    
         
            +
                @timeit
         
     | 
| 
      
 584 
     | 
    
         
            +
                @with_session
         
     | 
| 
       530 
585 
     | 
    
         
             
                async def join(self, join_presence: Presence):
         
     | 
| 
       531 
586 
     | 
    
         
             
                    user_full_jid = join_presence.get_from()
         
     | 
| 
       532 
587 
     | 
    
         
             
                    requested_nickname = join_presence.get_to().resource
         
     | 
| 
         @@ -548,17 +603,16 @@ class LegacyMUC( 
     | 
|
| 
       548 
603 
     | 
    
         
             
                        requested_nickname,
         
     | 
| 
       549 
604 
     | 
    
         
             
                    )
         
     | 
| 
       550 
605 
     | 
    
         | 
| 
       551 
     | 
    
         
            -
                     
     | 
| 
       552 
     | 
    
         
            -
             
     | 
| 
       553 
     | 
    
         
            -
                     
     | 
| 
       554 
     | 
    
         
            -
             
     | 
| 
       555 
     | 
    
         
            -
             
     | 
| 
       556 
     | 
    
         
            -
             
     | 
| 
       557 
     | 
    
         
            -
                        participant = self.Participant.from_store(self.session, db_participant)
         
     | 
| 
      
 606 
     | 
    
         
            +
                    user_nick = self.user_nick
         
     | 
| 
      
 607 
     | 
    
         
            +
                    user_participant = None
         
     | 
| 
      
 608 
     | 
    
         
            +
                    async for participant in self.get_participants():
         
     | 
| 
      
 609 
     | 
    
         
            +
                        if participant.is_user:
         
     | 
| 
      
 610 
     | 
    
         
            +
                            user_participant = participant
         
     | 
| 
      
 611 
     | 
    
         
            +
                            continue
         
     | 
| 
       558 
612 
     | 
    
         
             
                        participant.send_initial_presence(full_jid=user_full_jid)
         
     | 
| 
       559 
613 
     | 
    
         | 
| 
       560 
     | 
    
         
            -
                     
     | 
| 
       561 
     | 
    
         
            -
             
     | 
| 
      
 614 
     | 
    
         
            +
                    if user_participant is None:
         
     | 
| 
      
 615 
     | 
    
         
            +
                        user_participant = await self.get_user_participant()
         
     | 
| 
       562 
616 
     | 
    
         
             
                    if not user_participant.is_user:  # type:ignore
         
     | 
| 
       563 
617 
     | 
    
         
             
                        self.log.warning("is_user flag not set participant on user_participant")
         
     | 
| 
       564 
618 
     | 
    
         
             
                        user_participant.is_user = True  # type:ignore
         
     | 
| 
         @@ -589,7 +643,7 @@ class LegacyMUC( 
     | 
|
| 
       589 
643 
     | 
    
         
             
                            maxstanzas=maxstanzas,
         
     | 
| 
       590 
644 
     | 
    
         
             
                            since=since,
         
     | 
| 
       591 
645 
     | 
    
         
             
                        )
         
     | 
| 
       592 
     | 
    
         
            -
                     
     | 
| 
      
 646 
     | 
    
         
            +
                    self.__get_subject_setter_participant().set_room_subject(
         
     | 
| 
       593 
647 
     | 
    
         
             
                        self._subject if self.HAS_SUBJECT else (self.description or self.name),
         
     | 
| 
       594 
648 
     | 
    
         
             
                        user_full_jid,
         
     | 
| 
       595 
649 
     | 
    
         
             
                        self.subject_date,
         
     | 
| 
         @@ -643,20 +697,21 @@ class LegacyMUC( 
     | 
|
| 
       643 
697 
     | 
    
         
             
                        construction (optional)
         
     | 
| 
       644 
698 
     | 
    
         
             
                    :return:
         
     | 
| 
       645 
699 
     | 
    
         
             
                    """
         
     | 
| 
       646 
     | 
    
         
            -
                    if fill_first:
         
     | 
| 
       647 
     | 
    
         
            -
                         
     | 
| 
       648 
     | 
    
         
            -
             
     | 
| 
       649 
     | 
    
         
            -
                     
     | 
| 
       650 
     | 
    
         
            -
                         
     | 
| 
       651 
     | 
    
         
            -
                            self. 
     | 
| 
       652 
     | 
    
         
            -
             
     | 
| 
       653 
     | 
    
         
            -
             
     | 
| 
       654 
     | 
    
         
            -
                             
     | 
| 
      
 700 
     | 
    
         
            +
                    if fill_first and not self._participants_filled:
         
     | 
| 
      
 701 
     | 
    
         
            +
                        async for _ in self.get_participants():
         
     | 
| 
      
 702 
     | 
    
         
            +
                            pass
         
     | 
| 
      
 703 
     | 
    
         
            +
                    if self.pk is not None:
         
     | 
| 
      
 704 
     | 
    
         
            +
                        with self.xmpp.store.session():
         
     | 
| 
      
 705 
     | 
    
         
            +
                            stored = self.__participants_store.get_by_nickname(
         
     | 
| 
      
 706 
     | 
    
         
            +
                                self.pk, nickname
         
     | 
| 
      
 707 
     | 
    
         
            +
                            ) or self.__participants_store.get_by_resource(self.pk, nickname)
         
     | 
| 
      
 708 
     | 
    
         
            +
                            if stored is not None:
         
     | 
| 
      
 709 
     | 
    
         
            +
                                return self.Participant.from_store(self.session, stored)
         
     | 
| 
       655 
710 
     | 
    
         | 
| 
       656 
711 
     | 
    
         
             
                    if raise_if_not_found:
         
     | 
| 
       657 
712 
     | 
    
         
             
                        raise XMPPError("item-not-found")
         
     | 
| 
       658 
713 
     | 
    
         
             
                    p = self.Participant(self, nickname, **kwargs)
         
     | 
| 
       659 
     | 
    
         
            -
                    if store:
         
     | 
| 
      
 714 
     | 
    
         
            +
                    if store and not self._updating_info:
         
     | 
| 
       660 
715 
     | 
    
         
             
                        self.__store_participant(p)
         
     | 
| 
       661 
716 
     | 
    
         
             
                    if (
         
     | 
| 
       662 
717 
     | 
    
         
             
                        not self.get_lock("fill participants")
         
     | 
| 
         @@ -694,29 +749,43 @@ class LegacyMUC( 
     | 
|
| 
       694 
749 
     | 
    
         
             
                    :return:
         
     | 
| 
       695 
750 
     | 
    
         
             
                    """
         
     | 
| 
       696 
751 
     | 
    
         
             
                    await self.session.contacts.ready
         
     | 
| 
       697 
     | 
    
         
            -
             
     | 
| 
       698 
     | 
    
         
            -
                     
     | 
| 
       699 
     | 
    
         
            -
             
     | 
| 
       700 
     | 
    
         
            -
                         
     | 
| 
       701 
     | 
    
         
            -
             
     | 
| 
       702 
     | 
    
         
            -
                             
     | 
| 
       703 
     | 
    
         
            -
                                self. 
     | 
| 
       704 
     | 
    
         
            -
             
     | 
| 
      
 752 
     | 
    
         
            +
             
     | 
| 
      
 753 
     | 
    
         
            +
                    if self.pk is not None:
         
     | 
| 
      
 754 
     | 
    
         
            +
                        assert c.contact_pk is not None
         
     | 
| 
      
 755 
     | 
    
         
            +
                        with self.__store.session():
         
     | 
| 
      
 756 
     | 
    
         
            +
                            stored = self.__participants_store.get_by_contact(self.pk, c.contact_pk)
         
     | 
| 
      
 757 
     | 
    
         
            +
                            if stored is not None:
         
     | 
| 
      
 758 
     | 
    
         
            +
                                return self.Participant.from_store(
         
     | 
| 
      
 759 
     | 
    
         
            +
                                    self.session, stored, muc=self, contact=c
         
     | 
| 
      
 760 
     | 
    
         
            +
                                )
         
     | 
| 
       705 
761 
     | 
    
         | 
| 
       706 
762 
     | 
    
         
             
                    nickname = c.name or _unescape_node(c.jid_username)
         
     | 
| 
       707 
     | 
    
         
            -
             
     | 
| 
      
 763 
     | 
    
         
            +
             
     | 
| 
      
 764 
     | 
    
         
            +
                    if self.pk is None:
         
     | 
| 
      
 765 
     | 
    
         
            +
                        nick_available = True
         
     | 
| 
      
 766 
     | 
    
         
            +
                    else:
         
     | 
| 
      
 767 
     | 
    
         
            +
                        nick_available = self.__store.nickname_is_available(self.pk, nickname)
         
     | 
| 
      
 768 
     | 
    
         
            +
             
     | 
| 
      
 769 
     | 
    
         
            +
                    if not nick_available:
         
     | 
| 
       708 
770 
     | 
    
         
             
                        self.log.debug("Nickname conflict")
         
     | 
| 
       709 
771 
     | 
    
         
             
                        nickname = f"{nickname} ({c.jid_username})"
         
     | 
| 
       710 
772 
     | 
    
         
             
                    p = self.Participant(self, nickname, **kwargs)
         
     | 
| 
       711 
773 
     | 
    
         
             
                    p.contact = c
         
     | 
| 
       712 
774 
     | 
    
         | 
| 
      
 775 
     | 
    
         
            +
                    if self._updating_info:
         
     | 
| 
      
 776 
     | 
    
         
            +
                        return p
         
     | 
| 
      
 777 
     | 
    
         
            +
             
     | 
| 
      
 778 
     | 
    
         
            +
                    self.__store_participant(p)
         
     | 
| 
       713 
779 
     | 
    
         
             
                    # FIXME: this is not great but given the current design,
         
     | 
| 
       714 
780 
     | 
    
         
             
                    #        during participants fill and history backfill we do not
         
     | 
| 
       715 
     | 
    
         
            -
                    #        want to send presence, because we might update affiliation
         
     | 
| 
      
 781 
     | 
    
         
            +
                    #        want to send presence, because we might :update affiliation
         
     | 
| 
       716 
782 
     | 
    
         
             
                    #        and role afterwards.
         
     | 
| 
       717 
783 
     | 
    
         
             
                    # We need a refactor of the MUC class… later™
         
     | 
| 
       718 
     | 
    
         
            -
                     
     | 
| 
       719 
     | 
    
         
            -
             
     | 
| 
      
 784 
     | 
    
         
            +
                    if (
         
     | 
| 
      
 785 
     | 
    
         
            +
                        self._participants_filled
         
     | 
| 
      
 786 
     | 
    
         
            +
                        and not self.get_lock("fill participants")
         
     | 
| 
      
 787 
     | 
    
         
            +
                        and not self.get_lock("fill history")
         
     | 
| 
      
 788 
     | 
    
         
            +
                    ):
         
     | 
| 
       720 
789 
     | 
    
         
             
                        p.send_last_presence(force=True, no_cache_online=True)
         
     | 
| 
       721 
790 
     | 
    
         
             
                    return p
         
     | 
| 
       722 
791 
     | 
    
         | 
| 
         @@ -729,21 +798,6 @@ class LegacyMUC( 
     | 
|
| 
       729 
798 
     | 
    
         
             
                        return await self.get_user_participant(**kwargs)
         
     | 
| 
       730 
799 
     | 
    
         
             
                    return await self.get_participant_by_contact(c, **kwargs)
         
     | 
| 
       731 
800 
     | 
    
         | 
| 
       732 
     | 
    
         
            -
                async def get_participants(self, fill_first=True):
         
     | 
| 
       733 
     | 
    
         
            -
                    """
         
     | 
| 
       734 
     | 
    
         
            -
                    Get all known participants of the group, ensure :meth:`.LegacyMUC.fill_participants`
         
     | 
| 
       735 
     | 
    
         
            -
                    has been awaited once before. Plugins should not use that, internal
         
     | 
| 
       736 
     | 
    
         
            -
                    slidge use only.
         
     | 
| 
       737 
     | 
    
         
            -
                    :return:
         
     | 
| 
       738 
     | 
    
         
            -
                    """
         
     | 
| 
       739 
     | 
    
         
            -
                    if fill_first:
         
     | 
| 
       740 
     | 
    
         
            -
                        await self.__fill_participants()
         
     | 
| 
       741 
     | 
    
         
            -
                    assert self.pk is not None
         
     | 
| 
       742 
     | 
    
         
            -
                    return [
         
     | 
| 
       743 
     | 
    
         
            -
                        self.Participant.from_store(self.session, s)
         
     | 
| 
       744 
     | 
    
         
            -
                        for s in self.__participants_store.get_all(self.pk)
         
     | 
| 
       745 
     | 
    
         
            -
                    ]
         
     | 
| 
       746 
     | 
    
         
            -
             
     | 
| 
       747 
801 
     | 
    
         
             
                def remove_participant(self, p: "LegacyParticipantType", kick=False, ban=False):
         
     | 
| 
       748 
802 
     | 
    
         
             
                    """
         
     | 
| 
       749 
803 
     | 
    
         
             
                    Call this when a participant leaves the room
         
     | 
| 
         @@ -1029,10 +1083,9 @@ class LegacyMUC( 
     | 
|
| 
       1029 
1083 
     | 
    
         
             
                ):
         
     | 
| 
       1030 
1084 
     | 
    
         
             
                    """
         
     | 
| 
       1031 
1085 
     | 
    
         
             
                    Triggered when the user requests changing the affiliation of a contact
         
     | 
| 
       1032 
     | 
    
         
            -
                    for this group 
     | 
| 
      
 1086 
     | 
    
         
            +
                    for this group.
         
     | 
| 
       1033 
1087 
     | 
    
         | 
| 
       1034 
     | 
    
         
            -
                    Examples: promotion them to moderator,  
     | 
| 
       1035 
     | 
    
         
            -
                    ban (affiliation=outcast).
         
     | 
| 
      
 1088 
     | 
    
         
            +
                    Examples: promotion them to moderator, ban (affiliation=outcast).
         
     | 
| 
       1036 
1089 
     | 
    
         | 
| 
       1037 
1090 
     | 
    
         
             
                    :param contact: The contact whose affiliation change is requested
         
     | 
| 
       1038 
1091 
     | 
    
         
             
                    :param affiliation: The new affiliation
         
     | 
| 
         @@ -1041,6 +1094,16 @@ class LegacyMUC( 
     | 
|
| 
       1041 
1094 
     | 
    
         
             
                    """
         
     | 
| 
       1042 
1095 
     | 
    
         
             
                    raise NotImplementedError
         
     | 
| 
       1043 
1096 
     | 
    
         | 
| 
      
 1097 
     | 
    
         
            +
                async def on_kick(self, contact: "LegacyContact", reason: Optional[str]):
         
     | 
| 
      
 1098 
     | 
    
         
            +
                    """
         
     | 
| 
      
 1099 
     | 
    
         
            +
                    Triggered when the user requests changing the role of a contact
         
     | 
| 
      
 1100 
     | 
    
         
            +
                    to "none" for this group. Action commonly known as "kick".
         
     | 
| 
      
 1101 
     | 
    
         
            +
             
     | 
| 
      
 1102 
     | 
    
         
            +
                    :param contact: Contact to be kicked
         
     | 
| 
      
 1103 
     | 
    
         
            +
                    :param reason: A reason for this kick
         
     | 
| 
      
 1104 
     | 
    
         
            +
                    """
         
     | 
| 
      
 1105 
     | 
    
         
            +
                    raise NotImplementedError
         
     | 
| 
      
 1106 
     | 
    
         
            +
             
     | 
| 
       1044 
1107 
     | 
    
         
             
                async def on_set_config(
         
     | 
| 
       1045 
1108 
     | 
    
         
             
                    self,
         
     | 
| 
       1046 
1109 
     | 
    
         
             
                    name: Optional[str],
         
     | 
| 
         @@ -1127,6 +1190,7 @@ class LegacyMUC( 
     | 
|
| 
       1127 
1190 
     | 
    
         
             
                    )
         
     | 
| 
       1128 
1191 
     | 
    
         
             
                    muc.pk = stored.id
         
     | 
| 
       1129 
1192 
     | 
    
         
             
                    muc.type = stored.muc_type  # type: ignore
         
     | 
| 
      
 1193 
     | 
    
         
            +
                    muc.user_nick = stored.user_nick
         
     | 
| 
       1130 
1194 
     | 
    
         
             
                    if stored.name:
         
     | 
| 
       1131 
1195 
     | 
    
         
             
                        muc.DISCO_NAME = stored.name
         
     | 
| 
       1132 
1196 
     | 
    
         
             
                    if stored.description:
         
     | 
| 
         @@ -1137,17 +1201,11 @@ class LegacyMUC( 
     | 
|
| 
       1137 
1201 
     | 
    
         
             
                    if stored.subject_date is not None:
         
     | 
| 
       1138 
1202 
     | 
    
         
             
                        muc._subject_date = stored.subject_date.replace(tzinfo=timezone.utc)
         
     | 
| 
       1139 
1203 
     | 
    
         
             
                    muc._participants_filled = stored.participants_filled
         
     | 
| 
       1140 
     | 
    
         
            -
                    muc. 
     | 
| 
      
 1204 
     | 
    
         
            +
                    muc._n_participants = stored.n_participants
         
     | 
| 
      
 1205 
     | 
    
         
            +
                    muc._history_filled = stored.history_filled
         
     | 
| 
       1141 
1206 
     | 
    
         
             
                    if stored.user_resources is not None:
         
     | 
| 
       1142 
1207 
     | 
    
         
             
                        muc._user_resources = set(json.loads(stored.user_resources))
         
     | 
| 
       1143 
     | 
    
         
            -
                     
     | 
| 
       1144 
     | 
    
         
            -
                        muc.subject_setter = (
         
     | 
| 
       1145 
     | 
    
         
            -
                            LegacyParticipant.get_self_or_unique_subclass().from_store(
         
     | 
| 
       1146 
     | 
    
         
            -
                                session,
         
     | 
| 
       1147 
     | 
    
         
            -
                                stored.subject_setter,
         
     | 
| 
       1148 
     | 
    
         
            -
                                muc=muc,
         
     | 
| 
       1149 
     | 
    
         
            -
                            )
         
     | 
| 
       1150 
     | 
    
         
            -
                        )
         
     | 
| 
      
 1208 
     | 
    
         
            +
                    muc._subject_setter = stored.subject_setter
         
     | 
| 
       1151 
1209 
     | 
    
         
             
                    muc.archive = MessageArchive(muc.pk, session.xmpp.store.mam)
         
     | 
| 
       1152 
1210 
     | 
    
         
             
                    muc._set_avatar_from_store(stored)
         
     | 
| 
       1153 
1211 
     | 
    
         
             
                    return muc
         
     | 
    
        slidge/main.py
    CHANGED
    
    | 
         @@ -17,6 +17,7 @@ import asyncio 
     | 
|
| 
       17 
17 
     | 
    
         
             
            import importlib
         
     | 
| 
       18 
18 
     | 
    
         
             
            import logging
         
     | 
| 
       19 
19 
     | 
    
         
             
            import os
         
     | 
| 
      
 20 
     | 
    
         
            +
            import re
         
     | 
| 
       20 
21 
     | 
    
         
             
            import signal
         
     | 
| 
       21 
22 
     | 
    
         
             
            from pathlib import Path
         
     | 
| 
       22 
23 
     | 
    
         | 
| 
         @@ -49,7 +50,7 @@ class MainConfig(ConfigModule): 
     | 
|
| 
       49 
50 
     | 
    
         
             
                        args.home_dir = Path("/var/lib/slidge") / str(args.jid)
         
     | 
| 
       50 
51 
     | 
    
         | 
| 
       51 
52 
     | 
    
         
             
                    if args.user_jid_validator is None:
         
     | 
| 
       52 
     | 
    
         
            -
                        args.user_jid_validator = ".*@" + args.server
         
     | 
| 
      
 53 
     | 
    
         
            +
                        args.user_jid_validator = ".*@" + re.escape(args.server)
         
     | 
| 
       53 
54 
     | 
    
         | 
| 
       54 
55 
     | 
    
         
             
                    if args.db_url is None:
         
     | 
| 
       55 
56 
     | 
    
         
             
                        args.db_url = f"sqlite:///{args.home_dir}/slidge.sqlite"
         
     | 
| 
         @@ -116,8 +117,6 @@ def configure(): 
     | 
|
| 
       116 
117 
     | 
    
         
             
                db_file = config.HOME_DIR / "slidge.db"
         
     | 
| 
       117 
118 
     | 
    
         
             
                user_store.set_file(db_file, args.secret_key)
         
     | 
| 
       118 
119 
     | 
    
         | 
| 
       119 
     | 
    
         
            -
                avatar_cache.set_dir(h / "slidge_avatars_v3")
         
     | 
| 
       120 
     | 
    
         
            -
             
     | 
| 
       121 
120 
     | 
    
         
             
                config.UPLOAD_REQUESTER = config.UPLOAD_REQUESTER or config.JID.bare
         
     | 
| 
       122 
121 
     | 
    
         | 
| 
       123 
122 
     | 
    
         
             
                return unknown_argv
         
     | 
| 
         @@ -161,6 +160,7 @@ def main(): 
     | 
|
| 
       161 
160 
     | 
    
         
             
                gateway: BaseGateway = BaseGateway.get_unique_subclass()()
         
     | 
| 
       162 
161 
     | 
    
         
             
                avatar_cache.http = gateway.http
         
     | 
| 
       163 
162 
     | 
    
         
             
                avatar_cache.store = gateway.store.avatars
         
     | 
| 
      
 163 
     | 
    
         
            +
                avatar_cache.set_dir(config.HOME_DIR / "slidge_avatars_v3")
         
     | 
| 
       164 
164 
     | 
    
         
             
                avatar_cache.legacy_avatar_type = gateway.AVATAR_ID_TYPE
         
     | 
| 
       165 
165 
     | 
    
         | 
| 
       166 
166 
     | 
    
         
             
                PepAvatar.store = gateway.store
         
     | 
| 
         @@ -57,6 +57,8 @@ class VCard4Provider(BasePlugin): 
     | 
|
| 
       57 
57 
     | 
    
         
             
                    if not hasattr(self.xmpp, "get_session_from_jid"):
         
     | 
| 
       58 
58 
     | 
    
         
             
                        return None
         
     | 
| 
       59 
59 
     | 
    
         
             
                    jid = JID(jid)
         
     | 
| 
      
 60 
     | 
    
         
            +
                    if not jid.local:
         
     | 
| 
      
 61 
     | 
    
         
            +
                        return None
         
     | 
| 
       60 
62 
     | 
    
         
             
                    requested_by = JID(requested_by)
         
     | 
| 
       61 
63 
     | 
    
         
             
                    session = self.xmpp.get_session_from_jid(requested_by)
         
     | 
| 
       62 
64 
     | 
    
         
             
                    if session is None:
         
     | 
    
        slidge/util/archive_msg.py
    CHANGED
    
    | 
         @@ -1,6 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            from copy import copy
         
     | 
| 
       2 
2 
     | 
    
         
             
            from datetime import datetime, timezone
         
     | 
| 
       3 
3 
     | 
    
         
             
            from typing import Optional, Union
         
     | 
| 
      
 4 
     | 
    
         
            +
            from uuid import uuid4
         
     | 
| 
       4 
5 
     | 
    
         
             
            from xml.etree import ElementTree as ET
         
     | 
| 
       5 
6 
     | 
    
         | 
| 
       6 
7 
     | 
    
         
             
            from slixmpp import Message
         
     | 
| 
         @@ -30,7 +31,7 @@ class HistoryMessage: 
     | 
|
| 
       30 
31 
     | 
    
         
             
                    else:
         
     | 
| 
       31 
32 
     | 
    
         
             
                        from_db = False
         
     | 
| 
       32 
33 
     | 
    
         | 
| 
       33 
     | 
    
         
            -
                    self.id = stanza["stanza_id"]["id"]
         
     | 
| 
      
 34 
     | 
    
         
            +
                    self.id = stanza["stanza_id"]["id"] or uuid4().hex
         
     | 
| 
       34 
35 
     | 
    
         
             
                    self.when: datetime = (
         
     | 
| 
       35 
36 
     | 
    
         
             
                        when or stanza["delay"]["stamp"] or datetime.now(tz=timezone.utc)
         
     | 
| 
       36 
37 
     | 
    
         
             
                    )
         
     |