atagen

macltok

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

Blame: examples/headless.rs

f12377bce9b9atagen2026-09-26 12:32 UTC1
use std::sync::Arc;
f12377bce9b9atagen2026-09-26 12:32 UTC2
96fdf90aacd6atagen2026-09-08 08:24 UTC3
use macltok::{
6f1d6591c10datagen2026-09-26 09:52 UTC4
    EncryptedSessionStore, EngineConfig, Event, PasswordLogin, Restore, Retain, RoomMembership,
6f1d6591c10datagen2026-09-26 09:52 UTC5
    Session, SessionStore, StoreConfig, TimelineContent, TimelineDiff, TimelineId, TimelineItem,
713205a95af2atagen2026-09-26 09:24 UTC6
    imbl::Vector,
96fdf90aacd6atagen2026-09-08 08:24 UTC7
};
96fdf90aacd6atagen2026-09-08 08:24 UTC8
96fdf90aacd6atagen2026-09-08 08:24 UTC9
#[tokio::main]
96fdf90aacd6atagen2026-09-08 08:24 UTC10
async fn main() -> Result<(), Box<dyn std::error::Error>> {
5a52012c9b1datagen2026-09-24 23:45 UTC11
    let store_path = std::path::PathBuf::from(required("MATRIX_STORE_PATH")?);
5a52012c9b1datagen2026-09-24 23:45 UTC12
    let passphrase = required("MATRIX_STORE_PASSPHRASE")?;
f12377bce9b9atagen2026-09-26 12:32 UTC13
    let sessions = Arc::new(EncryptedSessionStore::file(
713205a95af2atagen2026-09-26 09:24 UTC14
        store_path.with_extension("session"),
713205a95af2atagen2026-09-26 09:24 UTC15
        StoreConfig::sqlite(&store_path, &passphrase),
f12377bce9b9atagen2026-09-26 12:32 UTC16
    ));
f12377bce9b9atagen2026-09-26 12:32 UTC17
    let session = match Session::restore(sessions.clone()).await? {
6f1d6591c10datagen2026-09-26 09:52 UTC18
        Some(Restore::Resumed(session)) => session,
6f1d6591c10datagen2026-09-26 09:52 UTC19
        Some(Restore::SoftLoggedOut(soft)) => {
f12377bce9b9atagen2026-09-26 12:32 UTC20
            Session::login_password(
f12377bce9b9atagen2026-09-26 12:32 UTC21
                soft.password_login(required("MATRIX_PASSWORD")?),
f12377bce9b9atagen2026-09-26 12:32 UTC22
                sessions.clone(),
f12377bce9b9atagen2026-09-26 12:32 UTC23
            )
f12377bce9b9atagen2026-09-26 12:32 UTC24
            .await?
6f1d6591c10datagen2026-09-26 09:52 UTC25
        }
d8d8a9fe770aatagen2026-10-22 07:44 UTC26
        None | Some(Restore::OAuthPending) => {
713205a95af2atagen2026-09-26 09:24 UTC27
            Session::login_password(
5a52012c9b1datagen2026-09-24 23:45 UTC28
                PasswordLogin::new(
5a52012c9b1datagen2026-09-24 23:45 UTC29
                    required("MATRIX_HOMESERVER")?,
5a52012c9b1datagen2026-09-24 23:45 UTC30
                    required("MATRIX_USERNAME")?,
5a52012c9b1datagen2026-09-24 23:45 UTC31
                    required("MATRIX_PASSWORD")?,
5a52012c9b1datagen2026-09-24 23:45 UTC32
                )
5a52012c9b1datagen2026-09-24 23:45 UTC33
                .with_device_display_name("macltok headless example"),
f12377bce9b9atagen2026-09-26 12:32 UTC34
                sessions.clone(),
5a52012c9b1datagen2026-09-24 23:45 UTC35
            )
713205a95af2atagen2026-09-26 09:24 UTC36
            .await?
5a52012c9b1datagen2026-09-24 23:45 UTC37
        }
5a52012c9b1datagen2026-09-24 23:45 UTC38
    };
96fdf90aacd6atagen2026-09-08 08:24 UTC39
    let (engine, handle, mut events) = session.build(EngineConfig::default()).await?;
96fdf90aacd6atagen2026-09-08 08:24 UTC40
    let running = tokio::spawn(engine.run());
31ce2453c82fatagen2026-09-24 13:07 UTC41
    let mut opened: Option<TimelineId> = None;
d5d8097c3afdatagen2026-09-26 09:33 UTC42
    let mut items = Vector::new();
31ce2453c82fatagen2026-09-24 13:07 UTC43
    let mut stale = false;
96fdf90aacd6atagen2026-09-08 08:24 UTC44
96fdf90aacd6atagen2026-09-08 08:24 UTC45
    loop {
96fdf90aacd6atagen2026-09-08 08:24 UTC46
        tokio::select! {
96fdf90aacd6atagen2026-09-08 08:24 UTC47
            event = events.recv() => {
31ce2453c82fatagen2026-09-24 13:07 UTC48
                let Some(event) = event else { break };
96fdf90aacd6atagen2026-09-08 08:24 UTC49
                match event {
31ce2453c82fatagen2026-09-24 13:07 UTC50
                    Event::RoomsChanged if opened.is_none() => {
31ce2453c82fatagen2026-09-24 13:07 UTC51
                        let Some(room) = handle
96fdf90aacd6atagen2026-09-08 08:24 UTC52
                            .rooms()
96fdf90aacd6atagen2026-09-08 08:24 UTC53
                            .await
96fdf90aacd6atagen2026-09-08 08:24 UTC54
                            .into_iter()
96fdf90aacd6atagen2026-09-08 08:24 UTC55
                            .find(|room| room.membership == RoomMembership::Joined)
31ce2453c82fatagen2026-09-24 13:07 UTC56
                        else {
31ce2453c82fatagen2026-09-24 13:07 UTC57
                            continue;
31ce2453c82fatagen2026-09-24 13:07 UTC58
                        };
31ce2453c82fatagen2026-09-24 13:07 UTC59
                        opened = Some(room.room_id.clone().into());
31ce2453c82fatagen2026-09-24 13:07 UTC60
                        // answers arrive on their own, the event loop keeps draining meanwhile
31ce2453c82fatagen2026-09-24 13:07 UTC61
                        let handle = handle.clone();
31ce2453c82fatagen2026-09-24 13:07 UTC62
                        tokio::spawn(async move {
31ce2453c82fatagen2026-09-24 13:07 UTC63
                            if let Err(error) = handle.open_room(room.room_id).await {
31ce2453c82fatagen2026-09-24 13:07 UTC64
                                eprintln!("could not open {error}");
31ce2453c82fatagen2026-09-24 13:07 UTC65
                            }
31ce2453c82fatagen2026-09-24 13:07 UTC66
                        });
96fdf90aacd6atagen2026-09-08 08:24 UTC67
                    }
31ce2453c82fatagen2026-09-24 13:07 UTC68
                    Event::TimelineReset { timeline_id, items: snapshot }
31ce2453c82fatagen2026-09-24 13:07 UTC69
                        if opened.as_ref() == Some(&timeline_id) =>
31ce2453c82fatagen2026-09-24 13:07 UTC70
                    {
31ce2453c82fatagen2026-09-24 13:07 UTC71
                        items = snapshot;
31ce2453c82fatagen2026-09-24 13:07 UTC72
                        stale = false;
31ce2453c82fatagen2026-09-24 13:07 UTC73
                        print_messages(&items);
96fdf90aacd6atagen2026-09-08 08:24 UTC74
                    }
31ce2453c82fatagen2026-09-24 13:07 UTC75
                    Event::TimelineChanged { timeline_id, diffs }
31ce2453c82fatagen2026-09-24 13:07 UTC76
                        if !stale && opened.as_ref() == Some(&timeline_id) =>
31ce2453c82fatagen2026-09-24 13:07 UTC77
                    {
d5d8097c3afdatagen2026-09-26 09:33 UTC78
                        if TimelineDiff::try_apply_all(diffs, &mut items).is_err() {
31ce2453c82fatagen2026-09-24 13:07 UTC79
                            stale = true;
31ce2453c82fatagen2026-09-24 13:07 UTC80
                            let handle = handle.clone();
31ce2453c82fatagen2026-09-24 13:07 UTC81
                            tokio::spawn(async move { handle.resync(timeline_id).await });
31ce2453c82fatagen2026-09-24 13:07 UTC82
                            continue;
96fdf90aacd6atagen2026-09-08 08:24 UTC83
                        }
31ce2453c82fatagen2026-09-24 13:07 UTC84
                        print_messages(&items);
96fdf90aacd6atagen2026-09-08 08:24 UTC85
                    }
96fdf90aacd6atagen2026-09-08 08:24 UTC86
                    Event::Stopped => break,
96fdf90aacd6atagen2026-09-08 08:24 UTC87
                    _ => {}
96fdf90aacd6atagen2026-09-08 08:24 UTC88
                }
96fdf90aacd6atagen2026-09-08 08:24 UTC89
            }
96fdf90aacd6atagen2026-09-08 08:24 UTC90
            result = tokio::signal::ctrl_c() => {
96fdf90aacd6atagen2026-09-08 08:24 UTC91
                result?;
31ce2453c82fatagen2026-09-24 13:07 UTC92
                let handle = handle.clone();
31ce2453c82fatagen2026-09-24 13:07 UTC93
                tokio::spawn(async move { handle.shutdown().await });
96fdf90aacd6atagen2026-09-08 08:24 UTC94
            }
96fdf90aacd6atagen2026-09-08 08:24 UTC95
        }
96fdf90aacd6atagen2026-09-08 08:24 UTC96
    }
713205a95af2atagen2026-09-26 09:24 UTC97
    let termination = running.await??;
713205a95af2atagen2026-09-26 09:24 UTC98
    drop((handle, events));
713205a95af2atagen2026-09-26 09:24 UTC99
    match termination.retain() {
fe4133dbcd47atagen2026-09-26 09:05 UTC100
        Retain::RecordAndStore => {}
6f1d6591c10datagen2026-09-26 09:52 UTC101
        Retain::Store => sessions.soft_log_out().await?,
713205a95af2atagen2026-09-26 09:24 UTC102
        Retain::Nothing => sessions.discard().await?,
fe4133dbcd47atagen2026-09-26 09:05 UTC103
    }
96fdf90aacd6atagen2026-09-08 08:24 UTC104
    Ok(())
96fdf90aacd6atagen2026-09-08 08:24 UTC105
}
96fdf90aacd6atagen2026-09-08 08:24 UTC106
d5d8097c3afdatagen2026-09-26 09:33 UTC107
fn print_messages(items: &Vector<TimelineItem>) {
31ce2453c82fatagen2026-09-24 13:07 UTC108
    for item in items {
960ac0721079atagen2026-09-25 00:24 UTC109
        if let TimelineItem::Event(event) = item
960ac0721079atagen2026-09-25 00:24 UTC110
            && let TimelineContent::Message(message) = &event.content
960ac0721079atagen2026-09-25 00:24 UTC111
        {
960ac0721079atagen2026-09-25 00:24 UTC112
            println!("{}  {}", event.sender.name(), message.body);
31ce2453c82fatagen2026-09-24 13:07 UTC113
        }
31ce2453c82fatagen2026-09-24 13:07 UTC114
    }
31ce2453c82fatagen2026-09-24 13:07 UTC115
}
31ce2453c82fatagen2026-09-24 13:07 UTC116
96fdf90aacd6atagen2026-09-08 08:24 UTC117
fn required(name: &str) -> Result<String, Box<dyn std::error::Error>> {
96fdf90aacd6atagen2026-09-08 08:24 UTC118
    std::env::var(name).map_err(|_| format!("{name} is required").into())
96fdf90aacd6atagen2026-09-08 08:24 UTC119
}