Initial implementation of login and status verbs

This commit is contained in:
Jez Cope 2021-06-10 17:23:51 +01:00
parent 977c85baf1
commit c984e84cb3
6 changed files with 2453 additions and 4 deletions

1
.gitignore vendored
View File

@ -4,3 +4,4 @@
# Added by cargo
/target
/session_info

2319
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -7,3 +7,9 @@ edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
clap = "2.33.3"
rpassword = "5.0"
matrix-sdk = { git = "https://github.com/matrix-org/matrix-rust-sdk" }
tokio = { version = "*", features = ["full"] }
serde = "1.0"
serde-lexpr = "0.1.0"

View File

@ -11,11 +11,12 @@ TODO: policy on maintenance and contributions
- [ ] Login and store session info
- [ ] Log out
- [ ] Show login status
- [ ] Handle multiple accounts on different homeservers
- [ ] Tombstone room
- [ ] Add room alias
- [ ] Remove room alias
- [ ] List rooms
- [ ] Filter room list in various ways (esp. Spaces!)
- [ ] Tombstone room
- [ ] Handle multiple accounts on different homeservers
- [ ] Upgrade room
- [ ] Make it easier to do workflows like "invite mjolnir -> op mjolnir" in multiple rooms

87
src/commands.rs Normal file
View File

@ -0,0 +1,87 @@
use matrix_sdk::{identifiers::UserId, Client, Session};
use rpassword::prompt_password_stderr;
use serde_lexpr;
use std::convert::TryFrom;
use std::fs::File;
use std::io::{self, Write};
static SESSION_FILE: &str = "session_info";
pub async fn login(username: Option<&str>) -> Result<(), Box<dyn std::error::Error>> {
let stdin = io::stdin();
let user_id = match username {
Some(s) => UserId::try_from(s)?,
None => {
let mut s = String::new();
eprint!("Username: ");
io::stderr().flush().unwrap();
stdin.read_line(&mut s)?;
UserId::try_from(s.trim_end())?
}
};
let password = prompt_password_stderr("Password: ")?;
let client = Client::new_from_user_id(user_id.clone()).await?;
println!(
"{} logged in? {}",
client.homeserver().await,
client.logged_in().await
);
let response = client
.login(user_id.localpart(), &password, None, Some("mxadm"))
.await?;
println!("{:?}", response);
println!(
"{} logged in? {}",
client.homeserver().await,
client.logged_in().await
);
let session_info = Session {
access_token: response.access_token,
user_id: response.user_id,
device_id: response.device_id,
};
println!("{:?}", session_info);
let mut session_file = File::create(SESSION_FILE)?;
serde_lexpr::to_writer(&mut session_file, &session_info)?;
Ok(())
}
pub async fn status() -> Result<(), Box<dyn std::error::Error>> {
let session_file = match File::open(SESSION_FILE) {
Ok(f) => f,
_ => {
println!("Not logged in: no session info file found");
return Ok(())
}
};
let session_info: Session = serde_lexpr::from_reader(session_file)?;
let client = Client::new_from_user_id(session_info.user_id.clone()).await?;
if let Err(e) = client.restore_login(session_info).await {
println!("Not logged in: unable to restore session; {}", e);
return Ok(())
}
// Need to make a query requiring a valid token to check session validity
if let Ok(_) = client.devices().await {
println!(
"Logged in as {} ({}) with device ID {}",
client.user_id().await.unwrap(),
client.display_name().await?.unwrap(),
client.device_id().await.unwrap()
);
} else {
println!("Not logged in");
}
Ok(())
}

View File

@ -1,3 +1,38 @@
fn main() {
println!("Hello, world!");
#[macro_use]
extern crate clap;
mod commands;
use clap::{App, SubCommand};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let matches = App::new(crate_name!())
.version(crate_version!())
.about(crate_description!())
.args_from_usage(
"-c, --config=[FILE] 'Sets custom config file'
-v, --verbose 'Prints more details when running'
-T, --token=[TOKEN] 'Specifies token for authentication'",
)
.subcommand(
SubCommand::with_name("login")
.about("authenticates and saves the session details")
.args_from_usage("-U, --user=[USERNAME] 'Specifies the username to log in with'"),
)
.subcommand(SubCommand::with_name("logout").about("ends the current session"))
.subcommand(SubCommand::with_name("status").about("displays current session status"))
.subcommand(SubCommand::with_name("list-rooms").about("lists rooms available to the user"))
.get_matches();
match matches.subcommand() {
("login", Some(login_matches)) => commands::login(login_matches.value_of("user")).await?,
("logout", Some(_)) => println!("logging out..."),
("status", Some(_)) => commands::status().await?,
("list-rooms", Some(_)) => println!("listing rooms..."),
("", None) => println!("No subcommand given"),
_ => unreachable!(),
}
Ok(())
}