atagen

matrix-rust-sdk

public
Star0
Watch0
ssh://brewery@pls.lobotomise.me:33/atagen/matrix-rust-sdk.git
examples/backups/src/main.rsRust120 lines3.4 KB
1
use anyhow::Result;
2
use clap::Parser;
3
use futures_util::{StreamExt, pin_mut};
4
use matrix_sdk::{
5
    Client,
6
    config::SyncSettings,
7
    encryption::{backups::BackupState, secret_storage::SecretStore},
8
};
9
use url::Url;
10
11
/// A command line example showcasing how to resume backups by importing the
12
/// backup key from secret storage.
13
#[derive(Parser, Debug)]
14
struct Cli {
15
    /// The homeserver to connect to.
16
    #[clap(value_parser)]
17
    homeserver: Url,
18
19
    /// The user ID that should be used to restore the session.
20
    #[clap(value_parser)]
21
    user_name: String,
22
23
    /// The password that should be used for the login.
24
    #[clap(value_parser)]
25
    password: String,
26
27
    /// Set the proxy that should be used for the connection.
28
    #[clap(short, long)]
29
    proxy: Option<Url>,
30
31
    /// Enable verbose logging output.
32
    #[clap(short, long, action)]
33
    verbose: bool,
34
35
    /// The secret storage key, this key will be used to open the secret-store.
36
    #[clap(long, action)]
37
    secret_store_key: String,
38
}
39
40
async fn import_known_secrets(client: &Client, secret_store: SecretStore) -> Result<()> {
41
    secret_store.import_secrets().await?;
42
43
    let status = client
44
        .encryption()
45
        .cross_signing_status()
46
        .await
47
        .expect("We should be able to get our cross-signing status");
48
49
    if status.is_complete() {
50
        println!("Successfully imported all the cross-signing keys");
51
    } else {
52
        eprintln!("Couldn't import all the cross-signing keys: {status:?}");
53
    }
54
55
    Ok(())
56
}
57
58
async fn login(cli: &Cli) -> Result<Client> {
59
    let builder = Client::builder().homeserver_url(&cli.homeserver);
60
61
    let builder = if let Some(proxy) = &cli.proxy { builder.proxy(proxy) } else { builder };
62
63
    let client = builder.build().await?;
64
65
    client
66
        .matrix_auth()
67
        .login_username(&cli.user_name, &cli.password)
68
        .initial_device_display_name("rust-sdk")
69
        .await?;
70
71
    Ok(client)
72
}
73
74
async fn listen_for_backup_state_changes(client: Client) {
75
    let stream = client.encryption().backups().state_stream();
76
    pin_mut!(stream);
77
78
    while let Some(state) = stream.next().await {
79
        let Ok(state) = state else { panic!("Error while receiving backup state updates") };
80
81
        match state {
82
            BackupState::Unknown => (),
83
            BackupState::Enabling => println!("Trying to enable backups"),
84
            BackupState::Resuming => println!("Trying to resume backups"),
85
            BackupState::Enabled => println!("Backups have been successfully enabled"),
86
            BackupState::Downloading => println!("Downloading the room keys from the backup"),
87
            BackupState::Disabling => println!("Disabling the backup"),
88
            BackupState::Creating => println!("Trying to create a new backup"),
89
        }
90
    }
91
}
92
93
#[tokio::main]
94
async fn main() -> Result<()> {
95
    let cli = Cli::parse();
96
97
    if cli.verbose {
98
        tracing_subscriber::fmt::init();
99
    }
100
101
    let client = login(&cli).await?;
102
103
    client.sync_once(Default::default()).await?;
104
105
    let secret_store =
106
        client.encryption().secret_storage().open_secret_store(&cli.secret_store_key).await?;
107
108
    let _task = tokio::spawn({
109
        let client = client.clone();
110
        async move { listen_for_backup_state_changes(client).await }
111
    });
112
113
    import_known_secrets(&client, secret_store).await?;
114
115
    loop {
116
        if let Err(e) = client.sync(SyncSettings::new()).await {
117
            eprintln!("Error syncing: {e:?}")
118
        }
119
    }
120
}