slidge 0.3.0b4__tar.gz → 0.3.1__tar.gz
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-0.3.0b4 → slidge-0.3.1}/PKG-INFO +1 -1
- {slidge-0.3.0b4 → slidge-0.3.1}/slidge/__init__.py +0 -1
- {slidge-0.3.0b4 → slidge-0.3.1}/slidge/command/user.py +25 -6
- {slidge-0.3.0b4 → slidge-0.3.1}/slidge/contact/contact.py +3 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/slidge/core/dispatcher/muc/misc.py +27 -2
- {slidge-0.3.0b4 → slidge-0.3.1}/slidge/core/dispatcher/presence.py +31 -26
- {slidge-0.3.0b4 → slidge-0.3.1}/slidge/core/mixins/db.py +23 -1
- {slidge-0.3.0b4 → slidge-0.3.1}/slidge/core/mixins/message_maker.py +2 -2
- {slidge-0.3.0b4 → slidge-0.3.1}/slidge/core/mixins/presence.py +2 -2
- {slidge-0.3.0b4 → slidge-0.3.1}/slidge/core/pubsub.py +1 -1
- {slidge-0.3.0b4 → slidge-0.3.1}/slidge/core/session.py +11 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/slidge/group/archive.py +3 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/slidge/group/participant.py +19 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/slidge/group/room.py +12 -0
- slidge-0.3.1/slidge/slixfix/xep_0292/vcard4.py +24 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/slidge.egg-info/PKG-INFO +1 -1
- {slidge-0.3.0b4 → slidge-0.3.1}/slidge.egg-info/SOURCES.txt +1 -1
- {slidge-0.3.0b4 → slidge-0.3.1}/tests/test_avatar.py +1 -1
- {slidge-0.3.0b4 → slidge-0.3.1}/tests/test_muc.py +2 -5
- slidge-0.3.0b4/tests/test_empty_subject.py → slidge-0.3.1/tests/test_muc_subject.py +70 -2
- {slidge-0.3.0b4 → slidge-0.3.1}/tests/test_session_2.py +1 -1
- {slidge-0.3.0b4 → slidge-0.3.1}/tests/test_shakespeare.py +2 -2
- {slidge-0.3.0b4 → slidge-0.3.1}/tests/test_util.py +9 -1
- {slidge-0.3.0b4 → slidge-0.3.1}/tests/test_vcard.py +4 -1
- slidge-0.3.0b4/slidge/slixfix/xep_0292/vcard4.py +0 -14
- {slidge-0.3.0b4 → slidge-0.3.1}/.gitignore +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/.pre-commit-config.yaml +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/.woodpecker/container-ci.yaml +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/.woodpecker/docs.yaml +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/.woodpecker/package.yaml +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/.woodpecker/test.yaml +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/Dockerfile +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/LICENSE +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/README.md +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/commitlint.config.js +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/dev/assets/5x5.png +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/dev/assets/slidge-color-small.png +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/dev/assets/slidge-color.png +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/dev/assets/slidge-mono-black.png +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/dev/assets/slidge-mono-white.png +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/dev/assets/slidge.svg +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/dev/confs/movim.env +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/dev/confs/nginx.conf +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/dev/confs/slidge-dev.ini +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/dev/confs/slidge-example.ini +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/dev/hot-reload.sh +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/dev/prettify_tests.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/doap.xml +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/docker-compose.yml +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/docs/Makefile +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/docs/source/admin/attachments.rst +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/docs/source/admin/component.rst +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/docs/source/admin/config/index.rst +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/docs/source/admin/daemon.rst +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/docs/source/admin/examples/ejabberd.yaml +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/docs/source/admin/examples/index.rst +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/docs/source/admin/examples/prosody.cfg.lua +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/docs/source/admin/index.rst +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/docs/source/admin/install.rst +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/docs/source/admin/note.rst +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/docs/source/admin/privilege.rst +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/docs/source/codeberg.svg +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/docs/source/conf.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/docs/source/dev/contributing.rst +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/docs/source/dev/design.rst +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/docs/source/dev/howto.rst +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/docs/source/dev/index.rst +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/docs/source/dev/tutorial.rst +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/docs/source/glossary.rst +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/docs/source/index.rst +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/docs/source/user/commands.rst +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/docs/source/user/contacts.rst +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/docs/source/user/foxyproxy.png +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/docs/source/user/gajim.png +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/docs/source/user/index.rst +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/docs/source/user/low_profile.rst +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/docs/source/user/movim1.png +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/docs/source/user/movim2.png +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/docs/source/user/note.rst +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/docs/source/user/register.rst +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/pyproject.toml +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/setup.cfg +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/slidge/__main__.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/slidge/command/__init__.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/slidge/command/adhoc.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/slidge/command/admin.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/slidge/command/base.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/slidge/command/categories.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/slidge/command/chat_command.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/slidge/command/register.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/slidge/contact/__init__.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/slidge/contact/roster.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/slidge/core/__init__.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/slidge/core/config.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/slidge/core/dispatcher/__init__.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/slidge/core/dispatcher/caps.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/slidge/core/dispatcher/disco.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/slidge/core/dispatcher/message/__init__.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/slidge/core/dispatcher/message/chat_state.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/slidge/core/dispatcher/message/marker.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/slidge/core/dispatcher/message/message.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/slidge/core/dispatcher/muc/__init__.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/slidge/core/dispatcher/muc/admin.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/slidge/core/dispatcher/muc/mam.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/slidge/core/dispatcher/muc/owner.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/slidge/core/dispatcher/muc/ping.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/slidge/core/dispatcher/registration.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/slidge/core/dispatcher/search.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/slidge/core/dispatcher/session_dispatcher.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/slidge/core/dispatcher/util.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/slidge/core/dispatcher/vcard.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/slidge/core/gateway.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/slidge/core/mixins/__init__.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/slidge/core/mixins/attachment.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/slidge/core/mixins/avatar.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/slidge/core/mixins/base.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/slidge/core/mixins/disco.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/slidge/core/mixins/message.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/slidge/core/mixins/message_text.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/slidge/core/mixins/recipient.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/slidge/db/__init__.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/slidge/db/alembic/__init__.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/slidge/db/alembic/env.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/slidge/db/alembic/script.py.mako +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/slidge/db/alembic/versions/cef02a8b1451_initial_schema.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/slidge/db/avatar.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/slidge/db/meta.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/slidge/db/models.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/slidge/db/store.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/slidge/group/__init__.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/slidge/group/bookmarks.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/slidge/main.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/slidge/migration.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/slidge/py.typed +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/slidge/slixfix/__init__.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/slidge/slixfix/delivery_receipt.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/slidge/slixfix/link_preview/__init__.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/slidge/slixfix/link_preview/link_preview.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/slidge/slixfix/link_preview/stanza.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/slidge/slixfix/roster.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/slidge/slixfix/xep_0077/__init__.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/slidge/slixfix/xep_0077/register.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/slidge/slixfix/xep_0077/stanza.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/slidge/slixfix/xep_0100/__init__.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/slidge/slixfix/xep_0100/gateway.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/slidge/slixfix/xep_0100/stanza.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/slidge/slixfix/xep_0153/__init__.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/slidge/slixfix/xep_0153/vcard_avatar.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/slidge/slixfix/xep_0292/__init__.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/slidge/util/__init__.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/slidge/util/archive_msg.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/slidge/util/conf.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/slidge/util/jid_escaping.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/slidge/util/lock.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/slidge/util/test.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/slidge/util/types.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/slidge/util/util.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/slidge.egg-info/dependency_links.txt +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/slidge.egg-info/entry_points.txt +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/slidge.egg-info/requires.txt +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/slidge.egg-info/top_level.txt +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/superduper/__init__.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/superduper/__main__.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/superduper/contact.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/superduper/gateway.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/superduper/group.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/superduper/legacy_client.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/superduper/session.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/superduper/util.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/tests/conftest.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/tests/test_adhoc/test_access.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/tests/test_adhoc/test_confirmation.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/tests/test_adhoc/test_form.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/tests/test_adhoc/test_reported.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/tests/test_attachment.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/tests/test_backfill.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/tests/test_chat_commands.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/tests/test_config.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/tests/test_db/test_store.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/tests/test_db/test_user.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/tests/test_feature_restriction.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/tests/test_mam_archivable.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/tests/test_mds.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/tests/test_resourceprep.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/tests/test_session.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/tests/test_set_name_before_fill.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/tests/test_stanza_link_preview.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/tests/test_type_conversion.py +0 -0
- {slidge-0.3.0b4 → slidge-0.3.1}/uv.lock +0 -0
@@ -34,8 +34,9 @@ class Search(Command):
|
|
34
34
|
async def run(
|
35
35
|
self, session: Optional[AnyBaseSession], _ifrom: JID, *args: str
|
36
36
|
) -> Union[Form, SearchResult, None]:
|
37
|
+
assert session is not None
|
38
|
+
await session.ready
|
37
39
|
if args:
|
38
|
-
assert session is not None
|
39
40
|
return await session.on_search(
|
40
41
|
{self.xmpp.SEARCH_FIELDS[0].var: " ".join(args)}
|
41
42
|
)
|
@@ -70,6 +71,8 @@ class SyncContacts(Command):
|
|
70
71
|
CATEGORY = CONTACTS
|
71
72
|
|
72
73
|
async def run(self, session: Optional[AnyBaseSession], _ifrom, *_) -> Confirmation:
|
74
|
+
assert session is not None
|
75
|
+
await session.ready
|
73
76
|
return Confirmation(
|
74
77
|
prompt="Are you sure you want to sync your roster?",
|
75
78
|
success=None,
|
@@ -133,6 +136,7 @@ class ListContacts(Command):
|
|
133
136
|
self, session: Optional[AnyBaseSession], _ifrom: JID, *_
|
134
137
|
) -> TableResult:
|
135
138
|
assert session is not None
|
139
|
+
await session.ready
|
136
140
|
contacts = sorted(
|
137
141
|
session.contacts, key=lambda c: c.name.casefold() if c.name else ""
|
138
142
|
)
|
@@ -152,7 +156,7 @@ class ListGroups(Command):
|
|
152
156
|
|
153
157
|
async def run(self, session, _ifrom, *_):
|
154
158
|
assert session is not None
|
155
|
-
await session.
|
159
|
+
await session.ready
|
156
160
|
groups = sorted(session.bookmarks, key=lambda g: g.DISCO_NAME.casefold())
|
157
161
|
return TableResult(
|
158
162
|
description="Your groups",
|
@@ -201,6 +205,7 @@ class CreateGroup(Command):
|
|
201
205
|
|
202
206
|
async def run(self, session: Optional[AnyBaseSession], _ifrom, *_):
|
203
207
|
assert session is not None
|
208
|
+
await session.ready
|
204
209
|
contacts = session.contacts.known_contacts(only_friends=True)
|
205
210
|
return Form(
|
206
211
|
title="Create a new group",
|
@@ -260,20 +265,34 @@ class Preferences(Command):
|
|
260
265
|
instructions=self.HELP,
|
261
266
|
fields=fields,
|
262
267
|
handler=self.finish, # type:ignore
|
268
|
+
handler_kwargs={"previous": current},
|
263
269
|
)
|
264
270
|
|
265
271
|
async def finish(
|
266
|
-
self,
|
272
|
+
self,
|
273
|
+
form_values: UserPreferences,
|
274
|
+
session: Optional[AnyBaseSession],
|
275
|
+
*_,
|
276
|
+
previous,
|
267
277
|
) -> str:
|
268
278
|
assert session is not None
|
279
|
+
if previous == form_values:
|
280
|
+
return "No preference was changed"
|
281
|
+
|
269
282
|
user = session.user
|
270
283
|
user.preferences.update(form_values) # type:ignore
|
271
|
-
|
284
|
+
self.xmpp.store.users.update(user)
|
285
|
+
|
286
|
+
try:
|
287
|
+
await session.on_preferences(previous, form_values) # type:ignore[arg-type]
|
288
|
+
except NotImplementedError:
|
289
|
+
pass
|
290
|
+
|
291
|
+
if not previous["sync_avatar"] and form_values["sync_avatar"]:
|
272
292
|
await self.xmpp.fetch_user_avatar(session)
|
273
293
|
else:
|
274
294
|
user.avatar_hash = None
|
275
295
|
|
276
|
-
self.xmpp.store.users.update(user)
|
277
296
|
return "Your preferences have been updated."
|
278
297
|
|
279
298
|
|
@@ -308,7 +327,7 @@ class LeaveGroup(Command):
|
|
308
327
|
|
309
328
|
async def run(self, session, _ifrom, *_):
|
310
329
|
assert session is not None
|
311
|
-
await session.
|
330
|
+
await session.ready
|
312
331
|
groups = sorted(session.bookmarks, key=lambda g: g.DISCO_NAME.casefold())
|
313
332
|
return Form(
|
314
333
|
title="Leave a group",
|
@@ -325,6 +325,7 @@ class LegacyContact(
|
|
325
325
|
email: Optional[str] = None,
|
326
326
|
country: Optional[str] = None,
|
327
327
|
locality: Optional[str] = None,
|
328
|
+
pronouns: Optional[str] = None,
|
328
329
|
) -> None:
|
329
330
|
vcard = VCard4()
|
330
331
|
vcard.add_impp(f"xmpp:{self.jid.bare}")
|
@@ -357,6 +358,8 @@ class LegacyContact(
|
|
357
358
|
vcard.add_address(country, locality)
|
358
359
|
elif country:
|
359
360
|
vcard.add_address(country, locality)
|
361
|
+
if pronouns:
|
362
|
+
vcard["pronouns"]["text"] = pronouns
|
360
363
|
|
361
364
|
self.stored.vcard = str(vcard)
|
362
365
|
self.stored.vcard_fetched = True
|
@@ -1,6 +1,14 @@
|
|
1
1
|
import logging
|
2
2
|
|
3
|
-
from slixmpp import
|
3
|
+
from slixmpp import (
|
4
|
+
JID,
|
5
|
+
CoroutineCallback,
|
6
|
+
Iq,
|
7
|
+
MatchXMLMask,
|
8
|
+
Message,
|
9
|
+
Presence,
|
10
|
+
StanzaPath,
|
11
|
+
)
|
4
12
|
from slixmpp.exceptions import XMPPError
|
5
13
|
|
6
14
|
from ..util import DispatcherMixin, exceptions_to_xmpp_errors
|
@@ -23,6 +31,15 @@ class MucMiscMixin(DispatcherMixin):
|
|
23
31
|
)
|
24
32
|
xmpp.add_event_handler("groupchat_subject", self.on_groupchat_subject)
|
25
33
|
xmpp.add_event_handler("groupchat_message_error", self.__on_group_chat_error)
|
34
|
+
xmpp.register_handler(
|
35
|
+
CoroutineCallback(
|
36
|
+
"muc_thread_subject",
|
37
|
+
MatchXMLMask(
|
38
|
+
"<message xmlns='jabber:component:accept' type='groupchat'><subject/><thread/></message>"
|
39
|
+
),
|
40
|
+
self.on_thread_subject,
|
41
|
+
)
|
42
|
+
)
|
26
43
|
|
27
44
|
async def __on_group_chat_error(self, msg: Message) -> None:
|
28
45
|
condition = msg["error"].get_condition()
|
@@ -43,7 +60,7 @@ class MucMiscMixin(DispatcherMixin):
|
|
43
60
|
# (not sure why?), but is of no consequence
|
44
61
|
log.debug("%s was not in the resources of %s", resource, muc)
|
45
62
|
else:
|
46
|
-
log.
|
63
|
+
log.debug(
|
47
64
|
"Removed %s from the resources of %s because of error", resource, muc
|
48
65
|
)
|
49
66
|
|
@@ -106,6 +123,14 @@ class MucMiscMixin(DispatcherMixin):
|
|
106
123
|
)
|
107
124
|
await muc.on_set_subject(msg["subject"])
|
108
125
|
|
126
|
+
@exceptions_to_xmpp_errors
|
127
|
+
async def on_thread_subject(self, msg: Message):
|
128
|
+
if msg["body"]:
|
129
|
+
return
|
130
|
+
session, muc, thread = await self._get_session_recipient_thread(msg)
|
131
|
+
assert thread is not None
|
132
|
+
await muc.on_set_thread_subject(thread, msg["subject"]) # type:ignore[union-attr]
|
133
|
+
|
109
134
|
|
110
135
|
KICKABLE_ERRORS = {
|
111
136
|
"gone",
|
@@ -4,6 +4,7 @@ from slixmpp import JID, Presence
|
|
4
4
|
from slixmpp.exceptions import XMPPError
|
5
5
|
|
6
6
|
from ...contact.roster import ContactIsUser
|
7
|
+
from ...util.types import AnyBaseSession
|
7
8
|
from ...util.util import merge_resources
|
8
9
|
from ..session import BaseSession
|
9
10
|
from .util import DispatcherMixin, exceptions_to_xmpp_errors
|
@@ -98,7 +99,7 @@ class PresenceHandlerMixin(DispatcherMixin):
|
|
98
99
|
reply.send()
|
99
100
|
|
100
101
|
@exceptions_to_xmpp_errors
|
101
|
-
async def on_presence(self, p: Presence):
|
102
|
+
async def on_presence(self, p: Presence) -> None:
|
102
103
|
if p.get_plugin("muc_join", check=True):
|
103
104
|
# handled in on_groupchat_join
|
104
105
|
# without this early return, since we switch from and to in this
|
@@ -112,31 +113,8 @@ class PresenceHandlerMixin(DispatcherMixin):
|
|
112
113
|
|
113
114
|
pto = p.get_to()
|
114
115
|
if pto == self.xmpp.boundjid.bare:
|
115
|
-
|
116
|
-
|
117
|
-
return
|
118
|
-
if not session.user.preferences.get("sync_presence", False):
|
119
|
-
session.log.debug("User does not want to sync their presence")
|
120
|
-
return
|
121
|
-
# NB: get_type() returns either a proper presence type or
|
122
|
-
# a presence show if available. Weird, weird, weird slix.
|
123
|
-
resources = self.xmpp.roster[self.xmpp.boundjid.bare][
|
124
|
-
p.get_from()
|
125
|
-
].resources
|
126
|
-
try:
|
127
|
-
await session.on_presence(
|
128
|
-
p.get_from().resource,
|
129
|
-
ptype, # type: ignore
|
130
|
-
p["status"],
|
131
|
-
resources,
|
132
|
-
merge_resources(resources),
|
133
|
-
)
|
134
|
-
except NotImplementedError:
|
135
|
-
pass
|
136
|
-
if p.get_type() == "available":
|
137
|
-
await self.xmpp.pubsub.on_presence_available(p, None)
|
138
|
-
for contact in session.contacts:
|
139
|
-
await self.xmpp.pubsub.on_presence_available(p, contact)
|
116
|
+
await self._on_presence_to_component(session, p)
|
117
|
+
return
|
140
118
|
|
141
119
|
if p.get_type() == "available":
|
142
120
|
try:
|
@@ -181,6 +159,33 @@ class PresenceHandlerMixin(DispatcherMixin):
|
|
181
159
|
)
|
182
160
|
error_stanza.send()
|
183
161
|
|
162
|
+
async def _on_presence_to_component(
|
163
|
+
self, session: AnyBaseSession, p: Presence
|
164
|
+
) -> None:
|
165
|
+
session.log.debug("Received a presence from %s", p.get_from())
|
166
|
+
if (ptype := p.get_type()) not in _USEFUL_PRESENCES:
|
167
|
+
return
|
168
|
+
if not session.user.preferences.get("sync_presence", False):
|
169
|
+
session.log.debug("User does not want to sync their presence")
|
170
|
+
return
|
171
|
+
# NB: get_type() returns either a proper presence type or
|
172
|
+
# a presence show if available. Weird, weird, weird slix.
|
173
|
+
resources = self.xmpp.roster[self.xmpp.boundjid.bare][p.get_from()].resources
|
174
|
+
try:
|
175
|
+
await session.on_presence(
|
176
|
+
p.get_from().resource,
|
177
|
+
ptype, # type: ignore
|
178
|
+
p["status"],
|
179
|
+
resources,
|
180
|
+
merge_resources(resources),
|
181
|
+
)
|
182
|
+
except NotImplementedError:
|
183
|
+
pass
|
184
|
+
if p.get_type() == "available":
|
185
|
+
await self.xmpp.pubsub.on_presence_available(p, None)
|
186
|
+
for contact in session.contacts:
|
187
|
+
await self.xmpp.pubsub.on_presence_available(p, contact)
|
188
|
+
|
184
189
|
|
185
190
|
_USEFUL_PRESENCES = {"available", "unavailable", "away", "chat", "dnd", "xa"}
|
186
191
|
|
@@ -2,7 +2,7 @@ import logging
|
|
2
2
|
import typing
|
3
3
|
from contextlib import contextmanager
|
4
4
|
|
5
|
-
from ...db.models import Base, Contact,
|
5
|
+
from ...db.models import Base, Contact, Room
|
6
6
|
|
7
7
|
if typing.TYPE_CHECKING:
|
8
8
|
from slidge import BaseGateway
|
@@ -41,13 +41,35 @@ class UpdateInfoMixin(DBMixin):
|
|
41
41
|
def __init__(self, *args, **kwargs) -> None:
|
42
42
|
super().__init__(*args, **kwargs)
|
43
43
|
self._updating_info = False
|
44
|
+
self.__deserialize()
|
45
|
+
|
46
|
+
def __deserialize(self):
|
44
47
|
if self.stored.extra_attributes is not None:
|
45
48
|
self.deserialize_extra_attributes(self.stored.extra_attributes)
|
46
49
|
|
50
|
+
def refresh(self) -> None:
|
51
|
+
with self.xmpp.store.session(expire_on_commit=False) as orm:
|
52
|
+
orm.add(self.stored)
|
53
|
+
orm.refresh(self.stored)
|
54
|
+
self.__deserialize()
|
55
|
+
|
47
56
|
def serialize_extra_attributes(self) -> dict | None:
|
57
|
+
"""
|
58
|
+
If you want custom attributes of your instance to be stored persistently
|
59
|
+
to the DB, here is where you have to return them as a dict to be used in
|
60
|
+
`deserialize_extra_attributes()`.
|
61
|
+
|
62
|
+
"""
|
48
63
|
return None
|
49
64
|
|
50
65
|
def deserialize_extra_attributes(self, data: dict) -> None:
|
66
|
+
"""
|
67
|
+
This is where you get the dict that you passed in
|
68
|
+
`serialize_extra_attributes()`.
|
69
|
+
|
70
|
+
⚠ Since it is serialized as json, dictionary keys are converted to strings!
|
71
|
+
Be sure to convert to other types if necessary.
|
72
|
+
"""
|
51
73
|
pass
|
52
74
|
|
53
75
|
@contextmanager
|
@@ -1,7 +1,7 @@
|
|
1
|
+
import uuid
|
1
2
|
import warnings
|
2
3
|
from datetime import datetime, timezone
|
3
4
|
from typing import TYPE_CHECKING, Iterable, Optional, cast
|
4
|
-
from uuid import uuid4
|
5
5
|
|
6
6
|
from slixmpp import JID, Message
|
7
7
|
from slixmpp.types import MessageTypes
|
@@ -95,7 +95,7 @@ class MessageMaker(BaseSender):
|
|
95
95
|
msg["stanza_id"]["id"] = i
|
96
96
|
msg["stanza_id"]["by"] = self.muc.jid # type: ignore
|
97
97
|
elif self.USE_STANZA_ID:
|
98
|
-
msg["stanza_id"]["id"] = str(uuid4())
|
98
|
+
msg["stanza_id"]["id"] = str(uuid.uuid4())
|
99
99
|
msg["stanza_id"]["by"] = self.muc.jid # type: ignore
|
100
100
|
|
101
101
|
def _legacy_to_xmpp(self, legacy_id: LegacyMessageType) -> str:
|
@@ -279,9 +279,9 @@ class PresenceMixin(BaseSender, DBMixin):
|
|
279
279
|
pass
|
280
280
|
|
281
281
|
|
282
|
-
def get_last_seen_fallback(last_seen: datetime):
|
282
|
+
def get_last_seen_fallback(last_seen: datetime) -> tuple[str, bool]:
|
283
283
|
now = datetime.now(tz=timezone.utc)
|
284
284
|
if now - last_seen < timedelta(days=7):
|
285
|
-
return f"Last seen {last_seen:%A %H:%M GMT}", True
|
285
|
+
return f"Last seen {last_seen:%A %H:%M %p GMT}", True
|
286
286
|
else:
|
287
287
|
return f"Last seen {last_seen:%b %-d %Y}", False
|
@@ -120,7 +120,7 @@ class PubSubComponent(NamedLockMixin, BasePlugin):
|
|
120
120
|
try:
|
121
121
|
iq = await self.xmpp.plugin["xep_0030"].get_info(from_)
|
122
122
|
except (IqError, IqTimeout):
|
123
|
-
log.debug("Could get disco#info of %s, ignoring", from_)
|
123
|
+
log.debug("Could not get disco#info of %s, ignoring", from_)
|
124
124
|
return []
|
125
125
|
info = iq["disco_info"]
|
126
126
|
return info["features"]
|
@@ -539,6 +539,17 @@ class BaseSession(
|
|
539
539
|
"""
|
540
540
|
raise NotImplementedError
|
541
541
|
|
542
|
+
async def on_preferences(
|
543
|
+
self, previous: dict[str, Any], new: dict[str, Any]
|
544
|
+
) -> None:
|
545
|
+
"""
|
546
|
+
This is called when the user updates their preferences.
|
547
|
+
|
548
|
+
Override this if you need set custom preferences field and need to trigger
|
549
|
+
something when a preference has changed.
|
550
|
+
"""
|
551
|
+
raise NotImplementedError
|
552
|
+
|
542
553
|
def __reset_ready(self) -> None:
|
543
554
|
self.ready = self.xmpp.loop.create_future()
|
544
555
|
|
@@ -22,6 +22,7 @@ from ..util.types import (
|
|
22
22
|
CachedPresence,
|
23
23
|
Hat,
|
24
24
|
LegacyMessageType,
|
25
|
+
LegacyThreadType,
|
25
26
|
MessageOrPresenceTypeVar,
|
26
27
|
MucAffiliation,
|
27
28
|
MucRole,
|
@@ -526,6 +527,24 @@ class LegacyParticipant(
|
|
526
527
|
msg.xml.append(ET.Element(f"{{{msg.namespace}}}subject"))
|
527
528
|
self._send(msg, full_jid)
|
528
529
|
|
530
|
+
def set_thread_subject(
|
531
|
+
self,
|
532
|
+
thread: LegacyThreadType,
|
533
|
+
subject: str | None,
|
534
|
+
when: Optional[datetime] = None,
|
535
|
+
) -> None:
|
536
|
+
msg = self._make_message()
|
537
|
+
msg["thread"] = str(thread)
|
538
|
+
if when is not None:
|
539
|
+
msg["delay"].set_stamp(when)
|
540
|
+
msg["delay"]["from"] = self.muc.jid
|
541
|
+
if subject:
|
542
|
+
msg["subject"] = subject
|
543
|
+
else:
|
544
|
+
# may be simplified if slixmpp lets it do it more easily some day
|
545
|
+
msg.xml.append(ET.Element(f"{{{msg.namespace}}}subject"))
|
546
|
+
self._send(msg)
|
547
|
+
|
529
548
|
|
530
549
|
def escape_nickname(muc_jid: JID, nickname: str) -> tuple[str, JID]:
|
531
550
|
nickname = nickname_no_illegal = strip_illegal_chars(nickname)
|
@@ -43,6 +43,7 @@ from ..util.types import (
|
|
43
43
|
LegacyGroupIdType,
|
44
44
|
LegacyMessageType,
|
45
45
|
LegacyParticipantType,
|
46
|
+
LegacyThreadType,
|
46
47
|
LegacyUserIdType,
|
47
48
|
Mention,
|
48
49
|
MucAffiliation,
|
@@ -1335,6 +1336,17 @@ class LegacyMUC(
|
|
1335
1336
|
"""
|
1336
1337
|
raise NotImplementedError
|
1337
1338
|
|
1339
|
+
async def on_set_thread_subject(
|
1340
|
+
self, thread: LegacyThreadType, subject: str
|
1341
|
+
) -> None:
|
1342
|
+
"""
|
1343
|
+
Triggered when the user requests changing the subject of a specific thread.
|
1344
|
+
|
1345
|
+
:param thread: Legacy identifier of the thread
|
1346
|
+
:param subject: The new subject for this thread.
|
1347
|
+
"""
|
1348
|
+
raise NotImplementedError
|
1349
|
+
|
1338
1350
|
@property
|
1339
1351
|
def participants_filled(self) -> bool:
|
1340
1352
|
return self.stored.participants_filled
|
@@ -0,0 +1,24 @@
|
|
1
|
+
from slixmpp import register_stanza_plugin, __version_info__
|
2
|
+
from slixmpp.plugins.base import BasePlugin, register_plugin
|
3
|
+
from slixmpp.plugins.xep_0292.stanza import NS, _VCardTextElementBase, VCard4
|
4
|
+
|
5
|
+
|
6
|
+
class VCard4Provider(BasePlugin):
|
7
|
+
name = "xep_0292_provider"
|
8
|
+
description = "VCard4 Provider"
|
9
|
+
dependencies = {"xep_0030"}
|
10
|
+
|
11
|
+
def plugin_init(self) -> None:
|
12
|
+
self.xmpp.plugin["xep_0030"].add_feature(NS)
|
13
|
+
|
14
|
+
|
15
|
+
|
16
|
+
|
17
|
+
register_plugin(VCard4Provider)
|
18
|
+
|
19
|
+
|
20
|
+
if __version_info__[0] <= 1 and __version_info__[1] <= 11:
|
21
|
+
class Pronouns(_VCardTextElementBase):
|
22
|
+
name = plugin_attrib = "pronouns"
|
23
|
+
|
24
|
+
register_stanza_plugin(VCard4, Pronouns)
|
@@ -164,11 +164,11 @@ tests/test_avatar.py
|
|
164
164
|
tests/test_backfill.py
|
165
165
|
tests/test_chat_commands.py
|
166
166
|
tests/test_config.py
|
167
|
-
tests/test_empty_subject.py
|
168
167
|
tests/test_feature_restriction.py
|
169
168
|
tests/test_mam_archivable.py
|
170
169
|
tests/test_mds.py
|
171
170
|
tests/test_muc.py
|
171
|
+
tests/test_muc_subject.py
|
172
172
|
tests/test_resourceprep.py
|
173
173
|
tests/test_session.py
|
174
174
|
tests/test_session_2.py
|
@@ -268,9 +268,6 @@ class Base(ClearSessionMixin, SlidgeTest):
|
|
268
268
|
cls.patches = [
|
269
269
|
unittest.mock.patch("uuid.uuid4", return_value="uuid"),
|
270
270
|
unittest.mock.patch("slidge.group.room.uuid4", return_value="uuid"),
|
271
|
-
unittest.mock.patch(
|
272
|
-
"slidge.core.mixins.message_maker.uuid4", return_value="uuid"
|
273
|
-
),
|
274
271
|
]
|
275
272
|
for p in cls.patches:
|
276
273
|
p.start()
|
@@ -2903,7 +2900,7 @@ class TestMuc(Base):
|
|
2903
2900
|
from="room-private@aim.shakespeare.lit/firstwitch"
|
2904
2901
|
to="romeo@montague.lit/movim">
|
2905
2902
|
<show>away</show>
|
2906
|
-
<status>blabla -- Last seen {last_seen:%A %H:%M GMT}</status>
|
2903
|
+
<status>blabla -- Last seen {last_seen:%A %H:%M %p GMT}</status>
|
2907
2904
|
<idle xmlns="urn:xmpp:idle:1"
|
2908
2905
|
since="{dt}" />
|
2909
2906
|
<x xmlns="http://jabber.org/protocol/muc#user">
|
@@ -2922,7 +2919,7 @@ class TestMuc(Base):
|
|
2922
2919
|
from="firstwitch@aim.shakespeare.lit/slidge"
|
2923
2920
|
to="romeo@montague.lit">
|
2924
2921
|
<show>away</show>
|
2925
|
-
<status>blabla -- Last seen {last_seen:%A %H:%M GMT}</status>
|
2922
|
+
<status>blabla -- Last seen {last_seen:%A %H:%M %p GMT}</status>
|
2926
2923
|
<idle xmlns="urn:xmpp:idle:1"
|
2927
2924
|
since="{dt}" />
|
2928
2925
|
<c xmlns="http://jabber.org/protocol/caps"
|
@@ -40,7 +40,7 @@ class MUC(LegacyMUC):
|
|
40
40
|
self.type = MucType.GROUP
|
41
41
|
|
42
42
|
@pytest.mark.usefixtures("avatar")
|
43
|
-
class
|
43
|
+
class TestMUCSubject(AvatarFixtureMixin, SlidgeTest):
|
44
44
|
plugin = globals()
|
45
45
|
xmpp: Gateway
|
46
46
|
|
@@ -97,7 +97,7 @@ class TestEmptySubject(AvatarFixtureMixin, SlidgeTest):
|
|
97
97
|
|
98
98
|
def test_empty_subject(self):
|
99
99
|
muc = self.run_coro(self.romeo_session.bookmarks.by_legacy_id("group"))
|
100
|
-
with unittest.mock.patch("
|
100
|
+
with unittest.mock.patch("uuid.uuid4", return_value="uuid"):
|
101
101
|
self.recv( # language=XML
|
102
102
|
f"""
|
103
103
|
<presence from="romeo@montague.lit/movim"
|
@@ -137,3 +137,71 @@ class TestEmptySubject(AvatarFixtureMixin, SlidgeTest):
|
|
137
137
|
""",
|
138
138
|
use_values=False,
|
139
139
|
)
|
140
|
+
|
141
|
+
def test_set_thread_subject(self):
|
142
|
+
muc: MUC = self.run_coro(self.romeo_session.bookmarks.by_legacy_id("group"))
|
143
|
+
muc.add_user_resource("movim")
|
144
|
+
with unittest.mock.patch("uuid.uuid4", return_value="uuid"):
|
145
|
+
juliet_participant = self.run_coro(muc.get_participant("juliet"))
|
146
|
+
juliet_participant.set_thread_subject("legacy-thread-id", "some-subject")
|
147
|
+
self.send( # language=XML
|
148
|
+
"""
|
149
|
+
<message xmlns="jabber:component:accept"
|
150
|
+
type="groupchat"
|
151
|
+
from="group@aim.shakespeare.lit/juliet"
|
152
|
+
to="romeo@montague.lit/movim">
|
153
|
+
<stanza-id xmlns="urn:xmpp:sid:0"
|
154
|
+
id="uuid"
|
155
|
+
by="group@aim.shakespeare.lit" />
|
156
|
+
<thread>legacy-thread-id</thread>
|
157
|
+
<subject>some-subject</subject>
|
158
|
+
<occupant-id xmlns="urn:xmpp:occupant-id:0"
|
159
|
+
id="uuid" />
|
160
|
+
</message>
|
161
|
+
"""
|
162
|
+
)
|
163
|
+
|
164
|
+
def test_user_set_thread_subject(self):
|
165
|
+
muc: MUC = self.run_coro(self.romeo_session.bookmarks.by_legacy_id("group"))
|
166
|
+
muc.add_user_resource("movim")
|
167
|
+
with unittest.mock.patch("slidge.group.room.LegacyMUC.on_set_subject") as on_set_subject, unittest.mock.patch("slidge.group.room.LegacyMUC.on_set_thread_subject") as on_set_thread_subject:
|
168
|
+
self.recv( # language=XML
|
169
|
+
"""
|
170
|
+
<message xmlns="jabber:component:accept"
|
171
|
+
type="groupchat"
|
172
|
+
from="romeo@montague.lit/movim"
|
173
|
+
to="group@aim.shakespeare.lit">
|
174
|
+
<stanza-id xmlns="urn:xmpp:sid:0"
|
175
|
+
id="uuid"
|
176
|
+
by="group@aim.shakespeare.lit" />
|
177
|
+
<thread>thread-id</thread>
|
178
|
+
<subject>some-subject</subject>
|
179
|
+
<occupant-id xmlns="urn:xmpp:occupant-id:0"
|
180
|
+
id="uuid" />
|
181
|
+
</message>
|
182
|
+
"""
|
183
|
+
)
|
184
|
+
on_set_subject.assert_not_called()
|
185
|
+
on_set_thread_subject.assert_awaited_once()
|
186
|
+
assert on_set_thread_subject.call_args[0] == ("thread-id", "some-subject")
|
187
|
+
|
188
|
+
with unittest.mock.patch("slidge.group.room.LegacyMUC.on_set_subject") as on_set_subject, unittest.mock.patch("slidge.group.room.LegacyMUC.on_set_thread_subject") as on_set_thread_subject:
|
189
|
+
self.recv( # language=XML
|
190
|
+
"""
|
191
|
+
<message xmlns="jabber:component:accept"
|
192
|
+
type="groupchat"
|
193
|
+
from="romeo@montague.lit/movim"
|
194
|
+
to="group@aim.shakespeare.lit">
|
195
|
+
<stanza-id xmlns="urn:xmpp:sid:0"
|
196
|
+
id="uuid"
|
197
|
+
by="group@aim.shakespeare.lit" />
|
198
|
+
<thread>thread-id</thread>
|
199
|
+
<subject>some-subject</subject>
|
200
|
+
<body>some-body</body>
|
201
|
+
<occupant-id xmlns="urn:xmpp:occupant-id:0"
|
202
|
+
id="uuid" />
|
203
|
+
</message>
|
204
|
+
"""
|
205
|
+
)
|
206
|
+
on_set_subject.assert_not_called()
|
207
|
+
on_set_thread_subject.assert_not_called()
|
@@ -639,7 +639,7 @@ class TestSession2(AvatarFixtureMixin, SlidgeTest):
|
|
639
639
|
""",
|
640
640
|
use_values=False,
|
641
641
|
)
|
642
|
-
with unittest.mock.patch("
|
642
|
+
with unittest.mock.patch("uuid.uuid4", return_value="uuid"):
|
643
643
|
part.react("msg-id", "♥")
|
644
644
|
self.send( # language=XML
|
645
645
|
"""
|
@@ -1639,7 +1639,7 @@ class TestContact(ClearSessionMixin, SlidgeTest):
|
|
1639
1639
|
juliet.away(status="Bye bye", last_seen=now)
|
1640
1640
|
|
1641
1641
|
p = self.next_sent()
|
1642
|
-
assert p["status"] == f"Bye bye -- Last seen {now:%A %H:%M GMT}"
|
1642
|
+
assert p["status"] == f"Bye bye -- Last seen {now:%A %H:%M %p GMT}"
|
1643
1643
|
assert p["idle"]["since"] == now
|
1644
1644
|
assert p["show"] == "away"
|
1645
1645
|
|
@@ -1651,7 +1651,7 @@ class TestContact(ClearSessionMixin, SlidgeTest):
|
|
1651
1651
|
|
1652
1652
|
juliet.busy(last_seen=now)
|
1653
1653
|
p = self.next_sent()
|
1654
|
-
assert p["status"] == f"Last seen {now:%A %H:%M GMT}"
|
1654
|
+
assert p["status"] == f"Last seen {now:%A %H:%M %p GMT}"
|
1655
1655
|
assert p["idle"]["since"] == now
|
1656
1656
|
assert p["show"] == "dnd"
|
1657
1657
|
|