diff --git a/gateway/platforms/matrix.py b/gateway/platforms/matrix.py index f7837a1f7d69e..1144a92876360 100644 --- a/gateway/platforms/matrix.py +++ b/gateway/platforms/matrix.py @@ -39,6 +39,11 @@ from pathlib import Path from typing import Any, Dict, Optional, Set +try: + import fresholm.import_hook # noqa: F401 — intercepts ``import olm`` for mautrix E2EE +except ImportError: + pass + try: from mautrix.types import ( ContentURI, @@ -138,8 +143,7 @@ def __init__(self, session_key: str, chat_id: str, message_id: str, resolved: bo ) _E2EE_INSTALL_HINT = ( - "Install with: pip install 'mautrix[encryption]' asyncpg aiosqlite " - "(requires libolm C library)" + "Install with: pip install 'mautrix>=0.20' fresholm unpaddedbase64 pycryptodome base58 asyncpg aiosqlite" ) _MATRIX_IMAGE_FILENAME_EXTS = frozenset({ @@ -217,16 +221,18 @@ def _create_matrix_session(proxy_url: str | None): def _check_e2ee_deps() -> bool: """Return True if mautrix E2EE dependencies are available. - Verifies python-olm (via mautrix.crypto.OlmMachine), the SQLite crypto - store backend (mautrix.crypto.store.asyncpg.PgCryptoStore — yes, the - PgCryptoStore class also drives the sqlite backend in mautrix 0.21), - and the database drivers actually used at connect time (``asyncpg`` for - the underlying upgrade_table machinery, ``aiosqlite`` for the - ``sqlite:///`` URL we pass to ``Database.create``). Without all four, - encrypted rooms fail at connect time with a confusing - ``No module named 'asyncpg'`` (#31116). + Checks for fresholm (Rust/vodozemac, preferred) or python-olm (legacy), + plus the non-olm encryption deps that ``mautrix[encryption]`` would install + (unpaddedbase64, pycryptodome, base58). Without these, + ``mautrix.crypto.signature`` blows up at import time with + ``ModuleNotFoundError: No module named 'unpaddedbase64'``. + + Also verifies the SQLite crypto store backend and database drivers. """ try: + import unpaddedbase64 # noqa: F401 + import Crypto # pycryptodome # noqa: F401 + import base58 # noqa: F401 from mautrix.crypto import OlmMachine # noqa: F401 from mautrix.crypto.store.asyncpg import PgCryptoStore # noqa: F401 import asyncpg # noqa: F401 @@ -234,7 +240,15 @@ def _check_e2ee_deps() -> bool: return True except (ImportError, AttributeError): - return False + # Fresholm import hook may not have run yet (e.g. early startup). + # Try activating it explicitly and retrying. + try: + import fresholm.import_hook # noqa: F401 + from mautrix.crypto import OlmMachine # noqa: F401 + + return True + except (ImportError, AttributeError): + return False def check_matrix_requirements() -> bool: diff --git a/pyproject.toml b/pyproject.toml index 327088d3999d8..9f23b1342909d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -87,7 +87,7 @@ dev = ["debugpy==1.8.20", "pytest==9.0.2", "pytest-asyncio==1.3.0", "pytest-time messaging = ["python-telegram-bot[webhooks]==22.6", "discord.py[voice]==2.7.1", "aiohttp==3.13.3", "brotlicffi==1.2.0.1", "slack-bolt==1.27.0", "slack-sdk==3.40.1", "qrcode==7.4.2"] cron = [] # croniter is now a core dependency; this extra kept for back-compat slack = ["slack-bolt==1.27.0", "slack-sdk==3.40.1", "aiohttp==3.13.3"] -matrix = ["mautrix[encryption]==0.21.0", "Markdown==3.10.2", "aiosqlite==0.22.1", "asyncpg==0.31.0", "aiohttp-socks==0.11.0"] +matrix = ["mautrix>=0.20,<1", "fresholm>=0.2.4", "unpaddedbase64>=1.0.0", "pycryptodome>=3.0", "base58>=2.0.0", "Markdown==3.10.2", "aiosqlite==0.22.1", "asyncpg==0.31.0", "aiohttp-socks==0.11.0"] # WeCom callback-mode adapter — parses untrusted XML POST bodies from # WeCom-controlled callback endpoints, so we use defusedxml (drop-in # replacement for stdlib xml.etree.ElementTree) to block billion-laughs