Compare commits
6
Commits
331999d65c
...
e82fd64695
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e82fd64695 | ||
|
|
4f3027b54f | ||
|
|
438710f830 | ||
|
|
870e7138d5 | ||
|
|
54d9a55ff6 | ||
|
|
af33d5e4a2 |
+16
-8
@@ -136,7 +136,7 @@ from .generic import task # 触发 @register_task(当前唯一任务类型)
|
||||
| 19 | `notify` | 发通知 | — | `title`("")、`message`("")、`level`("info") | — | 推一条**自定义**通知(事件 `task.notify.custom`):标题正文自己写,支持 `{device} {serial} {job} {time} {app} {screen}`;谁收到取决于 webhook 的事件订阅。两者都空则跳过 |
|
||||
| 20 | `stop_self` | 停止本设备 | — | `reason`("") | — | 只停**本设备**的任务(其它设备照跑):置 worker 停止位,后续步骤不再执行,任务记成**被停止而不是失败**(不触发重试) |
|
||||
| 21 | `gesture` | 录制手势 | `points`(录出来的) | `speed`(1.0) | — | **纯录制回放**:把录下的轨迹点列 `[[x,y,t_ms],…]` 按原路径与时长交给设备插值,不做弧线/抖动/手速加工。与「滑动」是两套东西,见 §3.2 |
|
||||
| 22 | `mark_done` | 记为已做 | —(身份元素可选填 `selector_type`/`selector_value`) | — | — | 把身份值记进**去重账本**(跨设备共享)。放动作**成功之后**——失败不记账,下次重跑会重试。留空 = 用设备 `serial` 当身份。**配套「条件判断 → 去重」用**,见 §4.6 |
|
||||
| 22 | `mark_done` | 记为已做 | —(身份元素可选填 `selector_type`/`selector_value`) | — | — | 把身份值记进**去重账本**(跨设备共享)。放动作**成功之后**——失败不记账,下次重跑会重试。**留空 = 自动跟随上面「去重」检查的身份(推荐)**;没有前置检查时才退回设备 `serial`。见 §4.6 |
|
||||
|
||||
### 3.2 录制手势:**纯录制、纯回放**(`core/gesture.py`)
|
||||
|
||||
@@ -375,21 +375,29 @@ from .generic import task # 触发 @register_task(当前唯一任务类型)
|
||||
"then": [{"type": "stop_self", "params": {"reason": "这个号今天已经评过了"}}],
|
||||
"else": [
|
||||
{"type": "click", "params": {"selector_type": "text", "selector_value": "发送"}},
|
||||
{"type": "mark_done", "params": {"selector_type": "resourceId",
|
||||
"selector_value": "com.ss.android.ugc.aweme:id/506"}}
|
||||
{"type": "mark_done", "params": {}} // 身份留空 = 自动跟随上面检查用的身份
|
||||
]}}
|
||||
```
|
||||
|
||||
**身份值**(去重的"谁")= 身份元素读出来的文本(上例=抖音号)。
|
||||
留空则该步骤用**设备 `serial`** 当身份(适合"一号一机")。
|
||||
|
||||
身份**只需在检查侧配一次**:
|
||||
|
||||
| 位置 | 身份元素 | 结果 |
|
||||
|---|---|---|
|
||||
| 条件判断(去重) | 填账号元素 | 检查用这个身份 |
|
||||
| **记为已做** | **留空(推荐)** | **自动跟随上面检查刚解析出的身份** —— 检查与记账必然用同一个 key |
|
||||
| 记为已做 | 也填了 | 用自己填的;**与检查侧不一致时编辑器会告警** |
|
||||
| 记为已做 | 留空 + 任务里没有去重检查 | 退回**设备 `serial`** 当身份("一号一机"场景) |
|
||||
|
||||
> ⚠ 为什么把「留空 = 自动跟随」做成默认:检查与记账算的是同一个 key,
|
||||
> 两处各配一遍、一边填了一边忘了 → 两个 key → **去重静默失效**(最难查的那种 bug,
|
||||
> 现象是"去重没生效、还是重复做")。编辑器保存时会告警「两处身份不一致」
|
||||
> 「有去重检查但没有记为已做」「有记为已做但没有去重检查」。
|
||||
|
||||
**有效期**(去重多久算"新的一轮")在**任务级**配一次:`dedup_reset` = `day`(默认,每天一次)
|
||||
/ `all`(只做一次)/ `hours`(每 `dedup_hours` 小时)。任务编辑器顶部有「去重有效期」下拉。
|
||||
|
||||
> ⚠ **为什么有效期和身份元素都强制"只配一处"**:检查与记账两边算的是同一个 key,
|
||||
> 一边填得不一样就会算出两个 key → **去重静默失效**(最难查的那种 bug)。
|
||||
> 编辑器保存时会直接告警:「身份元素不一致」「有去重检查但没有记为已做」等。
|
||||
|
||||
**四条必须知道的语义**:
|
||||
|
||||
| 情况 | 行为 | 为什么 |
|
||||
|
||||
+12
-8
@@ -507,12 +507,13 @@ var _stepEditor={
|
||||
true, true, isDedup?{hideValue:true}:null)+identRow;
|
||||
}else if(step.type==='mark_done'){
|
||||
h+=_selRowHtml(p,
|
||||
'<div class="help">把<b>身份元素</b>的值记进平台「去重账本」(所有设备共享一份)。'
|
||||
'<div class="help">把身份值记进平台「去重账本」(所有设备共享一份)。'
|
||||
+'放在动作<b>成功之后</b>——动作失败就不会记账,下次重跑还会重试这台设备。'
|
||||
+'<br><b>留空 = 用本设备当身份</b>(一号一机时这样就行)。'
|
||||
+'身份读不到时<b>不记账</b>(记一个空身份会把别的设备挡住)。'
|
||||
+'<br>有效期在上方任务级的<b>「去重有效期」</b>里设。'
|
||||
+'<br>配套的「条件判断 → 去重」放在动作<b>之前</b>,两边填<b>同一个身份元素</b>。</div>');
|
||||
+'<br>✅ <b>留空 = 自动跟随上面「去重」检查的身份</b>(推荐):'
|
||||
+'检查与记账必须用<b>同一个身份字符串</b>,key 才算得到一起;'
|
||||
+'两处各配一遍的话,一边填了另一边忘了去重就会失效。'
|
||||
+'<br>没有前置去重检查、又留空时,退回用<b>本设备</b>当身份(一号一机场景)。'
|
||||
+'<br>有效期在上方任务级的<b>「去重有效期」</b>里设。</div>');
|
||||
}
|
||||
// 共享:触发概率(所有步骤通用,0~100)
|
||||
h+='<div class="form-row" style="margin-top:8px"><div class="form-group"><label>触发概率(%)</label><input type="number" class="form-control" data-key="probability" value="'+(p.probability!=null?p.probability:100)+'" min="0" max="100"><div class="help">0~100,<100 表示按概率执行(如 30=隔几次才触发一次,适合"偶尔点赞")</div></div></div>';
|
||||
@@ -942,8 +943,10 @@ var _stepEditor={
|
||||
}
|
||||
}else if(s.type==='mark_done'){
|
||||
hasMarkDone=true;
|
||||
dedupIdents.push({where:name,type:s.params.selector_type||'text',
|
||||
value:(s.params.selector_value||'').trim()});
|
||||
// 留空 = 自动跟随去重检查的身份(执行器就是这么做的),所以**空值不参与比对**;
|
||||
// 只有"填了、且和检查侧不一样"才是真问题
|
||||
var mv=(s.params.selector_value||'').trim();
|
||||
if(mv)dedupIdents.push({where:name,type:s.params.selector_type||'text',value:mv});
|
||||
}
|
||||
_childArrs(s).forEach(function(a){
|
||||
if(a.length)check(a,name+' 子步骤');
|
||||
@@ -968,7 +971,8 @@ var _stepEditor={
|
||||
warnings.push('去重两处的身份不一致:'+first.where+' 是 '+showId(first)
|
||||
+',而 '+d.where+' 是 '+showId(d)
|
||||
+' —— 身份不一致会算出两个不同的 key,去重会失效'
|
||||
+'(检查查不到记账 / 或者反过来,结果就是重复或漏做)。请把两处填成同一个身份元素。');
|
||||
+'(检查查不到记账 / 或者反过来,结果就是重复或漏做)。'
|
||||
+'建议把「记为已做」的身份元素留空(自动跟随检查),或两处填成同一个元素。');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
+29
-2
@@ -231,6 +231,10 @@ class GenericStepsWorker(BaseWorker):
|
||||
self._path = []
|
||||
self._step_rows = 0 # 本次运行已记录的明细条数(封顶见 _record_step)
|
||||
self._step_capped = False
|
||||
# 「去重」条件最近一次解析出的身份:`mark_done` 留空时复用它,
|
||||
# 保证"检查"与"记账"用的是**同一个身份字符串**(否则 key 对不上、去重失效)
|
||||
self._last_ident = ""
|
||||
self._dedup_seen = False # 本轮是否跑过「去重」检查(区分"没配"与"配了但没读到")
|
||||
# 公共巡检(任务级配置,独立于步骤画布;见 core/patrol.py):
|
||||
# _wid 内部标识,用来记"下次到点 / 上次命中"两个节奏
|
||||
self._watchers = []
|
||||
@@ -693,11 +697,13 @@ class GenericStepsWorker(BaseWorker):
|
||||
# 去重条件:**这个身份在这个任务里做过了吗**(跨设备共享一份账本,
|
||||
# 见 core/dedup.py)。命中=做过 → 走 then 分支(通常放"跳过/停止/发通知")。
|
||||
kind, hours = self._dedup_policy()
|
||||
self._dedup_seen = True # 供「记为已做」区分"没配检查"与"配了但没读到"
|
||||
ident = self._dedup_identity(d, params, "ident_type", "ident_value")
|
||||
if ident is None:
|
||||
found = False # 身份判定不了 → 当"没做过",照常往下做(宁可重复不误拦)
|
||||
detail = "去重:身份读不到,按未做过处理"
|
||||
else:
|
||||
self._last_ident = ident # 供后面「记为已做」复用(见 _exec_mark_done)
|
||||
found = dedup.check(self.ctx.get("job_id", ""), ident, kind, hours)
|
||||
detail = f"去重:身份『{ident}』{'做过' if found else '没做过'}(有效期 {kind})"
|
||||
elif sel_type == "screen":
|
||||
@@ -829,10 +835,31 @@ class GenericStepsWorker(BaseWorker):
|
||||
**放在动作成功之后**:动作失败就不记账,下次重跑会重试该设备——
|
||||
拆成"检查在前、记账在后"两步,天然就是"成功才记账、失败不丢"。
|
||||
身份读不到时**不记账**(记一个空身份会把别人挡住)。
|
||||
|
||||
**身份留空 = 复用前面「去重」条件刚解析出的那个身份**(推荐这样配):
|
||||
检查与记账必须用**同一个身份字符串**,key 才算得到一起;让用户在两处各配一遍
|
||||
元素,一旦不一致(填了一个、另一个忘了)去重就静默失效——这是最容易踩的坑,
|
||||
所以默认行为改成"自动跟随上面的检查"。
|
||||
"""
|
||||
kind, hours = self._dedup_policy()
|
||||
ident = self._dedup_identity(d, params, "selector_type", "selector_value")
|
||||
if ident is None:
|
||||
if (params.get("selector_value") or "").strip():
|
||||
ident = self._dedup_identity(d, params, "selector_type", "selector_value")
|
||||
elif self._last_ident:
|
||||
ident = self._last_ident
|
||||
_log.info(f"[{self.serial}] 记为已做:复用上面去重检查的身份『{ident}』")
|
||||
elif self._dedup_seen:
|
||||
# 上面**跑了**去重检查、但身份读不到(元素没出来/页面没到)。
|
||||
# 这时**绝不能**退回设备身份记一笔:那会在"什么都没做成"的情况下
|
||||
# 把设备标成已做。不记 → 下次重跑会重试。
|
||||
ident = None
|
||||
_log.warning(f"[{self.serial}] 记为已做:上面去重检查没读到身份(页面没到?)"
|
||||
f"→ 本次不记账,下次重跑会重试")
|
||||
else:
|
||||
# 任务里根本没有前置去重检查、又没填身份元素 → 退回设备身份(一号一机场景)
|
||||
ident = self.serial
|
||||
_log.info(f"[{self.serial}] 记为已做:没填身份元素也没有前置去重检查 → "
|
||||
f"按设备身份记账({ident})")
|
||||
if not ident:
|
||||
self.set_action("记为已做:身份读不到 → 本次不记(下次重跑会重试)")
|
||||
return True
|
||||
first = dedup.mark(self.ctx.get("job_id", ""), ident, kind, hours,
|
||||
|
||||
Reference in New Issue
Block a user