def __init__(self, bridge: 'Bridge', homeserver_address: str, user_id_prefix: str, user_id_suffix: str, db_url: str, key_sharing_config: Dict[str, bool] = None ) -> None: self.loop = bridge.loop or asyncio.get_event_loop() self.bridge = bridge self.az = bridge.az self.device_name = bridge.name self._id_prefix = user_id_prefix self._id_suffix = user_id_suffix self._share_session_events = {} self.key_sharing_config = key_sharing_config or {} pickle_key = "mautrix.bridge.e2ee" if db_url.startswith("postgres://"): if not PgCryptoStore or not PgCryptoStateStore: raise RuntimeError("Database URL is set to postgres, but asyncpg is not installed") self.crypto_db = Database(url=db_url, upgrade_table=PgCryptoStore.upgrade_table, log=logging.getLogger("mau.crypto.db"), loop=self.loop) self.crypto_store = PgCryptoStore("", pickle_key, self.crypto_db) self.state_store = PgCryptoStateStore(self.crypto_db, bridge.get_portal) elif db_url.startswith("pickle:///"): self.crypto_db = None self.crypto_store = PickleCryptoStore("", pickle_key, db_url[len("pickle:///"):]) self.state_store = SQLCryptoStateStore(bridge.get_portal) else: raise RuntimeError("Unsupported database scheme") self.client = Client(base_url=homeserver_address, mxid=self.az.bot_mxid, loop=self.loop, sync_store=self.crypto_store) self.crypto = OlmMachine(self.client, self.crypto_store, self.state_store) self.crypto.allow_key_share = self.allow_key_share
def _prepare_crypto(self) -> None: self.crypto_store = PgCryptoStore(account_id=self.id, pickle_key="mau.crypto", db=self.maubot.crypto_db) self.crypto = OlmMachine( self.client, self.crypto_store, self.maubot.state_store, log=self.client.crypto_log, ) self.client.crypto = self.crypto
def __init__( self, bridge: br.Bridge, homeserver_address: str, user_id_prefix: str, user_id_suffix: str, db_url: str, key_sharing_config: dict[str, bool] = None, ) -> None: self.loop = bridge.loop or asyncio.get_event_loop() self.bridge = bridge self.az = bridge.az self.device_name = bridge.name self._id_prefix = user_id_prefix self._id_suffix = user_id_suffix self._share_session_events = {} self.key_sharing_config = key_sharing_config or {} pickle_key = "mautrix.bridge.e2ee" self.crypto_db = Database.create( url=db_url, upgrade_table=PgCryptoStore.upgrade_table, log=logging.getLogger("mau.crypto.db"), ) self.crypto_store = PgCryptoStore("", pickle_key, self.crypto_db) self.state_store = PgCryptoStateStore(self.crypto_db, bridge.get_portal) default_http_retry_count = bridge.config.get( "homeserver.http_retry_count", None) self.client = Client( base_url=homeserver_address, mxid=self.az.bot_mxid, loop=self.loop, sync_store=self.crypto_store, log=self.log.getChild("client"), default_retry_count=default_http_retry_count, ) self.crypto = OlmMachine(self.client, self.crypto_store, self.state_store) self.client.add_event_handler(InternalEventType.SYNC_STOPPED, self._exit_on_sync_fail) self.crypto.allow_key_share = self.allow_key_share
def __init__(self, db_instance: DBClient) -> None: self.db_instance = db_instance self.cache[self.id] = self self.log = log.getChild(self.id) self.references = set() self.started = False self.sync_ok = True self.remote_displayname = None self.remote_avatar_url = None self.client = MaubotMatrixClient(mxid=self.id, base_url=self.homeserver, token=self.access_token, client_session=self.http_client, log=self.log, loop=self.loop, device_id=self.device_id, sync_store=SyncStoreProxy( self.db_instance), state_store=self.global_state_store) if OlmMachine and self.device_id and (self.crypto_db or self.crypto_pickle_dir): self.crypto_store = self._make_crypto_store() self.crypto = OlmMachine(self.client, self.crypto_store, self.global_state_store) self.client.crypto = self.crypto else: self.crypto_store = None self.crypto = None self.client.ignore_initial_sync = True self.client.ignore_first_sync = True self.client.presence = PresenceState.ONLINE if self.online else PresenceState.OFFLINE if self.autojoin: self.client.add_event_handler(EventType.ROOM_MEMBER, self._handle_invite) self.client.add_event_handler(EventType.ROOM_TOMBSTONE, self._handle_tombstone) self.client.add_event_handler(InternalEventType.SYNC_ERRORED, self._set_sync_ok(False)) self.client.add_event_handler(InternalEventType.SYNC_SUCCESSFUL, self._set_sync_ok(True))
async def main(): http_client = ClientSession(loop=loop) global client, bot await db.start() nb = await NextBatch(db, user_id).load() client_log = logging.getLogger("maubot.client").getChild(user_id) client = MaubotMatrixClient( mxid=user_id, base_url=homeserver, token=access_token, client_session=http_client, loop=loop, log=client_log, sync_store=nb, state_store=state_store, device_id=device_id, ) client.ignore_first_sync = config["user.ignore_first_sync"] client.ignore_initial_sync = config["user.ignore_initial_sync"] if crypto_store: await crypto_store.upgrade_table.upgrade(db) await state_store.upgrade_table.upgrade(db) await crypto_store.open() client.crypto = OlmMachine(client, crypto_store, state_store) crypto_device_id = await crypto_store.get_device_id() if crypto_device_id and crypto_device_id != device_id: log.fatal("Mismatching device ID in crypto store and config " f"(store: {crypto_device_id}, config: {device_id})") sys.exit(10) await client.crypto.load() if not crypto_device_id: await crypto_store.put_device_id(device_id) log.debug("Enabled encryption support") if web_runner: await web_runner.setup() site = web.TCPSite(web_runner, config["server.hostname"], config["server.port"]) await site.start() log.info(f"Web server listening on {site.name}") while True: try: whoami = await client.whoami() except Exception as e: log.error( f"Failed to connect to homeserver: {type(e).__name__}: {e}" " - retrying in 10 seconds...") await asyncio.sleep(10) continue if whoami.user_id != user_id: log.fatal( f"User ID mismatch: configured {user_id}, but server said {whoami.user_id}" ) sys.exit(11) elif whoami.device_id and device_id and whoami.device_id != device_id: log.fatal(f"Device ID mismatch: configured {device_id}, " f"but server said {whoami.device_id}") sys.exit(12) log.debug( f"Confirmed connection as {whoami.user_id} / {whoami.device_id}") break if config["user.sync"]: if not nb.filter_id: filter_id = await client.create_filter( Filter(room=RoomFilter(timeline=RoomEventFilter(limit=50)))) await nb.put_filter_id(filter_id) _ = client.start(nb.filter_id) if config["user.autojoin"]: log.debug("Autojoin is enabled") @client.on(EventType.ROOM_MEMBER) async def _handle_invite(evt: StrippedStateEvent) -> None: if evt.state_key == client.mxid and evt.content.membership == Membership.INVITE: await client.join_room(evt.room_id) displayname, avatar_url = config["user.displayname"], config[ "user.avatar_url"] if avatar_url != "disable": await client.set_avatar_url(avatar_url) if displayname != "disable": await client.set_displayname(displayname) plugin_log = cast(TraceLogger, logging.getLogger("maubot.instance.__main__")) if meta.database: if meta.database_type == DatabaseType.SQLALCHEMY: import sqlalchemy as sql plugin_db = sql.create_engine(config["database"]) if db.scheme == Scheme.SQLITE: log.warning( "Using SQLite with legacy plugins in standalone mode can cause database " "locking issues. Switching to Postgres or updating the plugin to use the " "new asyncpg/aiosqlite database interface is recommended.") elif meta.database_type == DatabaseType.ASYNCPG: plugin_db = db upgrade_table = plugin.get_db_upgrade_table() if upgrade_table: await upgrade_table.upgrade(plugin_db) else: log.fatal(f"Unsupported database type {meta.database_type}") sys.exit(13) else: plugin_db = None bot = plugin( client=client, loop=loop, http=http_client, instance_id="__main__", log=plugin_log, config=bot_config, database=plugin_db, webapp=plugin_webapp, webapp_url=public_url, loader=loader, ) await bot.internal_start()