slidge 0.3.1__tar.gz → 0.3.2__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.1 → slidge-0.3.2}/PKG-INFO +1 -1
- slidge-0.3.2/docs/source/admin/config/index.rst +57 -0
- {slidge-0.3.1 → slidge-0.3.2}/docs/source/conf.py +1 -1
- {slidge-0.3.1 → slidge-0.3.2}/pyproject.toml +3 -0
- {slidge-0.3.1 → slidge-0.3.2}/slidge/contact/contact.py +1 -1
- {slidge-0.3.1 → slidge-0.3.2}/slidge/contact/roster.py +6 -1
- {slidge-0.3.1 → slidge-0.3.2}/slidge/core/config.py +43 -9
- {slidge-0.3.1 → slidge-0.3.2}/slidge/core/dispatcher/message/message.py +1 -0
- {slidge-0.3.1 → slidge-0.3.2}/slidge/core/dispatcher/presence.py +1 -0
- {slidge-0.3.1 → slidge-0.3.2}/slidge/core/dispatcher/registration.py +1 -1
- {slidge-0.3.1 → slidge-0.3.2}/slidge/core/dispatcher/search.py +1 -5
- {slidge-0.3.1 → slidge-0.3.2}/slidge/core/dispatcher/util.py +3 -1
- {slidge-0.3.1 → slidge-0.3.2}/slidge/core/gateway.py +12 -1
- {slidge-0.3.1 → slidge-0.3.2}/slidge/core/mixins/attachment.py +62 -8
- {slidge-0.3.1 → slidge-0.3.2}/slidge/core/mixins/message_maker.py +1 -1
- {slidge-0.3.1 → slidge-0.3.2}/slidge/core/mixins/message_text.py +1 -1
- {slidge-0.3.1 → slidge-0.3.2}/slidge/core/mixins/presence.py +26 -8
- {slidge-0.3.1 → slidge-0.3.2}/slidge/core/session.py +2 -0
- {slidge-0.3.1 → slidge-0.3.2}/slidge/db/models.py +1 -1
- {slidge-0.3.1 → slidge-0.3.2}/slidge/db/store.py +29 -1
- {slidge-0.3.1 → slidge-0.3.2}/slidge/group/room.py +5 -2
- {slidge-0.3.1 → slidge-0.3.2}/slidge/util/test.py +3 -2
- {slidge-0.3.1 → slidge-0.3.2}/slidge/util/util.py +6 -4
- {slidge-0.3.1 → slidge-0.3.2}/slidge.egg-info/PKG-INFO +1 -1
- {slidge-0.3.1 → slidge-0.3.2}/slidge.egg-info/SOURCES.txt +1 -0
- {slidge-0.3.1 → slidge-0.3.2}/tests/conftest.py +9 -5
- {slidge-0.3.1 → slidge-0.3.2}/tests/test_attachment.py +131 -8
- {slidge-0.3.1 → slidge-0.3.2}/tests/test_backfill.py +0 -1
- {slidge-0.3.1 → slidge-0.3.2}/tests/test_config.py +1 -1
- {slidge-0.3.1 → slidge-0.3.2}/tests/test_db/test_store.py +41 -2
- slidge-0.3.2/tests/test_gateway_wide_reaction_restrictions.py +187 -0
- {slidge-0.3.1 → slidge-0.3.2}/tests/test_muc.py +23 -4
- {slidge-0.3.1 → slidge-0.3.2}/tests/test_muc_subject.py +0 -4
- {slidge-0.3.1 → slidge-0.3.2}/tests/test_session.py +0 -4
- {slidge-0.3.1 → slidge-0.3.2}/tests/test_session_2.py +0 -4
- {slidge-0.3.1 → slidge-0.3.2}/tests/test_set_name_before_fill.py +0 -4
- {slidge-0.3.1 → slidge-0.3.2}/tests/test_shakespeare.py +10 -11
- {slidge-0.3.1 → slidge-0.3.2}/tests/test_util.py +2 -2
- {slidge-0.3.1 → slidge-0.3.2}/tests/test_vcard.py +0 -4
- {slidge-0.3.1 → slidge-0.3.2}/uv.lock +17 -1
- slidge-0.3.1/docs/source/admin/config/index.rst +0 -35
- {slidge-0.3.1 → slidge-0.3.2}/.gitignore +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/.pre-commit-config.yaml +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/.woodpecker/container-ci.yaml +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/.woodpecker/docs.yaml +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/.woodpecker/package.yaml +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/.woodpecker/test.yaml +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/Dockerfile +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/LICENSE +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/README.md +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/commitlint.config.js +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/dev/assets/5x5.png +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/dev/assets/slidge-color-small.png +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/dev/assets/slidge-color.png +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/dev/assets/slidge-mono-black.png +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/dev/assets/slidge-mono-white.png +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/dev/assets/slidge.svg +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/dev/confs/movim.env +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/dev/confs/nginx.conf +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/dev/confs/slidge-dev.ini +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/dev/confs/slidge-example.ini +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/dev/hot-reload.sh +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/dev/prettify_tests.py +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/doap.xml +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/docker-compose.yml +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/docs/Makefile +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/docs/source/admin/attachments.rst +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/docs/source/admin/component.rst +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/docs/source/admin/daemon.rst +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/docs/source/admin/examples/ejabberd.yaml +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/docs/source/admin/examples/index.rst +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/docs/source/admin/examples/prosody.cfg.lua +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/docs/source/admin/index.rst +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/docs/source/admin/install.rst +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/docs/source/admin/note.rst +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/docs/source/admin/privilege.rst +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/docs/source/codeberg.svg +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/docs/source/dev/contributing.rst +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/docs/source/dev/design.rst +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/docs/source/dev/howto.rst +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/docs/source/dev/index.rst +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/docs/source/dev/tutorial.rst +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/docs/source/glossary.rst +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/docs/source/index.rst +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/docs/source/user/commands.rst +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/docs/source/user/contacts.rst +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/docs/source/user/foxyproxy.png +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/docs/source/user/gajim.png +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/docs/source/user/index.rst +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/docs/source/user/low_profile.rst +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/docs/source/user/movim1.png +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/docs/source/user/movim2.png +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/docs/source/user/note.rst +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/docs/source/user/register.rst +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/setup.cfg +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/slidge/__init__.py +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/slidge/__main__.py +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/slidge/command/__init__.py +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/slidge/command/adhoc.py +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/slidge/command/admin.py +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/slidge/command/base.py +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/slidge/command/categories.py +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/slidge/command/chat_command.py +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/slidge/command/register.py +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/slidge/command/user.py +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/slidge/contact/__init__.py +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/slidge/core/__init__.py +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/slidge/core/dispatcher/__init__.py +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/slidge/core/dispatcher/caps.py +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/slidge/core/dispatcher/disco.py +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/slidge/core/dispatcher/message/__init__.py +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/slidge/core/dispatcher/message/chat_state.py +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/slidge/core/dispatcher/message/marker.py +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/slidge/core/dispatcher/muc/__init__.py +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/slidge/core/dispatcher/muc/admin.py +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/slidge/core/dispatcher/muc/mam.py +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/slidge/core/dispatcher/muc/misc.py +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/slidge/core/dispatcher/muc/owner.py +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/slidge/core/dispatcher/muc/ping.py +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/slidge/core/dispatcher/session_dispatcher.py +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/slidge/core/dispatcher/vcard.py +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/slidge/core/mixins/__init__.py +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/slidge/core/mixins/avatar.py +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/slidge/core/mixins/base.py +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/slidge/core/mixins/db.py +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/slidge/core/mixins/disco.py +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/slidge/core/mixins/message.py +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/slidge/core/mixins/recipient.py +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/slidge/core/pubsub.py +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/slidge/db/__init__.py +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/slidge/db/alembic/__init__.py +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/slidge/db/alembic/env.py +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/slidge/db/alembic/script.py.mako +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/slidge/db/alembic/versions/cef02a8b1451_initial_schema.py +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/slidge/db/avatar.py +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/slidge/db/meta.py +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/slidge/group/__init__.py +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/slidge/group/archive.py +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/slidge/group/bookmarks.py +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/slidge/group/participant.py +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/slidge/main.py +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/slidge/migration.py +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/slidge/py.typed +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/slidge/slixfix/__init__.py +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/slidge/slixfix/delivery_receipt.py +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/slidge/slixfix/link_preview/__init__.py +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/slidge/slixfix/link_preview/link_preview.py +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/slidge/slixfix/link_preview/stanza.py +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/slidge/slixfix/roster.py +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/slidge/slixfix/xep_0077/__init__.py +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/slidge/slixfix/xep_0077/register.py +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/slidge/slixfix/xep_0077/stanza.py +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/slidge/slixfix/xep_0100/__init__.py +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/slidge/slixfix/xep_0100/gateway.py +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/slidge/slixfix/xep_0100/stanza.py +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/slidge/slixfix/xep_0153/__init__.py +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/slidge/slixfix/xep_0153/vcard_avatar.py +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/slidge/slixfix/xep_0292/__init__.py +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/slidge/slixfix/xep_0292/vcard4.py +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/slidge/util/__init__.py +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/slidge/util/archive_msg.py +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/slidge/util/conf.py +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/slidge/util/jid_escaping.py +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/slidge/util/lock.py +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/slidge/util/types.py +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/slidge.egg-info/dependency_links.txt +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/slidge.egg-info/entry_points.txt +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/slidge.egg-info/requires.txt +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/slidge.egg-info/top_level.txt +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/superduper/__init__.py +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/superduper/__main__.py +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/superduper/contact.py +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/superduper/gateway.py +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/superduper/group.py +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/superduper/legacy_client.py +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/superduper/session.py +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/superduper/util.py +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/tests/test_adhoc/test_access.py +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/tests/test_adhoc/test_confirmation.py +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/tests/test_adhoc/test_form.py +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/tests/test_adhoc/test_reported.py +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/tests/test_avatar.py +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/tests/test_chat_commands.py +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/tests/test_db/test_user.py +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/tests/test_feature_restriction.py +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/tests/test_mam_archivable.py +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/tests/test_mds.py +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/tests/test_resourceprep.py +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/tests/test_stanza_link_preview.py +0 -0
- {slidge-0.3.1 → slidge-0.3.2}/tests/test_type_conversion.py +0 -0
@@ -0,0 +1,57 @@
|
|
1
|
+
Configuration
|
2
|
+
=============
|
3
|
+
|
4
|
+
.. include:: ../note.rst
|
5
|
+
|
6
|
+
By default, slidge uses all config files found in ``/etc/slidge/conf.d/*``.
|
7
|
+
You can change this using the ``SLIDGE_CONF_DIR`` env var, eg
|
8
|
+
``SLIDGE_CONF_DIR=/path/dir1:/path/dir2:/path/dir3``.
|
9
|
+
|
10
|
+
It is recommended to use ``/etc/slidge/conf.d/`` to store configuration options
|
11
|
+
common to all slidge components (eg, attachment handling, logging options,
|
12
|
+
etc.), and to specify a plugin-specific file on startup, eg:
|
13
|
+
|
14
|
+
.. code-block:: bash
|
15
|
+
|
16
|
+
slidge -c /etc/slidge/superduper.conf
|
17
|
+
|
18
|
+
.. note::
|
19
|
+
For the debian unofficial package, just edit the ``/etc/slidge/conf.d/common.conf`` and
|
20
|
+
``/etc/slidge/*.conf`` files, and use :ref:`Debian packages (systemd)` to
|
21
|
+
launch slidge.
|
22
|
+
|
23
|
+
Command-line arguments
|
24
|
+
----------------------
|
25
|
+
|
26
|
+
.. code-block:: text
|
27
|
+
|
28
|
+
-h, --help show this help message and exit
|
29
|
+
-c, --config CONFIG Path to a INI config file. [env var: SLIDGE_CONFIG]
|
30
|
+
--log-config LOG_CONFIG
|
31
|
+
Path to a INI config file to personalise logging output.
|
32
|
+
-q, --quiet loglevel=WARNING (unused if --log-config is specified) [env var: SLIDGE_QUIET]
|
33
|
+
-d, --debug loglevel=DEBUG (unused if --log-config is specified) [env var: SLIDGE_DEBUG]
|
34
|
+
--version show program's version number and exit
|
35
|
+
|
36
|
+
|
37
|
+
Regarding the ``--log-config`` argument, refer to the `official python documentation
|
38
|
+
<https://docs.python.org/3/library/logging.config.html#configuration-file-format>`_
|
39
|
+
for the syntax.
|
40
|
+
|
41
|
+
Other options
|
42
|
+
-------------
|
43
|
+
|
44
|
+
.. warning::
|
45
|
+
|
46
|
+
Because of an ugly mess that will soon™ be fixed, it is impossible to use
|
47
|
+
the config file to turn off boolean arguments that are true by default.
|
48
|
+
As a workaround, use CLI args instead, e.g., ``--some-opt=false``.
|
49
|
+
|
50
|
+
The following options can be used:
|
51
|
+
|
52
|
+
* in a config file (see top of this page);
|
53
|
+
* as command line arguments, prepended with ``--``, e.g., ``--some-option=value``;
|
54
|
+
* as environment variables, upper case, prepended with ``SLIDGE_``,
|
55
|
+
and with dashes substituted with underscores, e.g., ``SLIDGE_SOME_OPTION=value``.
|
56
|
+
|
57
|
+
.. config-obj:: slidge.core.config
|
@@ -39,9 +39,9 @@ extensions = [
|
|
39
39
|
"sphinx.ext.extlinks",
|
40
40
|
# "sphinx.ext.viewcode", # crashes build unfortunately
|
41
41
|
"sphinx.ext.autodoc.typehints",
|
42
|
-
"sphinx.ext.autosectionlabel",
|
43
42
|
"sphinxarg.ext",
|
44
43
|
"autoapi.extension",
|
44
|
+
"slidge_sphinx_extensions.config_obj",
|
45
45
|
]
|
46
46
|
|
47
47
|
autodoc_typehints = "description"
|
@@ -63,6 +63,7 @@ dev = [
|
|
63
63
|
"types-pillow>=10.2.0.20240822",
|
64
64
|
"utidylib>=0.10",
|
65
65
|
"xmldiff>=2.7.0",
|
66
|
+
"slidge-sphinx-extensions>=0.2.1,<0.3",
|
66
67
|
]
|
67
68
|
|
68
69
|
[tool.mypy]
|
@@ -106,6 +107,8 @@ filterwarnings = [
|
|
106
107
|
"ignore::UserWarning:slidge",
|
107
108
|
"ignore:coroutine 'XMLStream._end_stream_wait' was never awaited.*:RuntimeWarning"
|
108
109
|
]
|
110
|
+
log_cli = true
|
111
|
+
log_level = "DEBUG"
|
109
112
|
|
110
113
|
[[tool.uv.index]]
|
111
114
|
name = "codeberg"
|
@@ -411,7 +411,7 @@ class LegacyContact(
|
|
411
411
|
# we only broadcast pubsub events for contacts added to the roster
|
412
412
|
# so if something was set before, we need to push it now
|
413
413
|
self.added_to_roster = True
|
414
|
-
self.send_last_presence()
|
414
|
+
self.send_last_presence(force=True)
|
415
415
|
|
416
416
|
async def __broadcast_pubsub_items(self) -> None:
|
417
417
|
if not self.is_friend:
|
@@ -4,7 +4,7 @@ import warnings
|
|
4
4
|
from typing import TYPE_CHECKING, AsyncIterator, Generic, Iterator, Optional, Type
|
5
5
|
|
6
6
|
from slixmpp import JID
|
7
|
-
from slixmpp.exceptions import IqError, IqTimeout
|
7
|
+
from slixmpp.exceptions import IqError, IqTimeout, XMPPError
|
8
8
|
from sqlalchemy.orm import Session
|
9
9
|
from sqlalchemy.orm import Session as OrmSession
|
10
10
|
|
@@ -92,6 +92,10 @@ class LegacyRoster(
|
|
92
92
|
# :return:
|
93
93
|
# """
|
94
94
|
username = contact_jid.node
|
95
|
+
if not username:
|
96
|
+
raise XMPPError(
|
97
|
+
"bad-request", "Contacts must have a local part in their JID"
|
98
|
+
)
|
95
99
|
contact_jid = JID(contact_jid.bare)
|
96
100
|
async with self.lock(("username", username)):
|
97
101
|
legacy_id = await self.jid_username_to_legacy_id(username)
|
@@ -239,6 +243,7 @@ class LegacyRoster(
|
|
239
243
|
warnings.warn(f"Could not add to roster: {e}")
|
240
244
|
else:
|
241
245
|
contact.added_to_roster = True
|
246
|
+
contact.send_last_presence(force=True)
|
242
247
|
orm.commit()
|
243
248
|
self.__filling = False
|
244
249
|
|
@@ -1,22 +1,26 @@
|
|
1
|
-
from datetime import timedelta
|
2
1
|
from pathlib import Path
|
3
|
-
from typing import Optional
|
2
|
+
from typing import Optional
|
4
3
|
|
5
4
|
from slixmpp import JID as JIDType
|
6
5
|
|
6
|
+
# REQUIRED, so not default value
|
7
7
|
|
8
|
-
class _TimedeltaSeconds(timedelta):
|
9
|
-
def __new__(cls, s: str) -> Self:
|
10
|
-
return super().__new__(cls, seconds=int(s))
|
11
8
|
|
9
|
+
class _Categories:
|
10
|
+
MANDATORY = (0, "Mandatory settings")
|
11
|
+
BASE = (10, "Basic configuration")
|
12
|
+
ATTACHMENTS = (20, "Attachments")
|
13
|
+
LOG = (30, "Logging")
|
14
|
+
ADVANCED = (40, "Advanced settings")
|
12
15
|
|
13
|
-
# REQUIRED, so not default value
|
14
16
|
|
15
17
|
LEGACY_MODULE: str
|
16
18
|
LEGACY_MODULE__DOC = (
|
17
|
-
"Importable python module containing (at least) "
|
18
|
-
"
|
19
|
+
"Importable python module containing (at least) a BaseGateway and a LegacySession subclass. "
|
20
|
+
"NB: this is not needed if you use a gateway-specific entrypoint, e.g., `slidgram` or "
|
21
|
+
"`python -m slidgram`."
|
19
22
|
)
|
23
|
+
LEGACY_MODULE__CATEGORY = _Categories.BASE
|
20
24
|
|
21
25
|
SERVER: str = "localhost"
|
22
26
|
SERVER__DOC = (
|
@@ -27,17 +31,21 @@ SERVER__DOC = (
|
|
27
31
|
"you change this."
|
28
32
|
)
|
29
33
|
SERVER__SHORT = "s"
|
34
|
+
SERVER__CATEGORY = _Categories.BASE
|
30
35
|
|
31
36
|
SECRET: str
|
32
37
|
SECRET__DOC = "The gateway component's secret (required to connect to the XMPP server)"
|
38
|
+
SECRET__CATEGORY = _Categories.MANDATORY
|
33
39
|
|
34
40
|
JID: JIDType
|
35
41
|
JID__DOC = "The gateway component's JID"
|
36
42
|
JID__SHORT = "j"
|
43
|
+
JID__CATEGORY = _Categories.MANDATORY
|
37
44
|
|
38
45
|
PORT: str = "5347"
|
39
46
|
PORT__DOC = "The XMPP server's port for incoming component connections"
|
40
47
|
PORT__SHORT = "p"
|
48
|
+
PORT__CATEGORY = _Categories.BASE
|
41
49
|
|
42
50
|
# Dynamic default (depends on other values)
|
43
51
|
|
@@ -47,6 +55,7 @@ HOME_DIR__DOC = (
|
|
47
55
|
"Defaults to /var/lib/slidge/${SLIDGE_JID}. "
|
48
56
|
)
|
49
57
|
HOME_DIR__DYNAMIC_DEFAULT = True
|
58
|
+
HOME_DIR__CATEGORY = _Categories.BASE
|
50
59
|
|
51
60
|
DB_URL: str
|
52
61
|
DB_URL__DOC = (
|
@@ -54,6 +63,7 @@ DB_URL__DOC = (
|
|
54
63
|
"Defaults to sqlite:///${HOME_DIR}/slidge.sqlite"
|
55
64
|
)
|
56
65
|
DB_URL__DYNAMIC_DEFAULT = True
|
66
|
+
DB_URL__CATEGORY = _Categories.ADVANCED
|
57
67
|
|
58
68
|
USER_JID_VALIDATOR: str
|
59
69
|
USER_JID_VALIDATOR__DOC = (
|
@@ -62,11 +72,13 @@ USER_JID_VALIDATOR__DOC = (
|
|
62
72
|
"you probably want to change that to .*@example.com"
|
63
73
|
)
|
64
74
|
USER_JID_VALIDATOR__DYNAMIC_DEFAULT = True
|
75
|
+
USER_JID_VALIDATOR__CATEGORY = _Categories.BASE
|
65
76
|
|
66
77
|
# Optional, so default value + type hint if default is None
|
67
78
|
|
68
79
|
ADMINS: tuple[JIDType, ...] = ()
|
69
80
|
ADMINS__DOC = "JIDs of the gateway admins"
|
81
|
+
ADMINS__CATEGORY = _Categories.BASE
|
70
82
|
|
71
83
|
UPLOAD_SERVICE: Optional[str] = None
|
72
84
|
UPLOAD_SERVICE__DOC = (
|
@@ -74,11 +86,13 @@ UPLOAD_SERVICE__DOC = (
|
|
74
86
|
"This is optional, as it should be automatically determined via service"
|
75
87
|
"discovery."
|
76
88
|
)
|
89
|
+
UPLOAD_SERVICE__CATEGORY = _Categories.ATTACHMENTS
|
77
90
|
|
78
91
|
AVATAR_SIZE = 200
|
79
92
|
AVATAR_SIZE__DOC = (
|
80
93
|
"Maximum image size (width and height), image ratio will be preserved"
|
81
94
|
)
|
95
|
+
AVATAR_SIZE__CATEGORY = _Categories.ADVANCED
|
82
96
|
|
83
97
|
USE_ATTACHMENT_ORIGINAL_URLS = False
|
84
98
|
USE_ATTACHMENT_ORIGINAL_URLS__DOC = (
|
@@ -86,11 +100,13 @@ USE_ATTACHMENT_ORIGINAL_URLS__DOC = (
|
|
86
100
|
"let XMPP clients directly download them from this URL. Note that this will "
|
87
101
|
"probably leak your client IP to the legacy network."
|
88
102
|
)
|
103
|
+
USE_ATTACHMENT_ORIGINAL_URLS__CATEGORY = _Categories.ATTACHMENTS
|
89
104
|
|
90
105
|
UPLOAD_REQUESTER: Optional[str] = None
|
91
106
|
UPLOAD_REQUESTER__DOC = (
|
92
107
|
"Set which JID should request the upload slots. Defaults to the component JID."
|
93
108
|
)
|
109
|
+
UPLOAD_REQUESTER__CATEGORY = _Categories.ATTACHMENTS
|
94
110
|
|
95
111
|
NO_UPLOAD_PATH: Optional[str] = None
|
96
112
|
NO_UPLOAD_PATH__DOC = (
|
@@ -98,38 +114,45 @@ NO_UPLOAD_PATH__DOC = (
|
|
98
114
|
"You need to set NO_UPLOAD_URL_PREFIX too if you use this option, and configure "
|
99
115
|
"an web server to serve files in this dir."
|
100
116
|
)
|
117
|
+
NO_UPLOAD_PATH__CATEGORY = _Categories.ATTACHMENTS
|
101
118
|
|
102
119
|
NO_UPLOAD_URL_PREFIX: Optional[str] = None
|
103
120
|
NO_UPLOAD_URL_PREFIX__DOC = (
|
104
121
|
"Base URL that servers files in the dir set in the no-upload-path option, "
|
105
122
|
"eg https://example.com:666/slidge-attachments/"
|
106
123
|
)
|
124
|
+
NO_UPLOAD_URL_PREFIX__CATEGORY = _Categories.ATTACHMENTS
|
107
125
|
|
108
126
|
NO_UPLOAD_METHOD: str = "copy"
|
109
127
|
NO_UPLOAD_METHOD__DOC = (
|
110
128
|
"Whether to 'copy', 'move', 'hardlink' or 'symlink' the files in no-upload-path."
|
111
129
|
)
|
130
|
+
NO_UPLOAD_METHOD__CATEGORY = _Categories.ATTACHMENTS
|
112
131
|
|
113
132
|
NO_UPLOAD_FILE_READ_OTHERS = False
|
114
133
|
NO_UPLOAD_FILE_READ_OTHERS__DOC = (
|
115
134
|
"After writing a file in NO_UPLOAD_PATH, change its permission so that 'others' can"
|
116
135
|
" read it."
|
117
136
|
)
|
137
|
+
NO_UPLOAD_FILE_READ_OTHERS__CATEGORY = _Categories.ATTACHMENTS
|
118
138
|
|
119
|
-
IGNORE_DELAY_THRESHOLD =
|
139
|
+
IGNORE_DELAY_THRESHOLD = 300
|
120
140
|
IGNORE_DELAY_THRESHOLD__DOC = (
|
121
141
|
"Threshold, in seconds, below which the <delay> information is stripped "
|
122
142
|
"out of emitted stanzas."
|
123
143
|
)
|
144
|
+
IGNORE_DELAY_THRESHOLD__CATEGORY = _Categories.ADVANCED
|
124
145
|
|
125
146
|
PARTIAL_REGISTRATION_TIMEOUT = 3600
|
126
147
|
PARTIAL_REGISTRATION_TIMEOUT__DOC = (
|
127
148
|
"Timeout before registration and login. Only useful for legacy networks where "
|
128
149
|
"a single step registration process is not enough."
|
129
150
|
)
|
151
|
+
PARTIAL_REGISTRATION_TIMEOUT__CATEGORY = _Categories.ADVANCED
|
130
152
|
|
131
153
|
QR_TIMEOUT = 60
|
132
154
|
QR_TIMEOUT__DOC = "Timeout for QR code flashing confirmation."
|
155
|
+
QR_TIMEOUT__CATEGORY = _Categories.ADVANCED
|
133
156
|
|
134
157
|
FIX_FILENAME_SUFFIX_MIME_TYPE = False
|
135
158
|
FIX_FILENAME_SUFFIX_MIME_TYPE__DOC = (
|
@@ -138,9 +161,11 @@ FIX_FILENAME_SUFFIX_MIME_TYPE__DOC = (
|
|
138
161
|
" Therefore the MIME Type of the file is checked, if the suffix is not valid for"
|
139
162
|
" that MIME Type, a valid one will be picked."
|
140
163
|
)
|
164
|
+
FIX_FILENAME_SUFFIX_MIME_TYPE__CATEGORY = _Categories.ATTACHMENTS
|
141
165
|
|
142
166
|
LOG_FILE: Optional[Path] = None
|
143
167
|
LOG_FILE__DOC = "Log to a file instead of stdout/err"
|
168
|
+
LOG_FILE__CATEGORY = _Categories.LOG
|
144
169
|
|
145
170
|
LOG_FORMAT: str = "%(levelname)s:%(name)s:%(message)s"
|
146
171
|
LOG_FORMAT__DOC = (
|
@@ -148,41 +173,50 @@ LOG_FORMAT__DOC = (
|
|
148
173
|
"https://docs.python.org/3/library/logging.html#logrecord-attributes "
|
149
174
|
"for available options."
|
150
175
|
)
|
176
|
+
LOG_FORMAT__CATEGORY = _Categories.LOG
|
151
177
|
|
152
178
|
MAM_MAX_DAYS = 7
|
153
179
|
MAM_MAX_DAYS__DOC = "Maximum number of days for group archive retention."
|
180
|
+
MAM_MAX_DAYS__CATEGORY = _Categories.BASE
|
154
181
|
|
155
182
|
ATTACHMENT_MAXIMUM_FILE_NAME_LENGTH = 200
|
156
183
|
ATTACHMENT_MAXIMUM_FILE_NAME_LENGTH__DOC = (
|
157
184
|
"Some legacy network provide ridiculously long filenames, strip above this limit, "
|
158
185
|
"preserving suffix."
|
159
186
|
)
|
187
|
+
ATTACHMENT_MAXIMUM_FILE_NAME_LENGTH__CATEGORY = _Categories.ATTACHMENTS
|
160
188
|
|
161
189
|
AVATAR_RESAMPLING_THREADS = 2
|
162
190
|
AVATAR_RESAMPLING_THREADS__DOC = (
|
163
191
|
"Number of additional threads to use for avatar resampling. Even in a single-core "
|
164
192
|
"context, this makes avatar resampling non-blocking."
|
165
193
|
)
|
194
|
+
AVATAR_RESAMPLING_THREADS__CATEGORY = _Categories.ADVANCED
|
166
195
|
|
167
196
|
DEV_MODE = False
|
168
197
|
DEV_MODE__DOC = (
|
169
198
|
"Enables an interactive python shell via chat commands, for admins."
|
170
199
|
"Not safe to use in prod, but great during dev."
|
171
200
|
)
|
201
|
+
DEV_MODE__CATEGORY = _Categories.ADVANCED
|
202
|
+
|
172
203
|
|
173
204
|
STRIP_LEADING_EMOJI_ADHOC = False
|
174
205
|
STRIP_LEADING_EMOJI_ADHOC__DOC = (
|
175
206
|
"Strip the leading emoji in ad-hoc command names, if present, in case you "
|
176
207
|
"are a emoji-hater."
|
177
208
|
)
|
209
|
+
STRIP_LEADING_EMOJI_ADHOC__CATEGORY = _Categories.ADVANCED
|
178
210
|
|
179
211
|
COMPONENT_NAME: Optional[str] = None
|
180
212
|
COMPONENT_NAME__DOC = (
|
181
213
|
"Overrides the default component name with a custom one. This is seen in service discovery and as the nickname "
|
182
214
|
"of the component in chat windows."
|
183
215
|
)
|
216
|
+
COMPONENT_NAME__CATEGORY = _Categories.ADVANCED
|
184
217
|
|
185
218
|
WELCOME_MESSAGE: Optional[str] = None
|
186
219
|
WELCOME_MESSAGE__DOC = (
|
187
220
|
"Overrides the default welcome message received by newly registered users."
|
188
221
|
)
|
222
|
+
WELCOME_MESSAGE__CATEGORY = _Categories.ADVANCED
|
@@ -3,7 +3,6 @@ from typing import TYPE_CHECKING
|
|
3
3
|
from slixmpp import JID, CoroutineCallback, Iq, StanzaPath
|
4
4
|
from slixmpp.exceptions import XMPPError
|
5
5
|
|
6
|
-
from ...db.models import GatewayUser
|
7
6
|
from .util import DispatcherMixin, exceptions_to_xmpp_errors
|
8
7
|
|
9
8
|
if TYPE_CHECKING:
|
@@ -32,10 +31,7 @@ class SearchMixin(DispatcherMixin):
|
|
32
31
|
"""
|
33
32
|
Prepare the search form using :attr:`.BaseSession.SEARCH_FIELDS`
|
34
33
|
"""
|
35
|
-
|
36
|
-
user = orm.query(GatewayUser).one_or_none()
|
37
|
-
if user is None:
|
38
|
-
raise XMPPError(text="Search is only allowed for registered users")
|
34
|
+
await self._get_session(iq)
|
39
35
|
|
40
36
|
xmpp = self.xmpp
|
41
37
|
|
@@ -172,7 +172,9 @@ def exceptions_to_xmpp_errors(cb: HandlerType) -> HandlerType:
|
|
172
172
|
except NotImplementedError:
|
173
173
|
log.debug("NotImplementedError raised in %s", cb)
|
174
174
|
raise XMPPError(
|
175
|
-
"feature-not-implemented",
|
175
|
+
"feature-not-implemented",
|
176
|
+
f"{cb.__name__} is not implemented by the legacy module",
|
177
|
+
clear=False,
|
176
178
|
)
|
177
179
|
except Exception as e:
|
178
180
|
log.error("Failed to handle incoming stanza: %s", args, exc_info=e)
|
@@ -330,6 +330,7 @@ class BaseGateway(
|
|
330
330
|
self.jid_validator: re.Pattern = re.compile(config.USER_JID_VALIDATOR)
|
331
331
|
self.qr_pending_registrations = dict[str, asyncio.Future[Optional[dict]]]()
|
332
332
|
|
333
|
+
self.register_plugins()
|
333
334
|
self.__setup_legacy_module_subclasses()
|
334
335
|
|
335
336
|
self.get_session_from_stanza: Callable[
|
@@ -339,7 +340,6 @@ class BaseGateway(
|
|
339
340
|
self._session_cls.from_user
|
340
341
|
)
|
341
342
|
|
342
|
-
self.register_plugins()
|
343
343
|
self.__register_slixmpp_events()
|
344
344
|
self.__register_slixmpp_api()
|
345
345
|
self.roster.set_backend(RosterBackend(self))
|
@@ -390,6 +390,16 @@ class BaseGateway(
|
|
390
390
|
bookmarks_cls = LegacyBookmarks.get_self_or_unique_subclass()
|
391
391
|
roster_cls = LegacyRoster.get_self_or_unique_subclass()
|
392
392
|
|
393
|
+
if contact_cls.REACTIONS_SINGLE_EMOJI: # type:ignore[attr-defined]
|
394
|
+
form = Form()
|
395
|
+
form["type"] = "result"
|
396
|
+
form.add_field(
|
397
|
+
"FORM_TYPE", "hidden", value="urn:xmpp:reactions:0:restrictions"
|
398
|
+
)
|
399
|
+
form.add_field("max_reactions_per_user", value="1", type="number")
|
400
|
+
form.add_field("scope", value="domain")
|
401
|
+
self.plugin["xep_0128"].add_extended_info(data=form)
|
402
|
+
|
393
403
|
session_cls.xmpp = self # type:ignore[attr-defined]
|
394
404
|
contact_cls.xmpp = self # type:ignore[attr-defined]
|
395
405
|
muc_cls.xmpp = self # type:ignore[attr-defined]
|
@@ -1002,6 +1012,7 @@ SLIXMPP_PLUGINS = [
|
|
1002
1012
|
"xep_0106", # JID Escaping
|
1003
1013
|
"xep_0115", # Entity capabilities
|
1004
1014
|
"xep_0122", # Data Forms Validation
|
1015
|
+
"xep_0128", # Service Discovery Extensions
|
1005
1016
|
"xep_0153", # vCard-Based Avatars (for MUC avatars)
|
1006
1017
|
"xep_0172", # User nickname
|
1007
1018
|
"xep_0184", # Message Delivery Receipts
|
@@ -162,15 +162,12 @@ class AttachmentMixin(TextMessageMixin):
|
|
162
162
|
legacy_file_id=None
|
163
163
|
if attachment.legacy_file_id is None
|
164
164
|
else str(attachment.legacy_file_id),
|
165
|
-
url=attachment.url,
|
165
|
+
url=attachment.url if config.USE_ATTACHMENT_ORIGINAL_URLS else None,
|
166
166
|
)
|
167
167
|
|
168
168
|
async def __get_url(
|
169
169
|
self, attachment: LegacyAttachment, stored: Attachment
|
170
170
|
) -> tuple[bool, Optional[Path], str]:
|
171
|
-
if attachment.url and config.USE_ATTACHMENT_ORIGINAL_URLS:
|
172
|
-
return False, None, attachment.url
|
173
|
-
|
174
171
|
file_name = attachment.name
|
175
172
|
content_type = attachment.content_type
|
176
173
|
file_path = attachment.path
|
@@ -221,7 +218,9 @@ class AttachmentMixin(TextMessageMixin):
|
|
221
218
|
|
222
219
|
assert isinstance(file_path, Path)
|
223
220
|
if config.FIX_FILENAME_SUFFIX_MIME_TYPE:
|
224
|
-
file_name =
|
221
|
+
file_name, content_type = fix_suffix(file_path, content_type, file_name)
|
222
|
+
attachment.content_type = content_type
|
223
|
+
attachment.name = file_name
|
225
224
|
|
226
225
|
if config.NO_UPLOAD_PATH:
|
227
226
|
local_path, new_url = await self.__no_upload(
|
@@ -312,6 +311,50 @@ class AttachmentMixin(TextMessageMixin):
|
|
312
311
|
stored.sfs = str(sfs)
|
313
312
|
msg.append(sfs)
|
314
313
|
|
314
|
+
async def __set_sfs_and_sims_without_download(
|
315
|
+
self, msg: Message, attachment: LegacyAttachment
|
316
|
+
) -> None:
|
317
|
+
assert attachment.url is not None
|
318
|
+
|
319
|
+
if not any(
|
320
|
+
(
|
321
|
+
attachment.content_type,
|
322
|
+
attachment.name,
|
323
|
+
attachment.disposition,
|
324
|
+
)
|
325
|
+
):
|
326
|
+
return
|
327
|
+
|
328
|
+
sims = self.xmpp.plugin["xep_0385"].stanza.Sims()
|
329
|
+
ref = self.xmpp["xep_0372"].stanza.Reference()
|
330
|
+
|
331
|
+
ref["uri"] = attachment.url
|
332
|
+
ref["type"] = "data"
|
333
|
+
sims["sources"].append(ref)
|
334
|
+
sims.enable("file")
|
335
|
+
|
336
|
+
xep_0447_stanza = self.xmpp.plugin["xep_0447"].stanza
|
337
|
+
sfs = xep_0447_stanza.StatelessFileSharing()
|
338
|
+
url_data = xep_0447_stanza.UrlData()
|
339
|
+
url_data["target"] = attachment.url
|
340
|
+
sfs["sources"].append(url_data)
|
341
|
+
sfs.enable("file")
|
342
|
+
|
343
|
+
if attachment.content_type:
|
344
|
+
sims["file"]["media-type"] = attachment.content_type
|
345
|
+
sfs["file"]["media-type"] = attachment.content_type
|
346
|
+
if attachment.caption:
|
347
|
+
sims["file"]["desc"] = attachment.caption
|
348
|
+
sfs["file"]["desc"] = attachment.caption
|
349
|
+
if attachment.name:
|
350
|
+
sims["file"]["name"] = attachment.name
|
351
|
+
sfs["file"]["name"] = attachment.name
|
352
|
+
if attachment.disposition:
|
353
|
+
sfs["disposition"] = attachment.disposition
|
354
|
+
|
355
|
+
msg.append(sims)
|
356
|
+
msg.append(sfs)
|
357
|
+
|
315
358
|
def __send_url(
|
316
359
|
self,
|
317
360
|
msg: Message,
|
@@ -325,6 +368,9 @@ class AttachmentMixin(TextMessageMixin):
|
|
325
368
|
) -> list[Message]:
|
326
369
|
msg["oob"]["url"] = uploaded_url
|
327
370
|
msg["body"] = uploaded_url
|
371
|
+
if msg.get_plugin("sfs", check=True):
|
372
|
+
msg["fallback"].enable("body")
|
373
|
+
msg["fallback"]["for"] = self.xmpp.plugin["xep_0447"].stanza.NAMESPACE
|
328
374
|
if caption:
|
329
375
|
m1 = self._send(msg, carbon=carbon, **kwargs)
|
330
376
|
m2 = self.send_text(
|
@@ -465,7 +511,10 @@ class AttachmentMixin(TextMessageMixin):
|
|
465
511
|
new_url = stored.url
|
466
512
|
else:
|
467
513
|
is_temp, local_path, new_url = await self.__get_url(attachment, stored)
|
468
|
-
if new_url is None
|
514
|
+
if new_url is None or (
|
515
|
+
local_path is not None and local_path.stat().st_size == 0
|
516
|
+
):
|
517
|
+
log.warning("Something went wrong with this attachment: %s", attachment)
|
469
518
|
msg["body"] = (
|
470
519
|
"I tried to send a file, but something went wrong. "
|
471
520
|
"Tell your slidge admin to check the logs."
|
@@ -474,8 +523,13 @@ class AttachmentMixin(TextMessageMixin):
|
|
474
523
|
return None, [self._send(msg, **kwargs)]
|
475
524
|
|
476
525
|
stored.url = new_url
|
477
|
-
|
478
|
-
|
526
|
+
if config.USE_ATTACHMENT_ORIGINAL_URLS and attachment.url:
|
527
|
+
await self.__set_sfs_and_sims_without_download(msg, attachment)
|
528
|
+
else:
|
529
|
+
thumbnail = await self.__set_sims(
|
530
|
+
msg, new_url, local_path, attachment, stored
|
531
|
+
)
|
532
|
+
self.__set_sfs(msg, new_url, local_path, attachment, stored, thumbnail)
|
479
533
|
|
480
534
|
if self.session is not NotImplemented:
|
481
535
|
with self.xmpp.store.session(expire_on_commit=False) as orm:
|
@@ -112,7 +112,7 @@ class MessageMaker(BaseSender):
|
|
112
112
|
if when.tzinfo is None:
|
113
113
|
when = when.astimezone(timezone.utc)
|
114
114
|
if self.STRIP_SHORT_DELAY:
|
115
|
-
delay = datetime.now().astimezone(timezone.utc) - when
|
115
|
+
delay = (datetime.now().astimezone(timezone.utc) - when).seconds
|
116
116
|
if delay < config.IGNORE_DELAY_THRESHOLD:
|
117
117
|
return
|
118
118
|
msg["delay"].set_stamp(when)
|
@@ -191,7 +191,7 @@ class TextMessageMixin(MessageMaker):
|
|
191
191
|
xmpp_id = kwargs.pop("xmpp_id", None)
|
192
192
|
if not xmpp_id:
|
193
193
|
xmpp_id = self._legacy_to_xmpp(legacy_msg_id)
|
194
|
-
self.xmpp["xep_0444"].set_reactions(msg, to_id=xmpp_id, reactions=emojis)
|
194
|
+
self.xmpp["xep_0444"].set_reactions(msg, to_id=xmpp_id, reactions=set(emojis))
|
195
195
|
self.__add_reaction_fallback(msg, legacy_msg_id, emojis)
|
196
196
|
self._send(msg, **kwargs)
|
197
197
|
|
@@ -46,6 +46,8 @@ def _clear_last_seen_task(contact_pk: int, _task) -> None:
|
|
46
46
|
class PresenceMixin(BaseSender, DBMixin):
|
47
47
|
_ONLY_SEND_PRESENCE_CHANGES = False
|
48
48
|
|
49
|
+
# this attribute actually only exists for contacts and not participants
|
50
|
+
_updating_info: bool
|
49
51
|
stored: Contact | Participant
|
50
52
|
|
51
53
|
def __init__(self, *a, **k) -> None:
|
@@ -55,15 +57,24 @@ class PresenceMixin(BaseSender, DBMixin):
|
|
55
57
|
# to DB at the end of update_info()
|
56
58
|
self.cached_presence: Optional[CachedPresence] = None
|
57
59
|
|
60
|
+
def __is_contact(self) -> bool:
|
61
|
+
return isinstance(self.stored, Contact)
|
62
|
+
|
58
63
|
def __stored(self) -> Contact | None:
|
59
|
-
if
|
64
|
+
if self.__is_contact():
|
65
|
+
assert isinstance(self.stored, Contact)
|
60
66
|
return self.stored
|
61
67
|
else:
|
68
|
+
assert isinstance(self.stored, Participant)
|
62
69
|
try:
|
63
70
|
return self.stored.contact
|
64
71
|
except DetachedInstanceError:
|
65
72
|
with self.xmpp.store.session() as orm:
|
66
73
|
orm.add(self.stored)
|
74
|
+
if self.stored.contact is None:
|
75
|
+
return None
|
76
|
+
orm.refresh(self.stored.contact)
|
77
|
+
orm.merge(self.stored)
|
67
78
|
return self.stored.contact
|
68
79
|
|
69
80
|
@property
|
@@ -85,15 +96,22 @@ class PresenceMixin(BaseSender, DBMixin):
|
|
85
96
|
)
|
86
97
|
|
87
98
|
def _store_last_presence(self, new: CachedPresence) -> None:
|
88
|
-
|
89
|
-
if
|
90
|
-
|
91
|
-
|
92
|
-
|
99
|
+
stored_contact = self.__stored()
|
100
|
+
if stored_contact is None:
|
101
|
+
return
|
102
|
+
stored_contact.cached_presence = True
|
103
|
+
for k, v in new._asdict().items():
|
104
|
+
setattr(stored_contact, k, v)
|
105
|
+
if self.__is_contact() and self._updating_info:
|
106
|
+
return
|
107
|
+
with self.xmpp.store.session(expire_on_commit=False) as orm:
|
93
108
|
try:
|
94
|
-
|
109
|
+
orm.add(stored_contact)
|
95
110
|
except InvalidRequestError:
|
96
|
-
|
111
|
+
stored_contact = orm.merge(stored_contact)
|
112
|
+
orm.add(stored_contact)
|
113
|
+
|
114
|
+
orm.commit()
|
97
115
|
|
98
116
|
def _make_presence(
|
99
117
|
self,
|