use std::io::Write;
use futures_util::stream::StreamExt;
use matrix_sdk::Client;
use matrix_sdk::config::SyncSettings;
use matrix_sdk::encryption::verification::{
EmojiShortAuthString,
SasState,
SasVerification,
Verification,
VerificationRequestState,
format_emojis,
};
use matrix_sdk::ruma::OwnedUserId;
use matrix_sdk::ruma::api::client::filter::FilterDefinition;
use matrix_sdk::ruma::events::key::verification::request::ToDeviceKeyVerificationRequestEvent;
use matrix_sdk::ruma::events::room::message::OriginalSyncRoomMessageEvent;
use rdzobot::Rdzobot;
async fn confirm_sas(
sas_verification: SasVerification,
emojis: Option<EmojiShortAuthString>,
decimals: (u16, u16, u16),
) {
print!(
"confirm user_id {} device {} emojis\n{}\ndecimals {} {} {}? [y/N] ",
sas_verification.other_device().user_id(),
sas_verification.other_device().device_id(),
if emojis.is_some() {
format_emojis(emojis.unwrap().emojis)
} else {
"-".to_string()
},
decimals.0,
decimals.1,
decimals.2,
);
std::io::stdout().flush().expect("failed to flush stdout");
let mut input = String::new();
std::io::stdin().read_line(&mut input).expect("faile to read stdin");
match input.trim().to_lowercase().as_ref() {
"yes" | "y" => {
sas_verification.confirm().await.expect("failed to confirm SAS verification")
}
_ => sas_verification.cancel().await.expect("failed to cancel SAS verification"),
}
}
async fn sas_verification_handler(sas_verification: SasVerification) {
sas_verification.accept().await.expect("can't accept sas verification request");
let mut stream = sas_verification.changes();
while let Some(state) = stream.next().await {
match state {
SasState::KeysExchanged { emojis, decimals } => {
tokio::spawn(confirm_sas(sas_verification.clone(), emojis, decimals));
}
SasState::Done { .. } => {
let device = sas_verification.other_device();
println!(
"Successfully verified device {} {} {:?}",
device.user_id(),
device.device_id(),
device.local_trust_state(),
);
}
| SasState::Created { .. }
| SasState::Started { .. }
| SasState::Accepted { .. }
| SasState::Confirmed
| SasState::Cancelled { .. } => (),
}
}
}
#[rustfmt::skip]
async fn request_verification_handler(client: Client, sender: OwnedUserId, flow_id: String) {
let request = client
.encryption()
.get_verification_request(&sender, flow_id)
.await
.expect("request object wasn't created");
request.accept().await.expect("can't accept verification request");
let mut stream = request.changes();
while let Some(state) = stream.next().await {
match state {
| VerificationRequestState::Created { .. }
| VerificationRequestState::Requested { .. }
| VerificationRequestState::Ready { .. } => (),
VerificationRequestState::Transitioned { verification } => {
if let Verification::SasV1(sas_verification) = verification {
tokio::spawn(sas_verification_handler(sas_verification));
}
}
| VerificationRequestState::Done
| VerificationRequestState::Cancelled(_) => break,
}
}
}
async fn on_verification_request(event: ToDeviceKeyVerificationRequestEvent, client: Client) {
tokio::spawn(request_verification_handler(
client,
event.sender.clone(),
event.content.transaction_id.to_string(),
));
}
async fn on_message_verification_request(event: OriginalSyncRoomMessageEvent, client: Client) {
tokio::spawn(request_verification_handler(
client,
event.sender.clone(),
event.event_id.to_string(),
));
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let bot = Rdzobot::new().await?;
bot.client.add_event_handler(on_verification_request);
bot.client.add_event_handler(on_message_verification_request);
let sync_settings =
SyncSettings::default().filter(FilterDefinition::with_lazy_loading().into());
bot.client.sync(sync_settings).await?;
Ok(())
}