atagen

macltok

public
Star0
Watch0
31ce2453c82f470aa8b5d3861ab0eec4273afc5d
3
ssh://brewery@pls.lobotomise.me:33/atagen/macltok.git

Blame: examples/headless.rs

96fdf90aacd6atagen2026-09-08 08:24 UTC1
use macltok::{
31ce2453c82fatagen2026-09-24 13:07 UTC2
    EngineConfig, Event, PasswordLogin, RoomMembership, Session, StoreConfig, TimelineId,
31ce2453c82fatagen2026-09-24 13:07 UTC3
    TimelineItem,
96fdf90aacd6atagen2026-09-08 08:24 UTC4
};
96fdf90aacd6atagen2026-09-08 08:24 UTC5
96fdf90aacd6atagen2026-09-08 08:24 UTC6
#[tokio::main]
96fdf90aacd6atagen2026-09-08 08:24 UTC7
async fn main() -> Result<(), Box<dyn std::error::Error>> {
96fdf90aacd6atagen2026-09-08 08:24 UTC8
    let session = Session::login_password(
31ce2453c82fatagen2026-09-24 13:07 UTC9
        PasswordLogin::new(
31ce2453c82fatagen2026-09-24 13:07 UTC10
            required("MATRIX_HOMESERVER")?,
31ce2453c82fatagen2026-09-24 13:07 UTC11
            required("MATRIX_USERNAME")?,
31ce2453c82fatagen2026-09-24 13:07 UTC12
            required("MATRIX_PASSWORD")?,
31ce2453c82fatagen2026-09-24 13:07 UTC13
        )
31ce2453c82fatagen2026-09-24 13:07 UTC14
        .with_device_display_name("macltok headless example"),
31ce2453c82fatagen2026-09-24 13:07 UTC15
        StoreConfig::new(
31ce2453c82fatagen2026-09-24 13:07 UTC16
            required("MATRIX_STORE_PATH")?,
31ce2453c82fatagen2026-09-24 13:07 UTC17
            required("MATRIX_STORE_PASSPHRASE")?,
31ce2453c82fatagen2026-09-24 13:07 UTC18
        ),
96fdf90aacd6atagen2026-09-08 08:24 UTC19
    )
96fdf90aacd6atagen2026-09-08 08:24 UTC20
    .await?;
96fdf90aacd6atagen2026-09-08 08:24 UTC21
    let (engine, handle, mut events) = session.build(EngineConfig::default()).await?;
96fdf90aacd6atagen2026-09-08 08:24 UTC22
    let running = tokio::spawn(engine.run());
31ce2453c82fatagen2026-09-24 13:07 UTC23
    let mut opened: Option<TimelineId> = None;
31ce2453c82fatagen2026-09-24 13:07 UTC24
    let mut items = Vec::new();
31ce2453c82fatagen2026-09-24 13:07 UTC25
    let mut stale = false;
96fdf90aacd6atagen2026-09-08 08:24 UTC26
96fdf90aacd6atagen2026-09-08 08:24 UTC27
    loop {
96fdf90aacd6atagen2026-09-08 08:24 UTC28
        tokio::select! {
96fdf90aacd6atagen2026-09-08 08:24 UTC29
            event = events.recv() => {
31ce2453c82fatagen2026-09-24 13:07 UTC30
                let Some(event) = event else { break };
96fdf90aacd6atagen2026-09-08 08:24 UTC31
                match event {
31ce2453c82fatagen2026-09-24 13:07 UTC32
                    Event::RoomsChanged if opened.is_none() => {
31ce2453c82fatagen2026-09-24 13:07 UTC33
                        let Some(room) = handle
96fdf90aacd6atagen2026-09-08 08:24 UTC34
                            .rooms()
96fdf90aacd6atagen2026-09-08 08:24 UTC35
                            .await
96fdf90aacd6atagen2026-09-08 08:24 UTC36
                            .into_iter()
96fdf90aacd6atagen2026-09-08 08:24 UTC37
                            .find(|room| room.membership == RoomMembership::Joined)
31ce2453c82fatagen2026-09-24 13:07 UTC38
                        else {
31ce2453c82fatagen2026-09-24 13:07 UTC39
                            continue;
31ce2453c82fatagen2026-09-24 13:07 UTC40
                        };
31ce2453c82fatagen2026-09-24 13:07 UTC41
                        opened = Some(room.room_id.clone().into());
31ce2453c82fatagen2026-09-24 13:07 UTC42
                        // answers arrive on their own, the event loop keeps draining meanwhile
31ce2453c82fatagen2026-09-24 13:07 UTC43
                        let handle = handle.clone();
31ce2453c82fatagen2026-09-24 13:07 UTC44
                        tokio::spawn(async move {
31ce2453c82fatagen2026-09-24 13:07 UTC45
                            if let Err(error) = handle.open_room(room.room_id).await {
31ce2453c82fatagen2026-09-24 13:07 UTC46
                                eprintln!("could not open {error}");
31ce2453c82fatagen2026-09-24 13:07 UTC47
                            }
31ce2453c82fatagen2026-09-24 13:07 UTC48
                        });
96fdf90aacd6atagen2026-09-08 08:24 UTC49
                    }
31ce2453c82fatagen2026-09-24 13:07 UTC50
                    Event::TimelineReset { timeline_id, items: snapshot }
31ce2453c82fatagen2026-09-24 13:07 UTC51
                        if opened.as_ref() == Some(&timeline_id) =>
31ce2453c82fatagen2026-09-24 13:07 UTC52
                    {
31ce2453c82fatagen2026-09-24 13:07 UTC53
                        items = snapshot;
31ce2453c82fatagen2026-09-24 13:07 UTC54
                        stale = false;
31ce2453c82fatagen2026-09-24 13:07 UTC55
                        print_messages(&items);
96fdf90aacd6atagen2026-09-08 08:24 UTC56
                    }
31ce2453c82fatagen2026-09-24 13:07 UTC57
                    Event::TimelineChanged { timeline_id, diffs }
31ce2453c82fatagen2026-09-24 13:07 UTC58
                        if !stale && opened.as_ref() == Some(&timeline_id) =>
31ce2453c82fatagen2026-09-24 13:07 UTC59
                    {
31ce2453c82fatagen2026-09-24 13:07 UTC60
                        if diffs.into_iter().any(|diff| diff.try_apply(&mut items).is_err()) {
31ce2453c82fatagen2026-09-24 13:07 UTC61
                            stale = true;
31ce2453c82fatagen2026-09-24 13:07 UTC62
                            let handle = handle.clone();
31ce2453c82fatagen2026-09-24 13:07 UTC63
                            tokio::spawn(async move { handle.resync(timeline_id).await });
31ce2453c82fatagen2026-09-24 13:07 UTC64
                            continue;
96fdf90aacd6atagen2026-09-08 08:24 UTC65
                        }
31ce2453c82fatagen2026-09-24 13:07 UTC66
                        print_messages(&items);
96fdf90aacd6atagen2026-09-08 08:24 UTC67
                    }
96fdf90aacd6atagen2026-09-08 08:24 UTC68
                    Event::Stopped => break,
96fdf90aacd6atagen2026-09-08 08:24 UTC69
                    _ => {}
96fdf90aacd6atagen2026-09-08 08:24 UTC70
                }
96fdf90aacd6atagen2026-09-08 08:24 UTC71
            }
96fdf90aacd6atagen2026-09-08 08:24 UTC72
            result = tokio::signal::ctrl_c() => {
96fdf90aacd6atagen2026-09-08 08:24 UTC73
                result?;
31ce2453c82fatagen2026-09-24 13:07 UTC74
                let handle = handle.clone();
31ce2453c82fatagen2026-09-24 13:07 UTC75
                tokio::spawn(async move { handle.shutdown().await });
96fdf90aacd6atagen2026-09-08 08:24 UTC76
            }
96fdf90aacd6atagen2026-09-08 08:24 UTC77
        }
96fdf90aacd6atagen2026-09-08 08:24 UTC78
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC79
    running.await??;
96fdf90aacd6atagen2026-09-08 08:24 UTC80
    Ok(())
96fdf90aacd6atagen2026-09-08 08:24 UTC81
}
96fdf90aacd6atagen2026-09-08 08:24 UTC82
31ce2453c82fatagen2026-09-24 13:07 UTC83
fn print_messages(items: &[TimelineItem]) {
31ce2453c82fatagen2026-09-24 13:07 UTC84
    for item in items {
31ce2453c82fatagen2026-09-24 13:07 UTC85
        if let TimelineItem::Message(message) = item {
31ce2453c82fatagen2026-09-24 13:07 UTC86
            println!("{}  {}", message.sender_name, message.body);
31ce2453c82fatagen2026-09-24 13:07 UTC87
        }
31ce2453c82fatagen2026-09-24 13:07 UTC88
    }
31ce2453c82fatagen2026-09-24 13:07 UTC89
}
31ce2453c82fatagen2026-09-24 13:07 UTC90
96fdf90aacd6atagen2026-09-08 08:24 UTC91
fn required(name: &str) -> Result<String, Box<dyn std::error::Error>> {
96fdf90aacd6atagen2026-09-08 08:24 UTC92
    std::env::var(name).map_err(|_| format!("{name} is required").into())
96fdf90aacd6atagen2026-09-08 08:24 UTC93
}