4.9 KiB
4.9 KiB
agent 调用方式
必备工具
- HTTP 客户端:agent 自带
web_fetch/curl/ Node 22fetch任一 - Node 22:执行
scripts/pospal.mjs(Linux / macOS 自带node或 NVM) - 隐藏式输入:Linux/macOS
bash read -rs、或 Nodereadline关闭回显 - 单次请求硬性超时:
AbortSignal.timeout(12000)(12 秒)
凭证管理
首次获取(agent 主动询问)
printf "PSPLVISITORID: " >&2
IFS= read -rs PSPAL_VISITOR_ID
printf "\n" >&2
旧版 bash 不支持 -s 时:
stty -echo
printf "PSPLVISITORID: " >&2
read PSPAL_VISITOR_ID
stty echo
printf "\n" >&2
注入到 helper 调用
export POSPAL_VISITOR_ID="$PSPAL_VISITOR_ID"
node scripts/pospal.mjs build-booking-body < continuous-slots.json
helper 也支持 --visitor <value>,但优先用 env——避免值出现在进程列表 (ps aux)。
Session 结束
unset POSPAL_VISITOR_ID
新 session 必须重新索取,不允许持久化。
helper 子命令
| 子命令 | 作用 | 何时用 |
|---|---|---|
parse-availability |
把 list 端点原始 JSON 转结构化 slots(含状态判定、活动合并) | 每次拿到 list 响应后必跑 |
find-continuous |
从 slots 中找符合 --start HH --end HH 的完整连续组合 |
拿到结构化 slots 后 |
build-booking-body |
从连续组合生成锁场请求体 | 锁场前 |
parse-booking-response |
解析锁场响应,剥离 script.paySign |
锁场后 |
fingerprint |
访客凭证 → 12 字符 hex 指纹 | 日志关联 |
redact |
任意 JSON → 移除敏感字段 | 落盘或汇报前 |
所有子命令从 stdin 读 JSON(除 fingerprint),都支持 --help。
工作流示例
单次查询
# 1. 调 list 端点
RAW=$(curl -sS -X POST 'https://wxservice-stg48.pospal.cn/wxapi/AppointmentVenue/LoadValidClassRoomApptSettingV2' \
-H 'Accept: */*' \
-H 'APPTYPE: 1' \
-H 'Content-Type: application/json' \
-H 'PSPLVISITORAUTO: API' \
-H 'Referer: https://servicewechat.com/wx2088ca46b47d8be7/11/page-frame.html' \
-H 'STOREID: 6038652' \
-H 'VERSIONINFO: NC|2026.07.03' \
-d '{"dateTime":"2026-07-16","userId":6038652,"projectUid":"1775556129325750199"}')
# 2. 解析
echo "$RAW" | node scripts/pospal.mjs parse-availability --date 2026-07-16
# 3. 找连续组合
echo "$RAW" | node scripts/pospal.mjs parse-availability --date 2026-07-16 \
| node scripts/pospal.mjs find-continuous --start 19 --end 21
锁场
# 1. 重新查一次(不复用旧缓存)
RAW=$(curl -sS -X POST '...' -d '...')
# 2. 解析 + 找连续 + 构造请求体
BODY=$(echo "$RAW" | node scripts/pospal.mjs parse-availability --date 2026-07-16 \
| node scripts/pospal.mjs find-continuous --start 19 --end 21 \
| node scripts/pospal.mjs build-booking-body)
# 3. 调锁场
RESP=$(curl -sS -X POST 'https://wxservice-stg48.pospal.cn/wxapi/AppointmentVenue/SaveVenueAppointmentV2' \
-H "PSPLVISITORID: $POSPAL_VISITOR_ID" \
-H @write-headers.txt \
-d "$BODY")
# 4. 解析并脱敏
echo "$RESP" | node scripts/pospal.mjs parse-booking-response --slots continuous-slots.json
write-headers.txt 模板见 references/protocol.md 末尾。
监控循环(agent 自行实现)
for date in today, today+1, ..., today+6:
slots = curl list(date) → parse-availability
cont = find-continuous(slots, target_start, target_end)
key = (date, cont.txtClassroomUid, cont.startTime, cont.endTime, sum_price)
if cont and key not in seen:
notify(cont)
seen.add(key)
elif not cont and key in seen:
notify("removed: {key}")
seen.discard(key)
sleep 10
整点(如 19:00:00.000)agent 主动绕过冷却立即查一次。同一 date 的并发查询只允许一个在飞。
失败处理速查
| 现象 | agent 动作 |
|---|---|
列表返回 successed: false |
视为该日无可订数据,不重试 |
| 列表返回缺字段 | 视为接口变更,停止对该球场所有操作,告诉用户"接口字段变化,暂停服务" |
锁场 successed: false |
换时段最多重试 1 次,仍失败则停下 |
| 锁场 HTTP 5xx / 网络错误 | 不重试,告诉用户"结果不确定,请去小程序核实" |
锁场后 script.expireTime 已过 |
锁场已失效,告知用户 |
enrollSlots 含目标时段 |
该时段按已订处理,不计入连续组合 |
PSPLVISITORID 未索取 |
写操作前必须先向用户隐藏式索取;缺则停下 |
调试与脱敏
# 任何写操作前,确认 visitor 指纹
node scripts/pospal.mjs fingerprint "$POSPAL_VISITOR_ID"
# → "a3f9b21c0e4d" (12 字符)
# 落盘任何响应前必须 redact
curl ...list... | node scripts/pospal.mjs redact > list-redacted.json
# 看 helper 自检
node scripts/pospal.mjs parse-availability --date 2026-07-16 \
< evals/fixtures/availability-sample.json
完整 list-headers.txt / write-headers.txt 模板见 references/protocol.md 末尾的 curl 示例。