From b808119a73861e171a067c203bdb39cd7cbb7e52 Mon Sep 17 00:00:00 2001
From: Jason Robinson <mail@jasonrobinson.me>
Date: Tue, 15 Dec 2020 00:38:47 +0200
Subject: [PATCH] Enable logging in using an access token (#21)

We do a `client.load_store()` to restore a previous session.

If both token and password are defined, token is preferred, since it wont create a new device for the bot. One or the other needs to be defined.

Requires https://github.com/anoadragon453/nio-template/pull/20 for the configuration change since password and access token must both be optional (but one must be given).
---
 my_project_name/config.py |  6 ++++-
 my_project_name/main.py   | 50 ++++++++++++++++++++++++---------------
 sample.config.yaml        |  4 +++-
 3 files changed, 39 insertions(+), 21 deletions(-)

diff --git a/my_project_name/config.py b/my_project_name/config.py
index c7d44a0..ff8104c 100644
--- a/my_project_name/config.py
+++ b/my_project_name/config.py
@@ -85,7 +85,11 @@ class Config(object):
         if not re.match("@.*:.*", self.user_id):
             raise ConfigError("matrix.user_id must be in the form @name:domain")
 
-        self.user_password = self._get_cfg(["matrix", "user_password"], required=True)
+        self.user_password = self._get_cfg(["matrix", "user_password"], required=False)
+        self.user_token = self._get_cfg(["matrix", "user_token"], required=False)
+        if not self.user_token and not self.user_password:
+            raise ConfigError("Must supply either user token or password")
+
         self.device_id = self._get_cfg(["matrix", "device_id"], required=True)
         self.device_name = self._get_cfg(
             ["matrix", "device_name"], default="nio-template"
diff --git a/my_project_name/main.py b/my_project_name/main.py
index bcb9ddd..f3173ef 100644
--- a/my_project_name/main.py
+++ b/my_project_name/main.py
@@ -51,6 +51,10 @@ async def main():
         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,))
@@ -59,28 +63,36 @@ async def main():
     # 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,
-                )
+            if config.user_token:
+                # Use token to log in
+                client.load_store()
 
-                # Check if login failed
-                if type(login_response) == LoginError:
-                    logger.error("Failed to login: %s", login_response.message)
+                # 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
-            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!
+                # Login succeeded!
 
             logger.info(f"Logged in as {config.user_id}")
             await client.sync_forever(timeout=30000, full_state=True)
diff --git a/sample.config.yaml b/sample.config.yaml
index 969b3a5..d33538e 100644
--- a/sample.config.yaml
+++ b/sample.config.yaml
@@ -9,8 +9,10 @@ command_prefix: "!c"
 matrix:
   # The Matrix User ID of the bot account
   user_id: "@bot:example.com"
-  # Matrix account password
+  # Matrix account password (optional if access token used)
   user_password: ""
+  # Matrix account access token (optional if password used)
+  #user_token: ""
   # The URL of the homeserver to connect to
   homeserver_url: https://example.com
   # The device ID that is **non pre-existing** device