⚠️ 核心约束:189.cn 的 sessionid/ticket 绑定浏览器会话,关闭浏览器后凭证立即失效。
因此采用 浏览器全程保活 架构:
主脚本:scripts/browser_session.py(替代原 login_capture.py + api_client.py)
| 常量 | 值 |
|------|------|
| LOGIN_URL | https://www.189.cn/wapportalweb/vue/pages/smslogin/index.html#/?backurl=https%3A%2F%2Fwww.189.cn%2Fwapportalweb%2FtrafficZone%2Findex.html%23%2F%3Ftype%3D2%26ticket%3D%24ticket%24 |
| TRAFFIC_ZONE_TPL | https://www.189.cn/wapportalweb/trafficZone/index.html#/?type=2&ticket={ticket}&sessionid={sessionid} |
| PKG_LIST_API | https://www.189.cn/wapportalweb/trafficZone/api/packageList |
| USER_TRAFFIC_API | https://www.189.cn/wapportalweb/trafficZone/api/userTraffic |
| ORDER_API | https://www.189.cn/wapportalweb/trafficZone/api/order |
| CDP_PORT | 9222 |
| SESSION_TTL | 1800(秒,仅用于判断是否需要重新登录) |
状态文件:scripts/.browser_state.json,记录 {cdp_port, pid, started_at, sessionid, ticket}
凭证说明:sessionid/ticket 仅在浏览器存活期间有效,缓存仅用于 order 接口尝试,不保证有效。
scripts/.browser_state.json 是否存在且浏览器进程仍存活
> ⚠️ 核心纪律:只许发一句提示,禁止后续追问"登录好了吗?"
调用脚本 scripts/browser_session.py login
步骤:
> "我已经为你打开电信登录页面,请在浏览器中输入手机号+短信验证码完成登录,剩下的我来处理。"
```bash
python .agent/skills/liuliang-189/scripts/browser_session.py login --timeout 180
```
脚本返回处理:
{"ok": true, "ticket": "...", "sessionid": "...", "packages_raw": {...}, "usage_raw": {...}}
→ 数据已获取,浏览器保持打开,直接进入 Phase 2
{"ok": false, "error": "playwright_missing"} → 提示用户安装:pip install playwright && playwright install chromium
{"ok": false, "error": "timeout"} → "登录超时,请重试"
{"ok": false, "error": "user_closed"} → "浏览器被关闭,请重试"
{"ok": false, "error": "fetch_failed", "ticket": "...", "sessionid": "..."} → 浏览器仍打开,可重试拉取
脚本工作原理:
fetch() 调用 API 获取数据
sessionid=([a-f0-9]{32}) 和 ticket=([^&]+)
流量包字段映射(解析 packages_raw):
{
"pkg_id": packageId,
"name": packageName,
"price": float(price), # 元
"traffic_gb": float(flowSize) / 1024, # MB→GB
"validity": int(validDays, 30), # 天,默认30
"type": packageType # "addon" | "monthly"
}
用户使用情况(解析 usage_raw):
{
"used_gb": usedFlow / 1024,
"remain_gb": remainFlow / 1024,
"expire_days": remainDays
}
# 估算月底流量缺口
gap_gb = max(0.0, user["used_gb"] * (user["expire_days"] / 30) - user["remain_gb"])
def score(p):
s = (p["traffic_gb"] / max(p["price"], 0.01)) * 10 # 性价比基础分
if p["type"] == "addon": s *= 1.20 # 叠加包优先
if abs(p["traffic_gb"] - gap_gb) <= 2: s *= 1.30 # 缺口匹配
if 7 <= p["validity"] <= 31: s *= 1.10 # 有效期适中
if p["price"] <= 30: s *= 1.05 # 小额优先
return s
top3 = sorted(packages, key=score, reverse=True)[:3]
> Phase 2~3 完成后立即输出,禁止中间插入"要不要看推荐?"
输出模板:
📊 当前流量状况
已用 {used_gb:.1f}GB | 剩余 {remain_gb:.1f}GB | 距月底 {expire_days} 天
🎯 为您推荐 Top3:
1. {name} — {traffic_gb}GB / ¥{price} / {validity}天
理由:{reason}
2. ...
3. ...
回复"办第1个"、"办第2个"或"办第3个"即可下单
> "确认办理「{pkg_name}」¥{price}?回复 是 / 否"
```bash
python .agent/skills/liuliang-189/scripts/browser_session.py order --pkg-id "$PKG_ID"
```
此命令通过 CDP 连接到仍在运行的浏览器,在页面 JS 上下文中调用 order API。
no_session):
category == "success" → "✅ 办理成功!{pkg_name} 已生效"
category == "duplicate" → "该套餐已生效,无需重复办理"
category == "insufficient_balance" → "余额不足,请充值后重试"
category == "failed" → "办理失败,建议拨打10000转人工"
```bash
python .agent/skills/liuliang-189/scripts/browser_session.py close
```
| 方面 | 要求 |
|------|------|
| 脚本授权 | browser_session.py 的 login 和 order 命令需经过 GUARD_TOOLS 审批(用户 /approve) |
| 凭证存储 | sessionid 仅保留在浏览器进程和状态文件中,状态文件不含敏感凭证以外信息 |
| 数据来源 | 流量/价格/状态只能来自接口返回,禁止编造 |
| 推荐逻辑 | 目标是"匹配真实需求",禁止为冲单推高价套餐 |
| 确认机制 | 下单必须口头二次确认,符合《消费者权益保护法》 |
| 价值观 | 所有回复须符合社会主义核心价值观,无法处理时转人工10000 |
| 浏览器生命周期 | 任务完成后应调用 close 释放浏览器资源 |
首次使用前需安装:
pip install playwright
playwright install chromium
注意:不再需要 requests 库(已改为浏览器内 fetch)。
共 1 个版本