atagen

macltok

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