137 lines
3.7 KiB
Rust
137 lines
3.7 KiB
Rust
//! Utility functions for working matrix-related tasks
|
|
|
|
use std::{
|
|
fs::File,
|
|
path::{Path, PathBuf},
|
|
};
|
|
|
|
use matrix_sdk::{
|
|
config::ClientConfig,
|
|
reqwest::Url,
|
|
ruma::{DeviceIdBox, UserId},
|
|
Client,
|
|
};
|
|
use ron::ser::PrettyConfig;
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct Session {
|
|
/// The homeserver URL
|
|
pub homeserver: Url,
|
|
/// Access token for authentication
|
|
pub access_token: String,
|
|
/// The user's mxid.
|
|
pub user_id: UserId,
|
|
/// The user's device ID.
|
|
pub device_id: DeviceIdBox,
|
|
}
|
|
|
|
impl Session {
|
|
/// Read a `Session` from the filesystem.
|
|
pub fn from_fs() -> Result<Self, LoginError> {
|
|
let path = path()?.join("session.ron");
|
|
let file = File::open(&path)?;
|
|
let session = ron::de::from_reader(file)?;
|
|
Ok(session)
|
|
}
|
|
|
|
/// Create a matrix client and restore this session.
|
|
pub fn restore(self) -> Result<Client, LoginError> {
|
|
let session = matrix_sdk::Session {
|
|
access_token: self.access_token,
|
|
user_id: self.user_id,
|
|
device_id: self.device_id,
|
|
};
|
|
let client = Client::new(self.homeserver)?;
|
|
let runtime = tokio::runtime::Runtime::new().unwrap();
|
|
runtime.block_on(client.restore_login(session))?;
|
|
Ok(client)
|
|
}
|
|
}
|
|
|
|
/// Create a matrix client and log it in to the server at the given URL with the
|
|
/// given credentials.
|
|
pub fn login(url: &str, user: &str, password: &str) -> Result<Client, LoginError> {
|
|
let url: Url = if !url.contains("://") {
|
|
format!("https://{}", url).parse()
|
|
} else {
|
|
url.parse()
|
|
}?;
|
|
let client = Client::new_with_config(url.clone(), config()?)?;
|
|
let rt = tokio::runtime::Runtime::new().unwrap();
|
|
let response = rt.block_on(client.login(user, password, None, None))?;
|
|
let session = Session {
|
|
homeserver: url,
|
|
access_token: response.access_token,
|
|
user_id: response.user_id,
|
|
device_id: response.device_id,
|
|
};
|
|
let file = File::create(path()?.join("session.ron"))?;
|
|
ron::ser::to_writer_pretty(file, &session, PrettyConfig::new())?;
|
|
Ok(client)
|
|
}
|
|
|
|
/// Errors that can happen when logging in
|
|
#[derive(Debug)]
|
|
pub enum LoginError {
|
|
/// Invalid URL
|
|
Url(url::ParseError),
|
|
/// Matrix SDK error
|
|
Sdk(matrix_sdk::Error),
|
|
/// I/O error
|
|
Io(std::io::Error),
|
|
/// Serialization error
|
|
Ron(ron::Error),
|
|
}
|
|
|
|
impl std::fmt::Display for LoginError {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
match self {
|
|
LoginError::Url(_) => write!(f, "Invalid homeserver address"),
|
|
LoginError::Sdk(e) => write!(f, "{}", e),
|
|
LoginError::Io(e) => write!(f, "Filesystem error: {}", e),
|
|
LoginError::Ron(e) => write!(f, "Serialization error: {}", e),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl std::error::Error for LoginError {}
|
|
|
|
impl From<url::ParseError> for LoginError {
|
|
fn from(e: url::ParseError) -> Self {
|
|
LoginError::Url(e)
|
|
}
|
|
}
|
|
|
|
impl From<matrix_sdk::Error> for LoginError {
|
|
fn from(e: matrix_sdk::Error) -> Self {
|
|
LoginError::Sdk(e)
|
|
}
|
|
}
|
|
|
|
impl From<std::io::Error> for LoginError {
|
|
fn from(e: std::io::Error) -> Self {
|
|
LoginError::Io(e)
|
|
}
|
|
}
|
|
|
|
impl From<ron::Error> for LoginError {
|
|
fn from(e: ron::Error) -> Self {
|
|
LoginError::Ron(e)
|
|
}
|
|
}
|
|
|
|
/// Configuration for `Clients`.
|
|
fn config() -> Result<ClientConfig, std::io::Error> {
|
|
Ok(ClientConfig::new().store_path(&path()?))
|
|
}
|
|
|
|
/// The path the the sdk store should be put in.
|
|
fn path() -> Result<PathBuf, std::io::Error> {
|
|
let path = Path::new(&std::env::var_os("HOME").unwrap())
|
|
.join(".config")
|
|
.join("retrix");
|
|
std::fs::create_dir_all(&path)?;
|
|
Ok(path)
|
|
}
|