diff --git a/README.md b/README.md index 9a9f8ca..f669468 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ 虎牙活动积分兑换/抢购的 Go 复刻版。当前支持两种传输层: - `http`:走 `https://cdnws.api.huya.com/`,兼容原有命令。 -- `wss`:走浏览器同源 WebSocket 协议,更贴近手动抢购抓包里的链路。 +- `wss`:实验实现。浏览器手动抓包确认兑换链路会走 WSS,但当前 Go WSS 外层协议仍需按 TAF `WebSocketCommand` 重做。 ## 构建 @@ -57,19 +57,19 @@ CLI 示例: 直接兑换指定 PID: ```bash -./huya-go -cmd exchange -sid 2203 -pid 12889 -cookie-file cookie.txt -transport wss +./huya-go -cmd exchange -sid 2203 -pid 12889 -cookie-file cookie.txt -transport http ``` 按奖品名轮询并抢购: ```bash -./huya-go -cmd monitor -sid 2203 -name 小黄鸭 -cookie-file cookie.txt -transport wss -loops 20 -delay 120 -concurrency 5 +./huya-go -cmd monitor -sid 2203 -name 小黄鸭 -cookie-file cookie.txt -transport http -loops 20 -delay 120 -concurrency 5 ``` 定时开始: ```bash -./huya-go -cmd monitor -sid 2203 -pid 12889 -cookie-file cookie.txt -transport wss -at "2026-07-06 20:00:00" +./huya-go -cmd monitor -sid 2203 -pid 12889 -cookie-file cookie.txt -transport http -at "2026-07-06 20:00:00" ``` ## 参数说明 @@ -79,7 +79,7 @@ CLI 示例: - `-pid`:奖品 PID。`exchange` 必填,`monitor` 可用 `-pid` 或 `-name`。 - `-cookie`:单账号 Cookie。 - `-cookie-file`:多账号 Cookie 文件。 -- `-transport`:`http` 或 `wss`,默认 `http`;抢购建议使用 `wss`。 +- `-transport`:`http` 或 `wss`,默认 `http`;当前实际使用建议保持 `http`。 - `-wss-host`:WebSocket host。默认值来自 2026-07-06 手动抓包,失效时应从浏览器抓包或 `launch.queryHttpDns` 结果更新。 - `-timeout`:单次调用超时秒数。 - `-loops`、`-delay`、`-concurrency`:轮询次数、轮询间隔、并发账号数。 diff --git a/WSS_NOTES.md b/WSS_NOTES.md index 4c0f6c4..c16a8f6 100644 --- a/WSS_NOTES.md +++ b/WSS_NOTES.md @@ -1,92 +1,110 @@ # WSS 协议要点 -本文件记录 Go 版本当前实现依据,来源包括浏览器手动抢购 HAR、软件运行 HAR/日志、逆向导出的分析文本,以及 Python 参考项目 `/Users/yml/codes/douyu_login_py/core/huya`。 +本文件记录 2026-07-06 手动浏览器 HAR、页面 JS 和 Go 版本当前实现的复核结论。 + +## 抓包结论 + +手动浏览器链路是 HTTP + WSS 混合: + +- 页面初始化、商品信息、HttpDNS 等有多条 `https://cdnws.api.huya.com/` POST。 +- 浏览器同时建立了 3 条 `wss://77bc035c-ws.va.huya.com/` 连接。 +- `scoreExchangePrize` 出现在 WSS 连接数据和静态 JS 中,没有出现在 `cdnws` POST 请求中。 +- 当前 HTTP 版本可用,是因为 `webActUI` 的 WUP over HTTP 编码已经对齐。 +- 当前 WSS 版本会超时,是因为 Go 里的 WSS 外层协议模型不符合浏览器 JS。 ## 连接 - WebSocket URL:`wss:///?baseinfo=` -- 默认 host:`77bc035c-ws.va.huya.com` +- 手动 HAR host:`77bc035c-ws.va.huya.com` - host 来自 `launch.queryHttpDns` 或浏览器抓包,不应长期写死。 - 握手启用 `permessage-deflate`。 -- Go 版本使用 `github.com/gorilla/websocket` 处理握手、mask 与压缩。 +- 浏览器请求扩展:`permessage-deflate; client_max_window_bits` +- 服务端返回扩展:`permessage-deflate;server_no_context_takeover;client_no_context_takeover` -`baseinfo` 是 `WSConnectParaInfo` 的 TAF 编码后 base64,再 URL escape。WSS 模式会把 Cookie 中的 `huya_ua` 归一为: +## 正确外层结构 + +浏览器 JS 不是 `1B version + 1B command + 4B sequence + body`。 + +页面实际构造 TAF/JCE `WebSocketCommand`: ```text -webh5&0.0.1&websocket&&diypc_52775 +WebSocketCommand { + 0: iCmdType int32 + 1: vData bytes + 2: lRequestId int64 + 3: traceId string + 4: iEncryptType int32 + 5: lTime int64 + 6: sMD5 string +} ``` -## 外层消息 - -Python 参考中对应 `WssMessage/WssCommand`,Go 版本实现为 `internal/huya/wss_message.go`。 +WUP 请求发送逻辑: ```text -1B version(0) + 1B command + 4B sequence(big-endian) + body +Wup.setServant(servant) +Wup.setFunc(func) +Wup.setRequestId(requestId) +Wup.writeStruct("tReq", req) + +WebSocketCommand.iCmdType = EWSCmd_WupReq(3) +WebSocketCommand.vData = Wup.encode() +WebSocketCommand.traceId = "::0:" +ws.send(WebSocketCommand.writeTo(JceOutputStream)) ``` -已知命令: +服务端返回 `EWSCmd_WupRsp(4)`,内部 `vData` 才是 WUP 响应。 -- `0x03`:RPC request -- `0x04`:RPC response -- `0x10`:heartbeat send -- `0x11`:heartbeat recv -- `0x17`:confirm -- `0x21`:register +## 命令枚举 -已知 sequence: +页面 JS 中可见的关键枚举: -- 心跳:`0x1d00000d` -- `launch.wsLaunch`:`0x1d000078` -- `mobileui.getConfig`:`0x1d000076` -- register:`0x1d00003c` -- confirm:`0x1d000002` -- 业务 RPC:`0x1d000106` +- `3`:`EWSCmd_WupReq` +- `4`:`EWSCmd_WupRsp` +- `5`:`EWSCmdC2S_HeartBeat` +- `6`:`EWSCmdS2C_HeartBeatAck` +- `10`:`EWSCmdC2S_VerifyCookieReq` +- `11`:`EWSCmdS2C_VerifyCookieRsp` +- `20`:`EWSCmdC2S_HeartBeatReq` +- `21`:`EWSCmdS2C_HeartBeatRsp` +- `23`:`EWSCmdC2S_UpdateUserExpsReq` +- `24`:`EWSCmdS2C_UpdateUserExpsRsp` +- `33`:`EWSCmdC2S_UpdateUserInfoReq` +- `34`:`EWSCmdS2C_UpdateUserInfoRsp` -## RPC body +手动 HAR 解出的服务端 WSS 回包包括: -WUP 请求由 `internal/wup` 编码,输出自带 4 字节大端总长度。 - -小包格式: - -```text -[4B WUP 总长度][WUP body] + 2c 36 00 4c 5c 66 00 -``` - -大包格式: - -```text -[1B 长度低字节][4B WUP 总长度][裸 WUP body] + trace 尾部 -``` - -其中 trace 尾部形如: - -```text -,6%::0:3 + 4c 5c 66 00 -``` - -响应解析时,Go 版本会从 WSS body 中切出内部 WUP 包,再交给 `wup.Response.Decode`。 +- `EWSCmdS2C_HeartBeatRsp` +- `EWSCmd_WupRsp launch.wsLaunch` +- `EWSCmd_WupRsp mobileui.getConfig` +- `EWSCmd_WupRsp webActUI.getActPrizeList` +- `EWSCmd_WupRsp webActUI.getUserScore` +- `EWSCmd_WupRsp webActUI.scoreExchangePrize` ## 初始化流程 -WSS session 建立后按以下顺序初始化: +浏览器 WSS session 的关键流程: -1. 发送心跳。 -2. 调用 `launch.wsLaunch`,读取 `guid` 和 `clientIp`。 -3. 调用 `mobileui.getConfig`,失败只记录日志,继续后续流程。 -4. 使用 `wsLaunch` 返回的 `guid` 发送 register。 -5. 发送 confirm。 +1. 连接 `wss:///?baseinfo=`。 +2. 发送 `EWSCmdC2S_HeartBeatReq`。 +3. 通过 WSS 调用 `launch.wsLaunch`。 +4. `wsLaunch` 后触发 `sendWSUpdateUserInfoReq`。 +5. `launch.queryHttpDns` 仍通过 HTTP `cdnws` 调用。 +6. 后续 WUP 业务在连接可用时走 `signalSdk.sendWup`,连接不可用时可 fallback 到 `sendWupHttp`。 -之后业务接口复用同一个连接,并用 FIFO 队列匹配 `0x04` RPC response。 +响应应按 WUP `iRequestId` 匹配回调,不能只用 FIFO。 ## 活动接口 -当前 Go 版本的 HTTP 与 WSS 都实现 `huya.ActivityClient`: +当前 Go HTTP 版本已实现: - `webActUI.getActPrizeList` - `webActUI.getUserScore` - `webActUI.getUserPrizeRecords` - `webActUI.scoreExchangePrize` +WSS 版本仍是实验状态,需要按 `WebSocketCommand` 结构重写。 + 抢购兑换请求结构: ```text @@ -112,6 +130,8 @@ tReq.isRole = 1 ## 待完善 +- 实现 TAF `WebSocketCommand` 编解码。 +- WSS WUP response 按 `iRequestId` 匹配请求。 +- 实现 `WSUpdateUserInfoReq`、心跳、verify cookie 等浏览器初始化步骤。 - 自动调用 `launch.queryHttpDns` 获取最新 WSS host。 -- 对 WSS RPC response 做 request id 或业务包级匹配,当前与 Python 参考一致使用 FIFO。 -- 增加基于真实抓包样本的 WSS body 编解码单测。 +- 增加基于真实抓包样本的 WSS 编解码单测。 diff --git a/build/bin/huya-go.app/Contents/MacOS/huya-gui b/build/bin/huya-go.app/Contents/MacOS/huya-gui index b8568c8..d4ee478 100755 Binary files a/build/bin/huya-go.app/Contents/MacOS/huya-gui and b/build/bin/huya-go.app/Contents/MacOS/huya-gui differ diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index dc46abc..7b8571f 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -181,7 +181,7 @@ function statusTag(status?: number, error?: string): ReactNode { function pickConfig(values: Partial): UiConfig { return { - transport: values.transport || 'wss', + transport: values.transport || 'http', wssHost: values.wssHost || '', sid: Number(values.sid || 0), pid: Number(values.pid || 0), @@ -418,8 +418,8 @@ export default function Dashboard() {