Initial commit: OpenClaw ops workspace
This commit is contained in:
103
skills/capmetro-monitor/scripts/watch-departure.sh
Executable file
103
skills/capmetro-monitor/scripts/watch-departure.sh
Executable file
@@ -0,0 +1,103 @@
|
||||
#!/bin/bash
|
||||
# Route 5 departure watcher — runs in background, posts to Discord when bus departs
|
||||
# Usage: bash watch-departure.sh <scheduled_time_UTC> [channel_id]
|
||||
# Example: bash watch-departure.sh "2026-02-12T13:30:00Z" 1467247377743347953
|
||||
set -eo pipefail
|
||||
|
||||
SCHED_DEPART="$1"
|
||||
CHANNEL="${2:-1467247377743347953}" # Default: DM channel
|
||||
VP_URL="https://data.texas.gov/download/cuc7-ywmd/application%2Fjson"
|
||||
FIRST_STOP="5854"
|
||||
USER_STOP="964"
|
||||
TOKEN=$(printenv DISCORD_BOT_TOKEN)
|
||||
MAX_POLLS=40 # ~13 minutes max watch time
|
||||
POLL_INTERVAL=20 # seconds between polls
|
||||
|
||||
# Find the trip matching this scheduled departure
|
||||
find_trip() {
|
||||
local TU=$(curl -sL --max-time 10 "https://data.texas.gov/download/mqtr-wwpy/application%2Fjson" 2>/dev/null)
|
||||
echo "$TU" | jq -r --arg sched "$SCHED_DEPART" '.entity[] |
|
||||
select(.tripUpdate.trip.routeId == "5" and .tripUpdate.trip.directionId == 0) |
|
||||
select([.tripUpdate.stopTimeUpdate[] | select(.stopId == "5854") |
|
||||
((.departure.time // .arrival.time) | tonumber)] | .[0] == ($sched | sub("Z$";"") | strptime("%Y-%m-%dT%H:%M:%S") | mktime)) |
|
||||
.tripUpdate.trip.tripId' 2>/dev/null | head -1
|
||||
}
|
||||
|
||||
# Convert ISO to epoch
|
||||
sched_epoch() {
|
||||
date -d "$SCHED_DEPART" +%s 2>/dev/null || date -u -d "${SCHED_DEPART%Z}" +%s 2>/dev/null
|
||||
}
|
||||
|
||||
SCHED_TS=$(sched_epoch)
|
||||
SCHED_CST=$(TZ=America/Chicago date -d "@$SCHED_TS" +"%I:%M %p" 2>/dev/null)
|
||||
|
||||
# Find the trip ID
|
||||
TRIP_ID=$(find_trip)
|
||||
if [ -z "$TRIP_ID" ]; then
|
||||
# Fallback: find closest eastbound trip
|
||||
TU=$(curl -sL --max-time 10 "https://data.texas.gov/download/mqtr-wwpy/application%2Fjson" 2>/dev/null)
|
||||
TRIP_ID=$(echo "$TU" | jq -r --arg ts "$SCHED_TS" '[.entity[] |
|
||||
select(.tripUpdate.trip.routeId == "5" and .tripUpdate.trip.directionId == 0) |
|
||||
{tripId: .tripUpdate.trip.tripId, depart: ([.tripUpdate.stopTimeUpdate[] | select(.stopId == "5854") | (.departure.time // .arrival.time)] | .[0] | tonumber)}] |
|
||||
sort_by((. .depart - ($ts | tonumber)) | fabs) | .[0].tripId' 2>/dev/null)
|
||||
fi
|
||||
|
||||
if [ -z "$TRIP_ID" ]; then
|
||||
# Post error
|
||||
curl -s -X POST -H "Authorization: Bot $TOKEN" -H "Content-Type: application/json" \
|
||||
-d "{\"content\":\"⚠️ Could not find Route 5 trip for $SCHED_CST departure.\"}" \
|
||||
"https://discord.com/api/v10/channels/$CHANNEL/messages" > /dev/null
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Poll until departure
|
||||
for i in $(seq 1 $MAX_POLLS); do
|
||||
VP=$(curl -sL --max-time 8 "https://data.texas.gov/download/cuc7-ywmd/application%2Fjson" 2>/dev/null)
|
||||
|
||||
VEHICLE=$(echo "$VP" | jq -c ".entity[] | select(.vehicle.trip.tripId == \"$TRIP_ID\")" 2>/dev/null)
|
||||
|
||||
if [ -z "$VEHICLE" ]; then
|
||||
sleep $POLL_INTERVAL
|
||||
continue
|
||||
fi
|
||||
|
||||
STOP_ID=$(echo "$VEHICLE" | jq -r '.vehicle.stopId')
|
||||
STATUS=$(echo "$VEHICLE" | jq -r '.vehicle.currentStatus')
|
||||
SPEED=$(echo "$VEHICLE" | jq -r '.vehicle.position.speed')
|
||||
VEH_ID=$(echo "$VEHICLE" | jq -r '.vehicle.vehicle.label')
|
||||
|
||||
# Bus has left the first stop
|
||||
if [ "$STOP_ID" != "$FIRST_STOP" ] || ([ "$STATUS" = "IN_TRANSIT_TO" ] && [ "$STOP_ID" != "$FIRST_STOP" ]); then
|
||||
DEPART_TS=$(date +%s)
|
||||
DEPART_CST=$(TZ=America/Chicago date +"%I:%M:%S %p")
|
||||
DELAY=$((DEPART_TS - SCHED_TS))
|
||||
DELAY_MIN=$((DELAY / 60))
|
||||
|
||||
# Calculate ETA at user stop (7m26s from first stop)
|
||||
ETA_TS=$((DEPART_TS + 446))
|
||||
ETA_CST=$(TZ=America/Chicago date -d "@$ETA_TS" +"%I:%M %p" 2>/dev/null)
|
||||
|
||||
if [ "$DELAY_MIN" -le 0 ]; then
|
||||
STATUS_MSG="🟢 On time"
|
||||
elif [ "$DELAY_MIN" -le 2 ]; then
|
||||
STATUS_MSG="🟡 ~${DELAY_MIN}min late"
|
||||
else
|
||||
STATUS_MSG="🔴 ${DELAY_MIN}min late"
|
||||
fi
|
||||
|
||||
MSG="🚌 **Route 5 Departed!**\nBus ${VEH_ID} left Anderson/Northcross at ${DEPART_CST}\nScheduled: ${SCHED_CST} | ${STATUS_MSG}\n📍 ETA at Woodrow/Choquette: **${ETA_CST}**"
|
||||
|
||||
CONTENT=$(printf "$MSG")
|
||||
curl -s -X POST -H "Authorization: Bot $TOKEN" -H "Content-Type: application/json" \
|
||||
-d "$(jq -n --arg c "$CONTENT" '{content: $c}')" \
|
||||
"https://discord.com/api/v10/channels/$CHANNEL/messages" > /dev/null
|
||||
exit 0
|
||||
fi
|
||||
|
||||
sleep $POLL_INTERVAL
|
||||
done
|
||||
|
||||
# Timeout — bus never departed (or we missed it)
|
||||
curl -s -X POST -H "Authorization: Bot $TOKEN" -H "Content-Type: application/json" \
|
||||
-d "{\"content\":\"⚠️ Route 5 watcher timed out — could not confirm $SCHED_CST departure from Anderson/Northcross.\"}" \
|
||||
"https://discord.com/api/v10/channels/$CHANNEL/messages" > /dev/null
|
||||
Reference in New Issue
Block a user