#!/usr/bin/env bash # Important-inbox watcher: polls sean@seanmattsson.com for new inbound mail from # real humans that may need Sean's reply. Wakes a worker only when candidates # appear; stays silent otherwise (and during quiet hours 10pm-6am ET). # Campaign replies, bounces, and booking notifications belong to the # prospect-reply-watch hook and are excluded here. set -euo pipefail source "$HATCH_HOOK_RUNTIME" STATE_DIR="$HOME/hooks/state/important-reply-draft" SEEN="$STATE_DIR/seen_ids" PENDING="$STATE_DIR/pending.tsv" mkdir -p "$STATE_DIR" touch "$SEEN" : > "$PENDING" DRY=0 if [ "${HATCH_HOOK_DRY_RUN:-0}" = "1" ]; then DRY=1 fi # Quiet hours: 10pm-6am ET. Stay silent WITHOUT marking seen, so the morning # poll catches overnight mail and reports it after quiet hours. HOUR=$(TZ=America/New_York date +%H) if [ "$HOUR" -ge 22 ] || [ "$HOUR" -lt 6 ]; then silent "quiet hours" "{\"hour\":\"$HOUR\"}" fi ACCT="d48c9e2643f643aeb3c4dacf347245b8" SENTLOG="$HOME/workspace/goals/sales-weapon-ae-copilot/hidden_files/prospect-queues/sent-log.json" RAW=$(hatch_gws_cli gmail --account "$ACCT" +triage \ --query 'newer_than:1d -from:sean@seanmattsson.com -from:smattsson@stratixsystems.com -category:promotions -category:social' \ --max 30 --format json 2>/dev/null) || { log "gmail fetch failed" '{}' silent "fetch failed" '{}' } mapfile -t CAMPAIGN < <(jq -r '.[].email' "$SENTLOG" 2>/dev/null | sort -u) FOUND=0 while IFS=$'\t' read -r ID FROM SUBJ DATE; do [ -z "${ID:-}" ] && continue # Booking notifications and delivery bounces belong to the reply-watch hook. case "$SUBJ" in *"Appointment booked"*) continue ;; esac SENDER=$(printf '%s' "$FROM" | grep -oE '[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}' | tail -n 1 || true) [ -z "${SENDER:-}" ] && continue SENDER_LC=$(printf '%s' "$SENDER" | tr '[:upper:]' '[:lower:]') # Obviously automated senders never need a drafted reply. case "$SENDER_LC" in *mailer-daemon*|*postmaster*|noreply*|no-reply*|donotreply*|do-not-reply*|\ newsletter*|marketing@*|notify@*|notification@*|alerts@*|calendar-notification*) continue ;; esac # Campaign contacts belong to the reply-watch hook. SKIP=0 for e in ${CAMPAIGN[@]+"${CAMPAIGN[@]}"}; do EL=$(printf '%s' "$e" | tr '[:upper:]' '[:lower:]') if [ "$SENDER_LC" = "$EL" ]; then SKIP=1; break; fi done [ "$SKIP" = "1" ] && continue if grep -qxF "$ID" "$SEEN" 2>/dev/null; then continue; fi printf '%s\t%s\t%s\t%s\n' "$ID" "$FROM" "$SUBJ" "$DATE" >> "$PENDING" if [ "$DRY" = "0" ]; then printf '%s\n' "$ID" >> "$SEEN" fi FOUND=$((FOUND + 1)) done < <(printf '%s' "$RAW" | jq -r '.messages[]? | [.id, .from, .subject, .date] | @tsv') if [ "$FOUND" -eq 0 ]; then silent "no new human inbound" '{}' fi PAYLOAD=$(jq -R -s ' split("\n") | map(select(length > 0) | split("\t") | {id: .[0], from: .[1], subject: .[2], date: .[3]}) | {candidates: .} ' "$PENDING") log "important inbox candidates" "$PAYLOAD" wake "inbox activity detected" "$PAYLOAD"