get things cleaned up a little bit more

This commit is contained in:
Taylor C. Richberger 2020-04-18 17:09:55 -06:00
parent f129f45864
commit 7cc29ddf8f
2 changed files with 78 additions and 43 deletions

View File

@ -1,33 +1,34 @@
use serde::{self, Deserialize, Serialize}; use serde::{self, Deserialize, Serialize};
use reqwest::Client; use reqwest::{Client, Url};
use crate::matrix::SyncCommand; use crate::matrix::SyncCommand;
use std::path::PathBuf;
use std::fs;
const USER_AGENT: &str = "AxFive Matrix DiceBot/0.1.0 (+https://gitlab.com/Taywee/axfive-matrix-dicebot)";
/// The "matrix" section of the config, which gives home server, login information, and etc.
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
pub struct MatrixConfig { pub struct MatrixConfig {
user: String, /// Your homeserver of choice, as an FQDN without scheme or path
password: String, pub home_server: String,
home_server: String,
next_batch: Option<String>, /// The next batch to grab. This should be set automatically
pub next_batch: Option<String>,
pub login: toml::Value,
} }
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
pub struct Config { pub struct Config {
matrix: MatrixConfig, pub matrix: MatrixConfig,
} }
pub struct DiceBot { pub struct DiceBot {
config_path: Option<PathBuf>,
config: Config, config: Config,
access_token: String, access_token: String,
next_batch: Option<String>, next_batch: Option<String>,
client: Client, client: Client,
} home_server: Url,
#[derive(Serialize, Debug)]
struct LoginRequest<'a, 'b, 'c> {
#[serde(rename = "type")]
type_: &'a str,
user: &'b str,
password: &'c str,
} }
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
@ -36,35 +37,65 @@ struct LoginResponse {
} }
impl DiceBot { impl DiceBot {
pub async fn new(config: Config) -> Result<Self, Box<dyn std::error::Error>> { pub async fn new(config_path: Option<PathBuf>, config: Config) -> Result<Self, Box<dyn std::error::Error>> {
let home_server: Url = format!("https://{}", config.matrix.home_server).parse()?;
let client = Client::new(); let client = Client::new();
let request = LoginRequest { let request = serde_json::to_string(&config.matrix.login)?;
type_: "m.login.password", let mut login_url = home_server.clone();
user: &config.matrix.user, login_url.set_path("/_matrix/client/r0/login");
password: &config.matrix.password, let response = client.post(login_url)
}; .header("user-agent", USER_AGENT)
let response = client.post(&format!("https://{}/_matrix/client/r0/login", config.matrix.home_server)) .body(request)
.body(serde_json::to_string(&request)?)
.send() .send()
.await?; .await?;
let body: LoginResponse = serde_json::from_str(&response.text().await?)?; let body: LoginResponse = serde_json::from_str(&response.text().await?)?;
let next_batch = config.matrix.next_batch.clone();
Ok(DiceBot{ Ok(DiceBot{
home_server,
config_path,
client, client,
config, config,
access_token: body.access_token, access_token: body.access_token,
next_batch: None, next_batch,
}) })
} }
pub async fn sync(&mut self) -> Result<(), Box<dyn std::error::Error>> { pub async fn from_path<P: Into<PathBuf>>(config_path: P) -> Result<Self, Box<dyn std::error::Error>> {
// TODO: handle http 429 let config_path = config_path.into();
let mut url = format!("https://{}/_matrix/client/r0/sync?access_token={}&timeout=3000", let config = {
self.config.matrix.home_server, let contents = fs::read_to_string(&config_path)?;
self.access_token); toml::from_str(&contents)?
if let Some(since) = &self.next_batch { };
url.push_str(&format!("&since={}", since)); DiceBot::new(Some(config_path), config).await
}
/// Build a url using the current home server and the given path, as well as appending the
/// access token
fn url(&self, path: &str, query: &[(&str, &str)]) -> Url {
let mut url = self.home_server.clone();
url.set_path(path);
{
let mut query_pairs = url.query_pairs_mut();
query_pairs.append_pair("access_token", &self.access_token);
for pair in query.iter() {
query_pairs.append_pair(pair.0, pair.1);
}
} }
let body = self.client.get(&url)
url
}
pub async fn sync(&mut self) -> Result<(), Box<dyn std::error::Error>> {
let mut sync_url = self.url("/_matrix/client/r0/sync", &[("timeout", "30000")]);
// TODO: handle http 429
if let Some(since) = &self.next_batch {
sync_url.query_pairs_mut()
.append_pair("since", since);
}
let body = self.client.get(sync_url)
.header("user-agent", USER_AGENT)
.send() .send()
.await? .await?
.text() .text()
@ -75,12 +106,21 @@ impl DiceBot {
Ok(()) Ok(())
} }
pub async fn logout(self) -> Result<(), Box<dyn std::error::Error>> { pub async fn logout(mut self) -> Result<(), Box<dyn std::error::Error>> {
// TODO: Write out next_batch let logout_url = self.url("/_matrix/client/r0/logout", &[]);
self.client.post(&format!("https://{}/_matrix/client/r0/logout?access_token={}", self.config.matrix.home_server, self.access_token)) self.client.post(logout_url)
.header("user-agent", USER_AGENT)
.body("{}") .body("{}")
.send() .send()
.await?; .await?;
self.config.matrix.next_batch = self.next_batch;
if let Some(config_path) = self.config_path {
let config = toml::to_string_pretty(&self.config)?;
fs::write(config_path, config)?;
}
Ok(()) Ok(())
} }
} }

View File

@ -1,8 +1,6 @@
use tokio::select; use tokio::select;
use tokio::signal::unix::{signal, SignalKind}; use tokio::signal::unix::{signal, SignalKind};
use axfive_matrix_dicebot::matrix::SyncCommand; use axfive_matrix_dicebot::bot::DiceBot;
use axfive_matrix_dicebot::bot::{DiceBot, Config};
use std::fs::read_to_string;
#[tokio::main] #[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> { async fn main() -> Result<(), Box<dyn std::error::Error>> {
@ -10,12 +8,8 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
.skip(1) .skip(1)
.next() .next()
.expect("Need a config as an argument"); .expect("Need a config as an argument");
let config = {
let contents = read_to_string(config_path)?;
toml::from_str(&contents)?
};
println!("Logging in"); println!("Logging in");
let mut bot = DiceBot::new(config).await?; let mut bot = DiceBot::from_path(config_path).await?;
println!("Logged in"); println!("Logged in");
let mut sigint = signal(SignalKind::interrupt())?; let mut sigint = signal(SignalKind::interrupt())?;
@ -26,7 +20,8 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
_ = sigint.recv() => { _ = sigint.recv() => {
break; break;
} }
_ = bot.sync() => { result = bot.sync() => {
result?;
} }
} }
} }