fix: 兼容采购规格面板确定按钮 (#157)
This commit is contained in:
@@ -34,8 +34,10 @@ from .pdd_page_classifier import (
|
||||
PAGE_HOME,
|
||||
PAGE_LOGIN_REQUIRED,
|
||||
PAGE_NETWORK_ERROR,
|
||||
PAGE_ORDER_CONFIRMATION,
|
||||
PAGE_PAYMENT,
|
||||
PAGE_RISK_CONTROL,
|
||||
PAGE_UNKNOWN,
|
||||
GoodsOpenTracker,
|
||||
PddPageObservation,
|
||||
classify_pdd_page,
|
||||
@@ -80,6 +82,11 @@ _DIAGNOSTIC_MARKERS = (
|
||||
"手机号登录",
|
||||
"操作频繁",
|
||||
"网络不给力",
|
||||
"确认款式",
|
||||
"确认规格",
|
||||
"已选",
|
||||
"请选择",
|
||||
"确定",
|
||||
)
|
||||
|
||||
|
||||
@@ -167,7 +174,11 @@ def _goods_id_from_url(goods_url: str) -> str:
|
||||
|
||||
|
||||
def _page_kind(root: ET.Element, current_package: str) -> str:
|
||||
return classify_pdd_page(root, current_package).kind
|
||||
kind = classify_pdd_page(root, current_package).kind
|
||||
contextual_targets = _contextual_confirm_targets(root)
|
||||
if kind in {PAGE_UNKNOWN, PAGE_GOODS} and len(contextual_targets) == 1:
|
||||
return PAGE_ORDER_CONFIRMATION
|
||||
return kind
|
||||
|
||||
|
||||
def _selected(root: ET.Element, target: str) -> bool:
|
||||
@@ -324,9 +335,136 @@ def _final_submit_targets(root: ET.Element) -> list[Bounds]:
|
||||
if (bounds[1] + bounds[3]) // 2 < screen_bottom * 0.6:
|
||||
continue
|
||||
targets.add(bounds)
|
||||
targets.update(_contextual_confirm_targets(root))
|
||||
return sorted(targets)
|
||||
|
||||
|
||||
def _contextual_confirm_targets(root: ET.Element) -> list[Bounds]:
|
||||
"""只在强证据规格面板内接受底部“确定”作为最终提交按钮。"""
|
||||
|
||||
nodes = list(root.iter("node"))
|
||||
if sum(
|
||||
node.get("package") == PDD_PACKAGE_NAME for node in nodes
|
||||
) < 3:
|
||||
return []
|
||||
|
||||
parsed_bounds = [
|
||||
bounds
|
||||
for node in nodes
|
||||
if (bounds := _parse_bounds(node.get("bounds", ""))) is not None
|
||||
]
|
||||
if not parsed_bounds:
|
||||
return []
|
||||
screen_right = max(bounds[2] for bounds in parsed_bounds)
|
||||
screen_bottom = max(bounds[3] for bounds in parsed_bounds)
|
||||
screen_area = screen_right * screen_bottom
|
||||
parents = {
|
||||
child: parent
|
||||
for parent in root.iter()
|
||||
for child in parent
|
||||
if child.tag == "node"
|
||||
}
|
||||
|
||||
targets: set[Bounds] = set()
|
||||
for confirm in nodes:
|
||||
if _label(confirm).strip() not in {"确定", "確定"}:
|
||||
continue
|
||||
bounds = _parse_bounds(confirm.get("bounds", ""))
|
||||
if bounds is None or confirm.get("clickable") != "true":
|
||||
continue
|
||||
if (
|
||||
confirm.get("visible-to-user") != "true"
|
||||
or confirm.get("enabled") != "true"
|
||||
):
|
||||
continue
|
||||
if (bounds[1] + bounds[3]) // 2 < screen_bottom * 0.6:
|
||||
continue
|
||||
|
||||
panel = parents.get(confirm)
|
||||
while panel is not None:
|
||||
panel_bounds = _parse_bounds(panel.get("bounds", ""))
|
||||
if panel_bounds is not None:
|
||||
area = (panel_bounds[2] - panel_bounds[0]) * (
|
||||
panel_bounds[3] - panel_bounds[1]
|
||||
)
|
||||
if (not screen_area or area < screen_area * 0.95) and (
|
||||
_has_confirmation_panel_evidence(panel)
|
||||
):
|
||||
targets.add(bounds)
|
||||
break
|
||||
panel = parents.get(panel)
|
||||
return sorted(targets)
|
||||
|
||||
|
||||
def _has_confirmation_panel_evidence(panel: ET.Element) -> bool:
|
||||
"""确认标题、已选摘要、规格标题和数量控件位于同一面板。"""
|
||||
|
||||
nodes = list(panel.iter("node"))
|
||||
labels = [_label(node).replace(" ", "") for node in nodes]
|
||||
has_title = any(
|
||||
label in {"确认款式", "確認款式", "确认规格", "確認規格"}
|
||||
for label in labels
|
||||
)
|
||||
has_summary = any(
|
||||
label.startswith(("已选", "已選", "已选择", "已選擇", "请选择", "請選擇"))
|
||||
for label in labels
|
||||
)
|
||||
has_dimension = any(
|
||||
re.sub(r"[((]\d+[))]$", "", label)
|
||||
in {
|
||||
"颜色分类",
|
||||
"顏色分類",
|
||||
"颜色",
|
||||
"顏色",
|
||||
"尺码",
|
||||
"尺碼",
|
||||
"规格",
|
||||
"規格",
|
||||
}
|
||||
for label in labels
|
||||
)
|
||||
editors = [
|
||||
node
|
||||
for node in nodes
|
||||
if node.get("class") == "android.widget.EditText"
|
||||
and node.get("text", "").strip().isdigit()
|
||||
and int(node.get("text", "0")) > 0
|
||||
]
|
||||
has_decrease = len(_quantity_targets_in_nodes(nodes, "减少数量")) == 1
|
||||
has_increase = len(_quantity_targets_in_nodes(nodes, "增加数量")) == 1
|
||||
return (
|
||||
has_title
|
||||
and has_summary
|
||||
and has_dimension
|
||||
and len(editors) == 1
|
||||
and has_decrease
|
||||
and has_increase
|
||||
)
|
||||
|
||||
|
||||
def _quantity_targets_in_nodes(
|
||||
nodes: list[ET.Element], description: str
|
||||
) -> set[Bounds]:
|
||||
"""读取指定面板内唯一、可用的数量按钮。"""
|
||||
|
||||
targets: set[Bounds] = set()
|
||||
for node in nodes:
|
||||
if description not in {
|
||||
node.get("text", "").strip(),
|
||||
node.get("content-desc", "").strip(),
|
||||
}:
|
||||
continue
|
||||
bounds = _parse_bounds(node.get("bounds", ""))
|
||||
if bounds is None or node.get("clickable") != "true":
|
||||
continue
|
||||
if node.get("enabled", "true") == "false":
|
||||
continue
|
||||
if node.get("visible-to-user", "true") == "false":
|
||||
continue
|
||||
targets.add(bounds)
|
||||
return targets
|
||||
|
||||
|
||||
class U2PddPurchaseAdapter(PddPurchaseAdapter):
|
||||
"""PDD 真机采购演练会话,不提供真实下单能力。"""
|
||||
|
||||
|
||||
Reference in New Issue
Block a user