2023-03-18 02:14:45 -06:00
|
|
|
import json
|
2023-04-19 17:01:07 -06:00
|
|
|
import logging
|
2023-03-18 02:14:45 -06:00
|
|
|
import os
|
|
|
|
from pathlib import Path
|
|
|
|
from typing import Union
|
|
|
|
|
|
|
|
from nio import AsyncClient, AsyncClientConfig, LoginError
|
|
|
|
from nio import LoginResponse
|
|
|
|
|
2023-04-19 17:01:07 -06:00
|
|
|
logger = logging.getLogger('MatrixGPT')
|
2023-03-18 02:14:45 -06:00
|
|
|
|
2023-09-15 22:49:00 -06:00
|
|
|
|
2023-03-18 02:14:45 -06:00
|
|
|
class MatrixNioGPTHelper:
|
|
|
|
"""
|
|
|
|
A simple wrapper class for common matrix-nio actions.
|
|
|
|
"""
|
|
|
|
client = None
|
|
|
|
|
2024-02-22 14:56:34 -07:00
|
|
|
# Encryption is disabled because it's handled by Pantalaimon.
|
|
|
|
client_config = AsyncClientConfig(max_limit_exceeded=0, max_timeouts=0, store_sync_tokens=True, encryption_enabled=False)
|
2023-03-18 02:14:45 -06:00
|
|
|
|
|
|
|
def __init__(self, auth_file: Union[Path, str], user_id: str, passwd: str, homeserver: str, store_path: str, device_name: str = 'MatrixGPT', device_id: str = None):
|
|
|
|
self.auth_file = auth_file
|
|
|
|
self.user_id = user_id
|
|
|
|
self.passwd = passwd
|
|
|
|
|
|
|
|
self.homeserver = homeserver
|
|
|
|
if not (self.homeserver.startswith("https://") or self.homeserver.startswith("http://")):
|
|
|
|
self.homeserver = "https://" + self.homeserver
|
|
|
|
|
|
|
|
self.store_path = store_path
|
|
|
|
Path(self.store_path).mkdir(parents=True, exist_ok=True)
|
|
|
|
|
|
|
|
self.device_name = device_name
|
2024-02-22 14:56:34 -07:00
|
|
|
self.client = AsyncClient(homeserver=self.homeserver, user=self.user_id, config=self.client_config, device_id=device_id)
|
2023-03-18 02:14:45 -06:00
|
|
|
|
|
|
|
async def login(self) -> tuple[bool, LoginError] | tuple[bool, LoginResponse | None]:
|
2023-04-08 15:13:09 -06:00
|
|
|
try:
|
2024-02-22 14:56:34 -07:00
|
|
|
# If there are no previously-saved credentials, we'll use the password.
|
2023-04-08 15:13:09 -06:00
|
|
|
if not os.path.exists(self.auth_file):
|
2023-04-19 17:01:07 -06:00
|
|
|
logger.info('Using username/password.')
|
2023-04-08 15:13:09 -06:00
|
|
|
resp = await self.client.login(self.passwd, device_name=self.device_name)
|
2023-03-18 02:14:45 -06:00
|
|
|
|
2024-02-22 14:56:34 -07:00
|
|
|
# check that we logged in successfully.
|
2023-04-08 15:13:09 -06:00
|
|
|
if isinstance(resp, LoginResponse):
|
|
|
|
self.write_details_to_disk(resp)
|
|
|
|
return True, resp
|
|
|
|
else:
|
|
|
|
return False, resp
|
2023-03-18 02:14:45 -06:00
|
|
|
else:
|
2024-02-22 14:56:34 -07:00
|
|
|
# Otherwise the config file exists, so we'll use the stored credentials.
|
2023-04-19 17:01:07 -06:00
|
|
|
logger.info('Using cached credentials.')
|
2023-04-08 15:13:09 -06:00
|
|
|
with open(self.auth_file, "r") as f:
|
|
|
|
config = json.load(f)
|
|
|
|
client = AsyncClient(config["homeserver"])
|
|
|
|
client.access_token = config["access_token"]
|
|
|
|
client.user_id = config["user_id"]
|
|
|
|
client.device_id = config["device_id"]
|
|
|
|
resp = await self.client.login(self.passwd, device_name=self.device_name)
|
|
|
|
if isinstance(resp, LoginResponse):
|
|
|
|
self.write_details_to_disk(resp)
|
|
|
|
return True, resp
|
|
|
|
else:
|
|
|
|
return False, resp
|
|
|
|
except Exception:
|
|
|
|
return False, None
|
2023-03-18 02:14:45 -06:00
|
|
|
|
|
|
|
def write_details_to_disk(self, resp: LoginResponse) -> None:
|
|
|
|
"""Writes the required login details to disk so we can log in later without
|
|
|
|
using a password.
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
resp {LoginResponse} -- the successful client login response.
|
|
|
|
homeserver -- URL of homeserver, e.g. "https://matrix.example.org"
|
|
|
|
"""
|
|
|
|
with open(self.auth_file, "w") as f:
|
|
|
|
json.dump({"homeserver": self.homeserver, # e.g. "https://matrix.example.org"
|
|
|
|
"user_id": resp.user_id, # e.g. "@user:example.org"
|
|
|
|
"device_id": resp.device_id, # device ID, 10 uppercase letters
|
|
|
|
"access_token": resp.access_token, # cryptogr. access token
|
|
|
|
}, f, )
|