Initial commit: OpenClaw ops workspace
This commit is contained in:
165
scripts/email-review-run.sh
Executable file
165
scripts/email-review-run.sh
Executable file
@@ -0,0 +1,165 @@
|
||||
#!/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
|
||||
Reference in New Issue
Block a user