use anyhow::Result;
use matrix_sdk::ruma::events::room::message::RoomMessageEventContent;
use matrix_sdk::ruma::OwnedRoomId;
use matrix_sdk::Client;
#[tokio::main]
async fn main() -> Result<()> {
tracing_subscriber::fmt()
.with_env_filter(
tracing_subscriber::EnvFilter::try_from_default_env()
.unwrap_or_else(|_| "test_sdk=info,matrix_sdk=warn".parse().unwrap()),
)
.init();
let args: Vec<String> = std::env::args().collect();
if args.len() < 4 {
eprintln!("Usage: test_sdk <homeserver> <username> <password> [room_id]");
eprintln!("Example: test_sdk https://matrix.org myuser mypass \"!abc:matrix.org\"");
std::process::exit(1);
}
let homeserver = &args[1];
let username = &args[2];
let password = &args[3];
let room_id_str = args.get(4).map(|s| s.as_str());
println!("\n[1/5] Building client for {homeserver}...");
let data_dir = std::env::temp_dir().join("synpad-test-sdk");
std::fs::create_dir_all(&data_dir)?;
let client = Client::builder()
.homeserver_url(homeserver)
.sqlite_store(&data_dir, None)
.build()
.await?;
println!(" OK - Client built (store: {})", data_dir.display());
println!("\n[2/5] Logging in as {username}...");
client
.matrix_auth()
.login_username(username, password)
.initial_device_display_name("Netrix Test")
.await?;
let user_id = client.user_id().expect("No user ID after login");
let device_id = client.device_id().expect("No device ID after login");
println!(" OK - Logged in as {user_id} (device: {device_id})");
println!("\n[3/5] Running initial sync...");
let sync_settings = matrix_sdk::config::SyncSettings::default()
.timeout(std::time::Duration::from_secs(10));
client.sync_once(sync_settings).await?;
println!(" OK - Sync complete");
let joined = client.joined_rooms();
println!("\n Joined rooms ({}):", joined.len());
for room in &joined {
let name = match room.display_name().await {
Ok(n) => n.to_string(),
Err(_) => "(unknown)".to_string(),
};
let members = room.joined_members_count();
println!(" {} - {} ({} members)", room.room_id(), name, members);
}
let Some(room_id_str) = room_id_str else {
println!("\n No room_id provided. Pass a room ID as 4th argument to test messaging.");
println!(" Example: cargo run --bin test_sdk -- {homeserver} {username} <password> \"!roomid:server\"");
cleanup(&client).await;
return Ok(());
};
let room_id: OwnedRoomId = room_id_str.try_into()?;
let room = client
.get_room(&room_id)
.ok_or_else(|| anyhow::anyhow!("Room not found: {room_id}"))?;
println!("\n[4/5] Loading timeline for {room_id}...");
let options = matrix_sdk::room::MessagesOptions::backward();
let response = room.messages(options).await?;
println!(
" OK - Got {} events (pagination token: {})",
response.chunk.len(),
response.end.as_deref().unwrap_or("none")
);
for event in response.chunk.iter().rev().take(5) {
let raw = event.raw();
if let Ok(json) = serde_json::from_str::<serde_json::Value>(raw.json().get()) {
let sender = json.get("sender").and_then(|v| v.as_str()).unwrap_or("?");
let event_type = json.get("type").and_then(|v| v.as_str()).unwrap_or("?");
if event_type == "m.room.message" {
let body = json
.get("content")
.and_then(|c| c.get("body"))
.and_then(|b| b.as_str())
.unwrap_or("(no body)");
println!(" [{sender}] {body}");
} else {
println!(" [{sender}] ({event_type})");
}
}
}
let test_msg = format!(
"Netrix headless test - {}",
chrono::Local::now().format("%Y-%m-%d %H:%M:%S")
);
println!("\n[5/5] Sending test message: \"{test_msg}\"...");
let content = RoomMessageEventContent::text_plain(&test_msg);
let _send_response = room.send(content).await?;
println!(" OK - Message sent!");
println!("\n Syncing to verify...");
let sync_settings = matrix_sdk::config::SyncSettings::default()
.timeout(std::time::Duration::from_secs(5));
client.sync_once(sync_settings).await?;
let options = matrix_sdk::room::MessagesOptions::backward();
let response = room.messages(options).await?;
let mut found = false;
for event in &response.chunk {
let raw = event.raw();
if let Ok(json) = serde_json::from_str::<serde_json::Value>(raw.json().get()) {
let body = json
.get("content")
.and_then(|c| c.get("body"))
.and_then(|b| b.as_str())
.unwrap_or("");
if body == test_msg {
found = true;
break;
}
}
}
if found {
println!(" VERIFIED - Test message found in timeline!");
} else {
println!(" WARNING - Test message not yet visible in timeline (may appear after next sync)");
}
cleanup(&client).await;
println!("\n=== All tests passed! ===\n");
Ok(())
}
async fn cleanup(client: &Client) {
println!("\n Logging out test device...");
if let Err(e) = client.matrix_auth().logout().await {
eprintln!(" Warning: logout failed: {e}");
} else {
println!(" OK - Logged out");
}
}