use std::sync::Arc;
use anyhow::Result;
+use futures_util::StreamExt;
use matrix_sdk::{
config::SyncSettings,
+ encryption::verification::{SasState, SasVerification, Verification, VerificationRequest, VerificationRequestState},
room::Room,
ruma::{
- events::room::message::{MessageType, OriginalSyncRoomMessageEvent, RoomMessageEventContent},
+ events::{
+ key::verification::request::ToDeviceKeyVerificationRequestEvent,
+ room::message::{MessageType, OriginalSyncRoomMessageEvent, RoomMessageEventContent},
+ },
OwnedRoomId, UserId,
},
Client,
};
-use tracing::{error, info};
+use tracing::{error, info, warn};
use crate::{
config::Config,
pub async fn run(&self) -> Result<()> {
info!("Starting bot event listener...");
+ // Start verification handler
+ self.start_verification_handler().await?;
+
// Register event handler
let db = self.db.clone();
self.client
Ok(())
}
+ async fn start_verification_handler(&self) -> Result<()> {
+ info!("Setting up verification handlers...");
+
+ let client = self.client.clone();
+
+ // To-device verification request handler
+ client.add_event_handler(
+ |ev: ToDeviceKeyVerificationRequestEvent, client: Client| async move {
+ info!("Received to-device verification request from {}", ev.sender);
+ if let Some(request) = client
+ .encryption()
+ .get_verification_request(&ev.sender, &ev.content.transaction_id)
+ .await
+ {
+ tokio::spawn(handle_verification_request(client, request));
+ } else {
+ warn!("Failed to get verification request object for transaction {}", ev.content.transaction_id);
+ }
+ },
+ );
+
+ // Room verification request handler
+ let client2 = self.client.clone();
+ client2.add_event_handler(|ev: OriginalSyncRoomMessageEvent, client: Client| async move {
+ if let MessageType::VerificationRequest(_) = &ev.content.msgtype {
+ info!("Received room verification request from {}", ev.sender);
+ if let Some(request) = client
+ .encryption()
+ .get_verification_request(&ev.sender, &ev.event_id)
+ .await
+ {
+ tokio::spawn(handle_verification_request(client, request));
+ } else {
+ warn!("Failed to get verification request object for event {}", ev.event_id);
+ }
+ }
+ });
+
+ info!("Verification handlers setup complete");
+ Ok(())
+ }
+
pub async fn send_message(&self, room_id: &OwnedRoomId, message: &str) -> Result<()> {
let content = RoomMessageEventContent::text_plain(message);
if let Some(room) = self.client.get_room(room_id) {
Ok(())
}
+async fn handle_verification_request(client: Client, request: VerificationRequest) {
+ info!("Processing verification request from {}", request.other_user_id());
+
+ // Accept the verification request
+ match request.accept().await {
+ Ok(_) => info!("Accepted verification request from {}", request.other_user_id()),
+ Err(e) => {
+ error!("Failed to accept verification request from {}: {}", request.other_user_id(), e);
+ return;
+ }
+ }
+
+ // Monitor the verification request state changes
+ let mut stream = request.changes();
+
+ while let Some(state) = stream.next().await {
+ match state {
+ VerificationRequestState::Transitioned { verification } => {
+ info!("Verification transitioned to specific method");
+ match verification {
+ Verification::SasV1(sas) => {
+ tokio::spawn(handle_sas_verification(client, sas));
+ break;
+ }
+ // QR code verification requires the qrcode feature which we don't have enabled
+ // So we only handle SAS verification
+ _ => {
+ info!("Unsupported verification method requested");
+ break;
+ }
+ }
+ }
+ VerificationRequestState::Done => {
+ info!("Verification completed successfully with {}", request.other_user_id());
+ break;
+ }
+ VerificationRequestState::Cancelled(cancel_info) => {
+ info!("Verification cancelled by {}: {}", request.other_user_id(), cancel_info.reason());
+ break;
+ }
+ VerificationRequestState::Created { .. }
+ | VerificationRequestState::Requested { .. }
+ | VerificationRequestState::Ready { .. } => {
+ // These are intermediate states, we can ignore them
+ }
+ }
+ }
+}
+
+async fn handle_sas_verification(_client: Client, sas: SasVerification) {
+ let user_id = sas.other_device().user_id();
+ let device_id = sas.other_device().device_id();
+
+ info!("Starting SAS verification with {} ({})", user_id, device_id);
+
+ // Accept the SAS verification
+ match sas.accept().await {
+ Ok(_) => info!("Accepted SAS verification with {} ({})", user_id, device_id),
+ Err(e) => {
+ error!("Failed to accept SAS verification with {} ({}): {}", user_id, device_id, e);
+ return;
+ }
+ }
+
+ // Monitor the SAS verification state changes
+ let mut stream = sas.changes();
+
+ while let Some(state) = stream.next().await {
+ match state {
+ SasState::KeysExchanged { emojis, decimals } => {
+ info!("SAS keys exchanged with {} ({})", user_id, device_id);
+
+ // Log the emojis for auditing purposes
+ if let Some(emoji_list) = emojis {
+ info!("SAS emojis: {:?}", emoji_list.emojis);
+ }
+
+ // Log the decimal codes for auditing (decimals is a tuple of three u16 values)
+ info!("SAS decimals: {} {} {}", decimals.0, decimals.1, decimals.2);
+
+ // Auto-confirm the SAS verification (bot doesn't need manual confirmation)
+ match sas.confirm().await {
+ Ok(_) => info!("Confirmed SAS verification with {} ({})", user_id, device_id),
+ Err(e) => error!("Failed to confirm SAS verification with {} ({}): {}", user_id, device_id, e),
+ }
+ }
+ SasState::Done { .. } => {
+ info!("SAS verification completed successfully with {} ({})", user_id, device_id);
+
+ // The device is now verified locally
+ let device = sas.other_device();
+ info!(
+ "Device {} {} is now verified locally with trust state: {:?}",
+ device.user_id(),
+ device.device_id(),
+ device.local_trust_state()
+ );
+ break;
+ }
+ SasState::Cancelled(cancel_info) => {
+ info!("SAS verification cancelled by {} ({}): {}", user_id, device_id, cancel_info.reason());
+ break;
+ }
+ SasState::Created { .. }
+ | SasState::Started { .. }
+ | SasState::Accepted { .. }
+ | SasState::Confirmed => {
+ // These are intermediate states, we can ignore them
+ }
+ }
+ }
+}
\ No newline at end of file