#!/usr/bin/env python3 """ Matrix Helper Utilities Shared utilities for Matrix client operations, reducing code duplication across matrix_mcp.py and matrix_integration.py. """ import json import logging from contextlib import asynccontextmanager from pathlib import Path from typing import AsyncIterator try: from nio import AsyncClient except ImportError: raise ImportError( "matrix-nio not installed. Install with: pip3 install matrix-nio\n" "Note: Without [e2e] extra, only unencrypted rooms are supported" ) # Configuration CREDENTIALS_FILE = Path.home() / ".matrix-credentials.json" MATRIX_DATA_DIR = Path.home() / ".matrix-data" logger = logging.getLogger(__name__) def load_credentials() -> dict: """Load Matrix credentials from JSON file""" if not CREDENTIALS_FILE.exists(): raise FileNotFoundError( f"Credentials file not found: {CREDENTIALS_FILE}\n" f"Run setup_matrix.sh to create credentials" ) try: with open(CREDENTIALS_FILE, 'r') as f: return json.load(f) except Exception as e: logger.error(f"Failed to load credentials: {e}") raise @asynccontextmanager async def matrix_client() -> AsyncIterator[AsyncClient]: """ Async context manager for Matrix client operations. Handles client setup, session restoration, and cleanup automatically. Ensures client.close() is always called, preventing resource leaks. Usage: async with matrix_client() as client: response = await client.room_send(...) Yields: AsyncClient: Configured and authenticated Matrix client """ creds = load_credentials() # Create data directory for E2EE store MATRIX_DATA_DIR.mkdir(exist_ok=True) client = AsyncClient( homeserver=creds['homeserver'], user=creds['user_id'], store_path=str(MATRIX_DATA_DIR), ) # Restore session from credentials client.access_token = creds['access_token'] client.device_id = creds['device_id'] try: yield client finally: await client.close()