atagen

macltok

public
Star0
Watch0
fa9114fcf75f7bdf1a7cec3b3367d604cf7de472
3
ssh://brewery@pls.lobotomise.me:33/atagen/macltok.git
examples/headless.rsRust93 lines3.8 KB
1
use macltok::{
2
    Command, CommandKind, EngineConfig, Event, LifecycleCommand, PasswordLogin, RoomCommand,
3
    RoomMembership, Session, StoreConfig, TimelineCommand,
4
};
5
6
#[tokio::main]
7
async fn main() -> Result<(), Box<dyn std::error::Error>> {
8
    let homeserver = required("MATRIX_HOMESERVER")?;
9
    let username = required("MATRIX_USERNAME")?;
10
    let password = required("MATRIX_PASSWORD")?;
11
    let store_path = required("MATRIX_STORE_PATH")?;
12
    let store_passphrase = required("MATRIX_STORE_PASSPHRASE")?;
13
    let session = Session::login_password(
14
        PasswordLogin {
15
            homeserver,
16
            username,
17
            password,
18
            device_display_name: Some("macltok headless example".into()),
19
            device_id: None,
20
        },
21
        StoreConfig::new(store_path, store_passphrase),
22
    )
23
    .await?;
24
    let (engine, handle, mut events) = session.build(EngineConfig::default()).await?;
25
    let running = tokio::spawn(engine.run());
26
    let mut room_open_id = None;
27
    let mut resync_id = None;
28
    let mut timeline_items = Vec::new();
29
30
    loop {
31
        tokio::select! {
32
            event = events.recv() => {
33
                let Some(event) = event else { break; };
34
                match event {
35
                    Event::RoomsChanged if room_open_id.is_none() => {
36
                        if let Some(room) = handle
37
                            .rooms()
38
                            .await
39
                            .into_iter()
40
                            .find(|room| room.membership == RoomMembership::Joined)
41
                        {
42
                            room_open_id = Some(handle
43
                                .send(Command::Room(RoomCommand::Open(room.room_id)))
44
                                .await?);
45
                        }
46
                    }
47
                    Event::TimelineReset { items, .. } => {
48
                        timeline_items = items;
49
                        resync_id = None;
50
                    }
51
                    Event::TimelineChanged { timeline_id, diffs } if resync_id.is_none() => {
52
                        for diff in diffs {
53
                            if diff.try_apply(&mut timeline_items).is_err() {
54
                                resync_id = Some(handle.send(Command::Timeline(
55
                                    TimelineCommand::Resync { timeline_id },
56
                                )).await?);
57
                                break;
58
                            }
59
                        }
60
                        if resync_id.is_none() {
61
                            for item in &timeline_items {
62
                                if let macltok::TimelineItem::Message(message) = item {
63
                                    println!("{}  {}", message.sender_name, message.body);
64
                                }
65
                            }
66
                        }
67
                    }
68
                    Event::CommandSucceeded { id, command: CommandKind::Room }
69
                        if room_open_id == Some(id) => println!("room ready"),
70
                    Event::CommandFailed(error) if room_open_id == Some(error.id)
71
                        || resync_id == Some(error.id) => return Err(error.message.into()),
72
                    Event::CommandFailed(error) => {
73
                        eprintln!("{:?}  {}", error.command, error.message);
74
                    }
75
                    Event::Stopped => break,
76
                    _ => {}
77
                }
78
            }
79
            result = tokio::signal::ctrl_c() => {
80
                result?;
81
                handle
82
                    .send(Command::Lifecycle(LifecycleCommand::Shutdown))
83
                    .await?;
84
            }
85
        }
86
    }
87
    running.await??;
88
    Ok(())
89
}
90
91
fn required(name: &str) -> Result<String, Box<dyn std::error::Error>> {
92
    std::env::var(name).map_err(|_| format!("{name} is required").into())
93
}