Added senario name in config selector. also now digipay works
This commit is contained in:
@@ -204,8 +204,8 @@
|
|||||||
<option value="onclick">URL match — onclick goToProduct URL fragment</option>
|
<option value="onclick">URL match — onclick goToProduct URL fragment</option>
|
||||||
<option value="text">Text match — product title text</option>
|
<option value="text">Text match — product title text</option>
|
||||||
</select>
|
</select>
|
||||||
<p class="hint" id="digipay-onclick-hint">Paste URL fragment from <code>goToProduct('https://…')</code> — e.g. <code>roshdbook.ir/product/شیمی-دهم/</code></p>
|
<p class="hint" id="digipay-onclick-hint">Paste URL fragment from goToProduct('https://…')— e.g. roshdbook.ir/product/شیمی-دهم/</p>
|
||||||
<p class="hint" id="digipay-text-hint" style="display:none">Paste the visible Persian product title — e.g. <code>شیمی دهم تک جلدی انتشارات مبتکران</code></p>
|
<p class="hint" id="digipay-text-hint" style="display:none">Paste the visible Persian product title — e.g. شیمی دهم تک جلدی انتشارات مبتکران</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-row">
|
<div class="form-row">
|
||||||
<div class="fg">
|
<div class="fg">
|
||||||
@@ -821,7 +821,8 @@ function renderConfigs() {
|
|||||||
<span class="pill id-badge">#${c.id}</span>
|
<span class="pill id-badge">#${c.id}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="config-meta">
|
<div class="config-meta">
|
||||||
Searches: <strong>${(c.search_texts_json || []).length}</strong> text(s) ·
|
<span class="pill" style="font-size:11px;text-transform:uppercase;letter-spacing:.05em">${esc(c.scenario || 'dynamic')}</span>
|
||||||
|
· Searches: <strong>${(c.search_texts_json || []).length}</strong> text(s) ·
|
||||||
Items: <strong>${(c.target_item_ids_json || []).length}</strong> ID(s) ·
|
Items: <strong>${(c.target_item_ids_json || []).length}</strong> ID(s) ·
|
||||||
Max scrolls: <strong>${c.max_scrolls}</strong> ·
|
Max scrolls: <strong>${c.max_scrolls}</strong> ·
|
||||||
Search box: <code style="font-size:11px">${esc(c.search_box_selector || '—')}</code>
|
Search box: <code style="font-size:11px">${esc(c.search_box_selector || '—')}</code>
|
||||||
@@ -846,7 +847,7 @@ function _refreshBatchConfigSelect() {
|
|||||||
const sel = document.getElementById('batch-config-select');
|
const sel = document.getElementById('batch-config-select');
|
||||||
const cur = sel.value;
|
const cur = sel.value;
|
||||||
sel.innerHTML = '<option value="">— select a config —</option>' +
|
sel.innerHTML = '<option value="">— select a config —</option>' +
|
||||||
_configs.map(c => `<option value="${c.id}" ${String(c.id) === cur ? 'selected' : ''}>${esc(c.name)} (#${c.id})</option>`).join('');
|
_configs.map(c => `<option value="${c.id}" ${String(c.id) === cur ? 'selected' : ''}>${esc(c.name)} (#${c.id}) [${esc(c.scenario || 'dynamic')}]</option>`).join('');
|
||||||
}
|
}
|
||||||
|
|
||||||
async function deleteConfig(id) {
|
async function deleteConfig(id) {
|
||||||
|
|||||||
+8
-4
@@ -1,4 +1,5 @@
|
|||||||
"""Stealth browser factory — bypasses Cloudflare/ArvanCloud bot detection."""
|
"""Stealth browser factory — bypasses Cloudflare/ArvanCloud bot detection."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import os
|
import os
|
||||||
@@ -107,6 +108,11 @@ def make_driver(headless: bool | None = None) -> uc.Chrome:
|
|||||||
use_subprocess=True,
|
use_subprocess=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if not use_headless:
|
||||||
|
driver.maximize_window()
|
||||||
|
else:
|
||||||
|
driver.set_window_size(1920, 1080)
|
||||||
|
|
||||||
stealth(
|
stealth(
|
||||||
driver,
|
driver,
|
||||||
languages=["en-US", "en"],
|
languages=["en-US", "en"],
|
||||||
@@ -119,13 +125,11 @@ def make_driver(headless: bool | None = None) -> uc.Chrome:
|
|||||||
|
|
||||||
driver.execute_cdp_cmd(
|
driver.execute_cdp_cmd(
|
||||||
"Page.addScriptToEvaluateOnNewDocument",
|
"Page.addScriptToEvaluateOnNewDocument",
|
||||||
{
|
{"source": """
|
||||||
"source": """
|
|
||||||
Object.defineProperty(navigator, 'webdriver', {get: () => undefined});
|
Object.defineProperty(navigator, 'webdriver', {get: () => undefined});
|
||||||
Object.defineProperty(navigator, 'plugins', {get: () => [1, 2, 3, 4, 5]});
|
Object.defineProperty(navigator, 'plugins', {get: () => [1, 2, 3, 4, 5]});
|
||||||
Object.defineProperty(navigator, 'languages', {get: () => ['en-US', 'en']});
|
Object.defineProperty(navigator, 'languages', {get: () => ['en-US', 'en']});
|
||||||
"""
|
"""},
|
||||||
},
|
|
||||||
)
|
)
|
||||||
log.info("Driver ready (Chrome %d)", major)
|
log.info("Driver ready (Chrome %d)", major)
|
||||||
return driver
|
return driver
|
||||||
|
|||||||
@@ -36,13 +36,41 @@ _SEARCH_TRIGGER_CANDIDATES = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
_CONTAINER_FIX_JS = """
|
||||||
|
(function() {
|
||||||
|
function inject() {
|
||||||
|
if (document.getElementById('__seed_fix')) return;
|
||||||
|
var s = document.createElement('style');
|
||||||
|
s.id = '__seed_fix';
|
||||||
|
s.textContent = '.container{margin-left:0!important;margin-right:0!important}';
|
||||||
|
document.head.appendChild(s);
|
||||||
|
}
|
||||||
|
inject();
|
||||||
|
new MutationObserver(inject).observe(document.head, {childList: true});
|
||||||
|
new MutationObserver(inject).observe(document.documentElement, {childList: true});
|
||||||
|
})();
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def _inject_container_fix(driver: uc.Chrome) -> None:
|
||||||
|
driver.execute_script(_CONTAINER_FIX_JS)
|
||||||
|
|
||||||
|
|
||||||
def _wait_spa_ready(driver: uc.Chrome, timeout: float = 20.0) -> None:
|
def _wait_spa_ready(driver: uc.Chrome, timeout: float = 20.0) -> None:
|
||||||
"""Wait for DOM ready + Vue/React hydration (no pending network requests)."""
|
"""Wait for DOM ready + Vue/React hydration (no pending network requests)."""
|
||||||
|
# Force desktop viewport — --window-size sets outer chrome, not inner viewport
|
||||||
|
driver.execute_cdp_cmd("Emulation.setDeviceMetricsOverride", {
|
||||||
|
"width": 1920,
|
||||||
|
"height": 1080,
|
||||||
|
"deviceScaleFactor": 1,
|
||||||
|
"mobile": False,
|
||||||
|
})
|
||||||
WebDriverWait(driver, timeout).until(
|
WebDriverWait(driver, timeout).until(
|
||||||
lambda d: d.execute_script("return document.readyState") == "complete"
|
lambda d: d.execute_script("return document.readyState") == "complete"
|
||||||
)
|
)
|
||||||
# Extra pause for SPA JS to hydrate
|
# Extra pause for SPA JS to hydrate
|
||||||
time.sleep(1.5)
|
time.sleep(1.5)
|
||||||
|
_inject_container_fix(driver)
|
||||||
|
|
||||||
|
|
||||||
def _find_search_input(driver: uc.Chrome, wait: WebDriverWait) -> Any:
|
def _find_search_input(driver: uc.Chrome, wait: WebDriverWait) -> Any:
|
||||||
@@ -106,6 +134,7 @@ class DigiPayFlow(DynamicFlow):
|
|||||||
wait_long.until(
|
wait_long.until(
|
||||||
EC.presence_of_element_located((By.CSS_SELECTOR, _RESULTS_WAIT_CSS))
|
EC.presence_of_element_located((By.CSS_SELECTOR, _RESULTS_WAIT_CSS))
|
||||||
)
|
)
|
||||||
|
_inject_container_fix(self.driver)
|
||||||
human_delay(1.0, 2.0)
|
human_delay(1.0, 2.0)
|
||||||
|
|
||||||
ms = int((time.monotonic() - t0) * 1000)
|
ms = int((time.monotonic() - t0) * 1000)
|
||||||
|
|||||||
Reference in New Issue
Block a user