slidge 0.1.3__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/__init__.py +3 -5
- slidge/__main__.py +2 -196
- slidge/__version__.py +5 -0
- slidge/command/adhoc.py +8 -1
- slidge/command/admin.py +6 -7
- slidge/command/base.py +1 -2
- slidge/command/register.py +32 -16
- slidge/command/user.py +85 -6
- slidge/contact/contact.py +165 -49
- slidge/contact/roster.py +122 -47
- slidge/core/config.py +14 -11
- slidge/core/gateway/base.py +148 -36
- slidge/core/gateway/caps.py +7 -5
- slidge/core/gateway/disco.py +2 -4
- slidge/core/gateway/mam.py +1 -4
- slidge/core/gateway/muc_admin.py +1 -1
- slidge/core/gateway/ping.py +2 -3
- slidge/core/gateway/presence.py +1 -1
- slidge/core/gateway/registration.py +32 -21
- slidge/core/gateway/search.py +3 -5
- slidge/core/gateway/session_dispatcher.py +120 -57
- slidge/core/gateway/vcard_temp.py +7 -5
- slidge/core/mixins/__init__.py +11 -1
- slidge/core/mixins/attachment.py +32 -14
- slidge/core/mixins/avatar.py +90 -25
- slidge/core/mixins/base.py +8 -2
- slidge/core/mixins/db.py +18 -0
- slidge/core/mixins/disco.py +0 -10
- slidge/core/mixins/message.py +18 -8
- slidge/core/mixins/message_maker.py +17 -9
- slidge/core/mixins/presence.py +17 -4
- slidge/core/pubsub.py +54 -220
- slidge/core/session.py +69 -34
- slidge/db/__init__.py +4 -0
- slidge/db/alembic/env.py +64 -0
- slidge/db/alembic/script.py.mako +26 -0
- slidge/db/alembic/versions/09f27f098baa_add_missing_attributes_in_room.py +36 -0
- slidge/db/alembic/versions/2461390c0af2_store_contacts_caps_verstring_in_db.py +36 -0
- slidge/db/alembic/versions/29f5280c61aa_store_subject_setter_in_room.py +37 -0
- slidge/db/alembic/versions/2b1f45ab7379_store_room_subject_setter_by_nickname.py +41 -0
- slidge/db/alembic/versions/82a4af84b679_add_muc_history_filled.py +48 -0
- slidge/db/alembic/versions/8d2ced764698_rely_on_db_to_store_contacts_rooms_and_.py +133 -0
- slidge/db/alembic/versions/aa9d82a7f6ef_db_creation.py +85 -0
- slidge/db/alembic/versions/b33993e87db3_move_everything_to_persistent_db.py +214 -0
- slidge/db/alembic/versions/b64b1a793483_add_source_and_legacy_id_for_archived_.py +48 -0
- slidge/db/alembic/versions/c4a8ec35a0e8_per_room_user_nick.py +34 -0
- slidge/db/alembic/versions/e91195719c2c_store_users_avatars_persistently.py +26 -0
- slidge/db/avatar.py +235 -0
- slidge/db/meta.py +65 -0
- slidge/db/models.py +375 -0
- slidge/db/store.py +1078 -0
- slidge/group/archive.py +58 -14
- slidge/group/bookmarks.py +72 -57
- slidge/group/participant.py +87 -28
- slidge/group/room.py +369 -211
- slidge/main.py +201 -0
- slidge/migration.py +30 -0
- slidge/slixfix/__init__.py +35 -2
- slidge/slixfix/roster.py +11 -4
- slidge/slixfix/xep_0292/vcard4.py +3 -0
- slidge/util/archive_msg.py +2 -1
- slidge/util/db.py +1 -47
- slidge/util/test.py +71 -4
- slidge/util/types.py +29 -4
- slidge/util/util.py +22 -0
- {slidge-0.1.3.dist-info → slidge-0.2.0a1.dist-info}/METADATA +4 -4
- slidge-0.2.0a1.dist-info/RECORD +114 -0
- slidge/core/cache.py +0 -183
- slidge/util/schema.sql +0 -126
- slidge/util/sql.py +0 -508
- slidge-0.1.3.dist-info/RECORD +0 -96
- {slidge-0.1.3.dist-info → slidge-0.2.0a1.dist-info}/LICENSE +0 -0
- {slidge-0.1.3.dist-info → slidge-0.2.0a1.dist-info}/WHEEL +0 -0
- {slidge-0.1.3.dist-info → slidge-0.2.0a1.dist-info}/entry_points.txt +0 -0
| @@ -9,7 +9,6 @@ from slixmpp.plugins.xep_0084.stanza import Info | |
| 9 9 |  | 
| 10 10 | 
             
            from ... import LegacyContact
         | 
| 11 11 | 
             
            from ...group.room import LegacyMUC
         | 
| 12 | 
            -
            from ...util.sql import db
         | 
| 13 12 | 
             
            from ...util.types import LinkPreview, Recipient, RecipientType
         | 
| 14 13 | 
             
            from ...util.util import (
         | 
| 15 14 | 
             
                dict_to_named_tuple,
         | 
| @@ -62,6 +61,13 @@ class SessionDispatcher: | |
| 62 61 | 
             
                            _exceptions_to_xmpp_errors(self.on_muc_owner_set),  # type: ignore
         | 
| 63 62 | 
             
                        )
         | 
| 64 63 | 
             
                    )
         | 
| 64 | 
            +
                    xmpp.register_handler(
         | 
| 65 | 
            +
                        CoroutineCallback(
         | 
| 66 | 
            +
                            "ibr_remove",
         | 
| 67 | 
            +
                            StanzaPath("/iq/register"),
         | 
| 68 | 
            +
                            _exceptions_to_xmpp_errors(self.on_ibr_remove),  # type: ignore
         | 
| 69 | 
            +
                        )
         | 
| 70 | 
            +
                    )
         | 
| 65 71 |  | 
| 66 72 | 
             
                    for event in (
         | 
| 67 73 | 
             
                        "legacy_message",
         | 
| @@ -157,7 +163,7 @@ class SessionDispatcher: | |
| 157 163 | 
             
                    reply_fallback = None
         | 
| 158 164 | 
             
                    if msg.get_plugin("reply", check=True):
         | 
| 159 165 | 
             
                        try:
         | 
| 160 | 
            -
                            reply_to_msg_xmpp_id = _xmpp_msg_id_to_legacy(
         | 
| 166 | 
            +
                            reply_to_msg_xmpp_id = self._xmpp_msg_id_to_legacy(
         | 
| 161 167 | 
             
                                session, msg["reply"]["id"]
         | 
| 162 168 | 
             
                            )
         | 
| 163 169 | 
             
                        except XMPPError:
         | 
| @@ -170,7 +176,7 @@ class SessionDispatcher: | |
| 170 176 | 
             
                        else:
         | 
| 171 177 | 
             
                            reply_to_jid = JID(msg["reply"]["to"])
         | 
| 172 178 | 
             
                            if msg["type"] == "chat":
         | 
| 173 | 
            -
                                if reply_to_jid.bare != session. | 
| 179 | 
            +
                                if reply_to_jid.bare != session.user_jid.bare:
         | 
| 174 180 | 
             
                                    try:
         | 
| 175 181 | 
             
                                        reply_to = await session.contacts.by_jid(reply_to_jid)
         | 
| 176 182 | 
             
                                    except XMPPError:
         | 
| @@ -242,13 +248,17 @@ class SessionDispatcher: | |
| 242 248 | 
             
                    if isinstance(e, LegacyMUC):
         | 
| 243 249 | 
             
                        await e.echo(msg, legacy_msg_id)
         | 
| 244 250 | 
             
                        if legacy_msg_id is not None:
         | 
| 245 | 
            -
                             | 
| 251 | 
            +
                            self.xmpp.store.sent.set_group_message(
         | 
| 252 | 
            +
                                session.user_pk, legacy_msg_id, msg.get_id()
         | 
| 253 | 
            +
                            )
         | 
| 246 254 | 
             
                    else:
         | 
| 247 255 | 
             
                        self.__ack(msg)
         | 
| 248 256 | 
             
                        if legacy_msg_id is not None:
         | 
| 249 | 
            -
                             | 
| 257 | 
            +
                            self.xmpp.store.sent.set_message(
         | 
| 258 | 
            +
                                session.user_pk, legacy_msg_id, msg.get_id()
         | 
| 259 | 
            +
                            )
         | 
| 250 260 | 
             
                            if session.MESSAGE_IDS_ARE_THREAD_IDS and (t := msg["thread"]):
         | 
| 251 | 
            -
                                session. | 
| 261 | 
            +
                                self.xmpp.store.sent.set_thread(session.user_pk, t, legacy_msg_id)
         | 
| 252 262 |  | 
| 253 263 | 
             
                async def on_groupchat_message(self, msg: Message):
         | 
| 254 264 | 
             
                    await self.on_legacy_message(msg)
         | 
| @@ -257,9 +267,11 @@ class SessionDispatcher: | |
| 257 267 | 
             
                    session, entity, thread = await self.__get_session_entity_thread(msg)
         | 
| 258 268 | 
             
                    xmpp_id = msg["replace"]["id"]
         | 
| 259 269 | 
             
                    if isinstance(entity, LegacyMUC):
         | 
| 260 | 
            -
                        legacy_id =  | 
| 270 | 
            +
                        legacy_id = self.xmpp.store.sent.get_group_legacy_id(
         | 
| 271 | 
            +
                            session.user_pk, xmpp_id
         | 
| 272 | 
            +
                        )
         | 
| 261 273 | 
             
                    else:
         | 
| 262 | 
            -
                        legacy_id = _xmpp_msg_id_to_legacy(session, xmpp_id)
         | 
| 274 | 
            +
                        legacy_id = self._xmpp_msg_id_to_legacy(session, xmpp_id)
         | 
| 263 275 |  | 
| 264 276 | 
             
                    if isinstance(entity, LegacyMUC):
         | 
| 265 277 | 
             
                        mentions = await entity.parse_mentions(msg["body"])
         | 
| @@ -323,12 +335,16 @@ class SessionDispatcher: | |
| 323 335 |  | 
| 324 336 | 
             
                    if isinstance(entity, LegacyMUC):
         | 
| 325 337 | 
             
                        if new_legacy_msg_id is not None:
         | 
| 326 | 
            -
                             | 
| 338 | 
            +
                            self.xmpp.store.sent.set_group_message(
         | 
| 339 | 
            +
                                session.user_pk, new_legacy_msg_id, msg.get_id()
         | 
| 340 | 
            +
                            )
         | 
| 327 341 | 
             
                        await entity.echo(msg, new_legacy_msg_id)
         | 
| 328 342 | 
             
                    else:
         | 
| 329 343 | 
             
                        self.__ack(msg)
         | 
| 330 344 | 
             
                        if new_legacy_msg_id is not None:
         | 
| 331 | 
            -
                             | 
| 345 | 
            +
                            self.xmpp.store.sent.set_message(
         | 
| 346 | 
            +
                                session.user_pk, new_legacy_msg_id, msg.get_id()
         | 
| 347 | 
            +
                            )
         | 
| 332 348 |  | 
| 333 349 | 
             
                async def on_message_retract(self, msg: Message):
         | 
| 334 350 | 
             
                    session, entity, thread = await self.__get_session_entity_thread(msg)
         | 
| @@ -338,7 +354,7 @@ class SessionDispatcher: | |
| 338 354 | 
             
                            "This legacy service does not support message retraction.",
         | 
| 339 355 | 
             
                        )
         | 
| 340 356 | 
             
                    xmpp_id: str = msg["retract"]["id"]
         | 
| 341 | 
            -
                    legacy_id = _xmpp_msg_id_to_legacy(session, xmpp_id)
         | 
| 357 | 
            +
                    legacy_id = self._xmpp_msg_id_to_legacy(session, xmpp_id)
         | 
| 342 358 | 
             
                    if legacy_id:
         | 
| 343 359 | 
             
                        await session.on_retract(entity, legacy_id, thread=thread)
         | 
| 344 360 | 
             
                        if isinstance(entity, LegacyMUC):
         | 
| @@ -362,7 +378,7 @@ class SessionDispatcher: | |
| 362 378 | 
             
                        to_mark = [displayed_msg_id]
         | 
| 363 379 | 
             
                    for xmpp_id in to_mark:
         | 
| 364 380 | 
             
                        await session.on_displayed(
         | 
| 365 | 
            -
                            e, _xmpp_msg_id_to_legacy(session, xmpp_id), legacy_thread
         | 
| 381 | 
            +
                            e, self._xmpp_msg_id_to_legacy(session, xmpp_id), legacy_thread
         | 
| 366 382 | 
             
                        )
         | 
| 367 383 | 
             
                        if isinstance(e, LegacyMUC):
         | 
| 368 384 | 
             
                            await e.echo(msg, None)
         | 
| @@ -397,7 +413,7 @@ class SessionDispatcher: | |
| 397 413 | 
             
                    if special_msg:
         | 
| 398 414 | 
             
                        legacy_id = react_to
         | 
| 399 415 | 
             
                    else:
         | 
| 400 | 
            -
                        legacy_id = _xmpp_msg_id_to_legacy(session, react_to)
         | 
| 416 | 
            +
                        legacy_id = self._xmpp_msg_id_to_legacy(session, react_to)
         | 
| 401 417 |  | 
| 402 418 | 
             
                    if not legacy_id:
         | 
| 403 419 | 
             
                        log.debug("Ignored reaction from user")
         | 
| @@ -434,9 +450,10 @@ class SessionDispatcher: | |
| 434 450 | 
             
                    else:
         | 
| 435 451 | 
             
                        self.__ack(msg)
         | 
| 436 452 |  | 
| 437 | 
            -
                    multi =  | 
| 453 | 
            +
                    multi = self.xmpp.store.multi.get_xmpp_ids(session.user_pk, react_to)
         | 
| 438 454 | 
             
                    if not multi:
         | 
| 439 455 | 
             
                        return
         | 
| 456 | 
            +
                    multi = [m for m in multi if react_to != m]
         | 
| 440 457 |  | 
| 441 458 | 
             
                    if isinstance(entity, LegacyMUC):
         | 
| 442 459 | 
             
                        for xmpp_id in multi:
         | 
| @@ -461,14 +478,17 @@ class SessionDispatcher: | |
| 461 478 |  | 
| 462 479 | 
             
                    pto = p.get_to()
         | 
| 463 480 | 
             
                    if pto == self.xmpp.boundjid.bare:
         | 
| 464 | 
            -
                         | 
| 465 | 
            -
                        #     a presence show if available. Weird, weird, weird slix.
         | 
| 481 | 
            +
                        session.log.debug("Received a presence from %s", p.get_from())
         | 
| 466 482 | 
             
                        if (ptype := p.get_type()) not in _USEFUL_PRESENCES:
         | 
| 467 483 | 
             
                            return
         | 
| 484 | 
            +
                        if not session.user.preferences.get("sync_presence", False):
         | 
| 485 | 
            +
                            session.log.debug("User does not want to sync their presence")
         | 
| 486 | 
            +
                            return
         | 
| 487 | 
            +
                        # NB: get_type() returns either a proper presence type or
         | 
| 488 | 
            +
                        #     a presence show if available. Weird, weird, weird slix.
         | 
| 468 489 | 
             
                        resources = self.xmpp.roster[self.xmpp.boundjid.bare][
         | 
| 469 490 | 
             
                            p.get_from()
         | 
| 470 491 | 
             
                        ].resources
         | 
| 471 | 
            -
                        session.log.debug("Received a presence from %s", p.get_from())
         | 
| 472 492 | 
             
                        await session.on_presence(
         | 
| 473 493 | 
             
                            p.get_from().resource,
         | 
| 474 494 | 
             
                            ptype,  # type: ignore
         | 
| @@ -478,8 +498,12 @@ class SessionDispatcher: | |
| 478 498 | 
             
                        )
         | 
| 479 499 | 
             
                        return
         | 
| 480 500 |  | 
| 481 | 
            -
                    muc = session.bookmarks. | 
| 482 | 
            -
             | 
| 501 | 
            +
                    muc = session.bookmarks.by_jid_only_if_exists(JID(pto.bare))
         | 
| 502 | 
            +
             | 
| 503 | 
            +
                    if muc is not None and p.get_type() == "unavailable":
         | 
| 504 | 
            +
                        return muc.on_presence_unavailable(p)
         | 
| 505 | 
            +
             | 
| 506 | 
            +
                    if muc is None or p.get_from().resource not in muc.get_user_resources():
         | 
| 483 507 | 
             
                        return
         | 
| 484 508 |  | 
| 485 509 | 
             
                    if pto.resource == muc.user_nick:
         | 
| @@ -530,29 +554,36 @@ class SessionDispatcher: | |
| 530 554 | 
             
                        return
         | 
| 531 555 |  | 
| 532 556 | 
             
                    stanza_id = msg["pubsub_event"]["items"]["item"]["displayed"]["stanza_id"]["id"]
         | 
| 533 | 
            -
                    await session.on_displayed( | 
| 557 | 
            +
                    await session.on_displayed(
         | 
| 558 | 
            +
                        chat, self._xmpp_msg_id_to_legacy(session, stanza_id)
         | 
| 559 | 
            +
                    )
         | 
| 534 560 |  | 
| 535 561 | 
             
                async def on_avatar_metadata_publish(self, m: Message):
         | 
| 536 | 
            -
                    if not config.SYNC_AVATAR:
         | 
| 537 | 
            -
                        return
         | 
| 538 | 
            -
             | 
| 539 562 | 
             
                    session = await self.__get_session(m, timeout=None)
         | 
| 563 | 
            +
                    if not session.user.preferences.get("sync_avatar", False):
         | 
| 564 | 
            +
                        session.log.debug("User does not want to sync their avatar")
         | 
| 565 | 
            +
                        return
         | 
| 540 566 | 
             
                    info = m["pubsub_event"]["items"]["item"]["avatar_metadata"]["info"]
         | 
| 541 567 |  | 
| 542 568 | 
             
                    await self.on_avatar_metadata_info(session, info)
         | 
| 543 569 |  | 
| 544 570 | 
             
                async def on_avatar_metadata_info(self, session: BaseSession, info: Info):
         | 
| 545 | 
            -
                    session.log.debug("Avatar metadata info: %s", info)
         | 
| 546 571 | 
             
                    hash_ = info["id"]
         | 
| 547 572 |  | 
| 548 | 
            -
                    if session.avatar_hash == hash_:
         | 
| 573 | 
            +
                    if session.user.avatar_hash == hash_:
         | 
| 574 | 
            +
                        session.log.debug("We already know this avatar hash")
         | 
| 549 575 | 
             
                        return
         | 
| 550 | 
            -
                    session | 
| 576 | 
            +
                    with self.xmpp.store.session() as orm_session:
         | 
| 577 | 
            +
                        user = self.xmpp.store.users.get(session.user_jid)
         | 
| 578 | 
            +
                        assert user is not None
         | 
| 579 | 
            +
                        user.avatar_hash = hash_
         | 
| 580 | 
            +
                        orm_session.add(user)
         | 
| 581 | 
            +
                        orm_session.commit()
         | 
| 551 582 |  | 
| 552 583 | 
             
                    if hash_:
         | 
| 553 584 | 
             
                        try:
         | 
| 554 585 | 
             
                            iq = await self.xmpp.plugin["xep_0084"].retrieve_avatar(
         | 
| 555 | 
            -
                                session. | 
| 586 | 
            +
                                session.user_jid, hash_, ifrom=self.xmpp.boundjid.bare
         | 
| 556 587 | 
             
                            )
         | 
| 557 588 | 
             
                        except IqError as e:
         | 
| 558 589 | 
             
                            session.log.warning("Could not fetch the user's avatar: %s", e)
         | 
| @@ -593,7 +624,7 @@ class SessionDispatcher: | |
| 593 624 | 
             
                            "Slidge only implements moderation/retraction",
         | 
| 594 625 | 
             
                        )
         | 
| 595 626 |  | 
| 596 | 
            -
                    legacy_id = _xmpp_msg_id_to_legacy(session, xmpp_id)
         | 
| 627 | 
            +
                    legacy_id = self._xmpp_msg_id_to_legacy(session, xmpp_id)
         | 
| 597 628 | 
             
                    await session.on_moderate(muc, legacy_id, moderate["reason"] or None)
         | 
| 598 629 | 
             
                    iq.reply(clear=True).send()
         | 
| 599 630 |  | 
| @@ -601,14 +632,28 @@ class SessionDispatcher: | |
| 601 632 | 
             
                    session = await self.__get_session(iq)
         | 
| 602 633 | 
             
                    session.raise_if_not_logged()
         | 
| 603 634 |  | 
| 604 | 
            -
                    item = iq["mucadmin_query"]["item"]
         | 
| 605 | 
            -
                    contact = await session.contacts.by_jid(JID(item["jid"]))
         | 
| 606 | 
            -
             | 
| 607 635 | 
             
                    muc = await session.bookmarks.by_jid(iq.get_to())
         | 
| 608 636 |  | 
| 609 | 
            -
                     | 
| 610 | 
            -
             | 
| 611 | 
            -
             | 
| 637 | 
            +
                    item = iq["mucadmin_query"]["item"]
         | 
| 638 | 
            +
                    if item["jid"]:
         | 
| 639 | 
            +
                        contact = await session.contacts.by_jid(JID(item["jid"]))
         | 
| 640 | 
            +
                    else:
         | 
| 641 | 
            +
                        part = await muc.get_participant(
         | 
| 642 | 
            +
                            item["nick"], fill_first=True, raise_if_not_found=True
         | 
| 643 | 
            +
                        )
         | 
| 644 | 
            +
                        assert part.contact is not None
         | 
| 645 | 
            +
                        contact = part.contact
         | 
| 646 | 
            +
             | 
| 647 | 
            +
                    if item["affiliation"]:
         | 
| 648 | 
            +
                        await muc.on_set_affiliation(
         | 
| 649 | 
            +
                            contact,
         | 
| 650 | 
            +
                            item["affiliation"],
         | 
| 651 | 
            +
                            item["reason"] or None,
         | 
| 652 | 
            +
                            item["nick"] or None,
         | 
| 653 | 
            +
                        )
         | 
| 654 | 
            +
                    elif item["role"] == "none":
         | 
| 655 | 
            +
                        await muc.on_kick(contact, item["reason"] or None)
         | 
| 656 | 
            +
             | 
| 612 657 | 
             
                    iq.reply(clear=True).send()
         | 
| 613 658 |  | 
| 614 659 | 
             
                async def on_groupchat_direct_invite(self, msg: Message):
         | 
| @@ -718,25 +763,39 @@ class SessionDispatcher: | |
| 718 763 | 
             
                        )
         | 
| 719 764 | 
             
                    await muc.on_set_subject(msg["subject"])
         | 
| 720 765 |  | 
| 766 | 
            +
                async def on_ibr_remove(self, iq: Iq):
         | 
| 767 | 
            +
                    if iq.get_to() == self.xmpp.boundjid.bare:
         | 
| 768 | 
            +
                        return
         | 
| 721 769 |  | 
| 722 | 
            -
             | 
| 723 | 
            -
             | 
| 724 | 
            -
                if sent:
         | 
| 725 | 
            -
                    return sent
         | 
| 770 | 
            +
                    session = await self.__get_session(iq)
         | 
| 771 | 
            +
                    session.raise_if_not_logged()
         | 
| 726 772 |  | 
| 727 | 
            -
             | 
| 728 | 
            -
             | 
| 729 | 
            -
             | 
| 773 | 
            +
                    if iq["type"] == "set" and iq["register"]["remove"]:
         | 
| 774 | 
            +
                        muc = await session.bookmarks.by_jid(iq.get_to())
         | 
| 775 | 
            +
                        await session.on_leave_group(muc.legacy_id)
         | 
| 776 | 
            +
                        iq.reply().send()
         | 
| 777 | 
            +
                        return
         | 
| 730 778 |  | 
| 731 | 
            -
             | 
| 732 | 
            -
             | 
| 733 | 
            -
                 | 
| 734 | 
            -
                     | 
| 735 | 
            -
             | 
| 736 | 
            -
             | 
| 737 | 
            -
             | 
| 738 | 
            -
             | 
| 739 | 
            -
                     | 
| 779 | 
            +
                    raise XMPPError("feature-not-implemented")
         | 
| 780 | 
            +
             | 
| 781 | 
            +
                def _xmpp_msg_id_to_legacy(self, session: "BaseSession", xmpp_id: str):
         | 
| 782 | 
            +
                    sent = self.xmpp.store.sent.get_legacy_id(session.user_pk, xmpp_id)
         | 
| 783 | 
            +
                    if sent is not None:
         | 
| 784 | 
            +
                        return self.xmpp.LEGACY_MSG_ID_TYPE(sent)
         | 
| 785 | 
            +
             | 
| 786 | 
            +
                    multi = self.xmpp.store.multi.get_legacy_id(session.user_pk, xmpp_id)
         | 
| 787 | 
            +
                    if multi:
         | 
| 788 | 
            +
                        return self.xmpp.LEGACY_MSG_ID_TYPE(multi)
         | 
| 789 | 
            +
             | 
| 790 | 
            +
                    try:
         | 
| 791 | 
            +
                        return session.xmpp_to_legacy_msg_id(xmpp_id)
         | 
| 792 | 
            +
                    except XMPPError:
         | 
| 793 | 
            +
                        raise
         | 
| 794 | 
            +
                    except Exception as e:
         | 
| 795 | 
            +
                        log.debug("Couldn't convert xmpp msg ID to legacy ID.", exc_info=e)
         | 
| 796 | 
            +
                        raise XMPPError(
         | 
| 797 | 
            +
                            "internal-server-error", "Couldn't convert xmpp msg ID to legacy ID."
         | 
| 798 | 
            +
                        )
         | 
| 740 799 |  | 
| 741 800 |  | 
| 742 801 | 
             
            def _ignore(session: "BaseSession", msg: Message):
         | 
| @@ -755,14 +814,18 @@ async def _xmpp_to_legacy_thread( | |
| 755 814 | 
             
                    return
         | 
| 756 815 |  | 
| 757 816 | 
             
                if session.MESSAGE_IDS_ARE_THREAD_IDS:
         | 
| 758 | 
            -
                    return session. | 
| 817 | 
            +
                    return session.xmpp.store.sent.get_legacy_thread(session.user_pk, xmpp_thread)
         | 
| 759 818 |  | 
| 760 819 | 
             
                async with session.thread_creation_lock:
         | 
| 761 | 
            -
                     | 
| 762 | 
            -
             | 
| 763 | 
            -
             | 
| 764 | 
            -
             | 
| 765 | 
            -
             | 
| 820 | 
            +
                    legacy_thread_str = session.xmpp.store.sent.get_legacy_thread(
         | 
| 821 | 
            +
                        session.user_pk, xmpp_thread
         | 
| 822 | 
            +
                    )
         | 
| 823 | 
            +
                    if legacy_thread_str is None:
         | 
| 824 | 
            +
                        legacy_thread = str(await recipient.create_thread(xmpp_thread))
         | 
| 825 | 
            +
                        session.xmpp.store.sent.set_thread(
         | 
| 826 | 
            +
                            session.user_pk, xmpp_thread, legacy_thread
         | 
| 827 | 
            +
                        )
         | 
| 828 | 
            +
                return session.xmpp.LEGACY_MSG_ID_TYPE(legacy_thread)
         | 
| 766 829 |  | 
| 767 830 |  | 
| 768 831 | 
             
            async def _get_entity(session: "BaseSession", m: Message) -> RecipientType:
         | 
| @@ -770,7 +833,7 @@ async def _get_entity(session: "BaseSession", m: Message) -> RecipientType: | |
| 770 833 | 
             
                if m.get_type() == "groupchat":
         | 
| 771 834 | 
             
                    muc = await session.bookmarks.by_jid(m.get_to())
         | 
| 772 835 | 
             
                    r = m.get_from().resource
         | 
| 773 | 
            -
                    if r not in muc. | 
| 836 | 
            +
                    if r not in muc.get_user_resources():
         | 
| 774 837 | 
             
                        session.create_task(muc.kick_resource(r))
         | 
| 775 838 | 
             
                        raise XMPPError("not-acceptable", "You are not connected to this chat")
         | 
| 776 839 | 
             
                    return muc
         | 
| @@ -40,11 +40,13 @@ class VCardTemp: | |
| 40 40 | 
             
                        return await self.__handle_set_vcard_temp(iq)
         | 
| 41 41 |  | 
| 42 42 | 
             
                async def __fetch_user_avatar(self, session: BaseSession):
         | 
| 43 | 
            -
                    hash_ = session.avatar_hash
         | 
| 43 | 
            +
                    hash_ = session.user.avatar_hash
         | 
| 44 44 | 
             
                    if not hash_:
         | 
| 45 | 
            -
                        raise XMPPError( | 
| 45 | 
            +
                        raise XMPPError(
         | 
| 46 | 
            +
                            "item-not-found", "The slidge user does not have any avatar set"
         | 
| 47 | 
            +
                        )
         | 
| 46 48 | 
             
                    meta_iq = await self.xmpp.plugin["xep_0060"].get_item(
         | 
| 47 | 
            -
                        session. | 
| 49 | 
            +
                        session.user_jid,
         | 
| 48 50 | 
             
                        MetaData.namespace,
         | 
| 49 51 | 
             
                        hash_,
         | 
| 50 52 | 
             
                        ifrom=self.xmpp.boundjid.bare,
         | 
| @@ -52,14 +54,14 @@ class VCardTemp: | |
| 52 54 | 
             
                    info = meta_iq["pubsub"]["items"]["item"]["avatar_metadata"]["info"]
         | 
| 53 55 | 
             
                    type_ = info["type"]
         | 
| 54 56 | 
             
                    data_iq = await self.xmpp.plugin["xep_0084"].retrieve_avatar(
         | 
| 55 | 
            -
                        session. | 
| 57 | 
            +
                        session.user_jid, hash_, ifrom=self.xmpp.boundjid.bare
         | 
| 56 58 | 
             
                    )
         | 
| 57 59 | 
             
                    bytes_ = data_iq["pubsub"]["items"]["item"]["avatar_data"]["value"]
         | 
| 58 60 | 
             
                    return bytes_, type_
         | 
| 59 61 |  | 
| 60 62 | 
             
                async def __handle_get_vcard_temp(self, iq: Iq):
         | 
| 61 63 | 
             
                    session = self.xmpp.get_session_from_stanza(iq)
         | 
| 62 | 
            -
                    entity = await session.get_contact_or_group_or_participant(iq.get_to())
         | 
| 64 | 
            +
                    entity = await session.get_contact_or_group_or_participant(iq.get_to(), False)
         | 
| 63 65 | 
             
                    if not entity:
         | 
| 64 66 | 
             
                        raise XMPPError("item-not-found")
         | 
| 65 67 |  | 
    
        slidge/core/mixins/__init__.py
    CHANGED
    
    | @@ -2,6 +2,8 @@ | |
| 2 2 | 
             
            Mixins
         | 
| 3 3 | 
             
            """
         | 
| 4 4 |  | 
| 5 | 
            +
            from typing import Optional
         | 
| 6 | 
            +
             | 
| 5 7 | 
             
            from .avatar import AvatarMixin
         | 
| 6 8 | 
             
            from .disco import ChatterDiscoMixin
         | 
| 7 9 | 
             
            from .message import MessageCarbonMixin, MessageMixin
         | 
| @@ -16,4 +18,12 @@ class FullCarbonMixin(ChatterDiscoMixin, MessageCarbonMixin, PresenceMixin): | |
| 16 18 | 
             
                pass
         | 
| 17 19 |  | 
| 18 20 |  | 
| 19 | 
            -
             | 
| 21 | 
            +
            class StoredAttributeMixin:
         | 
| 22 | 
            +
                def serialize_extra_attributes(self) -> Optional[dict]:
         | 
| 23 | 
            +
                    return None
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                def deserialize_extra_attributes(self, data: dict) -> None:
         | 
| 26 | 
            +
                    pass
         | 
| 27 | 
            +
             | 
| 28 | 
            +
             | 
| 29 | 
            +
            __all__ = ("AvatarMixin", "FullCarbonMixin", "StoredAttributeMixin")
         | 
    
        slidge/core/mixins/attachment.py
    CHANGED
    
    | @@ -9,7 +9,7 @@ import warnings | |
| 9 9 | 
             
            from datetime import datetime
         | 
| 10 10 | 
             
            from mimetypes import guess_type
         | 
| 11 11 | 
             
            from pathlib import Path
         | 
| 12 | 
            -
            from typing import IO, Collection, Optional, Sequence, Union
         | 
| 12 | 
            +
            from typing import IO, AsyncIterator, Collection, Optional, Sequence, Union
         | 
| 13 13 | 
             
            from urllib.parse import quote as urlquote
         | 
| 14 14 | 
             
            from uuid import uuid4
         | 
| 15 15 | 
             
            from xml.etree import ElementTree as ET
         | 
| @@ -22,7 +22,7 @@ from slixmpp.plugins.xep_0363 import FileUploadError | |
| 22 22 | 
             
            from slixmpp.plugins.xep_0385.stanza import Sims
         | 
| 23 23 | 
             
            from slixmpp.plugins.xep_0447.stanza import StatelessFileSharing
         | 
| 24 24 |  | 
| 25 | 
            -
            from ... | 
| 25 | 
            +
            from ...db.avatar import avatar_cache
         | 
| 26 26 | 
             
            from ...util.types import (
         | 
| 27 27 | 
             
                LegacyAttachment,
         | 
| 28 28 | 
             
                LegacyMessageType,
         | 
| @@ -31,11 +31,14 @@ from ...util.types import ( | |
| 31 31 | 
             
            )
         | 
| 32 32 | 
             
            from ...util.util import fix_suffix
         | 
| 33 33 | 
             
            from .. import config
         | 
| 34 | 
            -
            from ..cache import avatar_cache
         | 
| 35 34 | 
             
            from .message_maker import MessageMaker
         | 
| 36 35 |  | 
| 37 36 |  | 
| 38 37 | 
             
            class AttachmentMixin(MessageMaker):
         | 
| 38 | 
            +
                def __init__(self, *a, **kw):
         | 
| 39 | 
            +
                    super().__init__(*a, **kw)
         | 
| 40 | 
            +
                    self.__store = self.xmpp.store.attachments
         | 
| 41 | 
            +
             | 
| 39 42 | 
             
                def send_text(self, *_, **k) -> Optional[Message]:
         | 
| 40 43 | 
             
                    raise NotImplementedError
         | 
| 41 44 |  | 
| @@ -138,6 +141,7 @@ class AttachmentMixin(MessageMaker): | |
| 138 141 | 
             
                async def __get_url(
         | 
| 139 142 | 
             
                    self,
         | 
| 140 143 | 
             
                    file_path: Optional[Path] = None,
         | 
| 144 | 
            +
                    async_data_stream: Optional[AsyncIterator[bytes]] = None,
         | 
| 141 145 | 
             
                    data_stream: Optional[IO[bytes]] = None,
         | 
| 142 146 | 
             
                    data: Optional[bytes] = None,
         | 
| 143 147 | 
             
                    file_url: Optional[str] = None,
         | 
| @@ -146,13 +150,13 @@ class AttachmentMixin(MessageMaker): | |
| 146 150 | 
             
                    legacy_file_id: Optional[Union[str, int]] = None,
         | 
| 147 151 | 
             
                ) -> tuple[bool, Optional[Path], str]:
         | 
| 148 152 | 
             
                    if legacy_file_id:
         | 
| 149 | 
            -
                        cache =  | 
| 153 | 
            +
                        cache = self.__store.get_url(str(legacy_file_id))
         | 
| 150 154 | 
             
                        if cache is not None:
         | 
| 151 155 | 
             
                            async with self.session.http.head(cache) as r:
         | 
| 152 156 | 
             
                                if r.status < 400:
         | 
| 153 157 | 
             
                                    return False, None, cache
         | 
| 154 158 | 
             
                                else:
         | 
| 155 | 
            -
                                     | 
| 159 | 
            +
                                    self.__store.remove(str(legacy_file_id))
         | 
| 156 160 |  | 
| 157 161 | 
             
                    if file_url and config.USE_ATTACHMENT_ORIGINAL_URLS:
         | 
| 158 162 | 
             
                        return False, None, file_url
         | 
| @@ -173,14 +177,23 @@ class AttachmentMixin(MessageMaker): | |
| 173 177 | 
             
                                with file_path.open("wb") as f:
         | 
| 174 178 | 
             
                                    f.write(await r.read())
         | 
| 175 179 |  | 
| 176 | 
            -
                         | 
| 177 | 
            -
                             | 
| 178 | 
            -
                                data = data_stream.read()
         | 
| 180 | 
            +
                        elif data_stream is not None:
         | 
| 181 | 
            +
                            data = data_stream.read()
         | 
| 179 182 | 
             
                            if data is None:
         | 
| 180 183 | 
             
                                raise RuntimeError
         | 
| 181 184 |  | 
| 182 185 | 
             
                            with file_path.open("wb") as f:
         | 
| 183 186 | 
             
                                f.write(data)
         | 
| 187 | 
            +
                        elif async_data_stream is not None:
         | 
| 188 | 
            +
                            # TODO: patch slixmpp to allow this as data source for
         | 
| 189 | 
            +
                            #       upload_file() so we don't even have to write anything
         | 
| 190 | 
            +
                            #       to disk.
         | 
| 191 | 
            +
                            with file_path.open("wb") as f:
         | 
| 192 | 
            +
                                async for chunk in async_data_stream:
         | 
| 193 | 
            +
                                    f.write(chunk)
         | 
| 194 | 
            +
                        elif data is not None:
         | 
| 195 | 
            +
                            with file_path.open("wb") as f:
         | 
| 196 | 
            +
                                f.write(data)
         | 
| 184 197 |  | 
| 185 198 | 
             
                        is_temp = not bool(config.NO_UPLOAD_PATH)
         | 
| 186 199 | 
             
                    else:
         | 
| @@ -198,7 +211,7 @@ class AttachmentMixin(MessageMaker): | |
| 198 211 | 
             
                        local_path = file_path
         | 
| 199 212 | 
             
                        new_url = await self.__upload(file_path, file_name, content_type)
         | 
| 200 213 | 
             
                    if legacy_file_id:
         | 
| 201 | 
            -
                         | 
| 214 | 
            +
                        self.__store.set_url(self.session.user_pk, str(legacy_file_id), new_url)
         | 
| 202 215 |  | 
| 203 216 | 
             
                    return is_temp, local_path, new_url
         | 
| 204 217 |  | 
| @@ -211,7 +224,7 @@ class AttachmentMixin(MessageMaker): | |
| 211 224 | 
             
                    caption: Optional[str] = None,
         | 
| 212 225 | 
             
                    file_name: Optional[str] = None,
         | 
| 213 226 | 
             
                ):
         | 
| 214 | 
            -
                    cache =  | 
| 227 | 
            +
                    cache = self.__store.get_sims(uploaded_url)
         | 
| 215 228 | 
             
                    if cache:
         | 
| 216 229 | 
             
                        msg.append(Sims(xml=ET.fromstring(cache)))
         | 
| 217 230 | 
             
                        return
         | 
| @@ -238,7 +251,7 @@ class AttachmentMixin(MessageMaker): | |
| 238 251 | 
             
                            thumbnail["media-type"] = "image/blurhash"
         | 
| 239 252 | 
             
                            thumbnail["uri"] = "data:image/blurhash," + urlquote(h)
         | 
| 240 253 |  | 
| 241 | 
            -
                     | 
| 254 | 
            +
                    self.__store.set_sims(uploaded_url, str(sims))
         | 
| 242 255 |  | 
| 243 256 | 
             
                    msg.append(sims)
         | 
| 244 257 |  | 
| @@ -251,7 +264,7 @@ class AttachmentMixin(MessageMaker): | |
| 251 264 | 
             
                    caption: Optional[str] = None,
         | 
| 252 265 | 
             
                    file_name: Optional[str] = None,
         | 
| 253 266 | 
             
                ):
         | 
| 254 | 
            -
                    cache =  | 
| 267 | 
            +
                    cache = self.__store.get_sfs(uploaded_url)
         | 
| 255 268 | 
             
                    if cache:
         | 
| 256 269 | 
             
                        msg.append(StatelessFileSharing(xml=ET.fromstring(cache)))
         | 
| 257 270 | 
             
                        return
         | 
| @@ -262,7 +275,7 @@ class AttachmentMixin(MessageMaker): | |
| 262 275 | 
             
                    sfs = self.xmpp["xep_0447"].get_sfs(path, [uploaded_url], content_type, caption)
         | 
| 263 276 | 
             
                    if file_name:
         | 
| 264 277 | 
             
                        sfs["file"]["name"] = file_name
         | 
| 265 | 
            -
                     | 
| 278 | 
            +
                    self.__store.set_sfs(uploaded_url, str(sfs))
         | 
| 266 279 |  | 
| 267 280 | 
             
                    msg.append(sfs)
         | 
| 268 281 |  | 
| @@ -293,6 +306,7 @@ class AttachmentMixin(MessageMaker): | |
| 293 306 | 
             
                    file_path: Optional[Union[Path, str]] = None,
         | 
| 294 307 | 
             
                    legacy_msg_id: Optional[LegacyMessageType] = None,
         | 
| 295 308 | 
             
                    *,
         | 
| 309 | 
            +
                    async_data_stream: Optional[AsyncIterator[bytes]] = None,
         | 
| 296 310 | 
             
                    data_stream: Optional[IO[bytes]] = None,
         | 
| 297 311 | 
             
                    data: Optional[bytes] = None,
         | 
| 298 312 | 
             
                    file_url: Optional[str] = None,
         | 
| @@ -309,6 +323,7 @@ class AttachmentMixin(MessageMaker): | |
| 309 323 | 
             
                    Send a single file from this :term:`XMPP Entity`.
         | 
| 310 324 |  | 
| 311 325 | 
             
                    :param file_path: Path to the attachment
         | 
| 326 | 
            +
                    :param async_data_stream: Alternatively (and ideally) an AsyncIterator yielding bytes
         | 
| 312 327 | 
             
                    :param data_stream: Alternatively, a stream of bytes (such as a File object)
         | 
| 313 328 | 
             
                    :param data: Alternatively, a bytes object
         | 
| 314 329 | 
             
                    :param file_url: Alternatively, a URL
         | 
| @@ -339,6 +354,7 @@ class AttachmentMixin(MessageMaker): | |
| 339 354 |  | 
| 340 355 | 
             
                    is_temp, local_path, new_url = await self.__get_url(
         | 
| 341 356 | 
             
                        Path(file_path) if file_path else None,
         | 
| 357 | 
            +
                        async_data_stream,
         | 
| 342 358 | 
             
                        data_stream,
         | 
| 343 359 | 
             
                        data,
         | 
| 344 360 | 
             
                        file_url,
         | 
| @@ -472,7 +488,9 @@ class AttachmentMixin(MessageMaker): | |
| 472 488 | 
             
                            ids.append(stanza_id["id"])
         | 
| 473 489 | 
             
                        else:
         | 
| 474 490 | 
             
                            ids.append(msg.get_id())
         | 
| 475 | 
            -
                     | 
| 491 | 
            +
                    self.xmpp.store.multi.set_xmpp_ids(
         | 
| 492 | 
            +
                        self.session.user_pk, str(legacy_msg_id), ids
         | 
| 493 | 
            +
                    )
         | 
| 476 494 |  | 
| 477 495 |  | 
| 478 496 | 
             
            def get_blurhash(path: Path, n=9) -> tuple[str, int, int]:
         |