Matrix 养号系统 — 全面架构文档 v2.2
最后更新:2026-05-10
作者:Claw
核心原则:原子操作 × 状态机 × 唯一特征码 × 蓝图链
0. 总架构哲学
每个操作都是一条因果链:
关键规则:
- 每个状态必须有唯一的特征码组合(URL + DOM锚点 + video数量 + 其他)
- 每个操作执行前必须先验证前置状态
- 每个操作执行后必须验证后置状态
- 验证失败 → 不执行操作,先恢复到一个已知状态
- 所有状态可查询、可追踪、可恢复
1. 完整状态机
1.1 状态定义与特征码
| 状态ID |
特征码 |
描述 |
INIT |
浏览器刚启动,无页面 |
初始态 |
LOGIN |
URL=douyin.com + sessionid Cookie 存在 + [data-e2e="user-avatar"] 可见 |
已登录首页 |
HOME_GRID |
URL=douyin.com/jingxuan + [data-e2e="alink-item"] >= 3 + video=1(背景) |
精选页卡片列表 |
HOME_RECOMMEND |
URL=douyin.com/?recommend=1 + [data-e2e="alink-item"] >= 3 |
推荐页卡片列表 |
VIDEO_PLAYER |
URL 含 /video/ 或 modal_id + video>=2 + [data-e2e="video-player-digg"] 存在 |
视频播放页 |
COMMENT_PANEL |
VIDEO_PLAYER + [data-e2e="comment-list"] 可见 + .public-DraftEditor-content 存在 |
评论区已打开 |
INPUT_FOCUSED |
COMMENT_PANEL + document.activeElement 是 .public-DraftEditor-content + 光标闪烁 |
输入框已聚焦 |
TEXT_ENTERED |
INPUT_FOCUSED + .public-DraftEditor-content 的 textContent.length > 0 |
文字已输入 |
COMMENT_SENT |
评论已发送 + [data-e2e="comment-list"] 内含刚发的文本 |
评论已发送 |
SEARCH |
URL 含 /search/ + [data-e2e="searchbar-input"] |
搜索结果页 |
VERIFY_CODE |
.second-verify-panel 或 input[placeholder*="验证码"] 可见 |
验证码弹窗 |
UNKNOWN |
无法匹配任何已知特征码 |
未知状态 |
1.2 状态迁移图
1.3 状态检测函数
核心检测函数 detect_state(page) 返回当前状态ID:
2. 原子操作完整清单
每个原子操作 = (名称, 前置状态, 操作, 后置状态, 超时, 重试策略)
2.1 登录段
| 原子操作 |
前置 |
操作 |
后置 |
特征码变化 |
navigate_login |
INIT |
goto douyin.com → 等待页面加载 |
LOGIN |
URL→douyin + sessionid存在 |
2.2 首页段
| 原子操作 |
前置 |
操作 |
后置 |
特征码变化 |
goto_home |
LOGIN/任意 |
导航到 douyin.com/?recommend=1 |
HOME_GRID |
URL→jingxuan + alink-item≥3 |
click_video_card |
HOME_GRID |
双击 .discover-video-card-item 或 [data-e2e="alink-item"] |
VIDEO_PLAYER |
video≥2 + /video/ 或 player UI |
search_keyword |
HOME_GRID |
输入关键词+Enter |
SEARCH |
URL→/search/ |
click_search_result |
SEARCH |
点击第一个结果卡片 |
VIDEO_PLAYER |
video≥2 |
scroll_feed |
HOME_GRID |
鼠标滚轮/键盘下滑 |
HOME_GRID |
URL不变,卡片刷新 |
2.3 视频播放段
| 原子操作 |
前置 |
操作 |
后置 |
特征码变化 |
wait_watch |
VIDEO_PLAYER |
等待N秒 |
VIDEO_PLAYER |
video 内容变化(可选) |
like_video |
VIDEO_PLAYER |
按Z/点digg按钮 |
VIDEO_PLAYER |
digg_state→digged |
collect_video |
VIDEO_PLAYER |
点收藏按钮 |
VIDEO_PLAYER |
收藏状态变化 |
follow_author |
VIDEO_PLAYER |
按G/点关注 |
VIDEO_PLAYER |
关注按钮变化 |
next_video |
VIDEO_PLAYER |
键盘ArrowDown / 滚轮 |
VIDEO_PLAYER |
video.src变化 |
open_comments |
VIDEO_PLAYER |
按X / 点评论图标 |
COMMENT_PANEL |
comment-list可见 + .public-DraftEditor-content存在 |
2.4 评论段
| 原子操作 |
前置 |
操作 |
后置 |
特征码变化 |
focus_editor |
COMMENT_PANEL |
Playwright locator双击 .public-DraftEditor-content |
INPUT_FOCUSED |
activeElement=编辑器 |
type_comment |
INPUT_FOCUSED |
page.keyboard.type() 逐字输入 |
TEXT_ENTERED |
editor textContent>0 |
send_comment |
TEXT_ENTERED |
点击发送按钮 / Ctrl+Enter / Alt+Enter |
COMMENT_SENT / VERIFY_CODE |
comment-list含文本 |
close_comments |
COMMENT_PANEL |
按X / Escape |
VIDEO_PLAYER |
comment-list消失 |
2.5 恢复段
| 原子操作 |
前置 |
操作 |
后置 |
特征码变化 |
recover_home |
任意 |
导航到 douyin.com → 等待卡片加载 |
HOME_GRID |
URL+卡片 |
recover_video |
HOME_GRID/任意 |
点卡片进入视频 |
VIDEO_PLAYER |
video≥2 |
recover_comments |
VIDEO_PLAYER |
开评论(键盘+DOM双策略) |
COMMENT_PANEL |
comment-list可见 |
3. 完整蓝图链:评论流程
4. 数据表结构
atomic_ops — 原子操作注册表
| 字段 |
类型 |
说明 |
| id |
INTEGER PK |
|
| op_name |
TEXT UNIQUE |
操作名 |
| description |
TEXT |
描述 |
| action_type |
TEXT |
click/nav/key/eval/wait |
| target |
TEXT |
操作目标(选择器/键名/URL) |
| pre_state |
TEXT |
前置状态ID |
| post_state |
TEXT |
后置状态ID |
| timeout |
INTEGER |
超时秒数 |
| retry |
INTEGER |
重试次数 |
| verify_rule |
TEXT |
后置验证规则(JSON) |
| created_at |
DATETIME |
|
anchor_rules — 状态特征码规则
| 字段 |
类型 |
说明 |
| id |
INTEGER PK |
|
| state_name |
TEXT |
状态ID |
| field |
TEXT |
检测字段(url/videoCount/selector等) |
| operator |
TEXT |
eq/neq/gt/gte/lt/lte/includes |
| value |
TEXT |
期望值 |
| logic_group |
INTEGER |
同一组AND,不同组OR |
| active |
BOOLEAN |
|
| created_at |
DATETIME |
|
state_transitions — 合法状态迁移
| 字段 |
类型 |
说明 |
| id |
INTEGER PK |
|
| from_state |
TEXT |
起始状态 |
| to_state |
TEXT |
目标状态 |
| via_op |
TEXT |
通过哪个操作 |
| weight |
INTEGER |
权重(优先级) |
action_logs — 操作执行日志
| 字段 |
类型 |
说明 |
| id |
INTEGER PK |
|
| execution_id |
TEXT |
执行批次 |
| identity |
TEXT |
账号ID |
| op_name |
TEXT |
操作名 |
| pre_state |
TEXT |
执行前状态 |
| post_state |
TEXT |
执行后状态 |
| success |
BOOLEAN |
|
| duration_ms |
INTEGER |
|
| error |
TEXT |
|
| created_at |
DATETIME |
|
blueprints — 蓝图定义
| 字段 |
类型 |
说明 |
| id |
TEXT PK |
蓝图ID |
| name |
TEXT |
名称 |
| version |
TEXT |
版本 |
| steps |
JSON |
步骤序列 |
| created_at |
DATETIME |
|
5. 关键选择器清单
5.1 页面检测
| 检测项 |
选择器/代码 |
说明 |
| 视频播放页 |
document.querySelectorAll('video').length >= 2 + [data-e2e="video-player-digg"] |
video≥2 + player按钮 |
| 评论区打开 |
[data-e2e="comment-list"] + .public-DraftEditor-content |
列表+编辑器都存在 |
| 输入框聚焦 |
document.activeElement === document.querySelector('.public-DraftEditor-content') |
activeElement是编辑器 |
| 输入框有内容 |
.public-DraftEditor-content.textContent.length > 0 |
编辑器内有文字 |
| 验证码弹窗 |
.second-verify-panel / input[placeholder*="验证码"] |
|
| 首页卡片 |
[data-e2e="alink-item"] / .discover-video-card-item |
|
5.2 操作目标
| 操作 |
方式 |
目标 |
| 进入视频 |
Playwright locator双击 |
.discover-video-card-item 或 [data-e2e="alink-item"] |
| 打开评论 |
键盘x / DOM 点 |
[data-e2e="video-comment-count"] 或 [data-e2e="feed-comment-icon"] |
| 聚焦编辑器 |
Playwright locator双击 |
.public-DraftEditor-content |
| 输入文字 |
page.keyboard.type(text) |
已聚焦的activeElement |
| 发送 |
DOM找按钮点击 |
按钮含"发送"/上箭头 / Ctrl+Enter / Alt+Enter |
6. 已校准配置
| 参数 |
窗口 |
值 |
校准日期 |
| 输入框中心X |
702×783 |
479 |
2026-05-10 (实测算) |
| 输入框中心Y |
702×783 |
687 |
2026-05-10 (实测算) |
| 离右边缘 |
702 |
223px |
2026-05-10 |
| 离底边缘 |
783 |
96px |
2026-05-10 |
| 编辑器选择器 |
- |
.public-DraftEditor-content |
2026-05-10 |
7. 已知问题与修复记录
| 问题 |
状态 |
根因 |
修复 |
a[href*="/video/"] 不存在 |
✅ 已修 |
抖音用JS点击事件,不用a标签 |
改用 [data-e2e="alink-item"] |
_detect_page_state 太严格 |
✅ 已修 |
要求video≥2+playerUI才判player |
简化:有video就算player |
KeyX 不生效 |
✅ 已修 |
没先点视频区域获取焦点 |
加 _ensure_video_focused() 再按x |
| 焦点检测在两快键之间 |
✅ 已修 |
page.evaluate 干扰焦点 |
删除中间的detect调用 |
| 评论验证假阳性 |
✅ 已修 |
body.innerText.includes 命中input内容 |
改查 [data-e2e="comment-list"] |
| execCommand不触发React |
⚠️ 待测 |
Draft.js拦截DOM修改 |
改用 page.keyboard.type() |
| Camoufox profile lock |
⚠️ 待修 |
强杀不释放 .parentlock |
每次启动前检测+清理 |
8. 后续开发规范
- 任何修改前先确认状态 — 读当前状态ID,只执行当前状态允许的操作
- 一次只改一个原子操作 — 不改整条链,改完就测
- 改完更新文档 — 状态机、蓝图、选择器、配置同步更新
- 所有坐标必须从配置读 — 不硬编码,
ui_layout.py 统一管理
- 验证防假阳性 — 检查特定元素,不用
body.innerText.includes()
- 双击之间不插操作 — 不调用
page.evaluate 或其他可能干扰焦点的操作