Example #1
0
 def __init__(self,
              bot_mxid: UserID,
              login_shared_secret: str,
              homeserver_address: str,
              user_id_prefix: str,
              user_id_suffix: str,
              device_name: str,
              loop: Optional[asyncio.AbstractEventLoop] = None) -> None:
     self.loop = loop or asyncio.get_event_loop()
     self.bot_mxid = bot_mxid
     self.device_name = device_name
     self._id_prefix = user_id_prefix
     self._id_suffix = user_id_suffix
     self.login_shared_secret = login_shared_secret.encode("utf-8")
     config = AsyncClientConfig(store=NioStore,
                                encryption_enabled=True,
                                pickle_key="mautrix-python",
                                store_sync_tokens=True)
     device_id = DBAccount.first_device_id(self.bot_mxid)
     if device_id:
         self.log.debug(f"Found device ID in database: {device_id}")
     self.client = AsyncClient(homeserver=homeserver_address,
                               user=bot_mxid,
                               device_id=device_id,
                               config=config,
                               store_path="3:<")
Example #2
0
async def main():
    # Read config file
    config = Config("config.yaml")

    # Configuration options for the AsyncClient
    nio_client_config = AsyncClientConfig(
        max_limit_exceeded=0,
        max_timeouts=0,
        store_sync_tokens=True,
    )

    # Initialize the matrix client
    nio_client = AsyncClient(
        config.homeserver_url,
        config.user_id,
        device_id=config.device_id,
        config=nio_client_config,
        store_path="/home/brendan/Documents/matrix-monzo-next/nio_store",
    )

    # Initialise the monzo client
    monzo_client = Monzo(config.monzo_access_token)

    await nio_client.login(config.password, "monzo_bot")

    # Set up event callbacks
    callbacks = Callbacks(config, nio_client, monzo_client)
    nio_client.add_event_callback(callbacks.message, (RoomMessageText, ))
    nio_client.add_event_callback(callbacks.invite, (InviteEvent, ))

    # First do a sync with full_state = true to retrieve the state of the room.
    await nio_client.sync(full_state=True)

    await nio_client.sync_forever(30000)
Example #3
0
async def create_client() -> AsyncClient:
    homeserver = ARGS.server
    user_id = ARGS.user
    password = ARGS.userpass
    if not ARGS.batch:
        homeserver = input(
            f"Enter URL of your homeserver: [{homeserver}] ") or homeserver
        user_id = input(f"Enter your full user ID: [{user_id}] ") or user_id
        password = getpass.getpass()
    client = AsyncClient(
        homeserver=homeserver,
        user=user_id,
        config=AsyncClientConfig(store=store.SqliteMemoryStore),
    )
    await client.login(password, DEVICE_NAME)
    client.load_store()
    room_keys_path = ARGS.keys
    room_keys_password = ARGS.keyspass
    if not ARGS.batch:
        room_keys_path = input(
            f"Enter full path to room E2E keys: [{room_keys_path}] "
        ) or room_keys_path
        room_keys_password = getpass.getpass("Room keys password: "******"Importing keys. This may take a while...")
    await client.import_keys(room_keys_path, room_keys_password)
    return client
Example #4
0
    async def run(self) -> None:
        client_config = AsyncClientConfig(store_sync_tokens=True,
                                          encryption_enabled=True)
        self.client = AsyncClient(config.matrix_homeserver,
                                  store_path="./store",
                                  config=client_config)

        self.client.restore_login(
            user_id=config.matrix_user_id,
            device_id=config.matrix_device_id,
            access_token=config.matrix_access_token,
        )

        if self.client.should_upload_keys:
            await self.client.keys_upload()
        if self.client.should_query_keys:
            await self.client.keys_query()
        if self.client.should_claim_keys:
            await self.client.keys_claim()
        await self.client.sync(full_state=True)

        self.client.add_event_callback(self.on_message, RoomMessageText)
        self.client.add_event_callback(self.on_invite, InviteMemberEvent)

        await self.client.sync_forever(timeout=30000)
        await self.client.close()
Example #5
0
def make_client():
    client_config = AsyncClientConfig(encryption_enabled=True)
    return AsyncClient(settings.homeserver,
                       settings.mxid,
                       device_id=settings.device_id,
                       config=client_config,
                       store_path=settings.store_dir)
Example #6
0
async def async_client_pair(tempdir, loop):
    ALICE_ID = "@alice:example.org"
    ALICE_DEVICE = "JLAFKJWSCS"

    BOB_ID = "@bob:example.org"
    BOB_DEVICE = "ASDFOEAK"

    config = AsyncClientConfig(max_timeouts=3)
    alice = AsyncClient(
        "https://example.org",
        ALICE_ID,
        ALICE_DEVICE,
        tempdir,
        config=config,
    )
    bob = AsyncClient(
        "https://example.org",
        BOB_ID,
        BOB_DEVICE,
        tempdir,
        config=config,
    )

    await alice.receive_response(
        LoginResponse(ALICE_ID, ALICE_DEVICE, "alice_1234"))
    await bob.receive_response(LoginResponse(BOB_ID, BOB_DEVICE, "bob_1234"))

    yield (alice, bob)

    await alice.close()
    await bob.close()
Example #7
0
async def init(homeserver: str, username: str, password: str,
               keyfile: Optional[str],
               keyphrase: Optional[str]) -> AsyncClient:
    config = AsyncClientConfig(store=SqliteMemoryStore, store_sync_tokens=True)
    client = AsyncClient(homeserver, username, config=config)
    response = await client.login(password)
    client.add_event_callback(event_cb, RoomMessage)
    await client.import_keys(keyfile, keyphrase)
    return client
Example #8
0
async def async_client(tempdir, loop):
    client = AsyncClient(
        "https://example.org",
        "ephemeral",
        "DEVICEID",
        tempdir,
        config=AsyncClientConfig(max_timeouts=3),
    )
    yield client

    await client.close()
Example #9
0
async def _parseandpost():
    if len(sys.argv) < 4 or len(sys.argv) > 5:
        logger.error("Usage: {} <room(s)> <file path> <message> [<additional url>]".format(sys.argv[0]))
        return

    the_rooms = re.split("[, ;]+", sys.argv[1].strip())
    the_picture_filename = sys.argv[2]
    the_message = sys.argv[3]
    if len(sys.argv) == 5:
        the_additional_link = sys.argv[4]
    else:
        the_additional_link = None

    error = _read_config()

    if error:
        logger.error(f"Error reading config: {error}")
        return

    client = None
    try:
        # Configuration options for the AsyncClient
        client_config = AsyncClientConfig(
            max_limit_exceeded=0,
            max_timeouts=0,
            store_sync_tokens=True,
            encryption_enabled=False,
        )
        client = AsyncClient(homeserver=_config_values[_config_string_url],
                             user=_config_values[_config_string_username],
                             config=client_config,
                             ssl=True
                             )
        resp = await client.login(password=_config_values[_config_string_password])
        if isinstance(resp, LoginError):
            logger.error(f"Could not login: {resp}")
            return
        await client.sync(30000, True)
        for room_id in the_rooms:
            resp = await client.join(room_id=room_id)
            if isinstance(resp, JoinError):
                logger.warning(f"Failed to join {room_id}: {resp}")
                continue
            if isinstance(resp, JoinResponse):
                internal_room_id = resp.room_id
                await _send_message(client, internal_room_id, the_picture_filename, the_message, the_additional_link)

        await client.sync(3000, True)
        await client.logout()
        return
    finally:
        await client.close()
Example #10
0
async def main():
    # Read config file
    config = Config("config.yaml")

    # Configure the database
    store = Storage(config.database_filepath)

    # Configuration options for the AsyncClient
    client_config = AsyncClientConfig(
        max_limit_exceeded=0,
        max_timeouts=0,
    )

    # Initialize the matrix client
    client = AsyncClient(
        config.homeserver_url,
        config.user_id,
        device_id=config.device_id,
        config=client_config,
    )

    logger.debug("Connected to Matrix!")

    # Assign an access token to the bot instead of logging in and creating a new device
    client.access_token = config.access_token

    # Set up event callbacks
    callbacks = Callbacks(client, store, config)
    client.add_event_callback(callbacks.message, (RoomMessageText, ))
    client.add_event_callback(callbacks.invite, (InviteEvent, ))

    # Retrieve the last sync token if it exists
    token = store.get_sync_token()

    # Sync loop
    while True:
        # Sync with the server
        sync_response = await client.sync(timeout=30000,
                                          full_state=True,
                                          since=token)

        # Check if the sync had an error
        if type(sync_response) == SyncError:
            logger.warning("Error in client sync: %s", sync_response.message)
            continue

        # Save the latest sync token
        token = sync_response.next_batch
        if token:
            store.save_sync_token(token)
    def __init__(self, join_rooms: set):
        self.STORE_PATH = os.environ['LOGIN_STORE_PATH']
        self.CONFIG_FILE = f"{self.STORE_PATH}/credentials.json"

        self.join_rooms = join_rooms
        self.client: AsyncClient = None
        self.client_config = AsyncClientConfig(
            max_limit_exceeded=0,
            max_timeouts=0,
            store_sync_tokens=True,
            encryption_enabled=True,
        )

        self.greeting_sent = False
Example #12
0
 def __init__(self, config):
     super().__init__(config)
     self.has_synced = False
     self.identity = config.BOT_IDENTITY
     for key in ('email', 'auth_dict', 'site'):
         if key not in self.identity:
             log.fatal(
                 f"You need to supply the key `{key}` for me to use. `{key}` and its value "
                 "can be found in your bot's `matrixniorc` config file.")
             sys.exit(1)
     # Store the sync token in order to avoid replay of old messages.
     config = AsyncClientConfig(store_sync_tokens=True)
     self.client = nio.AsyncClient(self.identity['site'],
                                   self.identity['email'],
                                   config=config)
Example #13
0
    def __init__(self,
                 bot: Bot,
                 home_server: str,
                 username: str,
                 access_token: str,
                 device_id: str,
                 store_filepath: str,
                 web_dir: str,
                 public_url: str,
                 display_name: str,
                 avatar_path: str,
                 debug: bool = False):

        if not os.path.exists(store_filepath):
            os.makedirs(store_filepath)

        self.debug = debug
        self.public_url = public_url
        self.web_dir = web_dir

        self.display_name = display_name
        self.avatar_path = avatar_path

        self.username = username
        self.identifier = f"@{username}:{home_server[home_server.find('//') + 2:]}"

        self.matrix = AsyncClient(home_server,
                                  self.identifier,
                                  device_id,
                                  store_path=store_filepath,
                                  config=AsyncClientConfig(
                                      encryption_enabled=True,
                                      store=SqliteStore,
                                      store_name="matrix.db",
                                      store_sync_tokens=True))
        self.matrix.access_token = access_token
        self.matrix.user_id = self.identifier

        self.matrix.add_event_callback(self.handle_message, RoomMessageText)
        self.matrix.add_event_callback(self.crypto_event, MegolmEvent)
        self.matrix.add_event_callback(self.invite_event, InviteMemberEvent)
        self.matrix.add_event_callback(self.room_event, RoomMemberEvent)

        self.matrix.restore_login(self.identifier, device_id, access_token)
        self.bot = bot

        self.log.level = logging.DEBUG
        self.log.debug(f"Initialized Matrix Bot: {self.identifier}")
Example #14
0
async def create_client() -> AsyncClient:
    homeserver = "https://matrix-client.matrix.org"
    homeserver = input(
        f"Enter URL of your homeserver: [{homeserver}] ") or homeserver
    user_id = input(f"Enter your full user ID (e.g. @user:matrix.org): ")
    password = getpass.getpass()
    client = AsyncClient(
        homeserver=homeserver,
        user=user_id,
        config=AsyncClientConfig(store=store.SqliteMemoryStore),
    )
    await client.login(password, DEVICE_NAME)
    client.load_store()
    room_keys_path = input("Enter full path to room E2E keys: ")
    room_keys_password = getpass.getpass("Room keys password: "******"Importing keys. This may take a while...")
    await client.import_keys(room_keys_path, room_keys_password)
    return client
async def main() -> None:
    matrix_homeserver = "https://matrix.org"
    matrix_user_id = "@myawesomebot:matrix.org"
    matrix_password = getpass.getpass(f"Enter password for {matrix_user_id}: ")

    client = AsyncClient(matrix_homeserver, matrix_user_id, config=AsyncClientConfig(encryption_enabled=True), store_path="./store")
    resp = await client.login(matrix_password, device_name="glagolitic-bot")
    if isinstance(resp, LoginResponse):
        print (f"""Add these parameters to config.py:

matrix_homeserver = '{matrix_homeserver}'
matrix_user_id = '{resp.user_id}'
matrix_device_id = '{resp.device_id}'
matrix_access_token = '{resp.access_token}'
""")

    else:
        print (f"Login failed: {resp}")
    await client.close()
Example #16
0
    check_if_user_is_existing, create_new_room, create_new_user,
    create_new_message, get_number_of_links_to_be_shown, get_salt_value,
    add_salt_value, check_if_room_is_existing, get_all_domains_original,
    get_room_ids)
from nlp import language_processing
from message_evaluation import evaluate_message
from response_management import generate_response
from config import Config

#set filepath of config file
config_filepath = "../config.yaml"
config = Config(config_filepath)

#AsyncClient configuration options
client_config = AsyncClientConfig(
    store_sync_tokens=True,
    encryption_enabled=True,
)

#initialize matrix client
client = AsyncClient(
    config.homeserver_url,
    config.user_id,
    config=client_config,
)

'''
The main module controls the processing of events via callback functions
1) InviteEvents --> auto-join room
2) MessageEvents --> pass message to other modules

The module also sends messages to the matrix homeserver
Example #17
0
async def main():
    # Read config file

    # A different config file path can be specified as the first command line argument
    if len(sys.argv) > 1:
        config_filepath = sys.argv[1]
    else:
        config_filepath = "config.yaml"
    config = Config(config_filepath)

    # Configure the database
    store = Storage(config.database_filepath)

    # Configuration options for the AsyncClient
    client_config = AsyncClientConfig(
        max_limit_exceeded=0,
        max_timeouts=0,
        store_sync_tokens=True,
        encryption_enabled=True,
    )

    # Initialize the matrix client
    client = AsyncClient(
        config.homeserver_url,
        config.user_id,
        device_id=config.device_id,
        store_path=config.store_filepath,
        config=client_config,
    )

    # Set up event callbacks
    callbacks = Callbacks(client, store, config)
    client.add_event_callback(callbacks.message, (RoomMessageText, ))
    client.add_event_callback(callbacks.invite, (InviteMemberEvent, ))

    # Keep trying to reconnect on failure (with some time in-between)
    while True:
        try:
            # Try to login with the configured username/password
            try:
                login_response = await client.login(
                    password=config.user_password,
                    device_name=config.device_name,
                )

                # Check if login failed
                if type(login_response) == LoginError:
                    logger.error(f"Failed to login: %s",
                                 login_response.message)
                    return False
            except LocalProtocolError as e:
                # There's an edge case here where the user hasn't installed the correct C
                # dependencies. In that case, a LocalProtocolError is raised on login.
                logger.fatal(
                    "Failed to login. Have you installed the correct dependencies? "
                    "https://github.com/poljar/matrix-nio#installation "
                    "Error: %s", e)
                return False

            # Login succeeded!

            # Sync encryption keys with the server
            # Required for participating in encrypted rooms
            if client.should_upload_keys:
                await client.keys_upload()

            logger.info(f"Logged in as {config.user_id}")
            await client.sync_forever(timeout=30000, full_state=True)

        except (ClientConnectionError, ServerDisconnectedError):
            logger.warning(
                "Unable to connect to homeserver, retrying in 15s...")

            # Sleep so we don't bombard the server with login requests
            sleep(15)
        finally:
            # Make sure to close the client connection on disconnect
            await client.close()
Example #18
0
async def main():

    # TODO: this really needs to be replaced
    # probably using https://docs.python.org/3.8/library/functools.html#functools.partial
    global client
    global plugin_loader

    # Read config file
    config = Config("config.yaml")

    # Configure the database
    store = Storage(config.database_filepath)

    # Configuration options for the AsyncClient
    client_config = AsyncClientConfig(
        max_limit_exceeded=0,
        max_timeouts=0,
        store_sync_tokens=True,
        encryption_enabled=config.enable_encryption,
    )

    # Initialize the matrix client
    client = AsyncClient(
        config.homeserver_url,
        config.user_id,
        device_id=config.device_id,
        store_path=config.store_filepath,
        config=client_config,
    )

    # instantiate the pluginLoader
    plugin_loader = PluginLoader()

    # Set up event callbacks
    callbacks = Callbacks(client, store, config, plugin_loader)
    client.add_event_callback(callbacks.message, (RoomMessageText, ))
    client.add_event_callback(callbacks.invite, (InviteEvent, ))
    client.add_event_callback(callbacks.event_unknown, (UnknownEvent, ))
    client.add_response_callback(run_plugins)

    # Keep trying to reconnect on failure (with some time in-between)
    error_retries: int = 0
    while True:
        try:
            # Try to login with the configured username/password
            try:
                login_response = await client.login(
                    password=config.user_password,
                    device_name=config.device_name,
                )

                # Check if login failed
                if type(login_response) == LoginError:
                    logger.error(
                        f"Failed to login: {login_response.message}, retrying in 15s... ({error_retries})"
                    )
                    # try logging in a few times to work around temporary login errors during homeserver restarts
                    if error_retries < 3:
                        error_retries += 1
                        await sleep(15)
                        continue
                    else:
                        return False
                else:
                    error_retries = 0

            except LocalProtocolError as e:
                # There's an edge case here where the user enables encryption but hasn't installed
                # the correct C dependencies. In that case, a LocalProtocolError is raised on login.
                # Warn the user if these conditions are met.
                if config.enable_encryption:
                    logger.fatal(
                        "Failed to login and encryption is enabled. Have you installed the correct dependencies? "
                        "https://github.com/poljar/matrix-nio#installation")
                    return False
                else:
                    # We don't know why this was raised. Throw it at the user
                    logger.fatal(f"Error logging in: {e}")

            # Login succeeded!

            # Sync encryption keys with the server
            # Required for participating in encrypted rooms
            if client.should_upload_keys:
                await client.keys_upload()

            logger.info(f"Logged in as {config.user_id}")
            await client.sync_forever(timeout=30000, full_state=True)

        except (ClientConnectionError, ServerDisconnectedError, AttributeError,
                asyncio.TimeoutError) as err:
            logger.debug(err)
            logger.warning(
                f"Unable to connect to homeserver, retrying in 15s...")

            # Sleep so we don't bombard the server with login requests
            await sleep(15)
        finally:
            # Make sure to close the client connection on disconnect
            await client.close()
Example #19
0
    def __init__(
        self,
        server_name,
        pan_store,
        pan_conf,
        homeserver,
        queue=None,
        user_id="",
        device_id="",
        store_path="",
        config=None,
        ssl=None,
        proxy=None,
        store_class=None,
    ):
        config = config or AsyncClientConfig(
            store=store_class or SqliteStore, store_name="pan.db"
        )
        super().__init__(homeserver, user_id, device_id, store_path, config, ssl, proxy)

        index_dir = os.path.join(store_path, server_name, user_id)

        try:
            os.makedirs(index_dir)
        except OSError:
            pass

        self.server_name = server_name
        self.pan_store = pan_store
        self.pan_conf = pan_conf

        if INDEXING_ENABLED:
            logger.info("Indexing enabled.")
            from pantalaimon.index import IndexStore

            self.index = IndexStore(self.user_id, index_dir)
        else:
            logger.info("Indexing disabled.")
            self.index = None

        self.task = None
        self.queue = queue

        # Those two events are mainly used for testing.
        self.new_fetch_task = asyncio.Event()
        self.fetch_loop_event = asyncio.Event()

        self.room_members_fetched = defaultdict(bool)

        self.send_semaphores = defaultdict(asyncio.Semaphore)
        self.send_decision_queues = dict()  # type: asyncio.Queue
        self.last_sync_token = None

        self.history_fetcher_task = None
        self.history_fetch_queue = asyncio.Queue()

        self.add_to_device_callback(self.key_verification_cb, KeyVerificationEvent)
        self.add_to_device_callback(
            self.key_request_cb, (RoomKeyRequest, RoomKeyRequestCancellation)
        )
        self.add_event_callback(self.undecrypted_event_cb, MegolmEvent)

        if INDEXING_ENABLED:
            self.add_event_callback(
                self.store_message_cb,
                (
                    RoomMessageText,
                    RoomMessageMedia,
                    RoomEncryptedMedia,
                    RoomTopicEvent,
                    RoomNameEvent,
                ),
            )

        self.add_response_callback(self.keys_query_cb, KeysQueryResponse)
        self.add_response_callback(self.sync_tasks, SyncResponse)
async def main():
    # Read config file
    # A different config file path can be specified as the first command line arg
    if len(sys.argv) > 1:
        config_filepath = sys.argv[1]
    else:
        config_filepath = "config.yaml"
    CONFIG.read_config(config_filepath)

    # Configure the python job scheduler
    SCHEDULER.configure({"apscheduler.timezone": CONFIG.timezone})

    # Configuration options for the AsyncClient
    client_config = AsyncClientConfig(
        max_limit_exceeded=0,
        max_timeouts=0,
        store_sync_tokens=True,
        encryption_enabled=True,
    )

    # Initialize the matrix client
    client = AsyncClient(
        CONFIG.homeserver_url,
        CONFIG.user_id,
        device_id=CONFIG.device_id,
        store_path=CONFIG.store_path,
        config=client_config,
    )

    # Configure the database
    store = Storage(client)

    # Set up event callbacks
    callbacks = Callbacks(client, store)
    client.add_event_callback(callbacks.message, (RoomMessageText,))
    client.add_event_callback(callbacks.invite, (InviteMemberEvent,))
    client.add_event_callback(callbacks.decryption_failure, (MegolmEvent,))

    # Keep trying to reconnect on failure (with some time in-between)
    while True:
        try:
            # Try to login with the configured username/password
            try:
                login_response = await client.login(
                    password=CONFIG.user_password, device_name=CONFIG.device_name,
                )

                # Check if login failed. Usually incorrect password
                if type(login_response) == LoginError:
                    logger.error("Failed to login: %s", login_response.message)
                    logger.warning("Trying again in 15s...")

                    # Sleep so we don't bombard the server with login requests
                    sleep(15)
                    continue
            except LocalProtocolError as e:
                # There's an edge case here where the user hasn't installed the correct C
                # dependencies. In that case, a LocalProtocolError is raised on login.
                logger.fatal(
                    "Failed to login. Have you installed the correct dependencies? "
                    "https://github.com/poljar/matrix-nio#installation "
                    "Error: %s",
                    e,
                )
                return False

            # Login succeeded!

            logger.info(f"Logged in as {CONFIG.user_id}")
            logger.info("Startup complete")

            # Allow jobs to fire
            try:
                SCHEDULER.start()
            except SchedulerAlreadyRunningError:
                pass

            await client.sync_forever(timeout=30000, full_state=True)

        except (ClientConnectionError, ServerDisconnectedError, TimeoutError):
            logger.warning("Unable to connect to homeserver, retrying in 15s...")

            # Sleep so we don't bombard the server with login requests
            sleep(15)
        except Exception:
            logger.exception("Unknown exception occurred:")
            logger.warning("Restarting in 15s...")

            # Sleep so we don't bombard the server with login requests
            sleep(15)
        finally:
            # Make sure to close the client connection on disconnect
            await client.close()
async def login() -> AsyncClient:
    """Handle login with or without stored credentials."""
    # Configuration options for the AsyncClient
    client_config = AsyncClientConfig(
        max_limit_exceeded=0,
        max_timeouts=0,
        store_sync_tokens=True,
        encryption_enabled=True,
    )

    # If there are no previously-saved credentials, we'll use the password
    if not os.path.exists(CONFIG_FILE):
        print("First time use. Did not find credential file. Asking for "
              "homeserver, user, and password to create credential file.")
        homeserver = "https://matrix.example.org"
        homeserver = input(f"Enter your homeserver URL: [{homeserver}] ")

        if not (homeserver.startswith("https://")
                or homeserver.startswith("http://")):
            homeserver = "https://" + homeserver

        user_id = "@user:example.org"
        user_id = input(f"Enter your full user ID: [{user_id}] ")

        device_name = "matrix-nio"
        device_name = input(f"Choose a name for this device: [{device_name}] ")

        if not os.path.exists(STORE_PATH):
            os.makedirs(STORE_PATH)

        # Initialize the matrix client
        client = AsyncClient(
            homeserver,
            user_id,
            store_path=STORE_PATH,
            config=client_config,
        )
        pw = getpass.getpass()

        resp = await client.login(password=pw, device_name=device_name)

        # check that we logged in succesfully
        if (isinstance(resp, LoginResponse)):
            write_details_to_disk(resp, homeserver)
        else:
            print(f"homeserver = \"{homeserver}\"; user = \"{user_id}\"")
            print(f"Failed to log in: {resp}")
            sys.exit(1)

        print("Logged in using a password. Credentials were stored. "
              "On next execution the stored login credentials will be used.")

    # Otherwise the config file exists, so we'll use the stored credentials
    else:
        # open the file in read-only mode
        with open(CONFIG_FILE, "r") as f:
            config = json.load(f)
            # Initialize the matrix client based on credentials from file
            client = AsyncClient(
                config['homeserver'],
                config['user_id'],
                device_id=config['device_id'],
                store_path=STORE_PATH,
                config=client_config,
            )

            client.restore_login(user_id=config['user_id'],
                                 device_id=config['device_id'],
                                 access_token=config['access_token'])
        print("Logged in using stored credentials.")

    return client
Example #22
0
async def main():
    """The first function that is run when starting the bot"""

    # Read user-configured options from a config file.
    # A different config file path can be specified as the first command line argument
    if len(sys.argv) > 1:
        config_path = sys.argv[1]
    else:
        config_path = "config.yaml"

    # Read the parsed config file and create a Config object
    config = Config(config_path)

    # Configure the database
    store = Storage(config.database)

    # Configuration options for the AsyncClient
    client_config = AsyncClientConfig(
        max_limit_exceeded=0,
        max_timeouts=0,
        store_sync_tokens=True,
        encryption_enabled=True,
    )

    # Initialize the matrix client
    client = AsyncClient(
        config.homeserver_url,
        config.user_id,
        device_id=config.device_id,
        store_path=config.store_path,
        config=client_config,
    )

    if config.user_token:
        client.access_token = config.user_token
        client.user_id = config.user_id

    # Set up event callbacks
    callbacks = Callbacks(client, store, config)
    client.add_event_callback(callbacks.invite, (InviteMemberEvent, ))
    client.add_event_callback(callbacks.decryption_failure, (MegolmEvent, ))
    client.add_response_callback(callbacks.sync, (SyncResponse, ))
    client.add_event_callback(callbacks.unknown, (UnknownEvent, ))

    # Keep trying to reconnect on failure (with some time in-between)
    while True:
        try:
            if config.user_token:
                # Use token to log in
                client.load_store()

                # Sync encryption keys with the server
                if client.should_upload_keys:
                    await client.keys_upload()
            else:
                # Try to login with the configured username/password
                try:
                    login_response = await client.login(
                        password=config.user_password,
                        device_name=config.device_name,
                    )

                    # Check if login failed
                    if type(login_response) == LoginError:
                        logger.error("Failed to login: %s",
                                     login_response.message)
                        return False
                except LocalProtocolError as e:
                    # There's an edge case here where the user hasn't installed the correct C
                    # dependencies. In that case, a LocalProtocolError is raised on login.
                    logger.fatal(
                        "Failed to login. Have you installed the correct dependencies? "
                        "https://github.com/poljar/matrix-nio#installation "
                        "Error: %s",
                        e,
                    )
                    return False

                # Login succeeded!

            logger.info(f"Logged in as {config.user_id}")
            # join the pins room we'll be writing to
            result = client.join(config.pins_room)
            if type(result) == JoinError:
                raise Exception(
                    f"Error joining pins room {config.pins_room}", )

            await client.sync_forever(timeout=30000, full_state=True)

        except Exception as e:
            logger.error("%s", e)
            logger.warning(
                "Unable to connect to homeserver, retrying in 15s...")

            # Sleep so we don't bombard the server with login requests
            sleep(15)
        finally:
            # Make sure to close the client connection on disconnect
            try:
                await client.close()
            except Exception as e2:
                logger.error("Also got exception while closing, %s", e2)
Example #23
0
async def main():
    # Read config file

    # A different config file path can be specified as the first command line argument
    if len(sys.argv) > 1:
        config_path = sys.argv[1]
    else:
        config_path = "config.yaml"
    config = Config(config_path)

    # Configure the database
    store = Storage(config.database)

    # Configuration options for the AsyncClient
    client_config = AsyncClientConfig(
        max_limit_exceeded=0,
        max_timeouts=0,
        store_sync_tokens=True,
        encryption_enabled=True,
    )

    # Initialize the matrix client
    client = AsyncClient(
        config.homeserver_url,
        config.user_id,
        device_id=config.device_id,
        store_path=config.store_path,
        config=client_config,
    )

    if config.user_token:
        client.access_token = config.user_token
        client.user_id = config.user_id

    # Set up event callbacks
    callbacks = Callbacks(client, store, config)
    # noinspection PyTypeChecker
    client.add_event_callback(callbacks.message, (RoomMessageText, ))
    # noinspection PyTypeChecker
    client.add_event_callback(callbacks.invite, (InviteMemberEvent, ))

    # Keep trying to reconnect on failure (with some time in-between)
    while True:
        try:
            if config.user_token:
                # Use token to log in
                client.load_store()

                # Sync encryption keys with the server
                if client.should_upload_keys:
                    await client.keys_upload()
            else:
                # Try to login with the configured username/password
                try:
                    login_response = await client.login(
                        password=config.user_password,
                        device_name=config.device_name,
                    )

                    # Check if login failed
                    if type(login_response) == LoginError:
                        logger.error("Failed to login: %s",
                                     login_response.message)
                        break
                except LocalProtocolError as e:
                    # There's an edge case here where the user hasn't installed the correct C
                    # dependencies. In that case, a LocalProtocolError is raised on login.
                    logger.fatal(
                        "Failed to login. Have you installed the correct dependencies? "
                        "https://github.com/poljar/matrix-nio#installation "
                        "Error: %s",
                        e,
                    )
                    break

                # Login succeeded!

            # Join the management room or fail
            response = await with_ratelimit(client, "join",
                                            config.management_room)
            if type(response) == JoinError:
                logger.fatal("Could not join the management room, aborting.")
                break
            else:
                logger.info(f"Management room membership is good")

            # Resolve management room ID if not known
            if config.management_room.startswith('#'):
                # Resolve the room ID
                response = await with_ratelimit(client, "room_resolve_alias",
                                                config.management_room)
                if type(response) == RoomResolveAliasResponse:
                    config.management_room_id = response.room_id
                else:
                    logger.fatal(
                        "Could not resolve the management room ID from alias, aborting"
                    )
                    break

            logger.info(f"Logged in as {config.user_id}")
            await client.sync_forever(timeout=30000, full_state=True)

        except (ClientConnectionError, ServerDisconnectedError):
            logger.warning(
                "Unable to connect to homeserver, retrying in 15s...")

            # Sleep so we don't bombard the server with login requests
            sleep(15)
        finally:
            # Make sure to close the client connection on disconnect
            await client.close()
Example #24
0
async def main():
    """Entry point."""
    # Read config file
    config = Config("config.yaml")

    # Configure the database
    store = Storage(config.database_filepath)

    # Configuration options for the AsyncClient
    client_config = AsyncClientConfig(
        max_limit_exceeded=0,
        max_timeouts=0,
        # store_sync_tokens=True,
        encryption_enabled=config.enable_encryption,
    )

    # Initialize the matrix client
    client = AsyncClient(
        config.homeserver_url,
        config.user_id,
        device_id=config.device_id,
        store_path=config.store_filepath,
        config=client_config,
    )

    # Assign an access token to the bot instead of logging in and creating a new device
    client.access_token = config.access_token

    # Set up event callbacks
    callbacks = Callbacks(client, store, config)
    client.add_event_callback(callbacks.message, (RoomMessageText,))
    client.add_event_callback(callbacks.invite, (InviteMemberEvent,))
    client.add_event_callback(callbacks.joined, (RoomMemberEvent,))

    # Create a new sync token, attempting to load one from the database if it has one already
    sync_token = SyncToken(store)

    # Keep trying to reconnect on failure (with some time in-between)
    while True:
        # try:
        # Try to login with the configured username/password
        # try:
        #     login_response = await client.login(
        #         password=config.user_password,
        #         device_name=config.device_name,
        #     )

        #     # Check if login failed
        #     if type(login_response) == LoginError:
        #         logger.error(f"Failed to login: %s", login_response.message)
        #         return False
        # except LocalProtocolError as e:
        #     # There's an edge case here where the user enables encryption
        #     # but hasn't installed the correct C dependencies. In that case,
        #     # a LocalProtocolError is raised on login.
        #     # Warn the user if these conditions are met.
        #     if config.enable_encryption:
        #         logger.fatal(
        #             "Failed to login and encryption is enabled. "
        #             "Have you installed the correct dependencies? "
        #             "https://github.com/poljar/matrix-nio#installation"
        #         )
        #         return False
        #     else:
        #         # We don't know why this was raised. Throw it at the user
        #         logger.fatal("Error logging in: %s", e)
        #         return False

        # Login succeeded!

        # ===============================
        # Sync encryption keys with the server
        # Required for participating in encrypted rooms
        # if client.should_upload_keys:
        #     await client.keys_upload()

        # logger.info(f"Logged in as {config.user_id}")
        # await client.sync_forever(timeout=30000, full_state=True)
        # ===============================

        # ===============================
        logger.debug("Syncing: %s", sync_token.token)
        sync_response = await client.sync(timeout=30000, since=sync_token.token)

        # Check if the sync had an errors
        if type(sync_response) == SyncError:
            logger.warning("Error in client sync: %s", sync_response.message)
            continue

        # Save the latest sync token to the database
        token = sync_response.next_batch
        if token:
            sync_token.update(token)
Example #25
0
async def main():  # noqa
    """Create bot as Matrix client and enter event loop."""
    # Read config file
    # A different config file path can be specified
    # as the first command line argument
    if len(sys.argv) > 1:
        config_filepath = sys.argv[1]
    else:
        config_filepath = "config.yaml"
    config = Config(config_filepath)

    # Configure the database
    store = Storage(config.database_filepath)

    # Configuration options for the AsyncClient
    client_config = AsyncClientConfig(
        max_limit_exceeded=0,
        max_timeouts=0,
        store_sync_tokens=True,
        encryption_enabled=True,
    )

    # Initialize the matrix client
    client = AsyncClient(
        config.homeserver_url,
        config.user_id,
        device_id=config.device_id,
        store_path=config.store_filepath,
        config=client_config,
    )

    # Set up event callbacks
    callbacks = Callbacks(client, store, config)
    client.add_event_callback(callbacks.message, (RoomMessageText, ))
    client.add_event_callback(callbacks.invite, (InviteMemberEvent, ))
    client.add_to_device_callback(callbacks.accept_all_verify,
                                  (KeyVerificationEvent, ))

    # Keep trying to reconnect on failure (with some time in-between)
    while True:
        try:
            # Try to login with the configured username/password
            try:
                if config.access_token:
                    logger.debug("Using access token from config file to log "
                                 f"in. access_token={config.access_token}")

                    client.restore_login(user_id=config.user_id,
                                         device_id=config.device_id,
                                         access_token=config.access_token)
                else:
                    logger.debug("Using password from config file to log in.")
                    login_response = await client.login(
                        password=config.user_password,
                        device_name=config.device_name,
                    )

                    # Check if login failed
                    if type(login_response) == LoginError:
                        logger.error("Failed to login: "******"{login_response.message}")
                        return False
                    logger.info((f"access_token of device {config.device_name}"
                                 f" is: \"{login_response.access_token}\""))
            except LocalProtocolError as e:
                # There's an edge case here where the user hasn't installed
                # the correct C dependencies. In that case, a
                # LocalProtocolError is raised on login.
                logger.fatal(
                    "Failed to login. "
                    "Have you installed the correct dependencies? "
                    "Error: %s", e)
                return False

            # Login succeeded!
            logger.debug(f"Logged in successfully as user {config.user_id} "
                         f"with device {config.device_id}.")
            # Sync encryption keys with the server
            # Required for participating in encrypted rooms
            if client.should_upload_keys:
                await client.keys_upload()

            if config.change_device_name:
                content = {"display_name": config.device_name}
                resp = await client.update_device(config.device_id, content)
                if isinstance(resp, UpdateDeviceError):
                    logger.debug(f"update_device failed with {resp}")
                else:
                    logger.debug(f"update_device successful with {resp}")

            await client.sync(timeout=30000, full_state=True)
            for device_id, olm_device in client.device_store[
                    config.user_id].items():
                logger.info("Setting up trust for my own "
                            f"device {device_id} and session key "
                            f"{olm_device.keys}.")
                client.verify_device(olm_device)

            await client.sync_forever(timeout=30000, full_state=True)

        except (ClientConnectionError, ServerDisconnectedError):
            logger.warning(
                "Unable to connect to homeserver, retrying in 15s...")

            # Sleep so we don't bombard the server with login requests
            sleep(15)
        finally:
            # Make sure to close the client connection on disconnect
            await client.close()
Example #26
0
async def main():
    """The first function that is run when starting the bot"""

    # Read user-configured options from a config file.
    # A different config file path can be specified as the first command line argument
    if len(sys.argv) > 1:
        config_path = sys.argv[1]
    else:
        config_path = "config.yaml"

    # Read the parsed config file and create a Config object
    config = Config(config_path)

    # Configure the database
    store = Storage(config.database)

    # Configuration options for the AsyncClient
    client_config = AsyncClientConfig(
        max_limit_exceeded=0,
        max_timeouts=0,
        store_sync_tokens=True,
        encryption_enabled=True,
    )

    # Initialize the matrix client
    client = AsyncClient(
        config.homeserver_url,
        config.user_id,
        device_id=config.device_id,
        store_path=config.store_path,
        config=client_config,
    )

    if config.user_token:
        client.access_token = config.user_token
        client.user_id = config.user_id

    # Set up event callbacks
    callbacks = Callbacks(client, store, config)
    client.add_event_callback(callbacks.message, (RoomMessageText, ))
    client.add_event_callback(callbacks.invite, (InviteMemberEvent, ))
    client.add_event_callback(callbacks.decryption_failure, (MegolmEvent, ))
    client.add_event_callback(callbacks.unknown, (UnknownEvent, ))

    # Set up a scheduler
    scheduler = AsyncIOScheduler()

    # Set up MISPAlert
    misp_alert = MISPAlert(client, config, store)

    # Add a job that checks for new taged events every minute
    trigger = IntervalTrigger(
        seconds=60,
        start_date=datetime.now() + timedelta(seconds=2),
    )

    # Add the query job
    scheduler.add_job(misp_alert.alerter, trigger=trigger)

    # Keep trying to reconnect on failure (with some time in-between)
    while True:
        try:
            if config.user_token:
                # Use token to log in
                client.load_store()

                # Sync encryption keys with the server
                if client.should_upload_keys:
                    await client.keys_upload()
            else:
                # Try to login with the configured username/password
                try:
                    login_response = await client.login(
                        password=config.user_password,
                        device_name=config.device_name,
                    )

                    # Check if login failed
                    if type(login_response) == LoginError:
                        logger.error("Failed to login: %s",
                                     login_response.message)
                        return False
                except LocalProtocolError as e:
                    # There's an edge case here where the user hasn't installed the correct C
                    # dependencies. In that case, a LocalProtocolError is raised on login.
                    logger.fatal(
                        "Failed to login. Have you installed the correct dependencies? "
                        "https://github.com/poljar/matrix-nio#installation "
                        "Error: %s",
                        e,
                    )
                    return False

                # Login succeeded!

            logger.info(f"Logged in as {config.user_id}")

            # Allow jobs to fire
            try:
                scheduler.start()
            except SchedulerAlreadyRunningError:
                pass

            await client.sync_forever(timeout=30000, full_state=True)

        except (ClientConnectionError, ServerDisconnectedError):
            logger.warning(
                "Unable to connect to homeserver, retrying in 15s...")

            # Sleep so we don't bombard the server with login requests
            sleep(15)
        finally:
            # Make sure to close the client connection on disconnect
            await client.close()