feat(#2050): set matrix avatar in the daemon over the live client
This commit is contained in:
parent
63e9e80232
commit
c99fa714d9
8 changed files with 118 additions and 210 deletions
|
|
@ -184,3 +184,57 @@ async fn whoami(homeserver: &str, token: &str) -> Result<(OwnedUserId, OwnedDevi
|
|||
let device_id: OwnedDeviceId = device_id_raw.into();
|
||||
Ok((user_id, device_id))
|
||||
}
|
||||
|
||||
/// Best-effort: set this account's matrix avatar from the rasterized icon
|
||||
/// PNG. The path comes from `HIVE_ICON_PNG` (a nix-built derivation the
|
||||
/// harness forwards into the daemon env; absent when no icon resolves).
|
||||
/// Idempotent via a per-account `avatar-icon-hash` file in the account's
|
||||
/// sdk `state_dir` — a re-upload only happens when the icon bytes change,
|
||||
/// so daemon restarts don't re-spam the homeserver.
|
||||
///
|
||||
/// This replaces the old `matrix-avatar-sync` systemd curl oneshot: the
|
||||
/// daemon already holds an authenticated `Client` pointed at the correct
|
||||
/// homeserver, so it uploads over the live connection — no hardcoded URL,
|
||||
/// no token re-read, no token-file globbing. Any failure is logged and
|
||||
/// swallowed: avatar trouble must never break account bring-up or sync.
|
||||
pub async fn sync_avatar(client: &Client, state_dir: &Path, account: &str) {
|
||||
let Ok(png_path) = std::env::var("HIVE_ICON_PNG") else {
|
||||
return; // no icon forwarded → nothing to sync
|
||||
};
|
||||
let bytes = match fs::read(&png_path).await {
|
||||
Ok(b) => b,
|
||||
Err(e) => {
|
||||
tracing::warn!(account, path = %png_path, error = %e, "matrix avatar: icon PNG unreadable; skipping");
|
||||
return;
|
||||
}
|
||||
};
|
||||
// A non-crypto content hash is enough to answer "did the icon change?".
|
||||
let hash = {
|
||||
use std::hash::{Hash, Hasher};
|
||||
let mut h = std::collections::hash_map::DefaultHasher::new();
|
||||
bytes.hash(&mut h);
|
||||
format!("{:016x}", h.finish())
|
||||
};
|
||||
let hash_file = state_dir.join("avatar-icon-hash");
|
||||
if let Ok(prev) = fs::read_to_string(&hash_file).await
|
||||
&& prev.trim() == hash
|
||||
{
|
||||
tracing::debug!(account, "matrix avatar: icon unchanged; skipping upload");
|
||||
return;
|
||||
}
|
||||
match client
|
||||
.account()
|
||||
.upload_avatar(&mime::IMAGE_PNG, bytes)
|
||||
.await
|
||||
{
|
||||
Ok(mxc) => {
|
||||
tracing::info!(account, mxc = %mxc, "matrix avatar set");
|
||||
if let Err(e) = fs::write(&hash_file, &hash).await {
|
||||
tracing::warn!(account, error = %e, "matrix avatar: set ok but hash write failed (re-uploads next start)");
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
tracing::warn!(account, error = %format!("{e:#}"), "matrix avatar: upload failed; skipping (non-fatal)");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -196,6 +196,10 @@ async fn bring_up_account(
|
|||
client::build_and_restore(&homeserver, &cfg.token_file, &cfg.state_dir, is_primary)
|
||||
.await
|
||||
.with_context(|| format!("build matrix client for account {}", cfg.name))?;
|
||||
// Best-effort: sync the agent icon to this account's matrix avatar over
|
||||
// the live (authenticated, correct-homeserver) Client. Replaces the old
|
||||
// curl oneshot; failures are swallowed inside sync_avatar.
|
||||
client::sync_avatar(&client, &cfg.state_dir, &cfg.name).await;
|
||||
timeline::install_message_handler(&client, hyperhive_socket.to_path_buf(), tag.clone());
|
||||
|
||||
let sync_client = client.clone();
|
||||
|
|
|
|||
Loading…
Reference in a new issue