harness-base: matrix-avatar-sync oneshot mirroring forge avatar (#548 phase 2.5)
This commit is contained in:
parent
666c32ae65
commit
7e041e6079
1 changed files with 96 additions and 0 deletions
|
|
@ -745,6 +745,102 @@
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# One-shot: upload the agent's configured icon to its matrix profile
|
||||||
|
# avatar so the icon shows up next to messages in matrix rooms (#548
|
||||||
|
# phase 2.5). Mirrors the forge-avatar-sync flow above, only differs
|
||||||
|
# in protocol: matrix avatars are a two-step `media upload` → `set
|
||||||
|
# avatar_url` dance, both authenticated by the access_token written
|
||||||
|
# by hive-c0re's `matrix::ensure_user_for`. No-op when the icon
|
||||||
|
# isn't configured, the matrix token isn't present, or the
|
||||||
|
# homeserver isn't reachable. *Always* exits 0.
|
||||||
|
systemd.services.matrix-avatar-sync = {
|
||||||
|
description = "sync agent icon to matrix profile avatar (best-effort)";
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
# No `after = [ "tea-login.service" ]` — matrix has no
|
||||||
|
# equivalent prerequisite; we just need the homeserver up.
|
||||||
|
serviceConfig = {
|
||||||
|
Type = "oneshot";
|
||||||
|
RemainAfterExit = true;
|
||||||
|
};
|
||||||
|
path = [
|
||||||
|
pkgs.curl
|
||||||
|
pkgs.coreutils
|
||||||
|
pkgs.jq
|
||||||
|
pkgs.librsvg
|
||||||
|
];
|
||||||
|
script = ''
|
||||||
|
ICON=/etc/hyperhive/icon.svg
|
||||||
|
if [ ! -f "$ICON" ]; then
|
||||||
|
echo "matrix-avatar-sync: no icon configured; skipping"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
# Token written by `hive-c0re::matrix::ensure_user_for` to the
|
||||||
|
# agent's bind-mounted state dir. Either appears at the legacy
|
||||||
|
# `/state/` (manager) or per-agent `/agents/<name>/state/` path.
|
||||||
|
TOKEN_FILE=""
|
||||||
|
for f in /state/matrix-token /agents/*/state/matrix-token; do
|
||||||
|
if [ -f "$f" ]; then
|
||||||
|
TOKEN_FILE="$f"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
if [ -z "$TOKEN_FILE" ]; then
|
||||||
|
echo "matrix-avatar-sync: no matrix-token found; skipping"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
TOKEN=$(cat "$TOKEN_FILE")
|
||||||
|
# Local tuwunel reachable on shared host netns at the
|
||||||
|
# default matrix-spec port. Override via the future
|
||||||
|
# `hyperhive.matrix.url` if the operator ever runs the
|
||||||
|
# homeserver elsewhere (deferred to #548 phase 4).
|
||||||
|
MATRIX_URL=http://localhost:8008
|
||||||
|
# whoami → user_id. Needed to scope the avatar set call.
|
||||||
|
# Tolerant of the homeserver being unreachable (`-f` makes
|
||||||
|
# curl fail on 4xx/5xx; `|| true` swallows the exit).
|
||||||
|
USER_ID=$(curl -sf --max-time 5 \
|
||||||
|
-H "Authorization: Bearer $TOKEN" \
|
||||||
|
"$MATRIX_URL/_matrix/client/v3/account/whoami" 2>/dev/null \
|
||||||
|
| jq -r '.user_id // empty' || true)
|
||||||
|
if [ -z "$USER_ID" ]; then
|
||||||
|
echo "matrix-avatar-sync: whoami failed or homeserver unreachable; skipping"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
# Rasterize SVG → PNG (matrix media accepts any image type
|
||||||
|
# but we already standardise on PNG for the forge sync).
|
||||||
|
PNG=$(mktemp --suffix=.png)
|
||||||
|
if ! rsvg-convert -f png -w 512 -h 512 "$ICON" -o "$PNG" 2>/dev/null; then
|
||||||
|
echo "matrix-avatar-sync: rsvg-convert failed; skipping"
|
||||||
|
rm -f "$PNG"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
# Step 1: upload bytes → mxc:// URI.
|
||||||
|
MXC=$(curl -sf --max-time 10 \
|
||||||
|
-X POST "$MATRIX_URL/_matrix/media/v3/upload" \
|
||||||
|
-H "Authorization: Bearer $TOKEN" \
|
||||||
|
-H "Content-Type: image/png" \
|
||||||
|
--data-binary "@$PNG" 2>/dev/null \
|
||||||
|
| jq -r '.content_uri // empty' || true)
|
||||||
|
rm -f "$PNG"
|
||||||
|
if [ -z "$MXC" ]; then
|
||||||
|
echo "matrix-avatar-sync: media upload failed; skipping"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
# Step 2: set avatar_url on the profile.
|
||||||
|
PAYLOAD=$(jq -n --arg url "$MXC" '{avatar_url:$url}')
|
||||||
|
CODE=$(curl -s --max-time 10 \
|
||||||
|
-X PUT "$MATRIX_URL/_matrix/client/v3/profile/$USER_ID/avatar_url" \
|
||||||
|
-H "Authorization: Bearer $TOKEN" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d "$PAYLOAD" \
|
||||||
|
-o /dev/null -w "%{http_code}" 2>/dev/null || true)
|
||||||
|
if [ "$CODE" = "200" ]; then
|
||||||
|
echo "matrix-avatar-sync: avatar set on $USER_ID"
|
||||||
|
else
|
||||||
|
echo "matrix-avatar-sync: avatar PUT returned HTTP $CODE — skipping (non-fatal)"
|
||||||
|
fi
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
# Write declared dashboardLinks to the state dir so hive-c0re can read
|
# Write declared dashboardLinks to the state dir so hive-c0re can read
|
||||||
# them without accessing the container's /etc/ from the host.
|
# them without accessing the container's /etc/ from the host.
|
||||||
# Runs every boot; idempotent (overwrite). Always exits 0.
|
# Runs every boot; idempotent (overwrite). Always exits 0.
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue