modularize things

This commit is contained in:
Taylor C. Richberger 2020-04-17 16:53:27 -06:00
parent 8be108a57c
commit b30eec72b3
4 changed files with 180 additions and 90 deletions

84
src/bot.rs Normal file
View File

@ -0,0 +1,84 @@
use serde::{self, Deserialize, Serialize};
use reqwest::Client;
use crate::matrix::SyncCommand;
#[derive(Serialize, Deserialize, Debug)]
pub struct MatrixConfig {
user: String,
password: String,
home_server: String,
next_batch: Option<String>,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct Config {
matrix: MatrixConfig,
}
pub struct DiceBot {
config: Config,
access_token: String,
next_batch: Option<String>,
client: Client,
}
#[derive(Serialize, Debug)]
struct LoginRequest<'a, 'b, 'c> {
#[serde(rename = "type")]
type_: &'a str,
user: &'b str,
password: &'c str,
}
#[derive(Deserialize, Debug)]
struct LoginResponse {
access_token: String,
}
impl DiceBot {
pub async fn new(config: Config) -> Result<Self, Box<dyn std::error::Error>> {
let client = Client::new();
let request = LoginRequest {
type_: "m.login.password",
user: &config.matrix.user,
password: &config.matrix.password,
};
let response = client.post(&format!("https://{}/_matrix/client/r0/login", config.matrix.home_server))
.body(serde_json::to_string(&request)?)
.send()
.await?;
let body: LoginResponse = serde_json::from_str(&response.text().await?)?;
Ok(DiceBot{
client,
config,
access_token: body.access_token,
next_batch: None,
})
}
pub async fn sync(&mut self) -> Result<(), Box<dyn std::error::Error>> {
let mut url = format!("https://{}/_matrix/client/r0/sync?access_token={}&timeout=3000",
self.config.matrix.home_server,
self.access_token);
if let Some(since) = &self.next_batch {
url.push_str(&format!("&since={}", since));
}
let body = self.client.get(&url)
.send()
.await?
.text()
.await?;
let sync: SyncCommand = serde_json::from_str(&body).unwrap();
println!("{:#?}", sync);
self.next_batch = Some(sync.next_batch);
Ok(())
}
pub async fn logout(self) -> Result<(), Box<dyn std::error::Error>> {
self.client.post(&format!("https://{}/_matrix/client/r0/logout?access_token={}", self.config.matrix.home_server, self.access_token))
.body("{}")
.send()
.await?;
Ok(())
}
}

2
src/lib.rs Normal file
View File

@ -0,0 +1,2 @@
pub mod matrix;
pub mod bot;

View File

@ -1,109 +1,36 @@
use serde::{self, Deserialize, Serialize};
use std::collections::HashMap;
use std::env;
use tokio::select; use tokio::select;
use tokio::signal::unix::{signal, SignalKind}; use tokio::signal::unix::{signal, SignalKind};
use axfive_matrix_dicebot::matrix::SyncCommand;
#[derive(Serialize, Deserialize, Debug)] use axfive_matrix_dicebot::bot::{DiceBot, Config};
#[serde(tag = "msgtype")] use std::fs::read_to_string;
enum MessageContent {
#[serde(rename = "m.text")]
Text { body: String },
#[serde(other)]
Other,
}
#[derive(Serialize, Deserialize, Debug)]
#[serde(tag = "membership")]
enum MemberContent {
#[serde(rename = "invite")]
Invite {
// TODO: maybe leave empty?
#[serde(default)]
#[serde(alias = "displayname")]
display_name: Option<String>,
},
#[serde(other)]
Other,
}
#[derive(Serialize, Deserialize, Debug)]
struct RoomEvent {
content: MessageContent,
event_id: String,
}
#[derive(Serialize, Deserialize, Debug)]
struct MemberEvent {
content: MemberContent,
}
#[derive(Serialize, Deserialize, Debug)]
#[serde(tag = "type")]
enum Event {
#[serde(rename = "m.room.message")]
Room(RoomEvent),
#[serde(rename = "m.room.member")]
Member(MemberEvent),
#[serde(other)]
Other,
}
#[derive(Serialize, Deserialize, Debug)]
struct Timeline {
events: Vec<Event>,
}
#[derive(Serialize, Deserialize, Debug)]
struct Room {
timeline: Timeline,
}
#[derive(Serialize, Deserialize, Debug)]
struct Rooms {
invite: HashMap<String, serde_json::Value>,
join: HashMap<String, Room>,
}
#[derive(Serialize, Deserialize, Debug)]
struct SyncCommand {
next_batch: String,
rooms: Rooms,
}
async fn sync<S: AsRef<str>>(key: S) -> Result<(), Box<dyn std::error::Error>> {
let body = reqwest::get(&format!(
"https://matrix.org/_matrix/client/r0/sync?access_token={}&timeout=3000",
key.as_ref()
))
.await?
.text()
.await?;
let sync: SyncCommand = serde_json::from_str(&body)?;
println!("{:#?}", sync);
Ok(())
}
#[tokio::main] #[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> { async fn main() -> Result<(), Box<dyn std::error::Error>> {
let key = std::env::args() let config_path = std::env::args()
.skip(1) .skip(1)
.next() .next()
.expect("Need a key 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");
let mut bot = DiceBot::new(config).await?;
println!("Logged in");
let mut sigint = signal(SignalKind::interrupt())?; let mut sigint = signal(SignalKind::interrupt())?;
loop { loop {
println!("Loop");
select! { select! {
_ = sigint.recv() => { _ = sigint.recv() => {
break; break;
} }
_ = sync(&key) => { _ = bot.sync() => {
} }
} }
} }
Ok(()) println!("Logging out");
bot.logout().await
} }

77
src/matrix.rs Normal file
View File

@ -0,0 +1,77 @@
use serde::{self, Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Serialize, Deserialize, Debug)]
#[serde(tag = "msgtype")]
#[serde(rename = "m.text")]
pub struct TextMessage {
body: String
}
// Need untagged because redactions are blank
#[derive(Serialize, Deserialize, Debug)]
#[serde(untagged)]
pub enum MessageContent {
Text(TextMessage),
Other(serde_json::Value),
}
#[derive(Serialize, Deserialize, Debug)]
#[serde(tag = "membership")]
pub enum MemberContent {
#[serde(rename = "invite")]
Invite {
// TODO: maybe leave empty?
#[serde(default)]
#[serde(alias = "displayname")]
display_name: Option<String>,
},
#[serde(other)]
Other,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct RoomEvent {
pub content: MessageContent,
pub event_id: String,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct MemberEvent {
pub content: MemberContent,
}
#[derive(Serialize, Deserialize, Debug)]
#[serde(tag = "type")]
pub enum Event {
#[serde(rename = "m.room.message")]
Room(RoomEvent),
#[serde(rename = "m.room.member")]
Member(MemberEvent),
#[serde(other)]
Other,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct Timeline {
pub events: Vec<Event>,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct Room {
pub timeline: Timeline,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct Rooms {
pub invite: HashMap<String, serde_json::Value>,
pub join: HashMap<String, Room>,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct SyncCommand {
pub next_batch: String,
pub rooms: Rooms,
}