#!/usr/bin/env bash # Send one prepared post (pr-hall-of-helix/posts/.yaml) through a # short-lived local adapter, then stop it. # # The bot token goes from OpenBao straight into the adapter's environment and # is never printed. The adapter's store is in memory, so idempotency on post_id # holds only within one run: do not re-run for a post that already went out. # # Run it yourself inside the attended OpenBao login. warden fails closed on any # output, so redirect both streams, and use absolute paths (HOME is replaced): # # warden access openbao-platform-admin-login --exec -- bash -c \ # '/home/worsch/fluid-telegram/scripts/send-post.sh \ # /home/worsch/pr-hall-of-helix/posts/.yaml test \ # > /home/worsch/send-post.log 2>&1' set -euo pipefail post=$(realpath "$1") channel=${2:-test} port=${PORT:-18089} cd "$(dirname "$(realpath "$0")")/.." spec=${SPEC:-$(realpath ../pr-hall-of-helix/presence/telegram.yaml)} # Under `warden access ... --exec`, HOME is a throwaway session directory that # holds the OpenBao token helper. Keep Go's caches at the real home so the # build does not start from nothing there. realhome=$(getent passwd "$(id -u)" | cut -d: -f6) export GOPATH=${GOPATH:-$realhome/go} export GOMODCACHE=${GOMODCACHE:-$GOPATH/pkg/mod} export GOCACHE=${GOCACHE:-$realhome/.cache/go-build} read -r want reviewed < <(python3 -c ' import sys, yaml d = yaml.safe_load(open(sys.argv[1])) print(d["channel"], d["request"].get("reviewed_by") or "-")' "$post") [ "$want" = "$channel" ] || { echo "post is for channel '$want', not '$channel'" >&2; exit 2; } [ "$reviewed" != "-" ] || { echo "request.reviewed_by is empty; a person reviews before sending" >&2; exit 2; } TELEGRAM_CHANNEL_ID=$(go run ./cmd/provision adapter-config --spec "$spec" --channel "$channel" \ | sed -n 's/^TELEGRAM_CHANNEL_ID=//p') [ -n "$TELEGRAM_CHANNEL_ID" ] || { echo "no chat id for channel '$channel'" >&2; exit 2; } tmp=$(mktemp -d) go build -o "$tmp/adapter" ./cmd/adapter go build -o "$tmp/compose" ./cmd/compose # The image, resolved before any credential is touched. A post's own # request.visual_url wins; otherwise its source entry's rendered portrait; # an entry with only a visual prompt is held until the portrait is rendered; # a post with no prompt at all (an outlet post) carries the campaign image. # Public posts always carry an image. hall=${HALL_DIR:-$(realpath ../hall-of-helix)} forge=${FORGE_RAW:-https://forgejo.coulomb.social/coulomb} python3 - "$post" "$channel" "$tmp" "$hall" "$forge" <<'PY' || { rm -rf "$tmp"; exit 2; } import json, os, subprocess, sys, yaml post, channel, tmp, hall, forge = sys.argv[1:] d = yaml.safe_load(open(post)) req = d["request"] if not req.get("visual_url"): cmd = [f"{tmp}/compose", "visual", "--hall-dir", hall, "--hall-raw-base", f"{forge}/hall-of-helix/raw/branch/main", "--fallback", f"{forge}/pr-hall-of-helix/raw/branch/main/assets/helix-forge.png"] if d.get("source_entry_path"): cmd += ["--entry", os.path.join(hall, d["source_entry_path"])] v = json.loads(subprocess.run(cmd, check=True, capture_output=True, text=True).stdout) if v["kind"] == "needs-render": sys.exit(f"held: the source entry has a visual prompt but no rendered portrait " f"(expected at {v.get('target') or 'an unnamed visuals/ file'}); render it into the hall first") req["visual_url"] = v["visual_url"] print(f"image: {v['kind']} {v['visual_url']}") else: print(f"image: post {req['visual_url']}") if channel == "public" and not req.get("visual_url"): sys.exit("public posts carry an image") json.dump(req, open(f"{tmp}/request.json", "w")) PY # Read the KV v2 data path directly: `bao kv get` first queries # sys/internal/ui/mounts, which a workload-scoped token may not be allowed to. token=$(bao read -format=json \ platform/data/workloads/infotech/fluid-telegram/hall-of-helix/bot-token \ | python3 -c 'import sys, json; print(json.load(sys.stdin)["data"]["data"]["token"])') \ || { rm -rf "$tmp"; echo "could not read the bot token from OpenBao (logged in? read on platform/data/workloads/infotech/fluid-telegram/hall-of-helix/*?)" >&2; exit 2; } TELEGRAM_BOT_TOKEN=$token TELEGRAM_CHANNEL_ID=$TELEGRAM_CHANNEL_ID ATTACH_VISUAL=true \ ADAPTER_LISTEN_ADDR=127.0.0.1:$port "$tmp/adapter" & pid=$! unset token trap 'kill $pid 2>/dev/null; rm -rf "$tmp"' EXIT for _ in $(seq 50); do curl -s -o /dev/null "http://127.0.0.1:$port/" && break sleep 0.1 done curl -sS -w '\nHTTP %{http_code}\n' -X POST -H 'Content-Type: application/json' \ --data-binary @"$tmp/request.json" "http://127.0.0.1:$port/v1/channel-posts"