diff --git a/admin/routes.py b/admin/routes.py
index 902adfa..19eab4d 100644
--- a/admin/routes.py
+++ b/admin/routes.py
@@ -86,6 +86,7 @@ class FlowConfigIn(BaseModel):
target_item_ids: list[str] = []
target_url: str = ""
scenario: str = "dynamic"
+ stop_on_first_click: bool = False
def _to_db_dict(body: FlowConfigIn) -> dict[str, Any]:
@@ -113,6 +114,7 @@ def _to_db_dict(body: FlowConfigIn) -> dict[str, Any]:
"target_item_ids_json": body.target_item_ids,
"target_url": body.target_url,
"scenario": body.scenario,
+ "stop_on_first_click": int(body.stop_on_first_click),
}
diff --git a/admin/static/dashboard.html b/admin/static/dashboard.html
index 2052851..caa7238 100644
--- a/admin/static/dashboard.html
+++ b/admin/static/dashboard.html
@@ -186,6 +186,11 @@
+
+
+
+
+
@@ -1028,6 +1033,7 @@ function openNewForm() {
document.getElementById('cfg-target-url').value = '';
document.getElementById('cfg-search-texts').value = '';
document.getElementById('cfg-item-ids').value = '';
+ document.getElementById('cfg-stop-on-first-click').checked = false;
_resetAdvanced();
document.getElementById('adv-details').removeAttribute('open');
_applyScenario('dynamic');
@@ -1069,6 +1075,7 @@ function openEditForm(id) {
// DigiPay fields
const dpMatch = (scenario === 'digipay') ? (_ist || 'onclick') : 'onclick';
document.getElementById('cfg-digipay-match').value = dpMatch;
+ document.getElementById('cfg-stop-on-first-click').checked = c.stop_on_first_click !== 0;
document.getElementById('cfg-dp-max-scrolls').value = c.max_scrolls ?? 20;
document.getElementById('cfg-dp-scroll-pause').value = c.scroll_pause_ms ?? 1500;
document.getElementById('digipay-onclick-hint').style.display = dpMatch === 'onclick' ? '' : 'none';
@@ -1090,6 +1097,7 @@ function collectForm() {
target_url: document.getElementById('cfg-target-url').value.trim(),
search_texts: document.getElementById('cfg-search-texts').value.split('\n').map(s => s.trim()).filter(Boolean),
target_item_ids: document.getElementById('cfg-item-ids').value.split('\n').map(s => s.trim()).filter(Boolean),
+ stop_on_first_click: document.getElementById('cfg-stop-on-first-click').checked,
};
if (scenario === 'digipay') {
diff --git a/crawler/dynamic_flow.py b/crawler/dynamic_flow.py
index c680fa8..7320743 100644
--- a/crawler/dynamic_flow.py
+++ b/crawler/dynamic_flow.py
@@ -136,6 +136,9 @@ class DynamicFlow:
click_step = self._run_click(item_id, scroll_step.data.get("element"))
result.steps.append(click_step)
+ if click_step.success and self.cfg.get("stop_on_first_click", 0):
+ log.info("Item '%s' clicked — stopping flow", item_id)
+ return result
human_delay(1.0, 2.5)
log.info(
diff --git a/db/connection.py b/db/connection.py
index cf2def3..cf9538e 100644
--- a/db/connection.py
+++ b/db/connection.py
@@ -97,7 +97,8 @@ def _migrate(conn: sqlite3.Connection) -> None:
"max_pages": "INTEGER NOT NULL DEFAULT 10",
"pagination_wait_ms": "INTEGER NOT NULL DEFAULT 1500",
"target_url": "TEXT NOT NULL DEFAULT ''",
- "scenario": "TEXT NOT NULL DEFAULT 'dynamic'",
+ "scenario": "TEXT NOT NULL DEFAULT 'dynamic'",
+ "stop_on_first_click": "INTEGER NOT NULL DEFAULT 0",
}
for col, definition in additions.items():
if col not in existing:
diff --git a/db/repositories/flow_configs.py b/db/repositories/flow_configs.py
index 881aee6..10b214b 100644
--- a/db/repositories/flow_configs.py
+++ b/db/repositories/flow_configs.py
@@ -17,7 +17,7 @@ _FIELDS = [
"max_scrolls", "scroll_pause_ms", "no_new_content_timeout_ms",
"pagination_selector", "pagination_selector_type", "max_pages", "pagination_wait_ms",
"item_selector_template", "item_selector_type", "item_url_template", "target_item_ids_json",
- "target_url", "scenario",
+ "target_url", "scenario", "stop_on_first_click",
]