diff --git a/docs/superpowers/specs/phase0-findings.md b/docs/superpowers/specs/phase0-findings.md new file mode 100644 index 0000000..4acbee7 --- /dev/null +++ b/docs/superpowers/specs/phase0-findings.md @@ -0,0 +1,83 @@ +# Phase 0 侦察结论(实现期实测补充) + +- **日期**:2026-08-01 +- **方式**:浏览器实地登录云客 CRM,在页内 `fetch` 真实接口 + 抓包 +- **配套**:[数据字典](2026-08-01-yunke-data-dictionary.md)、[设计文档](2026-08-01-yunke-data-mirror-design.md) +- **状态**:关键项全部确认 ✅;少量次要项标注 ⏳ + +--- + +## 1. 登录与鉴权 ✅ + +- **`GET /newuc/login/getSecureKey`**(公开接口,无需登录)返回 RSA 公钥: + ``` + {"code":10000,"data":{"public_exponent":"10001","modulus":"9a370c33...(hex)"}} + ``` + → **密码用 RSA 加密**(PKCS1 v1.5,exponent=0x10001=65537,modulus 为返回的 hex)。 +- **`POST /usercenter/login/pcLogin`**:请求体 `{account, password}`(`password`=RSA 加密后的 hex/base64)—— 字段名为推断,实现时若失败尝试 `{username, password}` / `{account, pwd}` 等变体。登录成功返回会话 cookie + `userid`/`company`。 +- **数据接口鉴权 = 会话 cookie**:实测在登录后的页内用 `fetch('/wechatweb/...', {credentials:'include'})`(**不带** `userid`/`company` 等自定义头)即可 200 返回数据。自定义请求头(`userid`/`company`/`source:yunkepc`/`channel:4`)是**信息性的,非必需**。 + - **影响**:`YunkeClient` 只需持有会话 cookie 即可调所有数据接口;自定义头可继续带(无害)。 +- **会话可从 localStorage 重建**:`userid`、`company`、`h5Token`、`userList` 都在 localStorage(冒烟脚本 existing-session 模式可据此 + cookie 运行)。 + +## 2. 微信消息类型码 ✅(实测枚举) + +| type | 含义 | 处理 | +|---|---|---| +| `1` | 文字 | 存 `text` | +| `-2` | 日期分隔符 | **丢弃**(非真实消息) | +| `2` | 图片/表情(webp) | `file`/`fileTh` 为**直接 URL** + `md5` → 下载去重 | +| `3` | 图片(旧格式) | `file` 为**不透明 ID**(如 `07215407292664565911ce5105`)→ 需另解析/下载接口 ⏳ | +| `4` | 视频(mp4) | `file` 直接 URL + `md5` → 下载 | +| `10` | 系统/链接/名片/邀请 | `file` 为 `support.weixin.qq.com/...` URL → **非媒体,跳过下载** | +| `6` `9` `13` `15` `21` `22` | 其他(语音/文件/转账/红包/引用等疑似的码) | ⏳ 实现期按需枚举确认(`raw_json` 已留底) | + +> `normalize_wechat_type` 已映射 `1→text`、`-2→divider`;需补 `2→image`、`3→image_legacy`、`4→video`、`10→system` 等。 + +## 3. 媒体下载机制 ✅ + +**聊天媒体**(图片/视频/文件): +- 主机:`wx.oss.yunkecn.com` +- 路径:`/wechatMsgFile/{companyCode}/{userId}/ossCallback/{aa}/{bb}/{cc}/{md5}.{ext}` + - 例:`https://wx.oss.yunkecn.com/wechatMsgFile/8m0atd/{userId}/ossCallback/ea/8f/74/ea8f74e7797e63505731e8c51222c554.webp` +- 消息里给 `file`(原图)/`fileTh`(缩略图)/`fileHd`(高清) + `md5`(去重键) +- **直接 GET 即可下载**(公开 OSS,无需鉴权);按 `md5` 去重存储 +- 例外:type=`3` 图片的 `file` 是不透明 ID(非 URL)→ 需另找下载接口 ⏳ + +**通话录音 / 语音通话录音**: +- 主机:`yunke-pcfile.oss-cn-beijing.aliyuncs.com` +- 电话录音:`/callFile/{company}/{userId}/YYYY/MMDD/{callNumber}_{ts}.mp3`(`PhoneCall.recordFile`) +- 微信语音通话录音:`/wechat-call/{company}/{imei}/ossCallback/{ts}_{ts}.mp3`(`voiceCall.ossFileName`) + +**头像**:`wx.qlogo.cn`、`mmhead.c2c.wechat.com`、`pc.oss.yunkecn.com`(外链,抓到即下载) + +## 4. 语音通话 ✅ + +- `POST /wechatweb/callRecord/queryWeChatVoiceList`,total=**333** +- 字段:`id`、`userId`/`userName`/`userPhone`/`userWeChatNickName`/`userWeChatHeadImg`(员工)、`talker`/`talkerNickName`/`talkerAlias`/`talkerHeadImg`(客户)、`callType`、`isSend`(0呼入/1呼出)、`callStatus`(1接通有录音/2无应答)、**`ossFileName`(录音URL)**、`ossFileExist`、`duration`(秒,字符串)/`durationFile`(秒,int)、`startTime`/`endTime`、`weChatId`、`remark`(如"通话时长02:31"/"对方无应答")、`isRoom`(群通话?)、`callFileId` +- 归一到 `PhoneCall` 表,`channel='voice_call'`,`record_file`←`ossFileName` + +## 5. WhatsApp 联系人 ✅ + +- `POST /qqweb/WhatsAppUser/businessFriendList`,total=**11292** +- 字段:`uid`(员工 `phone@s.whatsapp.net`)、`userId`/`userName`/`salePhone`/`salesNickName`(员工)、`friendUid`(客户→`friend_id`)、`nickName`、`phone`、`createTime`/`lastUpdate`、`groupNum`/`contactsNum`/`talkerCount`/`messageCount`/`sensitiveWordNum` +- 归一到 `Customer` 表,`channel='whatsapp'` + +## 6. WhatsApp 媒体 ⏳ + +- 消息里给 `mediaName`(文件名,如 `{uid}_{keyId}.jpg`),**非 URL** → 需专门下载接口 +- ⏳ 实现期:读 `whatsapp-service-*.js` 或触发一次真实图片加载,找到 `mediaName → 文件` 的下载接口(很可能也是 `wx.oss.yunkecn.com` 或同系 OSS) + +## 7. 群成员昵称 ⏳ + +- `chatroom/getChatroomList` 的 `memberIds` 给的是**分号分隔的 wxid 列表**(不含昵称/头像) +- ⏳ 群内成员的昵称/头像可能需另调接口(实现期确认);本期可先存 wxid,昵称后补 + +--- + +## 对实现的影响(解锁项) + +- **登录**:`app/session/login.py::encrypt_password` 改为真实 RSA(用 `cryptography` 从 modulus/exponent 构造公钥加密)→ 解锁自动登录 + 真实端到端冒烟 +- **媒体下载(M2.6)**:微信 type 2/4 直接 URL 下载、按 md5 去重;type 3 标记待解析;type 10 跳过 +- **语音通话(M2.4)**:`queryWeChatVoiceList` 字段已确认,录音直链下载 +- **WhatsApp 联系人(M3.3)**:`businessFriendList` 字段已确认 +- **鉴权简化**:`YunkeClient` cookie-only 即可(自定义头可选)