example_emoji_verification/
main.rs1use std::io::Write;
2
3use anyhow::Result;
4use clap::Parser;
5use futures_util::stream::StreamExt;
6use matrix_sdk::{
7 config::SyncSettings,
8 encryption::verification::{
9 format_emojis, Emoji, SasState, SasVerification, Verification, VerificationRequest,
10 VerificationRequestState,
11 },
12 ruma::{
13 events::{
14 key::verification::request::ToDeviceKeyVerificationRequestEvent,
15 room::message::{MessageType, OriginalSyncRoomMessageEvent},
16 },
17 UserId,
18 },
19 Client,
20};
21use url::Url;
22
23async fn wait_for_confirmation(sas: SasVerification, emoji: [Emoji; 7]) {
24 println!("\nDo the emojis match: \n{}", format_emojis(emoji));
25 print!("Confirm with `yes` or cancel with `no`: ");
26 std::io::stdout().flush().expect("We should be able to flush stdout");
27
28 let mut input = String::new();
29 std::io::stdin().read_line(&mut input).expect("error: unable to read user input");
30
31 match input.trim().to_lowercase().as_ref() {
32 "yes" | "true" | "ok" => sas.confirm().await.unwrap(),
33 _ => sas.cancel().await.unwrap(),
34 }
35}
36
37async fn print_devices(user_id: &UserId, client: &Client) {
38 println!("Devices of user {user_id}");
39
40 for device in client.encryption().get_user_devices(user_id).await.unwrap().devices() {
41 if device.device_id()
42 == client.device_id().expect("We should be logged in now and know our device id")
43 {
44 continue;
45 }
46
47 println!(
48 " {:<10} {:<30} {:<}",
49 device.device_id(),
50 device.display_name().unwrap_or("-"),
51 if device.is_verified() { "✅" } else { "❌" }
52 );
53 }
54}
55
56async fn sas_verification_handler(client: Client, sas: SasVerification) {
57 println!(
58 "Starting verification with {} {}",
59 &sas.other_device().user_id(),
60 &sas.other_device().device_id()
61 );
62 print_devices(sas.other_device().user_id(), &client).await;
63 sas.accept().await.unwrap();
64
65 let mut stream = sas.changes();
66
67 while let Some(state) = stream.next().await {
68 match state {
69 SasState::KeysExchanged { emojis, decimals: _ } => {
70 tokio::spawn(wait_for_confirmation(
71 sas.clone(),
72 emojis.expect("We only support verifications using emojis").emojis,
73 ));
74 }
75 SasState::Done { .. } => {
76 let device = sas.other_device();
77
78 println!(
79 "Successfully verified device {} {} {:?}",
80 device.user_id(),
81 device.device_id(),
82 device.local_trust_state()
83 );
84
85 print_devices(sas.other_device().user_id(), &client).await;
86
87 break;
88 }
89 SasState::Cancelled(cancel_info) => {
90 println!("The verification has been cancelled, reason: {}", cancel_info.reason());
91
92 break;
93 }
94 SasState::Created { .. }
95 | SasState::Started { .. }
96 | SasState::Accepted { .. }
97 | SasState::Confirmed => (),
98 }
99 }
100}
101
102async fn request_verification_handler(client: Client, request: VerificationRequest) {
103 println!("Accepting verification request from {}", request.other_user_id(),);
104 request.accept().await.expect("Can't accept verification request");
105
106 let mut stream = request.changes();
107
108 while let Some(state) = stream.next().await {
109 match state {
110 VerificationRequestState::Created { .. }
111 | VerificationRequestState::Requested { .. }
112 | VerificationRequestState::Ready { .. } => (),
113 VerificationRequestState::Transitioned { verification } => {
114 if let Verification::SasV1(s) = verification {
116 tokio::spawn(sas_verification_handler(client, s));
117 break;
118 }
119 }
120 VerificationRequestState::Done | VerificationRequestState::Cancelled(_) => break,
121 }
122 }
123}
124
125async fn sync(client: Client) -> matrix_sdk::Result<()> {
126 client.add_event_handler(
127 |ev: ToDeviceKeyVerificationRequestEvent, client: Client| async move {
128 let request = client
129 .encryption()
130 .get_verification_request(&ev.sender, &ev.content.transaction_id)
131 .await
132 .expect("Request object wasn't created");
133
134 tokio::spawn(request_verification_handler(client, request));
135 },
136 );
137
138 client.add_event_handler(|ev: OriginalSyncRoomMessageEvent, client: Client| async move {
139 if let MessageType::VerificationRequest(_) = &ev.content.msgtype {
140 let request = client
141 .encryption()
142 .get_verification_request(&ev.sender, &ev.event_id)
143 .await
144 .expect("Request object wasn't created");
145
146 tokio::spawn(request_verification_handler(client, request));
147 }
148 });
149
150 client.sync(SyncSettings::new()).await?;
151
152 Ok(())
153}
154
155#[derive(Parser, Debug)]
156struct Cli {
157 #[clap(value_parser)]
159 homeserver: Url,
160
161 #[clap(value_parser)]
163 user_name: String,
164
165 #[clap(value_parser)]
167 password: String,
168
169 #[clap(short, long)]
171 proxy: Option<Url>,
172
173 #[clap(short, long, action)]
175 verbose: bool,
176}
177
178async fn login(cli: Cli) -> Result<Client> {
179 let builder = Client::builder().homeserver_url(cli.homeserver);
180
181 let builder = if let Some(proxy) = cli.proxy { builder.proxy(proxy) } else { builder };
182
183 let client = builder.build().await?;
184
185 client
186 .matrix_auth()
187 .login_username(&cli.user_name, &cli.password)
188 .initial_device_display_name("rust-sdk")
189 .await?;
190
191 Ok(client)
192}
193
194#[tokio::main]
195async fn main() -> Result<()> {
196 let cli = Cli::parse();
197
198 if cli.verbose {
199 tracing_subscriber::fmt::init();
200 }
201
202 let client = login(cli).await?;
203
204 sync(client).await?;
205
206 Ok(())
207}