Files
openclaw-skills/workspace-security/monitor-unauthorized/scripts/index-threads.sh
b3nw 3b7d6bb67c Initial commit: custom OpenClaw skills from docker-test
- workspace: capmetro-monitor, github-notifications, model-selector
- workspace-security: vt-monitor, monitor-unauthorized
- workspace-home: cron-manager, monitor-unauthorized
- extensions: vt-sentinel (VT-Sentinel plugin)

Includes sync.sh for pull/push, README, AGENTS.md, .gitignore.
2026-02-16 15:32:44 +00:00

124 lines
3.7 KiB
Bash
Executable File

#!/bin/bash
# Thread index manager for monitor-unauthorized
#
# Maintains a local JSON index of Discord threads so the cron-wrapper
# can determine whether to create a new thread or update an existing one.
#
# The index is populated BY THE AGENT (not by this script) because only
# the agent has access to OpenClaw session/thread listing tools.
#
# This script provides helper operations:
# bash index-threads.sh lookup <ip> — find thread for an IP (exit 0 = found)
# bash index-threads.sh status — check if index exists and is fresh
# bash index-threads.sh record <ip> <session_key> — add/update an entry
# bash index-threads.sh needs-refresh — exit 0 if index is missing/stale
#
# Index location: STATE_DIR/thread-index.json
# Format:
# {
# "indexed_at": "2026-02-16T12:00:00Z",
# "channel_id": "1471181304782389381",
# "threads": {
# "1.2.3.4": {
# "session_key": "agent:security:discord:channel:1471181304782389381:thread:🚨 1.2.3.4 — unauthorized gateway access",
# "thread_name": "🚨 1.2.3.4 — unauthorized gateway access",
# "first_indexed": "2026-02-10T08:00:00Z",
# "last_updated": "2026-02-16T12:00:00Z",
# "update_count": 3
# }
# }
# }
set -e
STATE_DIR="/home/node/.openclaw/workspace-security/memory"
INDEX_FILE="$STATE_DIR/thread-index.json"
CHANNEL_ID="1471181304782389381"
MAX_AGE=86400 # 24 hours before considered stale
mkdir -p "$STATE_DIR"
# Initialize empty index if missing
init_index() {
if [ ! -f "$INDEX_FILE" ] || [ ! -s "$INDEX_FILE" ]; then
jq -n --arg ts "$(date -u +%Y-%m-%dT%H:%M:%SZ)" --arg ch "$CHANNEL_ID" \
'{indexed_at: $ts, channel_id: $ch, threads: {}}' > "$INDEX_FILE"
fi
}
# Check if index needs refresh (missing, empty, or stale)
needs_refresh() {
if [ ! -f "$INDEX_FILE" ] || [ ! -s "$INDEX_FILE" ]; then
echo "missing"
return 0
fi
local age=$(( $(date +%s) - $(stat -c%Y "$INDEX_FILE" 2>/dev/null || echo 0) ))
if [ "$age" -gt "$MAX_AGE" ]; then
echo "stale"
return 0
fi
echo "fresh"
return 1
}
# Look up a thread by IP
lookup() {
local ip="$1"
init_index
local result
result=$(jq -e --arg ip "$ip" '.threads[$ip] // empty' "$INDEX_FILE" 2>/dev/null)
if [ -n "$result" ]; then
echo "$result"
return 0
fi
return 1
}
# Record a thread entry for an IP
record() {
local ip="$1"
local session_key="$2"
local thread_name="${3:-🚨 $ip — unauthorized gateway access}"
local now
now=$(date -u +%Y-%m-%dT%H:%M:%SZ)
init_index
local tmp
tmp=$(mktemp)
jq --arg ip "$ip" \
--arg sk "$session_key" \
--arg tn "$thread_name" \
--arg now "$now" \
'
.threads[$ip] = (
(.threads[$ip] // {first_indexed: $now, update_count: 0}) |
.session_key = $sk |
.thread_name = $tn |
.last_updated = $now |
.update_count = (.update_count + 1)
)
' "$INDEX_FILE" > "$tmp" && mv "$tmp" "$INDEX_FILE"
echo '{"ok":true}'
}
# Print index status
status() {
init_index
local count
count=$(jq '.threads | length' "$INDEX_FILE")
local indexed_at
indexed_at=$(jq -r '.indexed_at' "$INDEX_FILE")
local age=$(( $(date +%s) - $(stat -c%Y "$INDEX_FILE" 2>/dev/null || echo 0) ))
echo "{\"entries\":$count,\"indexed_at\":\"$indexed_at\",\"age_seconds\":$age,\"stale\":$([ $age -gt $MAX_AGE ] && echo true || echo false)}"
}
# Dispatch
case "${1:-status}" in
lookup) lookup "$2" ;;
record) record "$2" "$3" "$4" ;;
status) status ;;
needs-refresh) needs_refresh ;;
*) echo "Usage: $0 {lookup|record|status|needs-refresh} [args...]" >&2; exit 1 ;;
esac