fix(切号): 等面板/等回读改成轮询(固定 sleep 会把"没加载出来"误判成"本机没登这个号")+ 实测选择器入档

A01(抖音 40.6.0)实测:
· 冷启动后按固定 sleep(1.2s) 找账号列表行 → 列表还没渲染 → 判成"本机没登这个号" → **漏发且不报错**;
  现在两处都轮询到 deadline:等面板出目标行、等回读变成目标名。
· 实测选择器(写进 doc/research/U2_ELEMENT_SELECTORS.md §七 与 .env.example):
  昵称 `resourceId=com.ss.android.ugc.aweme:id/tk+`、切换入口 `descriptionContains=切换账号`、
  切号按名字点行 `textContains=<名字>`(行 TextView 不可点但 u2 按中心坐标点,行容器接住)、
  「我」页签 `descriptionContains=我`(desc 是「我,按钮」)。
· 并记下"为什么不要用 (…b7w)[N]":序号型会随列表行数/顺序错位 → 点错号 = 发到别人号上。
· 冷启动切号 17.2s 成功、热态 12.9s;A01 最终已切回原账号。
This commit is contained in:
2026-10-12 09:12:23 +08:00
parent beda0f3779
commit 1e8b97e386
6 changed files with 112 additions and 33 deletions
+3 -3
View File
@@ -140,8 +140,8 @@ from .generic import task # 触发 @register_task(当前唯一任务类型)
| 22 | `mark_done` | 记为已做 | —(身份元素可选填 `selector_type`/`selector_value`) | — | — | 把身份值记进**去重账本**(跨设备共享)。放动作**成功之后**——失败不记账,下次重跑会重试。**留空 = 自动跟随上面「去重」检查的身份(推荐)**;没有前置检查时才退回设备 `serial`。见 §4.6 |
| 23 | `push_release` | 推送发布视频 | `date_source`(today/fixed)、`fixed_date` | `account`(name/phone)、`account_phone`、`album_dir`(/sdcard/DCIM/Camera)、`to_clipboard`(True)、`retry_failed`(True)、`max_same_run`(1) | — | 取**本次要发的那个名字**名下的「发布计划」→ **只把素材推到手机**(+ 触发相册刷新 + 标题写进剪贴板)。**不发布** —— 抖音流程你自己在后面画。`account=name`(默认)时**推送前会重读昵称核对**,对不上就不推。幂等靠原子占位(多设备/重跑不会重复推)。见 §4.7 |
| 24 | `mark_release` | 标记发布结果 | `result`(published/failed/unknown)、`why` | `capture_link`(True)、`delete_phone`(True)、`album_dir` | — | 放在**你自己发布流程的末尾**:把"发出去了没有"回写到计划行(成功时顺手抓作品分享链接、删手机上的素材)。拿不准就填 `unknown` —— 平台不会自动重发它。见 §4.7 |
| 25 | `read_account_name` | 读取本机账号名字 | `selector_value`(昵称控件,**待抓**) | `selector_type`(xpath)、`timeout`(8)、`on_missing`(fail) | — | 读抖音「我」页面上的**昵称** → 清洗后存进运行期变量(`account_name`),并记下用的选择器供 `push_release`/`switch_account` 核对。**读不到 = 本机没有号**:条件判断走 `else`、推送跳过(都不发)。`on_missing=fail`(默认) 让这一步直接失败、`skip` 只记一句继续。见 §4.7 |
| 26 | `switch_account` | 切换账号 | `switch_selector_value`(进「账号切换」列表那一下,**待抓**) | `target`(`var:target_name`)、`target_value`、`name_selector_*`、`me_selector_*`(descriptionContains=我)、`timeout`(8)、`verify`(True)、`max_attempts`(2)、`on_login_page`(stop) | — | **只在本机已登录过的号之间切**:当前已是目标→直接成功;否则点「我」→ 点切换入口 → **在列表里按名字找控件点击**(**禁 `[N]` 序号**:序号型会随界面变形错位)→ 等重载 → **回读校验**。列表里没这个名字 = 本机没登这个号 → 立刻 False(不重试、不算故障)。停在登录/验证码页 → 通知 + 默认停本设备(平台不做登录/滑块)。见 §4.7 |
| 25 | `read_account_name` | 读取本机账号名字 | `selector_value`(昵称控件;值填在 `.env` 的 `DOUYIN_NAME_SELECTOR_VALUE`,40.6.0 实测值见 [research/U2_ELEMENT_SELECTORS.md](research/U2_ELEMENT_SELECTORS.md) §七) | `selector_type`(xpath)、`timeout`(8)、`on_missing`(fail) | — | 读抖音「我」页面上的**昵称** → 清洗后存进运行期变量(`account_name`),并记下用的选择器供 `push_release`/`switch_account` 核对。**读不到 = 本机没有号**:条件判断走 `else`、推送跳过(都不发)。`on_missing=fail`(默认) 让这一步直接失败、`skip` 只记一句继续。见 §4.7 |
| 26 | `switch_account` | 切换账号 | `switch_selector_value`(进「账号切换」列表那一下;填 `.env` 的 `DOUYIN_SWITCH_SELECTOR_VALUE`,实测值同上) | `target`(`var:target_name`)、`target_value`、`name_selector_*`、`me_selector_*`(descriptionContains=我)、`timeout`(8)、`verify`(True)、`max_attempts`(2)、`on_login_page`(stop) | — | **只在本机已登录过的号之间切**:当前已是目标→直接成功;否则点「我」→ 点切换入口 → **在列表里按名字找控件点击**(**禁 `[N]` 序号**:序号型会随界面变形错位)→ 等重载 → **回读校验**。列表里没这个名字 = 本机没登这个号 → 立刻 False(不重试、不算故障)。停在登录/验证码页 → 通知 + 默认停本设备(平台不做登录/滑块)。见 §4.7 |
| 27 | `foreach_account` | 循环本机待发的号 | — | `date_source`(today/fixed)、`fixed_date`、`retry_failed`(True)、`max_accounts`(0=不限) | `children` | **一轮把本机待发的号跑完**:数据源 = 计划表里还有待发内容的名字(`pending_names`,**不需要账号台账**),逐个写进运行期变量 `target_name` 后跑 children;试过的名字不回头(防死循环);内层"这个名字还有待发就一直发",跑一轮没进展(切号失败/守卫没过)就换下一个。三个干净出口:停止 / 任务时长上限 / `max_accounts`。见 §4.7 |
### 3.2 录制手势:**纯录制、纯回放**(`core/gesture.py`)
@@ -478,7 +478,7 @@ from .generic import task # 触发 @register_task(当前唯一任务类型)
**先读名字、再校验、最后才发**(一台手机登好几个号时必配):
- 「读取本机账号名字」:抓抖音「我」页面上的**昵称控件**(选择器待抓,抓一次即可),
- 「读取本机账号名字」:读抖音「我」页面上的**昵称控件**(选择器抓一次填进 `.env` 的 `DOUYIN_NAME_SELECTOR_VALUE`),
清洗后的名字进运行期变量(见 §3 第 25 行)。**读不到 = 本机没有号**。
- 条件判断用 `cmp_source=account_target`(候选值 = 本次要发的名字,见 §4.2)+ `包含`;
没命中就走 `else` —— **宁可不发,也不发错号**。