Files
openclaw-ops/scripts/email-review-run.sh

166 lines
5.1 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
ROOT="/home/node/.openclaw/workspace"
STATE_DIR="$ROOT/mail/state"
RUN_DIR="$ROOT/mail/runs"
STATE_FILE="$STATE_DIR/tasks.json"
ACTION_FILE="$STATE_DIR/action-items.json"
MAX_ITEMS="${EMAIL_REVIEW_MAX_ITEMS:-10}"
WEBHOOK_URL="${N8N_EMAIL_WEBHOOK_URL:-}"
NOTIFY="${EMAIL_REVIEW_NOTIFY:-1}"
mkdir -p "$STATE_DIR" "$RUN_DIR"
if [[ -z "$WEBHOOK_URL" ]]; then
echo "ERROR: N8N_EMAIL_WEBHOOK_URL is required" >&2
exit 2
fi
if [[ ! -f "$STATE_FILE" ]]; then
cat > "$STATE_FILE" <<'JSON'
{
"schemaVersion": 1,
"updatedAt": null,
"items": []
}
JSON
fi
if [[ ! -f "$ACTION_FILE" ]]; then
cat > "$ACTION_FILE" <<'JSON'
{
"schemaVersion": 1,
"updatedAt": null,
"items": []
}
JSON
fi
RUN_TS="$(date -u +%Y%m%dT%H%M%SZ)"
RUN_FILE="$RUN_DIR/run-$RUN_TS.json"
TMP_RESP="$(mktemp)"
TMP_ITEMS="$(mktemp)"
trap 'rm -f "$TMP_RESP" "$TMP_ITEMS"' EXIT
curl -sS -X POST "$WEBHOOK_URL" \
-H 'content-type: application/json' \
-d '{}' > "$TMP_RESP"
jq '{fetchedAt: now|todate, response: .}' "$TMP_RESP" > "$RUN_FILE"
if ! jq -e '.emails? // [] | type == "array"' "$TMP_RESP" >/dev/null 2>&1; then
echo "ERROR: n8n response missing emails[] array" >&2
exit 3
fi
jq -c '.emails // [] | .[]' "$TMP_RESP" | head -n "$MAX_ITEMS" > "$TMP_ITEMS" || true
NEW_COUNT=0
UPDATED_COUNT=0
TOTAL=0
while IFS= read -r email_json; do
[[ -z "$email_json" ]] && continue
TOTAL=$((TOTAL+1))
MESSAGE_ID="$(jq -r '.messageId // empty' <<<"$email_json")"
SUBJECT="$(jq -r '.subject // ""' <<<"$email_json")"
FROM_ADDR="$(jq -r '.from.value[0].address // .from.text // ""' <<<"$email_json")"
RECEIVED_AT="$(jq -r '.date // empty' <<<"$email_json")"
BODY="$(jq -r '(.textPlain // .snippet // .textHtml // "") | tostring' <<<"$email_json" | sed 's/\s\+/ /g' | cut -c1-8000)"
[[ -z "$MESSAGE_ID" ]] && continue
TRIAGE_INPUT="$(jq -cn \
--arg messageId "$MESSAGE_ID" \
--arg subject "$SUBJECT" \
--arg from "$FROM_ADDR" \
--arg receivedAt "$RECEIVED_AT" \
--arg body "$BODY" \
'{type:"email_review", messageId:$messageId, subject:$subject, from:$from, receivedAt:$receivedAt, body:$body}')"
TRIAGE_RAW="$(openclaw agent --agent mail-triage --message "$TRIAGE_INPUT" --json 2>/dev/null || true)"
TRIAGE_TEXT="$(jq -r '.result.payloads[0].text // empty' <<<"$TRIAGE_RAW" 2>/dev/null || true)"
if [[ -z "$TRIAGE_TEXT" ]]; then
continue
fi
TRIAGE_JSON="$(jq -c . <<<"$TRIAGE_TEXT" 2>/dev/null || true)"
if [[ -z "$TRIAGE_JSON" ]]; then
continue
fi
ITEM_KEY="$MESSAGE_ID"
EXISTING="$(jq -c --arg k "$ITEM_KEY" '.items[]? | select(.messageId==$k)' "$STATE_FILE")"
RECORD="$(jq -cn \
--arg messageId "$MESSAGE_ID" \
--arg updatedAt "$(date -u +%FT%TZ)" \
--argjson source "$email_json" \
--argjson triage "$TRIAGE_JSON" \
'{messageId:$messageId, updatedAt:$updatedAt, source:$source, triage:$triage}')"
if [[ -z "$EXISTING" ]]; then
jq --argjson rec "$RECORD" --arg ts "$(date -u +%FT%TZ)" '.items += [$rec] | .updatedAt=$ts' "$STATE_FILE" > "$STATE_FILE.tmp" && mv "$STATE_FILE.tmp" "$STATE_FILE"
NEW_COUNT=$((NEW_COUNT+1))
SUMMARY="$(jq -r '.summary // "(no summary)"' <<<"$TRIAGE_JSON")"
echo "NEW ACTIONABLE EMAIL: $SUBJECT"
echo "- from: $FROM_ADDR"
echo "- summary: $SUMMARY"
else
OLD_HASH="$(jq -Sc '.triage' <<<"$EXISTING" | sha256sum | awk '{print $1}')"
NEW_HASH="$(jq -Sc . <<<"$TRIAGE_JSON" | sha256sum | awk '{print $1}')"
if [[ "$OLD_HASH" != "$NEW_HASH" ]]; then
jq --arg k "$ITEM_KEY" --argjson rec "$RECORD" --arg ts "$(date -u +%FT%TZ)" '
.items = (.items | map(if .messageId==$k then $rec else . end))
| .updatedAt=$ts
' "$STATE_FILE" > "$STATE_FILE.tmp" && mv "$STATE_FILE.tmp" "$STATE_FILE"
UPDATED_COUNT=$((UPDATED_COUNT+1))
echo "UPDATED EMAIL TRIAGE: $SUBJECT"
fi
fi
done < "$TMP_ITEMS"
# Build normalized action list for downstream reminder/overview use.
jq --arg ts "$(date -u +%FT%TZ)" '
. as $root
| {
schemaVersion: 1,
updatedAt: $ts,
items: [
$root.items[]?
| {
messageId,
emailSubject: (.source.subject // ""),
from: (.source.from.value[0].address // .source.from.text // ""),
receivedAt: (.source.date // null),
priority: (.triage.priority // "unknown"),
urgency: (.triage.urgency // "unknown"),
no_action_needed: (.triage.no_action_needed // false),
summary: (.triage.summary // ""),
actions: (
[(.triage.actions // [])[]? | {
action: (.action // ""),
owner: (.owner // "Ben"),
deadline: (.deadline // null),
estimated: (.estimated // false),
evidence: (.evidence // "")
}]
)
}
]
}
' "$STATE_FILE" > "$ACTION_FILE"
SUMMARY_LINE="email-review-run complete: total=$TOTAL new=$NEW_COUNT updated=$UPDATED_COUNT"
echo "$SUMMARY_LINE"
if [[ "$NOTIFY" == "1" && $((NEW_COUNT + UPDATED_COUNT)) -gt 0 ]]; then
openclaw system event --text "Email review: $NEW_COUNT new, $UPDATED_COUNT updated actionable item(s)." --mode now >/dev/null 2>&1 || true
fi