Exemple #1
0
 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
Exemple #2
0
 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
Exemple #3
0
 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
Exemple #4
0
 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))
Exemple #5
0
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()